ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多模态、MCP、Harness:Agent 工程落地的三条边界

多模态、MCP、Harness:Agent 工程落地的三条边界

最近我在重构一个 Agent 项目,重构完以后,脑子里反复出现的三个词是:多模态、Harness、MCP。大家写技术文章时通常喜欢把它们拆开讲,好像一个是模型能力问题,一个是评测框架问题,一个是协议标准问题。但我在实际项目里的体感完全不是这样:对一个天天和 Agent 打交道的人来说,它们其实是一条链路上的三段。多模态决定了 Agent 能看见什么,MCP 决定了它能碰什么,Harness 决定了它在什么规则下运行。少了任何一环,另外两个都很难真正落地。

这篇文章算是我自己的技术笔记,主要写给正在做 Agent 开发、或者准备把多模态模型接进真实业务的开发者。我会拿最近一个“视频内容问答 + 内部资料检索 + 表格草稿生成”的项目当主线,把当时的系统设计、工具接入、踩坑记录和最终取舍都尽量写详细。如果只是听说过这几个概念,也可以把它当成一条进入 Agent 工程的路线图来读,至少能避开一些我一开始没避开的问题。

1. 为什么这三个词放在一起,才算一个完整的大问题

1.1 我最初把它们拆开解决,结果项目推进得很慢

刚开始做这个项目时,我的思路是典型的“TODO 三列并行”:模型那边处理多模态能力,工程那边搭 Harness,工具那边研究 MCP 接入。分工看起来很清楚,但实际一跑就发现出了问题。视频理解出来的结果,Harness 不知道该按什么格式存进上下文;MCP 暴露出来的一堆工具,模型不知道哪个能用、哪个不能碰;Harness 想加人工审批,却发现写操作被某个 MCP server 直接绕过,审批节点形同虚设。

这些问题的共同点是:它们全发生在三个部分的接缝处。后来我把设计文档重写了一遍,开头就画了一条链路:感知层产生观察,决策层生成动作,工具层执行动作,Harness 约束每一步。多模态、MCP、Harness 分别管这条链路的前端、后端和边界。一旦用这个视角看,很多模糊地带就清楚了。

1.2 多模态划定的是 Agent 的“感知边界”

Agent 和普通聊天机器人的最大区别在于,它要根据观察做出行动。观察哪里来?过去主要靠文本,而现实业务里大量信息在图像、音频、视频里。多模态模型在这里干的不是“识别图片”这种孤立任务,而是把非结构化媒体变成结构化上下文。比如一段视频,直接塞给模型既塞不下,也没法稳定地让模型找到某个信息点。所以我一般会先做一次媒体摘要,把视频变成带时间轴的事件列表,再喂进 Agent 的上下文。

感知边界的意思是说:模型能看到什么,它后面所有决策都建立在什么上。如果媒体摘要漏了关键信息,工具调用再准也救不回来。所以我会把感知这一步当成一个独立子系统来测,而不是让模型临场发挥。

1.3 MCP 划定的是 Agent 的“工具边界”

工具层的问题在过去是每个系统各写各的:文档检索一套 SDK,数据库查询一套连接方式,图像理解再走一个内部 HTTP 接口。Agent 的 prompt 里堆了三套完全不同的 schema,模型经常把参数名写混。MCP 的价值在于把工具抽象成统一协议:工具由 server 动态暴露,Agent 侧不用写死调用方式,换来的是可组合和可插拔。

工具边界同时也意味着权限边界。MCP server 暴露什么,Agent 就能用什么;不暴露的,Agent 再聪明也够不着。我后来把内部系统的不少接口逐步 MCP 化,一个重要理由就是:统一入口方便我做鉴权、限流和审计。

1.4 Harness 划定的是 Agent 的“运行边界”

Harness 这个词很多做纯模型的人会觉得陌生,但这恰恰是 Agent 工程和模型评估的分水岭。模型只负责生成下一段文本,Harness 负责决定这段文本是否被允许成为一次工具调用、调用顺序是否合理、循环几轮必须停、哪些动作需要人工审批。

说白一点,没有 Harness,你只是在“调用模型”;有了 Harness,你才是在“运行一个 Agent”。我后来对团队里的同学反复强调:模型是发动机,Harness 是方向盘、刹车片和仪表盘,三者缺一个都不叫一辆能上路的车。

