---
title: "破局流式传输占位符泄露：基于多模型协作的前端重构深度复盘"
date: 2026-04-28
description: "弱网或信号切换时，聊天气泡偶现 __STREAM_MARKER__ 协议标记泄露——根因是历史遗留 SSE 字符串拼接未做滑动窗口状态清洗。本文复盘一次「轻模型编码 → 中模型评审 → 快模型补强」三阶段 Pipeline：4.3% Token 预算完成零缺陷交付，5/5 单测通过，相较单模型全链路节约 66% 成本，并完整呈现闭包状态机滤网的设计细节。"
canonical_url: "https://yijinlee.com/articles/article-49"
tags:
  - "AI Coding"
  - Cursor
  - gpt-5.3-codex
  - gpt-5.5-medium
  - "Composer 2.5 Fast"
  - SSE
  - Vue
  - 状态机
  - 三模型协作
  - "Prompt Engineering"
topic: "AI 外骨骼"
---

METRICS: 4.3% Token · 5/5 单测 · 66% 成本节约 · 36.54s 构建

在日常的业务迭代中，我们常常会遭遇一些让人哭笑不得的线上缺陷。前阵子，我们团队负责的出海 App 聊天界面，被用户截屏反馈了一个奇怪的现象：在网络环境较差、或者频繁遭遇信号切换的场景下，聊天气泡里偶尔会蹦出一段突兀的字符串——`__STREAM_MARKER__`。这就是典型的**流式数据边界标记（Marker）泄露**。

从技术源头来看，导致这个问题的根本原因在于历史遗留的 `src/xx/chat/index.vue` 组件。该组件在处理 SSE（Server-Sent Events）流式响应时，由于当时为了赶进度，直接采用了最原始的字符串拼接逻辑，导致网络层面的 TCP 分包断裂或分片重组时，未对底层的协议标记做滑动窗口式的状态清洗。

处理这个缺陷本身并不难，难的是如何在**零缺陷交付**、严格遵循 SOLID 原则、函数认知复杂度 ≤ 15、全中文注释、不破坏既有功能的苛刻指标下，优雅地完成重构。针对这次重构，我们设计了一套**「轻模型编码 → 中模型评审 → 快模型补强」**的工程 Pipeline 协作范式。

### 一、为什么传统的"单模型重构"注定失败？

在进入具体的技术方案之前，我们不妨先聊聊很多团队在使用大模型辅助重构时容易踩入的两个极端误区。

- **误区一：对"全能模型"的过度迷信**：许多开发者倾向于将几十 KB 甚至是上万行的旧文件直接丢给旗舰大模型，并附上一句简短的指令："帮我重构成符合 SOLID 原则的代码"。大模型在面对复杂的视图层文件时，往往会因为上下文过长而产生注意力涣散——可能在重构核心逻辑的同时，不小心删掉了某个角落里的 CSS 样式，或是把 `v-if` 改成了 `v-show`。更糟糕的是，旗舰模型的推理成本极其高昂，大量 Token 被无谓地浪费在解析重复的 HTML 模板上。

- **误区二：低估了网络流式传输的"狡猾"**：很多轻量级模型或初级开发人员，在看到 `__STREAM_MARKER__` 泄露时，第一反应往往是"这不就是个字符串 replace 的事情吗？"然而，真实的生产环境网络是极其复杂的。SSE 数据流在经过移动网关、Wi-Fi 路由器和弱网代理时，一个原本完整的标记字符串 `__STREAM_MARKER__`，完全有可能在传输层被切碎——第一个 TCP 数据包的末尾收到了 `__STREAM_`，而剩下的 `MARKER__` 却落在了第二个数据包的开头。如果你仅仅在单个 chunk 到达时进行静态的正则替换，那么这个断裂的标记就会完美避开清洗逻辑，最终拼接进用户的聊天气泡中。

### 二、混合模型策略与 Token 投资回报率（ROI）

为了用最理性的成本解决最感性的业务痛点，我们对本次重构的任务进行了精细化的阶梯式拆解，调度三种不同特性的模型各司其职：

| 阶段 | 选用模型 | 核心职责 | 单次配额消耗 |
| :--- | :--- | :--- | ---: |
| 1. 编码 | gpt-5.3-codex | 定位老旧拼接点，提取逻辑，生成工具库基线代码与 Vue 层调用骨架 | 2.0% |
| 2. 评审 | gpt-5.5-medium | 边界漏洞审查，重点捕捉弱网下的异常分片风险 | 1.1% |
| 3. 补强 | composer-2.5-fast | 落地状态机滤网，编写 5 大场景单测，补充中文技术文档 | 1.2% |

