保姆级教程 · 面向想把现有工具接进工作流的创作者和开发者
项目 信息 课程系列 AI 创作入门 · L1-D 智能体/自动化 课程编号 L1D-06 难度等级 ★★★★☆ 适用人群 已会搭工作流,准备接入搜索、图片、TTS、表格或自有 API 的人 预计学时 90-120 分钟 前置课程 L1D-03、L1D-05;参考 L1B-07 语音工程化、L1C-07 联动工作流 更新日期 2026 年 8 月 内容声明 工具调用会产生真实数据读写和费用。密钥、权限、输入校验和人工审核必须先于自动执行。
学完本教程后,你将能够:
{"name": "search", "parameters": {"query": "..."}} 这种结构。模型不会凭空执行函数。完整的工具调用流程是:
用户问题:"帮我把这段文案配成语音"
│
▼
模型判断:需要调用 TTS 工具
→ 输出结构化调用请求:
{ "tool": "synthesize_voice",
"parameters": { "text": "...", "voice_id": "narrator" } }
│
▼
你的程序校验:
→ 参数合法?(text 不为空,voice_id 在白名单内)
→ 权限足够?(当前用户有 TTS 调用权限)
→ 预算足够?(本次调用费用在预算内)
│
▼
程序执行:真正调用 TTS API
→ 返回结构化结果:
{ "status": "success",
"task_id": "tts-001",
"audio_path": "/audio/tts-001.wav",
"duration": 15.3 }
│
▼
模型收到结果,生成下一步回答:
"已生成语音,时长 15.3 秒,文件保存在 /audio/tts-001.wav。
需要我播放或下载吗?"
程序永远是工具的守门人。 不能因为模型请求格式正确,就跳过权限和业务校验。
模型的角色是"判断需要什么工具、填什么参数"。程序的角色是"校验请求、执行调用、返回结果"。这两个角色不能混为一谈:
| 角色 | 职责 | 不能做什么 |
|---|---|---|
| 模型 | 判断需要调什么工具、填什么参数 | 不能直接执行、不能接触密钥 |
| 程序 | 校验、执行、返回结果 | 不能跳过校验直接执行 |
模型靠"工具描述"判断该不该用某个工具。描述写得好,模型选对工具;描述写得差,模型可能乱选或漏选。
{
"name": "search_topics",
"description": "在已授权的资料源中搜索与账号定位相关的主题",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"limit": {"type": "integer", "minimum": 1, "maximum": 20, "description": "返回数量上限"}
},
"required": ["query"]
}
}
好描述的三要素:
search_topics 比 search 更清楚(搜什么?搜选题)。limit 有最小值和最大值,防止模型填一个离谱的数字。一个好工具做到四点:名字准确、参数少而清楚、结果结构稳定、失败信息可处理。
❌ 宽接口(危险):
execute_any_api(url, method, headers, body)
→ 模型可以访问任意 URL,包括内部系统
✅ 窄接口(安全):
synthesize_voice(text, voice_id, format)
→ 只能做语音合成,不能做其他
不要提供"执行任意 SQL""访问任意 URL""删除任意文件"这种过宽工具。 工具越宽,模型误判造成的后果越大。
voice_id 比 id 好,output_format 比 fmt 好。作用:让 Agent 能搜索信息。
设计要点:
工具名:search_topics
输入:query(关键词,1-50字), limit(1-20)
输出:
results: [
{ title, summary, url, date, fetch_status }
]
拒绝:空 query、超长 query、非白名单域名
图片生成工具:
TTS 工具:
工具名:synthesize_voice
输入:text(1-2000字), voice_id(白名单), format(wav/mp3)
输出:task_id, status, audio_path, duration, voice_version
拒绝:空文本、未授权音色、超长文本、未知格式
设计要点:
工具名:read_content_library
输入:status(枚举:draft/approved/published), limit(1-50)
输出:rows: [ { id, title, status, updated_at } ]
拒绝:无权限的表、非只读操作的默认拒绝
工具名:write_content_record(需要人工确认)
输入:id, title, status
输出:task_id, write_status, affected_rows
拒绝:无写入权限、字段不合法
人工确认:是(写入前必须人工确认)
设计要点:
工具名:create_draft(可自动)
输入:platform, title, content, images
输出:draft_id, preview_url
拒绝:字段不完整
工具名:publish_draft(必须人工确认)
输入:draft_id
输出:published_url, published_at
拒绝:未审核的 draft_id
人工确认:是(发布前必须人工确认)
本节是本课的重点实战内容:把本项目 vbox 的对外 TTS API 封装成一个 Agent 可调用的"配音工具节点"。
本项目 vbox 提供两套鉴权体系:
Authorization: Bearer <jwt> 调用。/api/open/v1/*):使用 API Key,通过 Authorization: Bearer <key> 或 X-API-Key 传入。Agent 工具应该用对外 API,不要用 JWT 内部接口。 API Key 可以独立管理、限额和撤销,比 JWT 更适合工具调用场景。
重要:接入前必须先读当前
server/app/controller/open_api.py和接口文档,确认请求字段、状态码、超时和错误结构。不要把 JWT 当成对外 API Key 使用。对外 API 的响应格式(数字型status)与内部接口(字符串型status)不同,不要混用。
推荐封装为窄接口:
synthesize_voice(text, voice_id, format)
而不是把整个 HTTP 请求透传给模型。封装层负责:
┌──────────────────────────────────────────────────────┐
│ vbox TTS 工具调用流程 │
├──────────────────────────────────────────────────────┤
│ │
│ 1. Agent 决定调用 synthesize_voice │
│ 参数: text="欢迎收听本期节目", │
│ voice_id="narrator_warm", │
│ format="wav" │
│ │
│ 2. 封装层校验 │
│ - text 长度: 10 字,在 1-2000 范围内 ✓ │
│ - voice_id: 在白名单内 ✓ │
│ - format: "wav" 在白名单内 ✓ │
│ - 预算检查: 本周调用次数未超限 ✓ │
│ │
│ 3. 封装层发起 HTTP 请求 │
│ POST /api/open/v1/tts │
│ Headers: │
│ Authorization: Bearer <API_KEY> │
│ (或 X-API-Key: <API_KEY>) │
│ Body: │
│ { "text": "欢迎收听本期节目", │
│ "voice_id": "narrator_warm", │
│ "format": "wav" } │
│ │
│ 4. vbox 服务处理 │
│ - 鉴权(API Key 校验) │
│ - 调用 TTS 引擎合成 │
│ - 记录 API 用量(ApiUsage) │
│ - 返回结果 │
│ │
│ 5. 封装层收到响应 │
│ 成功: │
│ { "status": 1, │
│ "task_id": "tts-20260820-001", │
│ "audio_url": "/cdn/audio/tts-001.wav", │
│ "duration": 3.2 } │
│ 失败(示例): │
│ { "status": 0, │
│ "error": "voice_not_found", │
│ "message": "音色 ID 不存在" } │
│ │
│ 6. 封装层返回给 Agent │
│ 成功: │
│ { "status": "success", │
│ "task_id": "tts-20260820-001", │
│ "audio_path": "/cdn/audio/tts-001.wav", │
│ "duration": 3.2, │
│ "voice_version": "narrator_warm_v2" } │
│ 失败: │
│ { "status": "error", │
│ "error_code": "voice_not_found", │
│ "message": "音色 ID 不存在,请检查白名单" } │
│ │
│ 7. Agent 生成回答 │
│ "已生成语音,时长 3.2 秒。 │
│ 音色版本 narrator_warm_v2。 │
│ 文件保存在 /cdn/audio/tts-001.wav。" │
│ │
└──────────────────────────────────────────────────────┘
把这份契约文档保存下来,作为工具接入的规范:
┌──────────────────────────────────────────────────────┐
│ vbox TTS 工具契约 v1.0 │
├──────────────────────────────────────────────────────┤
│ 工具名:synthesize_voice │
│ 用途:将文本合成为语音音频 │
│ │
│ 输入参数: │
│ text (string, 必填) 1-2000 字 │
│ voice_id (string, 必填) 音色白名单内的 ID │
│ format (string, 可选) wav 或 mp3,默认 wav │
│ │
│ 输出: │
│ task_id (string) 任务唯一标识 │
│ status (string) success / error │
│ audio_path (string) 音频文件路径 │
│ duration (float) 音频时长(秒) │
│ voice_version (string) 音色版本号 │
│ error_code (string) 失败时的错误码 │
│ message (string) 失败时的可读信息 │
│ │
│ 拒绝条件: │
│ - 空文本 │
│ - 未授权的 voice_id │
│ - 超过 2000 字的文本 │
│ - 未知格式 │
│ - 超出预算或调用次数上限 │
│ │
│ 人工确认: │
│ - 发布前试听 │
│ - 声音授权确认 │
│ - 最终剪辑确认 │
│ │
│ 安全措施: │
│ - API Key 从环境变量读取 │
│ - 超时 30 秒,最多重试 2 次(间隔递增) │
│ - 调用日志不记录完整文本和 Key │
│ - 每次 API 用量被 vbox 记录到 ApiUsage 表 │
└──────────────────────────────────────────────────────┘
vbox 的 TTS API 支持单角色和多角色配音。在封装成 Agent 工具时:
工具接口可以设计为两个:
工具 1:synthesize_single_voice(text, voice_id, format)
→ 单角色配音
工具 2:synthesize_multi_voice(script, voice_mapping, format)
→ 多角色配音
script 格式:【角色名】对白内容
voice_mapping: { "旁白": "voice_1", "男主": "voice_2" }
两种模式的调用细节以当前 vbox 对外 API 文档为准。封装层负责把角色标签解析为正确的请求格式。
MCP(Model Context Protocol)可以理解为一套让 AI 客户端发现和调用外部工具的标准化约定。它解决的是"工具怎样描述、怎样暴露、怎样被客户端发现"的协作问题。
打个比方:以前每个工具都要写自己的接入文档,每个 AI 客户端都要适配不同的接入方式。MCP 像一个"标准插座"——只要工具按 MCP 标准暴露,任何支持 MCP 的客户端都能直接用。
| MCP 解决 | MCP 不解决 |
|---|---|
| 工具如何标准化描述 | 权限管理(仍需自己做) |
| 工具如何被客户端发现 | 数据合规(仍需自己确认) |
| 工具调用的基本协议 | 结果质量(仍需自己验证) |
| 多个工具的统一接入 | 成本控制(仍需自己设限) |
接入任何 MCP 服务前,逐项确认:
关键认知:MCP 解决接入标准,不替你解决安全和合规。一个 MCP 服务接入再方便,如果权限不明、数据去向不清,也不要用。
按以下顺序测试,每一步通过后再进下一步:
第 1 步:正常调用
status: success 和正确的 audio_path。duration 合理。第 2 步:参数校验
第 3 步:超时与重试
第 4 步:幂等性
task_id 重复调用 → 确认不会重复扣费或覆盖已确认结果。第 5 步:日志检查
sk-***1234)。| 项目 | 合格标准 | 通过? |
|---|---|---|
| 参数校验 | 非法值在调用外部服务前被拦截 | ☐ |
| 鉴权 | 凭证来自环境变量,不在提示词或代码中 | ☐ |
| 超时重试 | 有上限(30 秒超时,最多 2 次),避免重复副作用 | ☐ |
| 输出 | 字段稳定,包含 status 和 task_id | ☐ |
| 日志 | 能追踪,不泄露密钥和敏感文本 | ☐ |
| 人工节点 | 发布前可暂停,试听和授权确认 | ☐ |
| 成本 | 能统计单次和周期调用量 | ☐ |
| 幂等 | 重复调用不产生重复结果 | ☐ |
| 错误处理 | 失败时返回可读错误,不暴露内部堆栈 | ☐ |
不管你接入什么工具,用这张表做最终验收:
| 项目 | 合格标准 |
|---|---|
| 参数校验 | 非法值在调用外部服务前被拦截 |
| 鉴权 | 凭证来自安全配置,最小权限 |
| 超时重试 | 有上限,避免重复副作用 |
| 输出 | 字段稳定,包含状态和任务 ID |
| 日志 | 能追踪,不泄露秘密 |
| 人工节点 | 发布和不可逆动作前可暂停 |
| 成本 | 能统计单次和周期调用量 |
| 幂等 | 重试不产生重复结果 |
| 错误处理 | 失败时可读,不暴露内部信息 |
| 白名单 | 域名、参数值、工具范围有白名单 |
| 回滚 | 有问题可以禁用工具而不影响其他流程 |
不一定。可能是参数错误、鉴权失败、网络超时、限流或服务端错误。日志应区分这些原因,不能笼统重试所有错误。
排查顺序:
不建议。限制域名、路径和请求方法,并对返回内容做大小和格式限制,防止访问不该访问的资源。模型可能因为用户引导而访问恶意 URL,你的程序必须做好防护。
不能。MCP 只是接入约定,具体能力由服务暴露;权限、审核、限流和数据条款仍要自己确认。MCP 让"接入"变简单,但"安全使用"仍然是你的责任。
第一个版本接 2-3 个就够。工具越多,模型越容易选错工具或乱调。等流程稳定后,再根据明确需求逐步增加。每个新工具都要走完整的测试顺序。
三层控制:
现象:工具描述里有 20 个参数,模型经常填错或漏填。
原因:参数过多会增加模型误填概率。模型需要在多个参数之间做判断,参数越多,出错率越高。
处理:先提供完成任务必需的最小字段(3-5 个),复杂选项由固定配置控制。比如 TTS 工具只需要 text、voice_id、format 三个参数,语速、音调等高级参数用默认值,不由模型决定。
现象:搜索工具返回了 5000 字的网页全文,模型拿到后"迷失"在大量信息中,回答质量反而下降。
原因:模型处理超长上下文时,注意力会分散,关键信息容易被淹没。
处理:限制结果大小,返回摘要、状态、来源和可追踪 ID;原始大文件放在受控存储中,需要时再按 ID 取。比如搜索工具返回 title + summary(200字) + url,不返回全文。
现象:用一个万能 API Key 接所有工具,某个工具的配置泄露后,所有工具的权限都暴露了。
原因:没有按工具、环境和成员拆分凭证。
处理:
工具接入后,下一篇《L1D-07 自动化生产管线:从内容母稿到可监控交付》会把文字、图片、视频、配音和人工审核串成一条完整管线。你会学到如何把 vbox TTS 和远程图片 API 作为可替换的远程服务节点,以及如何在成本、质量和速度之间做权衡。
教程版本:v1.0 最后更新:2026-08 内容时效:API、MCP 服务和平台鉴权方式会变化,部署前请以当前官方文档和组织安全要求为准。vbox 对外 API 的具体字段和错误码以当前
server/app/controller/open_api.py和接口文档为准。