乐于分享
好东西不私藏

PDF Agent 别再整本吞:MCP Apps 让文档工具有了增量交互界面

PDF Agent 别再整本吞:MCP Apps 让文档工具有了增量交互界面

做文档型 Agent 的团队,迟早会遇到一个很朴素但很烦的问题:PDF 不是一段文本。

合同、巡检报告、故障复盘、审计材料、云资源账单、SOP 手册,表面上都可以被解析成文本,再丢给 RAG 或上下文拼接。但真实应用里,用户经常不是问“这份 PDF 里有哪些字”,而是在做更复杂的动作:

  • 看第 3 页的表格是不是和摘要一致
  • 找出表单字段并补齐缺项
  • 在某几页上批注风险点
  • 对比一处截图、一个图例和旁边的说明
  • 只打开前两页就先判断这份材料是否值得继续处理

如果工具只能返回一大段文本,Agent 就会被迫把“文档理解、界面交互、用户确认、上下文压缩”全部塞进一次模型调用里。结果通常很拧巴:上下文变胖、首屏变慢、用户看不到证据,出了错也很难定位到底是解析错了、检索错了,还是模型理解错了。

这也是 MCP Apps 最近这条更新值得关注的地方。MCP Apps 不是再造一个 Agent 框架,而是把 MCP 工具的输出形态从“文本和结构化数据”扩展到了“可嵌入的交互 UI”。在 2026 年 4 月 27 日发布的 v1.7.1 里,PDF Server 示例继续往工程化方向推进:display_pdf 不再为了抽取表单信息而整份下载和解析 PDF,而是通过 range transport 做增量读取;查看器侧也把注释和字段扫描延后到页面真正渲染时再做。

这个变化看起来是一个示例优化,但背后对应的是文档型 AI 应用的一条关键分界线:

Agent 不一定要把所有文档内容都吞进上下文,它可以调用工具拿到必要结构,再把可视化确认交给内嵌界面完成。

文档工具为什么容易把 Agent 拖慢

很多团队的第一版 PDF Agent 架构大概长这样:

PDF URL   -> 下载整份文件   -> 服务端解析文本、表格、表单、图片   -> 生成一份很大的 JSON 或 Markdown   -> 塞进模型上下文   -> 模型回答用户问题 

这个链路适合离线索引,但不适合交互式任务。

第一,首轮响应会被完整下载和完整解析卡住。哪怕用户只是想看第一页,工具也可能先把后面几十页都处理完。

第二,解析结果很难表达“证据位置”。模型说第 7 页有一个风险项,用户还要另外打开 PDF 去找。排障、审计和变更评审场景里,这种跳转成本会直接降低可信度。

第三,PDF 里的表单和注释不是普通文本。它们有坐标、字段名、控件类型、默认值、用户修改痕迹。强行压成 Markdown,会丢掉很多操作所需的状态。

第四,工具输出越大,Agent 越容易在上下文预算里和历史对话、检索片段、工具结果互相挤占。最后不是模型“不聪明”,而是输入已经变成了噪声堆。

所以,文档型 Agent 的核心问题不是“能不能解析 PDF”,而是:

能不能只处理当前任务真正需要的那部分内容,并且让用户在同一个对话环境里看见、编辑和确认证据。

MCP Apps 改变的是工具结果的形态

传统 MCP 工具一般返回两类东西:

text      -> 给模型读 json/data -> 给模型或客户端消费 

这当然足够覆盖很多后端能力,比如查日志、查指标、执行 SQL、读工单字段。但一旦工具结果本身需要人机协同,就会很别扭。比如图表需要缩放,表单需要填写,PDF 需要翻页,拓扑图需要点选,视频需要定位到某一帧。

MCP Apps 的做法是让工具声明一个 ui:// 资源,把 HTML 界面和工具关联起来。工具被调用后,Host 可以在沙箱 iframe 中渲染这个界面,并通过消息通道把工具数据传给 UI。UI 也可以在受控范围内再调用 Host 暴露的能力。

简化之后,它像这样:

MCP tool   -> 返回结构化结果   -> 关联 ui://pdf-viewer  Host   -> 获取 UI resource   -> 沙箱渲染 iframe   -> 将工具结果推给 UI  用户   -> 在对话内翻页、批注、查看字段   -> 必要时再触发工具调用 

对 AI 应用开发来说,这不是“给工具结果加个前端皮肤”。它会改变上下文工程的边界:

  • 模型上下文里只放决策需要的摘要、字段和用户意图
  • 大文件、页面渲染、局部状态留在工具和 UI 层
  • 用户确认不再依赖模型复述,而是直接发生在证据界面里
  • 工具调用结果可以从“一次性文本块”变成“可持续交互状态”

这对智能运维尤其有用。很多运维材料并不是干净的 API 结果,而是 PDF 报告、截图、拓扑、变更单附件、审计表格。让 Agent 把这些材料全部读成文本,既慢,也不可靠。

PDF Server 这次更新真正解决了什么

v1.7.1 的 PDF Server 变化可以拆成两层看。

第一层是服务端的增量读取。过去 display_pdf 为了判断表单字段,容易把整份 PDF 下载并解析一遍。新的实现把 PDF.js 的读取方式改成 range transport,让服务端只拉取当前探测需要的字节范围。对没有表单的 PDF,探测可以在只读取一部分文件的情况下结束;遇到加载失败,也要返回空结果而不是让工具调用挂住。

第二层是查看器的懒扫描。PDF UI 不再在首屏前把所有页面的注释和字段关系都扫完,而是在页面真正渲染时再做局部扫描。这样用户可以先看到第一页,后续页面继续按需加载。

