1. 为什么 HagiCode 要做多模型聚合:一个每天都在发生的基础设施问题
先说结论:模型调用这件事,正在从一个“选哪家 API”的问题,变成“怎么让不同模型在同一套工作流里无缝切换”的问题。我接触过不少团队,早期只接 OpenAI 的接口,等 GLM、MiniMax、Claude、Gemini 这些模型陆续成熟之后,发现再想切换成本已经上去了——代码里到处是 openai.ChatCompletion.create,prompt 模板跟着模型走,评测脚本也绑死了单一供应商,最后整个系统像个焊死的铁板,想换条腿走路得把全身骨头都拆一遍。
HagiCode 做多模型支持的出发点很简单:把模型当成可插拔的算力资源,而不是项目的底座。我们的做法是提供一个统一的调用层,所有模型都走同一套 Request/Response 协议,上层业务不关心背后到底是 GLM 还是 Gemini,只关心模型名、温度、max_tokens 这几个参数。
这个思路不是我们发明的,OpenAI 兼容层、Anthropic 兼容层到处都是,但真正落地的时候会发现一堆细节问题,比如:
- 各家模型的
system prompt支持程度不一样,MiniMax 的 role 映射就和 OpenAI 有细微差别; - 有的模型对
max_tokens的语义理解不同,GLM 早期版本把生成长度和上下文长度混在一起算; - 流式输出的格式不统一,有的发
delta,有的发text,有的甚至要先发一个role字段; - token 计费的口径五花八门,输入、输出、缓存命中的单价完全不同。
这些问题单个拎出来都不大,叠在一起就是灾难。我们在接入 GLM 和 Gemini CLI 的过程中,几乎把这些问题全踩了一遍。这篇博文就把整个过程拆开讲,包括架构设计、参数配置、实测数据,以及我们最后沉淀下来的排查经验。如果你也在做类似的聚合层,或者你只是想在命令行里同时用 GLM 和 Gemini 写代码,这篇都应该能给你省不少时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体设计与方案选型:为什么是 GLM、Gemini CLI 和“兼容为先”
2.1 模型选型:GLM 和 Gemini 各自的不可替代性
先说 GLM。智谱的 GLM 系列这几年迭代节奏很快,从 GLM-4 到 GLM-4.5,再到最近的 5.x 系列,最大的特点是中文语义理解扎实,在处理中文代码注释、技术文档、领域术语时明显比很多海外模型更稳。我们的业务里有大量中文语料要走模型,尤其在代码生成场景里,GLM 对中文注释的理解直接影响生成质量——你给它一段带“申请单”“审批流”“对公转账”这类业务词的代码,它能比较准确地理解上下文,不会像有些模型那样把业务词当成无关噪音丢掉。另外 GLM 的 API 价格相对有竞争力,尤其是 Flash 系列,作为高频调用时的兜底模型非常合适。
再说 Gemini。Gemini CLI 是 Google 出的开源命令行工具,支持用自然语言在终端里直接操作代码仓库,底层走的是 Gemini API。它的亮点是长上下文的处理能力,1M token 的上下文窗口在分析大型代码库时有天然优势,跑单测、查 bug、跨文件的 refactor 场景下表现不错。但 Gemini CLI 默认只支持 Google 的模型,这就带来一个问题:我们想用它交互式地写代码,但很多团队内部已经用习惯了 GLM,两套工具并存本身就是效率损耗。
所以我们的目标很明确:把 GLM 接入 Gemini CLI 的调用链,让用户在一个终端工具里,既能用 Gemini 处理超长上下文任务,也能切到 GLM 完成可控成本的中文任务。HagiCode 是这个聚合调度的底座,它负责把请求路由到正确的模型,并且把各家 API 的差异抹平。
2.2 整体架构:一层薄薄的协议转换器
HagiCode 的多模型调度层,本质上是一个协议转换服务。它对外暴露一个 OpenAI 风格的 RESTful API,内部通过 adapter 模式对接不同模型厂商:
- OpenAI 协议层:统一接收
POST /v1/chat/completions这样的请求,参数统一为model、messages、temperature、max_tokens等; - Adapter 层:根据
model参数把请求转换到具体厂商的 SDK 或原生 API。GLM 走智谱的接口,Gemini 走 Google 的接口; - 响应统一层:把各家返回的格式转成统一的
choices、usage结构,流式输出也统一转成 SSE 格式。
Gemini CLI 的接入是这个架构的一种特殊用法:Gemini CLI 本身是一个客户端,它通过 ANTHROPIC_BASE_URL 这样的环境变量支持自定义接口地址。我们让用户把 ANTHROPIC_BASE_URL 指向 HagiCode 的地址,然后在 HagiCode 里把 Anthropic 格式的请求转换成 OpenAI 格式,再路由到 GLM。相当于做了一个多层的“转译”:Gemini CLI(Anthropic 协议)→ HagiCode(Anthropic→OpenAI 转译)→ GLM API。
这个方案的取舍是:我们没有在 Gemini CLI 里改一行代码,也没有 fork 一个私有版本,而是利用它已有的自定义端点能力做适配。好处是 Gemini CLI 发
