拓冰建站拓冰建站
首页 / 资讯中心 / 正文

LLM智能体跨会话记忆难题:双迹编码架构设计与工程实践

1. 项目概述当LLM智能体需要“记住”跨会话的对话最近在折腾LLM智能体Agent时我遇到了一个挺普遍但棘手的问题如何让智能体在多次、独立的对话会话Session中记住并有效利用之前交流过的关键信息比如你昨天和智能体讨论了一个复杂的项目规划今天你重新打开对话窗口想接着问“我们昨天定的那个技术方案第一步具体要做什么” 理想情况下智能体应该能无缝衔接给出精准回答。但现实是大多数基于大语言模型的智能体其记忆机制要么是“金鱼记忆”会话内有效重启即忘要么是“硬盘记忆”把所有历史对话一股脑塞进上下文导致成本飙升、效率低下甚至因上下文长度限制而丢失关键信息。“Drawing on Memory: Dual-Trace Encoding Improves Cross-Session Recall in LLM Agents” 这个标题直译过来是“在记忆上作画双迹编码提升LLM智能体的跨会话回忆能力”。它精准地戳中了当前LLM智能体应用的核心痛点——长期、高效、精准的记忆与回忆。这不仅仅是技术问题更是决定智能体能否从“玩具”升级为“生产力工具”的关键。想象一下一个客服智能体能记住用户三个月前的投诉偏好一个编程助手能记住你整个项目的架构风格一个研究助理能串联起你过去几周阅读的所有文献要点——这种能力带来的体验提升是颠覆性的。这个项目提出的“双迹编码”Dual-Trace Encoding听起来像是一个从认知心理学或神经科学借来的概念。简单理解它可能是在模仿人类记忆的两种模式一种是细节丰富的“情景记忆”比如昨天会议的具体对话另一种是提炼后的“语义记忆”比如从会议中总结出的核心结论和待办事项。通过设计两种不同的信息编码和存储“痕迹”智能体或许能更智能地决定记什么、怎么记、以及何时调取什么记忆从而在跨会话场景下实现更精准、更经济的回忆。接下来我将结合我对LLM架构、向量数据库、记忆机制以及实际部署Agent的经验深入拆解这个项目可能涉及的核心思路、技术实现、实操难点以及背后的深层逻辑。无论你是想自己动手实现一个“长记性”的智能体还是单纯想理解下一代AI助手将如何工作这篇文章都会提供足够“硬核”的实操指南和避坑经验。2. 核心思路拆解为什么需要“双迹”以及它可能是什么要理解“双迹编码”我们得先看看现有方案的局限性。目前让LLM智能体拥有“记忆”的主流方法无外乎以下几种上下文窗口内记忆最简单粗暴把所有历史对话都放在当前Prompt的上下文里。优点是无损、准确。缺点是成本极高GPT-4等模型的API收费按Token计且受限于模型的最大上下文长度如128K对话越长早期信息被“挤出去”或注意力稀释的风险越大。这就像让你带着一本不断变厚的书去考试翻找答案会越来越慢。向量数据库检索将历史对话切片Chunk成片段转换成向量Embedding存入数据库如ChromaDB, Pinecone。当新问题到来时将问题也转换成向量去数据库中检索最相关的几个片段作为“记忆”插入当前上下文。这是目前最流行的长期记忆方案。但它有个核心问题检索的准确性严重依赖于向量相似度。如果用户的问题表述和历史上存储的片段在字面上差异很大但语义相关或者需要综合多个分散片段才能回答检索就可能失败。这好比你的记忆是散落的卡片靠关键词匹配来找有时会找不到关联性很强的卡片。摘要压缩记忆定期或按事件对历史对话进行摘要将冗长的对话压缩成精炼的要点存储起来。下次会话时加载这些摘要作为背景。这解决了信息过载问题但摘要是一个有损压缩过程可能会丢失对未来至关重要的细节。而且由谁、在何时、以何种粒度进行摘要都是需要精心设计的策略。“双迹编码”很可能是在上述方案特别是向量检索的基础上进行的一次架构升级。我的理解是它试图解决单一向量表示的不足“迹”Trace在这里可以理解为对同一段信息的不同编码或索引方式。一条“迹”代表一种访问路径或理解维度。“双迹”Dual-Trace很可能是指为每一段需要记忆的信息同时创建并存储两种或多种不同形式的“索引”或“表示”。2.1 双迹的可能形态与分工基于认知科学和现有技术我推测“双迹”可能指以下两种互补的编码方式Trace 1: 高精度语义向量迹细节记忆目标捕获对话片段的原始语义细节确保在需要精确回溯时能找到“原话”或最相关的上下文。实现使用强大的文本嵌入模型如OpenAI的text-embedding-3系列、BGE-M3等将对话片段可能是经过清洗和结构化的编码成高维向量。这条迹服务于精确检索。当用户的问题与历史细节高度相关时例如“把我昨天说的关于API鉴权方案的第二点再解释一下”主要依靠这条迹在向量数据库中进行相似度搜索。Trace 2: 抽象关键词/元数据迹要点记忆目标提取对话片段中的核心实体、动作、结论和关系形成一种结构化的、符号化的摘要。实现这不一定是一个向量。它可能是一组自动提取的关键词、实体人名、项目名、时间、动作决定、问题、待办或者是一个极简的摘要文本。这条迹可以存储在传统数据库如SQLite、PostgreSQL或图数据库中。它服务于关联检索和逻辑推理。例如当用户问“我们这个项目目前有哪些开放的风险”智能体不需要去向量库大海捞针而是可以直接查询“元数据迹”中标签为“项目XX”、“类型风险”、“状态开放”的所有记录。2.2 双迹协同的工作流程猜想当一个新的用户查询Query到来时智能体的记忆召回模块可能这样工作并行检索同时使用用户查询去搜索“向量迹”和“元数据迹”。向量迹检索将查询文本编码成向量在向量数据库中搜索相似度最高的K个片段。元数据迹检索对查询进行命名实体识别NER或意图分类提取出关键实体和意图然后在元数据数据库中进行查询。例如识别出“项目A”、“进度”等实体查找所有相关记录。结果融合与重排序将两条迹检索回来的结果可能是文本片段列表进行融合。这里需要一个重排序Re-ranking模型或策略来判断哪些结果综合来看最相关。重排序可以考虑原始向量相似度得分、元数据匹配度、时间新鲜度、结果多样性等。构造最终上下文将排名最高的若干个记忆片段连同当前的用户查询一起构造给LLM的Prompt形式可能是“以下是您之前的相关对话记录[记忆片段1] [记忆片段2] ... 基于以上历史和当前问题[用户问题]请回答”这种双管齐下的方式理论上能结合“模糊匹配”向量和“精确过滤”元数据的优点提高跨会话回忆的查全率Recall和查准率Precision。实操心得为什么“融合”是关键也是难点在实际系统中简单的结果合并如取并集可能会引入噪声。更有效的做法是使用一个轻量级的“交叉编码器”Cross-Encoder模型对候选记忆进行重排序。这个模型以“用户查询记忆文本”为输入直接输出一个相关度分数比单纯的向量余弦相似度更准。但这也增加了复杂性和延迟。在资源受限的场景下可以先用元数据迹做硬过滤缩小范围再用向量迹做精细检索最后用简单的规则如加权平均分进行融合。3. 系统架构设计与核心组件实现要实现一个基于双迹编码的记忆系统我们需要设计一个独立于核心LLM推理之外的记忆服务。这个服务负责记忆的写入、存储、更新和检索。下面是一个可落地的架构设计。3.1 整体架构图文字描述用户输入 | v [智能体主逻辑] - 生成记忆存储请求当前对话片段 | | | v | [记忆服务 - 编码与存储层] | | | 1. 文本预处理与分块 | 2. 双迹并行编码 | - 路径A: 调用嵌入模型 - 向量迹 | - 路径B: 调用LLM/规则提取 - 元数据迹 | 3. 双迹存储 | - 向量迹存入向量数据库 (如Chroma) | - 元数据迹存入关系型数据库 (如SQLite) | v [智能体需要回忆时] - 生成记忆检索请求当前用户问题 | v [记忆服务 - 检索与融合层] | 1. 双迹并行检索 - 向量检索问题嵌入在向量库查TOP-K - 元数据检索解析问题实体/意图在关系库查询 2. 结果去重与融合重排序 3. 返回TOP-N最相关记忆片段 | v [智能体主逻辑] - 获得记忆片段拼接到Prompt上下文请求LLM生成最终回答3.2 核心组件一记忆编码器这是实现“双迹”的核心。我们需要两个编码管道。管道A向量迹编码器# 伪代码示例 import openai from langchain.embeddings import OpenAIEmbeddings # 或者使用开源模型如sentence-transformers from sentence_transformers import SentenceTransformer class VectorTraceEncoder: def __init__(self, model_nametext-embedding-3-small): # 方案1: 使用OpenAI API (效果好有成本) # self.embeddings OpenAIEmbeddings(modelmodel_name) # 方案2: 使用本地开源模型 (可控无网络延迟) self.model SentenceTransformer(BAAI/bge-small-zh-v1.5) # 中文优选 def encode(self, text_chunk): 将文本块编码为向量。 # 确保文本不为空可在此处添加清洗逻辑如去除多余空格、特殊字符 cleaned_text self._clean_text(text_chunk) # 使用OpenAI # vector self.embeddings.embed_query(cleaned_text) # 使用本地模型 vector self.model.encode(cleaned_text, normalize_embeddingsTrue) # 归一化便于余弦相似度计算 return vector.tolist() # 转换为列表存储 def _clean_text(self, text): # 简单的文本清洗 import re text re.sub(r\s, , text).strip() return text管道B元数据迹编码器这部分更有趣也更考验设计。元数据可以是结构化的JSON。# 伪代码示例 from langchain.prompts import ChatPromptTemplate from langchain.chat_models import ChatOpenAI import json class MetadataTraceEncoder: def __init__(self): # 使用一个能力较强的LLM来提取结构化信息 self.llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # temperature0保证输出稳定 self.prompt_template ChatPromptTemplate.from_messages([ (system, 你是一个信息提取助手。请从给定的对话文本中提取出关键的结构化信息。), (human, 对话文本{text} 请提取以下信息并以JSON格式返回 1. entities: 列表出现的主要实体如人名、项目名、产品名、地点等。 2. actions: 列表描述的主要动作或事件如“决定采用X方案”、“报告了Y问题”、“计划下周完成Z”。 3. key_points: 列表核心结论或要点如“API鉴权使用OAuth2.0”、“前端框架确定为React”。 4. conversation_type: 字符串对话类型如“需求讨论”、“问题排查”、“方案评审”、“日常同步”等。 5. has_todo: 布尔值是否包含待办事项。 6. summary: 字符串一段非常简短的摘要不超过50字。 只返回JSON不要有其他解释。 ) ]) def encode(self, text_chunk): 调用LLM提取元数据返回结构化字典。 chain self.prompt_template | self.llm try: response chain.invoke({text: text_chunk}) # 解析LLM返回的JSON字符串 metadata json.loads(response.content) # 可以在这里添加一些后处理比如确保字段存在 return metadata except json.JSONDecodeError as e: print(f解析元数据JSON失败: {e}, 原始响应: {response.content}) # 返回一个兜底的元数据结构 return { entities: [], actions: [], key_points: [], conversation_type: unknown, has_todo: False, summary: 摘要提取失败。 }注意事项元数据编码的成本与稳定性使用LLM提取元数据虽然灵活强大但会产生额外的API调用成本并引入延迟。对于高频使用的系统可以考虑以下优化缓存对相同的文本块缓存其元数据提取结果。规则模型混合先用正则表达式或关键词匹配提取明显的实体如全大写的项目代号、日期再调用LLM处理复杂部分。微调小模型针对特定领域收集数据微调一个小的文本分类/信息抽取模型如基于BERT替代通用LLM以大幅降低成本和延迟。3.3 核心组件二记忆存储器我们需要两种存储后端。存储A向量数据库用于向量迹以ChromaDB轻量、易用为例import chromadb from chromadb.config import Settings class VectorMemoryStore: def __init__(self, persist_directory./chroma_db): # 创建持久化客户端 self.client chromadb.PersistentClient(pathpersist_directory, settingsSettings(allow_resetTrue)) # 获取或创建集合类似表 self.collection self.client.get_or_create_collection( nameconversation_memory, metadata{hnsw:space: cosine} # 使用余弦相似度 ) def store(self, id, vector, text_chunk, metadataNone): 存储一条向量记忆。metadata可以包含来源会话ID、时间戳等。 self.collection.add( documents[text_chunk], embeddings[vector], metadatas[metadata] if metadata else [{}], ids[id] ) def search(self, query_vector, top_k5): 检索最相似的top_k条记忆。 results self.collection.query( query_embeddings[query_vector], n_resultstop_k ) # results 包含 ids, documents, distances, metadatas return results存储B关系型数据库用于元数据迹以SQLite为例设计一张表-- 元数据记忆表 CREATE TABLE IF NOT EXISTS metadata_memory ( id TEXT PRIMARY KEY, -- 与向量记忆的ID对应 session_id TEXT, -- 所属会话ID timestamp INTEGER, -- 创建时间戳 raw_text TEXT, -- 原始文本可选也可只存ID去向量库查 entities_json TEXT, -- 实体列表存储为JSON字符串 actions_json TEXT, -- 动作列表存储为JSON字符串 key_points_json TEXT, -- 要点列表存储为JSON字符串 conversation_type TEXT, has_todo BOOLEAN, summary TEXT ); -- 为常用查询字段创建索引 CREATE INDEX idx_session ON metadata_memory (session_id); CREATE INDEX idx_type ON metadata_memory (conversation_type); CREATE INDEX idx_has_todo ON metadata_memory (has_todo); CREATE INDEX idx_timestamp ON metadata_memory (timestamp);使用Python操作import sqlite3 import json class MetadataMemoryStore: def __init__(self, db_path./memory.db): self.conn sqlite3.connect(db_path) self._init_db() def _init_db(self): # 执行上面的建表SQL cursor self.conn.cursor() # ... (省略建表语句执行) self.conn.commit() def store(self, memory_id, session_id, timestamp, raw_text, metadata_dict): 存储一条元数据记忆。 sql INSERT INTO metadata_memory (id, session_id, timestamp, raw_text, entities_json, actions_json, key_points_json, conversation_type, has_todo, summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) cursor self.conn.cursor() cursor.execute(sql, ( memory_id, session_id, timestamp, raw_text, json.dumps(metadata_dict.get(entities, []), ensure_asciiFalse), json.dumps(metadata_dict.get(actions, []), ensure_asciiFalse), json.dumps(metadata_dict.get(key_points, []), ensure_asciiFalse), metadata_dict.get(conversation_type, unknown), 1 if metadata_dict.get(has_todo, False) else 0, metadata_dict.get(summary, ) )) self.conn.commit() def search_by_entities(self, entity_list, limit10): 根据实体列表检索记忆。这是一个简化的示例实际查询可能更复杂。 # 这是一个低效的示例实际中可能需要全文搜索或更复杂的JSON查询 # 对于SQLite可以使用json_each函数或者将数据规范化到另一张表 placeholders ,.join(? for _ in entity_list) # 这是一个非常粗略的匹配查询entities_json字段是否包含任何一个实体词 # 注意这种查询效率不高仅作演示。生产环境应考虑使用PostgreSQL的jsonb或专门的搜索。 sql fSELECT * FROM metadata_memory WHERE id IN ( SELECT DISTINCT id FROM metadata_memory, json_each(entities_json) WHERE json_each.value IN ({placeholders}) ) ORDER BY timestamp DESC LIMIT ? cursor self.conn.cursor() cursor.execute(sql, (*entity_list, limit)) return cursor.fetchall()3.4 核心组件三记忆检索与融合器这是双迹系统智能与否的大脑。class DualTraceRetriever: def __init__(self, vector_store, metadata_store, rerankerNone): self.vector_store vector_store self.metadata_store metadata_store self.vector_encoder VectorTraceEncoder() # 复用编码器来编码查询 self.reranker reranker # 可选的重排序模型 def retrieve(self, query_text, top_k_vector10, top_k_metadata10, fusion_top_n5): 双迹检索与融合。 # 1. 编码查询 query_vector self.vector_encoder.encode(query_text) # 可以在这里添加对query_text的简单元数据提取如实体识别用于元数据检索 # 简化起见假设我们从查询中提取了几个关键词作为实体实际应用需更复杂NLP # extracted_entities simple_ner(query_text) # 2. 并行检索 # 向量检索 vector_results self.vector_store.search(query_vector, top_ktop_k_vector) # 元数据检索 (示例根据时间或类型这里简化) # 实际中应根据query解析出的意图和实体构建SQL查询 metadata_results self.metadata_store.search_recent(limittop_k_metadata) # 假设有一个查最近记录的方法 # 3. 结果对齐与去重 # vector_results 包含 documents, ids, distances # metadata_results 包含 raw_text, id 等 # 我们需要根据ID将两条迹的结果对齐。 all_candidates [] seen_ids set() # 处理向量检索结果 if vector_results and vector_results[documents]: for i, doc in enumerate(vector_results[documents][0]): # 注意chroma返回的结构 mem_id vector_results[ids][0][i] if mem_id in seen_ids: continue seen_ids.add(mem_id) all_candidates.append({ id: mem_id, text: doc, source: vector, score: 1 - vector_results[distances][0][i] if vector_results[distances] else 0.5, # 距离转相似度分数 metadata: vector_results[metadatas][0][i] if vector_results[metadatas] else {} }) # 处理元数据检索结果 (简化处理假设raw_text存在) for row in metadata_results: mem_id row[0] # 假设id在第一列 if mem_id in seen_ids: # 如果已从向量检索中添加可以更新其元数据或分数 for cand in all_candidates: if cand[id] mem_id: cand[metadata].update({sql_metadata: row}) # 补充元数据 cand[score] (cand[score] 0.7) / 2 # 简单融合分数假设元数据检索基础分0.7 continue seen_ids.add(mem_id) all_candidates.append({ id: mem_id, text: row[3], # 假设raw_text在第四列 source: metadata, score: 0.7, # 给予一个基础分数 metadata: {sql_row: row} }) # 4. 重排序 (如果配置了重排序器) if self.reranker: # 将候选文本和查询一起送给重排序模型打分 texts_to_rerank [cand[text] for cand in all_candidates] rerank_scores self.reranker.rank(query_text, texts_to_rerank) for i, cand in enumerate(all_candidates): cand[rerank_score] rerank_scores[i] # 按重排序分数排序 all_candidates.sort(keylambda x: x.get(rerank_score, x[score]), reverseTrue) else: # 按现有分数排序 all_candidates.sort(keylambda x: x[score], reverseTrue) # 5. 返回Top-N return all_candidates[:fusion_top_n]4. 实操部署与关键参数调优设计好组件后如何将它们集成到一个真实的LLM智能体应用中并调优到最佳状态以下是关键步骤和参数。4.1 记忆的写入策略何时触发存储不能每句话都存那会产生大量冗余和噪声。常见的触发策略有按对话轮次摘要存储每完成N轮对话例如5-10轮调用LLM对这几轮对话生成一个摘要并将摘要存储为一条记忆。这能捕捉一个连贯的“话题单元”。按事件/意图存储当检测到对话中出现了明确的“结论”、“决定”、“待办事项”、“问题解决方案”时触发存储。这需要较强的意图识别能力。用户显式指令存储当用户说“请记住这一点...”或“把这个记下来”时存储当前内容。定时/定量存储最简单的策略每隔一段时间或积累一定Token数后存储最近的对话内容。实操建议从按对话轮次摘要存储开始。它平衡了连贯性和存储频率。例如在LangChain框架中可以使用ConversationSummaryBufferMemory的变体但将其输出同时写入我们的双迹存储。4.2 记忆的分块Chunking策略原始对话流需要被切割成适合编码和检索的“块”。分块不当会导致信息碎片化或丢失上下文。固定长度分块按字符或Token数切分。简单但可能切断一个完整的句子或思路。句子或段落分块按自然语言边界句号、换行切分。更符合语义但块的大小可能不均匀。语义分块使用嵌入模型计算句子间的相似度在相似度低的地方切分。效果最好但计算复杂。滑动窗口分块设置重叠区域确保边界信息不会丢失。例如每块256个Token重叠64个Token。参数调优对于对话场景推荐按说话人轮次结合固定长度的方式。例如将连续的用户-助手交互对作为一个基础块如果这个块太长超过300 Token再按句子进行二次分割。重叠窗口可以设置为50-100个Token。4.3 检索的触发与上下文构造同样不是每个用户问题都需要去记忆库检索。检索触发判断可以先用一个简单的分类器或基于规则的判断来决定是否需要长期记忆。例如如果用户问题中包含“之前”、“上次”、“记得”、“关于XXX历史实体”等关键词则触发检索。也可以用一个轻量级模型来判断当前问题是否与历史相关。上下文构造检索到记忆片段后如何放入给LLM的Prompt典型结构是系统指令: 你是一个有帮助的助手可以参考以下历史对话来回答问题。 历史相关记忆: [记忆片段1时间昨天内容我们决定使用Python的FastAPI框架来构建后端。] [记忆片段2时间上周内容项目“雅典娜”的数据库选型定为PostgreSQL。] 当前对话: 用户我们后端框架和数据库分别定的是什么 助手关键点在每个记忆片段前加上简单的元数据如时间能极大帮助LLM理解记忆的时效性和上下文。同时要严格控制插入记忆的总Token数避免挤占当前对话的空间。4.4 元数据模式的设计与演进这是双迹系统的“灵魂”。一开始的元数据字段如entities,actions,type可能不完善。启动方案从简单开始。先定义3-5个最关键的字段如topics话题、has_decision是否有决定、people涉及人员。迭代优化在系统运行一段时间后分析检索成功和失败的案例。看看哪些问题本应通过元数据快速找到却失败了。根据这些分析增加或修改元数据字段。例如发现很多关于“截止日期”的问题检索不到就可以增加deadlines字段。自动化与人工标注初期可以结合规则和LLM来提取元数据。后期如果数据量大可以考虑微调一个小的信息抽取模型专门针对你的对话领域这样准确率和速度都会提升。5. 避坑指南与性能优化实战在实际部署中我踩过不少坑这里分享最关键的几点。5.1 坑一记忆污染与信息冲突当记忆库变得庞大不同会话、甚至同一会话不同阶段的信息可能产生矛盾。例如早期决定“用方案A”后期推翻了并决定“用方案B”。如果检索时同时返回了这两条矛盾记忆会干扰LLM判断。解决方案记忆版本化与衰减为每条记忆附加一个“强度”或“新鲜度”分数该分数随时间衰减。当检索到矛盾记忆时优先相信新鲜度高的。也可以在存储时如果检测到与已有记忆明显冲突通过LLM判断则标记旧记忆为“已覆盖”而不是删除以备追溯。会话隔离与全局共享设计记忆的访问权限。有些记忆可能是会话特定的如临时讨论的草稿有些是全局的如项目最终决定。在存储和检索时通过session_id和scope如session/global字段进行区分。5.2 坑二检索延迟与成本双迹编码意味着每次检索至少进行两次查询如果还用LLM做元数据提取和重排序延迟和API成本可能成为瓶颈。优化策略异步与非阻塞写入记忆的编码和存储操作尤其是调用LLM提取元数据应该与主对话流异步进行不要阻塞用户得到当前响应。缓存检索结果对于相同或相似的用户查询在一定时间窗口内例如5分钟可以直接返回缓存的记忆片段避免重复检索。简化元数据模型初期可以不使用LLM而是用基于规则的关键词提取和分类如使用spaCy或NLTK。虽然精度稍低但速度快、零成本。向量检索优化使用高效的向量索引如HNSWChroma和多数向量库默认支持。确保索引参数如M和ef_construction针对你的数据规模进行了调优。分级检索先使用成本低、速度快的元数据检索如基于关键词的数据库查询过滤出一个较小的候选集例如100条再在这个小集合上做精确但耗时的向量相似度计算和重排序。5.3 坑三LLM的“记忆幻觉”即使你提供了正确的历史记忆LLM也可能忽略它或者捏造一个看似合理但错误的“记忆”。这在记忆片段较多或相关性不强时尤其常见。缓解措施强化Prompt指令在系统指令中明确要求“严格依据提供的历史信息回答如果历史信息中没有提及请直接说明不知道不要编造”。格式化记忆呈现将记忆以清晰、结构化的方式呈现比如使用- [时间] 内容的列表格式并在开头加上“历史参考信息”等醒目标题。限制记忆数量不要一次性注入太多记忆片段。通过重排序和分数阈值只选择最相关的3-5条。质量优于数量。让LLM引用来源要求LLM在回答中指明其依据的是哪条记忆例如“根据[历史记录1]...”。这不仅能增加可信度也能在出错时方便溯源。5.4 评估记忆系统的有效性如何知道你的双迹系统是否真的提升了“跨会话回忆”能力需要设计评估指标。人工评估黄金标准构建一个测试集包含多个会话的历史对话和跨会话的查询由人工判断智能体的回答是否准确利用了历史信息。计算准确率。自动代理评估使用一个更强的LLM如GPT-4作为裁判给定历史对话、当前问题、智能体回答让裁判判断回答是否与历史信息一致、是否相关。检索指标在系统层面评估记忆检索的准确度检索召回率RecallK对于一个问题人工标注所有相关记忆片段看系统检索到的Top-K结果中包含多少相关片段。检索精确率PrecisionK系统检索到的Top-K结果中有多少是真正相关的。A/B测试在真实应用中将用户随机分组一组使用带双迹记忆的智能体另一组使用无记忆或单向量记忆的基线智能体比较任务完成率、用户满意度或对话轮次等业务指标。部署这样一个系统绝非一蹴而就。从简单的向量检索单迹系统开始逐步引入元数据迹小步快跑持续根据评估结果和用户反馈进行迭代才是稳健的落地之道。双迹编码的真正价值在于它为我们提供了一种框架性的思路让我们能够更结构化、更多维地处理智能体的记忆问题从而向构建真正“善解人意”且“博闻强识”的AI伙伴迈出坚实的一步。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门