modlink-agent 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: modlink-agent
3
+ Version: 1.1.0
4
+ Summary: 模联 ModLink 的 Agent 工具包——把平台模型封装为 CLI 与 MCP 工具,供 AI Agent 调用
5
+ Author: ModLink Platform Team
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://asr.syncmeet.tech:9443
8
+ Project-URL: Documentation, https://asr.syncmeet.tech:9443/docs
9
+ Keywords: mcp,agent,asr,tts,llm,modlink,语音识别,语音合成,图像生成
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: requests>=2.28
23
+ Provides-Extra: mcp
24
+ Requires-Dist: mcp<2,>=1.0; extra == "mcp"
25
+
26
+ # modlink-agent —— 模联平台的 Agent 工具包
27
+
28
+ 把模联(ModLink)的模型能力封装成 **CLI 命令**与 **MCP 工具**,供 AI Agent 调用。
29
+
30
+ 平台侧接口见 [API 文档](../../docs/api-reference.md),本包是它的 Agent 友好封装。
31
+
32
+ ---
33
+
34
+ ## ⚠️ 安全约束:不提供对话模型
35
+
36
+ **本包刻意不封装 LLM 对话模型(`qwen3-coder-next`)**,CLI 也没有 `chat` 子命令。
37
+
38
+ 平台十余个模型里,只有对话模型同时具备两个特性:
39
+
40
+ 1. **接受任意提示词** —— 其余模型都是「输入固定格式数据、输出固定格式结果」
41
+ 2. **能调用工具** —— 可通过 `tools` / `tool_choice` 组合出调用链
42
+
43
+ 把它做成 Agent 可调用的工具,等于**把 Agent 的控制面交给不可信输入**。提示词注入的后果是任意的:
44
+
45
+ - 诱导 Agent 读取不该读的文件、执行不该执行的命令
46
+ - 通过 `tools` 组合出「看起来像合法业务调用」的越权链
47
+ - 借 Agent 的身份绕过人类已经设好的权限边界
48
+
49
+ **这不是能力缺失,是刻意的取舍。**
50
+
51
+ - 需要使用对话模型的用户:仍可直接调平台的 `/v1/chat/completions` + 自建 API Key
52
+ - Agent 的规划与生成能力:应交给宿主自带的主模型,不要二次调用本平台的对话模型
53
+
54
+ 该约束由单元测试与端到端验证双重钉死(`hasattr` 断言 + 子命令集检查),
55
+ 防止日后有人「顺手加回来」。
56
+
57
+ ---
58
+
59
+ ## 安装
60
+
61
+ ```bash
62
+ pip install -e . # 只用 CLI
63
+ pip install -e ".[mcp]" # 需要 MCP Server
64
+ ```
65
+
66
+ 依赖只有 `requests` 一个;MCP SDK 是可选项,只用 CLI 不需要装。
67
+
68
+ ---
69
+
70
+ ## 配置 API Key
71
+
72
+ API Key 在模联**用户控制台 → API Key** 页创建。
73
+ 支持四种来源,**优先级从上到下**:
74
+
75
+ | 优先级 | 来源 | 说明 |
76
+ |---|---|---|
77
+ | 1 | 环境变量 `MODLINK_API_KEY` | **推荐**。不进 shell history 与进程列表 |
78
+ | 2 | 配置文件 `~/.config/modlink-agent/config.json` 的 `apiKey` | 免重复输入 |
79
+ | 3 | MCP 客户端配置的 `env` 段 | 见下方「MCP 接入」 |
80
+ | 4 | 交互式输入(仅 CLI 且在 TTY 下) | 首次试用 |
81
+
82
+ > **优先级不可绕过**:环境变量存在时会忽略明文。
83
+ > 否则「先设了环境变量、后又改了面板配置」会静默用错一把 Key,
84
+ > 表现为「明明配了 A 却按 B 的额度扣费」,极难排查。
85
+
86
+ ### 为什么不用命令行传 Key
87
+
88
+ `--api-key ml_live_xxx` 会进 shell history,也会被 `ps aux` 看到。
89
+ 环境变量在同一台机器上通过 `/proc/PID/environ` 也能被同用户读到,
90
+ 但**不会落进 history 与审计日志**——这是 CLI 工具的通行取舍。
91
+
92
+ `--key` 参数仍然保留(供程序化调用),但优先级低于环境变量。
93
+
94
+ ### 全部环境变量
95
+
96
+ | 变量 | 必填 | 默认 | 说明 |
97
+ |---|---|---|---|
98
+ | `MODLINK_API_KEY` | ✅ | — | API Key |
99
+ | `MODLINK_BASE_URL` | | `https://asr.syncmeet.tech:9443` | 网关地址 |
100
+ | `MODLINK_WORK_DIR` | | `./modlink-out` | 音频/图片落盘目录 |
101
+ | `MODLINK_TIMEOUT` | | `600` | 单请求超时(秒) |
102
+ | `MODLINK_ASYNC_THRESHOLD` | | `15` | 超过该秒数视为长任务,转异步轮询 |
103
+
104
+ ---
105
+
106
+ ## 模型白名单自动生效
107
+
108
+ 在用户端创建 Key 时若设了「模型白名单」,本包**无需任何配置**即可正确受限:
109
+
110
+ ```bash
111
+ # 用只授权向量的 Key
112
+ export MODLINK_API_KEY=ml_live_xxx
113
+ modlink models
114
+ # 只列出 qwen3-embedding —— 平台侧 /v1/models 已按白名单过滤
115
+
116
+ modlink translate "你好" --to 英语
117
+ # 当前 API Key 无权调用模型 hymt2-translate。该 Key 的模型白名单为:qwen3-embedding。
118
+ # 如需调用,请在「API Key」页修改白名单或新建 Key。
119
+ ```
120
+
121
+ ---
122
+
123
+ ## CLI 用法
124
+
125
+ ```bash
126
+ modlink models # 列出当前 Key 可调用的模型
127
+ modlink health # 检查平台可达性
128
+
129
+ modlink transcribe a.wav --model funasr --diarization
130
+ modlink synthesize "欢迎使用模联" --model cosyvoice
131
+ modlink translate "你好" --to 英语
132
+ modlink embed "模联平台" --dimensions 1024
133
+ modlink image photo.jpg --task upscale
134
+ modlink generate "一只橘猫" --width 1024 --height 1024
135
+ ```
136
+
137
+ > **没有 `chat` 子命令** —— 见上方「安全约束」。
138
+
139
+ 所有子命令支持 `--json` 输出结构化结果,便于脚本与 Agent 解析:
140
+
141
+ ```bash
142
+ modlink models --json | jq '.data[].id'
143
+ ```
144
+
145
+ ### 图像任务对应关系
146
+
147
+ | `--task` | 默认模型 | 额外必填 |
148
+ |---|---|---|
149
+ | `remove_background` | `birefnet-matting` | — |
150
+ | `erase` | `lama-inpaint` | `--mask` |
151
+ | `upscale` | `realesrgan-upscale` | — |
152
+ | `face_restore` | `codeformer-restore` | — |
153
+ | `face_swap` | `inswapper-face-swap` | `--source-image` |
154
+
155
+ ---
156
+
157
+ ## 作为 Python 库
158
+
159
+ ```python
160
+ from modlink_agent import ModLinkClient
161
+
162
+ client = ModLinkClient() # 自动读环境变量
163
+
164
+ # 文本类直接返回结果
165
+ print(client.translate("你好", target_lang="英语"))
166
+ print(client.embed(["文档A", "文档B"], dimensions=1024))
167
+
168
+ # 二进制类返回**文件路径**,不是内容
169
+ audio = client.synthesize("欢迎使用模联")
170
+ print(audio.path) # ./modlink-out/tts-20261006-200547-xxx.wav
171
+ print(audio.describe()) # 一句话摘要(不含任何二进制内容)
172
+ ```
173
+
174
+ > 客户端**没有** `chat` / `chat_text` / `stream_chat` 方法 —— 见上方「安全约束」。
175
+
176
+ ---
177
+
178
+ ## 三个为 Agent 做的关键设计
179
+
180
+ ### ① 二进制不进上下文
181
+
182
+ 一张 1024×1024 图转 base64 约 1.4MB token,会直接撑爆 Agent 上下文。
183
+ 本包**一律落盘后只返回绝对路径**:
184
+
185
+ ```python
186
+ result = client.generate_image("一只猫")
187
+ print(result.path, result.size_bytes) # 路径 + 字节数,不含内容
188
+ ```
189
+
190
+ ### ② 429 自动退避
191
+
192
+ 图像模型**同卡互斥**(平台文档明写「必须串行调用」),Agent 天生并发,一并发就吃 429。
193
+ 若把 429 原样抛出,Agent 只会反复重试并再次 429,形成死循环。
194
+
195
+ 本包读 `Retry-After` 头做指数退避,重试耗尽后返回**可执行的提示**:
196
+
197
+ ```
198
+ 模型互斥或触发限流(429),重试 4 次仍失败。
199
+ 平台说明:图像生成类模型**同卡互斥,必须串行调用**。
200
+ 请改为逐个调用(等上一个返回后再发下一个),或稍后再试。
201
+ ```
202
+
203
+ ### ③ 长任务不超时
204
+
205
+ - **ASR**:长音频自动走 `/v1/asr/tasks` 异步接口,内部轮询到完成
206
+ (按音频大小启发式判断,调用方无需决策)
207
+ - **图像**:平台无异步接口,用长超时 + 返回值带耗时秒数
208
+
209
+ 实测真机耗时(供参考,用于设置 MCP 客户端超时):
210
+
211
+ | 操作 | 耗时 |
212
+ |---|---|
213
+ | 翻译(2 条批量) | 0.16s |
214
+ | 向量化(2 条 / 256 维) | 0.09s |
215
+ | 图像超分(64×64) | 0.15s |
216
+ | 语音合成(1 句 → 86KB WAV) | 1.38s |
217
+ | 语音转写(1 句) | 0.29s |
218
+
219
+ > 图像**生成**(1024×1024,20B 模型)实测需数十秒到数分钟,
220
+ > 且同卡互斥(并发会 429)——MCP 客户端应给足超时并串行调用。
221
+ > 本包已内置 429 退避,但客户端超时也要相应放宽。
222
+
223
+ ---
224
+
225
+ ## 关于函数调用(工具调用)
226
+
227
+ 平台侧的 `/v1/chat/completions` **完整支持** `tools` / `tool_choice`
228
+ 与多轮 `tool_calls` 回传(**平台只做透传,不执行工具**——模型只负责
229
+ 「提出调用请求」,由调用方执行后把结果作为 `role=tool` 消息回传,
230
+ 漏掉 `tool_call_id` 会让模型重复发起同一次调用)。
231
+
232
+ 但本包**不封装对话模型**(见上方「安全约束」),因此 Agent 侧
233
+ 不会拿到这个入口——函数调用能力请直接在平台侧使用。
234
+
235
+ ---
236
+
237
+ ## MCP 接入
238
+
239
+ ### 配置
240
+
241
+ `command` 指向装了 MCP SDK 的那个解释器 —— 若报 `No module named mcp`,
242
+ 说明该解释器缺依赖,用装了 SDK 的那个(如 `…/venvs/mcp-agent/bin/python`)。
243
+
244
+ **方式一:Key 走环境变量(推荐)**
245
+
246
+ ```json
247
+ {
248
+ "mcpServers": {
249
+ "modlink": {
250
+ "command": "python",
251
+ "args": ["-m", "modlink_agent.mcp_server"],
252
+ "env": {
253
+ "MODLINK_API_KEY": "ml_live_xxxxxxxx"
254
+ }
255
+ }
256
+ }
257
+ }
258
+ ```
259
+
260
+ `env` 段写死的值就是「配置文件/显式传入」层,优先级低于机器上真实的环境变量
261
+ (见上方 Key 表格第 1 行)。也就是说:**面板里填了明文,但进程环境变量里
262
+ 存在 `MODLINK_API_KEY` 时,后者胜出**。这样 Key 可以从 CI secret 或
263
+ `~/.bashrc` 统一注入,不必落到面板配置里。
264
+
265
+ **方式二:环境变量已在 shell 里**
266
+
267
+ ```json
268
+ {
269
+ "mcpServers": {
270
+ "modlink": {
271
+ "command": "python",
272
+ "args": ["-m", "modlink_agent.mcp_server"],
273
+ "env": {}
274
+ }
275
+ }
276
+ }
277
+ ```
278
+
279
+ ### 工具清单(7 个)
280
+
281
+ 按「族」暴露,不按模型拆成一堆工具 —— 一模型一工具会让 Agent 在 20+ 个
282
+ 名字里纠结选择,且模型下线后工具名就变成死引用。
283
+
284
+ | 工具 | 作用 | 覆盖模型 |
285
+ |---|---|---|
286
+ | `modlink_list_models` | 列出当前 Key 可调模型 | 全部(按白名单过滤) |
287
+ | `modlink_transcribe` | 语音转写 | funasr / sensevoice / paraformer-bilingual / zipformer-bilingual |
288
+ | `modlink_synthesize` | 语音合成 | cosyvoice / indextts |
289
+ | `modlink_translate` | 文本翻译 | hymt2-translate |
290
+ | `modlink_embed` | 文本向量化 | qwen3-embedding |
291
+ | `modlink_image_process` | 图像处理 | 抠图 / 消除 / 超分 / 人脸修复 / 换脸 |
292
+ | `modlink_image_generate` | 图像生成 | flux2-generate / qwen-image-2.1 |
293
+
294
+ **不包含对话工具**(`modlink_chat` 不存在)——与 CLI 保持一致的安全边界。
295
+ 客户端可用 `modlink_list_models` 自查可调范围,但其中不会出现对话模型。
296
+
297
+ ### Server Instructions
298
+
299
+ Server 带一段面向 Agent 的说明,覆盖三类「模型自身不会说」的知识:
300
+
301
+ 1. 二进制结果返回的是**文件路径**而非内容,需要 Agent 再用文件工具读取
302
+ 2. 图像生成/处理**同卡互斥**,并发会 429
303
+ 3. 图像生成耗时长(1024×1024 数十秒到数分钟),需要给足超时
304
+
305
+ `modlink_list_models` 的存在是必要的:不调它,Agent 无从知道
306
+ 「这个 Key 到底能用什么」,只能靠猜或反复试错吃 403。
307
+
308
+ ---
309
+
310
+ ## 错误处理
311
+
312
+ | 异常 | 触发条件 | 提示要点 |
313
+ |---|---|---|
314
+ | `AuthenticationError` | 401 | Key 无效/吊销/过期,指向控制台检查 |
315
+ | `ForbiddenError` | 403 | 模型白名单未授权,提示去改白名单 |
316
+ | `InsufficientCredits` | 402 | 积分不足,指向充值页 |
317
+ | `RateLimitError` | 429 | 模型互斥,提示改串行 |
318
+ | `ModelNotFound` | 404 / 503 | 模型标识错或未部署 |
319
+ | `UpstreamError` | 5xx | 平台或引擎异常 |
320
+
321
+ 错误文案都是**给 Agent 读的**,每条都包含下一步该做什么——
322
+ Agent 拿到「HTTP 403」只会反复重试同一个错误调用。
323
+
324
+ ---
325
+
326
+ ## 测试
327
+
328
+ ```bash
329
+ python tests/test_client_local.py # 32 项:起本地假 HTTP 服务,不连生产
330
+ python tests/test_mcp_local.py # 44 项:含真实 stdio 握手
331
+ ```
332
+
333
+ `test_mcp_local.py` 会真的起一个 `python -m modlink_agent.mcp_server`
334
+ 子进程,走完整 JSON-RPC 握手并 `list_tools` —— 因为 **stdio 传输下
335
+ stdout 是协议通道,任何一处 `print` 都会破坏 JSON-RPC 帧**,
336
+ 而这类问题不真起进程就测不出来。
337
+
338
+ 真机验证覆盖 7 个族各调一次。
@@ -0,0 +1,313 @@
1
+ # modlink-agent —— 模联平台的 Agent 工具包
2
+
3
+ 把模联(ModLink)的模型能力封装成 **CLI 命令**与 **MCP 工具**,供 AI Agent 调用。
4
+
5
+ 平台侧接口见 [API 文档](../../docs/api-reference.md),本包是它的 Agent 友好封装。
6
+
7
+ ---
8
+
9
+ ## ⚠️ 安全约束:不提供对话模型
10
+
11
+ **本包刻意不封装 LLM 对话模型(`qwen3-coder-next`)**,CLI 也没有 `chat` 子命令。
12
+
13
+ 平台十余个模型里,只有对话模型同时具备两个特性:
14
+
15
+ 1. **接受任意提示词** —— 其余模型都是「输入固定格式数据、输出固定格式结果」
16
+ 2. **能调用工具** —— 可通过 `tools` / `tool_choice` 组合出调用链
17
+
18
+ 把它做成 Agent 可调用的工具,等于**把 Agent 的控制面交给不可信输入**。提示词注入的后果是任意的:
19
+
20
+ - 诱导 Agent 读取不该读的文件、执行不该执行的命令
21
+ - 通过 `tools` 组合出「看起来像合法业务调用」的越权链
22
+ - 借 Agent 的身份绕过人类已经设好的权限边界
23
+
24
+ **这不是能力缺失,是刻意的取舍。**
25
+
26
+ - 需要使用对话模型的用户:仍可直接调平台的 `/v1/chat/completions` + 自建 API Key
27
+ - Agent 的规划与生成能力:应交给宿主自带的主模型,不要二次调用本平台的对话模型
28
+
29
+ 该约束由单元测试与端到端验证双重钉死(`hasattr` 断言 + 子命令集检查),
30
+ 防止日后有人「顺手加回来」。
31
+
32
+ ---
33
+
34
+ ## 安装
35
+
36
+ ```bash
37
+ pip install -e . # 只用 CLI
38
+ pip install -e ".[mcp]" # 需要 MCP Server
39
+ ```
40
+
41
+ 依赖只有 `requests` 一个;MCP SDK 是可选项,只用 CLI 不需要装。
42
+
43
+ ---
44
+
45
+ ## 配置 API Key
46
+
47
+ API Key 在模联**用户控制台 → API Key** 页创建。
48
+ 支持四种来源,**优先级从上到下**:
49
+
50
+ | 优先级 | 来源 | 说明 |
51
+ |---|---|---|
52
+ | 1 | 环境变量 `MODLINK_API_KEY` | **推荐**。不进 shell history 与进程列表 |
53
+ | 2 | 配置文件 `~/.config/modlink-agent/config.json` 的 `apiKey` | 免重复输入 |
54
+ | 3 | MCP 客户端配置的 `env` 段 | 见下方「MCP 接入」 |
55
+ | 4 | 交互式输入(仅 CLI 且在 TTY 下) | 首次试用 |
56
+
57
+ > **优先级不可绕过**:环境变量存在时会忽略明文。
58
+ > 否则「先设了环境变量、后又改了面板配置」会静默用错一把 Key,
59
+ > 表现为「明明配了 A 却按 B 的额度扣费」,极难排查。
60
+
61
+ ### 为什么不用命令行传 Key
62
+
63
+ `--api-key ml_live_xxx` 会进 shell history,也会被 `ps aux` 看到。
64
+ 环境变量在同一台机器上通过 `/proc/PID/environ` 也能被同用户读到,
65
+ 但**不会落进 history 与审计日志**——这是 CLI 工具的通行取舍。
66
+
67
+ `--key` 参数仍然保留(供程序化调用),但优先级低于环境变量。
68
+
69
+ ### 全部环境变量
70
+
71
+ | 变量 | 必填 | 默认 | 说明 |
72
+ |---|---|---|---|
73
+ | `MODLINK_API_KEY` | ✅ | — | API Key |
74
+ | `MODLINK_BASE_URL` | | `https://asr.syncmeet.tech:9443` | 网关地址 |
75
+ | `MODLINK_WORK_DIR` | | `./modlink-out` | 音频/图片落盘目录 |
76
+ | `MODLINK_TIMEOUT` | | `600` | 单请求超时(秒) |
77
+ | `MODLINK_ASYNC_THRESHOLD` | | `15` | 超过该秒数视为长任务,转异步轮询 |
78
+
79
+ ---
80
+
81
+ ## 模型白名单自动生效
82
+
83
+ 在用户端创建 Key 时若设了「模型白名单」,本包**无需任何配置**即可正确受限:
84
+
85
+ ```bash
86
+ # 用只授权向量的 Key
87
+ export MODLINK_API_KEY=ml_live_xxx
88
+ modlink models
89
+ # 只列出 qwen3-embedding —— 平台侧 /v1/models 已按白名单过滤
90
+
91
+ modlink translate "你好" --to 英语
92
+ # 当前 API Key 无权调用模型 hymt2-translate。该 Key 的模型白名单为:qwen3-embedding。
93
+ # 如需调用,请在「API Key」页修改白名单或新建 Key。
94
+ ```
95
+
96
+ ---
97
+
98
+ ## CLI 用法
99
+
100
+ ```bash
101
+ modlink models # 列出当前 Key 可调用的模型
102
+ modlink health # 检查平台可达性
103
+
104
+ modlink transcribe a.wav --model funasr --diarization
105
+ modlink synthesize "欢迎使用模联" --model cosyvoice
106
+ modlink translate "你好" --to 英语
107
+ modlink embed "模联平台" --dimensions 1024
108
+ modlink image photo.jpg --task upscale
109
+ modlink generate "一只橘猫" --width 1024 --height 1024
110
+ ```
111
+
112
+ > **没有 `chat` 子命令** —— 见上方「安全约束」。
113
+
114
+ 所有子命令支持 `--json` 输出结构化结果,便于脚本与 Agent 解析:
115
+
116
+ ```bash
117
+ modlink models --json | jq '.data[].id'
118
+ ```
119
+
120
+ ### 图像任务对应关系
121
+
122
+ | `--task` | 默认模型 | 额外必填 |
123
+ |---|---|---|
124
+ | `remove_background` | `birefnet-matting` | — |
125
+ | `erase` | `lama-inpaint` | `--mask` |
126
+ | `upscale` | `realesrgan-upscale` | — |
127
+ | `face_restore` | `codeformer-restore` | — |
128
+ | `face_swap` | `inswapper-face-swap` | `--source-image` |
129
+
130
+ ---
131
+
132
+ ## 作为 Python 库
133
+
134
+ ```python
135
+ from modlink_agent import ModLinkClient
136
+
137
+ client = ModLinkClient() # 自动读环境变量
138
+
139
+ # 文本类直接返回结果
140
+ print(client.translate("你好", target_lang="英语"))
141
+ print(client.embed(["文档A", "文档B"], dimensions=1024))
142
+
143
+ # 二进制类返回**文件路径**,不是内容
144
+ audio = client.synthesize("欢迎使用模联")
145
+ print(audio.path) # ./modlink-out/tts-20261006-200547-xxx.wav
146
+ print(audio.describe()) # 一句话摘要(不含任何二进制内容)
147
+ ```
148
+
149
+ > 客户端**没有** `chat` / `chat_text` / `stream_chat` 方法 —— 见上方「安全约束」。
150
+
151
+ ---
152
+
153
+ ## 三个为 Agent 做的关键设计
154
+
155
+ ### ① 二进制不进上下文
156
+
157
+ 一张 1024×1024 图转 base64 约 1.4MB token,会直接撑爆 Agent 上下文。
158
+ 本包**一律落盘后只返回绝对路径**:
159
+
160
+ ```python
161
+ result = client.generate_image("一只猫")
162
+ print(result.path, result.size_bytes) # 路径 + 字节数,不含内容
163
+ ```
164
+
165
+ ### ② 429 自动退避
166
+
167
+ 图像模型**同卡互斥**(平台文档明写「必须串行调用」),Agent 天生并发,一并发就吃 429。
168
+ 若把 429 原样抛出,Agent 只会反复重试并再次 429,形成死循环。
169
+
170
+ 本包读 `Retry-After` 头做指数退避,重试耗尽后返回**可执行的提示**:
171
+
172
+ ```
173
+ 模型互斥或触发限流(429),重试 4 次仍失败。
174
+ 平台说明:图像生成类模型**同卡互斥,必须串行调用**。
175
+ 请改为逐个调用(等上一个返回后再发下一个),或稍后再试。
176
+ ```
177
+
178
+ ### ③ 长任务不超时
179
+
180
+ - **ASR**:长音频自动走 `/v1/asr/tasks` 异步接口,内部轮询到完成
181
+ (按音频大小启发式判断,调用方无需决策)
182
+ - **图像**:平台无异步接口,用长超时 + 返回值带耗时秒数
183
+
184
+ 实测真机耗时(供参考,用于设置 MCP 客户端超时):
185
+
186
+ | 操作 | 耗时 |
187
+ |---|---|
188
+ | 翻译(2 条批量) | 0.16s |
189
+ | 向量化(2 条 / 256 维) | 0.09s |
190
+ | 图像超分(64×64) | 0.15s |
191
+ | 语音合成(1 句 → 86KB WAV) | 1.38s |
192
+ | 语音转写(1 句) | 0.29s |
193
+
194
+ > 图像**生成**(1024×1024,20B 模型)实测需数十秒到数分钟,
195
+ > 且同卡互斥(并发会 429)——MCP 客户端应给足超时并串行调用。
196
+ > 本包已内置 429 退避,但客户端超时也要相应放宽。
197
+
198
+ ---
199
+
200
+ ## 关于函数调用(工具调用)
201
+
202
+ 平台侧的 `/v1/chat/completions` **完整支持** `tools` / `tool_choice`
203
+ 与多轮 `tool_calls` 回传(**平台只做透传,不执行工具**——模型只负责
204
+ 「提出调用请求」,由调用方执行后把结果作为 `role=tool` 消息回传,
205
+ 漏掉 `tool_call_id` 会让模型重复发起同一次调用)。
206
+
207
+ 但本包**不封装对话模型**(见上方「安全约束」),因此 Agent 侧
208
+ 不会拿到这个入口——函数调用能力请直接在平台侧使用。
209
+
210
+ ---
211
+
212
+ ## MCP 接入
213
+
214
+ ### 配置
215
+
216
+ `command` 指向装了 MCP SDK 的那个解释器 —— 若报 `No module named mcp`,
217
+ 说明该解释器缺依赖,用装了 SDK 的那个(如 `…/venvs/mcp-agent/bin/python`)。
218
+
219
+ **方式一:Key 走环境变量(推荐)**
220
+
221
+ ```json
222
+ {
223
+ "mcpServers": {
224
+ "modlink": {
225
+ "command": "python",
226
+ "args": ["-m", "modlink_agent.mcp_server"],
227
+ "env": {
228
+ "MODLINK_API_KEY": "ml_live_xxxxxxxx"
229
+ }
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ `env` 段写死的值就是「配置文件/显式传入」层,优先级低于机器上真实的环境变量
236
+ (见上方 Key 表格第 1 行)。也就是说:**面板里填了明文,但进程环境变量里
237
+ 存在 `MODLINK_API_KEY` 时,后者胜出**。这样 Key 可以从 CI secret 或
238
+ `~/.bashrc` 统一注入,不必落到面板配置里。
239
+
240
+ **方式二:环境变量已在 shell 里**
241
+
242
+ ```json
243
+ {
244
+ "mcpServers": {
245
+ "modlink": {
246
+ "command": "python",
247
+ "args": ["-m", "modlink_agent.mcp_server"],
248
+ "env": {}
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ ### 工具清单(7 个)
255
+
256
+ 按「族」暴露,不按模型拆成一堆工具 —— 一模型一工具会让 Agent 在 20+ 个
257
+ 名字里纠结选择,且模型下线后工具名就变成死引用。
258
+
259
+ | 工具 | 作用 | 覆盖模型 |
260
+ |---|---|---|
261
+ | `modlink_list_models` | 列出当前 Key 可调模型 | 全部(按白名单过滤) |
262
+ | `modlink_transcribe` | 语音转写 | funasr / sensevoice / paraformer-bilingual / zipformer-bilingual |
263
+ | `modlink_synthesize` | 语音合成 | cosyvoice / indextts |
264
+ | `modlink_translate` | 文本翻译 | hymt2-translate |
265
+ | `modlink_embed` | 文本向量化 | qwen3-embedding |
266
+ | `modlink_image_process` | 图像处理 | 抠图 / 消除 / 超分 / 人脸修复 / 换脸 |
267
+ | `modlink_image_generate` | 图像生成 | flux2-generate / qwen-image-2.1 |
268
+
269
+ **不包含对话工具**(`modlink_chat` 不存在)——与 CLI 保持一致的安全边界。
270
+ 客户端可用 `modlink_list_models` 自查可调范围,但其中不会出现对话模型。
271
+
272
+ ### Server Instructions
273
+
274
+ Server 带一段面向 Agent 的说明,覆盖三类「模型自身不会说」的知识:
275
+
276
+ 1. 二进制结果返回的是**文件路径**而非内容,需要 Agent 再用文件工具读取
277
+ 2. 图像生成/处理**同卡互斥**,并发会 429
278
+ 3. 图像生成耗时长(1024×1024 数十秒到数分钟),需要给足超时
279
+
280
+ `modlink_list_models` 的存在是必要的:不调它,Agent 无从知道
281
+ 「这个 Key 到底能用什么」,只能靠猜或反复试错吃 403。
282
+
283
+ ---
284
+
285
+ ## 错误处理
286
+
287
+ | 异常 | 触发条件 | 提示要点 |
288
+ |---|---|---|
289
+ | `AuthenticationError` | 401 | Key 无效/吊销/过期,指向控制台检查 |
290
+ | `ForbiddenError` | 403 | 模型白名单未授权,提示去改白名单 |
291
+ | `InsufficientCredits` | 402 | 积分不足,指向充值页 |
292
+ | `RateLimitError` | 429 | 模型互斥,提示改串行 |
293
+ | `ModelNotFound` | 404 / 503 | 模型标识错或未部署 |
294
+ | `UpstreamError` | 5xx | 平台或引擎异常 |
295
+
296
+ 错误文案都是**给 Agent 读的**,每条都包含下一步该做什么——
297
+ Agent 拿到「HTTP 403」只会反复重试同一个错误调用。
298
+
299
+ ---
300
+
301
+ ## 测试
302
+
303
+ ```bash
304
+ python tests/test_client_local.py # 32 项:起本地假 HTTP 服务,不连生产
305
+ python tests/test_mcp_local.py # 44 项:含真实 stdio 握手
306
+ ```
307
+
308
+ `test_mcp_local.py` 会真的起一个 `python -m modlink_agent.mcp_server`
309
+ 子进程,走完整 JSON-RPC 握手并 `list_tools` —— 因为 **stdio 传输下
310
+ stdout 是协议通道,任何一处 `print` 都会破坏 JSON-RPC 帧**,
311
+ 而这类问题不真起进程就测不出来。
312
+
313
+ 真机验证覆盖 7 个族各调一次。