这两个点合在一起,带来的不是一点性能小修,而是一种更适合 Agent 的运行方式:

用户请求显示 PDF   -> 工具先返回可用的轻量结构   -> UI 快速首屏   -> 后续页面和表单信息按需加载   -> 用户操作形成可追踪的状态差异   -> Agent 只在需要时读取关键结果 

这比“工具先把整份 PDF 解析成文本,再让模型解释给用户听”稳得多。

在智能运维场景里,可以把它类比成日志查询:你不会为了看一个错误窗口,先把全量日志下载到模型上下文里;你会先定位时间段、服务、trace id,再按需展开。文档 Agent 也应该这样做。

落地时可以怎么设计

如果你已经有 MCP Server,可以先从“哪些工具结果不适合纯文本”开始筛选,而不是一上来重写整套应用。

适合做成 MCP App 的工具通常有几个特征:

  • 结果有空间结构,比如 PDF、拓扑图、流程图、地图、看板
  • 用户需要确认证据,而不是只读一句答案
  • 结果会被继续编辑,比如表单、批注、参数面板
  • 数据量大,但每次只需要看一小部分
  • 工具结果需要和后续工具调用形成闭环

一个文档工具可以按下面这种方式拆分:

display_pdf(url)   -> 返回 doc_id、页数、初始 formFields、ui resource  read_page_text(doc_id, page)   -> 只抽取指定页文本  get_annotations(doc_id, page)   -> 获取某一页批注  persist_annotations(doc_id, diff)   -> 保存用户在 UI 中产生的修改  summarize_selection(doc_id, page, selection)   -> 只让模型处理用户选中的局部内容 

这里的重点是:display_pdf 不承担所有事情。它负责建立会话和展示界面,真正的重活拆成可增量调用的小工具。Agent 负责判断下一步需要哪个工具,而不是一次性拿走整份文件。

在 MCP Server 里,可以把 UI 资源和工具元数据绑定起来。伪代码大概是这样:

server.registerResource({
  uri: "ui://pdf-viewer",
  mimeType: "text/html;profile=mcp-app",
  content: pdfViewerHtml,
});

server.registerTool({
  name: "display_pdf",
  description: "Display a PDF with page-level interaction",
  inputSchema: DisplayPdfInput,
  annotations: {
    ui: "ui://pdf-viewer",
  },
  handler: async ({ url }) => {
const session = await openPdfSession(url);
const formFields = await probeFormFields(session, { incremental: true });

return {
      docId: session.id,
      pages: session.pageCount,
      formFields,
    };
  },
});

生产环境里不要把这段当作可以直接复制的 SDK 代码,关键是架构分工:

模型:判断任务、选择工具、解释结果 工具:获取数据、做权限校验、维护会话 UI:承载大对象、局部交互、用户确认 存储:保存差异、批注、审计记录 

这样做之后,Agent 的上下文会干净很多。模型不需要记住 PDF 每一页的细节,只需要知道当前用户正在处理哪份文档、哪些字段已确认、哪些页面需要进一步分析。

边界也要提前画清楚

MCP Apps 解决的是“工具结果如何在对话里交互呈现”,不是替你解决所有安全和产品问题。

第一,Host 支持不是天然一致。MCP Apps 是 MCP 的扩展能力,不同客户端的支持程度、沙箱策略、UI 尺寸、权限提示都会有差异。面向生产环境时,要准备纯文本降级结果。

第二,iframe 沙箱不等于业务安全。PDF URL 校验、内网地址访问、鉴权 token、文件大小、下载超时、内容类型校验,都应该在工具服务端完成。不要让模型生成的参数直接决定服务端可以访问哪里。

第三,UI 里的用户操作要进入审计链路。比如删除批注、修改字段、提交表单,这些动作不能只留在前端状态里。它们至少应该有 doc_id、page、operation、user、timestamp 和变更前后摘要。

第四,增量加载不等于永不全量处理。需要全文搜索、全局摘要、合规抽取时,仍然可能需要离线索引或后台任务。区别在于,交互链路不应该被全量任务阻塞。

第五,不要把 MCP App 变成“万能小网页”。它应该服务于工具调用的证据呈现和局部操作,而不是把完整业务系统塞进聊天窗口。聊天里的 UI 越复杂,越要有清晰的状态边界和退出路径。

对智能运维 Agent 的启发

智能运维开发里有大量“半结构化证据”:告警截图、巡检 PDF、变更附件、容量报表、云账单、拓扑图、压测报告。过去我们习惯把它们变成文本,再交给模型推理。

但很多时候,文本化只是折中方案。

更好的架构应该允许 Agent 这样工作:

1. 先调用工具打开证据对象 2. 用 UI 呈现可验证的局部内容 3. 用户在界面里选择、批注或确认 4. Agent 只读取被确认的结构化结果 5. 后续诊断、总结、工单流转再进入模型 

这会让 Agent 从“替用户读完所有材料”变成“带着用户处理关键证据”。对生产系统来说,后者往往更可靠。

MCP Apps 的价值就在这里:它把工具调用、交互界面和模型上下文拆开了。PDF Server 的增量加载只是一个具体例子,但这个例子已经说明,文档型 Agent 不必再把整份文件塞给模型硬扛。

真正值得借鉴的不是某个 PDF 示例本身,而是背后的工程原则:

大对象留在工具和 UI 层,模型只接收当前决策需要的最小事实。

这条原则会比任何一个单点优化都更长久。

#智能运维 #Agent开发 #MCP #MCP Apps #文档智能 #LLM应用工程