feat: DashScope 模型代理服务初始提交
支持 Anthropic 和 OpenAI 兼容协议的模型池反代,自动切换免费额度耗尽的模型。
This commit is contained in:
@@ -0,0 +1,455 @@
|
||||
# Q/A 与方案决策记录
|
||||
|
||||
本文记录本地 DashScope 反代服务的需求讨论、关键 Q/A、方案选择和最终共识,方便后续继续维护。
|
||||
|
||||
## 1. 初始需求
|
||||
|
||||
用户有多个 DashScope Anthropic 兼容模型 ID,希望对外只暴露一个固定 URL 和一个代理 key。
|
||||
|
||||
上游地址:
|
||||
|
||||
```text
|
||||
https://dashscope.aliyuncs.com/apps/anthropic
|
||||
```
|
||||
|
||||
典型免费额度耗尽错误:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "xxx",
|
||||
"code": "AccessDenied",
|
||||
"message": "The free tier of the model has been exhausted. If you wish to continue access the model on a paid basis, please disable the \"use free tier only\" mode in the management console."
|
||||
}
|
||||
```
|
||||
|
||||
判断条件:
|
||||
|
||||
```text
|
||||
HTTP 状态码 = 403
|
||||
code = AccessDenied
|
||||
message 包含 free tier 和 exhausted
|
||||
```
|
||||
|
||||
最终目标:
|
||||
|
||||
1. 客户端只配置一个本地代理 URL。
|
||||
2. 客户端只使用一个代理 key。
|
||||
3. 真实 DashScope API Key 不暴露给客户端。
|
||||
4. 模型进入冷却时自动切换,不需要手动改 model。
|
||||
|
||||
## 2. 技术选型
|
||||
|
||||
### Q:用什么框架搭建?
|
||||
|
||||
A:用户指定使用 Hono.js。
|
||||
|
||||
最终方案:
|
||||
|
||||
```text
|
||||
Node.js + TypeScript + Hono.js
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- 项目小,Hono 足够轻量。
|
||||
- Node 20+ 原生支持 `fetch` 和 Web Stream。
|
||||
- 适合做 Anthropic 兼容 HTTP 反代。
|
||||
|
||||
## 3. 项目位置
|
||||
|
||||
### Q:放在哪里?
|
||||
|
||||
A:单独新建项目,不放到原 uni-app 项目里。
|
||||
|
||||
最终路径:
|
||||
|
||||
```text
|
||||
proxy-server
|
||||
```
|
||||
|
||||
## 4. 对外接口
|
||||
|
||||
### Q:本地对外 URL 是什么?
|
||||
|
||||
A:默认根据 `.env` 的 `PORT` 决定。
|
||||
|
||||
当前给 Pi CLI 使用的配置:
|
||||
|
||||
```env
|
||||
PORT=3300
|
||||
PROXY_API_KEY=sk-001
|
||||
```
|
||||
|
||||
本地 URL:
|
||||
|
||||
```text
|
||||
http://localhost:3300
|
||||
```
|
||||
|
||||
Anthropic Messages 接口:
|
||||
|
||||
```text
|
||||
http://localhost:3300/v1/messages
|
||||
```
|
||||
|
||||
客户端请求头二选一:
|
||||
|
||||
```http
|
||||
Authorization: Bearer sk-001
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```http
|
||||
x-api-key: sk-001
|
||||
```
|
||||
|
||||
## 5. 代理是否修改 Prompt
|
||||
|
||||
### Q:代理有没有加提示词、改 system 或 messages?
|
||||
|
||||
A:没有。
|
||||
|
||||
当前代理只修改请求体里的:
|
||||
|
||||
```json
|
||||
"model"
|
||||
```
|
||||
|
||||
不会修改:
|
||||
|
||||
```text
|
||||
system
|
||||
messages
|
||||
tools
|
||||
tool_choice
|
||||
thinking
|
||||
max_tokens
|
||||
temperature
|
||||
任何 prompt 内容
|
||||
```
|
||||
|
||||
Pi CLI 中模型回答“我是 Claude,由 Anthropic 开发”不代表代理加了提示词。更可能来自:
|
||||
|
||||
1. Pi CLI 自己的系统提示词和代理框架上下文。
|
||||
2. DashScope Anthropic 兼容接口下模型的身份回答习惯。
|
||||
|
||||
## 6. 单个真实 key + 多模型阶段
|
||||
|
||||
### Q:最早版本怎么切换?
|
||||
|
||||
A:最早版本只有一个真实 DashScope key,多个 `MODEL_IDS`。现在即使只有一个真实 key,也统一写在 `DASHSCOPE_API_KEYS` 里。
|
||||
|
||||
配置示例:
|
||||
|
||||
```env
|
||||
DASHSCOPE_API_KEYS=sk-xxx
|
||||
MODEL_IDS=qwen3.7-max,qwen-plus,qwen-max
|
||||
```
|
||||
|
||||
切换逻辑:
|
||||
|
||||
1. 使用当前模型转发。
|
||||
2. 如果命中免费额度耗尽 403,冷却这个模型。
|
||||
3. 同一个请求改用下一个模型重试。
|
||||
4. 所有模型都不可用时返回 `503`。
|
||||
|
||||
## 7. 多 Key + 多模型阶段
|
||||
|
||||
### Q:多 key 的情况下怎么切?
|
||||
|
||||
A:所有 key 共用同一组 `MODEL_IDS`。切换规则是 key 优先。
|
||||
|
||||
最终配置:
|
||||
|
||||
```env
|
||||
DASHSCOPE_API_KEYS=sk-key-1,sk-key-2,sk-key-3
|
||||
MODEL_IDS=model-a,model-b,model-c
|
||||
```
|
||||
|
||||
最终切换规则:
|
||||
|
||||
1. 优先使用当前 key。
|
||||
2. 当前 key 下某个模型触发免费额度耗尽 403,只冷却这个 `key + model` 组合。
|
||||
3. 继续尝试当前 key 下的下一个模型。
|
||||
4. 只有当前 key 下所有模型都不可用,才切换到下一个 key。
|
||||
5. 所有 key+model 组合都不可用时返回 `503`。
|
||||
|
||||
这是本项目最重要的调度共识。
|
||||
|
||||
## 8. 冷却时间
|
||||
|
||||
### Q:冷却多久?
|
||||
|
||||
A:用户确认冷却时间为 1 个月。
|
||||
|
||||
最终配置:
|
||||
|
||||
```env
|
||||
MODEL_COOLDOWN_SECONDS=2592000
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
```text
|
||||
2592000 秒 = 30 天
|
||||
```
|
||||
|
||||
## 9. 状态持久化选择
|
||||
|
||||
### Q:为什么引入持久化状态?
|
||||
|
||||
A:服务重启后不能丢失冷却状态。
|
||||
|
||||
如果只用内存,重启后所有模型都会重新变成可用,可能继续打到已经进入免费额度冷却期的 key+model。
|
||||
|
||||
### Q:选什么存储?
|
||||
|
||||
A:最终选 JSON 状态文件。
|
||||
|
||||
原因:
|
||||
|
||||
- 本地部署,不需要额外安装 MySQL/Postgres/Redis。
|
||||
- 只有冷却状态和调度游标,不需要关系型查询能力。
|
||||
- 一个文件即可持久化,适合本地工具。
|
||||
- 避免 `better-sqlite3` 原生 binding 在 Windows 上安装或打包失败。
|
||||
- 默认路径清晰,方便备份和检查。
|
||||
|
||||
最终配置:
|
||||
|
||||
```env
|
||||
STATE_PATH=./data/proxy-state.json
|
||||
```
|
||||
|
||||
## 10. 状态文件隐私原则
|
||||
|
||||
### Q:状态文件会存真实 DashScope key 吗?
|
||||
|
||||
A:不会。
|
||||
|
||||
状态文件只存:
|
||||
|
||||
```text
|
||||
SHA-256(key)
|
||||
```
|
||||
|
||||
也就是 `key_hash`。
|
||||
|
||||
真实 key 只存在 `.env` 里。
|
||||
|
||||
## 11. 状态文件结构
|
||||
|
||||
### Q:`modelState` 存什么?
|
||||
|
||||
A:每条记录对应一个 DashScope key 与 `MODEL_ID` 组合。
|
||||
|
||||
例如:
|
||||
|
||||
```env
|
||||
DASHSCOPE_API_KEYS=key1,key2
|
||||
MODEL_IDS=modelA,modelB,modelC
|
||||
```
|
||||
|
||||
会生成:
|
||||
|
||||
```text
|
||||
2 * 3 = 6 条 modelState 记录
|
||||
```
|
||||
|
||||
字段详见:
|
||||
|
||||
```text
|
||||
docs/state-file.md
|
||||
```
|
||||
|
||||
### Q:`runtimeState` 存什么?
|
||||
|
||||
A:只存调度游标,不存 key、不存 prompt、不存错误详情。
|
||||
|
||||
当前记录类型:
|
||||
|
||||
```text
|
||||
key_cursor
|
||||
model_cursor:<key_hash>
|
||||
```
|
||||
|
||||
含义:
|
||||
|
||||
- `key_cursor`:当前优先使用 `.env` 里第几个 `DASHSCOPE_API_KEYS` 条目。
|
||||
- `model_cursor:<key_hash>`:某个 key 下当前优先使用 `MODEL_IDS` 里的第几个模型。
|
||||
|
||||
## 12. 游标策略共识
|
||||
|
||||
### Q:为什么 `model_cursor` 不直接存 modelId?
|
||||
|
||||
A:当前版本先按顺序来,使用游标绑定 `.env` 中的配置顺序。
|
||||
|
||||
共识:
|
||||
|
||||
1. 当前版本保持 `key_cursor` 和 `model_cursor:<key_hash>`。
|
||||
2. 它们都依赖 `.env` 中 `DASHSCOPE_API_KEYS` 和 `MODEL_IDS` 的顺序。
|
||||
3. 不要随便调整这两个列表的顺序。
|
||||
4. 如果后续调整顺序,建议同时删除或重置 `data/proxy-state.json`。
|
||||
5. 后续如果需要更稳,再升级为 `current_key_hash` 和 `current_model_id:<key_hash>`。
|
||||
|
||||
## 13. Pi CLI 接入
|
||||
|
||||
### Q:怎么让 Pi CLI 使用本地代理?
|
||||
|
||||
A:在 `~/.pi/agent/models.json` 增加本地 provider。
|
||||
|
||||
Provider 名称:
|
||||
|
||||
```text
|
||||
local-dashscope-proxy
|
||||
```
|
||||
|
||||
Base URL:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:3300
|
||||
```
|
||||
|
||||
API 类型:
|
||||
|
||||
```text
|
||||
anthropic-messages
|
||||
```
|
||||
|
||||
代理 key:
|
||||
|
||||
```text
|
||||
sk-001
|
||||
```
|
||||
|
||||
同时在 `~/.pi/agent/settings.json` 设置默认 provider:
|
||||
|
||||
```text
|
||||
defaultProvider = local-dashscope-proxy
|
||||
defaultModel = qwen3.7-max
|
||||
```
|
||||
|
||||
使用方式:
|
||||
|
||||
```bash
|
||||
pi
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```bash
|
||||
pi --provider local-dashscope-proxy --model qwen3.7-max
|
||||
```
|
||||
|
||||
## 14. 清理旧 Pi Provider
|
||||
|
||||
### Q:为什么删除 `dashscope` provider 下的两个模型?
|
||||
|
||||
A:用户希望 Pi CLI 里不再出现旧的直接 DashScope provider:
|
||||
|
||||
```text
|
||||
qwen3.7-max [dashscope]
|
||||
qwen3.7-max[1M] [dashscope]
|
||||
```
|
||||
|
||||
已删除 `~/.pi/agent/models.json` 中的 `dashscope` provider,保留本地代理 provider。
|
||||
|
||||
## 15. 当前模型列表
|
||||
|
||||
当前 `MODEL_IDS` 共 14 个:
|
||||
|
||||
```text
|
||||
qwen3.7-max-2026-06-08
|
||||
qwen3.7-plus-2026-05-26
|
||||
qwen3.7-plus
|
||||
qwen3.7-max-preview
|
||||
qwen3.7-max-2026-05-17
|
||||
qwen3.7-max
|
||||
qwen3.7-max-2026-05-20
|
||||
qwen3.6-27b
|
||||
qwen3.6-max-preview
|
||||
qwen3.6-flash
|
||||
qwen3.6-35b-a3b
|
||||
qwen3.6-flash-2026-04-16
|
||||
qwen3.6-plus
|
||||
qwen3.6-plus-2026-04-02
|
||||
```
|
||||
|
||||
数据库已同步初始化这些模型状态。
|
||||
|
||||
## 16. 请求日志
|
||||
|
||||
### Q:Hono 是否打印请求日志?
|
||||
|
||||
A:已增加统一请求日志中间件。
|
||||
|
||||
日志示例:
|
||||
|
||||
```text
|
||||
[request] GET /health status=200 duration=2ms bytes=- proxyKey=- model=- attempts=- ua="curl/8.7.1"
|
||||
```
|
||||
|
||||
成功代理 `/v1/messages` 时会打印:
|
||||
|
||||
```text
|
||||
proxyKey=<key hash 前缀>
|
||||
model=<实际使用的模型 ID>
|
||||
attempts=<本次请求尝试次数>
|
||||
```
|
||||
|
||||
不会打印:
|
||||
|
||||
```text
|
||||
Authorization
|
||||
x-api-key
|
||||
DASHSCOPE_API_KEYS
|
||||
请求 body
|
||||
messages / prompt
|
||||
```
|
||||
|
||||
## 17. 当前文档
|
||||
|
||||
使用说明:
|
||||
|
||||
```text
|
||||
docs/usage.md
|
||||
```
|
||||
|
||||
状态文件说明:
|
||||
|
||||
```text
|
||||
docs/state-file.md
|
||||
```
|
||||
|
||||
Q/A 与方案决策记录:
|
||||
|
||||
```text
|
||||
docs/qa-and-decisions.md
|
||||
```
|
||||
|
||||
## 18. OpenAI Chat Completions 入口
|
||||
|
||||
### Q:为什么又增加 OpenAI 协议入口?
|
||||
|
||||
A:Pi CLI 的官方 DeepSeek provider 使用 `openai-completions`,说明 Pi 对 OpenAI Chat Completions 兼容链路支持成熟。为了让本地代理也能按同一类方式接入 Qwen,新增:
|
||||
|
||||
```text
|
||||
POST /v1/chat/completions
|
||||
GET /v1/models
|
||||
```
|
||||
|
||||
上游默认使用:
|
||||
|
||||
```text
|
||||
OPENAI_UPSTREAM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
```
|
||||
|
||||
当前保留原有 Anthropic Messages 入口:
|
||||
|
||||
```text
|
||||
POST /v1/messages
|
||||
```
|
||||
|
||||
两条链路共用同一套 DashScope key 池、模型池、冷却状态和免费额度耗尽切换逻辑。
|
||||
|
||||
服务端是反代转发,不需要安装 `openai` SDK;`openai` SDK 只适合客户端调用代理时使用。
|
||||
@@ -0,0 +1,116 @@
|
||||
# 状态文件说明
|
||||
|
||||
本服务使用 JSON 文件保存模型冷却状态。这样服务重启后,已经进入冷却期的 DashScope key 与 `MODEL_ID` 组合不会丢失。
|
||||
|
||||
默认状态文件路径:
|
||||
|
||||
```env
|
||||
STATE_PATH=./data/proxy-state.json
|
||||
```
|
||||
|
||||
状态文件不会保存明文 DashScope API Key。代码会对每个 key 计算 SHA-256,并把结果写入 `modelState`。
|
||||
|
||||
## 文件结构
|
||||
|
||||
状态文件是一个版本化 JSON 对象:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"modelState": {},
|
||||
"runtimeState": {}
|
||||
}
|
||||
```
|
||||
|
||||
## modelState
|
||||
|
||||
`modelState` 保存每个 DashScope key 与 `MODEL_ID` 组合的状态。
|
||||
|
||||
例如配置:
|
||||
|
||||
```env
|
||||
DASHSCOPE_API_KEYS=key1,key2
|
||||
MODEL_IDS=modelA,modelB,modelC
|
||||
```
|
||||
|
||||
那么 `modelState` 会有 `2 * 3 = 6` 条记录。
|
||||
|
||||
字段说明:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `keyHash` | DashScope API Key 的 SHA-256 hash。用于区分不同 key,但不保存明文 key。 |
|
||||
| `modelId` | 模型 ID,来自 `MODEL_IDS`。 |
|
||||
| `cooldownUntil` | 冷却截止时间,Unix 毫秒时间戳。`0` 表示没有冷却。如果这个值大于当前时间,该 key+model 组合不可用。 |
|
||||
| `failureCount` | 该 key+model 组合触发免费额度耗尽 403 的次数。 |
|
||||
| `lastError` | 最近一次免费额度耗尽时,上游返回的错误信息。 |
|
||||
| `lastUsedAt` | 最近一次成功使用该 key+model 组合的时间,Unix 毫秒时间戳。 |
|
||||
| `updatedAt` | 最近一次状态更新时间,Unix 毫秒时间戳。 |
|
||||
|
||||
## runtimeState
|
||||
|
||||
`runtimeState` 保存调度游标,避免服务每次重启后都从第一个 key、第一条 model 开始打。
|
||||
|
||||
当前会使用的记录:
|
||||
|
||||
| `name` | 说明 |
|
||||
| --- | --- |
|
||||
| `key_cursor` | 当前优先使用的 DashScope API Key 下标。 |
|
||||
| `model_cursor:<key_hash>` | 某个 key 当前优先使用的模型下标。 |
|
||||
|
||||
每条记录包含:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `value` | 状态值。当前主要保存数字游标,但统一用文本存储。 |
|
||||
| `updatedAt` | 最近一次更新时间,Unix 毫秒时间戳。 |
|
||||
|
||||
## 写入策略
|
||||
|
||||
状态变更会同步写入文件。写入时先写临时文件,再用 `rename` 替换正式状态文件,降低进程中断时写坏文件的风险。
|
||||
|
||||
如果启动时发现状态文件不是有效 JSON,或结构不符合当前版本,服务会把原文件重命名为:
|
||||
|
||||
```text
|
||||
proxy-state.json.bak.<timestamp>
|
||||
```
|
||||
|
||||
然后创建新的空状态。
|
||||
|
||||
## 调度规则
|
||||
|
||||
当前实现是“key 优先”的切换策略:
|
||||
|
||||
1. 先使用 `key_cursor` 指向的 key。
|
||||
2. 在这个 key 内部,从该 key 的 `model_cursor` 指向的模型开始尝试。
|
||||
3. 如果某个模型返回免费额度耗尽 403,只冷却这个 `keyHash + modelId` 组合。
|
||||
4. 继续尝试同一个 key 下的下一个可用模型。
|
||||
5. 只有当前 key 下所有模型都不可用时,才切换到下一个 key。
|
||||
6. 如果所有 key+model 组合都不可用,服务返回 `503`。
|
||||
|
||||
## 冷却规则
|
||||
|
||||
默认冷却时间是 1 个月:
|
||||
|
||||
```env
|
||||
MODEL_COOLDOWN_SECONDS=2592000
|
||||
```
|
||||
|
||||
当某个 key+model 组合进入冷却:
|
||||
|
||||
```text
|
||||
cooldownUntil = 当前时间 + MODEL_COOLDOWN_SECONDS
|
||||
failureCount = failureCount + 1
|
||||
lastError = 上游错误信息
|
||||
updatedAt = 当前时间
|
||||
```
|
||||
|
||||
当某个 key+model 组合成功返回:
|
||||
|
||||
```text
|
||||
lastUsedAt = 当前时间
|
||||
lastError = null
|
||||
updatedAt = 当前时间
|
||||
```
|
||||
|
||||
成功返回不会清零 `failureCount`,这个字段保留历史失败次数。
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
# 使用说明
|
||||
|
||||
这个项目是一个本地 DashScope Anthropic 兼容反代服务。客户端只需要配置一个本地 URL 和一个代理 key,真实 DashScope key 和模型池都放在服务端。
|
||||
|
||||
## 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
运行时依赖不包含原生编译模块,Windows 上首次安装不需要额外安装 C++ 编译环境。
|
||||
|
||||
## 2. 配置 .env
|
||||
|
||||
复制配置模板:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
核心配置:
|
||||
|
||||
```env
|
||||
PORT=3300
|
||||
|
||||
PROXY_API_KEY=sk-001
|
||||
|
||||
DASHSCOPE_API_KEYS=sk-key-1,sk-key-2,sk-key-3
|
||||
UPSTREAM_BASE_URL=https://dashscope.aliyuncs.com/apps/anthropic
|
||||
OPENAI_UPSTREAM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
|
||||
MODEL_IDS=qwen3.7-max,qwen-plus,qwen-max
|
||||
MODEL_COOLDOWN_SECONDS=2592000
|
||||
|
||||
STATE_PATH=./data/proxy-state.json
|
||||
UPSTREAM_AUTH_MODE=authorization
|
||||
CORS_ORIGIN=*
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
| 配置 | 说明 |
|
||||
| --- | --- |
|
||||
| `PORT` | 本地服务端口。你当前给 Pi CLI 用的是 `3300`。 |
|
||||
| `PROXY_API_KEY` | 客户端调用本地代理时使用的 key。 |
|
||||
| `DASHSCOPE_API_KEYS` | 多个真实 DashScope API Key,英文逗号分隔。 |
|
||||
| `UPSTREAM_BASE_URL` | DashScope Anthropic 兼容接口上游地址。 |
|
||||
| `OPENAI_UPSTREAM_BASE_URL` | DashScope OpenAI Chat Completions 兼容接口上游地址。 |
|
||||
| `MODEL_IDS` | 所有 key 共用的模型 ID 列表,英文逗号分隔。 |
|
||||
| `MODEL_COOLDOWN_SECONDS` | 触发免费额度耗尽后冷却多久。当前默认 `2592000` 秒,也就是 30 天。 |
|
||||
| `STATE_PATH` | JSON 状态文件路径。用于保存冷却状态和调度游标。 |
|
||||
|
||||
## 3. 初始化状态文件
|
||||
|
||||
启动服务时会自动创建状态文件。
|
||||
|
||||
也可以手动初始化:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
node dist/index.js
|
||||
```
|
||||
|
||||
看到服务启动成功后按 `Ctrl+C` 停止即可。状态文件默认会生成在:
|
||||
|
||||
```text
|
||||
data/proxy-state.json
|
||||
```
|
||||
|
||||
状态文件结构见:
|
||||
|
||||
```text
|
||||
docs/state-file.md
|
||||
```
|
||||
|
||||
## 4. 启动服务
|
||||
|
||||
开发模式:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
生产模式:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
pnpm start
|
||||
```
|
||||
|
||||
启动成功后会看到类似日志:
|
||||
|
||||
```text
|
||||
[proxy] listening on http://localhost:3300
|
||||
[proxy] upstream base: https://dashscope.aliyuncs.com/apps/anthropic
|
||||
[proxy] openai upstream base: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
[proxy] api keys loaded: 3
|
||||
[proxy] models per key: 3
|
||||
[proxy] applied model ids:
|
||||
[proxy] - qwen3.7-max
|
||||
[proxy] - qwen-plus
|
||||
[proxy] - qwen-max
|
||||
[proxy] reminder: 请在 DashScope/百炼控制台为以上模型开启“模型用完即停”。
|
||||
[proxy] state file: ./data/proxy-state.json
|
||||
```
|
||||
|
||||
## 5. 客户端调用
|
||||
|
||||
本地代理地址:
|
||||
|
||||
```text
|
||||
http://localhost:3300
|
||||
```
|
||||
|
||||
Anthropic Messages 接口:
|
||||
|
||||
```text
|
||||
http://localhost:3300/v1/messages
|
||||
```
|
||||
|
||||
请求头可以用二选一:
|
||||
|
||||
```http
|
||||
Authorization: Bearer sk-001
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```http
|
||||
x-api-key: sk-001
|
||||
```
|
||||
|
||||
测试请求:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3300/v1/messages \
|
||||
-H "Authorization: Bearer sk-001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-d '{
|
||||
"model": "client-model-will-be-overwritten",
|
||||
"max_tokens": 256,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你好"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
请求体里的 `model` 会被代理覆盖成当前可用模型。
|
||||
|
||||
OpenAI Chat Completions 接口:
|
||||
|
||||
```text
|
||||
http://localhost:3300/v1/chat/completions
|
||||
```
|
||||
|
||||
OpenAI-compatible base URL:
|
||||
|
||||
```text
|
||||
http://localhost:3300/v1
|
||||
```
|
||||
|
||||
测试请求:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3300/v1/chat/completions \
|
||||
-H "Authorization: Bearer sk-001" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "client-model-will-be-overwritten",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你好"
|
||||
}
|
||||
],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
查看 OpenAI 风格模型列表:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3300/v1/models \
|
||||
-H "Authorization: Bearer sk-001"
|
||||
```
|
||||
|
||||
本服务端只是反代转发,使用 Node 原生 `fetch` 即可,不需要安装 `openai` SDK。`openai` SDK 是客户端接入时才需要的。
|
||||
|
||||
## 6. Pi CLI 接入
|
||||
|
||||
Pi CLI 的 provider 已配置为:
|
||||
|
||||
```text
|
||||
local-dashscope-proxy
|
||||
```
|
||||
|
||||
指向:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:3300
|
||||
```
|
||||
|
||||
默认使用:
|
||||
|
||||
```text
|
||||
provider: local-dashscope-proxy
|
||||
model: qwen3.7-max
|
||||
```
|
||||
|
||||
使用前先启动代理服务:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
然后运行:
|
||||
|
||||
```bash
|
||||
pi
|
||||
```
|
||||
|
||||
也可以显式指定:
|
||||
|
||||
```bash
|
||||
pi --provider local-dashscope-proxy --model qwen3.7-max
|
||||
```
|
||||
|
||||
查看 Pi 是否识别 provider:
|
||||
|
||||
```bash
|
||||
pi --list-models local --offline
|
||||
```
|
||||
|
||||
如果想让 Pi CLI 按 OpenAI Chat Completions 协议接入,可以在 `~/.pi/agent/models.json` 增加类似 provider:
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"local-dashscope-openai-proxy": {
|
||||
"baseUrl": "http://127.0.0.1:3300/v1",
|
||||
"api": "openai-completions",
|
||||
"apiKey": "sk-001",
|
||||
"compat": {
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsReasoningEffort": true,
|
||||
"maxTokensField": "max_tokens",
|
||||
"thinkingFormat": "qwen"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "qwen3.7-max",
|
||||
"name": "Qwen 3.7 Max (Local OpenAI Proxy)",
|
||||
"reasoning": true,
|
||||
"input": ["text", "image"],
|
||||
"contextWindow": 131072,
|
||||
"maxTokens": 32768,
|
||||
"thinkingLevelMap": {
|
||||
"off": null,
|
||||
"minimal": "low",
|
||||
"low": "low",
|
||||
"medium": "medium",
|
||||
"high": "high",
|
||||
"xhigh": "high"
|
||||
},
|
||||
"cost": {
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheRead": 0,
|
||||
"cacheWrite": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 查看服务状态
|
||||
|
||||
健康检查:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3300/health
|
||||
```
|
||||
|
||||
模型池状态:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3300/models/status \
|
||||
-H "Authorization: Bearer sk-001"
|
||||
```
|
||||
|
||||
返回中重要字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `totalKeys` | 当前加载的 DashScope key 数量。 |
|
||||
| `modelsPerKey` | 每个 key 下的模型数量。 |
|
||||
| `totalSlots` | key+model 总组合数。 |
|
||||
| `availableSlots` | 当前可用的 key+model 组合数。 |
|
||||
| `models[].keyHash` | key hash 前缀,用于区分 key,不是明文 key。 |
|
||||
| `models[].id` | 模型 ID。 |
|
||||
| `models[].available` | 当前组合是否可用。 |
|
||||
| `models[].cooldownUntil` | 冷却截止时间。 |
|
||||
| `models[].failureCount` | 累计触发免费额度耗尽次数。 |
|
||||
|
||||
## 8. 切换规则
|
||||
|
||||
服务按这个规则自动切换:
|
||||
|
||||
1. 优先使用当前 key。
|
||||
2. 当前 key 下某个模型触发免费额度耗尽 403,只冷却这个 key+model。
|
||||
3. 继续尝试当前 key 的下一个模型。
|
||||
4. 当前 key 下所有模型都冷却后,才切换到下一个 key。
|
||||
5. 所有 key+model 都冷却时,返回 `503`。
|
||||
|
||||
## 9. 常见操作
|
||||
|
||||
重新生成状态文件:
|
||||
|
||||
```bash
|
||||
rm data/proxy-state.json
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
只想清空冷却状态,也可以删除状态文件后重新启动。注意这会丢失所有历史冷却和失败计数。
|
||||
|
||||
查看状态文件:
|
||||
|
||||
```text
|
||||
data/proxy-state.json
|
||||
```
|
||||
|
||||
状态文件和自动备份文件不需要提交。
|
||||
Reference in New Issue
Block a user