Skip to content

Latest commit

 

History

History
402 lines (315 loc) · 29.7 KB

harness.md

File metadata and controls

402 lines (315 loc) · 29.7 KB

Harness:有界、可观测的 Agent 执行层

1. 理念 / 概念

Harness 只解决一件事:让一次 agent execution 在明确输入、能力、预算和完成契约下可靠运行。 它不理解 Unit、Hypothesis 或 Finding,也不决定某条结论是否值得发布。

ExecutionSpec ──▶ Execution.Run ──▶ ExecutionResult
                      │
                      ├─ ContextManager
                      ├─ Tool / Hook adapters
                      ├─ Recorder
                      └─ AgentGo loop
                              │
                              └─ events + session JSONL
对象 责任
ExecutionSpec 一次执行的输入消息、工具、hook、预算和 scope
Execution 单次运行的聚合根,持有 ContextManager、Recorder、完成状态与 AgentGo loop 生命周期
ContextManager 注入、去重、淘汰、压缩和投影上下文
Tool Registry 把工具定义、provider 与执行身份绑定
Hook / Event 提供领域扩展点和稳定观测事件
ExecutionResult 返回本次运行的完成状态、usage、工具统计和错误
LoopSession 持有跨 Execution 的 AgentGo 状态和原生 Session,由调用方控制生命周期
Session 以稳定 JSONL 持久化实际发生的执行事实

调用方可以用 biz_id 给整次 CCR 执行附加不透明业务身份。Harness 只在 session_start 持久化, Viewer 只展示;它不解释格式、不改变评审行为,也不进入模型 prompt。需求内容仍通过 background 提供。

Runner 可以用 Harness 执行 Unit Review 或 Hypothesis Review;Harness 不反向 import Runner、Unit、 Assessment 等评审领域对象。领域语义通过消息、工具、hook 和 event 适配进入。

2. 流程

2.1 启动 Execution

调用方组装 ExecutionSpec,其中 Messages 是领域消息而不是提前摊平的 provider 文本;它同时明确 本次执行允许看到和做到什么,再通过稳定边界启动:

execution, err := harness.NewExecution(spec)
result, err := execution.Run(ctx)

Execution 在构造时接管输入快照并组装 AgentGo model、tool 和 context 契约。它是单次使用的 运行实体;一次 Execution 的 Recorder、ContextManager、完成状态和其它运行事实不能被另一个 scope 复用。需要延续 conversation 时,调用方将同一个 LoopSession 交给每次 ExecutionSpec,每次仅追加 新任务的消息。LoopSession 持有 AgentGo 的 messages、artifacts 和运行状态;模型、工具和业务完成条件 由本次 Execution 重新绑定。状态保持在 Harness 边界内,调用方独占使用,并在最后一次执行结束后关闭。

Review 2 Lane 持有 LoopSession,Worker 每次执行一个 Hypothesis 后释放调度容量。LoopSession 无需常驻 Agent 或执行协程。每个新 Hypothesis 使用 AgentLoop 开始新运行,重置轮次与错误计数;同一任务的收卷 接续才使用 AgentLoopContinue 保留已消耗的运行进度。

2.2 每轮模型与工具循环

每轮按以下顺序推进:

  1. ContextManager 根据当前状态生成模型可见消息;
  2. recorder 记录实际发送给模型的 request;
  3. model adapter 调用模型并记录原始 response、usage 和 stop reason;
  4. tool call 经 Registry 找到 provider,经 hook 校验和执行;
  5. tool result 进入 transcript,同时发出稳定事件;
  6. completion contract 判断是否结束、强制收敛或继续下一轮。

预算耗尽、deadline、模型错误和缺少终态动作必须形成不同 completion 状态。调用方需要知道“没有 finding”究竟是审完了,还是执行没完成。

2.3 返回结果

Execution 结束后返回结构化 ExecutionResult,不直接生成 ReviewResult。Runner 将其解释成 Hypothesis、 Assessment 或 warning;任何领域后处理都发生在 Harness 外。

3. 关键设计

3.1 Typed message 是内部语义,wire message 是边界投影

