最新资讯
dev.to 你的 Agent Harness 应具备的 17 项能力 dev.to 离线 AI 记忆中 0.8 余弦阈值为何失效 dev.to Kimi K3.1 Pro 开放权重:自托管 2.8T 模型前的开发者清单 dev.to 如何通过工具调用引导模型以提升 Harness 质量 dev.to AI Agent 审计轨迹不等于对话记录 dev.to 精简 MCP server 工具数量:更少工具、更小影响面,双重收益 dev.to 为什么 git checkout 无法撤销 AI 编码 agent 的改动? wechat AI短剧门槛降降降!实测即梦短剧工作室:剧本分镜预演一条龙,伙伴计划长期护航 dev.to 让模型改写文档前,先量化事实保真度 dev.to 回放 153 个 Claude Code 与 Codex 会话,审计 Agent 的生产影响范围 dev.to 银行 77 意图分类最便宜 LLM:GPT-6.1 Terra 每次成功任务 $0.000727 theinformation.com Chesky的副业:Airbnb向CEO新设AI实验室出售IP
多元动态
登录 注册
行业动态 dev.to · @Raju Dandigam

用 Cypress 测试流式 AI 界面:不必断言每个 token

原文 我的收藏

文章指出,流式 AI 测试若断言 Hel→Hello→Hello, I→Hello, I can help 这类精确 token 序列会非常脆弱,因为 chunk 边界属于传输细节,会随缓冲、供应商行为、网络时序或客户端库升级而变化。作者建议把流建模为事件(started、text_delta、tool_started、tool_completed、completed、failed),先归一化再测试 UI 的确定性职责:显示进度、区分工具调用与普通文本、忽略过期事件、支持取消并以一致状态结束。

AI推荐理由 文章给出可操作建议:流式 AI 界面测试不应断言逐 token 的 chunk 边界,而应把流归一化为 started、text_delta、tool_started、tool_completed、completed、failed 等事件,再测试进度展示、工具调用区分、过期事件忽略、取消和最终状态等确定性契约;属于开发工具层面的具体方法,但未提供实测数据或完整示例。

68

正文

A streaming AI test becomes brittle the moment it expects this exact sequence: Hel → Hello → Hello, I → Hello, I can help Enter fullscreen mode Exit fullscreen mode Chunk boundaries are transport details. They can change with buffering, provider behavior, network timing, or a client-library upgrade while the visible result remains correct. The UI still has deterministic responsibilities. It must show progress, distinguish a tool call from ordinary text, ignore stale events, support cancellation, and end in a coherent state. Test those contracts instead of every token. Model the stream as events Do not let the component treat an incoming byte stream as an undocumented collection of callbacks. Normalize it first: type AgentEvent = | { type: "started"; runId: string } | { type: "text_delta"; runId: string; text: string } | { type: "tool_started"; runId: string; name: string } | { type: "tool_completed"; runId: string; name: string } | { type: "completed"; runId: string } | { type: "failed"; runId: string; code: string }; Enter fullscreen mode Exit fullscreen mode The renderer can derive a small set of user-visible states: idle → connecting → streaming → using_tool → streaming → complete └──────────────→ failed Enter fullscreen mode Exit fullscreen mode That state machine is the stable testing surface. “Two chunks arrived instead of three” usually is not. Inject a controllable stream in tests For component or end-to-end tests, put the transport behind an interface. In test builds, expose a synthetic event source: declare global { interface Window { agentTestStream?: { emit(event: AgentEvent): void }; } } Enter fullscreen mode Exit fullscreen mode Now Cypress controls causality without calling a model: it("shows a tool phase and completes the answer", () => { cy.visit("/assistant?stream=test"); cy.get("[data-testid=ask]").click(); cy.window().then(({ agentTestStream }) => { agentTestStream!.emit({ type: "started", runId: "run-1" }); agentTestStream!.emit({ type: "text_delta", runId: "run-1", text: "Checking the policy. ", }); agentTestStream!.emit({ type: "tool_started", runId: "run-1", name: "search_policy", }); }); cy.get("[data-testid=status]").should("contain", "Searching policy"); cy.window().then(({ agentTestStream }) => { agentTestStream!.emit({ type: "tool_completed", runId: "run-1", name: "search_policy", }); agentTestStream!.emit({ type: "text_delta", runId: "run-1", text: "Returns are accepted within 30 days.", }); agentTestStream!.emit({ type: "completed", runId: "run-1" }); }); cy.get("[data-testid=answer]") .should("contain", "Checking the policy") .and("contain", "30 days"); cy.get("[data-testid=status]").should("contain", "Complete"); }); Enter fullscreen mode Exit fullscreen mode This verifies progressive rendering and the final semantic landmarks. It does not care how the provider would divide the sentence. Test the failures streaming makes easy to miss The highest-value cases are usually not the happy path. A stale run writes into a newer answer Start run-1, then start run-2. Emit a late delta from run-1 and assert that it is ignored. Every event should carry a run identifier, and the renderer should accept only the active one. Cancellation changes the terminal state After the user clicks Stop, emit another delta. The UI must remain cancelled and must not append the text. Also assert that the transport's abort function was called. A tool fails after text has appeared Partial prose should not leave the interface looking complete. Emit tool_started, then failed, and assert that the error is visible while the incomplete answer is clearly marked. Duplicate events arrive Reconnects can replay events. Give events stable IDs or sequence numbers and assert that a duplicate delta is not rendered twice. The stream ends without a terminal event Close the transport after several deltas but before completed or failed. The UI should move to an explicit interrupted state instead of presenting partial prose as a finished answer. This catches a class of bugs that an HTTP status assertion cannot see. Use network interception at the right layer cy.intercept() is useful for controlling the initial request, authentication errors, HTTP status, and response delay. It is less useful when the test needs precise control over a long series of application-level stream events. An injected adapter keeps the browser behavior real while making the event schedule deterministic. Keep one integration test against the actual streaming endpoint to verify framing and parsing. Keep most UI state tests provider-free and synthetic. That split makes failures easier to classify: protocol problem or rendering problem. At the protocol layer, include fixtures where one logical event is split across network chunks and several events arrive in one chunk. Your parser must reconstruct frames before the UI reducer sees them. The component test can then remain deliberately unaware of TCP, SSE, or fetch buffering. Assert responsibilities, not prose A robust streaming test usually checks: the visible state transition; ordering around tool activity; cancellation and stale-event handling; accessibility announcements; the final structured outcome or a few semantic landmarks; absence of duplicated content. It should rarely check every intermediate string. For accessibility, assert that status changes are announced once and that rapidly arriving deltas do not flood a live region. The visual answer may update continuously while assistive technology receives milestone events such as “searching,” “approval required,” and “complete.” Streaming interfaces are asynchronous state machines wearing a chat UI. Once tests target that state machine, they become both stricter about real bugs and less sensitive to irrelevant token timing. References Cypress network interception Cypress retry-ability

来源报道 · 1 篇 · 以下链接均前往原始媒体

← 返回精选动态 原文

更多内容