上下文工程
上下文是每一步装配出的工作集
模型输入不是数据库记录的直接副本。一次执行开始时,aibuddy 分别构建系统指令与工具集合,并从原始任务消息恢复上一轮保存的压缩视图;进入 Agent 循环后,每一步再根据预算决定沿用当前消息、加载新能力,或缩减较早内容。
这条边界将“系统拥有的信息”与“模型当前需要的信息”分开。任务记录负责保存事实,运行时负责形成当前视图,模型只接收完成下一步所需的工作集。
稳定前缀与动态尾部承担不同职责
系统指令按变化频率排列。越稳定的内容越靠前,越容易变化的内容越靠后;这样既保留提示缓存的连续前缀,也让最新环境状态保持可见。
| 顺序 | 内容 | 生命周期 |
|---|---|---|
| 1 | Agent 基础指令与自身规则 | 一个任务内通常稳定 |
| 2 | 内置工具说明与场景流程 | 由本轮配置确定 |
| 3 | 记忆索引 | 同一轮内稳定,且仅在启用记忆工具时加入 |
| 4 | 日期、时区、运行环境与工作区 | 每次装配都可能变化 |
| 消息尾部 | 计划、待办、团队状态和一次性提醒 | 每一步调用前读取 |
指令与内置工具集合并行构建,随后与 MCP 工具合并。日期和工作区位于系统指令末段,轮内提醒则作为独立的尾部输入注入;二者不会迫使系统反复重写整段基础规则。
缓存只是排序依据,不是正确性约束。身份、权限、场景或环境变化时,运行时必须更新模型输入,即使因此失去缓存命中。
渐进式披露发生在不同能力边界
aibuddy 对 Skills、MCP、记忆和知识库采用同一项原则:先暴露足以判断相关性的索引,再读取执行所需的正文。但它们的加载路径并不相同。
| 能力 | 首次可见内容 | 深入加载 |
|---|---|---|
| Skill | 激活名称与加载指令 | view_skill 读取 SKILL.md,再按引用读取细则 |
| MCP | 小型工具集直接可见;大型工具集只保留搜索入口 | 原生工具搜索或 activeTools 路径激活匹配 schema |
| 记忆 | 标题与简短索引 | memory_recall 选择并读取相关记忆 |
| 知识库 | 主 Agent 只获得可委派的知识能力 | 知识子 Agent 在任务授权范围内读取文档地图、大纲与有界章节 |
| 大型工具结果 | 压缩记录与恢复提示 | view_tool_call 或工具自身的读取路径恢复原文 |
MCP 是否延迟与模型窗口大小无关:单个服务器超过 10 个工具,或所有 MCP 工具合计超过 30 个时,运行时进入延迟发现路径。支持原生工具搜索的 Anthropic 与 OpenAI 路径保持工具数组稳定;其他提供商通过 tool_search 与 activeTools 获得等价的按需激活能力。
知识库进一步隔离检索轨迹。只有任务已经附加可用文档时,知识子 Agent 才会出现;它先查看文档地图或运行 search_docs,再用 read_doc 获取大纲,最后通过 read_section 分页读取正文。主上下文接收综合后的答案与引用,不承担多步导航产生的中间内容。
渐进式披露降低常驻 token 和选择噪声,但可能增加一次发现步骤。发现失败与执行失败必须分别诊断:前者需要修正索引、搜索词或授权范围,后者才涉及参数、环境或工具实现。
轮内状态不依赖重新阅读全部历史
计划、待办、团队成员状态和临时约束不应靠模型从长消息中反复推断。RuntimeContext 用带键槽位管理这些提醒:每个来源只更新自己的值,不会覆盖其他来源,也不会在每一步持续追加旧版本。
运行时在模型调用前读取当前槽位,并把提醒放在消息尾部。以 once: 开头的一次性提醒在送达后移除;基础提醒和仍然有效的状态可以继续出现。该机制属于运行轮次的临时状态,不替代任务消息、任务状态或交付记录的持久化。
将操作状态放在尾部还有一个直接收益:更新用户偏好或执行进度时,不必改变前面的稳定系统指令。与此同时,提醒仍然只是模型输入,权限校验、任务锁和完成条件必须由运行时独立执行。
压缩生成视图,不改写原始任务记录
压缩预算先扣除系统指令、工具 schema 和预留输出空间,再从剩余窗口计算触发线。默认配置在有效窗口达到 65% 时开始缩减,目标回落到 50%;两条线之间的间隔避免每一步都重复压缩。
缩减按以下顺序进行:
- 新轮次从原始消息重建输入,并重放已保存的压缩快照与上一轮真实用量锚点。
- 大于 2,500 token 的工具结果在执行结束后后台预压缩,为后续步骤准备可复用记录。
- 达到触发线后,运行时优先缩减较早的大型工具结果,同时保护最近 5 个执行步骤。
- 若仍超过预算,再把更早的消息前缀汇总为任务上下文摘要。
- 连续两次压缩节省不足 10% 时暂停重复尝试;只有输入继续显著增长才重新放行。
压缩快照按稳定地址单独保存,原始任务消息不被覆盖。需要恢复的工具结果会保留 view_tool_call 或工具自身的读取提示;明确声明可丢弃的中间产物才只留下最小终态。压缩因此是一层可重建投影,而不是对事实记录的破坏性改写。
隔离可以比继续压缩更可靠
知识检索天然需要多次搜索、查看大纲和读取章节,因此 aibuddy 把这段轨迹放入知识子 Agent。通用子 Agent 也使用独立上下文执行边界清楚的研究或操作,只把结论、状态和交付物带回主任务。
隔离并非免费的压缩替代:委派目标必须自包含,返回结果也可能遗漏中间证据。它适合能明确验收的分支;强依赖主轨迹细节的工作仍应留在当前上下文中。
从故障现象定位装配阶段
| 现象 | 优先检查 |
|---|---|
| 附件、记忆或知识没有进入回答 | 授权范围、能力是否启用、索引与发现结果 |
| Agent 选择了错误工具 | 常驻工具规模、MCP 延迟阈值、工具描述 |
| 计划被误写成已经完成 | 动态状态来源与压缩摘要 |
| 原始数值或标识符丢失 | 工具压缩器、恢复指针与摘要覆盖范围 |
| 每一步成本持续升高 | 稳定前缀变化、压缩门槛与工具 schema 开销 |
| 压缩反复触发但收益很低 | 无效压缩计数与新增内容规模 |
排查应沿“选择 → 装配 → 预算 → 恢复”推进。继续增加系统提示词通常只会扩大输入,并不能修复缺失的授权、错误的索引或不可恢复的工具结果。
实现锚点
| 机制 | 主要实现 |
|---|---|
| 系统指令顺序 | instructions-builder.ts 的 instructionsBuilder.build |
| 工具与指令装配 | agent-loop.ts 的 buildAgentInputs |
| 带键动态提醒 | context.ts 的 RuntimeContext.reminders |
| Skill 激活 | inject-user-skills.ts 与 view_skill |
| MCP 延迟发现 | agent-setup.ts、tool-defer.ts 与 tool-discovery.ts |
| 压缩预算与阶梯 | budget.ts、compact.ts 与 compaction-state.ts |
| 工具结果恢复 | tool-compactor.ts 与 view_tool_call |
| 知识渐进读取 | knowledge-subagent.ts、read_doc、search_docs 与 read_section |