💡 **为什么这个组合能够省钱？** 本次任务总计消耗了综合预算额度的 **4.3%**（约 12,000 tokens）。若从一开始就使用 gpt-5.5-full 进行多轮对话和代码补全，全量文件的反复吞吐将至少吃掉 **35,000+ tokens**。通过合理的 Pipeline 设计，我们在保障产出质量的同时，为团队节约了近 **66%** 的 Token 成本——这就是"重推理留在评审，高频迭代交给快模型"的核心魅力。

### 三、多模型接力重构的真实心路历程

软件重构从来不是一条坦途，即便是有了 AI 的加持，整个过程依然充满了技术上的博弈与对边界条件的反复打磨。

#### 第一棒：gpt-5.3-codex 的"稳健"与"盲区"

在第一阶段，我们将 `index.vue` 的核心 SSE 接收逻辑和团队的编码规范输入给 gpt-5.3-codex。它几乎在几秒钟内，就敏锐地识别出了文件中老旧的 **7 处字符串拼接点**（比如 `this.message += chunk.data`），将这些凌乱的赋值语句抽象出来，在 `src/xx/streamContentFilter.js` 中创建了一个相对干净的基线函数，并生成了优雅的 Diff 调用骨架。重构后 `npm run build` 和 `eslint --fix` 控制台一片绿意，无任何新增 Lint 错误。

然而，轻量级编码模型本质上是在做高级的"模式匹配"，对于业务场景中动态、不可预测的网络分片缺乏直觉。它写出来的第一版过滤器，只是简单地用 `replaceAll('__STREAM_MARKER__', '')` 来打补丁——对"Marker 跨包断裂"这一经典边界风险完全没有感知。

#### 第二棒：gpt-5.5-medium 的"鹰眼评审"

带着第一阶段的产出，我们向 gpt-5.5-medium 提出了一个极具挑衅性的问题："请从网络传输不确定性的视角，严谨地指出这段代码还存在哪些导致泄露的 Medium 或 High 级别风险？"

中模型一针见血地指出："当上一个数据包以 `__STREAM_` 结尾，下一个数据包以 `MARKER__` 开头时，第一阶段的逻辑将彻底失效。你需要设计一个**残余前缀（pendingPrefix）缓冲区**，用来挂起那些疑似被截断的协议标记。"它还精准提示我们可以引用 `useStreamData.ts` 中已定义好的底层常量。中模型只给出了清晰的解法方向和精简的伪代码思路，并没有大包大揽去写具体实现——这种"只动脑、不动手"的特性，反而帮我们省下了大量的代码生成 Token。

#### 第三棒：composer-2.5-fast 的"敏捷落地"与小插曲

最后，我们将中模型的解法方案以及第一阶段的基线代码打包，交给了高吞吐的 composer-2.5-fast。快模型在理解了状态机的输入要求后，迅速落地了包含滑动窗口状态机的完整工具函数，并编写了包含 **5 大核心场景** 的自动化单元测试脚本，响应时间不到 3 秒。

然而，我们也遭遇了一个工程偶发事件：部分开发人员在 Windows 环境下未配置全局编辑器规范，快模型生成的中文注释在本地打开时变成了 GBK 乱码。随后我们通过调整 `.editorconfig`，迅速解决了这一乱码返工问题——AI 走得再快，也离不开底层工程规范的托底。

### 四、核心产出：优雅的架构与代码实现

为了彻底贯彻 SOLID 原则中的**单一职责原则（SRP）**，我们拒绝了向 Vue 视图层组件直接注入各种零散状态函数的方案。所有的复杂清洗逻辑与状态完全收拢在 `src/utils/streamContentFilter.js` 的闭包中，视图层只需要像管道一样进行调用。

#### 4.1 工具流设计：闭包状态机滤网（伪代码）

```text
// 辅助函数：检查内容末尾是否残留半截 Marker 前缀
FUNCTION getTrailingMarkerPrefix(content, marker):
    FOR i FROM marker.length - 1 DOWNTO 1:
        targetPrefix ← marker 的前 i 个字符
        IF content 以 targetPrefix 结尾:
            RETURN targetPrefix
    RETURN 空串

// 工厂函数：创建流式内容过滤器（闭包状态机）
FUNCTION createStreamContentFilter():
    pendingPrefix ← 空串          // 闭包内持久状态，挂起跨包残余
    MARKER ← '__STREAM_MARKER__'

    RETURN FUNCTION(chunk):       // 每收到一个 chunk 执行一次
        content ← pendingPrefix + chunk
        pendingPrefix ← 空串

        IF content 包含完整 MARKER:
            content ← 删除所有 MARKER

        trailingPrefix ← getTrailingMarkerPrefix(content, MARKER)
        IF trailingPrefix 非空:
            pendingPrefix ← trailingPrefix
            content ← 去掉末尾 trailingPrefix 对应长度

        RETURN content
```

