拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSE 接口设计 vs Agent UI:四个开源项目,把「模型吐词」和「界面更新」拆开后,我看懂了差距

SSE 接口设计 vs Agent UI:四个开源项目,把「模型吐词」和「界面更新」拆开后,我看懂了差距 SSE 接口设计 vs Agent UI四个开源项目把「模型吐词」和「界面更新」拆开后我看懂了差距一句话先给结论mewhelp、deepseek-harness、claudecode、codex-main 这四个开源项目都把「模型边吐词」和「界面边更新」这两件事拆开了真正的差别只在于两点——SSE 卡在哪一层以及Agent UI 真正订阅的是哪条通道。如果你是一个正在给 Agent 应用写聊天界面的后端工程师或者是一个正在纠结「要不要上 SSE、要不要对接 AG-UI 协议」的前端工程师这篇文章值得你花十五分钟读完。我会从四个真实开源仓库的实现出发把「模型流式输出」和「界面增量更新」这条链路一层一层拆开给你看。引言为什么我要盯着四个仓库的 SSE 看三天先说背景。最近我在做智能核稿与文档审核相关的 Agent 产品业务上需要把大模型的流式输出实时渲染到网页聊天界面里同时还要支持「工具调用审批」「订单卡片预览」「人机打断续流」这类交互。刚开始我的方案很朴素前端开一个 EventSource 连模型的流式接口把 token 一帧一帧贴到气泡里完事。但做着做着问题就来了。第一个问题是模型接口的 SSE 流里除了文本 token还有工具调用参数、引用来源、思考过程、中断信号。这些事件如果原样透传给前端前端得自己维护一套「解析模型协议」的逻辑一旦换模型供应商前端代码就要跟着改。第二个问题是人机协作。当 Agent 卡住需要用户确认订单信息时模型流可能已经结束了但业务状态还挂着。这时候前端该怎么知道「该弹出确认卡片了」靠 SSE 里的一个自定义事件那这个事件和模型协议的耦合度就太高了。第三个问题是多端复用。同一个 Agent 核心今天要接网页聊天明天要接 IDE 插件后天要接 TUI 终端。如果界面层直接吃模型的 SSE那每个端都要各自实现一遍协议适配维护成本爆炸。带着这三个问题我去翻了四个开源仓库mewhelp-python电商客服 Web 聊天、deepseek-harnessDeepSeek Agent 浏览器 GUI、claudecode终端 TUI、codex-mainTUI / Desktop / SDK。四个项目都号称「支持 SSE」但把代码翻到底之后我发现它们的 SSE 用法完全不是一回事。这篇文章就是这三天翻仓库的完整笔记。我不讲 AG-UI 规范原文那是另一套体系我只讲这四个真实项目里SSE 到底被用在了哪里、Agent UI 到底订阅了什么以及你该抄谁。文章目录SSE 接口设计 vs Agent UI四个开源项目把「模型吐词」和「界面更新」拆开后我看懂了差距引言为什么我要盯着四个仓库的 SSE 看三天一、痛点场景四个仓库同一个问题二、先抓住大图SSE 到底卡在哪一层2.1 mewhelp浏览器直接吃 SSE2.2 DSH / Claude Code / CodexUI 不直接吃模型 SSE2.3 一个类比帮你记住2.4 为什么不能直接把模型的 SSE 透传给界面三、同一轮回复四条路上的「包」长什么样3.1 mewhelp 的包流SSE 直达浏览器3.2 deepseek-harness 的包流SSE 止步于 Host 边界3.3 claudecode 的包流同一套契约多种传输3.4 codex-main 的包流双层投影3.5 从包流里读出的第一层结论3.6 两个容易被忽略的细节四、四维对照表UI 真正订阅的是什么4.1 产品形态4.2 模型侧流4.3 Agent UI 通道最关键的差异4.4 UI 事件粒度4.5 真相来源4.6 人机协作与 AG-UI4.7 从对照表读出的两条主线五、人机打断同一需求四种握手5.1 mewhelp图暂停 同构续流5.2 DSHWaterfall 问答5.3 Claude Code控制面往返5.4 CodexRPC 审批请求5.5 这节的结论5.6 四个容易被忽视的 HITL 工程细节六、「AG UI」到底在说什么先消歧义6.1 那 CopilotKit 的 AG-UI 协议又是什么6.2 AG UI 层的通用设计清单七、什么时候有用抄谁的形状7.1 学 mewhelpSSE 直达7.2 学 DSH日志 WS7.3 学 Claude CodeSDK 契约7.4 学 Codex双层投影7.5 共同限制SSE 的单向性八、收束一条因果链九、总结给你四个可落地的动作参考仓库与实现文件写在最后一、痛点场景四个仓库同一个问题先描述一个几乎每个 Agent 应用都会遇到的真实场景后面所有讨论都围绕它展开。假设你正在做一个电商客服机器人。用户在聊天框里输入「我要退掉昨天买的那个蓝牙耳机帮我看看订单」。你的 Agent 核心会这样工作模型开始生成回复一个词一个词地往外吐「好的我帮您查询订单……」中途模型决定调用一个工具查询用户订单列表工具返回结果后模型发现订单里有多笔交易需要用户确认「您要退的是哪一笔」用户在界面里点击了某个订单卡片Agent 继续生成回复最终输出一段完整的退款指引并附带引用来源。注意这短短五步里界面需要呈现的东西至少包括流式文本、工具调用状态「正在查询订单…」、订单卡片可点击、确认交互点击后恢复生成、最终结果与引用。而这一整套交互在四个项目里分别走了四条完全不同的技术路径。我把这个场景再拆细一点你会发现它背后其实是三个独立的问题在打架第一个问题是粒度的错配。模型输出的最小单位是 token一次增量可能只是半个词而界面需要的最小单位是「事件」一个事件可能对应一整张订单卡片、一次工具调用、或者一段引用。把 token 级的流直接喂给界面界面就得自己判断「这一帧是文本、还是工具参数、还是状态变更」——这种判断做一两次没问题做成通用逻辑就是灾难。第二个问题是生命周期的错配。模型流以[DONE]结束但业务对话没有结束。用户确认订单之后Agent 还要继续干活、继续吐词。如果界面的状态机是跟着模型流走的那「中断-确认-续流」这个循环会让状态机瞬间爆炸。第三个问题是消费者的多样性。同一个 Agent 核心今天接网页明天接 IDE后天接 TUI。每个消费者的渲染能力不同、交互能力不同、甚至网络环境都不同。让所有消费者都去解析同一个模型的 SSE 协议等于把模型供应商的协议变更风险复制粘贴到了每一个端上。这三个问题就是「模型边吐词」和「界面边更新」必须被拆开的根本原因模型输出的是一串 token 流而界面需要的是一个结构化的事件序列——文本增量、工具事件、审批请求、完成信号。两者的粒度不同、生命周期不同、消费者不同硬把模型流当界面协议用短期能跑长期必崩。带着这个场景我们开始拆第一层SSE 到底卡在哪一层。二、先抓住大图SSE 到底卡在哪一层打开四个仓库的第一件事是搞清楚 SSE 出现在哪一层。这是最容易产生误解的地方别把「有 SSE」理解成「前端在看 SSE」。在多数编程助手里SSE 只伺候模型 API前端另有自己的一套事件通道。我画了一张架构对比图左边是 mewhelp右边是另外三家的共性结构2.1 mewhelp浏览器直接吃 SSEmewhelp 的链路非常直接。它的 Agent UI 层就是一个网页聊天页前端用fetch直接读取/api/chat的流式响应SSE 通道贯穿始终——/api/chat负责主对话流/api/actions/resume负责中断后的续流。runtime 层基于 LangGraph模型上游的输出会被翻译成一张自研的小事件表然后以 SSE 帧的形式推给浏览器。这条链路的特点是SSE 是浏览器和 Agent 核心之间的唯一协议。模型能不能再流式输出对 UI 来说不重要因为 runtime 已经把模型输出封装成了统一的事件格式。换句话说mewhelp 把「模型协议」和「界面协议」合并成了同一条 SSE 通道只是中间加了一层事件翻译。2.2 DSH / Claude Code / CodexUI 不直接吃模型 SSE另外三家是另一种形态。它们的 Agent UI 层Web / TUI / Desktop订阅的是「领域事件」而不是模型的 SSE 流。中间的通道各不相同DSH 走 HTTP RPC 加 WebSocket mux/api/remote.muxClaude Code 本地用进程内生成器、远程走 WebSocket / NDJSON / SSEPOSTCodex 走 JSON-RPCstdio / unix socket / WebSocket。而模型的 SSE 流在三家架构里都严格止步于 Host 和 LLM 之间的边界DSH 的llm-deepseek/sse.ts把 DeepSeek 的 SSE 流翻译成 StreamChunk 后进入会话日志Codex 的codex-api/sse/responses.rs把 Responses API 的流翻译成 ResponseEvent 后再进 coreClaude Code 的 Anthropic 流被翻译成内部 Message 后走 SDK 消息契约。浏览器、TUI、桌面端全都看不到模型的原生 SSE。2.3 一个类比帮你记住mewhelp 像「厨房开窗客人直接闻炒菜香」——模型一吐词浏览器立刻能感知。另外三家像「后厨有烟道前厅只听服务员报菜名」——后厨模型炒菜的火候走烟道排走前厅UI只通过服务员Agent 核得知「这桌上了什么菜」。烟道模型 SSE和报菜UI 事件不是同一根管子。这里有一个需要特别强调的边界报菜仍然是流式的可以逐字上只是载体换成了 WebSocket / NDJSON / JSON-RPC 通知而不是浏览器去连模型的text/event-stream。记住这张图后面所有的细节都只是这两条架构路径的具体展开。2.4 为什么不能直接把模型的 SSE 透传给界面讲到这里值得把「为什么四家都不透传」这件事展开说透因为它背后是三个实打实的技术理由理由一模型协议不承诺稳定。各家模型的流式协议都在快速演进有的在流里加 reasoning 字段有的把工具调用改成流式参数有的新增了思考令牌thinking token。如果你的界面直接解析模型协议每次供应商升级协议你都要发版。而 Agent 核翻译一层之后协议变更被吸收在核内UI 契约纹丝不动。理由二模型事件语义太粗。模型的 SSE 流里只有「生成了什么」没有「业务上发生了什么」。订单卡片的展示、审批请求的弹出、任务完成的通知这些业务语义模型根本不知道是 Agent 核根据工具执行结果和状态机推导出来的。这些领域事件必须由核来产生UI 才能拿到有意义的信号。理由三连接形态不匹配。模型的 SSE 是服务端到 Agent 宿主的长连接它假设链路是可信的、独占的。而 UI 的场景千奇百怪网页可能断线重连、IDE 插件可能跨进程、桌面端可能离线工作。把模型连接的生命周期暴露给 UI等于让最不稳定的一环决定整个系统的稳定性。这三点就是「Agent 核译成领域事件」这个动作存在的全部理由。它不是设计洁癖而是工程必然。三、同一轮回复四条路上的「包」长什么样架构形态看完了接下来看最直观的东西同一轮回复里四个项目的「包」分别长什么样。我按事件在链路中流动的顺序把四个项目各拉了一条五段的包流黄色的是 SSE 包。注意看它们出现在哪一列、哪一层。3.1 mewhelp 的包流SSE 直达浏览器mewhelp 的链路是SSE ← /api/chat建立流 → 模型增量以delta帧推送例如「可以退…」→ 需要用户确认时推送interrupt事件select_order注意此时没有 done→ 用户操作后前端POST /resume服务端再开一条同构的 SSE 流 → 最后以done [DONE]收尾。这条链路里SSE 既是下行推送也是上行续流的载体resume 后重开流。所有事件——文本、工具、中断、完成——都在同一条通道上按序流动事件顺序即业务顺序。3.2 deepseek-harness 的包流SSE 止步于 Host 边界DSH 的链路是SSE 仅存在于 Host 和 DeepSeek 之间 →llm-deepseek/sse.ts把流翻译成StreamChunk→ 写入会话日志assistant/chunk→ UI 通过 WS mux 订阅session.follow流 → 审批类交互走 waterfallapproval。注意黄色 SSE 包只出现在第一段。UI 看到的全部是会话日志投影出来的领域事件载体是 WebSocket。3.3 claudecode 的包流同一套契约多种传输Claude Code 的链路是模型流被翻译成内部Message→ 本地通过AsyncGenerator直接喂给 Ink 渲染 → SDK 模式下走 NDJSON stdout → 远程模式可选「SSE 读 POST 写」→ 审批交互走control_request。它最特别的地方在于同一套 SDK 消息契约传输层是可插拔的。本地进程内用生成器跨进程用 NDJSON远程用 WebSocket 或 SSE。黄色 SSE 包只在远程传输段出现且永远是「读」方向。3.4 codex-main 的包流双层投影Codex 的链路是Responses API 的 SSE 流在codex-api客户端内被翻译成ResponseEvent→ core 层统一成EventMsg→ app-server 再投影成 JSON-RPC 推给 UIitem//delta、turn/→ 审批交互走 ServerRequest。它是最典型的「模型 SSE 严守在 API 客户端、core 发领域事件、UI 层再投影」的三段式。黄色 SSE 包同样只出现在最左侧。3.5 从包流里读出的第一层结论把四条链路并排看结论非常清晰模型侧的 SSE 通常到[DONE]就结束了UI 侧另有自己的生命周期——DSH 有 session follow、Claude Code 有 SDK result、Codex 有 turn completed。这两层有各自的起始、推进和终止语义把它们混成一种协议是 Agent 界面架构里最隐蔽也最昂贵的错误。3.6 两个容易被忽略的细节细节一[DONE]不是业务终点。模型流发完[DONE]只代表「这一轮模型生成完毕」不代表「任务完成」。mewhelp 的done事件和[DONE]是两码事前者是业务层的完成信号后者是传输层的结束标记。如果前端拿[DONE]当业务完成来用遇到 interrupt 续流模型流被业务打断就会误判。细节二同构续流依赖可恢复的执行上下文。mewhelp 之所以能在POST /resume之后再开一条同构 SSE是因为它的 runtime 基于 LangGraph图的执行状态哪个节点执行到哪、工具结果缓存是可持久化、可恢复的。续流接口的本质是「把图状态恢复然后继续推进」——没有这一层可恢复性任何续流设计都是空中楼阁。顺带说一句很多团队在做 SSE 方案时只盯着「下行怎么推」却忽略了「上行怎么续」。等到要做人机协作时才发现模型流已经断了业务状态也丢了只能让用户重新问一遍。这种体验用过一次就再也不想用第二次。四、四维对照表UI 真正订阅的是什么包流看完我们用一张四维对照表把所有关键差异固化下来。这张表的读法只有一个看 UI 真正订阅的是什么。表格里黄色底色的格子表示该格涉及 SSE4.1 产品形态mewhelp 是电商客服 Web 聊天DSH 是 DeepSeek Agent 的浏览器 GUIClaude Code 是终端 Ink TUI 加远程/桥接模式Codex 是 TUI / Desktop / SDK 三端并存。产品形态决定了它们对「界面通道」的需求强度单页聊天的通道可以极简三端共用的通道必须能承载多种宿主。4.2 模型侧流mewhelp 的上游模型可流式输出但 runtime 会把它转成事件UI 不感知DSH 把 DeepSeek SSE 翻译成 StreamChunkClaude Code 把 Anthropic 流翻译成内部 MessageCodex 把 Responses API 的 SSE/WS 翻译成 ResponseEvent。四家全部在模型边界做了翻译没有一家把模型原始流直接扔给 UI。4.3 Agent UI 通道最关键的差异这是四家分道扬镳的地方mewhelp浏览器 SSEPOST 请求 读 body这是四家里唯一把 SSE 直接接到 UI 的DSHHTTP RPC WS mux/api/remote.mux走 WebSocket 复用连接Claude Code本地是进程内生成器远程是 WS / Hybrid / SSEPOSTSDK 是 NDJSONCodexJSON-RPCstdio / unix socket / WebSocketSDK 是 JSONL。4.4 UI 事件粒度mewhelp 的事件粒度最小是一张小表delta / tool / citations / actions / interrupt / doneDSH 是会话日志流assistant/chunk、tool/、approval/加 follow 流Claude Code 是 SDKMessage 加 control_can_use_tool、interruptCodex 是 EventMsg 投影出的 item//delta、turn/* 和审批 ServerRequest。事件粒度直接决定前端逻辑的复杂度。mewhelp 的小表事件前端要自己拼状态Codex 的 item/turn 两级事件则把「生成项」和「回合」的边界在协议层就定义好了。4.5 真相来源mewhelp 的真相来源是「当前 HTTP 流 DB 会话」断流重连后状态需要前端自己恢复DSH 是可回放的 session logjournal任何时刻都能从日志重建 UI 状态Claude Code 是同一套 SDK 消息契约传输可插拔但契约唯一Codex 是 core 的 EventMsgapp-server 再投影给 UI。「真相来源」这一行值得单独划重点DSH 的可回放日志和 Codex 的单一 EventMsg 源都是把「真相」和「传输」解耦的典型做法。一旦 UI 出问题要排查你不需要重放模型流只需要重放事件日志。4.6 人机协作与 AG-UI人机协作这一行放到下一节专门讲。这里先剧透一个结论四家都没有采用业界 AG-UI 协议——mewhelp 是自研DSH 是 Typert RemoteClaude Code 是 Anthropic SDK stream-jsonCodex 是 app-server protocol。4.7 从对照表读出的两条主线整张表看完其实就两条主线贯穿始终主线一谁离模型更近谁的事件就越原始。mewhelp 的事件表最小也最贴近模型输出delta 直接对应 token因为它的界面离模型只隔了一层翻译Codex 的 item/turn 两级事件离模型最远因为中间还隔了 ResponseEvent 和 EventMsg 两层投影。事件粒度不是越高越好而是要和你的界面复杂度匹配界面交互越丰富越需要语义完整、粒度合适的领域事件。主线二真相来源决定了系统的可调试性。mewhelp 靠「当前流 DB 会话」兜底出问题只能看数据库DSH 的 session log 和 Codex 的 EventMsg 都是单一事件源出问题可以精确重放「这个用户、这个会话、第几步发生了什么」。如果你的 Agent 产品要支持问题排查、A/B 实验、行为回放请直接采用「可回放事件日志」作为真相来源不要事后补课。这两条主线一个是向外UI 看到的粒度一个是向内系统怎么被审计把表里七个维度串成了一个整体。五、人机打断同一需求四种握手「人机打断」HITL, Human-in-the-Loop是我认为这篇文章最值得细看的部分因为它最能暴露架构设计的分歧。四个项目都要实现「Agent 卡住等人确认」这件事但四种握手的形态完全不同5.1 mewhelp图暂停 同构续流mewhelp 的做法是把 HITL 嵌进聊天流。SSE 流里吐出一个interrupt事件不带 done前端据此渲染订单卡片或工单预览用户点击后前端POST /resume服务端基于同一个图LangGraph再开一条同构的 SSE 流继续跑。这个设计的优雅之处在于中断和续流用的是同一条通道、同一套事件格式前端只需要处理「流没结束但来了 interrupt」和「resume 后来了新流」两种状态。代价是中断状态天然绑定在当前的 HTTP 连接上多端共用时状态恢复要靠 DB 会话兜底。5.2 DSHWaterfall 问答DSH 的做法是独立出一个「瀑布」通道。Host 发出approval/request等待瀑布应答UI 通过$events订阅并应答结果再回传给 Host会话日志里另记 asked/decided 两个状态。它和 mewhelp 最大的不同是审批流和聊天流在协议层面就分开了。聊天继续聊天审批走审批两者各自有独立的生命周期。这给多窗口、插件投影比如 IDE 里弹审批框留出了空间。5.3 Claude Code控制面往返Claude Code 的做法最接近「远程调用」出站control_request: can_use_tool宿主或 UI 回control_response需要中断时走 interrupt 或 AbortController。它把「是否允许调用工具」这类决策做成了一对往返的请求-响应语义非常干净Agent 核不假设 UI 一定会同意每个敏感动作都显式请求授权。5.4 CodexRPC 审批请求Codex 的做法最「硬」app-server 向客户端发出item/.../requestApproval等ServerRequest——注意这是请求不是单向通知客户端必须回包。不回包Agent 就一直等。它把「审批」和「通知」在协议语义上彻底区分开通知是 fire-and-forget审批是 request-response。这让 Codex 的宿主TUI、Desktop、IDE 插件能统一处理「必须响应」的交互。5.5 这节的结论四种握手的本质差异只有一句话mewhelp 把 HITL 嵌进了「聊天流」另外三家把 HITL 做成了独立控制面。独立控制面的代价是协议面更大要单独定义审批事件、回包语义、超时处理收益是 TUI / IDE / 远程多宿主可以共用同一套审批逻辑且不会被聊天流的推进节奏绑架。如果你的产品未来大概率只有一个网页端mewhelp 的方案足够如果你知道会做 IDE 插件或桌面端请直接按「独立控制面」设计别回头改。5.6 四个容易被忽视的 HITL 工程细节把四种握手看完我还想提醒四个工程细节它们是 HITL 能不能真正落地的关键四个仓库里也都体现得很具体超时与超时后的行为。审批请求发出后用户可能一直不点。Codex 的 ServerRequest 语义里隐含着「宿主负责响应」但超时之后怎么办——重试、降级为默认值、还是挂起等用户回来——必须由业务层显式定义。四个项目里这一层语义都落在各自的宿主实现里而不是协议层。打断的幂等性。用户可能连点两次确认卡片或者点完确认又立刻撤回。mewhelp 的 resume 如果被重复调用图的执行必须幂等Claude Code 的 control_response 重复回包宿主必须能识别。HITL 通道越独立越要在接入端做幂等去重。状态可见性。用户等待审批时界面应该显示「Agent 正在等待确认」而不是「卡住了」。DSH 的 approval 状态写进会话日志、Codex 的审批是显式 ServerRequest都让「等待中」成为一个可查询的状态mewhelp 则依赖 interrupt 帧的语义让前端渲染等待态。多端协同时的审批归属。如果同一个会话同时在网页和 IDE 打开审批弹窗应该出在哪一端DSH 的插件投影和 Codex 的 JSON-RPC 多宿主设计本质都是在回答这个问题审批请求广播给所有宿主谁先响应谁生效或者指定优先级。这个问题现在不做多端上线那天一定会来找你。这四个细节决定了你的 HITL 是从「演示能跑」到「生产可用」之间的距离。六、「AG UI」到底在说什么先消歧义聊到 Agent 界面很多人会想到「AG-UI 协议」比如 CopilotKit 那套。这里必须先做一次消歧义四个仓库里都搜不到 CopilotKit 那种 AG-UI 协议。这里的 AG UI指的是 Agent 面向人的界面层是一个架构概念不是一个协议规范。这层理解很重要。因为「AG UI」在讨论里经常被当成一个技术标准来用而真实世界的开源项目各有各的私有实现。我们看四个项目各自的「AG UI 层」长什么样mewhelpapp/static/index.html里的readSSEStream函数按帧更新气泡、徽章、卡片整个 UI 就是「读流改 DOM」DSHui-chat/ui-conversation/ui-approval三个组件分别订阅 session.follow 日志流和 waterfall 审批流Claude CodeInk 的REPL组件加handleMessageFromStream远程模式下用sdkMessageAdapter把同一套消息灌进同一个状态机CodexTUI 和 Desktop 吃 app-server 的通知与审批 RPCTS SDK 吃更粗粒度的 JSONL item/turn 事件。四个项目的 AG UI 层差异很大但底层心智模型是一致的模型流先被翻译成领域事件UI 再订阅领域事件。这里必须纠正一个高频误解我单独拿出来讲因为它坑过很多人「支持 SSE」不等于「前端协议是 SSE」。Codex 和 DSH 都在大量使用 SSE但那是连模型的连 Agent UI 的是另一层。你去翻codex-api/sse/responses.rs会看到 SSE 处理被严格封装在 API 客户端内部你去翻 DSH 的llm-deepseek/sse.ts同样如此。所以下次有人在技术评审会上说「我们支持 SSE前端直接连就行」请先问一句你的 SSE 连的是模型还是连的是 UI这两个问题的答案是两种完全不同的架构。6.1 那 CopilotKit 的 AG-UI 协议又是什么既然提到了 AG-UI 协议就顺带把它和这四个仓库的关系说清楚避免大家混淆。CopilotKit 等社区推动的 AG-UI是一套面向 Agent 应用的标准协议提案目标是让「任何 Agent 后端」和「任何前端框架」之间能通过统一的事件格式互通——你可以把它理解成 Agent 界的 REST 或 MCP。它规定了事件怎么命名、流怎么分片、审批怎么表达属于「协议层」的规范。而本文这四个仓库全部是各自实现的私有协议mewhelp 的小事件表、DSH 的 Typert Remote、Claude Code 的 SDK stream-json、Codex 的 app-server protocol没有一个是照着 AG-UI 规范实现的。这不代表它们落后而是说明在标准化协议成熟之前每个产品都在用最贴合自己场景的方式解决同一个问题。这给我们的启示是如果你在 2026 年从零设计 Agent 界面架构不必死等 AG-UI 标准落地也不必完全自研——更好的姿势是按标准协议的思想设计自己的领域事件层事件命名规范、审批语义、流生命周期同时在接入层预留一个「协议适配器」将来 AG-UI 标准成熟时只换适配器不动内核。6.2 AG UI 层的通用设计清单最后给一份可落地的清单。无论你参考哪个项目AG UI 层至少要回答这五个问题界面订阅的事件从哪来是模型流直通还是 Agent 核翻译后的领域事件本文结论必须是后者事件的粒度怎么定细到 delta 逐字还是粗到 item/turn 整块取决于界面交互复杂度审批/打断走哪条通道聊天流内嵌还是独立控制面取决于端是否多样断线重连后状态从哪恢复DB 会话兜底还是可回放事件日志取决于是否需要审计与回放上行请求怎么发resume POST、WS 消息还是 JSON-RPC request取决于下行通道的单向性把这份清单当成评审清单用一份 Agent 界面架构方案拿出来五分钟就能判断它靠不靠谱。七、什么时候有用抄谁的形状技术方案没有银弹只有「在什么约束下最合理」。这一节给出选型建议原则只有一个按你要交付的产品选架构而不是按「谁在更新界面」选。7.1 学 mewhelpSSE 直达适合你的情况单页聊天、事件表很小delta / tool / interrupt / done 就够、希望 curl 就能看到流、HITL 就是聊天里点个卡片。这是四条路里成本最低的。但你要清楚它的边界多客户端、可回放、强类型 RPC 都不是它的目标。如果你确定产品永远只有一个网页端mewhelp 是最优解——它把「能跑」做到了极致。7.2 学 DSH日志 WS适合你的情况需要刷新续看页面关了再打开聊天记录和状态还在、多窗口、插件投影、审批与问答和聊天解耦。DSH 的核心资产是可回放的 session log。一切状态都从日志推导UI 只是日志的投影。代价是协议面更大你要维护 Typert Remote 加 session event map 这一整套东西。如果你的产品要做「历史会话可回放」或者「多端同步」这条路的投入是值得的。7.3 学 Claude CodeSDK 契约适合你的情况同一套消息既喂 TUI 又喂自动化宿主比如 CI 里跑 headless、传输可插拔stdio / WS / SSE 读随时换。它的核心思想是「内核稳、外壳多」消息契约是唯一的、稳定的传输层是策略化的、可替换的。如果你在做 SDK 型产品别人基于你的 SDK 开发自己的界面Claude Code 的形态就是范本。7.4 学 Codex双层投影适合你的情况桌面 TUI SDK 共用同一个核心、模型 SSE 必须严守在 API 客户端、UI 层需要多粒度的订阅粗粒度 item/turn 给 SDK细粒度 delta 给界面。它是四家里分层最彻底的模型 SSE → ResponseEvent → core EventMsg → app-server JSON-RPC 投影。每一层职责单一换模型供应商只动最外层换 UI 只动最内层。代价是链路长、概念多小团队要掂量一下维护成本。7.5 共同限制SSE 的单向性无论抄谁有一个限制逃不掉浏览器原生 EventSource 只支持 GET所以 mewhelp 用fetch读流Claude 的远程 SSE 也是「SSE 读 POST 写」的组合。SSE 天然是单向的——服务端推给客户端没问题客户端要发消息永远要另开一条上行通道。这一条决定了所有 SSE 方案的上行设计要么用 fetch POST 新开请求mewhelp 的 resume要么用 WebSocket 做双向DSH 的 mux要么用 JSON-RPC 的 requestCodex 的审批。下行是推送上行是请求-应答这两件事从一开始就要分开设计。八、收束一条因果链把全文压缩成一条因果链就是下面这张图模型吐 token →可选模型侧 SSE → Agent 核译成领域事件 → Agent UI 通道 → 界面增量更新人若介入走独立上行——resume POST / waterfall 回包 / control_response / approval RPC——而不是写回模型那根 SSE。这条链上每一环的选择都对应前面七节的结论模型侧 SSE 是「可选」的——有些供应商不流式你要在 Agent 核层把它兜住Agent 核译成领域事件是必须的——这是让 UI 不依赖模型供应商的唯一手段Agent UI 通道是多元的——mewhelp 选浏览器 SSEDSH 选 WSClaude Code 选生成器/NDJSON/WSCodex 选 JSON-RPC独立上行是刚需——SSE 单向决定了用户的动作永远走另一条路。如果你在评审一份 Agent 界面架构方案把这四行摆出来逐条对照方案的质量立判高下。九、总结给你四个可落地的动作文章写到这里把结论收敛成四条可执行的动作直接拿走用第一先画分层图再写代码。无论你选哪条路先在纸上画清楚四层模型层、Agent 核、UI 通道、界面层然后明确标注「SSE 出现在哪一层」。这一步能拦下 80% 的架构返工。第二模型流永远不要直接透传给 UI。在 Agent 核里做一次事件翻译让 UI 只消费领域事件。换模型供应商的成本就藏在这层翻译里。第三HITL 走独立控制面。只要你的产品有超过一个端网页 插件 桌面就把审批做成 request-response 的独立通道不要塞进聊天流里。第四上行和下行分开设计。SSE / WS 管下行推送resume / RPC 管上行请求。不要试图在一条单向通道里实现双向语义。这四个动作做完你的 Agent 界面架构至少不会在「SSE 到底连谁」这个最基础的坑里翻车。参考仓库与实现文件本文全部技术结论均依据以下四个开源仓库的实际实现整理非业界 AG-UI 规范mewhelpapp/api/chat.py、app/static/index.htmldeepseek-harnesspackages/api/gateway、session-controller、llm-deepseek/sse.tsclaudecodecli/transports/SSETransport.ts、entrypoints/sdk/*Schemas.tscodex-maincodex-api/sse/responses.rs、app-server-protocol如果你只想验证本文的某个结论直接去对应文件里搜索关键词即可mewhelp 搜readSSEStreamDSH 搜session.followClaude Code 搜control_requestCodex 搜requestApproval。源码不会骗人。写在最后SSE 和 Agent UI 的关系本质上是一个「职责边界」问题模型负责吐词Agent 核负责翻译界面负责呈现三者各司其职。四个开源项目用四种不同的通道组合回答了同一个问题而它们的共同点比差异点更重要——没有一家把模型的原始流直接扔给界面。希望这篇文章能帮你少踩一个坑。如果你正在做 Agent 界面架构或者对文中某个仓库的实现有不同理解欢迎在评论区交流。转载声明本文为原创文章如需转载请联系作者获得授权并注明出处。
返回列表