Skip to content

Latest commit

 

History

History
110 lines (80 loc) · 5.8 KB

File metadata and controls

110 lines (80 loc) · 5.8 KB

jcode-cli 设计规划

一个基于 Go 的 Agent 智能体终端工具,定位为通用编码助手(类 Claude Code / Cursor)。

  • 技术栈:Go + eino(Agent 编排) + bubbletea(TUI)
  • 模型:多 provider 可切换(OpenAI 兼容 / 火山 Ark 豆包 / Claude / Ollama 等)
  • 工具:文件读写、Shell 执行、联网搜索/HTTP、MCP 自定义工具扩展

一、整体架构

本质是三层:TUI 交互层(bubbletea)、Agent 编排层(eino)、工具执行层(自实现)。 难点不在 eino —— 它把 Agent 循环封装好了 —— 而在 TUI 与流式 Agent 的异步桥接工具的权限/安全控制

┌─────────────────────────────────────────────┐
│  TUI 层 (bubbletea)                            │
│  - 输入框 / 消息流渲染 / 工具调用确认弹窗          │
│  - 通过 channel 接收 Agent 的流式事件            │
└──────────────────┬──────────────────────────┘
                   │ tea.Cmd ⇄ event channel
┌──────────────────┴──────────────────────────┐
│  Agent 编排层 (eino react.Agent)               │
│  - ToolCallingChatModel (多 provider 可切换)    │
│  - ToolsNode (注册所有工具)                      │
│  - 流式 Recv() 循环 → 转成 TUI 事件             │
└──────────────────┬──────────────────────────┘
┌──────────────────┴──────────────────────────┐
│  工具执行层 (自实现 tool.InvokableTool)          │
│  文件读写 / Shell / HTTP / MCP 客户端            │
│  ↑ 每个工具执行前经过「权限网关」                  │
└──────────────────────────────────────────────┘

核心设计决策:eino 的 agent.Stream() 在一个 goroutine 里跑,通过 channel 把事件(文本片段、 工具调用请求、工具结果)发给 bubbletea 的 Update。工具执行前如需用户确认,工具函数会阻塞 等待 TUI 回传授权信号 —— 这是整个交互的关键耦合点。


二、技术选型

关注点 方案
Agent 循环 react.NewAgent + react.AgentConfig,不用自己写 ReAct 循环
多模型切换 统一到 ToolCallingChatModel 接口,启动时按配置选 provider(openai/ark/claude/ollama)。OpenAI 兼容接口可覆盖 DeepSeek 等大多数国产模型
工具抽象 tool.InvokableTool,用 utils.NewTool(toolInfo, fn) 快速构造
MCP 扩展 eino-ext 的 MCP 工具适配,把 MCP server 的工具桥接成 eino tool
流式 agent.Stream()StreamReader.Recv() 循环
多工具并发 eino 的 ToolsNode 内部已并发执行,只需保证工具本身线程安全

关键 eino 类型参考:

  • Agent 包:github.com/cloudwego/eino/flow/agent/react
  • react.NewAgent(ctx, &react.AgentConfig{...}),必填 ToolCallingModelToolsConfig
  • 模型接口:ToolCallingChatModel(含 WithTools),基类 BaseChatModel 提供 Generate / Stream
  • 工具配置:ToolsConfig 类型为 compose.ToolsNodeConfig
  • 上下文钩子:MessageModifier(每轮注入,不持久)、MessageRewriter(持久,做压缩)

三、开发里程碑

严格按依赖顺序推进,每阶段独立验证,避免一上来就纠缠 TUI+Agent 的异步问题。

M0 — 最小闭环(无 TUI)

纯命令行:读 stdin → eino ReactAgent → 打印流式输出。先接一个模型,挂一个假工具(如 get_time), 确认 Agent 循环和工具调用跑通。这一步用 fmt.Println 即可,不碰 bubbletea。

M1 — 真实工具集

实现文件读(Read)、文件写(Write/Edit)、Shell 执行、HTTP 请求四个工具。此时还没权限控制,在隔离目录测。

M2 — 权限网关

工具执行前的确认机制。Shell 和文件写是危险操作,设计「自动允许 / 单次确认 / 拒绝」三态。 先用命令行 y/n 验证。

M3 — TUI 接入

bubbletea 替换命令行交互。最复杂的一步:消息流渲染、流式打字机效果、工具确认弹窗、 把 M2 的 y/n 换成 TUI 交互。

M4 — 多模型 + 配置

配置文件(模型 provider、API key、工具开关),运行时切换模型。

M5 — MCP 扩展

接入 MCP 客户端,让用户挂外部工具。


四、已知难点与坑

  1. bubbletea 与流式的桥接是真正的难点。bubbletea 的 Update 是单线程消息循环, Agent 在另一个 goroutine。标准做法:Agent 把事件发到 channel,再用一个 tea.Cmd 不断从 channel 读并转成 tea.Msg。建议 M0/M1 完全不碰 TUI,把这个难点孤立到 M3。

  2. 工具确认的阻塞问题:工具函数运行在 eino 的 goroutine,要等用户在 TUI 点确认。 需要工具持有「请求授权 channel」+「接收结果 channel」,小心死锁。

  3. StreamToolCallChecker:Claude 和 OpenAI 流式吐 tool call 的方式不同(Claude 会先吐文本)。 多模型切换时这是个隐藏配置点。

  4. 多轮上下文管理:MessageModifier(每轮注入 system prompt,不持久) vs MessageRewriter(持久,做上下文压缩)。编码助手对话很长,M4 之后要考虑压缩。


参考资料