CCR 的 domain message 直接实现 AgentGo AgentMessage,保留文件、来源、范围、优先级和可重取性等语义。 AgentMessage 是 Harness 唯一的消息生命周期契约:普通 prompt 使用 agentgo.Message; 可识别的工具结果(例如 read_files)会重新提升为 File / FileBatch,因此后续压缩始终从 typed message 视角出发。调用模型前通过 ToMessage() 投影成 AgentGo 原生消息,provider 协议序列化由 AgentGo/litellm 负责:

agentgo.Message / msg.File / msg.Diff / msg.SearchResult
   ── context lifecycle / ToMessage ──▶ agentgo.Message ── AgentGo/litellm ──▶ provider API

Harness 外始终传入保留完整事实的消息。Compact(expect) 中的 ratio 只表达预算目标,不表达统一压缩档位; 每个 domain message 根据自己的信息结构精细决定内容取舍并返回实际比例。例如 File 在 source、outline、path 中选择,Diff 在完整 hunk、anchor、path 中选择,Search 还会单独保留无命中反证。 ContextManager 只把 expect 交给消息,不理解任何消息的内部形态;每个 domain message 的 ToMessage 只渲染自身已经选定的当前投影。 可识别消息同时按当前投影暴露 AgentGo ContextItem,所以轨迹看到的是模型实际收到的 source / outline / reference,而不是消息未经压缩时的原始档位。 Harness 接收的始终是完整消息;只有 ContextManager 判断需要压缩时才产生投影副本。 每个 domain message 的 Raw() 返回独立全本视图,不会清除或修改当前投影;AgentGo 负责 raw/current 投影生命周期。 文件内容不是“碰巧放在一段字符串里的文本”。保留类型后, ContextManager 才能判断两个范围是否 重叠、后一次完整读取是否覆盖前一次局部读取,以及 token 压力下哪些内容可以先淘汰后按需重取。

工具结果通过其前一条 assistant tool call 的 call ID 找回工具名和参数,再由统一 FromLLM 入口归入 少量语义类型,而不是“一种工具一个 class”。每种双向消息把 FromLLM 与 ToMessage 放在同一处, 修改 wire contract 或压缩投影时可以同时核对两个方向:

  • read_files / read_base_files → FileBatch(内部保留各个 File),current 与 baseline snapshot 参与身份,不能跨版本去重;一次 tool call 仍只对应一条 tool result;
  • read_diffs → Diff,压缩时保留 path 与 hunk anchor;
  • search_code / file_find → SearchResult,保留 query、命中位置或无命中反证;search_code 显式请求并成功展开的 symbol source 还作为可见 file range 参与复用判断;
  • FileContext → 初始 outline / reference 导航目录;source 只由独立 File 消息表达并参与覆盖判断;
  • 结果提交、终态和可恢复错误 → ToolReceipt,领域 artifact 仍只由 Runner collector 持有。

普通模型消息直接使用 AgentGo Message。初始任务、试验性的 Board 等只由 CCR 内部创建的单向消息没有可恢复的 wire 来源,因此只定义 ToMessage。

降低边界应保持可解释的顺序和一对一关系,避免 adapter 再暗中合并消息。Session 记录的是最终实际 发送的 wire shape,因此 Viewer 能回答“模型当时究竟看到了什么”。

read_files 只有批量 reads[] 契约,单文件也使用一个元素的数组;已经确定且彼此独立的范围由 provider 并行读取,并按请求顺序装回同一条结果。ContextManager 可以只剔除其中已覆盖的成员,执行 剩余成员后再按原顺序合并,因此批量不会削弱范围复用,也不需要保留另一套单文件入口。

进入模型上下文的单条工具结果最多 32 KiB(UTF-8 字节)。超限结果显式标注原始大小与 缩小查询/读取范围的建议,并以 ToolView 保留 call ID 与原始 AgentMessage;Raw() 仍返回完整工具结果。 截断视图不声明完整文件覆盖,后续读取仍可补齐源码。此上限作用于 Transform,不裁剪 Runner 收集的领域结果。 Session 的工具记录保留 provider 返回值,模型请求记录反映实际进入上下文的有界投影。

