之前写过一篇名为 IronClaw 安全机制解析 的文章,简单介绍了 IronClaw 的安全机制。当时 IronClaw 还处在 v0.18.0 版本,在它支持的 LLM 列表里只有 DeepSeek 一家是国内的(见下图)。写这篇文章的时候,IronClaw 来到了 v0.21.0 版本。

分析项目提交历史,从 0.18.0 到 0.21.0,IronClaw 这 4 个版本是在 9 天内陆续发布的,项目处在一个加速迭代的过程中。
除了持续的安全加固,IronClaw 扩展性也在增强,其中 v0.19.0 版本新增了两家 LLM 提供商,Z.AI 和 MiniMax,这两家都来自国内。因为看到 IronClaw 支持了 Z.AI,加上智谱会给国内新注册用户赠送资源包,本着免费至上的精神,抱着白嫖的态度,我开始了折腾,目标是搞一个接入 GLM 模型的 AI 助手,辅助处理一些琐碎杂务。

然后就开始一路踩坑,一直忙到凌晨才柳暗花明。这篇文章记录一下主要过程,略去了走过的一些弯路。
1. 我以为很简单,直接就能搞定
IronClaw 在 v0.19.0 支持的 Z.AI 是智谱 AI 国际版本的 API,要在 https://z.ai/manage-apikey/apikey-list 申请 API Key。
智谱 AI 国际版本网站打开就是“暗系装修风格”,直到现在我也没找到切换开关,基本可以确定网站只有一种风格。网站整体 UI 设计(下图),给我感觉就是两个字:简陋。或者说简约,😓。注册之后没有找到赠送的资源包,最低档套餐需要付费。收费加简陋,我一度以为自己进到了“钓鱼网站”。在用 AI 可以很轻易生成现代站点的今天,不敢相信这是一家 LLM 模型公司的站点。

小心谨慎地申请好 API Key,本地编译 IronClaw,配置智谱 AI 国际版本的 API,然后运行 IronClaw,最后通过 IronClaw 自带的 Gateway 页面对话,一切顺利。
这时候我才放下心来,没准智谱玩的是“酒香不怕巷子深”。
智谱 AI 国际版 API,从国内访问延迟较高,并且没有资源包白嫖。我又找到智谱 AI 国内版的网站 https://bigmodel.cn/ (不得不说,国内版和国际版的域名 https://z.ai/model-api 差别有点大)。
申请国内版本的 API Key,重新配置 IronClaw,使用国内版本的 API 入口和 API Key,运行 IronClaw,继续通过 IronClaw 自带的 Gateway 页面对话验证。和先前接入国际版本的流程一样,但是这次出问题了,IronClaw 一直收到国内版本 API 报错:{"error":{"code":"1210","message":"Invalid API parameter, please check the documentation."}}
查看 智谱 AI 国内版本 API 文档,关于错误码 1210 的解释信息(下图)和 API 报错返回的 message 内容类似。

点击页面左边列出的各个 API 请求,倒是可以查看 API 请求的参数规格(下图)。但这些对我的帮助不大:为了排查 API 报错而去阅读 API 规格是很浪费时间的做法。我更希望 API 报错能明确一些,告诉我请求中的哪个字段不合要求是最好的。面对笼统的报错,需要另想办法才能快速找出有问题的参数。

2. 问题到底出在哪里
排除 IronClaw 配置错误,除了表面上 API 接入点和 API Key 体系不一样之外,现在可以判断,智谱 AI 国内版和国际版的 API 在规格上也是不一样的。
快速浏览智谱 AI 国内、国外版本的文档,两边的开发文档都没有提及存在这种不一致。智谱 AI 国内版开发指南 仅仅声明它提供了 OpenAI API 兼容性(下图)。开发文档可能过时了,至少不够完善。

