Skip to main content

modlink-agent —— 模联平台的 Agent 工具包

把模联(ModLink)的模型能力封装成 CLI 命令与 MCP 工具,供 AI Agent 调用。

平台侧接口见 API 文档,本包是它的 Agent 友好封装。


⚠️ 安全约束:不提供对话模型

本包刻意不封装 LLM 对话模型(qwen3-coder-next),CLI 也没有 chat 子命令。

平台十余个模型里,只有对话模型同时具备两个特性:

  1. 接受任意提示词 —— 其余模型都是「输入固定格式数据、输出固定格式结果」
  2. 能调用工具 —— 可通过 tools / tool_choice 组合出调用链

把它做成 Agent 可调用的工具,等于把 Agent 的控制面交给不可信输入。提示词注入的后果是任意的:

  • 诱导 Agent 读取不该读的文件、执行不该执行的命令
  • 通过 tools 组合出「看起来像合法业务调用」的越权链
  • 借 Agent 的身份绕过人类已经设好的权限边界

这不是能力缺失,是刻意的取舍。

  • 需要使用对话模型的用户:仍可直接调平台的 /v1/chat/completions + 自建 API Key
  • Agent 的规划与生成能力:应交给宿主自带的主模型,不要二次调用本平台的对话模型

该约束由单元测试与端到端验证双重钉死(hasattr 断言 + 子命令集检查), 防止日后有人「顺手加回来」。


安装

已发布到 PyPI,MCP 客户端推荐用 uvx——它会自动准备隔离环境,无需预先安装任何东西:

{
  "mcpServers": {
    "modlink": {
      "command": "uvx",
      "args": ["--from", "modlink-agent", "--with", "modlink-agent[mcp]", "modlink-mcp"],
      "env": { "MODLINK_API_KEY": "ml_live_xxxxxxxx" }
    }
  }
}

四个参数缺一不可,各有原因(以下均为实测结论,不是推演):

参数 作用 不写会怎样
--from modlink-agent uvx 第一个参数是包名,不是命令名 uvx modlink-mcp 会去 PyPI 找一个叫 modlink-mcp 的包,报 was not found in the package registry
--with modlink-agent[mcp] uvx 默认不装 optional-dependencies 包装成功但启动即报「缺少 MCP SDK」,且没有任何工具可用
modlink-mcp 命令名 执行的是这个入口,而不是 CLI 的 modlink

为什么配置里没有 URL

因为本 Server 跑在用户自己的机器上,不部署在服务器上:

Agent ──stdin/stdout──> 本机 modlink-mcp 进程 ──HTTPS──> 模联网关
                          ↑ 网关地址在这一层,客户端不需要知道

你可能见过配置里带 "url": "http://…" 的 MCP,那是 HTTP 传输 (server 在远端,客户端主动去连)。本包用的是 stdio 传输 (客户端在本机起一个子进程,用标准输入输出通信), 所以配置里本来就不该有 URL 字段 —— 加了反而会让人误以为 server 在远端。

网关地址有内置默认值 https://asr.syncmeet.tech:9443, 需要改时在 env 里加 MODLINK_BASE_URL 覆盖(见「换域名时该做什么」)。

uvx 是 uv 自带的工具,需要先装一次:

