最近手头的几个 Agent 项目陆续跑起来之后,我明显感觉到一个趋势:纯粹"发一段文字、收一段文字"的玩法已经不够用了。业务方提的需求里开始出现"帮我看看这张报表截图的数据""把这个视频里提到的产品信息整理出来""按住这个操作界面走一遍流程"这类要求。于是多模态、Harness 和 MCP 这三个词,就成了我近一个月的高频检索对象。
单看概念,它们各自有各自的资料:多模态是讲模型怎么理解图片音频,Harness 讲 Agent 怎么编排工具和循环,MCP 讲怎么标准化的把外部数据源接进模型上下文。但真正把它们放在同一个 Agent 工程里,你会发现它们根本不能分开谈。多模态负责让 Agent 看得见,Harness 负责让 Agent 跑得稳,MCP 负责让 Agent 接得上。三者共同决定了一个真实业务 Agent 的天花板。
这篇笔记就是我这段时间的实践记录,里面有概念拆解、有代码片段、有踩坑清单。适合两类人看:一类是已经写过简单 Agent 原型、正准备往业务场景深挖的开发者;另一类是技术管理者,想搞明白"最近大家都在提的这几个新名词,底子里到底在解决什么问题"。
1. 先捋清三个概念在 Agent 里的分工
很多人一上来就陷入某个技术的细节里,结果越看越乱。我觉得第一步不是深入某个协议或某段代码,而是先把地图画出来:在一个 Agent 系统里,多模态、Harness、MCP 到底各自管哪一段。
1.1 多模态:Agent 的感官系统
多模态解决的是"输入太单一"的问题。传统大模型只能吃文本,也就是用户说什么你传什么。但真实业务里,信息天然是多种形态的:客户发来一张截图、扫描件里是表格、视频里有语音和字幕、设计稿里有视觉布局。多模态模型把这些非文本内容转成模型能理解的特征表示,让 Agent 有机会"自己看一眼"。
在 Agent 架构里,多模态处在最前端,承担的是感知层的角色。它决定 Agent 能不能准确理解任务里的图像约束,也决定后续工具调用是否正确。比如你要 Agent 根据一张系统截图去判断"当前哪个按钮是可用状态",模型必须真的能看清按钮位置、颜色、灰度差异。这个环节如果出错,后面 Harness 编排得再好也没用,因为它的输入已经是错的。
1.2 Harness:Agent 的骨架和执行环境
Harness 这个词在业界有两个常见的语境。一个是"评估框架",用来批量跑测试用例给 Agent 打分;另一个是"运行编排框架",用来管理 Agent 的执行流程。我在这篇笔记里主要聊后者,因为它在工程化时牵扯的细节更多。
Harness 管的是 Agent 的执行骨架:拿到用户请求之后怎么拆解、按什么顺序调用模型和工具、循环多少轮、中间结果存在哪里、出错了怎么重试、上下文多长要截断、一轮任务最多烧掉多少 token。说白了,模型是大脑,Harness 就是身体骨架和神经系统。没有 Harness,模型只能被动回答;有了 Harness,模型才具备反复尝试的能力。
我最早写 Agent 时没有 Harness 的概念,就是一个 while 循环里反复调模型接口,上下文不断拼接,直到模型说"完成了"。这种写法做 Demo 可以,一上真实业务就崩溃,后面会详细讲。
1.3 MCP:Agent 接外部世界的标准化插座
MCP 全称 Model Context Protocol,翻译过来是模型上下文协议。它解决的是"工具接入方式太碎片化"的问题。在没有 MCP 之前,每个 Agent 要接数据库、网盘、在线文档、消息系统,都得各自写一套接口函数。每个工具的鉴权方式、参数格式、返回值结构都不一样,代码里到处是 if-else 分支。MCP 把这套东西标准化:工具统一暴露为"工具列表",调用统一走一个协议消息格式,客户端的接入逻辑只需要写一次。
你可以把 MCP 理解成 USB-C 接口:以前每个设备都有自己的充电线,现在大家都按同一个标准设计接口,一套线材通用。MCP 之于 Agent 也是一样,它规定了工具提供方(Server)和工具消费方(Client)怎么握手、怎么声明能力、怎么传参、怎么返回结果。这让 Agent 在接入新工具时,不再需要从零适配私有 API,而是直接连接一个符合协议的 Server 就行。
1.4 三者的协作关系
把这三个角色放到同一个流程里看,关系就很清楚:用户发来需求,可能附着截图或语音;多模态模块先把这些非文本内容处理成模型能理解的信息;Harness 接收模型输出,判断下一步该调什么工具;工具调用通过 MCP 这个标准通道发给外部服务;外部服务的结果再回流到上下文;Harness 决定要不要进入下一轮循环;最后模型基于已有信息给出最终回复。
任意一环掉链子,整个 Agent 都会崩。多模态识别错了,后面全错;Harness 没有循环边界,任务能无限耗下去;MCP 没做好鉴权,任何使用这个 Agent 的人都能触达核心数据。所以我觉得这三个点不是三个并列的技术选项,而是一个整体工程里的三个必修课。
2. 多模态接入的实操路径和"看清"背后的细节
多模态这部分,我把自己踩过的坑和总结的流程一起写,重点讲 Agent 场景里最常用的视觉理解。
2.1 Agent 场景里最常见的多模态输入类型
我在实际项目里碰到的主要有四类:
- 截图:最常见,比如业务系统的操作界面、报错弹窗、聊天记录。Agent 需要根据截图判断当前状态,甚至模拟操作。
- 扫描件/PDF:表单、合同、发票,文本和表格混排,模型需要同时做版面分析和内容提取。
- 图片中的文字:广告图、海报、设计稿,纯英文或纯中文都还算简单,中英混排、艺术字难度就上来了。
- 音频/视频:客服录音、产品介绍视频,需要转写之后再进文本流程。这类通常不是直接丢给模型,而是先做转写,再交给 Agent 处理。
每种输入的预处理方式不太一样,但核心原则是:别把原始文件直接甩给模型,先想清楚模型到底需要看什么。
2.2 视觉理解任务的通用流程
以我做得最多的"截图理解 + 后续工具调用"为例,流程大概是四步:
- 图片预处理:压缩分辨率、裁剪无关区域、必要时转成 RGB 格式。很多模型对图片尺寸有上限,超过上限会被等比缩小,导致小字看不清。
- 图片编码:把图片交给多模态模型的视觉编码器,模型会把图像切成若干个小块,每个块转成特征向量。这个小块的分辨率理念很关键,它决定了模型能不能看清图里的小字。
- 推理生成:模型根据视觉特征和文本提示,输出对图像内容的描述、结构化数据或操作指令。
- 结构化校验:这一步很多人会漏掉。模型输出的是自然语言,你要拿它去触发工具调用,就必须从文本中抽出参数、校验格式。比如模型说"这个按钮是灰色不可用状态",Harness 需要把这个判断映射成一个布尔值,再决定分支走向。
2.3 一个关键的细节:图片分块和分辨率
这是我觉得视觉理解里最值得单独拎出来讲的一点。多数多模态模型并不是把整张图作为一个大输入直接处理,而是把图像切成固定大小的块,每个块独立编码之后再融合。这意味着图片里的文字如果横跨在两个分块边界,模型可能会丢失局部信息。
实操里的应对办法是:在丢给模型之前,把图片按目标模型建议的规则预处理。一般我会先做两件事。
第一,把图片缩放到合适的短边长度,比如 768px 或 1024px,具体看模型支持区间。不要盲目使用原图的高分辨率,很多情况下超高分辨率不会带来精度提升,反而增加计算量和 token 消耗。
第二,把图片里的关键区域适当放大。比如一张截图里,我需要模型关注的是右下角某个表格,那就先把这块区域裁剪出来再识别,而不是把整张大图直接扔过去。实测下来,裁剪后的识别准确率能提升很多。
2.4 多模态输出不可靠的三个典型时刻
依赖多模态模型时,你要接受它偶尔"看不清"或"看错"。我总结过三个高频翻车现场:
- 表格里的数字被认错,尤其是手写体或低分辨率扫描件,多一个少一个零是常事。
- 模型"脑补"图片里不存在的内容,比如截图里明明只有三个按钮,它非说还有第四个。
- 多张图混在一起时,模型弄不清哪张图对应哪个说明。
所以我的建议很简单:凡是多模态输出要驱动下一步操作的地方,必须加校验层。校验层可以是规则,比如"颜色值必须在预设集合里""金额必须是数字类型";也可以是二次提示,让模型自我纠错。多模态的容错不应该靠运气,而应该靠工程手段。
3. Harness:Agent 运行框架怎么搭才不容易翻车
这一节内容是我觉得整个笔记里最"经验向"的部分。Harness 没有统一的教科书答案,但有一些通用的设计取舍,我分享自己摸索出来的方案。
3.1 Harness 到底在管哪些事
一个可落地的 Harness 至少要管住下面几件事:
- 任务入口:接收用户的原始请求,附带多模态内容还是纯文本。
- 上下文管理:维护模型对话历史,控制长度,避免上下文爆炸。
- 工具注册:让模型知道当前环境里有哪些工具可用,以及每个工具的输入输出规范。
- 执行循环:决定什么时候该调模型、什么时候该调工具、什么时候该结束。
- 错误处理:工具超时、模型吐格式不对、外部接口限流,这些都要有兜底。
- 预算控制:一轮任务最多几轮循环、最多消耗多少 token,超过就强制熔断。
很多入门级的 Agent 框架把这些都埋在里面了,你只管写工具函数。但一旦你想做点定制逻辑,不深入理解 Harness 的设计,很容易在框架的默认行为上碰壁。
3.2 从"死循环"到"有界循环"
我最早写的伪代码是这样的:
messages = [{"role": "user", "content": "帮我查一下上个月的订单量"}] while True: response = model_completion(messages) messages.append(response) if response.has_tool_calls(): result = execute_tool(response.tool_calls) messages.append({"role": "tool", "content": result}) else: break这段代码看着没毛病,但真实跑起来会出几个问题。最大的问题是:模型可能永远不返回"最终回答",它可能不断调用工具、把中间结果塞回上下文、继续调用,直到把上下文撑爆。有一次我的演示项目跑到第 47 轮都没停,最后是因为接口报错才中断的。
后来我在 Harness 里加了三样东西:
- 最大轮数限制,默认 15 轮,超过直接终止并把中间结论返回给用户。
- 上下文长度上限,按字符数或 token 数双阈值判断,超了就触发摘要压缩。
- token 预算控制,每次调用前估一下剩余额度,不够就提前收尾。
有了这三个"刹车",Agent 的执行才算真正可控。
3.3 可观测性:给 Harness 装上仪表盘
Harness 不只是让 Agent 跑完,还要让你知道它为什么这么跑。我经历过最痛苦的一次调试是:用户反馈 Agent 回答错了,但我完全不知道它是基于哪条工具返回结果得出这个结论的。没有链路追踪,排查问题只能靠猜。
因此我后来的 Harness 强制加上了分层日志,每一轮循环都记录:
- 模型输入消息摘要,包含当前多少条消息、大概多少 token;
- 模型输出的原始内容,包括工具调用的参数;
- 工具执行的耗时、返回结果的关键片段;
- 上下文压缩或截断的触发原因。
把这些信息落成日志之后,配合时间戳和 TraceID,我基本可以还原 Agent 的每一次决策链。这个东西在业务上线后格外重要,因为你要向业务方解释"这个自动流程是怎么得出结论的",总不能说"大模型就是这么想的"。
3.4 Harness 里的状态持久化
早期我天真地认为,Agent 的所有状态都存在对话消息里就够了。后来发现不行:任务执行到一半,进程重启了,所有中间状态全丢了。用户得从头再来,这在正式产品里不能忍。
现在我会把 Harness 的状态分层存储:
- 短期状态:一轮对话里的临时变量,放内存或 Redis 都行,注意过期时间。
- 任务级状态:整个 Agent 任务的计划、已完成步骤、工具调用记录,放数据库,带任务 ID。
- 会话级状态:跨任务的用户偏好、历史摘要、授权信息,单独存储。
状态持久化之后,Agent 至少能做到"断点续跑",即使某次执行中断,也能从数据库里的任务快照恢复。当然,这带来额外复杂度,但如果你做的是严肃业务,这步不能省。
3.5 一个推荐的最小 Harness 结构
下面这个结构是我自己项目里的简化版,不依赖任何框架,方便看清楚原理:
class AgentHarness: def __init__(self, max_rounds=15, max_tokens=8000): self.messages = [] self.rounds = 0 self.max_rounds = max_rounds self.max_tokens = max_tokens def add_user_message(self, content): self.messages.append({"role": "user", "content": content}) def step(self): response = self.call_model(self.messages) self.rounds += 1 if self.rounds > self.max_rounds: raise RuntimeError("超过最大轮数") if response.is_tool_call: tool_result = self.execute_tool(response.tool_call) self.messages.append({ "role": "tool", "name": response.tool_call.name, "content": tool_result }) return "continue" else: self.messages.append({"role": "assistant", "content": response.content}) return "done" def run(self): while True: status = self.step() if status == "done": return self.messages[-1]["content"]这个结构虽然简单,却把循环、轮数、工具调用、状态存储都串起来了。你在它之上加日志、加摘要、加并发控制,就是一个能撑住真实业务的最小骨架。
4. MCP:把工具调用从"私有 API"变成"标准接口"
MCP 这个协议我一开始也是只停留在概念层,直到我真把两个不同系统接进 Agent 时,才意识到它的价值不是减少代码量,而是把接入方式从"点对点"变成"一对多"。
4.1 MCP 的核心抽象:工具、资源、提示
MCP 定义了三个核心概念,我在项目里主要用到的是"工具"这一块,但另外两个也要理解。
- 工具(Tool):可被模型调用的函数,比如查询数据库、发送消息、操作日历。工具面向的是"动作"。
- 资源(Resource):可被读取的数据,比如一个文档、一份配置、一张图片。资源面向的是"内容"。
- 提示(Prompt):可复用的提示词模板,比如"总结周报""生成复盘",这是给模型用的预设上下文。
在协议层面,最重要的消息就是工具的发现和调用。客户端先向服务器问"你有哪些工具",服务器返回工具列表和参数 schema;客户端再发起"调用某个工具",服务器执行并把结果返回。整个过程采用统一的 JSON-RPC 消息格式。
4.2 一个最小 MCP Server 实现
为了让不熟悉协议的人有个直观印象,我写一个最简的 MCP Server 示例,用标准库实现,重点是展示消息交换过程。实际生产环境里一般直接用官方 SDK,会更省事,但理解原理最重要。
import json from http.server import BaseHTTPRequestHandler, HTTPServer class MCPHandler(BaseHTTPRequestHandler): def do_POST(self): body = self.rfile.read(int(self.headers.get("Content-Length", 0))) request = json.loads(body) method = request.get("method") if method == "tools/list": response = { "jsonrpc": "2.0", "id": request.get("id"), "result": { "tools": [ { "name": "query_sales_data", "description": "查询指定月份的销售金额", "inputSchema": { "type": "object", "properties": { "month": {"type": "string"} }, "required": ["month"] } } ] } } elif method == "tools/call": params = request.get("params", {}) tool_name = params.get("name") arguments = params.get("arguments", {}) if tool_name == "query_sales_data": result = {"month": arguments.get("month"), "totalAmount": 128000} response = { "jsonrpc": "2.0", "id": request.get("id"), "result": {"content": [{"type": "text", "text": json.dumps(result)}]} } else: response = { "jsonrpc": "2.0", "id": request.get("id"), "error": {"code": -32601, "message": "unknown tool"} } else: response = { "jsonrpc": "2.0", "id": request.get("id"), "error": {"code": -32601, "message": "method not found"} } data = json.dumps(response).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) HTTPServer(("127.0.0.1", 8000), MCPHandler).serve_forever()这个示例演示了两件事:一是服务器如何向客户端"介绍"自己有哪些工具;二是服务器如何接收并执行工具调用。真实的 MCP 实现会在传输层上做更多工作,比如基于标准输入输出流、SSE、或者带鉴权的 HTTP 传输,但核心思想是一致的。
4.3 从私有实现迁移到 MCP 的实际收益
我之前有一个工具模块是写死的函数,比如有一个get_sales_data(month: str) -> dict函数,直接被我 import 进 Agent 代码里。看起来简单,但问题在于:当第二个 Agent 系统想复用这个能力时,它没法直接 import 那个模块,因为语言环境不一样、部署位置不一样、鉴权方式也不一样。于是就只能复制粘贴代码,或者写一层丑陋的 HTTP 适配器。
后来我按 MCP 把查询能力拆成了一个独立服务,Agent 端不再直接调用函数,而是通过 MCP 客户端去连接服务器。改完之后最直接的变化是:新的 Agent 接入时,只需要配置一下 MCP 服务器的地址,重新拉取一下工具列表,就能直接用上查询能力,代码几乎零改动。
这个迁移的收益不是"代码变少了",而是"边界变清楚了"。数据查询服务只负责通过 MCP 暴露自己的能力和参数格式,Agent 只负责通过 MCP 发现能力和发起调用。两边不再互相耦合在具体的函数签名里。
4.4 MCP 的安全边界:该锁死的地方必须锁死
MCP 接入外部工具时,有一个容易被忽视的隐患:模型拿到工具列表后,可能尝试调用它不应该调用的工具,或者用一些危险参数去调。所以安全控制不能只靠模型自律,必须放到服务器这层强制约束。
我会在 MCP Server 上做三件事:
- 参数白名单:对于查询类工具,只允许固定字段入参,其他参数一律拒绝。
- 权限范围:每个 MCP 客户端连接时带上身份标识,服务端按标识判断可用的工具子集。未登录的只能读公开数据,登录的才能写。
- 操作审计:每次工具调用都落日志,记录调用者、参数、返回状态。不仅方便排查,还能在异常时追溯。
在真实业务里,MCP 安全比功能更先要命。你肯定不想让一个 Agent 拿着数据库查询权限,因为一次 prompt 注入就被人指挥着把整张表导走。
5. 三个技术点拼在一起:一个报表解析 Agent 的改造实录
光讲理论不够,我结合一个具体的项目把三个技术点串起来:做一个"能看懂业务报表图片并执行查询"的 Agent。
5.1 业务场景和原始痛点
业务方给的需求很直接:市场部的同事经常在群里发一张后台报表截图,然后问"这个数对上个月怎么变化这么多"。他们希望有一个机器人能自己看截图、识别出报表里的关键指标、再自己到数据后台核实一下趋势,最后回复一段解释性结论。
这需求最开始的实现很笨:人工把截图里的数字抄进一个表格,再手工查询数据库。我们想要的是 End-to-End 的 Agent,能自动完成"看图 — 提取查询条件 — 查数 — 对比 — 回复"全流程。
5.2 整体设计:多模态进,Harness 控,MCP 出
最终落地的架构分为三层:
- 输入层:多模态模型负责解析截图,输出一个结构化的"查询意图",包含报表类型、月份、指标名称。
- 控制层:Harness 接收这个结构化意图,拆成子任务,按依赖顺序排好。先校验关键参数是否完整,再决定调用哪个数据服务。
- 工具层:数据查询能力通过 MCP Server 暴露,Harness 里的 Agent 通过 MCP 客户端连接,发起查询并拿到返回结果。
这个设计最关键的一点是:多模态模型不直接查数据库,它只负责"看和提取",查数据由 MCP 控制的正式工具完成。这样即使模型看图看错了,也只是提取出的参数不对,不会真的让模型直接摸到数据。
5.3 关键实现片段:查询意图提取和工具调用
查询意图提取这部分,我给多模态模型的提示词大约是这么设计的:
你是报表理解助手。我会给你一张截图,请提取报表类型、统计月份、主要指标,并按 JSON 格式输出。只输出 JSON 本体,不要解释。 如果截图里没有明确月份,请将月份设为 null。模型返回的 JSON 示例:
{ "report_type": "sales_summary", "month": "2025-03", "metric": "total_amount" }接下来 Harness 收到这个 JSON,会先做一次校验。校验规则包括:
- report_type 是否在支持的报表类型列表里;
- month 是否符合 YYYY-MM 格式;
- metric 是否属于已知指标名。
只有校验通过,Harness 才会继续。这步很值得坚持,因为模型输出的 JSON 可能会带上多余字符、字段名拼错甚至类型不对,不校验就发出去大概率会引发工具端报错。
工具调用阶段,Harness 通过 MCP 客户端发出 tools/call 请求:
{ "method": "tools/call", "params": { "name": "query_sales_summary", "arguments": { "month": "2025-03", "metric": "total_amount" } } }MCP Server 返回结果之后,Harness 把结果追加到上下文,再由模型生成最终的用户答复。答复里不仅要有数值,还要解释"环比变化、变化可能的原因、建议后续操作"。为了让模型解释得靠谱,我会额外给它一段同期的历史数据作为参考。
5.4 实测结果和参数选择
这个流程跑通后,我用 50 张真实报表截图做了验证。第一版的效果挺惨,准确率只有七成左右,问题集中在两处:一是截图里有多个表格,模型抓错主要指标;二是月份信息识别不准,会漏掉"3月"前面的"2025"。
后来做了三处调整,准确率提到了九成以上:
- 识别前先做表格定位,让模型只关注截图里最大区域的表格,减少干扰。
- 月份字段增加二次校验,模型如果输出 null,则启用一个正则规则从图片周围的文本里提取。
- 多模态模型输出后加一个"确认对话":模型先列出自己从截图里看到的关键信息,再由一个判断逻辑决定是否进入正式查询。这一步消耗一些额外 token,但能明显减少错误查询。
参数上,我最后把最大循环轮数设成了 10,上下文长度上限设成 6000 token 左右。视觉模型调用时,图片分辨率我控制在 1024px 短边,既不丢细节,也不浪费 token。
6. 常见问题与排查技巧实录
无论多模态、Harness 还是 MCP,实际用起来总会遇到各种各样的怪问题。我把高频问题整理成速查表,顺便补充点排查心得。
6.1 多模态相关的高频问题
视觉理解翻车,最常见的就是"看不清"和"看错"。我的排查顺序一般是这样:
- 先看原始图片分辨率,如果短边低于 512px,小字很容易糊。
- 再看模型提示词里是否要求输出结构化格式,没有的话模型可能自由发挥,把结果埋没在废话里。
- 然后看是否裁剪掉无关区域,截图里的导航栏、空白区域会分散注意力,给识别带来噪声。
- 最后再考虑换更强的多模态模型。
另外图片太大的问题也经常遇到。有些模型对单张图片的 token 消耗很高,一张高分辨率大图可能占掉几千 token。我的经验是先统一压到合理尺寸,不够清晰再局部放大,而不是一开始就用最大分辨率。
6.2 Harness 相关的排查思路
Harness 最容易出的问题是"跑着跑着不动了"或者"跑着跑着失控了"。我会优先看三点:
- 循环是否到了最大轮数,日志里有没有 "max round exceeded" 字样。如果经常触发,说明任务拆解不够细,模型在反复尝试同一个无效动作。
- 上下文是否接近上限。上下文太满时,模型会丢失早期信息,表现为答案前后矛盾,甚至开始重复调用同一个工具。
- 工具调用是否出现脏数据。某个工具返回了异常格式,模型没法解析,就会卡在重试循环里。
还有一个我犯过的低级错误:把工具返回的超长文本直接塞进上下文,导致后续模型响应越来越慢。后来我改了规则,工具返回结果如果太长,只保留前 2000 字符,并在前面加摘要。
6.3 MCP 连接常见问题
MCP 连接层的问题,大部分集中在"工具列表拉不到"和"工具调用超时"。
工具列表拉不到,我一般检查三处:服务器是否真的起来、配置的端点路径是否正确、鉴权 token 是否过期。MCP 客户端发现工具列表时,如果服务器返回空列表,最常见的原因就是服务器内部异常被静默吞掉了,导致注册表没初始化。处理办法是在服务器启动时主动打印工具注册结果。
工具调用超时,要区分是网络慢还是执行慢。我遇到过工具本身要跑 30 秒的报表计算,但 MCP 客户端默认超时只有 10 秒,结果必挂。这种问题要把超时时间调大,或者把工具改成异步任务模式,先返回一个任务 ID,再通过轮询获取最终结果。
6.4 通用排查速查表
| 问题现象 | 可能原因 | 先查哪里 |
|---|---|---|
| 多模态识别数字不对 | 图片分辨率低 / 表格混排 | 检查输入图片尺寸和裁剪区域 |
| 多模态回答偏离事实 | 模型产生幻觉 | 加结构化输出提示和二次校验 |
| Agent 循环不终止 | 没有最大轮数限制 | 看 Harness 日志里轮数是否超限 |
| 上下文过长导致响应慢 | 工具返回内容太大 | 检查工具结果是否做了截断或摘要 |
| MCP 工具列表为空 | Server 内部异常 / 注册失败 | 看 Server 启动日志和注册信息 |
| MCP 工具调用超时 | 超时时间配置不合理 | 调整客户端超时或改异步模式 |
| 工具调用参数错误 | 模型生成的参数不合法 | 在 Harness 层加参数校验规则 |
这张表不是标准答案,但它是我排查问题时的第一反应,省了不少时间。遇到问题先别急着改模型,先判断问题出在感知层、控制层还是接入层,再对症下药,效率会高很多。
最后再分享一点体会。这三个技术方向里,最容易学的是多模态的 API 调法,最难的是 Harness 的设计取舍,而 MCP 值得长期投入,因为它在一点点改变 Agent 接入外部系统的规格。我现在的习惯是每接手一个新 Agent 项目,都先问自己三个问题:模型需要"看懂"什么、执行流程怎么画边界、外部能力用什么协议接进来。把这三个问题想清楚,项目基本不会跑偏。后面如果我再碰到有意思的坑,再来更新这份笔记。