WorkBuddy开放平台实战:MCP协议驱动的Agent开发入门指南
2026/9/10 9:50:47 网站建设 项目流程

1. 这不是“又一个开放平台”,而是个人开发者真正能跑通 Agent 的第一块跳板

WorkBuddy 开放平台最近在技术圈里被反复提起,但多数人点开文档后很快关掉——不是因为没兴趣,而是发现它不像传统 API 平台那样“调个接口就能出结果”。它背后绑着 MCP(Model Control Protocol)协议、Agent 执行生命周期、Skill 编排逻辑、上下文状态管理这些新概念。我去年底开始接入,前两周几乎卡在“为什么我的 Skill 总是返回 400”上,后来才发现问题根本不在代码,而在对 WorkBuddy 整体执行模型的理解偏差。它不提供“HTTP 请求 → JSON 响应”的线性路径,而是一套带状态、可中断、支持多轮决策的 Agent 运行时环境。你提交的不是一次性的请求,而是一个可被平台调度、暂停、重试、回溯的执行单元。

核心关键词 workbuddy、开放平台、REST API、MCP、Agent 其实构成了一个三层嵌套结构:最外层是 WorkBuddy 提供的 REST API 接口层(用于注册、鉴权、触发、查询);中间层是 MCP 协议定义的通信语义(比如execute_skillstream_outputpause_execution);最内层才是你写的 Agent 逻辑本身——它可能调用本地 Python 函数、调用第三方 API、甚至启动一个小型 LLM 微服务。这三层必须对齐,缺一不可。很多人失败,是因为只写了第三层(Agent 逻辑),却没按第二层(MCP 消息格式)封装,更没通过第一层(REST API)正确注册和触发。我见过太多人把 Skill 当成普通 Webhook 写,结果平台收不到execution_id,直接判定为非法调用。

这个路径适合三类人:一是想快速验证自己 Agent 构思的独立开发者,不需要搭整套基础设施;二是正在学习 Agent 架构的学生或转行者,WorkBuddy 的沙箱环境比自己从零部署 LangChain + FastAPI + Redis 省掉至少 80% 的运维成本;三是已有成熟工具链(比如内部知识库、CRM 系统)的小团队,想用最小成本把已有能力包装成可被 AI 调用的 Skill。它不是替代你现有架构的方案,而是给你加一层“AI 可理解”的语义适配层。我自己的第一个上线 Skill 是“会议纪要自动归档到 Notion”,整个开发+调试+上线用了 3 天半,其中 2 天花在读透 MCP 的execution_context字段含义上——这个字段决定了你的 Skill 是单次执行还是支持多轮交互,而文档里只用了一句话带过。

2. 整体设计思路:为什么 WorkBuddy 不让你直接写 endpoint,而要走 MCP 封装?

2.1 传统 REST API 与 MCP 驱动型 Agent 的本质差异

你习惯的 REST API 是“请求-响应”模型:客户端发 POST /v1/translate,带 body{ "text": "hello", "to": "zh" },服务端返回{ "result": "你好" }。整个过程无状态、无上下文、不可中断。而 WorkBuddy 的 Agent 执行模型是“任务-生命周期”模型。当你调用/v1/skills/{skill_id}/trigger,平台不是立刻转发请求,而是先创建一个execution_id,然后按 MCP 协议向你的 Skill 服务发送execute_skill消息,里面包含完整的执行上下文(用户输入、历史对话、可用工具列表、超时设置、重试策略)。你的 Skill 收到后,可以:

  • 立即返回结果(简单场景);
  • 返回{"status": "running", "progress": "30%"}并保持长连接,后续由平台推送stream_output
  • 返回{"status": "paused", "reason": "awaiting_user_confirmation"},等待用户点击“确认继续”后再恢复;
  • 甚至主动调用平台提供的request_tool_use接口,申请调用另一个 Skill(比如先查天气,再生成穿衣建议)。

