本教程将系统解析 Tracker(追踪器) 在 AI Agent 架构中的关键作用,帮助你搭建一个稳定、可恢复的业务任务状态管理中心。无需深奥理论,从真实业务场景切入,带你彻底掌握 Tracker 如何充当 Agent 的运行时状态中枢。
记忆不止有 Memory 和 RAG
我最初研究 Agent 记忆机制时,也有一个常见的误解:
- 是不是引入一个
Memory模块,把历史对话缓存起来,Agent 就算具备记忆能力了? - 后来接触 RAG,又进一步认为:将历史聊天、用户画像、业务知识全部存入向量库,需要时检索出来,不就够用了吗?
直到我按照客服 Agent 的业务流程逐层拆解,才发现实际情况远非如此简单。
Memory 更像是“长期记忆”。RAG 更像是“查阅资料”。而 Tracker 解决的是另一个核心问题:当前任务执行到了哪一步。
因此,本文真正想探讨的不是“如何让模型记住更多对话内容”。而是:当 Agent 开始承载真实业务任务时,系统中是否有一个专门的模块,用来精准记录任务的执行进度?
这正是 DialogueStateTracker 的价值所在:它并非让模型变得更聪明,而是确保系统自身不遗漏任何关键状态。我更倾向于将其理解为:
Agent 的运行时状态管理中心。
这张图是本文的核心示意:LLM 并非状态中心,Tracker 才是。LLM 专注于理解与生成,业务操作交给 Tool,而全部状态流转则统一由 Tracker 管理。
一次客服请求背后,是一连串状态变迁
以“查询订单”为例。
用户:我要查订单
Agent:请提供订单号
用户:123456
Agent:您的订单已发货
但在系统内部,实际发生的是:
sequenceDiagram
participant User as 用户
participant Agent as Agent Runtime
participant Tracker as Tracker
participant LLM as LLM
participant Tool as 订单系统
User->>Agent: 我要查订单
Agent->>Tracker: 记录用户消息
Agent->>LLM: 判断意图
LLM-->>Agent: query_order
Agent->>Tracker: 开启查订单流程
Agent-->>User: 请提供订单号
User->>Agent: 123456
Agent->>Tracker: 记录订单号
Agent->>Tool: 查询订单
Tool-->>Agent: 已发货
Agent->>Tracker: 写入订单状态
Agent-->>User: 您的订单已发货
在这张流程图中,LLM 只承担理解与表达的任务。真正驱动系统有序运作的,是 Tracker 在一旁持续记录:
当前意图是什么
当前流程是什么
哪些信息已经收集
哪个工具已经调用
工具结果是什么
下一步应该做什么
缺少这些状态信息,Agent 将退化成一个“每次对话都像初次见面”的聊天窗口。
Tracker 具体存储哪些内容?
我现在通常把 Tracker 拆解为五大类信息。
| Tracker 里的东西 | 白话解释 | 客服例子 |
|---|---|---|
| 对话历史 | 用户和 Agent 说过什么 | “我要查订单”“请提供订单号” |
| 槽位 Slots | 业务字段 | order_id=123456 |
| 流程 Flow | 任务走到哪一步 | 正在查订单 / 正在退款 |
| 最近动作 | 上一步执行了什么 | 刚调用订单查询 |
| 序列化状态 | 能不能保存和恢复 | 服务重启后继续处理 |
Tracker 不像“记忆能力”,更像一个具备生命周期的对话上下文容器。
Controller、Service、Action、LLM 这些模块不应该各自维护一份状态。大家都统一读写同一个 Tracker 实例。
这就是 统一状态源 的核心价值:避免每个模块独立记录状态,否则业务一复杂就会产生数据冲突。
小提示:Tracker 并非把所有历史一股脑塞给 LLM。它可以保存完整的状态快照,但实际喂给模型的,通常只是部分对话记录和关键业务信息。其余槽位、工具结果和流程状态,更多是用于程序自身判断下一步动作。
Slot(槽位):将聊天中的关键信息转化为业务字段
客服 Agent 最怕一种情况:用户明明提供了订单号,系统后续却无法有效利用。
原因很简单:自然语言文本本身不是结构化业务字段。
用户说的是:
我的订单号是 123456
系统真正需要的是:
order_id = 123456
这就是 Slot(槽位) 的用途。
Slot 有点类似于表单字段。退款需要订单号,修改地址需要新地址,贷款需要金额,转接人工需要问题类型。这些信息都应该从对话中提取并沉淀为结构化字段。
但这里有一条重要的边界需要明确:
| 信息来源 | 谁来写入 | 例子 |
|---|---|---|
| 用户说出来的信息 | LLM 可以提取 | 订单号、地址、退款原因 |
| 系统查出来的信息 | Action 写入 | 订单状态、退款结果、会员等级 |
这个职责不能混淆。LLM 可以帮助理解“用户表达了什么”,但不能让它凭推测决定“订单是否已发货”。订单状态必须来源于真实的订单系统。
这张图的核心在于职责边界:理解归 LLM,事实归业务系统。
到这里,Tracker 已经远不止是“聊天记录”了。它开始演变为当前任务状态的核心载体。
Tracker 和 Memory 本质不同
前面已经梳理了 Memory、RAG 与 Tracker 的区别,下面再从生命周期角度进一步对比。
| 模块 | 解决的问题 | 例子 |
|---|---|---|
| Tracker | 当前这件事做到哪了 | 正在退款,已收集订单号 |
| Memory | 这个用户长期是什么样 | 喜欢中文、偏好某品牌 |
| Knowledge Base | 企业知道什么 | 退款规则、商品说明 |
这三类组件的生命周期差异显著:
- Tracker 中记录的“当前退款进度到确认金额”可能在 10 分钟后就不再有用。
- Memory 里存储的“用户偏好中文回复”可能在几个月后依然有效。
- Knowledge Base 中的“退款政策”属于企业级公共知识,不应混入某个用户的会话上下文。
边界如果划分不清,Agent 项目会越做越混乱。
小提示:这张对比图我会贴在所有 Agent 状态设计文档的最前面:生命周期不同,存储位置就不该一样。
Tracker 解决的三个生产环境痛点
第一个:上下文丢失
用户说完订单号后,Agent 需要清楚这个数字归属于刚才发起的“查订单任务”。这并非依赖模型的推测能力,而是靠 Tracker 精确记录:
active_flow = query_order
order_id = 123456
第二个:任务无法恢复
用户退款操作进行到一半,页面意外关闭。下次重新进入时,系统需要准确知道:
退款流程已经走到确认金额
订单号已经收集
身份已经验证
如果 Tracker 支持序列化,就可以将状态保存到 Redis 或数据库中,后续随时恢复。
sequenceDiagram
participant Agent as Agent
participant Tracker as Tracker
participant Storage as 状态存储
Agent->>Tracker: 更新退款流程状态
Tracker->>Storage: 保存状态快照
Note over Agent: 服务重启 / 用户离开
Agent->>Storage: 读取状态快照
Storage-->>Tracker: 恢复 Tracker
Agent-->>Agent: 从上次步骤继续
第三个:多模块状态不一致
没有统一 Tracker 时,很容易出现以下混乱:
LLM 以为在退款
Action 以为在查订单
前端显示人工接管
数据库里还是旧状态
这就是 状态中心 的核心价值。并非为了“架构好看”,而是为了杜绝业务系统中各模块各说各话。
小提示:一个系统越像“能真正办事的人”,就越需要清晰的状态管理中心。
常见问题
Q1:Tracker 和 Memory 可以混用吗?
不可以。它们的生命周期完全不同。Tracker 是会话级别的,随着会话结束而失效;Memory 是用户级别的,可以跨会话持久化。如果混用,会导致状态混乱,例如把临时任务状态误当作长期偏好来使用。
Q2:如何序列化 Tracker 状态?
推荐使用 JSON 序列化,将 Tracker 中的对话历史、槽位、流程、最近动作等结构化数据转换为字符串,存储在 Redis 或数据库中。恢复时反序列化即可。建议在每次关键状态变更后(如工具调用完成、槽位更新)执行一次快照保存。
Q3:Tracker 中存储的对话历史会占用大量内存吗?
会占用一定内存。但通常一个会话的对话历史在几百条以内,每条消息几十到几百字节,总内存占用是可控的。如果担心内存压力,可以设置最大历史长度(如保留最近 50 轮对话),或定期清理历史记录,只保留必要的结构化状态。
Q4:槽位(Slots)和 LLM 提取的信息如何避免冲突?
遵循“理解归 LLM,事实归业务系统”的原则。LLM 提取的槽位仅用于填充用户输入的信息,而业务系统(如订单系统)返回的数据由 Action 直接写入。如果两者冲突,以业务系统为准。例如,LLM 提取的订单号为“123456”,但订单系统返回该订单不存在,则应覆盖槽位状态。
这次学习对我最大的提醒是:审视 Agent 架构时,不要只关注“模型能否理解”,更要追问“系统是否有地方精确保存当前任务状态”。如果这个问题没有想清楚,后续接入再多的 Memory、RAG、Tool,都会让系统越来越混乱。