2. 多模态接入 Agent 循环:把媒体变成结构化观察

2.1 视频不是直接喂给模型的,是喂给一条感知管线

很多人一开始会尝试把视频文件直接塞进多模态接口,让它“自己看”。实际上,商用多模态模型大都有输入长度和帧数限制,更重要的是,视频里的信息密度极不均匀,真正有用的可能就是其中十几秒。全量送进去,既浪费 token,又容易让模型被无关画面干扰。

我现在的做法是先建感知管线:按固定间隔抽帧,抽出来的帧做镜头切分和重要片段筛选,然后对每一段做视觉描述,再汇总成带时间轴的候选事件列表。这一步跑下来,一段十分钟的视频会被压缩成两三页文字摘要,Agent 拿到的就是一个干净的结构化输入。注意,这里的“抽取”最好由专门步骤来完成,不要指望着在对话里临时告诉模型“你先抽帧再总结”,否则每次输出的格式都不稳定。

2.2 自由文本描述不可靠,结构化 JSON 才是 Agent 能信的输出

刚开始做多模态感知时,我让模型输出自然语言描述:“这段视频里出现了参数表,其中参数 X 被设置为 10。”听上去没问题,但一旦后续要基于这个描述去检索文档、生成数据库查询,字符串解析就成了新的不稳定源。

后来我改成强制输出 JSON 结构,字段固定为事件类型、发生时间、关键实体、置信度、原始文本片段。模型自由发挥的空间被压缩了,但输出变成机器可解析的对象。比如视频里出现一张表格截图,我让感知模块输出含table_candidates字段的 JSON,里面每个候选带上 OCR 文本和坐标框,Agent 看到之后可以直接拿去做文档匹配。这一步是个很小的改动,却让后面的工具调用准确率明显上升。

需要提醒的是:多模态模型的 JSON 输出仍然有幻觉空间。比如画面里根本没有“版本号”,模型可能根据常识脑补一个。所以我在 JSON schema 里加了grounded: true/false字段,要求模型只有看到明确文本时才置为 true,后面 Agent 的推理逻辑会根据这个字段决定是否采信。

2.3 多模态的成本与延迟,要当成硬预算来管理

多模态调用在成本和延迟上通常远高于纯文本调用,一次带图像的推理可能要消耗大量 token,返回时间也可能到几十秒。如果把视频抽帧后每帧都独立调用一次多模态模型,延迟会直接让 Agent 的“实时性”破功。

我的做法是给感知管线加缓存层:对于相同的内容指纹,直接复用上一次抽取结果,不再重复调用模型。另一个经验是,把“低成本的预处理模型”和“高成本的精细理解模型”分开。先用轻量方法做场景切分和文本提取,只把关键片段交给高成本的多模态模型去理解。这样既控制预算,又不牺牲关键信息的准确度。

3. Harness 不只管评测,还管 Agent 的沙箱、回放和刹车

3.1 我理解的 Harness,是“运行护栏”而不只是评测工具

Harness 这个词从测试领域借来,最早指的是测试夹具。但在 Agent 工程里,它的职责远不止“跑一批测试用例看通过率”。我自己更愿意把它定义为:Agent 运行时所需的全部外部约束和支撑设施。包括工具白名单、环境隔离、步数上限、日志追踪、人工审批点、问题回放能力。

没有这套东西,你会遇到非常现实的问题:模型突然想调用一个删除类工具,你拦还是不拦?Agent 陷入循环,一直重复同一个工具调用,怎么让它停下来?昨天跑得好好的流程,今天改了一版提示词之后行为悄悄变了,你怎么才能发现?这些问题都不是模型本身能回答的,必须由 Harness 来兜底。

3.2 我在 Harness 里最常设置的三类配置

我在实际项目里通常会把 Harness 的配置拆成三类:资源限制、动作约束、观测能力。下面是我常用的默认值和理由。

配置项我的默认值理由
最大循环步数8 到 15 步太长会导致延迟和费用失控
单次工具调用超时20 到 30 秒MCP 工具可能因为网络抖动拖很久
写操作审批开启人工复核涉及数据库变更时必须有人确认
工具白名单按场景最小集减少模型误调用风险
轨迹日志每次运行都落盘支撑回放和问题定位
并发控制每用户限制并发数防止资源被单个任务打满