这种设计不是为了炫技,而是解决真实 Agent 场景中的三个硬伤:

  1. 长耗时任务无法友好反馈:比如处理 100 页 PDF,传统 API 只能超时或轮询,而 MCP 支持流式进度推送;
  2. 多步决策缺乏状态锚点:用户说“帮我订明天下午三点去机场的车”,Agent 需查航班、查路况、比价、确认支付,每一步失败都需要回退或提示,MCP 的execution_context就是这个状态快照;
  3. 工具调用权限需集中管控:你不能让每个 Skill 随意调用数据库或发邮件,平台通过 MCP 的tool_requesttool_response机制统一审计和限流。

我最初尝试绕过 MCP,直接用 Flask 写了个/api/translateendpoint,然后在 WorkBuddy 后台填这个 URL。结果平台调用时始终报错invalid_mcp_message_format。查日志才发现,WorkBuddy 的请求体根本不是标准 JSON,而是带mcp_version: "1.2"message_type: "execute_skill"的 MCP 格式消息。它不接受“裸”HTTP 接口,只认 MCP 协议——这是设计底线,不是可选项。

2.2 为什么选择 REST API + MCP 组合,而不是纯 WebSocket 或 gRPC?

WorkBuddy 开放平台没有采用 WebSocket 或 gRPC,是有明确取舍的。WebSocket 适合高频双向通信(如实时聊天),但对 Skill 开发者来说,意味着必须维护长连接、处理心跳、应对断连重连,增加了 70% 的基础代码量。gRPC 虽高效,但要求开发者安装 protoc、编译 .proto 文件、处理二进制序列化,对 Python/JS 主力军不友好。而 REST + MCP 的组合,本质是“用最通用的传输层(HTTP),承载最灵活的语义层(MCP)”。

具体到实现,WorkBuddy 的 REST API 只做四件事:

  • POST /v1/applications:注册应用,获取client_idclient_secret
  • POST /v1/skills:注册 Skill,提交元信息(名称、描述、图标、MCP 兼容版本);
  • POST /v1/skills/{id}/trigger:触发执行,返回execution_id
  • GET /v1/executions/{id}:查询执行状态(含日志、输出、错误堆栈)。

所有业务逻辑、状态流转、工具调用,都通过 MCP 消息在 Skill 服务和平台之间完成。这意味着你的 Skill 服务可以是任何语言写的 HTTP 服务,只要它能解析 MCP 消息、按规范返回响应即可。我用 Python FastAPI 实现的第一个 Skill,核心逻辑只有 47 行代码,其中 32 行是解析execution_context和构造execute_skill响应,剩下 15 行才是真正的业务(调 Notion API)。如果换成 gRPC,光是生成 client stub 就得 200 行起步。

2.3 Skill 架构分层:从“函数”到“可调度单元”的跃迁

很多开发者以为写个 Skill 就是写个函数,比如def translate(text, to_lang)。但在 WorkBuddy 体系里,Skill 必须是一个可被平台识别、调度、监控、计费的独立运行单元。它有明确的三层结构:

  • 接入层(Adapter):负责接收平台 HTTP 请求,解析 MCP 消息,校验签名,转换为内部调用参数。这一层必须严格遵循 MCP v1.2 规范,字段名、类型、必选/可选属性都不能错。比如execution_context中的user_id是字符串,但如果你传了数字,平台会直接拒绝。
  • 逻辑层(Core Logic):这才是你熟悉的业务代码。但它不能直接操作 HTTP 响应,而要返回一个标准化的SkillResult对象,包含output(最终结果)、intermediate_steps(中间步骤,用于调试)、tools_used(调用的外部工具列表)。
  • 适配层(Tool Adapter):当 Skill 需要调用其他系统(如数据库、邮件服务)时,不能硬编码连接字符串,而要通过平台提供的tool_call接口申请。比如你要发邮件,得先返回{"tool_request": {"name": "send_email", "parameters": {...}}},平台审核通过后,再向你的服务推送tool_response

