AI Agent开发(3) -如何做上下文管理?

张开发
2026/4/8 13:19:17 15 分钟阅读

分享文章

AI Agent开发(3) -如何做上下文管理?
目录前言思路collection设计示例前言书接上文https://blog.csdn.net/roadtohacker/article/details/156004134在Agent开发中上下文信息的处理很重要当用户给出新的输入的时候如何让模型保持对旧消息的记忆我们知道对于大模型而言有知识和记忆两种数据输入。模型训练固化下来的参数和外挂的知识库可以认为是模型的知识模型还有长期记忆和短期记忆长期记忆可以理解为用户长时间之前的输入或者说用户画像短期记忆就是用户最近的输入。但每次请求对于模型而言都是独立的也就是说模型本身不会记住之前的输入因为它本身的参数不会跟着输入的变化而变化(但谷歌、英伟达在做类似的研究https://www.thepaper.cn/newsDetail_forward_32133128https://36kr.com/p/3647056233926277让模型在推理贵过程中动态调整参数实现真正的“记忆”)所以说让模型拥有记忆实际上是一个工程问题。思路一个简单的办法是把所有的对话都原封不动的发送给大模型这样能够保留所有的记录但问题是这样token的增长是线性的如果不控制一会就超过上下文限制了而且这种方式是把不太重要的消息跟重要的消息注意力提到同等位置也会导致模型忽略了重要的信息。所以我们可以考虑短期记忆和长期记忆结合的方式来做上下文的管理。对于短期记忆我们可以保留最近的n nn轮完整用户对话只需要把最近n nn轮对话原封不动再发送给大模型即可这样最近若干轮用户的输入模型一定是原原本本的保存下来的对于长期记忆可以通过摘要向量数据库(milvus)存储的方式对用户的输入到向量数据库与检索topk个历史记录把它跟短期记忆结合到一块这样就实现了完整的上下文记忆collection设计var(ChatHistoryCollectionCollection{Name:CHatHistoryCollectionName,Schema:entity.Schema{Description:AI助手用户对话历史表,CollectionName:CHatHistoryCollectionName,Fields:[]*entity.Field{{Name:id,DataType:entity.FieldTypeInt64,AutoID:true,PrimaryKey:true,},{Name:user,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:255,},},{Name:request_id,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:255,},},{Name:session,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:255,},},{Name:content,DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{max_length:65535,},},{Name:vector,DataType:entity.FieldTypeFloatVector,TypeParams:map[string]string{dim:1024,},},{Name:timestamp,DataType:entity.FieldTypeInt64,TypeParams:map[string]string{max_length:255,},},},},})// DB初始化func(a*AgentMilVusClient)DBInit(ctx context.Context)error{collections:[]Collection{ChatHistoryCollection,}for_,collection:rangecollections{has,err:a.MilVusCli.HasCollection(ctx,milvusclient.NewHasCollectionOption(collection.Name))iferr!nil{returnerr}ifhas{log.Logger.Info(AgentMilVusClient found collection, skipped,name,collection.Name)}else{log.Logger.Info(AgentMilVusClient creating collection...,name,collection.Name)iferra.MilVusCli.CreateCollection(ctx,milvusclient.NewCreateCollectionOption(collection.Name,collection.Schema));err!nil{returnerr}}index:milvusindex.NewGenericIndex(vector_index,map[string]string{metric_type:string(entity.COSINE),})log.Logger.Info(AgentMilVusClient creating index...,name,collection.Name,index,index.Name())createdIndexTask,err:a.MilVusCli.CreateIndex(ctx,milvusclient.NewCreateIndexOption(CHatHistoryCollectionName,vector,index))iferr!nil{log.Logger.Error(err,failed to create index,name,collection.Name)returnerr}iferrcreatedIndexTask.Await(ctx);err!nil{log.Logger.Error(err,failed to create index,name,collection.Name)returnerr}if_,erra.MilVusCli.LoadCollection(ctx,milvusclient.NewLoadCollectionOption(CHatHistoryCollectionName));err!nil{log.Logger.Error(err,failed to load collection,name,collection.Name)}log.Logger.Info(AgentMilVusClient has created collection,name,collection.Name)}returnnil}示例下面是一个长短期记忆结合的例子// loadUserHistory topK组长期记忆最近k轮短期记忆func(a*AgentService)loadUserHistory(ctx context.Context,cDto*dto.ChatDto,kint)[]*schema.Message{results,err:a.agentMilVusCli.Search(ctx,milvuscli.AgentSearchParams{TopK:5,Message:cDto.Message,FieldNames:[]string{content},Session:cDto.Session,User:cDto.Erp,})varmsgs[]*schema.Messageiferr!nil{log.Logger.Error(err,failed to search from milVus,user,cDto.Erp,session,cDto.Session)}else{for_,result:rangeresults{contentCol:result.GetColumn(content)fori:0;iresult.ResultCount;i{content,err:contentCol.Get(i)iferr!nil{log.Logger.Error(err,failed to get the content,user,cDto.Erp,session,cDto.Session)continue}msgsappend(msgs,schema.SystemMessage(gconv.String(content)))}}}returnappend(msgs,a.loadUserHistoryFromRedis(ctx,cDto,k)...)}上面是一个简单的思路但生产环境可能遇到下面的问题仅用“固定保留最近n轮”容易在长对话、长输入时爆token上限或把无关长段文字保留而挤占真正重要信息。单纯“向量top-k”易命中相似但不关键的片段且易受长文本噪声影响。具体实现需要结合下面的指标检测进行动态调整设定指标检索命中率是否命中真正所需记忆、答案正确率/一致性、摘要一致性与抽取真值对齐度、时延与token成本。灰度与A/B对记忆策略n、topk、权重公式做可控实验落库版本化便于回滚。

更多文章