search_code 在 provider 内先做批次合并:每条 query 保留顺序、文件、命中行号、空结果和错误; 共享源码区按文件与行号去重,重叠的命中行、附近上下文、symbol source 只输出一次。 每行最多 1 KiB,整个批次最多 32 KiB;一半预算预留给各 query 的独立摘要,其余供共享源码使用, 超出时明确标记裁剪。原有 200 条命中和 400 行上下文的采集限制仍适用。 只有正文逐行完整保留的 symbol range 才能参与后续读取去重;压缩到摘要后共享正文与 range receipt 一并移除。旧 Session 中没有共享源码区的批次仍可解码。

Artifact 是调用者在 Loop 中维护、利用的具名数据对象。其内容、可变性、消息关联和用途由调用者定义, 可以用于 prompt 投影,也可以用于工具、流程或结果处理。AgentGo 管理容器及 ID 唯一性。 CCR 当前用它登记源码、diff 和 Clue 正文;这些不可变、内容寻址的类型是 CCR 的具体策略。 Harness 从初始消息生成 InitialState.Artifacts,工具 Middleware 在最终结果入历史前登记新增材料,Turn Hook 登记追加输入。 SourceArtifact 保存完整源码行,其身份包含 snapshot/ref、path 与正文;重叠范围和不同内容各自保留, 并行工具无需覆盖共享的文件记录。材料字段支持 codec;Session 仍记录实际 prompt 与工具事实。

TransformContext 向投影提供本次 Loop 的材料能力。登记表示拥有材料,可见覆盖仍需在每次请求中 从实际正文重新计算;压缩后的引用不能仅凭库存宣称源码已展示。投影保留 Raw、错误结果和工具配对。 LoopSession 保存终态中的材料值;每次 Loop 创建独立 Manager,不向 Runner 暴露 Manager。

Review 1 的 ClueDoc 由 Runner 拆为独立 ClueMessage 消息,不再把正文嵌入固定 Instruction。 ClueMessage 持有原始 Clue(包括 ClueKind、来源与关系),由 Runner 决定各类型的保留策略。 Harness 通过 MaterialMessage 接口登记和投影材料,不依赖 Unit 类型。当前仅 doc 类型启用去重和引用压缩。 ClueArtifact 按 kind 与完整正文标识可复用内容;Snapshot、Ref、Relation 属于各条 ClueMessage。 完全相同的 before/after 正文可以复用一次展开,版本归属仍在消息上分别表达。文档可独立压缩为来源引用; 早先正文被压缩或移除后,后续请求重新展开仍保留的完整文档消息。

3.2 上下文生命周期统一在 ContextManager

上下文不是只增不减的聊天数组。Harness 统一处理:

  • 注入:system/task、静态源码消息、跨 turn provider 输出;
  • 去重:按版本、文件、行号与实际正文复用已有证据,只展示重叠读取的新增部分;
  • 复用:当 read_files 请求范围仍完整可见时返回轻量提示,不再次执行相同读取;
  • 淘汰:优先移除可重取、低价值的大块内容;
  • 压缩:只在轻量手段不足时进行有损总结;
  • 投影:临近调用时降成模型可见消息。

ContextManager.Transform 与 dry-run 调用同一个纯投影函数,输入消息与 Artifact 值,输出请求视图。 ContextManager.Transform 每次从当前 AgentMessage 基线生成请求视图,批次与历史使用同一套 覆盖判断,raw 不变。临时引用不会提交到历史。Compact 按基线判断预算,Compactor 接收消息与材料准备态;AgentGo 接受压缩结果后, 最终请求只执行一次 Transform,保证被引用正文被压缩后可以恢复展示。普通重试不重复压缩,overflow 单独触发恢复。Sync 只同步已接受的基线,工具读取去重保留上一模型请求实际可见的覆盖,避免原始历史 重新声明已被移出的源码可见。预算趋紧时调用 compactor.ZoneCompactor。接入层 context_compactor.go 负责注入执行配置、模型和摘要 prompt;分区与压缩策略位于 compactor 包。

