1. 从单模态到多模态Agent 感知能力的质变1.1 为什么纯文本 Agent 越来越不够用过去一年我一直在做 Agent 相关的开发最开始接触的都是纯文本场景——用户输入一段话模型理解意图调用工具返回结果。这套链路跑通之后你会发现一个很尴尬的事实真实世界里大量的信息根本不是纯文本形态的。用户发来一张截图让你分析界面问题发来一段录音让你整理会议纪要发来一个 PDF 扫描件让你提取表格数据。这些场景你用纯文本模型去硬扛要么做大量预处理把信息“降维”成文字要么直接告诉用户“抱歉我处理不了”。我踩过最典型的一个坑是早期做一个文档问答 Agent用户上传的 PDF 里有很多图表和流程图纯文本提取出来全是乱码和错位的表格。后来不得不引入 OCR 和版面分析但那是外挂式的补丁方案维护成本极高。多模态大模型的出现本质上是把“感知”这件事从外挂变成了内生能力。1.2 多模态能力在 Agent 链路中的三个关键位置多模态不是简单地把图片塞给模型就完事了。在实际 Agent 架构里它至少出现在三个位置每个位置的设计考量完全不同。第一个位置是输入感知层。用户发来的原始素材可能是图片、音频、视频、文档混合体。这一层要做的是把异构输入统一成模型能理解的 token 序列。这里有个容易忽略的细节图片的分辨率和切分策略直接影响后续推理质量。我实测下来对于界面截图类任务把长图按语义区块切分成多张子图分别送入比整图压缩后送入的准确率高出一大截。第二个位置是中间推理层。Agent 在执行多步任务时中间产物可能包含图表、示意图。比如让 Agent 根据数据生成一份分析报告它需要先画图再解读图。如果模型本身具备多模态理解能力它可以在生成图表后“回头看”自己的输出做自我校验。这个能力在纯文本模型上是缺失的。第三个位置是输出生成层。Agent 最终交付给用户的结果不一定是文字可能是带标注的图片、结构化表格、甚至一段合成语音。多模态输出让 Agent 的交付形态更贴近真实工作流。1.3 多模态 Agent 的典型架构与数据流我目前用的架构大致是这样的入口是一个路由模块判断输入类型并分发到对应的预处理管道。图片走视觉编码器音频走语音识别加声学特征提取文档走版面分析加 OCR。预处理后的统一表示进入主推理循环由大模型决定下一步动作——是调用工具、是继续推理、还是直接生成回复。这里有个设计上的取舍预处理到底做多深我的经验是能交给模型做的就不要在预处理阶段做死。比如图片里的文字识别早期我会在预处理阶段用 OCR 全部提取出来再送模型后来发现模型自己“看”图的能力已经很强了过度预处理反而丢失了版面信息。现在的做法是预处理只做最基础的格式转换和尺寸归一化把原始视觉信息尽量完整地保留给模型。注意多模态输入的 token 消耗远高于纯文本。一张高清图片可能消耗上千 token在做成本估算时一定要把这块算进去。我的做法是对图片做自适应压缩在保证关键信息不丢失的前提下控制 token 消耗。2. Harness 工程化让 Agent 从 Demo 走向可用2.1 什么是 Harness为什么它决定 Agent 的生死Harness 这个词在 Agent 开发圈里越来越高频直译过来是“挽具”或“ harness”但在工程语境下我理解它指的是包裹在模型外面的那一整套运行时框架——包括提示词管理、工具注册与调度、上下文窗口管理、错误处理与重试、状态持久化、可观测性等等。为什么说它决定生死因为模型能力再强如果没有一套可靠的 Harness 去承接Agent 在实际使用中会频繁出现“跑飞”的情况。我见过太多 Demo 很惊艳但一上生产就崩掉的案例问题几乎都出在 Harness 层上下文超了没处理、工具调用失败了没重试、多轮对话状态丢了、并发请求把 API 打爆了。2.2 Harness 的核心模块拆解我自己的 Harness 实现经过多次迭代目前稳定下来的核心模块有这么几个。提示词管理层负责模板的版本管理和动态组装。这里的关键是不要把提示词写死在代码里而是做成可配置的模板支持变量注入和条件片段。我吃过亏的地方是早期提示词硬编码改一个标点都要重新部署后来改成模板文件加热加载迭代效率提升非常明显。工具注册与调度层是 Harness 的心脏。每个工具需要声明名称、描述、参数 schema、执行函数、超时时间、重试策略。调度器根据模型返回的工具调用请求做参数校验、权限检查、并发控制、结果格式化。这里有个细节工具描述的质量直接影响模型选工具的准确率。我试过同一个功能用不同措辞写描述调用准确率能差出百分之二三十。上下文窗口管理是最容易被低估的模块。多轮对话加上工具返回结果上下文膨胀速度远超想象。我的策略是分层管理系统提示和核心指令永远保留近期对话保留原文远期对话做摘要压缩工具返回的大块数据只保留关键字段。这套策略下来同样长度的对话token 消耗能降低一半以上。错误处理与重试需要区分错误类型。网络超时类错误可以自动重试参数校验失败类错误应该把错误信息返回给模型让它自己修正权限类错误则应该直接终止并告知用户。一刀切的重试策略只会浪费 token 和时间。可观测性是我后期才补上的但补上之后发现真香。每次 Agent 运行的完整轨迹——输入、每步推理、工具调用参数和结果、最终输出、耗时、token 消耗——全部落盘。排查问题时直接看轨迹比翻日志快得多。2.3 Harness 设计中的常见陷阱第一个陷阱是过度抽象。一开始就想设计一个万能框架结果抽象层太多调试的时候一层层扒代码效率极低。我的建议是先用最直接的方式把链路跑通等模式稳定了再抽公共部分。第二个陷阱是忽略并发安全。Agent 服务通常要处理多个并发请求如果 Harness 里有全局可变状态很容易出现请求间互相污染。我遇到过一次诡异的问题两个并发请求的工具调用结果串了排查半天发现是工具注册表用了全局字典且没有做请求级隔离。第三个陷阱是状态管理混乱。多轮对话的状态到底存在哪、怎么过期、怎么恢复这些问题不提前想清楚后期会非常痛苦。我现在的做法是每个会话一个独立的状态对象持久化到外部存储Harness 本身无状态。3. MCP 协议工具生态的标准化尝试3.1 MCP 解决了什么问题MCP 全称是 Model Context Protocol我理解它的核心目标是把工具的定义和调用标准化。在没有 MCP 之前每个 Agent 框架都有自己的工具注册方式换个框架就要重写一遍工具适配层。MCP 试图定义一套通用的协议让工具提供方和工具使用方解耦。这个思路其实不新鲜类似 LSP 之于编辑器、USB 之于外设。但在 Agent 领域它的价值在于当工具生态足够丰富时标准化协议能极大降低集成成本。你可以想象一个场景某个团队开发了一个数据库查询工具只要它实现了 MCP 协议任何支持 MCP 的 Agent 框架都能直接使用不需要为每个框架单独适配。3.2 MCP 的核心概念与工作方式MCP 的核心概念包括 Server、Client、Resource、Tool、Prompt 几个部分。Server 是能力的提供方Client 是能力的消费方Resource 是可读取的数据源Tool 是可调用的函数Prompt 是可复用的提示模板。工作方式上MCP 支持多种传输层本地场景常用标准输入输出远程场景走网络协议。Client 连接到 Server 后可以查询 Server 暴露了哪些能力然后按需调用。这个发现机制很关键它让 Agent 可以在运行时动态获知可用工具而不是在编码时就写死。我实际用下来的感受是MCP 在本地工具集成场景下体验很好配置简单、发现机制清晰。但在远程多租户场景下认证、授权、限流这些企业级需求还需要额外方案来补。3.3 自建 MCP Server 的实操要点我最近把一个内部用的文件处理工具改造成了 MCP Server过程不算复杂但有几个点值得记录。首先是能力声明的粒度。一个 Server 可以暴露多个 Tool但粒度太细会导致模型选择困难粒度太粗又不够灵活。我的经验是按业务动作划分比如“读取文件内容”“搜索文件”“写入文件”这样而不是“打开文件句柄”“读取字节”这种底层操作。其次是参数 schema 的设计。MCP 用 JSON Schema 描述参数这里要特别注意必填项和可选项的区分以及参数描述的清晰度。我试过把参数描述写得含糊结果模型传参经常出错后来把每个参数的用途、格式、示例都写清楚准确率立竿见影地提升。最后是错误返回的规范。MCP 定义了错误结构但具体错误信息的内容需要自己设计。我的原则是错误信息要能让模型理解发生了什么并尝试修正而不是只给一个错误码。比如“文件不存在”比“ERROR 404”对模型友好得多。提示MCP Server 的调试可以用官方提供的 Inspector 工具能直观看到能力列表和调用过程比看日志高效。4. 三者融合一个多模态 Agent 的完整实现4.1 整体架构设计把多模态、Harness、MCP 三者串起来我目前的架构是这样的最外层是 Harness负责整个运行时生命周期Harness 内部集成多模态预处理管道工具调用统一走 MCP Client对接多个 MCP Server。数据流是这样的用户输入进入 HarnessHarness 调用多模态预处理器做格式归一化然后组装上下文送入模型。模型返回工具调用请求Harness 通过 MCP Client 转发给对应的 Server拿到结果后格式化再送回模型。循环直到模型生成最终回复Harness 做后处理并返回给用户。这个架构的好处是职责清晰Harness 管运行时多模态管道管感知MCP 管工具生态。每部分可以独立演进互不影响。4.2 关键实现细节与参数选择多模态预处理这块我目前对图片的处理策略是先判断图片类型截图类保留原始分辨率但按语义切分照片类压缩到长边 1024 像素文档扫描类走版面分析后分块。音频统一转成 16kHz 单声道再送模型。视频目前只做关键帧抽取抽帧间隔根据视频长度动态调整。Harness 的上下文管理参数系统提示预留 2000 token近期对话保留最近 10 轮远期对话摘要压缩到 500 token 以内工具返回结果单次不超过 4000 token。这些数字是反复调出来的不同模型可能略有差异。MCP Client 的连接管理我用了连接池每个 Server 维持少量长连接调用时复用。超时设置上本地 Server 给 5 秒远程 Server 给 30 秒。重试策略是本地不重试通常失败就是真失败远程重试两次。4.3 性能与成本的平衡多模态加 MCP 加完整 Harness这套东西跑起来成本不低。我的优化手段主要有几个图片自适应压缩、上下文分层管理、工具结果裁剪、模型分级调用简单任务用小模型复杂任务用大模型。实测下来一个典型的多模态 Agent 任务从输入到输出token 消耗在几千到几万之间耗时几秒到几十秒。这个成本对于内部工具场景可以接受对于面向海量用户的产品则需要更激进的优化。5. 踩坑实录与排查技巧5.1 多模态相关的典型问题图片理解偏差是最常见的问题。模型对图片的理解有时会“脑补”出不存在的内容。我的应对策略是在提示词里明确要求“只描述你确定看到的内容不确定的标注不确定”并且在关键任务上做二次校验。音频转写错误在嘈杂环境下很严重。我的做法是转写后让模型做一次语义合理性检查明显不通顺的地方标记出来让用户确认。文档版面解析错乱在复杂表格上尤其明显。目前的方案是对表格区域单独裁剪出来做结构化提取而不是依赖整页解析。5.2 Harness 层面的排查经验上下文超限是最频繁的问题。排查方法是打印每次请求的 token 统计看是哪部分膨胀了。常见原因是工具返回结果没裁剪或者对话历史没压缩。工具调用死循环也遇到过。模型反复调用同一个工具但参数不变陷入循环。解决方案是加调用次数上限和重复调用检测超过阈值就强制中断并返回错误。状态丢失在多轮对话中偶发。排查下来发现是持久化时机不对某些中间状态没落盘。改成每步操作后都持久化就解决了。5.3 MCP 集成中的坑Server 启动失败最常见的原因是端口占用或依赖缺失。我的做法是启动前做健康检查失败时给出明确的错误提示。能力发现失败通常是协议版本不匹配。MCP 还在演进中不同版本的 Server 和 Client 可能不兼容。锁定版本是个务实的做法。调用超时在远程 Server 上频发。除了调整超时时间我还加了熔断机制某个 Server 连续失败就暂时摘除避免拖垮整个 Agent。问题类型典型表现排查思路解决方案图片理解偏差模型描述与图片不符对比原图与模型输出提示词约束加二次校验上下文超限请求报错 token 超限打印 token 统计分层管理加结果裁剪工具调用死循环反复调用同一工具查看调用轨迹次数上限加重复检测MCP 连接失败Server 无响应检查端口和依赖健康检查加熔断状态丢失多轮对话上下文断裂检查持久化日志每步操作后持久化6. 一些个人体会与后续方向这套东西我从零搭到现在最大的体会是Agent 开发的核心难点不在模型而在工程。模型能力是给定的但怎么把模型能力稳定、可靠、低成本地交付给用户全靠 Harness 和周边生态。多模态和 MCP 都是在这个大框架下的具体能力扩展。另一个体会是不要过早追求通用。我早期总想设计一个能适配所有场景的架构结果处处掣肘。后来改成先做垂直场景把一条链路打磨透再逐步抽象共性部分反而走得更快。后续我打算在几个方向继续深入一是多模态的细粒度控制比如让 Agent 能主动决定“我要看图片的哪个区域”二是 MCP 生态的更多工具接入特别是数据库和 API 类工具三是 Harness 的可观测性再加强做到每个决策点都可追溯、可回放。最后分享一个小技巧调试多模态 Agent 时把中间过程的图片、音频、文本都保存下来出问题时按时间线回放比看纯文本日志直观得多。这个习惯帮我省了大量排查时间。