|
| 1 | +--- |
| 2 | +title: HagiCode 是怎么把 13 个 Agent CLI 接到一套系统里的 |
| 3 | +date: 2026-06-22 |
| 4 | +tags: [HagiCode, Agent CLI, 架构设计, AI] |
| 5 | +--- |
| 6 | + |
| 7 | +## HagiCode 是怎么把 13 个 Agent CLI 接到一套系统里的 |
| 8 | + |
| 9 | +> 其实这事儿吧,说难也不难,说简单呢,又不简单。聊聊我们怎么用一套分层架构,把 Claude Code、Codex、Copilot、Gemini 这些风格各异的 Agent CLI 统一管起来,还能随时插一个新的进来。 |
| 10 | +
|
| 11 | +## 背景 |
| 12 | + |
| 13 | +故事开始得突然,源于一个挺让人头疼的问题。 |
| 14 | + |
| 15 | +Agent CLI 这两年像竹笋一样冒头——Claude Code、OpenAI Codex、GitHub Copilot、Gemini CLI、Kimi、Qoder、Kiro…… 每隔几个月就钻出一个新的。作为一个想让用户"装一个 HagiCode、用全套 Agent"的项目,我们不可能只押宝某一个 CLI,可是又不可能为每个 CLI 写一套从安装、健康检查到调度的完整逻辑——那样代码会膨胀得没法维护,像乱成一团的毛线,谁都不敢碰。 |
| 16 | + |
| 17 | +更麻烦的是,这些 CLI 的脾气差得远:有的走 stdio、有的走 gRPC、有的只给你一个 shell 入口、流式输出格式还各说各话。要是直接在业务代码里写 `if (provider == ClaudeCode)` 这种判断,没过半年就会变成一坨谁都不敢动的"祖传代码"。毕竟,谁愿意去动一块看着就摇摇欲坠的砖呢? |
| 18 | + |
| 19 | +为了把这些痛处都收住,我们做了个决定:**在业务层和具体 CLI 之间,加一套薄薄的抽象层和共享运行时**。这事儿看着简单,可它直接决定了 HagiCode 能不能快速接入新 CLI。稍后我会具体说怎么做。 |
| 20 | + |
| 21 | +## 关于 HagiCode |
| 22 | + |
| 23 | +本文分享的方案,来自我们在 HagiCode 项目里摸爬滚打的实践。HagiCode 是一个 AI 代码助手整合平台,目标很纯粹——用一套安装、一套配置,把主流 Agent CLI 全都接进来给用户用。 |
| 24 | + |
| 25 | +## "13 个"这个数字是怎么来的 |
| 26 | + |
| 27 | +先说一个被反复问到的数字——为什么是 13 个 Agent CLI。 |
| 28 | + |
| 29 | +其实答案就藏在 `AIProviderType` 这个枚举里,像藏在窗外的竹影里,只要你肯看,就能看见。原始定义长这样: |
| 30 | + |
| 31 | +```csharp |
| 32 | +public enum AIProviderType |
| 33 | +{ |
| 34 | + ClaudeCodeCli = 0, |
| 35 | + CodexCli = 1, |
| 36 | + GitHubCopilot = 2, |
| 37 | + CodebuddyCli = 3, |
| 38 | + OpenCodeCli = 4, |
| 39 | + IFlowCli = 5, // 已废弃 |
| 40 | + HermesCli = 6, |
| 41 | + QoderCli = 7, |
| 42 | + KiroCli = 8, |
| 43 | + KimiCli = 9, |
| 44 | + GeminiCli = 10, |
| 45 | + DeepAgentsCli = 11, |
| 46 | + ReasonixCli = 12, |
| 47 | + PiCli = 13, |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +枚举一共 14 个值,可是 `IFlowCli=5` 这条路已经走不通了。在 `AIProviderFactory` 里,它被显式挡在了门外: |
| 52 | + |
| 53 | +```csharp |
| 54 | +if (providerType == AIProviderType.IFlowCli) |
| 55 | +{ |
| 56 | + throw new NotSupportedException("IFlowCli is no longer supported"); |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +再配合 `IsActivelySupportedProviderType()` 做一次过滤,真正在系统里"活着"的,就是 **13 个**:Claude Code、Codex、GitHub Copilot、CodeBuddy、OpenCode、Hermes、Qoder、Kiro、Kimi、Gemini、DeepAgents、Reasonix、Pi。 |
| 61 | + |
| 62 | +这就是"13"的由来。不是个营销数字,是代码里真真切切数出来的。毕竟数字是不会骗人的,骗人的只是我们自己罢了。 |
| 63 | + |
| 64 | +## 分层架构:把变化关在笼子里 |
| 65 | + |
| 66 | +接 13 个 CLI 的核心思路,其实就一句话:**让业务代码不关心它调的到底是哪一个**。 |
| 67 | + |
| 68 | +我们把它拆成了六层,从上往下看: |
| 69 | + |
| 70 | +### 1. 身份层 —— `AIProviderType` |
| 71 | + |
| 72 | +枚举就是每个 CLI 的"身份证号"。任何地方提到一个 CLI,都用这个枚举值标识,字符串和枚举之间用 `ToStringValue()` / `ToAIProviderType()` 互转。简单,却不可或缺。 |
| 73 | + |
| 74 | +### 2. 业务契约层 —— `IAIProvider` / `IAIProviderFactory` |
| 75 | + |
| 76 | +业务侧只认 `IAIProvider` 这个接口,里面定义的是"发一个 prompt、拿到流式回复"这种通用动作。至于底下是 Claude 还是 Codex,业务不关心——就像你写信,只管把信交出去,至于邮差姓什么,谁在乎呢? |
| 77 | + |
| 78 | +### 3. 适配器层 —— `*CliProvider` |
| 79 | + |
| 80 | +每个 CLI 对应一个薄适配器,比如 `PiCliProvider`、`ReasonixCliProvider`、`ClaudeCodeCliProvider`。这些适配器要做的事情很少:把通用的业务请求翻译成具体 CLI 能懂的参数,再把具体 CLI 的输出翻译回来。它们故意写得很薄,新加一个 CLI,基本就是抄一个现成的,改改而已。 |
| 81 | + |
| 82 | +### 4. 共享运行时层 —— `ICliProvider<TOptions>` |
| 83 | + |
| 84 | +这一层在 `HagiCode.Libs` 里,是真正干脏活累活的地方:跨平台拉起进程、处理 stdio 传输、解析流式输出、处理超时和重试。所有适配器都复用同一套运行时,所以对接一个新 CLI 时,进程管理这块基本不用重写。 |
| 85 | + |
| 86 | +打个比方,适配器层是"翻译官",共享运行时层是"快递公司"。翻译官只管把话说清楚;包裹怎么送、路上堵不堵车,那是快递公司的事。各司其职,世界就清净了。 |
| 87 | + |
| 88 | +### 5. 工厂路由层 —— `AIProviderFactory` |
| 89 | + |
| 90 | +`CreateProvider` 里一个 `switch`,按 `AIProviderType` 实例化对应适配器,顺带校验 `IsConfigured`。这是唯一一处"知道具体类型"的地方,被严格隔离在工厂里。变化只允许在一个角落里发生,其余地方都干干净净。 |
| 91 | + |
| 92 | +### 6. 目录 / UI 投影层 —— `main-professions.yaml` |
| 93 | + |
| 94 | +这一层有意思,它不是代码,是数据。 |
| 95 | + |
| 96 | +主职业清单("我是个前端"、"我是个后端"、"我是个全栈"这种角色画像)由 `main-professions.yaml` 这个预设文件驱动,通过 `HeroPrimaryProfessionPresetProvider` 读出来,再投影到前端 UI。新增一个主职业,不需要改一行代码,改 YAML 就行。数据代替代码,省心。 |
| 97 | + |
| 98 | +> 顺便说一句,这块是 HagiCode 重构最大的地方。早期版本里有个叫 `AgentCliInstallRegistry` 的代码内注册表,后来发现维护成本太高——代码写多了,人也就累了——整套被推倒,换成了数据驱动 + 健康监测的方案。这也是为什么 HagiCode 现在能快速扩展职业类型的原因。 |
| 99 | +
|
| 100 | +## 安装这事儿怎么解决 |
| 101 | + |
| 102 | +13 个 CLI 都要装,每个官方安装方式还不一样,这就是另一座山了。 |
| 103 | + |
| 104 | +我们的做法是 **Docker Compose 预装 + 外部管理兜底**。镜像里把主流 CLI(Claude Code、Codex、Copilot、CodeBuddy、OpenCode、Qoder、Kiro、Kimi、Gemini、Pi)都预装好,用户拉镜像就能用,不用自己一条条敲命令。装好了,心情自然也好。 |
| 105 | + |
| 106 | +对于需要在本地环境单独装的,安装命令矩阵大概是这样(已核对官方文档): |
| 107 | + |
| 108 | +| CLI | 官方安装方式 | |
| 109 | +| --- | --- | |
| 110 | +| Claude Code | `npm install -g @anthropic-ai/claude-code` | |
| 111 | +| Codex | `npm install -g @openai/codex` | |
| 112 | +| GitHub Copilot | `npm install -g @github/copilot` | |
| 113 | +| CodeBuddy | `npm install -g @tencent-ai/codebuddy-code` | |
| 114 | +| OpenCode | `npm i -g opencode-ai@latest` | |
| 115 | +| Qoder | `npm install -g @qoder-ai/qodercli` | |
| 116 | +| Kiro | `curl -fsSL https://cli.kiro.dev/install \| bash` | |
| 117 | +| Kimi | `curl -LsSf https://code.kimi.com/install.sh \| bash` | |
| 118 | +| Gemini | npm | |
| 119 | +| Hermes | 官方脚本,保留 docs-only 兜底 | |
| 120 | +| DeepAgents / Reasonix | 见各自官方文档 | |
| 121 | + |
| 122 | +前端 `PrimaryProfessionCard.tsx` 这块也跟着变了——它现在**没有"安装 CLI"按钮**,而是展示 CLI 可用性、版本探测结果,以及"这个 CLI 由外部管理"的兜底提示。也就是说,装不装得起来由系统层负责,UI 只负责如实反馈状态。状态和逻辑各写一遍,迟早会对不上,那又何必呢? |
| 123 | + |
| 124 | +## 加一个新 CLI 要做啥 |
| 125 | + |
| 126 | +落到实操,在 HagiCode 里加一个新 CLI,大概也就这么几步: |
| 127 | + |
| 128 | +1. 在 `AIProviderType` 里加一个枚举值 |
| 129 | +2. 抄一个现成的 `*CliProvider`,改成新 CLI 的参数和输出解析 |
| 130 | +3. 在 `AIProviderFactory` 的 `switch` 里加一行路由 |
| 131 | +4. 如果要进主职业目录,在 `main-professions.yaml` 里配一下 |
| 132 | +5. 镜像里加一条安装命令(或者走外部管理兜底) |
| 133 | + |
| 134 | +整套流程下来,核心改动不超过两百行代码——这便是这套抽象真正的价值。每多接一个 CLI,边际成本都很低,业务代码一行都不用改。条条大路通罗马,只是我们这条路,稍微好走一点罢了。 |
| 135 | + |
| 136 | +## 总结 |
| 137 | + |
| 138 | +回头看,"接 13 个 CLI"听着吓人,可拆开看,其实也就两层功夫: |
| 139 | + |
| 140 | +一层是**把变化隔离**——通过 `AIProviderType` 枚举 + `IAIProvider` 契约 + 薄适配器 + 共享运行时,让业务代码和具体 CLI 解耦;另一层是**把配置数据化**——用 `main-professions.yaml` 这种 YAML 预设驱动目录和 UI,避免每加一个东西都要动代码。 |
| 141 | + |
| 142 | +这套方案,是我们在 HagiCode 实际开发里踩过坑、迭代过几轮才稳定下来的。如果你正在做类似的"多 Provider 整合"系统,希望这个分层思路能给你一点参考。毕竟,Agent CLI 这两年还会继续冒出来,一个能快速接入新 CLI 的架构,比"现在支持了几个"重要得多...... |
| 143 | + |
| 144 | +<!-- HAGICLAW_PROGRAMMATIC_ENDING --> |
0 commit comments