分区按消息职责和完整轮次计算,不在消息上维护可变 Zone:

  • Fixed 保留 system 指令、当前任务消息的必要投影和该任务声明的必要材料(如目标 diff),源码使用独立消息承载。Hypothesis 的固定投影 保留待判断的主张和约束,原始内容仍可通过 Raw 追溯。
  • Active 保留最近的完整 assistant/tool 轮次,至少保护最新轮次,再按 token 预算向前扩展。 首次调用前的独立源码不属于工具轮次,允许选择 outline/reference 表示。
  • History 承载其余内容,先调用消息原生 Compact,再尝试工具结果裁剪、长文本裁剪和摘要。 同一历史段内先压低 Priority,同级先压旧内容;Priority 表达重要性,不替代活跃轮次保护。

历史预算扣除 Fixed 与 Active 的实际占用。当前任务锚点保留原来的时间位置,摘要不跨越它; 持续执行进入下一任务后,旧任务锚点进入历史区。 MessageCompactor、ToolResultCompactor、LightTrimCompactor、SummaryCompactor 均由 CCR 在 compactor 包实现,只处理传入的历史段,不再各自保留最近消息。MessageCompactor 同级先旧后新, 不向 AgentGo 添加排序配置;摘要继续使用 AgentGo 的模型执行与 ContextSummary 契约。结果满足预算和工具配对约束后才交给 Engine 提交,保护区自身 超限时明确报告预算不足;摘要失败不提交半成品。显式溢出恢复同样受保护区和窗口预算约束。

context.zones timeline stage 记录每区消息数、估算 token、执行策略、目标与结果,包括未达标的尝试; 整体提交结果仍通过 AgentGo 的 context_compacted 事件记录。这里的 token 是上下文估算,实际调用 成本继续以模型 usage 为准。

上下文占用由 AgentGo 按实际消息投影估算,包含 tool call 的名称和参数。有效的 API input usage 可校准此前的输入,但本轮 assistant 输出与后续工具结果仍需计入下一次请求。去重或压缩改写消息后, 旧 input usage 不再对应当前 prompt,直到下一次真实响应前使用估算;原始 usage 留在执行记录中用于成本统计。

3.3 预算是机制,完成策略属于调用方

Harness 提供 token、tool round、deadline 等预算机制,并通过 AgentGo BeforeTurn 在模型调用前处理 增量上下文与 wrap-up。调用方可指定独立的探索截止时间,到点关闭新调查调用而不取消执行 context; 探索中的每次模型请求(含上下文摘要)通过子 context 继承请求开始时的探索 deadline; 更短的调用方硬期限优先。动态期限更新影响后续请求,不追溯修改在途请求。仅当探索子请求到期、 调用方 context 仍有效时,Harness 使用 AgentGo 已提交的状态续跑一次收卷;保留已消耗的轮数、 上下文和已接受的结果,不重新获得预算。两次 Loop 使用不同的 timeline 请求命名空间, 同一个 Harness Execution 只发布一次终态。普通 provider 超时、用户取消和硬截止不触发该恢复。 已进入收卷的请求沿用调用方的取消信号与硬期限。 调用方可以提供固定或动态的探索截止时间;显式探索边界拥有 收卷余量分配,未指定时才使用通用 deadline 预留。调查轮次到达、探索时间到达、外层 deadline 余量不足、或剩余累计 token 不够“再调查一轮 + 收卷”时进入收卷。模式提示作为不可压缩消息追加到末尾,工具定义和 tool choice 保持稳定, 避免模式切换破坏可复用的请求前缀。首次收卷允许调用方声明的结果提交工具及 completion tool; 唯一一次纠正只允许 completion tool,natural completion 则要求自然结束。Tool middleware 拦截 越界调用,StopGuard 检查完成契约;仍未完成则以 truncated 结束。调用方定义终态动作和收敛语义, 例如 Unit Review 可在收卷后只允许提交 Hypothesis,Hypothesis Review 可要求每个输入都有 Assessment。

Harness 不能把 task_done 统一解释为领域完成;它只执行调用方给出的 completion contract。需要 完整结构化结果的流程可以要求 terminal tool;允许“检查完即结束”的流程可以选择 natural completion, 因此简单 Review 1 不必为了形式上的交卷继续等待或耗尽预算。超时或轮次耗尽时返回 partial/incomplete,不把空输出包装成成功;此前已被领域层接受的增量结果不随 Execution 的失败回滚。

