☰
解析 5 种 Agent CLI 输出流:OpenSquirrel runtime.rs 的 JSON 流解析与多轮会话实现
2026/10/12 20:31:32 网站建设 项目流程

【免费下载链接】OpenSquirrel

For people who get distracted by agents. A native Rust/GPUI control plane for running Claude Code, Codex, Cursor, and OpenCode side by side — because if you're going to be squirrely, you might as well optimize for it.

项目地址:https://gitcode.com/gh_mirrors/ope/OpenSquirrel
点击查看免费下载

OpenSquirrel 是一个基于 Rust + GPUI 的原生 AI Agent 控制台,可以把 Claude Code、Codex、Cursor Agent、OpenCode 等 5 种 Agent CLI 并排运行在同一窗口中。这篇文章带你拆解它最核心的工程难题:如何解析 5 种 Agent CLI 的 JSON 输出流,以及如何实现跨 CLI 的多轮会话——全部集中在 src/runtime.rs 这一个文件里完成。

为什么 Agent CLI 输出流解析这么棘手

每种 AI 编码 CLI 的"方言"都不一样:

  • 有的输出纯文本(直接给人看的 TUI 内容);
  • 有的输出一行一条 JSON 事件(stream-json 协议);
  • 有的事件里文本是增量片段(delta),有的则是完整段落;
  • Token 统计、会话 ID、错误信息藏的事件类型也各不相同。

OpenSquirrel 的做法很直接:为每种 CLI 写一个"适配器"解析函数,把方言翻译成统一的消息模型。

5 种 CLI 输出流速览:谁用 JSON,谁用纯文本

RuntimeCLI 命令输出模式解析函数
Claude Codeclaudestream-json(JSON 流)parse_claude_json
CodexcodexJSON 事件流parse_codex_json
Cursor Agentcursor agentJSON 流parse_cursor_json
OpenCodeopencodeJSON 流parse_opencode_json
Gemini CLIgemini纯文本(无 JSON)逐行直接透传

前 4 种走 JSON 流(is_json_mode),Gemini 则按纯文本逐行输出——这也是 README.md 中运行时对照表的核心内容。

JSON 流解析核心:逐行读取 + 事件分发

逐行解析,失败即跳过

所有解析函数签名几乎一致:输入一行文本,输出"本轮是否结束"的布尔值。核心套路三步走:

  1. serde_json::from_str尝试把整行解析为 JSON;
  2. 读取顶层type字段判断事件类型;
  3. match分发到对应的处理分支。

解析失败就静默跳过——这一点对抗"CLI 输出的杂音行"非常关键。

  • Claude:parse_claude_json(src/runtime.rs)
  • Codex:parse_codex_json(src/runtime.rs)
  • OpenCode:parse_opencode_json(src/runtime.rs)
  • Cursor:parse_cursor_json(src/runtime.rs)

四类事件:文本增量、工具调用、Token 统计、错误

以 Claude Code 为例,stream-json流里最重要的事件只有 4 类:

事件类型含义OpenSquirrel 的处理
content_block_delta文本/思考的增量片段追加进缓冲,拼出完整行再上屏
assistant助手消息块只提取tool_use(避免文本重复)
result本轮结束,附带 usage 与费用解析 Token 统计、成本、session_id,返回 true 结束本轮
error出错转发AgentMsg::Error

一个典型的result事件长这样(节选):

{ "type": "result", "session_id": "abc-123", "total_cost_usd": 0.012, "usage": { "input_tokens": 820, "output_tokens": 140 } }

增量拼行:line_buf 小缓冲的巧思

流式输出里,一个词的增量可能被拆成好几个 delta,甚至把一行的前半段单独发出来。OpenSquirrel 在解析函数外维护一个line_buf字符串缓冲:

  • 每收到一段text增量就push_str进缓冲;
  • 缓冲里一旦出现\n,就把完整的一行drain出去发送;
  • 遇到content_block_stop/result时,把缓冲区剩余的"最后半行"冲刷上屏。

这个十几行的机制,解决了"流式文本截半行显示"的常见毛刺问题。

统一消息模型:一切皆 AgentMsg

解析器不直接碰 UI,而是往一个async_channel里投递事件。UI 侧在 src/app.rs 定义的AgentMsg枚举中只关心 7 种消息:

  • OutputLine/StderrLine:输出行 / 错误行
  • ToolCall:工具调用(如 Bash、Read、Edit)
  • TokenUpdate:Token 与成本统计
  • Error:错误信息
  • Done { session_id }:本轮结束,并带回会话 ID
  • RemoteCursor:远程 tmux 轮询游标

解析(生产者)与渲染(消费者)彻底解耦——换任何一种 CLI,UI 代码都不用动。

多轮会话实现:持久进程 vs 一次性续接

这是 src/runtime.rs 中agent_thread主循环最精彩的部分:5 种 CLI 用两种不同的策略实现多轮。

策略一:claude 的持久多轮模式

Claude Code 支持--input-format stream-json --output-format stream-json --verbose全双工流式模式。OpenSquirrel 在 src/lib.rs 的build_persistent_runtime_args中规范化这些参数,然后走专用的agent_thread_claude_persistent(src/runtime.rs):

  1. 进程只启动一次,stdin 保持打开;
  2. 每收到一条新提示词,就往 stdin 写一行 JSON:
{ "type": "user", "message": { "role": "user", "content": "你的提示词" } }
  1. stdout 线程持续跑parse_claude_json,直到result事件才发出Done { session_id };
  2. 下一轮继续写 stdin,上下文完全保留,无需重启进程。

策略二:one-shot CLI 的会话续接

Codex、Cursor、OpenCode 是"一发一答"模式:每轮都要重新起进程,靠会话 ID 续接上下文:

  • OpenCode:追加--session <id> --continue
  • Cursor:追加--resume <id>
  • 会话 ID 从上一轮Done消息带回,存在应用状态里

UI 层则用SESSION:<id>\n<提示词>的私有前缀把两者打包传递,由 src/lib.rs 的parse_session_prompt统一拆解。

值得借鉴的 3 个设计点

  1. 适配器模式拉平方言差异:4 个解析函数签名完全一致,主循环里一个if/else就能切换 CLI,新增第 6 种 Agent 只需再写一个parse_xxx_json;
  2. bool返回值作为"轮次结束信号":解析到终止事件就返回true,主循环立即break,不依赖进程退出;
  3. 解析逻辑在本地与远程复用:SSH 到远端机器跑 Agent 时,OpenSquirrel 用 tmux 快照轮询屏幕,但喂给解析器的函数与本地完全相同(src/runtime.rs)——一套方言翻译,两处使用。

项目已归档,作者的经验是"终端已经赢了"——但 SPEC.md 与这套流式解析代码,依旧是理解多 Agent 编排的好教材。

【免费下载链接】OpenSquirrel

For people who get distracted by agents. A native Rust/GPUI control plane for running Claude Code, Codex, Cursor, and OpenCode side by side — because if you're going to be squirrely, you might as well optimize for it.

项目地址:https://gitcode.com/gh_mirrors/ope/OpenSquirrel
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询