还是回到 IronClaw 实现,检查 向 LLM 发请求的代码,可以明确 IronClaw 是通过 rust 包 rig-core 统一构造 LLM 请求的:rig-core 是在 IronClaw PR #36 引入,借助 rig-core 可以快速支持多个 LLM provider,避免为每个 LLM provider 手写 HTTP 客户端。
那么现在的问题变成:rig-core 构造的请求,智谱 AI 国际版可以处理,但是国内版却报错了。
既然官方文档上没有给出有价值的信息,那只好抱着“死马当活马医”的态度去问问 智谱 AI Chat。问:“智谱 AI 国内版本 API 和国际版本 API 在请求参数上有哪些不同?”,回答:“请求参数上存在多个关键差异”。

换个角度继续问,交叉验证:“通过 rig-core 生成的请求,国际版 API 可以接受,国内版 API 却报错。”

回答:“智谱国内版和国际版对 OpenAI 格式的兼容度不同”。至此,问题明确了。智谱 AI 国内版 API 声称兼容 OpenAI API,但实际上只实现了 2023 年初的简化版本,与现代 OpenAI 客户端(例如 rig-core)生成的请求格式存在多处不兼容。
说老实话,现在 LLM Provider 如过江之鲫,我能想象到不同 LLM Provider 之间肯定存在种种差异,却不太能理解同一家公司在国内和国外需要有两套 API 规格。可能是因为国内和国外对模型部署的法规要求不同,又或者国内和国外的 API 版本是由两个团队分别维护的。但是就接入层而言,统一的 API 规格,显然更能方便用户接入。
3. 问题怎么解决
有下面几种解决方案:
调整 IronClaw 代码,绕过
rig-core,手动支持智谱 AI 国内版本的 API。但是这样会破坏 IronClaw 的优雅。从个人角度,为了快速适配智谱 AI 国内版本 API,这样的“私人定制”倒还可以接受;向
rig-core提交 issue,让其兼容旧版本的 OpenAI API。就算 rig-core 接受并支持了这样的需求,也是很久之后的事情了。谁知道智谱 AI 国内版本的 API 会不会来个更新,完全兼容 OpenAI API 格式呢;在 IronClaw 和智谱 AI 国内版本 API 之间运行一个 LLM Proxy,将 rig-core 生成的请求参数清洗成智谱 AI 国内版本 API 能处理的格式;这方面开源的 LLM Proxy 比较多,可以拿来尝试。
从个人角度,我更倾向第 3 种解决方案。不过需要注意的是,有的 LLM Proxy,虽然支持智谱 AI 国内版本 API,却不一定能对 rig-core 生成的请求完成清洗,因为这需要处理两者之间的细微差别。
最终的解决方案是自己实现了一个本地运行的 LLM Proxy:专门清洗 IronClaw 生成的请求,然后转发给智谱 AI 国内版本的 API。虽然增加了 IronClaw 运行依赖,但是 Proxy 足够轻量,能快速满足自己当前的使用场景。代码已经上传 github 个人仓库,有需要的可以自行下载、编译:https://github.com/cuiguoke/glm-local-proxy
4. 写在最后
从明确问题根因,到实现 本地运行的 LLM Proxy,整个适配过程,跌宕起伏,一直到凌晨才最终搞定。事后来看,重要的转折点并没有太多,主要是解决智谱 AI 国内和国外版本 API 规格不一致带来的适配问题。另外,LLM Proxy 的实现,其实也不麻烦,关键在于明确 rig-core 生成的格式与智谱 AI 国内版 API 之间的差别。项目 README 提供了更详细的信息,感兴趣的可以看一下,这里就不再展开(如果有需要的话,我可以再写一篇单独介绍这个 LLM Proxy 的实现过程)。
最后吐槽一下,从老派网站界面、到国内外不统一的站点域名、到两套不统一的 API 规格,可能智谱专注在 LLM 的突破上,但是给我一种不重视品牌建设的感觉。当然,模型好不好用,我先白嫖为敬,😄。