Android Studio 把原生 Agent 能力做进来之后,很多人第一反应都是:能不能接自己的第三方 Provider?
能配。但真用起来,尤其是走「反重力 / CPA 一类反代」去打 Gemini 模型时,协议对不上的问题会很烦人。我后来干脆写了个本地桥接插件:as-api-bridge-plugin。
这篇文章把坑和做法讲清楚。
先说结论
Android Studio 新版 AI / Agent 侧,更偏向 OpenAI Responses API(/v1/responses)那一套。
而市面上大量第三方网关、反代、OneAPI / NewAPI、以及很多 Gemini 兼容层,实际更稳的是 Chat Completions(/v1/chat/completions)流式协议。
两边差的不只是路径名:
- 请求体结构不同
- 流式 SSE 事件模型不同
- tool / function call 的表达不同
- 有些上游对 JSON Schema 更挑,空
enum直接 400 - 有些网关首帧
content: null,Studio 界面会直接打出字面量null
所以你在 Studio 里配了第三方 Base URL 和 Key,不等于就能顺滑用。配置能通,协议不一定通。
我碰到的具体场景
目标很简单:
- Android Studio 原生 Agent 继续用
- Provider 走第三方 / 自建网关
- 上游实际是反代出来的 Gemini 模型
结果是:
- 有的请求直接 400
- 有的能出字,但流式体验差
- 有的 tool call 直接炸
- 有的首包把
null渲染到聊天区
根因基本可以归成一句话:
Studio 按 Responses 说话,反代按 Chat 听;中间缺一层翻译。
为什么「反代 Gemini」更容易踩坑
反代本身没问题,问题在「兼容层」往往只做了最常见路径。
很多反代 / 聚合网关优先保证:
/v1/chat/completions- 标准
messages - 标准
tool_calls - SSE
data: {choices:[{delta:...}]}
而 Android Studio Agent 侧更像在走:
/v1/responses- 另一套 input / output item
- 另一套 function_call 事件流
Gemini 这条线再叠一层:
- schema 校验更严
- 空
enum: []、空required更容易被拒 - 流式首帧行为不一定按 OpenAI 官方习惯来
于是就出现一种很典型的状态:
- Postman 测 Chat 接口正常
- Studio Agent 一开就不正常
- 你以为是 Key 或模型名错了,其实是协议形态不对
我的解法:本地桥,不改上游、不改模型名
插件名字叫 AS API Bridge,仓库在:
https://github.com/lategege/as-api-bridge-plugin
思路很直接:
| |
它干的事主要有五块:
1. Responses → Chat 的实时流式转换
命中转换规则的模型:
- 吃 Studio 的
/v1/responses - 转成上游
/v1/chat/completions,并且强制真流式 - 把上游 Chat SSE 再转回 Responses SSE 给 Studio
重点是 流式管道,不是等整段生成完再回包。这样首字延迟(TTFT)会好很多,打字机效果也正常。
2. 按模型规则决定「转」还是「透传」
不是所有模型都需要转换。
你可以配通配规则,比如:
| |
- 命中:走协议转换
- 未命中:原样透传
模型名本身不改。这个很重要,避免网关侧按模型名做的路由被你手贱改坏。
3. Tool Calls 映射
Responses 里的:
function_callfunction_call_output
会映射到 Chat 侧更常见的:
tool_callsrole: "tool"
Agent 场景如果没有这层,工具调用基本用不舒服。
4. JSON Schema 清洗
针对 Gemini / 部分网关常见的 400:
- 空
enum: [] - 空
required - 一些不友好的 schema 边角
请求发出去前先洗一遍,少踩无意义的 Bad Request。
5. 首帧 null 过滤
有些兼容网关第一帧会给:
| |
Studio 如果直接当文本处理,聊天区就可能冒出字符串 null。
插件里对 JSONObject.NULL 和 "null" 做了过滤。这个修在 1.2.2。
在 Android Studio 里怎么配
安装插件
目前按源码构建:
| |
产物大概在:
| |
然后:
Settings → Plugins → 齿轮 → Install Plugin from Disk... → 选 zip → 重启。
插件设置
Settings → Tools → AS API Bridge
常见配置:
| 项 | 作用 | 例子 |
|---|---|---|
| Listen Port | 本地监听端口 | 8088 |
| Upstream Base URL | 上游网关地址 | http://127.0.0.1:3000/v1 |
| API Key (Fallback) | 客户端没带 Key 时兜底 | sk-xxx |
| Need Protocol Conversion Models | 哪些模型要转换 | gemini* |
| Auto-start on IDE launch | 启动 IDE 自动开桥 | 建议开 |
菜单里也可以手动:
Tools → AS API Bridge → Start Bridge / Stop Bridge
Studio 的 Provider 指到本地桥
关键点:
- Android Studio 的自定义 Provider / Base URL,指到桥:
http://127.0.0.1:8088/v1 - 桥的 Upstream,再指到你的反代 / 网关真实地址
不要让 Studio 直连反代,然后指望反代同时完美兼容 Responses。把翻译放到本地,问题面更小,也更好排障。
推荐的排障顺序
如果还是不通,按这个顺序查:
桥有没有起来
本地端口通不通,插件是否 Start 成功。Studio 是不是打到了桥
Base URL 是否真的是127.0.0.1:端口,别还指着上游。模型名是否命中转换规则
Gemini 相关建议先写gemini*。不确定就临时用*验证,确认后再收窄。上游 Chat 接口本身是否健康
用同一 Key 直接打/v1/chat/completions。上游 Chat 都不稳,桥也救不了。是不是 schema / tools 400
看返回体。若是空 enum 一类,确认你用的是带清洗逻辑的版本。是不是首包 null
升级到 1.2.2+。
这插件解决了什么,没解决什么
解决了
- Studio Responses 协议和常见 Chat 反代之间的主路径鸿沟
- Gemini 兼容层上的一部分 schema 拒识
- tool call 形态差异
- 流式体验和首帧 null 显示问题
- 「部分模型要转、部分模型要透传」的现实需求
没打算伪装成
- 官方完美兼容层
- 所有 Provider 的万能适配器
- 对任何非标反代都 100% 语义等价
协议翻译天然有损。尤其是 Responses 和 Chat 在工具调用、输出分片、usage 字段上并不一一镜像。目标是:让 Android Studio 原生 Agent 在第三方 Gemini 反代场景下能稳定工作,不是证明两边协议数学上全等。
为什么我选择做 IDE 插件,而不是外部 nginx
也考虑过外部反代脚本。最后还是做成 Android Studio / IntelliJ 插件,原因很实际:
- 本地环回,链路短
- 跟 IDE 生命周期绑在一起,开关方便
- 设置页直接改规则,不用另维一套服务
- 鉴权可以优先透传 Studio 自己带的 Header
- 出问题看日志和端口都在本机,心智负担小
对个人开发者来说,这种「IDE 内一小桥」比再维护一个常驻转换服务更省事。
使用上的一点建议
先只转 Gemini
规则从gemini*开始,别一上来*全开,不好定位。上游尽量选真正支持 stream 的 Chat 接口
桥可以转协议,不能把假流式变真流式。模型名保持网关侧原名
桥的设计就是不改模型名,只改协议。Agent 场景优先验证 tool call
纯聊天通了不算完,带工具的链路才是 Agent 真正会炸的地方。把桥当适配层,不当业务层
限流、计费、模型路由还是放上游网关。
仓库和版本
- 项目:lategege/as-api-bridge-plugin
- 当前 README 对应版本:1.2.2
- 技术栈:Kotlin + IntelliJ Platform Plugin
- 兼容基线:Android Studio / IntelliJ 2024.1+(
since-build 241)
如果你也在用 Android Studio 原生 Agent,又想接自己的第三方 Provider,尤其是 Gemini 反代,被 /responses 和 /chat/completions 来回抽,可以试试这层本地桥。
有问题直接去仓库提 issue。我这篇更偏复盘和排障思路;安装细节以仓库 README 为准。