这些配置不能拍脑袋,要结合业务场景调。比如内部文档检索类工具可以放宽到 30 秒,但涉及支付的工具,我宁可超时短点也不要让用户等太久。

3.3 用“行为回放”做回归,比只看最终答案可靠得多

很多团队评估 Agent 只看最后答案对不对,我后来发现这远远不够。两个版本模型可能给出相同最终答案,但它们调用的工具序列完全不一样,一个稳定走文档检索,另一个在数据库查询和检索工具之间反复横跳。从产品角度,后者是灾难:每次调用都可能产生费用、触发限流、引入随机性。

所以我在 Harness 里加了一个回放模块:每次运行都记录下完整的工具调用序列、中间观察值、输入输出快照,并计算一个结构化的行为指纹。每次改模型、改提示词、改工具配置后,都用同一批种子场景跑回放,对比行为指纹的差异。凡是出现工具调用顺序漂移,就算最终答案没变,我也会当成高优先级去排查。这套方法帮我拦住过好几次“看起来没变但实际已经不稳定”的回归。

4. MCP 工具层改造:从写死 schema 到动态发现工具

4.1 为什么我决定从“直接调函数”改成 MCP 协议接入

在没有 MCP 之前,让 Agent 调用工具的基本做法是:在代码里定义一个 Python 函数,再把函数名、参数描述写进提示词,让模型输出一个 JSON 格式的调用指令,最后程序解析并执行。对于一个有两个工具的项目,这么干没问题。但一旦工具数量超过十个,维护就开始失控:prompt 被塞得越来越长,模型经常混淆参数名,每加一个新工具都要重新测一遍全量回归。

MCP 带来的变化是:工具不再由提示词静态定义,而是由 MCP server 动态暴露。Agent 运行时可以拉取当前可用的工具列表,按需调用。新增工具时,只要 server 侧声明好 schema,Agent 侧就能自动识别。我的项目从私有函数迁移到 MCP 之后,新增工具的接入时间从半天缩短到半小时以内。

4.2 在一个项目里搭起来的三个 MCP server

我在项目里最先 MCP 化的是三个最常用能力,每个 server 暴露的工具都很少,但职责很明确。

文档检索 server:暴露search_docs(query, top_k),返回文档片段列表,每个片段带来源和相似度分数。这个 server 主要解决“知识从哪里来”的问题。

视觉理解 server:暴露describe_media(media_ids),接收抽帧后的图片或视频片段,返回结构化的视觉描述与事件列表。这个 server 背后会调用多模态模型,内部做了缓存和降级。

数据库查询 server:暴露query_database(sql),但只允许只读 SQL,从源头禁止写操作。这样 Agent 可以查数据,但改数据必须走另一个需要审批的工具。

这几类工具暴露出来之后,模型的行为模式稳定了很多。因为每个 server 的工具数量都很少,工具选择空间有限,模型不容易在调用时迷路。

4.3 接入 MCP 时最容易忽略的两个问题

很多人接入 MCP 只关心“能不能把工具列表加载进来”,但实际跑起来之后,两个问题更容易让人头疼。

第一个是连接生命周期。MCP 基于长连接,多个 Agent 实例并发调用同一个 server 时,连接复用和超时管理很容易出问题。我的做法是给每个 server 的 client 加连接池,并为每次工具调用设置独立超时,避免一个慢查询拖垮整个 Agent 循环。

第二个是返回体积。MCP 工具可以返回很大的结果,比如文档片段全部塞回来,Agent 的上下文会被瞬间打爆。我的原则是:server 侧先做后处理,能摘要就摘要,能分页就分页,只返回 Agent 决策需要的那部分信息。这个点很像 API 设计里“为消费方裁剪响应”的思路,只不过消费方从人变成了模型。

5. 一个项目实例:视频内容问答加参数草稿生成

5.1 项目场景和期望行为

我最近手头的一个模拟项目是这样的:用户上传一段产品演示视频,视频里有人口播一套配置方案;同时系统里有一个内部的配置文档库,包含各种参数表格。Agent 的任务是:看完视频,找出口播中提到的关键参数,去文档库里检索对应说明,再生成一张可供业务方核对的新配置草稿。

这个任务单靠文本模型做不了,因为信息源头是视频;单靠多模态模型也做不了,因为参数和文档的比对需要外部知识;没有 Harness 也不行,因为生成草稿属于写操作,必须有人工审批闸口。正好把这一篇前面写的三块内容全串起来了。

