goose — 本课题摘录
读了哪几篇: 03-safety-chain(安全与权限链)、04-context-management(上下文管理)。
其余三篇(中央回复循环、工具与 MCP、巧妙之处)本轮没读。
这一家最值钱的一条:它把"持久化的真相"和"喂给模型的视图"用两个开关解耦了,而且这两个开关是每条消息各自带的。
它对本课题回答了什么
决定一最重要的一条:可见性双轨
每条消息带两个独立的可见性开关:对用户可见 / 对 agent 可见。喂给模型时只取"对 agent 可见"的。
这套双轨能玩出很多花样:
| 场景 | 可见性设置 | 效果 |
|---|---|---|
| 压缩后的原始历史 | 用户可见、模型不可见 | 用户翻得到原文,模型只看摘要 |
| 压缩摘要 / 续接说明 | 模型可见、用户不可见 | 模型靠摘要续上,界面不被噪声打扰 |
| 给模型的引导提醒、拒绝理由 | 模型可见、用户不可见 | 引导模型而不打扰用户 |
| 命令的回执 | 用户可见、模型不可见 | 用户看到确认,模型不被命令本身干扰 |
(依据:Agent 库 · goose · 上下文管理 —— 每条 Message 带两个独立可见性开关(对用户可见/对 agent 可见),喂给模型时只取 agent-visible 的;可用于「压缩后原始历史用户可见模型不可见」「压缩摘要模型可见用户不可见」等场景)
它自己的总结句:"历史里有"不等于"模型看得到"。可见性元数据把"持久化的真相"和"喂给模型的视图"解耦了——这是它上下文管理的地基。
这条是本课题"投影"这一族里最轻量的实现。 deepseek-harness 用"日志 + 可见面 + 投影"三层概念;nanobot 复制一份再修;deepagents 在调模型那一步算; goose 只在每条消息上加两个布尔。 对最小原型来说,goose 这个粒度最实际——两个字段就把大部分需求覆盖了。
而且这两个开关是独立的,不是"一个可见性等级"。四种组合都有实际用途(见上表),这就是为什么它必须是两个布尔而不是一个枚举。
决定一:三招防撑爆,而且分工明确
| 招 | 是什么 | 特点 |
|---|---|---|
| 整体压缩 | 到达窗口上限的 80% 时把历史摘要掉 | 大锤 |
| 工具对摘要 | 把一对对的「工具请求 + 工具结果」单独摘要掉 | 小锉刀,后台异步跑 |
| 可见性双轨 | 见上 | 地基 |
(依据:Agent 库 · goose · 上下文管理 —— 三招是 80% 阈值触发的整体摘要压缩、后台并行的「工具对」摘要(批大小 10)、可见性双轨;压缩分「进循环前预压」和「撞上超限错误后的恢复式压缩(最多 2 次)」)
"工具对摘要"的理由很实在:工具输出往往是上下文里最占地方的部分——一次列目录可能吐几百行。
它是在后台异步跑的:循环里派出去,不阻塞当前回合,一圈结束时再等它的结果。
"压缩放后台"这个做法别家没有。 压缩要调模型,是慢操作;放在主路径上会让每一轮都变慢。 代价:摘要落地时可能跟历史对不上,所以它加了一致性校验。
一致性校验:必须正好找到"请求 + 回复"两条匹配消息才替换,否则只告警不动——避免把对话搞成半残。
又一次撞上"调用与结果必须配对"这条不变量。 这已经是第五家强调它了。
压缩本身分两个时机:进循环前预压、以及撞上超限错误后的恢复式压缩(最多 2 次)。
决定一补充:token 怎么数 —— 用真实用量,别每次重数
| 优先级 | 做法 |
|---|---|
| 优先 | 用会话元数据里记录的真实用量——厂商在每次调用后回报的准确值,每圈累加 |
| 兜底 | 本地估算(只数"对模型可见"的消息) |
(依据:Agent 库 · goose · 上下文管理 —— 判断是否超阈值优先用 session 元数据里 provider 回报的真实 token 用量并每圈累加,没有才用本地 token_counter 估算(只数 agent-visible 的消息);用真实用量既准又省)
它的评价:用真实用量而非每次重数,既准又省。
这条要抄。 我们很容易一上来就写一个"估算 token 数"的函数, 但厂商每次调用后都回报了准确值——累加它就行,不用自己估。
还有一条判断:如果厂商自己管上下文(比如某些命令行包装),它就不插手。
决定三:工具执行前的检查器流水线
所有"执行前检查"被抽象成同一个接口:给一批工具请求,返回一批裁决。裁决只有三种——放行 / 拒绝 / 需要审批(可带警告文案)。
检查器按注册顺序跑,顺序就是优先级:
安全 ── 注入 / 危险命令的模式匹配(最先)
▼
出口 ── 数据外泄检测
▼
对抗 ── 基于模型的二次审查(需要显式开启)
▼
权限 ── 按权限模式/缓存决定
▼
重复 ── 检测工具调用陷入重复循环
(依据:Agent 库 · goose · 安全与权限链 —— ToolInspector 统一接口的裁决 只有 Allow/Deny/RequireApproval 三种,检查器按注册顺序跑——安全→出口→对抗→权限→重复;单个检查器报错不中断整条链,只记日志继续)
两条设计值得抄:
| 设计 | 说的是什么 |
|---|---|
| 单个检查器报错不中断整条链 | 一个安检器挂了不该让整个 agent 停摆,只记日志、继续 |
| 裁决合并往严了走 | 多个检查器的裁决折叠成一个结果时,取最严的 |
第一条是很好的健壮性判断:安全检查本身不该成为新的故障点。
最外层还有四种权限模式:自动批准 / 每次都问 / 只对敏感的问 / 纯聊天不执行工具。 "纯聊天"模式在循环里直接短路:工具请求被回一句"已跳过"而不执行。
它没回答什么
- 循环骨架本身——那是第 1 章,本轮没读。
- 不用原生工具调用怎么办——不在这两篇。
- 工具调用怎么解析——不在这两篇。
坑与代价
- "后台跑压缩"的代价是要处理"摘要回来时历史已经变了"。 它用一致性校验兜住,但兜住的方式是"对不上就不动"——也就是这次摘 要白做了。
- 五个检查器串行跑,每次工具调用都要过。 其中"对抗"那一档要调模型,默认是关的,可见成本不低。
- 可见性双轨要求所有产生消息的地方都记得设这两个开关。 漏设一处就会出现"用户看不到"或"模型看不到"的怪事。
判断(无锚): 这个风险可以靠默认值兜住——默认两个都为真,只有明确要藏时才改。 如果错,会错在: 如果大部分内部消息本来就该藏(比如系统提醒),那默认全可见会让界面很吵,应该反过来按类型定默认值。