Blog·Studio
文章系列日历归档关于搜索
Blog·Studio

一个记录思考、笔记与作品的技术博客。

Connect

© 2026 · Blog Studio

鄂ICP备19019526号

crafted with care

stay curious ✦

  1. 文章
  2. ›AI 应用交付物工程 2026:从工具结果到可验证产物

Index

  • 一、被长期忽略的一层:工具结果不等于交付物
  • 二、第一道工程约束:取回窗口与思考时长的赛跑
  • 三、第二道约束:交付物不该内联进上下文
  • 四、UI 交付层:MCP Apps 把「渲染」也变成协议的一部分
  • 五、CSP 与沙箱:可枚举的权限面
  • 六、第三道约束:分层验证与边际 ROI
  • 七、撤销、幂等与扇出:交付物是副作用,不是消息
  • 八、落地清单:把交付物当成一等对象
  • 九、这套方法与相邻工作的边界
  • 参考文献

AI 应用交付物工程 2026:从工具结果到可验证产物

工具返回值不是交付物本身。本文把 AI 应用的交付物当作一等工程对象,给出取回窗口、内容寻址句柄、分层验证 ROI 三套可计算方法,附 MCP Apps 规范、容器过期语义与 CSP 权限面的一手事实来源。

2026年10月31日·约 38 分钟阅读·11,266 字·1 次阅读·博主
#智能体与 AI 应用开发
AI 应用交付物工程 2026:从工具结果到可验证产物

Index

  • 一、被长期忽略的一层:工具结果不等于交付物
  • 二、第一道工程约束:取回窗口与思考时长的赛跑
  • 三、第二道约束:交付物不该内联进上下文
  • 四、UI 交付层:MCP Apps 把「渲染」也变成协议的一部分
  • 五、CSP 与沙箱:可枚举的权限面
  • 六、第三道约束:分层验证与边际 ROI
  • 七、撤销、幂等与扇出:交付物是副作用,不是消息
  • 八、落地清单:把交付物当成一等对象
  • 九、这套方法与相邻工作的边界
  • 参考文献

AI 应用交付物工程 2026:从工具结果到可验证产物

一句话摘要:本文把「AI 应用交付物」当作一类独立的一等工程对象,论证从工具返回值到用户手中可下载、可验证、可撤销产物的完整链路应当显式建模,并给出取回窗口、内容寻址、分层验证三套可计算的设计方法与全部一手事实来源。

一、被长期忽略的一层:工具结果不等于交付物

做 AI 应用时,一个几乎所有人都会先撞上的直觉是:模型的输出就是应用的核心。但这个直觉在工程化阶段会失效,因为「模型输出」和「用户拿到的东西」是两个不同的对象。

本文所说的交付物(deliverable),指的是应用最终交给用户的那份东西:一份生成的报告、一个可下载的表格文件、一段能直接落到代码库的补丁、一张能展开筛选的图表、一份能复核的引用清单。它的存在形式可以是文本、文件、可交互组件,甚至是一段由宿主代理执行的动作。这些东西有一个共同特征:它们的生命周期不由模型决定,而由应用自己的存储、取回与撤销策略决定。

这个区分之所以重要,是因为工具调用这一层已经把「返回」和「交付」混为一谈了。以 MCP 协议为例,传统工具返回文本、图片、资源或结构化数据,宿主把它显示在对话里就算完成。但当工具真的做了一件事——生成了 500 行的 CSV、跑了一段代码产出一张 PNG、改了用户的文件——返回的那个字符串只是这次交付的回执,不是交付本身。

OpenAI 的 Code Interpreter 文档把这个区分写得很直白:容器生成的图片和文件是作为 assistant 消息上的 annotation(注解)返回的,其中 container_file_citation 类型会带上 container_id、file_id 和 filename 三个字段,文档明确说「你可以解析这些注解来暴露下载链接,或者以其他方式处理这些文件」。也就是说,协议层面早就承认「结果」与「产物」是分离的:结果是一段注解,产物是容器里的一个文件。

这个分离在容器语义下会变成一个真实且不可回避的工程约束。同一份文档写着:容器在 20 分钟内未被使用就会过期,过期后容器里的一切数据「将被我们的系统丢弃且不可恢复」,而且「你不能把一个已过期的容器变回活跃状态」;文档的结论性建议是「把容器当作临时性的,把与该工具使用相关的所有数据存储在你自己的系统上」,并补充「你应当在容器活跃期间下载你可能需要的任何文件」。

