来源:抖音 @永泽
AGI-Saber项目旨在使用promptctcx包构建System Prompt。其核心在于每次LLM推理之前,根据当前Mode编排出System Prompt前缀,拒绝简单的字符串拼接。
1. 拼接流程
Agi-Saber的拼接流程始于用户输入。用户输入后,agent会触发ContextAssembler.Assemble(Query(Mode))方法,其主要步骤如下:
- 选择Schema:根据输入的Mode(如chat, tool, rag, react),匹配适合的上下文Schema。
- 并发请求:并发调用所有ContextSource(槽位数据提供者),获取对应的上下文数据。ContextSource是独立、可探测的源,支持多种SlotKind。
- SlotFilter过滤:根据每个槽位的特定过滤器(SlotFilter)进行数据筛选。SlotFilter定义了过滤规则,包括:
Categories:分类,其中之一即可,空表示不限。RequireTags:必须全部包含的标签。MinScore:返回的综合分数阈值。TopK:单槽位最多返回项数,0表示不截断。MaxAgeHours:最大年龄(小时),0表示不限。TokenBudget:单槽位字符预算(粗略以字符数近似token)。
- Token Budget裁剪:对每个槽位的数据进行独立的Token Budget裁剪。
- 全局预算与优先级裁减:填充完所有槽位数据后,系统会进行全局预算控制,默认2400字符。通过双层预算机制,即单槽位预算和全局预算,结合优先级(SlotPriority)进行裁减,保证重要信息不会丢失。例如,安全约束的优先级最高,确保在提示词中不丢失。
- System Prompt前缀生成:将处理后的上下文数据渲染成System Prompt前缀,喂给LLM。
2. 核心概念
2.1. SlotKind六种槽位
AGI-Saber定义了六种SlotKind(槽位类型),用于存储不同类型的记忆上下文,包括:
SlotProfile:用户画像(稳定身份/偏好)SlotPlanner:任务规划状态(纲领/进度/下一步)SlotTaskMem:当前任务的步骤观察缓存SlotToolState:可用工具+近期调用记录SlotConstraints:沙箱策略/硬性安全约束SlotRecall:遗忘的语义召回(episodic / fact / general)
2.2. SlotFilter含义
SlotFilter结构体用于描述Source在填充槽位时的过滤约束。它允许对召回数据进行细粒度控制,确保只有相关且符合预算的数据被填充进上下文,前文已详细介绍其字段含义。
2.3. RuntimeContextSchema: Mode与槽位编排表
AGI-Saber支持多种Mode,每个Mode都有特定的槽位编排需求。这些Mode包括:
- chat:聊天模式,主要关注对话历史和用户画像。
- tool:工具模式,注重可用工具和工具调用状态。
- rag:检索增强生成模式,需要召回相关的知识。
- react:多步推理模式,通常需要全部的上下文信息。
当遇到未知Mode时,系统会自动回退到chat模式。
2.4. ContextSource: 槽位数据提供者
ContextSource接口定义了数据提供者如何支持多种SlotKind,并独立探测和填充数据。例如,GraphMemory同时提供了Profile和Recall数据。新的ContextSource可以注册,以便在运行时动态获取数据。
3. 装配核心流程
核心装配流程从根据Mode选择Schema开始。然后,系统会并发地填充各个槽位的数据,并对数据进行内部Token Budget裁剪。最后,通过全局预算控制,根据优先级从低到高进行裁减,确保提示词的长度适中且关键信息得以保留。
4. 渲染
裁剪后的槽位数据最终被渲染成中文System Prompt前缀。这个前缀包含了Agent的各项状态和信息,如硬性约束、任务规划、工具调用结果、用户画像和相关记忆。例如,一个渲染后的System Prompt前缀可能包含:
【硬性约束】
- [禁止] 不允许执行 rm -rf
- [告警] 网络访问需要审批
【任务规划】
- 任务 t-001 状态=running 阶段=executing
- 进度:第 2/5 步
- 下一步:调用 weather_api (工具 = http_get)
【可用工具】
- get_time - 获取当前时间
- weather_api - 查询天气 (必填 city)
- 近期调用 weather_api [成功]: {"temp":22}
【用户画像】
- 城市:北京
- 语言:中文
- 用户叫张三
【相关回忆】
-上次访问天气API (重要性 = 0.70, 综合分 = 0.82)
空白或被跳过的项不会被渲染。
5. 与“普通字符串拼接”的差异
AGI-Saber的promptctcx方法在多方面优于普通字符串拼接:
| 维度 | 普通做法 | promptctcx |
|---|---|---|
| 组织方式 | 按数据类型 | 按SlotKind(profile/planner/…) |
| Mode区分 | 一套prompt走天下 | Schema驱动,4 Mode各取所需 |
| 召回策略 | Top-K全量 | SlotFilter声明式过滤 |
| 预算控制 | 仅个总长度裁断 | 双层budget + 优先级裁剪 |
| 安全约束 | 可能被裁断丢失 | Constraints优先级0,永不丢 |
| 数据获取 | 串行 | goroutine并发 |
| 可测试性 | 拼好的字符串 | 每个source独立单测 |
6. 主流程的使用
6.1. 装配阶段 (agent启动时一次)
Agent在启动时会进行一次性初始化,注册各种ContextSource,例如ProfileSource、ConstraintsSource、RecallSource等。
6.2. 开始前注入
在每次ReAct模式开始前,会装配一次memPrefix,并冻结复用。Promptctcx内部缓冲区会在循环中实时更新任务步骤和工具调用结果。但这些更新会“延迟一轮”才出现在prompt里,这相当于跨轮次的短期工作记忆。这样可以避免上下文污染,同时确保Token的高利用率和长任务能力。
7. 总结
采用“槽位拼接”的方式组织记忆上下文,具有以下几大优势:
- 避免上下文污染:带来最大的收益,确保提示词的纯净和相关性。
- 恢复Agent状态:不仅仅是恢复聊天记录,更能准确恢复Agent的工具调用状态和任务当前所处状态。
- Token利用率更高:通过精细的预算控制和优先级裁减,优化Token的使用效率。
- 长任务能力更强:精确的上下文管理支持Agent在复杂长任务中保持一致性和连贯性。
- 让不同记忆有不同生命周期:不同类型的槽位记忆可以根据其性质设置不同的生命周期和更新频率,提升系统的灵活性和准确性。
通过以上机制,AGI-Saber能够高效、精准地管理和组织记忆上下文,从而提升Agent的整体性能和可靠性。
图例
- User Input:用户输入。
- Runtime State:运行状态,包括当前目标、活动任务、执行模式、步骤/进度。
- ContextBuilder:上下文构建器,负责目标注入、规划器片段、记忆检索、工具投影、压缩和预算控制。
- External Tools:外部工具,如浏览器、代码解释器、搜索/RAG、API/插件。
- LLM:大型语言模型。
- Planner State:规划器状态,包括目标/计划、任务列表(DAG)、状态/进度、依赖关系、重试/回顾。
- Task Memory:任务记忆,包括观察、决策、结果、总结、关键事实。
- Tool State:工具状态,包括工具上下文、环境状态、会话/句柄、凭据/Cookie。
整个系统通过ContextBuilder将各种状态和信息整合,经过LLM的处理,最终响应用户输入。