模板的 MAX_TOKENS 表示输入上下文预算,MAX_COMPLETION_TOKENS 表示单次模型输出上限。 主循环及 Plan、摘要、Review 2、重定位等辅助请求使用输出上限,不把上下文容量当作响应容量。 默认 review 上下文为 200000 token,scan 为 58888,输出上限均为 16384;CLI 的 --max-completion-tokens 可覆盖输出上限。未声明输出字段的自定义模板沿用其原 MAX_TOKENS。

模型返回的 reasoning 与可见正文分别保存。后续 assistant 工具调用消息保留 provider 的 reasoning 字段,避免工具结果回传后丢失协议要求的思考内容;Session 按实际返回值记录这两部分。

整轮累计 token 预算与单次上下文窗口分别控制成本和容量。Review / Scan 的所有模型入口共享同一份 累计预算,包括 Plan、主循环、Review 2、压缩、重定位和汇总等辅助调用。每次调用返回后按 provider 报告的 input + output usage 累计,达到预算后拒绝新调用;派发器在获得并发槽位后再检查预算,避免 等待期间沿用旧额度。Scan 同时保留启动新文件前的成本预估。

收卷估算复用上下文估算器:以最近报告的输入 usage 加后续消息估计输入,已提交的 Compact 会更新 基线;输出参考近期调用并保留最低余量,收卷同时计入上下文输入和输出。判断在 BeforeTurn, 本轮压缩、未来工具输出和其它并发执行的消耗仍可能改变余额。Session 的 wrap_up artifact 记录 触发原因、轮次、剩余时间、剩余 token 及两部分估算,便于验证收卷是否及时。

这是软预算:已放行的并发请求及其 provider 内部重试可以完成,未报告的 usage 无法精确计费。 预算耗尽不额外购买收卷轮次;未完成的 Execution 以带原因的 truncated 结束,已接受的结果保留, 尚未派发的 Review Unit 记为 skipped_policy。局部预算拒绝不计为 provider 失败。

3.4 Tool 与 Hook 是执行能力,不是领域所有权

工具定义、参数解析、provider 调用和通用 telemetry 位于 Harness。某个工具是否在一个阶段可见、 调用后产生何种领域 artifact,由 Runner 的 execution spec / hook 决定。

这允许同一个 read_files 被多个流程复用,也允许 Review 2 只暴露只读证据工具而不暴露发布 Finding 的能力。Runner 适配可以依赖 Harness,Harness 不依赖 Runner。

模型可见的 tool argument 是行动语言,不是面向确定性调用方的通用 API。Harness 可以持续增加解析、 投影、预算、降级和观测能力,但只有同时满足以下条件的选择才进入 tool schema:它表达模型要完成的 最小行动意图;模型从当前上下文拥有充分信息;不同取值代表真实语义差异;Provider 又无法安全、 确定性地选择默认值。输出行数、展开策略、候选上限、预算和 timeout 等执行参数由 Provider、Config 或 gate 持有,不能为了暴露能力而转嫁成模型每次调用的生成负担。观测需要的标签优先从实际请求、 结果和 Session event 推导,不要求模型替 telemetry 填字段。

因此能力面与模型决策面独立演进:能力可以丰富,模型参数应保持最小。实验 gate 应尽量只改变 Provider 执行策略,使开关两臂共享相同 prompt 和 tool schema,避免把模型是否会选择新参数混入 能力效果。

3.5 AgentGo 定义统一运行时消息协议

AgentGo 负责模型循环、AgentMessage 和通用上下文机制。CCR 的 Runner 可直接组装 []agentgo.AgentMessage,其中普通 prompt 使用 agentgo.Message,文件、diff、search 等由 CCR domain message 实现同一接口。Harness 把 tool、hook 和事件收敛为稳定 ExecutionResult。

AgentGo 的运行时消息协议可以出现在 Runner 的 execution assembly,但不进入 Unit/Finding 等持久领域 模型,也不要求 Session Viewer 理解具体 domain message。

4. 可观测性:Session JSONL 与 HTML Viewer