于是「交付」这一步的真实工作内容是明确的:在生成的那一刻把产物从临时执行环境里取出来,落到应用自己的、有明确生命周期的存储上。这不是锦上添花,这是交付物能被用户取回的唯一前提。

二、第一道工程约束:取回窗口与思考时长的赛跑

上面那条 20 分钟的过期时钟,值得展开算一算,因为它揭示了一个反直觉的结论。

假设用户提出请求后,模型在容器里跑代码生成交付物,然后用户会花一段时间才想起来点下载。这个「思考时长」(D) 是个随机变量。取一个合理的工作假设:(D) 服从对数正态分布 (\mathrm{LogNormal}(m, \sigma)),(m) 是中位数(分钟),(\sigma) 是形状参数。那么交付物丢失的概率就是 (P(D > 20)),因为容器在闲置 20 分钟后过期,产物随之蒸发。

按这个模型算出来的一组数(全部由脚本现场计算,非经验值):

  • 中位数 2 分钟、(\sigma=1.0) 时,丢失率 1.065%
  • 中位数 5 分钟、(\sigma=1.0) 时,丢失率 8.283%
  • 中位数 10 分钟、(\sigma=1.0) 时,丢失率 24.411%
  • 中位数 10 分钟、(\sigma=1.5) 时,丢失率 32.201%
  • 中位数 20 分钟、(\sigma=1.0) 时,丢失率恰好 50.000%

最后一行是个 sanity check:中位数恰好等于阈值时,超出的概率应该是 50%,算出来确实如此,说明模型和实现都对。

现在做一次决策翻转。如果采用「急切取回」策略——在生成后 0.5 分钟内就把产物同步落盘到自有存储——那么丢失率就是 0%,因为容器过期这件事根本还没来得及发生。在中位数 10 分钟、(\sigma=1.5) 的假设下,这两种策略的差距是 32.201% 对 0%。

反解一下更实用的版本:要让懒取回的丢失率压到 10% 以下,中位思考时长需要多小?在 (\sigma=1.5) 时需要 (m < 2.925) 分钟;(\sigma=2.0) 时需要 (m < 1.541) 分钟。换句话说,「等用户来取」这个策略在任何现实可感的思考时长下都不可靠,除非你把中位思考时长压到几分钟以内。

这个计算给出的工程结论非常干脆:取回动作必须由系统发起,不能由用户发起。用户点「下载」的时候,系统读的是一个早就落盘好的副本,而不是现场去临时容器里捞。后者是把可用性押在用户的注意力时延上,而注意力时延的分布尾部很重。

还有一个容易忽略的细节:容器的过期锚点是 last_active_at,且「任何容器操作——无论是获取容器、添加还是删除文件——都会自动刷新容器的 last_active_at 时间」。这意味着只要系统定期去碰一下容器,就能给它续命。但续命策略和落盘策略相比是次优的:它把可用性绑定在一个持续运行的保活任务上,一旦保活任务挂了,所有未取回的产物一起丢。落盘是把可用性从「服务必须持续活着」换成「一次写成功」,这个交换在任何可靠性口径下都是划算的。

三、第二道约束:交付物不该内联进上下文

交付物一旦变大,就会遇到第二个问题:它该以什么形式存在于对话里。

先把量级算清楚。假设一个交付物是 10,000 行、5 列的表格。按每字段平均 12 字符估算,CSV 约 660,000 字节;若用 JSON 表示(每字段带引号和键名),约 870,000 字节,比 CSV 大 1.318 倍。

如果把这些内容内联进模型上下文,用常见的 token 估算系数(CSV 约 0.42 token/字节、JSON 约 0.28 token/字节):

  • CSV 内联约 277,200 token
  • JSON 内联约 243,600 token

这两个数字都很吓人。更值得警惕的是编码方式带来的隐性膨胀:二进制文件走 Base64 时,字节数精确地变成原来的 4/3 倍,也就是 +33.33%,再加 76 列换行的 1.316% 开销,总共 +34.65%。一张 1 MB 的 PNG 内联进上下文大约要 349,517 token。

而如果改为句柄引用——上下文里只放一个约 40 token 的 handle(内容哈希加版本号),真正的内容放在应用自己的存储里,由客户端按需拉取——277,200 token 就压缩成了 40 token,6930 倍。

