快速开始
注册账号之后跑一行脚本,它会把接口地址和密钥写进 `~/.config/agiplan/env`,并告诉你怎么让它生效。然后打开你原来用的编程工具,照常用就行。
# 交互式(会提示你粘贴密钥) curl -fsSL https://agiplan.dev/setup -o setup.sh && sh setup.sh # 或者一行搞定 AGIPLAN_API_KEY=ap_live_xxx sh -c "$(curl -fsSL https://agiplan.dev/setup)" . ~/.config/agiplan/env # 让它在当前 shell 生效
不想跑脚本也完全可以。 手动配两个环境变量效果一样,只是多套配置切换时麻烦一点。
export API_BASE_URL="https://agiplan.dev/v1" export API_KEY="ap_live_••••••••••••••••"
脚本到底做了什么
`curl | sh` 是个需要你信任我们的动作,所以我们把它做了什么逐条写出来。脚本本身以纯文本返回,浏览器直接打开 https://agiplan.dev/setup 就能读全文——你正要跑的和你看到的是同一份。
- 把接口地址和密钥写进 `~/.config/agiplan/env`,目录 700、文件 600,写之前先收紧 umask——否则密钥会在默认权限下短暂可读。不碰系统目录,不要 sudo。
- 同一个文件里还会导出 `OPENAI_BASE_URL` / `OPENAI_API_KEY`。多数工具认这两个名字,所以加载之后它们会走我们这里——这是有意的,但你应该知道。
- 不改你的 `.bashrc` / `.zshrc`——要不要开机加载由你决定,那是你的文件。
- 不装任何二进制。 命令行工具还没发布,脚本不假装装了什么——一个假装成功的安装脚本,比一个说清现状的危险得多。
- 不探测你装了什么工具,不改任何别的配置文件,不上传任何本机信息。
- 密钥不以 `ap_live_` 开头就直接退出,一个文件都不写。
脚本不改你的 shell 配置,只写 `~/.config/agiplan/env` 这一个文件——所以也没有什么「备份」需要还原。`agiplan uninstall` 就是删掉那一个文件,而且默认只列出要删什么,加 `--yes` 才真删。
认证
三套协议都用 Bearer token。密钥在控制台创建,明文只显示一次——我们只存哈希,找回不了,只能轮换。
Authorization: Bearer ap_live_xxxxxxxxxxxx
密钥可以设作用域、绑定到自定义模型、限制 IP、设过期时间。轮换比吊销更顺手是有意的设计:让正确的操作成为最省事的操作。旧密钥可以设宽限期,不会一刀切断。
兼容哪几套协议
同一个 base URL 下同时提供三套主流协议的兼容端点。用哪套取决于你的 SDK,不需要告诉我们。
| 端点 | 对应协议 | 说明 |
|---|---|---|
| /v1/chat/completions | OpenAI 风格 | 含 stream、tools、response_format |
| /v1/messages | Anthropic 风格 | 含 stream、tools、system |
| /v1/responses | OpenAI Responses | 含 stream、tools;`previous_response_id` 可以不重传上下文 |
| /v1/models | 三者通用 | 返回你当前可用的模型与自定义模型 |
| /v1/quota | 三者通用 | 不发起调用就能读三个窗口的剩余比例,需要 usage:read |
凡是允许改接口地址的工具都能直接用,不用装插件,不用改业务代码。各套协议的请求体差异我们在网关内部消化,你按自己熟悉的那套写就行。
`/v1/responses` 是三者里唯一有状态的:用 `previous_response_id` 续接时,那轮对话的消息内容会存在我们这边,最长 5 小时,之后由定时任务删除。不用这个参数就不产生这项存储。状态放在我们自己这边而不是上游,是为了让灾备切换对你透明——代价就是这 5 小时,隐私政策里也写着同一句。
还有几个端点没列在上面:`/v1/device/code`、`/v1/device/token`、`/v1/keys/current` 是命令行工具的传输层(`agiplan login` / `agiplan use` 用),不是给你直接调的。`/v1/traces/{追踪号}` 见下面的溯源一节。
发图片只支持内嵌的 `data:` URL(base64),远程 `http(s)` 图片链接还没有做。 标了「视觉」的模型确实能看图,但那张图得由你在请求体里带上;给一个 URL 的话,网关需要替你下载再转 base64,还要防住内网地址(SSRF),这件事我们没做完,所以现在会把它降级成一句文本占位。降级时响应头里会有 `x-agiplan-dropped-params: content[].image_url`——我们不想让你以为模型看过了那张图。音频和文件类型同理,当前直接丢弃并在同一个头里列出。
指定模型,或交给 Prism
同一个 `model` 字段两种用法:填具体模型就固定用它;填 `auto` 或你自己的自定义模型名就交给 Prism 按任务难度动态调度。
"model": "frontier-a" // 固定用这一个,Prism 不介入 "model": "auto" // 交给 Prism 自动挑 "model": "my-coding-model" // 你自己建的自定义模型
想在单次请求里覆盖难度判断,加一个请求头即可:`X-AGIPlan-Difficulty: 0.9`。全部模型与系数见 /models 页。
流式
三套协议的流式都按各自的原生格式返回 SSE,行为与你熟悉的一致。需要注意的只有一点:计费在流结束时结算。
请求进来时我们先按最坏情况预扣一笔额度——输出按你声明的 `max_tokens` 算,输入按请求里已经能数出来的 token 算。流正常结束、客户端中断、上游报错都走同一个收尾逻辑,用真实用量回填、多退少补。所以中途断开不会被按最坏情况扣钱。
配额响应怎么读
每个响应都带三个窗口的剩余比例和重置时间。给比例不给数量是刻意的:足够你决定要不要退避,而不用把套餐的绝对额度写进每一个响应头。要逐笔核对用了多少,看控制台的用量明细或月度对账单——那边是绝对值。触发限流时返回 429,并明确告诉你是哪个窗口满了、什么时候恢复——429 上同样带这三个头,那正是你最需要它们的时候。密钥无效(401)时没有这几个头:那时我们还不知道该报谁的额度。
X-AGIPlan-Quota-Session: remaining=68%; resets=2026-08-15T09:00:00.000Z X-AGIPlan-Quota-Week: remaining=34%; resets=2026-08-18T00:00:00.000Z X-AGIPlan-Quota-Month: remaining=81%; resets=2026-09-05T00:00:00.000Z Retry-After: 1740 // 秒,仅在 429 时出现
错误码与重试
| 状态码 | 错误码 | 含义 | 该怎么做 |
|---|---|---|---|
| 401 | invalid_api_key | 密钥无效或已吊销 | 换一个密钥,不要重试 |
| 404 | model_not_found | 生图端点:没有这个生图模型 | 看 /v1/models 里 kind 为 image 的那些 |
| 503 | size_unavailable | 生图端点:此刻没有线路支持这个尺寸 | 换个尺寸,或稍后重试。失败的调用不计费 |
| 401 | key_rotation_required | 这把密钥超过了组织设定的轮换周期 | 在控制台轮换一把新的 |
| 400 | capability_unavailable | 请求声明的能力(视觉、工具…)当前没有任何模型支持 | **重试不会改变结果。** 去掉这项要求,或等我们接上支持它的上游 |
| 400 | context_too_small | 你按真名点名的那个模型,上下文窗口小于这次请求要求的 `min_context_k` | **重试不会改变结果。** 缩短上下文,或换一个窗口更大的模型——/models 上每个模型都标了窗口 |
| 400 | upstream_rejected_request | 上游拒绝了请求本身(参数或内容不被接受) | **换模型也一样。** 检查请求;确认合法的话把追踪号发给我们 |
| 400 | request_exceeds_mix_cap | 按最坏情况估算超过了你的自定义模型里设的 `max_cu_per_request` | **是你自己设的上限。** 调小 max_tokens、缩短上下文,或改高那个值 |
| 403 | insufficient_scope | 这把密钥没有对应作用域 | 换一把有权限的密钥 |
| 403 | no_subscription | 这个账号没有生效中的订阅 | 去控制台订阅或续费 |
| 429 | quota_exceeded | 某个窗口的额度用尽 | 按 Retry-After 等待,或换到消耗更低的能力组 |
| 429 | concurrency_exceeded | 同时进行的请求达到档位上限 | 等一个跑完再发。**升档能解决,充值不能**——它和额度用尽是两回事 |
| 451 | region_not_available | 该能力组没有向你所在地提供的模型 | **重试不会改变结果。** 换一个能力组,或见 /models 的地域列 |
| 503 | no_supply_configured | 这个能力组当前没有接入任何上游 | **重试不会改变结果。** 换一个能力组,或者用 auto——/models 页上标着每一组此刻有几条线在跑 |
| 503 | policy_unavailable | 你调用的自定义模型现在编译不过——多半是它钉死的某个模型下架了 | 错误信息里写着是哪个池、哪个模型。去控制台「自定义模型」里把那个池改成别的模型或一个档位,保存后立刻生效 |
| 503 | model_unavailable | 你按真名点名的那个模型当前没有可用线路 | **我们不会替你换一个模型**——你写死了真名,换一个回去等于骗你。稍后重试,或改用能力组的名字(例如 `domestic-a`),那样网关会在同能力的线之间自动切换 |
| 503 | all_upstreams_failed | 该能力组的模型都试过了,均不可用 | 响应体里带完整尝试记录,可直接报障。失败的尝试不计费 |
| 流式中 | upstream_interrupted | 已经开始输出之后上游断了。此时状态码早已是 200,只能在流里发一个错误事件,**不会再有正常的结束标记** | **已经输出的部分按实际 token 计费。** 这时换模型会让回答前后不一致,所以网关不做灾备;请重新发起请求 |
5xx 不需要你自己重试。 灾备已经在网关内部做完了——你看到 503 说明同能力组的模型都试过了。客户端再重试一遍只会加重上游压力。
这张表列的是网关真的会返回的码。 额度用尽仍然是 429,开了按量续用的话请求会改从余额扣、不会被拒;余额也不够时还是 429,错误信息里会说清是哪一样没过(没开、到上限、余额不够)。有测试钉着这张表和代码里的错误码一致。
请按 `code` 判分支,不要按 `message`。 `code` 是稳定契约,改动会走公告;`message` 是给人读的一句话,措辞会变。还有一件该说清楚的:`message` 目前只有中文——站点、文档、控制台、通知邮件都已经是双语的,唯独这一句还没有。`code` 和 HTTP 状态码不受影响,所以按它判分支的代码不会因为语言而出问题。
追踪号与溯源
成功的响应都带追踪号和实际调用的模型标识。发生过灾备切换时会额外标记出来。被拒的响应没有追踪号——那次调用没有落成一条用量记录,给一个查不到的号比不给更糟。503 的时候链路直接放在响应体的 `attempts` 里,报障时把它一起发来就行。
X-AGIPlan-Trace: tr_01J8FQ3M7X X-AGIPlan-Model-Actual: frontier-b X-AGIPlan-Failover: true
用你自己的密钥就能拉完整链路:`GET /v1/traces/{id}`,返回结构化 JSON,保留 30 天。报障时把追踪号发给我们,客服能一键定位,不用你复述发生了什么。
命令行工具
先说一件更要紧的事:命令行工具目前还没有发布渠道。 不在 npm 上,没有下载地址,接入脚本也明说了「不装任何二进制」。所以下表标的是实现状态——哪几条写完了、哪条决定不做——不是「你现在敲下去能不能跑」。今天敲 `agiplan` 得到的是 command not found,而那不是你的配置问题。
「没做」「坏了」「做了但你拿不到」是三件不同的事,混在一起最费时间。没做的命令敲下去会明说「还没有实现」;而整个工具还没发布这件事,只能写在这里。发布方式(npm、单文件二进制、还是跟着 setup 脚本装)定下来之后,这一段会换成安装说明。
| 命令 | 作用 | 状态 |
|---|---|---|
| agiplan login | 设备码授权:浏览器里确认,密钥直接送回终端 | 可用 |
| agiplan status | 三个窗口的剩余比例、档位与重置时间 | 可用 |
| agiplan models | 可用模型与能力组 | 可用 |
| agiplan use <名字> | 给当前密钥绑定自定义模型,--none 解绑 | 可用 |
| agiplan env | 输出可 eval 的环境变量 | 可用 |
| agiplan keys rotate <配置> | 轮换密钥 | 不做,见下 |
| agiplan uninstall | 删掉 setup 写的配置文件(--yes 才真删) | 可用 |
`keys rotate` 我们决定不做,理由是安全不是工作量。 一把能给自己签发后继者的密钥会熬过吊销:密钥泄露之后攻击者先轮换一次,你在控制台吊销的是旧的那把,新的那把还活着,而你根本不知道它存在。轮换只走浏览器确认过的路径——控制台里点轮换,或者重新跑一遍 `agiplan login`。
支持项目级配置文件 `.agiplan`,里面只存配置名不存密钥,所以进 git 也安全。
从别处迁过来
如果你已经在用官方 SDK 或别的聚合服务,需要改的只有两处:接口地址和密钥。模型名如果不一致,用自定义模型名做一层映射就行,业务代码不用动。
有一处行为和官方不一样,值得单独说:不传 `max_tokens` 时我们会补一个 4096,并且真的发给上游。 官方那边不传意味着「用模型的上限」,所以同一段代码打过来,长回答会在 4096 处截断、带着 `finish_reason: "length"` 回来——看起来像模型自己停的。这么做是因为这个数同时决定预扣多少额度:不设上限就没法在调用前判断你的额度够不够。补了的时候响应头里有 `x-agiplan-max-tokens-default: 4096`,自己传一个就按你传的走。
还有一处会影响账单,写在这里而不是留给你自己去发现:请求需要某项能力、而你指定的能力组给不了时,我们会把它抬到能给的那一组,并按抬到的那一组计费。 最常见的是发图:自定义模型里选了「快速响应组」,而那一组没有视觉模型,于是这次调用落到国产旗舰组——输出系数从 0.06 变成 0.18,同样的 token 数三倍的 CU。往上抬是因为另一条路是直接失败,而失败对你更没用;只往上不往下,降级永远不会静默发生。每次响应都带 `x-agiplan-billed-group`,写的就是实际计费的那一组,和用量明细里的记录一致。不想被抬走就别在该自定义模型里发它给不了的东西,或者直接选一个本来就支持的组。
- 先建一个自定义模型,把你原来用的模型名当它的名字,路由到对应的能力组。
- 改 base URL 和 key,跑一遍你的测试。
- 对照 /models 页确认系数,用控制台的用量明细核对第一天的账。
第三步别跳过。 第一天就把账对上,比事后发现对不上再来查要省事得多——我们也希望你养成这个习惯。
文档还在持续补充。缺什么、哪里写得不清楚,发一封邮件到 support@agiplan.dev,我们会改并记在更新日志里。