一次昂贵或异常的 review 必须能回答三类问题:整体花费在哪里、每个 loop 如何演进、最终决策为何 产生。仅打印终端摘要不够,Harness 因而把实际执行持续写入 Session JSONL。

4.1 Framework Session 与业务 Operation

一个独立 loop 使用一个原生 AgentGo Session;同一 Lane 的连续 Execution 复用 Session,每次 loop 调用使用新的 Run。Session 记录模型请求/响应、工具请求/结果、消息提交和上下文变化,其 envelope 不包含 CCR 的业务 scope。CCR 领域消息只通过 codec 扩展内容表示,原始证据和压缩视图都保留。 Harness 在每次 Execution 的终态边界保存一次 AgentGo checkpoint;内存状态直接用于后续任务,文件中的 checkpoint 可由 AgentGo 加载。checkpoint 不包含模型、工具闭包或业务完成条件,也不按 turn 或定时追加。 局部状态持久化不提供恢复中断的整个 Review 的能力。

Review 持有业务 Operation journal,覆盖构图、Unit/Lane 排队、Review 1/2、Trial 和非 loop 动作。 Execution Operation 保存业务 outcome;模型与工具 Operation 通过 AgentGo Session.Ref 的四元组关联正文。 Harness 只将原生事件的源头时间投影为阶段,不重复存储 prompt/response/tool body。 Operation 状态同步追加到 Review JSONL,与业务产物共用存储;不再持有 Timeline Manager 或独立 .timeline 文件。

internal/recording.Review 只保留配置、业务 artifacts/findings、scope 计数与录制生命周期。 查询时按 ID 连接 Operation 和局部 Session,提供 Viewer、stats、export、eval 需要的内容视图。 同名 tool call 在不同 Session/Run 内互不冲突,缺失记录保持 incomplete,不补造成功或零成本。 一个 Unit 的业务过程投影为 agent-ledger/observation.JobView;持续式 Lane Session 只贡献属于该 Unit 的调用。 详见整轮时间线。

4.2 HTML Viewer 是诊断投影

Viewer 从 Timeline 列表进入一次 Review,以共享时钟的泳道展示 Unit、R1/R2 loop 与 Trial。 模型调用以一个 Stage 表达,点击弹框查看 Session 中的真实 Prompt、Response、token/cache 用量、 已记录的金额 cost,以及 Stage 状态与耗时。工具调用在弹框中展示实际 input/output。 调用正文按完整 Session reference 关联;原始 JSON 与记录身份默认折叠,不再另设 Scope/Execution 页面。 网络阶段仅用于失败诊断,不成为业务 Timeline 的子阶段。缺少记录或 usage 必须明确显示未知, 不能把缺失视为零成本,也不能把 0 Finding 当作完整评审的证明。

4.3 展示层不反向定义协议

JSONL schema 是执行与诊断之间的稳定协议;HTML 只是其中一种投影。新增图表或页面不应迫使 recorder 依赖模板结构,Viewer 也不应回写 session 或修改 Trial 结果。若需要新的诊断能力,先定义稳定事件或 artifact,再让 CLI、eval 和 HTML 分别消费。Viewer 只消费当前 schema,不为历史记录维护字段猜测或 完成状态 fallback;需要保留的历史数据应由离线迁移转成当前协议。

4.4 隐私与体积边界

Session 可能包含源码、prompt 和工具结果,应默认按本地敏感数据处理,不自动上传到外部目的地。 体积治理应依靠事件语义、可配置保留和离线汇总,不能为了缩小文件而漏记“模型实际看到了什么”。

5. 验证 Harness 的方式

Harness 变更至少验证:

  • 相同 typed input 降成稳定、可解释的 wire messages;
  • 工具结果与触发它的 model request 正确关联;
  • budget/deadline/terminal action 产生正确 completion 状态;
  • recorder 能在错误和 partial execution 下写出可读取 JSONL;
  • Viewer 的 overview 与 timeline 均由同一批事件推导,不出现统计和逐轮记录矛盾;
  • Harness 包保持不依赖 Runner、Unit 和评审领域。

References

模型截断与推理控制

