【免费下载链接】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.
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,谁用纯文本
| Runtime | CLI 命令 | 输出模式 | 解析函数 |
|---|---|---|---|
| Claude Code | claude | stream-json(JSON 流) | parse_claude_json |
| Codex | codex | JSON 事件流 | parse_codex_json |
| Cursor Agent | cursor agent | JSON 流 | parse_cursor_json |
| OpenCode | opencode | JSON 流 | parse_opencode_json |
| Gemini CLI | gemini | 纯文本(无 JSON) | 逐行直接透传 |
前 4 种走 JSON 流(is_json_mode),Gemini 则按纯文本逐行输出——这也是 README.md 中运行时对照表的核心内容。
JSON 流解析核心:逐行读取 + 事件分发
逐行解析,失败即跳过
所有解析函数签名几乎一致:输入一行文本,输出"本轮是否结束"的布尔值。核心套路三步走:
serde_json::from_str尝试把整行解析为 JSON;- 读取顶层
type字段判断事件类型; 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 }:本轮结束,并带回会话 IDRemoteCursor:远程 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):
- 进程只启动一次,stdin 保持打开;
- 每收到一条新提示词,就往 stdin 写一行 JSON:
{ "type": "user", "message": { "role": "user", "content": "你的提示词" } }- stdout 线程持续跑
parse_claude_json,直到result事件才发出Done { session_id }; - 下一轮继续写 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 个设计点
- 适配器模式拉平方言差异:4 个解析函数签名完全一致,主循环里一个
if/else就能切换 CLI,新增第 6 种 Agent 只需再写一个parse_xxx_json; bool返回值作为"轮次结束信号":解析到终止事件就返回true,主循环立即break,不依赖进程退出;- 解析逻辑在本地与远程复用: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.
相关推荐
A2A 流式输出与多轮对话实战:基于 LangGraph 的 Currency Agent 深度解析
A2A 流式输出与多轮对话实战:基于 LangGraph 的 Currency Agent 深度解析 本篇技术指南聚焦 Agent2Agent A2A 协议中
人工智能AI AgentAPI设计gpt-computer-assistant(Upsonic)Chat 会话层深度解析:多轮会话编排、流式/HITL 与成本观测
gpt computer assistant(Upsonic)Chat 会话层深度解析:多轮会话编排、流式/HITL 与成本观测 Upsonic 框架的 ups
人工智能大模型AI AgentAgent 框架自主智能体工具调用RAGAgent 记忆Agent 编排temporal-ai-agent:多轮对话AI Agent与Temporal工作流
temporal ai agent:多轮对话AI Agent与Temporal工作流 Temporal AI Agent 是一个开源项目,通过展示AI Agen
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考