5.2 完整流程拆解

整个流程跑起来是下面的样子:

  1. 用户上传视频,感知管线按固定策略抽帧,调用视觉理解的 MCP server 获取事件列表;
  2. 感知输出被转成结构化 JSON,写入 Agent 的上下文,同时保留原始片段链接方便追溯;
  3. 模型从事件列表里提取出候选参数实体,生成文档检索关键词;
  4. 模型调用文档检索 MCP server,获取相关的参数表片段;
  5. 模型综合视频信息和文档片段,生成草稿内容,并通过“创建草稿”工具提交;
  6. Harness 识别到这是一个写操作,自动挂起,等待人工审批;
  7. 人工确认后,草稿才真正落库。

这个流程看起来简单,但每一步都依赖前面设计时的细节。比如第 2 步如果没有结构化 JSON,第 4 步的检索词就可能是模型自己脑补的,而不是来自真实画面。

5.3 一个让我印象深刻的失败案例

第一次完整跑通后,我拿一批真实演示视频做验证,发现一个经典错误:视频口播里提到“采用 A 方案,并参考它的最小参数集”,Agent 生成的草稿里却去比对了“A_mini 方案”的参数表,最终草稿和口播内容并不一致。

排查后发现,问题出在感知阶段。抽帧摘要把“最小参数集”简化成了“参数集”,而文档检索 server 因为有模糊匹配,把“A_mini 方案”当成相关性最高的结果返回了。模型没有足够的信息去区分两者,于是跟着检索结果走了。

修复办法有两层。第一层,在感知摘要里要求模型保留完整的方案名实体,不做缩写替换;第二层,在文档检索 server 返回结果时,除了相似度分数,额外把每个方案的标题和上下文摘要一起返回。这样模型有了足够的信息可以校正误匹配。修复后,这类混淆明显减少了,但我也意识到:多模态感知的错误会一路传导到工具调用,必须在 Harness 里留好追踪链路,否则根本定位不到根因。

6. 踩过这一轮坑之后,我现在默认遵守的几条准则

6.1 我默认不会去做的事

第一,我不会再让 Agent 一次性拿到全部工具。之前图省事把所有工具一股脑通过 MCP 暴露出去,结果模型在几个相似工具之间反复犹豫,偶尔还会选错。现在每个场景只暴露最小工具集,宁可多配几个路由,也不让模型在工具选择上花费太多不确定性。

第二,我不会再把多模态输出直接当事实。多模态模型的幻觉比纯文本更难发现,因为它描述的画面往往听起来很合理。我现在强制要求感知模块把信息和原始片段绑定,没有证据支撑的观察,只能标注为“猜测”,不能直接驱动工具调用。

第三,我不再维护一套写死在提示词里的静态工具手册。工具数量少时还能应付,多了以后提示词里全是工具说明,挤占上下文,也容易和 MCP 实际返回的工具列表不一致。工具信息应该由 MCP 动态提供,提示词只管策略和风格。

6.2 我默认会固定去做的事

每次运行都开轨迹日志,哪怕只是一次内部测试。没有轨迹,后面遇到问题就只能靠猜。我现在会把trace_id贯穿整个调用链,从感知、模型生成到每个 MCP 工具调用,统一编号,方便做回放对比。

多模态成本会进入我的评估指标。之前只看准确率,后来发现某个方案准确率只提升了一个百分点,但视觉调用延迟翻了两倍,整体体验反而变差。现在我每次改动都会同时记录准确率、平均延迟、token 消耗这三个数,权衡之后再决定要不要上。

MCP server 侧的鉴权和限流会在第一天就加上,而不是等出问题再补。Agent 的调用模式和人的操作不一样,它会短时间高频触发工具,如果不做限流,一个测试循环就可能把下游系统打挂。

6.3 最后一点个人体会

把 Agent 当成“模型 + 工具 + 规则”三方协作的系统,而不是当成一个“更强的模型”,是我这一轮做得最有价值的认知转变。很多问题看起来很像是模型理解力不够,但真正排查下来,往往是感知阶段丢了信息、工具调用返回太大、或者 Harness 没有提供足够约束。多模态、MCP、Harness 这三个工程支柱,正好对应着感知、行动和规则的边界。想清楚每一条边界在哪里,Agent 才不是玩具,而是一个能放进业务里稳定跑的工具。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进