```statemachine
pendingPrefix 暂存|跨包拼合|将上一轮截断残余挂起，与新 chunk 拼接成完整窗口
全局 replaceAll|完整 Marker 清洗|窗口内出现完整 __STREAM_MARKER__ 时一次性清除
末尾 prefix 捕获|边界截断|检测内容末尾是否为半个 Marker，切除并挂起待下一轮
```

#### 4.2 视图层的极简"降维打击"

在未重构前，AI 曾建议我们在 Vue 的 data 里面新增好几个流状态变量（如 `isMarkerPending`、`lastChunkTail` 等）。经过架构解耦后，Vue 组件内部变得异常干净：

```javascript
// 初始化流式连接，符合开闭原则（OCP）
const streamFilter = createStreamContentFilter();

sse.onMessage((chunk) => {
  // 极简调用，视图层完全不需要关心状态机与网络截断
  this.chatMessage += streamFilter(chunk);
});
```

这种设计让函数的认知复杂度直接归零（从原先的 **18 锐减至 1**），后续无论协议标记怎么变，视图层代码都无需修改一行。

### 五、质量捍卫者：5 大单测场景全覆盖

为了让这次零缺陷交付不是一句空话，我们在 `scripts/test-stream-content-filter.mjs` 中构建了 5 个完全模拟真实网络极端环境的单元测试用例：

```testscenarios
场景 1 · 常规干净流式分片|Hello  + World! → Hello World!|最普通的打字机效果
场景 2 · 完整 Marker 夹带在包中间|AI回复：__STREAM_MARKER__我是最终答案 → AI回复：我是最终答案|完整内嵌 Marker 清洗
场景 3 · Marker 跨包断裂（核心）|半截前缀 + 半截后缀 → 空 + 重构已完成。|TCP 分片断裂状态机拦截
场景 4 · 伪断裂回写|*da + rling → 空 + *darling 按钮|疑似前缀但非 Marker，内容零丢失
场景 5 · 存量历史非流式消息|历史聊天记录__STREAM_MARKER__完全兼容 → 历史聊天记录完全兼容|整包消息完美兼容
```

在人工回归阶段，我们联合 QA 团队对**结构化日程、快速滚动、历史回放、FAQ 打字机、OCR 表单** 5 大核心业务场景进行了全面的盲测，最终实现了线上零缺陷交付，综合场景覆盖率 ≥ 90%。

### 六、开发者反思：那些 AI 没告诉你的事

虽然这次重构的结果非常圆满，但在真实的复盘会议上，我们团队也认真梳理了整个过程中暴露出的几个待改进点：

- **测试左移需要"工程规范先行"**：轻量级编码模型本身不具备高级未曾设想风险的洞察力——它写不出断裂分片的单测，是因为它压根不知道这个风险的存在。真正的闭环应该是将规范固化在 Prompt 模板中，强制加入约束："凡是涉及流式网络传输的重构，必须包含 TCP 分片断裂测试"。

- **用工具链代替人肉检查**：面对 Windows 环境下因字符集不一致导致的注释乱码返工，引入 iconv-lite 并非优雅解法。最彻底的做法是在项目根目录配置严谨的 `.editorconfig`，并利用 husky + lint-staged 在 git commit 前置管道中加入文件编码强制校验。

### 七、结语：让技术回归理性与温度

本次重构的最终数据是让人欣慰的：总 Token 消耗减少了 **66%**，构建耗时稳在 **36.54s**，零新增 Lint 错误，测试通过率 **100%**。但比这些冷冰冰的数据更让我们感到踏实的，是代码重构后带来的那份清爽与优雅。

软件工程从来不是一场拼谁用的模型更贵、谁写代码速度更快的奢华军备竞赛。它是一门关于平衡的艺术——在业务痛点、系统架构、以及开发成本之间，寻找那个最合理的支点。通过**「轻模型编码、中模型把关、快模型迭代」**的协作实践，我们不仅用极低的预算治愈了线上老旧代码的"顽疾"，更重要的是，它让我们看到了一种更具人情味、也更科学的研发姿态：把机械的、重复的骨架编写交给 AI 冲锋；把严谨的、高阶的风险审查交由中模型把关；而作为开发者的我们，则把宝贵的精力聚焦在架构的解耦、语义的抽象以及对用户体验的极致追求上。

## 参考文章：相关链接

- [MDN · ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)（官方文档）— 流读取 API，理解占位符泄露的传输层。
- [HTML Living Standard · Streaming](https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html)（权威引文）— 动态标记插入标准语境，对照流式 HTML 重构。
- [流式 FAQ 首尾截断架构重构](https://yijinlee.com/articles/article-56)（站内深度）— 流式 UI 收敛的分层方法论。
- [流式 HTML「川剧变脸」破案现场](https://yijinlee.com/articles/article-59)（站内深度）— 标签被切断的姊妹案。

