一年前遇到的难题,被AI解决了

一年前实习的时候,mentor 让我做一个 Agent 问答前端。

这个前端本身并不复杂:用户输入一个问题,后端通过 SSE 流式返回 Agent 的执行过程,前端把这些过程一步步渲染出来。比如先展示“开始分析问题”,然后展示“拆分问题”,再展示“搜索资料”,最后展示“组合答案”。

听起来是一个很标准的 Agent UI。真正麻烦的是,SSE 在 API 测试工具里一切正常,请求总耗时大概 10 秒,每一秒都会有新事件返回;但一到前端就变得非常奇怪:前 9 秒页面什么都没有,最后 1 秒所有内容一股脑全部冒出来。

这就很离谱。

如果后端本来就是 10 秒后一次性返回,那很好理解。但问题是,用 API 测试工具直接请求后端,明明能看到事件一条一条回来。也就是说,后端看起来没问题,SSE 协议看起来没问题,只有前端页面不对劲。

当时的误判

我一开始怀疑是前端代码太臃肿。

当时的想法是:是不是每秒都有数据传来,但前端代码来不及消费?是不是 Vue 的状态更新太频繁?是不是消息列表渲染太重,导致浏览器主线程被阻塞?后来 mentor 和一个专门做前端的同事也一起看,大家又怀疑过 nginx gateway、后端网关、缓存、压缩、浏览器差异。

这些猜测都很合理,因为 SSE 这种东西一旦出问题,表现经常像玄学。你以为是渲染问题,可能是网络问题;你以为是后端问题,可能是代理问题;你以为是代理问题,又可能是某个响应头没配对。

我们捯饬了两周,最后也没彻底弄明白。

这件事就这么悬着了。

今天 Codex 找到了原因

今天我重新把这个问题丢给 Codex,让它看代码、看现象、写排查报告。它最后给出的结论是:问题不在 Vue,也不在后端,而在开发环境里的 Vite proxy。

更准确地说,当时前端请求并不是直接打到真实后端,而是先经过本地 Vite 开发服务器的代理,再转发到后端。普通接口这么做完全没问题,但 SSE 是流式响应,它对中间链路非常敏感。

这条链路里,早期的 SSE 事件没有立刻被浏览器收到,而是在代理层被缓冲了。于是后端其实已经一条一条发了,API 测试工具也确实能一条一条看到;但浏览器通过 Vite 代理访问时,前面的事件被攒住,等到响应头和后续数据一起释放,前端看到的就是“前面一直空白,最后突然全出来”。

这解释了一个当时很迷惑的现象:Answer 部分有时又能正常流式输出,所以看起来特别像前端渲染性能问题。但真正判断问题位置的关键,不是看页面有没有更新,而是看浏览器 Network 面板里的响应内容是不是逐步增长。

如果 Network 里响应本身就是一批一批出现,那就说明前端代码还没拿到数据,谈不上“消费不过来”。这时优化 Vue 渲染、减少状态更新、改组件结构,都只是在错误的位置努力。

解决方案

最后的解决方案反而很简单:让 Agent 的 SSE 接口绕过 Vite proxy,直接请求后端 API Host。

具体做法是给开发环境单独加一个 Agent API 地址,比如:

VITE_APP_AGENT_API_HOST=http://真实后端地址

然后在接口层区分两类请求:

  • 普通 HTTP 接口继续走原来的 Vite proxy,保持开发体验不变。
  • Agent SSE 接口使用 agentApiBase + '/local_doc_qa/local_doc_agent_chat',由 fetchEventSource 直接请求真实后端。

这样一来,中间少了一层会影响流式传输的开发代理,浏览器就能像 API 测试工具一样,实时收到后端发来的 SSE 事件。

这个修复并不炫技,但它非常关键。它把问题从“前端是不是太慢”重新定义成了“流式响应有没有被中间链路破坏”。

这件事给我的提醒

SSE 问题最怕只看页面表现。

页面不更新,不一定是组件没渲染;也可能是浏览器压根没收到数据。排查时至少要对比三条链路:

  1. 直接请求真实后端,看 SSE 是否逐条返回。
  2. 请求前端开发代理地址,看是否出现攒包。
  3. 看浏览器 Network 里的响应内容,是逐步增长,还是最后批量出现。

只要真实后端正常、代理地址异常,基本就可以把注意力集中到代理、网关、压缩、缓冲、响应头这些中间环节上。

这也是我现在回头看这件事最大的感受:很多技术难题难的不是修复,而是定位。修复可能只改几行配置,但定位需要你知道应该怀疑谁、应该排除谁、应该看哪一个证据。

AI 编程带来的变化

这次最让我感慨的,其实不是一个 SSE 问题终于解决了,而是 AI 编程正在改变程序员这份工作的重心。

以前我们经常要把大半个脑子装满繁杂的代码细节:这个库的 API 怎么写,那个代理的行为是什么,某个框架的边界条件在哪里,某段异步代码为什么没按预期执行。很多精力花在“记住”和“试错”上。

但现在,像 Codex 这样的 AI 工具已经可以读代码、跑命令、对比现象、写排查报告,还能把一个模糊的问题拆成可验证的假设。程序员不再必须亲自背下所有细节,更多精力可以放在业务设计、系统设计、算法设计,以及判断 AI 给出的结论是否可靠。

这几乎是在重塑程序员职业。

当然,这一定会让一部分只会 CRUD、只会照着需求打代码的人变得危险。因为如果工作内容只是把字段从数据库搬到页面上,把接口返回值塞进表格里,AI 确实会越来越擅长。

但程序员的价值从来不只是写代码。

理解业务、抽象问题、设计系统边界、判断方案取舍、和人沟通、处理不确定性、为长期维护负责,这些能力依然重要,而且很难被简单替代。AI 可以帮你写代码,但它还需要人告诉它什么值得写、为什么这么写、做到什么程度算对。

我很喜欢 ATM 和银行柜员这个例子。直觉上,ATM 能取钱、存钱、查余额,似乎应该消灭银行柜员。但 James Bessen 在 IMF 的文章 Toil and Technology 里提到,ATM 降低了单个网点所需的柜员数量,同时也降低了开设银行网点的成本。银行开了更多网点,柜员的角色也逐渐从重复点钞,转向销售、客户服务和金融产品介绍。

新技术往往不是简单地把人替换掉,而是把人推向新的任务。

AI 替你写代码,企业可能因此开发更多内部系统、更多 AI 应用、更多自动化流程。程序员也会从“代码打字员”转向“软件系统设计者 + AI 驯兽师”。

这件事让我觉得很兴奋。一个一年前三个人看了两周都没彻底解决的问题,今天被 AI 帮我重新拆开、定位、修复。它不是让我觉得程序员不重要了,而是让我更强烈地意识到:未来真正重要的,可能不是你记住了多少代码细节,而是你能不能提出好问题、判断好答案、设计好系统。

代码还要写,但写代码已经不再是全部。

评论区已延迟加载滑到评论区附近时会自动加载,也可以现在手动打开。