这个数字对比说明了交付物设计的第一原则:交付物在对话里应该是引用,不应该是内容。这与 MCP Apps 扩展的设计是高度一致的——该扩展引入 ui:// URI scheme,让工具通过 _meta.ui.resourceUri 字段指向一个声明好的 UI 资源,宿主在工具被调用之前就可以预加载这个资源。规范把这条链路分成四步:UI 预加载、资源获取、沙箱化渲染、双向通信。注意第一步是预加载——资源在工具还没被调用时就已经就位了,这同样是「系统先准备好,不等用户触发」的思想。

那么上下文里应该留什么?三类东西足够:

  1. 可读的描述——文件名、类型、大小、生成参数,用户能据此判断要不要点开
  2. 稳定的句柄——内容寻址的哈希加版本号,让引用在版本之间可追溯
  3. 校验信息——哈希本身,让客户端能验证下载完整性

这里的关键判断是:描述和内容必须分离。把描述内联、把内容外置,是同时优化了上下文预算、缓存命中率和用户信任三件事。描述稳定不变,因此可以被缓存;内容可大可小,按需取用。

四、UI 交付层:MCP Apps 把「渲染」也变成协议的一部分

交付物有两种形态:文件型和界面型。文件型就是上一节说的那些;界面型是「一个可以直接在对话里操作的组件」——一个能点开筛选的下拉列表,一个能拖动调整的图表,一个能实时刷新的仪表盘。

这类交付物过去是每个应用自己想办法的:聊天框里塞一段 HTML,或者让模型输出一段 React 代码再动态执行。这种做法的三个问题是显然的:安全边界靠自觉、样式不统一、跨宿主不通用。