平台 命令
Windows `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1
macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh

包名 modlink-agent 与命令名 modlink-mcp 不同名,这是有意的: 命令名若复用 modlink,uvx modlink-agent 会去找名为 modlink-agent 的命令—— 而我们注册的是 modlink,找不到。代价是用户必须写 --from。

不用 uvx 也能走 pip,写法更短:

pip install "modlink-agent[mcp]"     # 含 MCP Server
pip install modlink-agent            # 仅 CLI

依赖只有 requests 一个;MCP SDK 是可选项,只用 CLI 不需要装。


配置 API Key

API Key 在模联用户控制台 → API Key 页创建。 支持四种来源,优先级从上到下:

优先级 来源 说明
1 环境变量 MODLINK_API_KEY 推荐。不进 shell history 与进程列表
2 配置文件 ~/.config/modlink-agent/config.json 的 apiKey 免重复输入
3 MCP 客户端配置的 env 段 见下方「MCP 接入」
4 交互式输入(仅 CLI 且在 TTY 下) 首次试用

优先级不可绕过:环境变量存在时会忽略明文。 否则「先设了环境变量、后又改了面板配置」会静默用错一把 Key, 表现为「明明配了 A 却按 B 的额度扣费」,极难排查。

为什么不用命令行传 Key

--api-key ml_live_xxx 会进 shell history,也会被 ps aux 看到。 环境变量在同一台机器上通过 /proc/PID/environ 也能被同用户读到, 但不会落进 history 与审计日志——这是 CLI 工具的通行取舍。

--key 参数仍然保留(供程序化调用),但优先级低于环境变量。

全部环境变量

变量 必填 默认 说明
MODLINK_API_KEY ✅ — API Key
MODLINK_BASE_URL https://asr.syncmeet.tech:9443 网关地址
MODLINK_WORK_DIR ./modlink-out 音频/图片落盘目录
MODLINK_TIMEOUT 600 单请求超时(秒)
MODLINK_ASYNC_THRESHOLD 15 超过该秒数视为长任务,转异步轮询

模型白名单自动生效

在用户端创建 Key 时若设了「模型白名单」,本包无需任何配置即可正确受限:

# 用只授权向量的 Key
export MODLINK_API_KEY=ml_live_xxx
modlink models
# 只列出 qwen3-embedding —— 平台侧 /v1/models 已按白名单过滤

modlink translate "你好" --to 英语
# 当前 API Key 无权调用模型 hymt2-translate。该 Key 的模型白名单为:qwen3-embedding。
# 如需调用,请在「API Key」页修改白名单或新建 Key。

CLI 用法

modlink models                      # 列出当前 Key 可调用的模型
modlink health                      # 检查平台可达性

modlink transcribe a.wav --model funasr --diarization
modlink synthesize "欢迎使用模联" --model cosyvoice
modlink translate "你好" --to 英语
modlink embed "模联平台" --dimensions 1024
modlink image photo.jpg --task upscale
modlink generate "一只橘猫" --width 1024 --height 1024

没有 chat 子命令 —— 见上方「安全约束」。

所有子命令支持 --json 输出结构化结果,便于脚本与 Agent 解析:

modlink models --json | jq '.data[].id'

图像任务对应关系

--task 默认模型 额外必填
remove_background birefnet-matting —
erase lama-inpaint --mask
upscale realesrgan-upscale —
face_restore codeformer-restore —
face_swap inswapper-face-swap --source-image

作为 Python 库

from modlink_agent import ModLinkClient

client = ModLinkClient()                     # 自动读环境变量

# 文本类直接返回结果
print(client.translate("你好", target_lang="英语"))
print(client.embed(["文档A", "文档B"], dimensions=1024))

# 二进制类返回**文件路径**,不是内容
audio = client.synthesize("欢迎使用模联")
print(audio.path)          # ./modlink-out/tts-20261006-200547-xxx.wav
print(audio.describe())    # 一句话摘要(不含任何二进制内容)

客户端没有 chat / chat_text / stream_chat 方法 —— 见上方「安全约束」。


三个为 Agent 做的关键设计

① 二进制不进上下文

一张 1024×1024 图转 base64 约 1.4MB token,会直接撑爆 Agent 上下文。 本包一律落盘后只返回绝对路径:

result = client.generate_image("一只猫")
print(result.path, result.size_bytes)   # 路径 + 字节数,不含内容

② 429 自动退避

图像模型同卡互斥(平台文档明写「必须串行调用」),Agent 天生并发,一并发就吃 429。 若把 429 原样抛出,Agent 只会反复重试并再次 429,形成死循环。

本包读 Retry-After 头做指数退避,重试耗尽后返回可执行的提示:

模型互斥或触发限流(429),重试 4 次仍失败。
平台说明:图像生成类模型**同卡互斥,必须串行调用**。
请改为逐个调用(等上一个返回后再发下一个),或稍后再试。

③ 长任务不超时

  • ASR:长音频自动走 /v1/asr/tasks 异步接口,内部轮询到完成 (按音频大小启发式判断,调用方无需决策)
  • 图像:平台无异步接口,用长超时 + 返回值带耗时秒数

实测真机耗时(供参考,用于设置 MCP 客户端超时):

操作 耗时
翻译(2 条批量) 0.16s
向量化(2 条 / 256 维) 0.09s
图像超分(64×64) 0.15s
语音合成(1 句 → 86KB WAV) 1.38s
语音转写(1 句) 0.29s

图像生成(1024×1024,20B 模型)实测需数十秒到数分钟, 且同卡互斥(并发会 429)——MCP 客户端应给足超时并串行调用。 本包已内置 429 退避,但客户端超时也要相应放宽。


关于函数调用(工具调用)

平台侧的 /v1/chat/completions 完整支持 tools / tool_choice 与多轮 tool_calls 回传(平台只做透传,不执行工具——模型只负责 「提出调用请求」,由调用方执行后把结果作为 role=tool 消息回传, 漏掉 tool_call_id 会让模型重复发起同一次调用)。

但本包不封装对话模型(见上方「安全约束」),因此 Agent 侧 不会拿到这个入口——函数调用能力请直接在平台侧使用。


MCP 接入

配置

方式一:uvx(推荐)

{
  "mcpServers": {
    "modlink": {
      "command": "uvx",
      "args": ["--from", "modlink-agent", "--with", "modlink-agent[mcp]", "modlink-mcp"],
      "env": {
        "MODLINK_API_KEY": "ml_live_xxxxxxxx"
      }
    }
  }
}

args 四个值缺一不可,原因见上方「安装」节的表格。

env 段写死的值就是「配置文件/显式传入」层,优先级低于机器上真实的环境变量 (见上方 Key 表格第 1 行)。也就是说:面板里填了明文,但进程环境变量里 存在 MODLINK_API_KEY 时,后者胜出。这样 Key 可以从 CI secret 或 ~/.bashrc 统一注入,不必落到面板配置里。

Claude Desktop 需填 uvx 的完整路径(它的 PATH 常常不含用户级可执行目录)。 先在终端跑 where uvx(Windows)或 which uvx(macOS/Linux), 把结果填进 command。其它 IDE 插件(Cursor / VS Code 等)用 uvx 即可。

首次启动会下载约 34 个包(含 MCP SDK 及其依赖),耗时十几秒到一分钟, 取决于网速。某些客户端在首次启动时会因超时而报「连接失败」, 属正常现象——等它下载完重连一次即可,后续启动是秒开。

方式二:已用 pip 装到本地

{
  "mcpServers": {
    "modlink": {
      "command": "modlink-mcp",
      "env": {}
    }
  }
}

Windows 上若提示找不到命令,改用完整路径 (C:\Users\<你>\AppData\Roaming\Python\Python312\Scripts\modlink-mcp.exe), 或改用 python -m modlink_agent.mcp_server。

方式三:从源码目录跑

{
  "mcpServers": {
    "modlink": {
      "command": "python",
      "args": ["-m", "modlink_agent.mcp_server"],
      "env": { "MODLINK_API_KEY": "ml_live_xxxxxxxx" }
    }
  }
}

前两种是用户场景,第三种供平台自身开发与排障。

工具清单(7 个)

按「族」暴露,不按模型拆成一堆工具 —— 一模型一工具会让 Agent 在 20+ 个 名字里纠结选择,且模型下线后工具名就变成死引用。

工具 作用 覆盖模型
modlink_list_models 列出当前 Key 可调模型 全部(按白名单过滤)
modlink_transcribe 语音转写 funasr / sensevoice / paraformer-bilingual / zipformer-bilingual
modlink_synthesize 语音合成 cosyvoice / indextts
modlink_translate 文本翻译 hymt2-translate
modlink_embed 文本向量化 qwen3-embedding
modlink_image_process 图像处理 抠图 / 消除 / 超分 / 人脸修复 / 换脸
modlink_image_generate 图像生成 flux2-generate / qwen-image-2.1

不包含对话工具(modlink_chat 不存在)——与 CLI 保持一致的安全边界。 客户端可用 modlink_list_models 自查可调范围,但其中不会出现对话模型。

Server Instructions

Server 带一段面向 Agent 的说明,覆盖三类「模型自身不会说」的知识:

  1. 二进制结果返回的是文件路径而非内容,需要 Agent 再用文件工具读取
  2. 图像生成/处理同卡互斥,并发会 429
  3. 图像生成耗时长(1024×1024 数十秒到数分钟),需要给足超时

modlink_list_models 的存在是必要的:不调它,Agent 无从知道 「这个 Key 到底能用什么」,只能靠猜或反复试错吃 403。


错误处理

异常 触发条件 提示要点
AuthenticationError 401 Key 无效/吊销/过期,指向控制台检查
ForbiddenError 403 模型白名单未授权,提示去改白名单
InsufficientCredits 402 积分不足,指向充值页
RateLimitError 429 模型互斥,提示改串行
ModelNotFound 404 / 503 模型标识错或未部署
NetworkError 连不上网关 点名 MODLINK_BASE_URL,并明确不要重试
UpstreamError 5xx 平台或引擎异常

错误文案都是给 Agent 读的,每条都包含下一步该做什么—— Agent 拿到「HTTP 403」只会反复重试同一个错误调用。

NetworkError 尤其关键:域名是会变的(平台换接入点、或用户填错地址)。 裸 requests 异常里只有英文堆栈,Agent 读不出「这是配置问题」, 于是反复重试同一个必然失败的地址。工具包把它翻译成 「请检查 MODLINK_BASE_URL……这是配置问题,重试无用」。


换域名时该做什么

网关地址有四级取值,优先级从高到低:

  1. 显式传入(--base-url / ModLinkClient(base_url=...))
  2. 环境变量 MODLINK_BASE_URL
  3. 配置文件 ~/.config/modlink-agent/config.json 的 baseUrl 字段
  4. 内置默认值 config.DEFAULT_BASE_URL

平台更换域名时,改代码里的默认值只对升级到新版的人生效—— 已经装在用户机器上的旧版本仍写着旧地址。完整处置:

  1. 改 config.py 的 DEFAULT_BASE_URL 与 pyproject.toml 的 project.urls,发新版;
  2. 旧域名保留一段转发,避免存量用户直接断线;
  3. 公告新的 MODLINK_BASE_URL 取值。

已知风险

uvx 每次启动都重新解析依赖。 pip 装一次的用户不受上游升级影响, 但 uvx 用户每次启动都在解析 —— 上游一次破坏性发布就能让所有用户的 MCP 同时崩溃。

因此 mcp 依赖硬 pin >=1.0,<2(mcp 2.x 已移除 mcp.server.fastmcp)。 先例:mcp-search-console 0.3.3 就是为此发过补丁版本。

若将来 mcp 2.x 的适配完成,应同时验证 uvx 与 pip 两条路径, 再放宽上限。

首次启动会下载约 34 个包(MCP SDK 及其依赖)。 部分 MCP 客户端在首次启动时会因超时而报「连接失败」—— 这是下载没完成,不是配置错误,等它下完重连即可。

包名与命令名不同名。 uvx 的第一个位置参数是包名, 所以必须写 --from modlink-agent,不能简写成 uvx modlink-mcp。 这是为避免「包名与命令名相同」带来的歧义所付的代价。


测试

python tests/test_client_local.py     # 32 项:起本地假 HTTP 服务,不连生产
python tests/test_mcp_local.py        # 44 项:含真实 stdio 握手

test_mcp_local.py 会真的起一个 python -m modlink_agent.mcp_server 子进程,走完整 JSON-RPC 握手并 list_tools —— 因为 stdio 传输下 stdout 是协议通道,任何一处 print 都会破坏 JSON-RPC 帧, 而这类问题不真起进程就测不出来。

真机验证覆盖 7 个族各调一次。

Metadata

Release files for modlink-agent 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modlink-agent 1.1.1
File Size Uploaded
modlink_agent-1.1.1.tar.gz 57.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for modlink-agent 1.1.1
File Interpreter ABI Platform
modlink_agent-1.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 100.7 kB

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files

1.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page