对话助手 Agent 工程:从上下文装配到数据飞轮
这篇文档梳理一个对话式 Agent 的完整架构。目标系统需要联网取数、调用外部工具,并在多轮对话和跨会话之间保留用户信息。
有三个问题贯穿全文:上下文由哪个组件装配、记忆在什么时机读写、工具列表按什么规则裁剪。这三点决定了系统在功能持续增加之后是否仍然可控。线上大量「同一个问题问两遍、回答不一致」的偶发问题都源于此:某一轮的用户偏好因历史过长被裁掉,或者工具结果这一轮拼进 user 消息、下一轮拼进 system。
内容按一次请求的处理顺序组织:入口分诊、上下文装配、记忆读写、工具调用、深度研究编排、稳定性保障,然后是离线数据闭环,最后给出一份最小可用版本的取舍建议。全文共八张架构图,建议按顺序阅读,后文会反复引用前面的结论。
以下内容不在覆盖范围内:基座模型的训练与微调、RAG 的索引构建与切分策略、多模态输入的理解链路、安全与合规的策略设计。这几项各自都能单独成篇,这里只在与主链路交接的地方带过。
图中的 token 预算比例、轮次上限等数值均为示意值,用于说明各部分的相对量级,不是实测数据。
1 全景分层架构
请求进入后,编排层的第一个动作是分诊而不是回答。一类问题要求秒级响应(「现在还开门 吗」),另一类允许较长耗时但要求覆盖完整(「六天行程怎么排」)。两者的延迟预算、状态管理和失败处理方式都不同,放在同一条链路上,响应快的那类会被慢的拖住。因此先在编排层把它们分开。
左侧六层是请求实际经过的路径。右侧三块不在请求路径上,但每一层都会向它们写入 Trace,或从它们读取配置(策略、降级开关)。
| 层 | 职责 | 关键决策 |
|---|---|---|
| 入口 | 独立端与宿主 App 内嵌等多入口 | 入口差异只体现在展示层与可用工具集,不分叉后端编排 |
| 接入 / BFF | 鉴权、限流、SSE 流式回传、会话句柄落定、策略与实验路由 | 会话 ID 在这一层确定,后面所有上下文读写都以它为键 |
| 编排 | 状态化任务编排,Router 分诊到三条链路 | 延迟敏感与深度研究物理隔离 |
| 能力 | 上下文装配、统一工具协议、记忆服务、检索、缓存 | Context Builder 是唯一的 prompt 装配点,避免各节点分别拼接 |
| 模型 | 主对话基座 + VLM + 一组旁路小模型 | 旁路模型独立 context window,不与主链路抢 token 预算 |
| 存储 | 热会话、冷对话、长期记忆、向量、Trace | 热冷分离;Trace 单独一路,是离线飞轮的取数口 |
2 一次对话的完整时序
这张图说明各组件之间的调用时序。其中三处容易被忽略:记忆在第 ⑤ 步一次性读出,不由模型自行查询;工具结果在第 ⑪ 步二次拼回上下文,由此构成 ReAct 循环;旁路小模型在第 ⑭ 步于轮末异步执行,不阻塞首 token。
用户感知到的延迟从「输出」阶段开始计算,因此 ①–⑧ 必须在首 token 之前完成,⑬–⑯ 全部推迟到轮末异步执行。⑨–⑪ 是可重复的循环,循环次数由 Reflect 判定,或在达到上限后强制终止。
做成工具意味着模型需要先判断是否查询、再执行查询、再生成回答,多出一整轮往返,首 token 延迟大约翻倍。而用户偏好在多数轮次中都会用到,为少数轮次牺牲全部轮次的延迟并不划算。因此长期记忆在装配阶段直接注入,只有需要精确检索历史事实的场景,才额外提供一个 recall 工具。
3 上下文装配与注入顺序
Context Builder 的输出是一串顺序固定的 message。这个顺序有明确依据:越靠前的内容越稳定,越靠后的内容越贴近当前这一轮的意图。收益是具体的:前几块跨轮不变,KV Cache 的前缀即可复用,同时降低成本和延迟。
红框表示每轮都会变化的部分,KV Cache 的可复用前缀到此为止;青框表示记忆来源;琥珀框表示一轮内可能多次追加的内容。比例为示意值,说明的是一个结论:History 和 Observation 占据主要开销,压缩应集中在这两块。
装配完成后送进模型的序列大致是这个形状:
[
{ "role": "system", "content": "<角色定义与安全边界>" },
{ "role": "system", "content": "<命中的场景策略片段>" }, // 未命中策略时不出现
{ "role": "system", "content": "<Env:当前时间 / 定位城市 / 入口来源>" },
{ "role": "system", "content": "<长期记忆:命中的事实卡片 × N>" },
{ "role": "system", "content": "<会话摘要:Rolling Summary>" }, // 历史超出预算后才出现
{ "role": "user", "content": "<第 n-2 轮用户消息>" },
{ "role": "assistant", "content": "<第 n-2 轮回答>" },
{ "role": "user", "content": "<第 n-1 轮用户消息>" },
{ "role": "assistant", "tool_calls": ["<第 n-1 轮的工具调用>"] },
{ "role": "tool", "content": "<规范化后的 Observation>" },
{ "role": "assistant", "content": "<第 n-1 轮回答>" },
{ "role": "user", "content": "<当前轮输入,含 Query Rewrite 补全>" }
]
// tools: <按当前意图裁剪后的 schema 子集>
// 输出约束与卡片协议随 tools 一起下发
前五条跨轮不变,构成 KV Cache 的可复用前缀,这也是把它们排在最前面的原因。多数模型 API 只接受一条 system 消息,实际实现时这五块会按同样的顺序拼成一条,分开写只是为了说明各块的来源和生命周期不同。
预算不足时的裁剪顺序
- 优先压缩 Observation。 工具返回的原始 JSON 字段数量很大,地图、酒店、航班类尤其明显。先按白名单筛选字段、统一单位与时区,再按相关度保留 Top-K,最后才考虑硬截断。
- History 从最早的轮次开始丢弃,但保留结构。 被丢弃的轮次进入摘要器压缩为 Rolling Summary,否则用户追问「刚才你说的那家」时系统无法回答。
- 长期记忆不做全量注入。 按置信度和相关度召回若干条命中的事实卡片即可。
- System 与卡片协议不参与裁剪。 这两块缺失会直接导致输出格式错误,端上组件无法渲染。
追问推荐(Suggestion)、会话标题(Topic)这类任务运行在小模型上,上下文窗口远小于主模型,无法直接复用主链路的 messages。做法是为每个旁路任务单独定义一份最小输入:Topic 只需要前几轮的用户话术,Suggestion 只需要最后一轮问答和已展示的卡片类型。这份输入从主上下文投影得到,不与主链路共享。
4 记忆分层与读写时机
「上下文」和「记忆」在讨论中经常混用。两者的区分很明确:上下文是这一轮送入模型的内容,记忆是跨轮、跨会话持久化的内容。记忆分三层,每层的存活周期、存储介质、读取时机和写入时机都不同。
左侧红色为读取路径,方向自下而上:长期记忆先注入,会话记忆随后。右侧青色为写入路径,方向相反,自上而下:轮内产物先落到会话层,会话内容再逐步提炼为长期事实。
这个方向不能反:模型在生成过程中不能直接写入长期记忆,不应保留任何这样的路径。否则一次幻觉就会永久污染用户画像,且事后难以定位。反映到用户侧,就是「越用越不懂我」。
| 层 | 读(注入)时机 | 写(沉淀)时机 | 失效方式 |
|---|---|---|---|
| 短期 / 轮内 | 轮内即时,Observation 随工具返回追加 | 不单独持久化,轮末随消息一起落库 | 轮结束 |
| 会话级 | 每轮装配时读窗口;窗口不足时补 Summary | 轮末同步写消息;超预算时异步触发压缩 | 会话 TTL / 新建会话 |
| 长期 / 用户级 | 会话首轮全量注入,后续按 query 命中式召回 | 轮末异步抽取,经去重与冲突消解后入库 | 用户主动删除 / 时效过期 / 置信度衰减 |
长期记忆写什么,以及怎么写
上表里「轮末异步抽取」这一步决定了记忆系统的实际质量,展开说明。
判定什么值得沉淀。 两个条件同时满足才写入:跨会话仍然成立,并且未来轮次用得上。「不吃香菜」两条都满足;「这次只看三亚」只在本次会话成立,属于会话级约束,写进长期记忆会让用户下次问别的城市时收到莫名其妙的限定。区分不了的时候倾向于不写,长期记忆的错误代价远高于漏记。
用受约束的抽取,不要让模型自由书写。 由小模型按固定 schema 输出结构化事实卡片,字段包括槽位、值、来源 turn、置信度、时效。schema 约束住输出,才能对结果做校验、去重和比对。让主模型用自然语言往记忆里追加文本,几轮之后就无法判断哪条是事实、哪条是推测。
冲突消解要有明确优先级。 同一槽位出现新旧冲突时,依次比较来源类型、更新时间、置信度:用户显式陈述优先于行为推断,新的优先于旧的,高置信度优先于低置信度。三者都相同才保留多值。
带时效的事实要能自动过期。 「下个月要去日本」是有效期很短的事实,到期后应当自动失效而不是永久留存。没有过期机制的记忆库会随使用时间不断积累噪声,而且这类噪声很难被发现。
每条事实都要能追溯和撤销。 记录来源 turn,用户说「我不是这个意思」时可以定位到具体记录并删除。这既是产品要求,也是排查记忆污染的唯一抓手。