我踩过最大的坑是在逻辑层直接用了requests.post("https://internal-api.example.com/notify")。测试时一切正常,但上线后发现平台防火墙屏蔽了该域名,且日志里没有任何错误提示——因为请求根本没发出,被网络层拦截了。后来改成用tool_call申请http_request工具,平台自动注入代理和认证头,问题瞬间解决。这说明 WorkBuddy 的设计哲学是:把基础设施依赖显式化、可控化、可审计化,而不是让开发者在黑盒里瞎猜。

3. 核心细节解析:从注册应用到 Skill 上线的 7 个关键实操节点

3.1 应用注册:别只盯着 client_secret,redirect_uri的坑比想象中深

注册应用看似简单:填名称、描述、官网,拿到client_idclient_secret。但redirect_uri这个字段,90% 的新手填错。它不是你前端页面的地址,而是 WorkBuddy 在 OAuth 流程中回调的后端地址。比如你前端在https://myapp.com/login触发登录,WorkBuddy 会重定向到https://api.myapp.com/auth/callback,这个https://api.myapp.com/auth/callback才是redirect_uri

常见错误:

  • 填成https://myapp.com/callback(前端地址,平台无法访问);
  • 填成http://localhost:3000/callback(本地开发用,上线必须换);
  • 填多个 URI 用空格分隔(正确方式是用英文逗号,分隔);
  • URI 末尾带/(平台校验严格匹配,https://api.com/callbackhttps://api.com/callback/视为不同)。

我第一次填错,导致用户授权后页面白屏,控制台报invalid_redirect_uri。查文档才发现,WorkBuddy 的 OAuth 2.0 实现要求redirect_uri必须精确匹配注册值,且必须是 HTTPS(除非 localhost)。解决方案是:开发阶段用https://localhost:8000/callback,配合 mkcert 生成本地证书;上线后用 Nginx 反向代理,确保redirect_uri指向你的 API 服务,而非前端。

提示:redirect_uri一旦注册无法修改,只能删掉重建应用。所以建议首次注册时就规划好环境:devstagingprod各建一个应用,对应不同的redirect_uri

3.2 Skill 元数据注册:icon_url 不是摆设,它影响用户信任度

注册 Skill 时,除了namedescriptionendpoint_urlicon_url是最容易被忽略但最关键的字段。它不只是显示在 WorkBuddy 工作台上的小图标,更是平台判断 Skill 可信度的信号之一。WorkBuddy 会检查icon_url是否满足:

  • 必须是 HTTPS 协议;
  • 图片尺寸必须是 64x64 像素(非此尺寸会被拉伸变形);
  • 文件大小不超过 100KB;
  • 不能是 base64 编码的 data URL(平台不支持)。

我最初用 Figma 导出的 PNG,尺寸是 128x128,上传后图标在工作台显示为模糊马赛克。后来用 ImageMagick 压缩并裁剪:convert icon.png -resize 64x64 -quality 85 icon_64.png,问题解决。更隐蔽的坑是 CDN 缓存:我更新了图标,但用户看到的还是旧版。解决方案是在 URL 后加时间戳参数:https://cdn.example.com/icon.png?v=20240520

另外,description字段有 200 字限制,但很多人写成“本 Skill 用于翻译文本”。这毫无竞争力。更好的写法是:“一键将会议录音转文字并翻译成中文,支持保留说话人标记和时间戳,输出 Markdown 格式”。前者是功能描述,后者是场景价值,用户一眼就知道能解决什么问题。

3.3 MCP 消息解析:execution_context里的session_id是状态管理的钥匙

当你收到平台发来的execute_skill请求,body 类似这样:

{ "mcp_version": "1.2", "message_type": "execute_skill", "execution_id": "exec_abc123", "execution_context": { "user_id": "usr_xyz789", "session_id": "sess_def456", "input": "把这份合同翻译成英文", "history": [ {"role": "user", "content": "帮我查下张三的合同"}, {"role": "assistant", "content": "已找到,合同编号 CT2024-001"} ], "available_tools": ["notion_read", "translate_api"], "timeout_ms": 30000 } }

其中session_id是关键。它标识了用户本次会话的上下文,不是每次请求都变。比如用户连续问:

  1. “把这份合同翻译成英文”
  2. “再把翻译结果发给李四”
  3. “顺便告诉他 deadline 是下周三”

这三个请求的session_id相同,但execution_id不同。这意味着你的 Skill 可以基于session_id缓存一些中间结果(比如第一次翻译后的英文文本),避免重复调用翻译 API。我做的“合同处理”Skill 就利用这点:第一次收到翻译请求,调用 DeepSeek API 得到结果,并存入 Redis(key 为session:{session_id}:translation);第二次收到“发给李四”,直接从 Redis 读取,省去 2 秒 API 延迟。

注意:session_id由平台生成,你不能自己生成或修改。缓存时务必加上过期时间(建议 30 分钟),避免内存泄漏。

3.4 Skill 响应构造:status字段决定你是“执行者”还是“协调者”

MCP 规范定义了 Skill 响应的status字段有四个合法值:

  • "success":任务完成,返回最终结果;
  • "running":任务进行中,需后续流式推送;
  • "paused":需要用户干预,比如确认敏感操作;
  • "failed":执行出错,附带error_codeerror_message

很多人只用"success""failed",错过了 Agent 的核心能力。比如“发送邮件”Skill,如果直接返回"success",用户不知道邮件是否真发出去;如果返回"running",你可以后续推送{"progress": "50%", "message": "正在连接 SMTP 服务器..."},最后再发一次"success"

更高级的用法是"paused"。我做的“财务报销”Skill,当检测到报销金额超过 5000 元时,返回:

{ "status": "paused", "pause_reason": "amount_exceeds_approval_limit", "required_action": { "type": "confirm", "message": "报销金额 ¥5,200 超过部门审批限额(¥5,000),是否提交至总监审批?", "options": ["是", "否"] } }

平台会弹出确认框,用户点击“是”后,再向你的 Skill 发送resume_execution消息,携带用户选择。这时你才真正调用财务系统 API。这种设计把风控逻辑从代码里抽出来,交由平台 UI 统一处理,既安全又一致。

3.5 工具调用(Tool Calling):不是“调 API”,而是“申请权限”

当 Skill 需要调用外部服务,不能直接发 HTTP 请求,而要通过 MCP 的tool_request。例如,你想查用户在 Notion 中的待办事项:

{ "status": "success", "tool_request": { "name": "notion_list_tasks", "parameters": { "database_id": "db_abc123", "filter": {"property": "Status", "equals": "To Do"} } } }

平台收到后,会:

  1. 校验notion_list_tasks是否在available_tools列表中;
  2. 检查当前用户是否有该工具的调用权限(管理员可配置);
  3. 注入认证凭据(如 Notion 的 integration token);
  4. 代为调用,并将结果封装成tool_response发回你的 Skill。

这个过程的关键是:你永远看不到原始 API 密钥。所有敏感凭据由平台托管,Skill 只通过抽象的工具名操作。我曾试图在tool_requestparameters里硬编码 token,结果平台直接返回invalid_tool_parameters错误。后来才明白,parameters只能传业务参数(如database_id),认证信息由平台自动注入。

3.6 本地调试:用workbuddy-cli模拟平台请求,比 Postman 高效 10 倍

WorkBuddy 官方提供了workbuddy-cli工具(npm install -g workbuddy-cli),它能模拟平台所有请求,无需部署到公网。调试流程是:

  1. 启动你的 Skill 服务(如uvicorn main:app --host 0.0.0.0 --port 8000);
  2. 运行workbuddy-cli trigger --skill-id sk_123 --input "hello world" --env dev
  3. CLI 自动构造符合 MCP v1.2 的请求,发送到http://localhost:8000/mcp
  4. 显示完整请求/响应日志,包括 HTTP 状态码、MCP 字段校验结果。

比 Postman 高效的地方在于:

  • 自动生成execution_idmcp_versionmessage_type
  • 自动计算并添加X-WorkBuddy-Signature签名头(需配置client_secret);
  • 内置 MCP 字段校验器,比如告诉你"execution_context.user_id is required but missing"

我用它调试execution_context解析逻辑时,发现history字段有时为空数组[],有时为null。文档没写清楚,但 CLI 的错误提示明确说"history must be an array",让我立刻补上默认值处理。

3.7 上线前必做三件事:签名验证、超时设置、错误分类

上线前,务必验证以下三点,否则生产环境会频繁失败:

  1. 签名验证:WorkBuddy 所有请求都带X-WorkBuddy-Signature头,格式为sha256=<hex_digest>,其中 digest 是body + client_secret的 SHA256。必须验证,否则恶意请求可伪造execution_id。我用 Python 的hmac模块实现:
import hmac import hashlib def verify_signature(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature.split('sha256=')[1], expected)
  1. 超时设置:Skill 服务的 HTTP 超时必须小于平台设置的timeout_ms(默认 30s)。我在 FastAPI 中设timeout=25,留 5 秒缓冲。如果设成 35s,平台会在 30s 后取消执行,但你的服务还在跑,造成资源浪费。

  2. 错误分类:不要所有错误都返回{"status": "failed", "error_message": "unknown error"}。WorkBuddy 期望具体的error_code,如"notion_api_rate_limit""translate_quota_exceeded"。平台会根据 code 做不同处理(如限流时自动重试,配额超限时提示用户升级)。

4. 实操过程:从零搭建一个“会议纪要智能归档”Skill 的完整记录

4.1 需求拆解:用户要的不是“转文字”,而是“可追溯的归档动作”

用户需求原文:“把会议录音自动转文字,提取关键结论,存到 Notion 指定数据库,并生成摘要发 Slack”。表面看是语音转文字 + NLP + API 调用,但深入分析发现三个隐藏需求:

  • 可追溯性:用户要能查到“哪次会议、谁发起、何时归档、用了哪个模型”;
  • 可干预性:如果 NLP 提取的结论有误,用户应能手动编辑再保存;
  • 可审计性:管理员要能看到所有归档记录的操作日志。

这意味着 Skill 不能是黑盒流水线,而要有明确的状态节点。我设计了四阶段执行流:

  1. transcribe_audio:调用 Whisper API,返回原始文字;
  2. extract_conclusions:用 LLM 提炼 3-5 条结论,返回结构化 JSON;
  3. create_notion_page:在 Notion 数据库创建页面,填入文字和结论;
  4. post_to_slack:发摘要到 Slack 频道。

每个阶段都对应一个 MCP 的status状态,用户可在任一阶段暂停或重试。

4.2 环境准备:用 Docker Compose 一键启动开发环境

不用折腾本地依赖,我用 Docker Compose 管理所有服务:

# docker-compose.yml version: '3.8' services: skill-app: build: . ports: ["8000:8000"] environment: - WORKBUDDY_CLIENT_SECRET=your_secret - NOTION_INTEGRATION_TOKEN=secret_xxx - SLACK_BOT_TOKEN=xoxb-xxx depends_on: [redis] redis: image: redis:7-alpine ports: ["6379:6379"] ngrok: image: wernight/ngrok:latest command: ngrok http --domain=your-subdomain.ngrok.io 8000 ports: ["4040:4040"]

ngrok服务自动暴露本地8000端口为公网 URL,填入 WorkBuddy 后台的endpoint_urlredis用于缓存session_id关联的中间结果。整个环境docker-compose up -d一条命令启动,比手动配环境快 5 倍。

4.3 核心代码实现:FastAPI + MCP 适配器的 62 行主逻辑

以下是main.py的核心部分(已脱敏,保留关键结构):

from fastapi import FastAPI, Request, HTTPException from pydantic import BaseModel import hmac import hashlib import json import redis import os app = FastAPI() r = redis.Redis(host='redis', port=6379, db=0) class MCPRequest(BaseModel): mcp_version: str message_type: str execution_id: str execution_context: dict @app.post("/mcp") async def handle_mcp(request: Request): body = await request.body() signature = request.headers.get("X-WorkBuddy-Signature") # 1. 签名验证 if not verify_signature(body, signature, os.getenv("WORKBUDDY_CLIENT_SECRET")): raise HTTPException(401, "Invalid signature") # 2. 解析 MCP 消息 try: data = json.loads(body) req = MCPRequest(**data) except Exception as e: raise HTTPException(400, f"Invalid MCP format: {e}") # 3. 提取关键上下文 session_id = req.execution_context.get("session_id", "unknown") user_input = req.execution_context.get("input", "") # 4. 从缓存读取或初始化执行状态 state_key = f"state:{req.execution_id}" state = r.hgetall(state_key) or {} # 5. 根据当前状态执行对应逻辑 if not state: # 首次执行:转文字 transcription = await transcribe_audio(user_input) r.hset(state_key, mapping={"transcription": transcription, "step": "transcribed"}) return {"status": "success", "output": {"transcription": transcription}} elif state.get(b"step") == b"transcribed": # 第二步:提取结论 conclusions = await extract_conclusions(state[b"transcription"].decode()) r.hset(state_key, mapping={"conclusions": json.dumps(conclusions), "step": "concluded"}) return {"status": "success", "output": {"conclusions": conclusions}} elif state.get(b"step") == b"concluded": # 第三步:存 Notion page_id = await create_notion_page( state[b"transcription"].decode(), json.loads(state[b"conclusions"].decode()) ) r.hset(state_key, mapping={"page_id": page_id, "step": "notioned"}) return {"status": "success", "output": {"notion_page_id": page_id}} else: # 最后一步:发 Slack await post_to_slack(state[b"transcription"].decode()) r.delete(state_key) # 清理状态 return {"status": "success", "output": {"done": True}} def verify_signature(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() return hmac.compare_digest(signature.split('sha256=')[1], expected)

这段代码体现了 WorkBuddy Skill 的典型模式:状态驱动、分步执行、缓存协同。62 行代码覆盖了签名、解析、状态管理、分步逻辑,比写一个纯 REST API 精简得多。

4.4 MCP 消息构造细节:execution_context字段的实测兼容性表

execution_context是 Skill 的输入核心,但不同场景下字段存在性不同。我实测了 200+ 次触发,总结出字段兼容性:

字段名是否必填类型说明实测备注
user_idstring用户唯一标识平台保证非空,长度 12-32 字符
session_idstring会话 ID同一会话内所有请求相同
inputstring用户原始输入可能含 emoji、换行符,需 utf-8 处理
historyarray对话历史首次请求为[],后续为[{"role":"user","content":"..."}]
available_toolsarray可用工具列表若未配置工具,此字段不存在
timeout_msnumber超时毫秒数默认 30000,可被平台覆盖

特别注意history字段:当用户首次发起请求时,history是空数组[],不是null。如果代码里写if context['history']:会误判为 False,导致跳过历史分析逻辑。正确写法是if context.get('history') and len(context['history']) > 0:

4.5 上线发布:WorkBuddy 后台的 5 个发布检查点

在 WorkBuddy 开放平台后台发布 Skill,必须通过以下检查:

  1. Endpoint 可达性测试:平台会向你的endpoint_url发送GET /health请求,必须返回200 OK{"status": "healthy"}。我一开始返回 HTML,结果卡在“健康检查失败”。
  2. MCP 兼容性扫描:平台用内置解析器检查你的响应是否符合 MCP v1.2 schema。比如status字段值必须是枚举值,不能是"ok"
  3. 图标合规性检查:自动下载icon_url,验证尺寸、格式、大小。PNG/JPEG 支持,GIF 不支持。
  4. 权限声明审查:如果你的 Skill 声明需要notion_write权限,平台会检查你是否在available_tools中列出了notion_write
  5. 沙箱环境执行测试:平台用预设用例(如input: "test")触发 Skill,验证能否在 10 秒内返回status: "success"

我第 3 次发布才通过,原因是icon_url的图片用了 WebP 格式(平台只支持 PNG/JPEG),改用 PNG 后立即通过。

5. 常见问题与排查技巧实录:那些文档里不会写的实战经验

5.1 问题速查表:高频错误代码与根因定位

错误代码HTTP 状态码典型现象根本原因解决方案
invalid_mcp_message_format400平台日志显示“MCP 解析失败”execution_context缺少必填字段,或status值非法workbuddy-cli检查请求体,对照 MCP v1.2 schema
signature_verification_failed401所有请求都被拒X-WorkBuddy-Signature计算错误,或client_secret配错确保 body 是原始字节(非 decoded JSON),secret 无空格
execution_timeout408技能执行一半中断Skill 服务 HTTP 超时 > 平台timeout_ms设 Skill 超时为timeout_ms - 5000
tool_not_available403tool_request被拒绝available_tools未声明该工具,或用户无权限在 Skill 注册时勾选所需工具,在后台分配权限
rate_limit_exceeded429突然大量失败平台对 Skill 的 QPS 限流(默认 5 QPS)在后台申请提额,或加本地缓存减少调用

我遇到过一次rate_limit_exceeded,原因是用户批量上传 100 个音频文件,触发 100 次 Skill。临时方案是加 Redis 计数器,1 秒内只允许 5 次调用;长期方案是联系 WorkBuddy 运营提额。

5.2 日志调试黄金法则:三段式日志结构

WorkBuddy 的执行日志只显示最后 100 行,且不区分服务端/客户端。我强制在 Skill 里用三段式日志:

# 格式:[EXECUTION_ID] [STAGE] [MESSAGE] logger.info(f"[{execution_id}] TRANSCRIBE_START Input length: {len(user_input)} chars") logger.info(f"[{execution_id}] TRANSCRIBE_END Result: {transcription[:50]}...") logger.error(f"[{execution_id}] NOTION_ERROR Status: {resp.status_code}, Body: {resp.text}")

这样在平台日志里搜索exec_abc123,就能串起完整执行链。比用print()好 10 倍。

5.3 签名验证的五个致命陷阱

  1. Body 必须是原始字节json.loads(request.body())后再签名是错的,必须用await request.body()的原始 bytes。
  2. Secret 末尾换行符:复制client_secret时可能带\n,用strip()清理。
  3. Signature 头格式:必须是sha256=abcdef123...,不能是SHA256: abcdef
  4. HMAC 比较用hmac.compare_digest:防止时序攻击,不能用==
  5. 大小写敏感X-WorkBuddy-Signature不能写成x-workbuddy-signature

我栽在第 2 条:client_secret复制时带了换行,导致签名永远不匹配。用repr(secret)打印才发现'\n'

5.4 本地开发与生产环境的三大差异

差异点本地开发生产环境应对方案
网络可达性localhost可访问平台无法访问localhost用 ngrok 或云服务器部署
SSL 证书mkcert 生成自签名平台要求有效 HTTPS用 Let's Encrypt 或云厂商免费证书
环境变量.env文件平台后台配置代码中os.getenv("KEY", "default"),避免崩溃

特别提醒:WorkBuddy 生产环境强制 HTTPS,HTTP 的endpoint_url会被拒绝。我上线前忘了配 Nginx SSL,结果所有请求 502。

5.5 Agent 执行终止的三种真实场景与对策

agent execution terminated due to error.这个错误很宽泛,实际分三类:

  1. 平台侧终止:如用户取消、超时、配额用尽。此时execution_id仍有效,可查GET /v1/executions/{id}获取termination_reason
  2. Skill 侧终止:你的代码抛出未捕获异常。WorkBuddy 会捕获并记录堆栈,但status仍为"failed"。对策:全局异常处理器,返回结构化错误。
  3. 网络侧终止:Skill 服务宕机或网络中断。平台会重试 3 次,间隔 1s。对策:确保服务高可用,用 PM2

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询