Harness 使用 AgentGo 的 EndReasonLength:当 length 恢复禁用或耗尽时,Execution 输出 truncated / length,保留部分响应和已报告用量,不归类为正常完成或网络错误。 ExecutionSpec.MaxLengthRecoveries 为 nil 时沿用 AgentGo 默认 3 次,显式 0 禁用;恢复仍受 原 deadline、MaxTurns 和 token budget 约束。Plan、重定位、scan 汇总/去重等单次任务使用 CompleteText,拒绝 length / max_tokens 及只有 reasoning 的响应,不把半份正文当完整结果。 上下文摘要被截断时也不提交为完整 checkpoint。

任务模板的 reasoning_effort 可分别用于 MAIN_TASK、PLAN_TASK、HYPOTHESIS_REVIEW_TASK、 RE_LOCATION_TASK 及 scan 的 PROJECT_SUMMARY_TASK / DEDUP_TASK。例如为 PLAN_TASK 增加 "reasoning_effort": "low",即可覆盖该次调用的 provider 默认;空值或 auto 保留默认。 既有模板不自动降档。其他 effort 值原样传给目标模型,不把 minimal/medium/xhigh/max 压成 high/low。 模型支持的值与别名映射由服务端校验。上下文压缩继续沿用原有的显式 off 策略,现在该设置会真正传到模型请求。

off / none 表示关闭思考。OpenAI Chat Completions 使用 reasoning_effort,同时覆盖该 endpoint 已配置的 thinking / enable_thinking 扩展;Anthropic 使用 thinking 与 output_config.effort。 覆盖只影响单次调用,不修改共享默认配置;不会按模型名猜测私有档位映射。

agentgo.Usage.Reasoning 与记录中的 reasoning_tokens 是 completion tokens 的分项, 不另计总 token 或费用;0 也可能表示供应商未报告。AgentGo Session 保留调用意图、截断恢复决定、 stop reason 和 reasoning 用量,CCR 的成本投影继续保留这个分项。调用意图不等于服务端实际生效档位。

Model routing ownership

CCR keeps stage model selection, effort, review budgets and recording. AgentGo's ChatModel and Router own model calls and endpoint failover; litellm owns vendor protocols. Native Harness messages carry opaque provider replay state through CCR without rebuilding SDK requests.

A routed pool has one routing.call_timeout_sec budget (default 180 seconds). routing.attempt_timeout_sec optionally bounds each endpoint; when omitted the call budget is divided by the pool size so a stalled primary leaves fallback time. Provider retries are disabled inside the pool. Authentication/validation/context overflow and non-retryable failures stop failover. finish_reason=length remains an explicit truncated response, and CCR's existing continuation/wrap-up policy handles it. Endpoint attempts report safe labels, timing, timeout scope and failover decisions; these facts do not prove production latency improvements.

CCR uses ModelExecutor.Call for Loop, summary and auxiliary calls. The provider request uses streaming; the executor returns the final result and publishes incremental facts. Harness recording, output budgets and lossless tool argument repair run as model middleware. TransformContext.ModelExecutor and staged artifacts pass through ContextManager and zone compaction explicitly. Summary progress never becomes a conversation message. The final system message gets a cache breakpoint unless explicitly configured; the provider decides whether caching is supported. No cache-hit claim is inferred from prompt layout alone.

All CCR model callers use AgentGo ChatModel, LLMRequest, LLMResponse and Message directly, including planning, relocation and context compression. Domain AgentMessage.ToMessage() is the sole projection into model messages; there is no CCR message DTO, native escape hatch, or reverse response conversion. The Harness only repairs lossless tool argument drift, retaining native reasoning, stop reason, usage, metadata and opaque provider state.

Auxiliary calls record the executor's native events in a local AgentGo Session, including the redacted request snapshot. Failed responses remain diagnostic PartialMessage evidence with reported usage; query, cost and Viewer surfaces retain it as an error, without executing or committing its tool calls. Router attempt facts are recorded there as well; loop executions use AgentGo's own event recording. The review budget charges each reported attempt once, including usage on failed attempts, with the final response as a fallback for models without attempt facts. Cache reads and reasoning are already included in input and output; cache writes are added once. Missing usage stays unknown.