MCP Apps 扩展(SEP-1865,状态为 Final,类型为 Extensions Track,创建于 2025-11-21)给出了标准化答案。它的扩展标识是 io.modelcontextprotocol/ui,核心内容有四项:UI 资源(用 ui:// URI scheme 声明)、工具-UI 关联(工具通过 metadata 引用 UI 资源)、双向通信(UI iframe 用标准 MCP JSON-RPC 协议与宿主通信)、以及安全模型(强制的 iframe 沙箱与可审计的通信)。

具体到工作流,规范描述得相当清楚。当 LLM 决定调用一个支持 MCP Apps 的工具时:

  1. UI 预加载:工具描述里的 _meta.ui.resourceUri 指向一个 ui:// 资源,宿主可以在工具被调用之前就预加载它,从而支持「把工具输入流式送进应用」这类特性
  2. 资源获取:宿主从服务器取回该 UI 资源,这是一段 HTML 页面,通常把 JS 和 CSS 打包在一起以简化实现;应用也可以从 _meta.ui.csp 指定的源加载外部脚本和资源
  3. 沙箱化渲染:Web 宿主通常把这个 HTML 渲染进对话内的沙箱 iframe 里
  4. 双向通信:应用与宿主通过一个 JSON-RPC 协议通信,它形成了 MCP 自己的一个方言——部分请求与通知和核心 MCP 共享(如 tools/call),部分类似但不同(如 ui/initialize),多数是新的 ui/ 前缀方法名

这个方言设计里有个值得单独指出的细节,规范要求宿主与视图之间的双向方法(ui/initialize 请求与 ui/notifications/initialized 通知)都由 View 发送,且「宿主 MUST NOT 向视图发送任何以 ui/notifications/sandbox- 开头的方法」——方向性是被显式约束的,这样双向通道才不会退化成任意调用。

安全模型同样值得逐条看。规范写明:沙箱阻止应用访问宿主窗口的 DOM、读取宿主的 cookie 或 local storage、导航宿主页面,或在宿主上下文中执行脚本;所有应用与宿主之间的通信都走 postMessage API;宿主控制应用能访问哪些能力,例如宿主可以限制应用能调用哪些工具,或者禁用 sendOpenLink 能力。

「宿主控制」这四个字是整个扩展的支点。交付物的渲染权限不是由交付方自己声明并自我执行的,而是由宿主在运行时裁决的。这一点在权限模型上比 CSP 声明更进一步。

规范还定义了工具侧的可见性控制(_meta.ui.visibility),取值是 ["model", "app"] 的组合:"model" 表示工具对 agent 可见且可调用,"app" 表示只能由该服务器上的 app 调用。规范给的例子很能说明设计意图:一个 get_weather 工具对模型和 app 都可见,而一个 refresh_dashboard 工具只对 app 可见——它是一个纯 UI 侧的刷新动作,不该占用模型的决策预算,更不该出现在模型的工具列表里。这直接关系到成本与准确率:把 UI 内部动作从模型可见集里剔除,能减少无效的工具调用。

规范同时标注了弃用项:扁平写法 _meta["ui/resourceUri"] 已被弃用,应改用 _meta.ui.resourceUri,且「弃用格式将在 GA 之前移除」。这类演进细节在阅读旧示例代码时容易踩坑,值得注意。

从生态侧看,SEP-1865 明确写了两条来路:MCP-UI 社区项目「已展示 MCP app 的可行性与价值」并「发展为社区的试验场和 UI 规范与 SDK 的来源」;OpenAI 的 Apps SDK「在 2025 年 11 月推出,进一步验证了对话式 AI 界面中富 UI 体验的需求」。而 MCP Apps 的动机段落里,列出的正是缺标准化的后果:服务器无法可靠地期待宿主支持 UI、各宿主行为可能略有差异、安全与审计模式不一致、开发者必须为不同宿主维护多套实现。

这几个开源仓库的实时数据(本次通过 GitHub API 拉取):modelcontextprotocol/ext-apps 3030 star、最后推送 2026-10-29、TypeScript;MCP-UI-Org/mcp-ui 5246 star、Apache-2.0、最后推送 2026-09-16;openai/openai-apps-sdk-examples 2382 star、MIT、最后推送 2026-04-15;openai/apps-sdk-ui 973 star、MIT、最后推送 2026-05-20。

宿主支持矩阵截至本次核验时列出了 Claude、Claude Desktop、VS Code GitHub Copilot、Microsoft 365 Copilot、Goose、Postman、MCPJam 与 Archestra.AI 等,且官方明确提示宿主支持情况各异——「交付物能在你的目标宿主里渲染」是一个需要在选型时逐个核验的前置条件,而不是可以假设的能力。

五、CSP 与沙箱:可枚举的权限面

UI 型交付物的安全边界最终落在 CSP 上。MCP Apps 规范把 CSP 配置定义成四个正交维度,每一项都有明确的默认语义:

  • connectDomains(对应 connect-src)——网络请求来源。省略或为空即表示不允许任何外部连接,这是安全默认值
  • resourceDomains(对应 img-src / script-src / style-src / font-src / media-src)——静态资源来源,同样默认为空,且支持 https://*.example.com 形式的通配子域
  • frameDomains(对应 frame-src)——嵌套 iframe 来源,默认为 frame-src 'none',即不允许任何嵌套 iframe
  • baseUriDomains(对应 base-uri)——文档的基础 URI,省略时只有同源被允许

规范给的示例是:connect 两个(https://api.weather.com、wss://realtime.service.com)、resource 两个(https://cdn.jsdelivr.net、https://*.cloudflare.com)、frame 两个(https://www.youtube.com、https://player.vimeo.com)、baseUri 一个(https://cdn.example.com),合计 7 个显式源。

这里有一个从数字里读出来的结论:通配源不适合做静态审计基准。https://*.cloudflare.com 展开后的主机集合取决于 DNS,静态不可枚举——你没法写一个「检查所有可达源」的静态测试,因为集合本身是开放的。相比之下,四个维度的显式源加起来是可以枚举的、可以做成快照对比的。

因此官方构建教程给出的省事方案值得注意:教程明确说 UI 资源「最终会在带 deny-by-default CSP 配置的安全 iframe 中渲染」,如果你的应用有 CSS 和 JS 资源,要么去配置 CSP,要么「用 vite-plugin-singlefile 这样的工具把资源打包进 HTML 里」——教程选的就是后者。打包进单文件 HTML 意味着资源域列表可以完全为空,这不仅更安全,还消除了对 CDN 的运行时依赖。教程的依赖清单也印证了这个思路:@modelcontextprotocol/ext-apps、@modelcontextprotocol/sdk,加上一堆 dev 依赖。

除了 CSP,规范还定义了一组 UI 可以「请求」的能力:camera、microphone、geolocation、clipboardWrite,各自映射到对应的 Permission Policy 特性。规范在这里的措辞很谨慎:宿主 MAY 予以满足(通过设置 iframe 的 allow 属性),而应用 SHOULD NOT 假定权限会被授予,应当用 JS 特性检测作为回退。

这条 SHOULD 值得单独强调,因为它是安全工程里少见的、由规范直接给出的「防御性编程」要求:应用必须能在「权限被拒」的情况下正常降级,而不是崩溃或静默失败。

MDN 关于 iframe sandbox 的文档还提供了两个必须知道的判据。其一:「当嵌入文档与嵌入页面同源时,强烈不建议同时使用 allow-scripts 和 allow-same-origin,因为这会让嵌入文档移除 sandbox 属性——使其不比完全不使用 sandbox 属性更安全。」其二,也是更根本的一条:「如果攻击者能在沙箱 iframe 之外展示内容,沙箱就毫无用处——例如查看器在新标签页中打开了该框架;这类内容还应当从独立源提供服务,以限制潜在损害。」

第二条对交付物设计有直接推论:沙箱不是万能护栏,它的前提是内容永远不离开 iframe。任何「在新标签页打开」「导出为独立页面」「复制 HTML 源码」的路径,都是绕过沙箱的路径。设计交付物 UI 时,如果提供了「在独立页面打开」的功能,那份独立页面就必须自己承担沙箱的职责——从独立源提供服务、重新施加 CSP、重新评估权限。

六、第三道约束:分层验证与边际 ROI

前两节解决的是「能不能拿到」和「拿到什么」,这一节解决「拿到的东西对不对」。

交付物的验证有个内在困难:它可能很大、很贵、很慢。给一份 5000 行的生成 CSV 做全量业务校验,成本可能比生成它本身还高。但完全不验证,风险又直接落在用户身上——一份数字错位的报告被拿去汇报,后果比生成失败严重得多。

这里有一个可以量化的框架。把交付物按生成置信度分成若干层,每层有自己的缺陷率 (p)、验证检出率 (d)、误拒率 (f) 和单次验证成本 (c)。设总交付物数 (N=1000),验证预算 (B)(单位:验证成本)。

取一个具体的分层设定:

层占比缺陷率 p检出率 d误拒率 f
T1 低置信生成20%12.0%90%3%
T2 中置信50%3.0%85%2%
T3 高置信生成30%0.5%80%1%

在「逃逸缺陷的严重度是误拒返工的 10 倍」这个权重下,每单位预算的边际 ROI(净减损除以支出)算出来是:T1 = 1.054、T2 = 0.236、T3 = 0.030。

ROI 严格递减,这带来一个结构性结论:在预算受限时,最优解必然是角点解——把预算优先给 ROI 最高的一层,直到它饱和,多余的才流向下一层。T1 全量核查的成本是 200 单位(20% × 1000 × 1.0),也就是说预算超过 200 之后才开始轮到 T2。

用网格搜索验证这个结论(在预算约束下枚举各层抽样比例):

  • 预算 B=150:分层最优抽样 75% / 0% / 0%,逃逸缺陷 24.30,总损失 246.96;均匀抽样 15% 时逃逸 35.17、损失 354.37;不验证逃逸 40.50、损失 405.00。分层比均匀降损 30.31%
  • 预算 B=250:分层最优 100% / 10% / 0%,逃逸 17.73、损失 183.55;均匀 25% 时损失 320.62;降损 42.75%
  • 预算 B=400:分层最优 100% / 40% / 0%,逃逸 13.91、损失 148.21;均匀 40% 时损失 269.99;降损 45.10%

这里有个值得注意的规律:在所有预算档位下,T3 的最优抽样率始终是 0%。因为 T3 的 ROI 只有 0.030,而 T2 有 0.236、T1 有 1.054——只要预算紧张,T3 就永远排不上。这直接给出一条实践规则:置信度分层不只是抽样依据,更应该是预算分配依据。对绝大多数应用,把验证预算平摊到所有交付物上是错误做法;正确做法是先按置信度分层,再优先验证最可疑的那一层。

同样值得指出的是,预算 B=150 时「不验证」的损失是 405.00,而分层最优只有 246.96——用 15% 的交付物接受率就换来了 39.02% 的损失下降。这说明在缺陷率高的层上做抽样验证,收益是显著的。

还有一个工程细节:验证本身有误拒(false reject)成本。误拒一个合格交付物不是零成本,它需要返工,代价通常是 (r=2) 个成本单位。这解释了为什么不能无脑全量验证——如果某层缺陷率太低而误拒率高,验证反而是净亏损。

七、撤销、幂等与扇出:交付物是副作用,不是消息

前面三层都假设交付物一旦生成就稳定存在。但交付物是副作用——它会被用户撤销、被上游数据变化作废、被合规要求强制删除。这让它在架构上更接近一次写操作,而不是一条消息。

撤销的工程难点在于扇出。一个交付物在被系统里留下多少个引用点?按本文的设计,至少是六处:工具结果消息体、容器内文件、widgetState 状态槽、CDN 对象、客户端缓存条目、审计日志行。撤销时必须让这六处同时失效。

如果一个交付物出现在一条会话的 (N) 个 turn 中,撤销的工作量就是 (N) 处会话引用加上 5 处全局引用。扇出不随时间收敛——会话越长,撤销越贵。这是「交付物一旦进入长对话就很难干净撤销」这个直觉的量化解释。

解法是内容寻址 + 版本号作为幂等键。当 handle 由内容哈希加版本号构成时:

  • 重复撤销天然收敛到同一个 handle,撤销操作本身是幂等的
  • 重复交付也收敛到同一个 handle,不会产生重复对象
  • 版本递增给出了「这份交付物是第几版」的确定答案,撤销可以精确到版本而非整个对象

这条设计与 MCP Apps 规范里的一个机制天然呼应:OpenAI Apps SDK 的示例仓库里,_meta["widgetSessionId"] 正是用来跨轮次保持 widgetState 的——README 写得很清楚,「widget 把工具响应与先前的 widgetState 合并,UI 动作(比如增减数量)反馈回共享状态,因此 assistant 始终看到最新的购物车」。会话 ID + 状态槽 + 内容寻址的句柄,这三个东西组合起来,正好是扇出可收敛的前提。

另一个可借鉴的一手事实来自 LangChain 的 ToolNode 文档,它把「工具调用与工具结果必须配对」这条不变量写成了硬约束:「消息历史中的每个工具调用都必须有对应的 ToolMessage」,并且「ToolNode 强制这条要求:如果更新中没有与该工具调用匹配的 ToolMessage,它会抛出一个 ValueError」。文档还说明并行工具调用的语义:「当模型在一步中调用多个工具时,它们会全部先执行完」,并且只有当这一批里每一个工具都是 return_direct=True 时,agent 才路由到 END;混合调用时它会带着所有 ToolMessage 回到模型,让模型能基于全部结果继续推理。

这三条对交付物设计的含义是直接的:交付物的 ID 与结果消息必须严格配对(这正是扇出图的边),并行产生的多个交付物要有确定的关联(否则撤销时不知道该撤销哪一个),以及当部分交付成功、部分失败时,必须把成功的那些一并回传(否则失败路径会丢掉已生成的产物,产物就泄漏在系统里了)。

八、落地清单:把交付物当成一等对象

综合前面七节,一份可以直接拿去用的工程清单:

存储与取回

  1. 交付物在生成后立即落盘到自有存储,不依赖执行环境的存活
  2. 记录执行环境的过期时钟(本文场景是 20 分钟),把「过期前必须取回」写进设计约束
  3. 不把保活任务当作取回策略的替代品——落盘优先于续命

上下文与传输 4. 上下文里只放描述、句柄、校验信息,不放内容 5. 句柄用内容哈希加版本号构造,天然幂等 6. 内联内容前先算 token 账单:本文场景下 10k×5 的表格内联约 27.7 万 token,句柄引用是 40 token

呈现与权限 7. UI 型交付物优先打包成单文件 HTML,使 CSP 的资源域列表可以为空 8. 渲染权限由宿主裁决,应用对权限被拒必须能降级而不是崩溃 9. 「在新标签页打开」这类逃逸路径必须重新施加沙箱与独立源

验证与撤销 10. 按生成置信度分层,验证预算优先给 ROI 最高的那一层(本文场景 ROI 是 1.054 / 0.236 / 0.030) 11. 交付物 ID 与结果消息严格配对,缺失即抛错而不是静默 12. 并行产生的多个交付物要有确定的关联关系 13. 撤销时清点全部扇出点(本设计是六处),并让重复撤销幂等

可观测 14. 记录每个交付物的完整生命周期:生成、内容哈希、版本、取回时刻、验证结果、撤销时刻

第 14 条常被漏掉,但它是后面所有能力的基础:只有把生命周期记全,才能在事故发生后回答「用户当时拿到的到底是哪一版」。

九、这套方法与相邻工作的边界

有必要说明本文与几个相邻话题的边界。

与 RAG 工程的关系:RAG 解决的是「检索什么进上下文」,交付物工程解决的是「生成出来的东西怎么交到用户手上」。两者在引用溯源上有交集(交付物也要能被溯源),但重心不同。

与结构化输出工程的关系:结构化输出关心的是「模型能不能吐出合规 JSON」,交付物工程关心的是「吐出来的东西怎么变成一个可下载、可撤销、可验证的对象」。前者是格式契约,后者是生命周期。

与生成式 UI 的关系:AI SDK 文档对 generative UI 的定义是「允许大语言模型超越文本并『生成 UI』的过程」,其核心是「把工具调用的结果连接到 React 组件上」。这与本文的 UI 交付层是同一件事的两种实现路径——一种由框架帮你做类型推导(AI SDK 提供 InferUITool 这类类型辅助工具,以及推荐用 parts 属性而非 content 属性来渲染消息的做法),一种由协议标准化(MCP Apps 的 ui:// + _meta.ui.resourceUri)。两条路都可行,但它们都解决不了本文前面四节讨论的存储、取回与验证问题——那部分与 UI 怎么渲染无关,是所有路径的公共下半段。

与 Agent 内核机制的关系:工具调用的重试语义、终止条件、规划与反思,属于 agent 内核,本文一概不涉及。本文只关心当内核跑完之后,产物怎么落地。

参考文献

  1. Model Context Protocol. SEP-1865: MCP Apps - Interactive User Interfaces for MCP. Extensions Track, Status Final, Created 2025-11-21. https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp.md
  2. Model Context Protocol. MCP Apps specification, version 2026-01-26. https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx
  3. Model Context Protocol. MCP Apps Overview. https://modelcontextprotocol.io/extensions/apps/overview.md
  4. Model Context Protocol. Build an MCP App. https://modelcontextprotocol.io/extensions/apps/build.md
  5. OpenAI. Code Interpreter guide (containers, container_file_citation annotations, expiration semantics). https://developers.openai.com/api/docs/guides/tools-code-interpreter
  6. OpenAI. Containers API reference (expires_after, anchor, memory_limit, network_policy). https://developers.openai.com/api/reference/resources/containers
  7. OpenAI. Tools guide (function calling, tool_choice, strict schema). https://developers.openai.com/api/docs/guides/tools
  8. OpenAI. Apps SDK developer documentation. https://developers.openai.com/apps-sdk
  9. Anthropic. Building effective agents. Published Dec 19, 2024. https://www.anthropic.com/engineering/building-effective-agents
  10. Vercel. AI SDK - Generative user interfaces. https://ai-sdk.dev/docs/ai-sdk-ui/generative-user-interfaces
  11. Vercel. AI SDK UI - Chatbot (message parts, useChat, InferUITool). https://ai-sdk.dev/docs/ai-sdk-ui/chatbot
  12. LangChain. Tools (ToolNode, ToolMessage pairing, parallel tool calls, ToolRuntime). https://docs.langchain.com/oss/python/langchain/tools
  13. MDN Web Docs. The Inline Frame element (iframe sandbox attribute, allow-scripts/allow-same-origin). https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe
  14. Model Context Protocol. ext-apps repository (spec and SDK for MCP Apps). https://github.com/modelcontextprotocol/ext-apps
  15. MCP-UI-Org. mcp-ui (UI over MCP). https://github.com/MCP-UI-Org/mcp-ui
  16. OpenAI. openai-apps-sdk-examples (widgetState and widgetSessionId usage). https://github.com/openai/openai-apps-sdk-examples
←返回文章列表

Related

可能也会喜欢

  • AI 应用的记忆写侧工程 2026:抽取、晋升与冲突消解10月30日
  • 向量数据库选型 2026:pgvector 与专用引擎的边界10月29日
  • AI 应用的数据删除与导出工程 2026:扇出图与验证闭环10月28日

Conversation

0 条

留下你的想法

加载评论中…

New comment