同一任务的中间态展示对比
同一任务的中间态展示对比
目标
继续沿用 Kiro 反代方案研究 这个样本,只回答一个问题:
同一个长任务在执行过程中,会出现很多“还没结束但系统已经在展示的内容”。这些中间态到底有哪些层?OpenCode 和 Claude Code 又分别把它们放在哪里?
如果不把这个问题拆开,你在读本地落盘时很容易把四种东西混在一起:
- 进度回复
- 后台任务提醒
- tool 结果摘要
- 最终答案
先说结论
两边都存在这四层,但承载位置不同。
OpenCode
- 进度回复:assistant
text part - 后台任务提醒:user
text part,但内容是系统注入的 reminder - tool 结果摘要:assistant
tool part或background_output的输出 - 最终答案:assistant
text part
Claude Code
- 进度回复:不一定稳定落成独立 transcript 文本,更多是运行时展示层的产物
- 后台任务提醒:如果有,也更可能作为 runtime 消息,而不是像
OpenCode一样直接写成会话消息 - tool 结果摘要:主要体现在
tool_result - 最终答案:运行时
result/success更关键,不一定完整写入主 transcript
一句话:
OpenCode 把中间态更多显式写进会话;Claude Code 把中间态更多保留在执行层。
用 Kiro 样本拆四层
第一层:进度回复
这是最容易被误认为“最终答复”的一层。
在这个样本里,OpenCode 的典型进度回复有:
已拿到仓库内的配置与 Oracle 评审结论,外部 Kiro 反代资料的 librarian 还在检索中。我先保持当前结论不下最终方案,等外部资料返回后立刻综合输出具体反代方案与配置清单。当前进度:仓库内查找与现有配置分析已完成,Oracle 风险评审已完成;外部 “Kiro 反代” 资料的 librarian 任务仍在运行中(等待其返回具体官方/论坛引用与配置示例)。收到外部结果后我会立即综合给出完整可执行方案。正在运行的是 **bg_038a6130**(librarian:Research kiro reverse proxy docs and forum patterns)。状态为 **running**。这些内容有几个特征:
- 是给用户看的自然语言
- 不是最终方案
- 会真实进入 session
- 在
OpenCode里和最终答案处于同一存储层,都是 assistanttext part
也就是说,在 OpenCode 里:
进度回复和最终答案在结构上是同类对象,只是语义不同。
第二层:后台任务提醒
这是这个样本最容易被忽略的一层。
在 OpenCode 里,它并不是隐形系统事件,而是直接写进 session 的 reminder 文本。
例如:
<system-reminder>
[BACKGROUND TASK COMPLETED]
**ID:** `bg_ec12e98c`
**Description:** Check docker-compose topology and networking for proxy insertion points
**Duration:** 31s
**2 tasks still in progress.** You WILL be notified when ALL complete.
Do NOT poll - continue productive work.
Use `background_output(task_id="bg_ec12e98c")` to retrieve this result when ready.
</system-reminder>
<!-- OMO_INTERNAL_INITIATOR -->以及:
<system-reminder>
[ALL BACKGROUND TASKS COMPLETE]
**Completed:**
- `bg_a60e9ffc`: Find reverse-proxy patterns and kiro mentions
- `bg_ec12e98c`: Check docker-compose topology and networking for proxy insertion points
- `bg_038a6130`: Research kiro reverse proxy docs and forum patterns
- `bg_c0e0185e`: Review Kiro reverse proxy plan and pitfalls
Use `background_output(task_id="<id>")` to retrieve each result.
</system-reminder>
<!-- OMO_INTERNAL_INITIATOR -->这些内容的关键点是:
- 在存储上它们属于
user消息 - 但语义上它们不是用户主动输入
- 它们是系统注入的流程控制信息
所以如果你只按 role=user 去读,会把用户输入和系统提醒混在一起。
这也是为什么 OpenCode 的“完整请求”看上去像一条很长的混合链。
第三层:tool 结果摘要
这一层不是最终给用户的自然语言总结,但它已经高度结构化,而且能直接解释“系统刚刚拿到了什么”。
在 OpenCode 里,典型形态是:
Task Result
Task ID: bg_ec12e98c
Description: Check docker-compose topology and networking for proxy insertion points
Duration: 31s
Session ID: ses_3297152baffeR2vKXiqlWm3BeF
...或者:
# Task Status
| Field | Value |
|-------|-------|
| Task ID | `bg_038a6130` |
| Description | Research kiro reverse proxy docs and forum patterns |
| Agent | librarian |
| Status | **running** |
| Duration | 18m 26s |
| Session ID | `ses_3297152b4ffeR0SBPyFTSUBnRa` |以及普通 read/grep/glob 的原始输出。
这些内容的作用不是“替代最终答案”,而是给主 agent 一个可继续综合的事实层。
所以:
- 进度回复回答“现在做到了哪”
- tool 结果摘要回答“系统刚拿到了什么”
这两层不一样。
第四层:最终答案
最终答案在样本里是这段大文本:
基于仓库现状与外部资料(Kiro Gateway 项目)可执行的**具体反代方案**如下。这里假设论坛讨论的“反代 Kiro”指的是 **Kiro Gateway**(OpenAI/Anthropic 兼容 API,端口 8000,SSE 流式),与你仓库的 CLIProxyAPI Plus 共存:
...
如果你希望我直接帮你生成完整的 `docker-compose.yml` + `nginx.conf` 文件并替换到仓库里,我可以继续完成。这一层和前面三层的差别是:
- 它不再只是状态
- 也不再只是工具输出
- 而是主 agent 对全部上下文做过综合之后给出的面向用户的结论
OpenCode:四层分别挂在哪里
把上面四层重新映射回 OpenCode 的落盘结构,就很清楚了。
1. 进度回复
挂在:
- assistant
message part.type = text
2. 后台任务提醒
挂在:
- user
message part.type = text
但本质上是系统注入的 reminder。
3. tool 结果摘要
挂在:
- assistant
message part.type = tool
尤其是:
taskbackground_outputreadgrepglob
这些 part 的 state.output。
4. 最终答案
挂在:
- assistant
message part.type = text
所以 OpenCode 最大的特点是:
四层都能在主 session 里看到,只是分布在不同 message/part 组合里。
Claude Code:四层分别挂在哪里
Claude Code 更不对称。
1. 进度回复
从 claude-code-rev 的代码设计看,这类内容更可能属于 runtime 层或 query 过程中的中间消息,不一定稳定保留成你在 transcript 里能直接看到的一段文本。
所以对本地 transcript 读者来说:
- “进度回复”不是主对象
- 就算 UI 里能看到,也不一定是 transcript 里最好找的一层
2. 后台任务提醒
本地 transcript 参考并没有表现出 OpenCode 那种“把 reminder 当作 user message 注入会话”的模式。
所以更合理的理解是:
- 这类提醒更偏 runtime 提示
- 不像
OpenCode那样直接成为主会话链的一部分
3. tool 结果摘要
这是 Claude Code transcript 最强的一层。
本地 transcript 中最稳定可见的就是:
tool_usetool_result
例如:
{"type":"tool_use","tool_name":"webfetch", ...}
{"type":"tool_result","tool_name":"webfetch", ...}所以如果你想从 Claude transcript 里找“中间态”,最应该看的不是 assistant 文本,而是 tool_result。
4. 最终答案
对 Claude Code 来说,最终答案更靠近 QueryEngine 向上层 yield 的:
{
"type": "result",
"subtype": "success",
"result": "..."
}也就是说:
- 最终答案在概念上存在
- 但对本地 transcript 读者来说,它未必像
OpenCode一样直接躺在主存储里
为什么你会觉得 OpenCode 更容易查
因为它把“中间态”显式写进了会话本体。
你查一个长任务时,虽然会觉得内容很多、消息很碎,但至少以下东西都在同一个 session 里:
- 阶段性进度
- 系统提醒
- 背景任务结果
- 最终结论
而 Claude Code 更偏向:
- 真正稳定落盘的是执行事件
- 最终可读答案并不总是和执行事件一起以同等显性的方式保存
所以它更适合恢复执行过程,不如 OpenCode 那样适合直接回看“完整对话稿”。
对同一个任务的直接对比
OpenCode
你可以把中间态理解成四层都在会话里:
- assistant 进度文本
- system reminder 文本
- tool 输出摘要
- assistant 最终答案
Claude Code
你可以把中间态理解成四层分散在两套对象里:
- runtime 提示层
- transcript 事件层
其中 transcript 事件层最稳的是:
usertool_usetool_result
而真正的最终自然语言结果,更靠近 runtime result/success。
最终结论
如果你以后再查长任务,不要再把所有中间内容都当成“回复正文”。
更稳的拆法是:
- 先找最终答案
- 再找进度回复
- 再找后台任务提醒
- 最后才找 tool 结果摘要
对 OpenCode 来说,这四层都能在 session 主存储里找到。
对 Claude Code 来说,最可靠的是先看 tool_result,再单独找 runtime 最终结果。
再压成一句:
OpenCode 的中间态更像聊天过程本身;Claude Code 的中间态更像执行痕迹。