页面有时间,AI却说没有:排查一个Bug,搞懂了AI应用的调试逻辑

第一版跑通了,挺有成就感。

接着用就发现问题了。界面对话区太小,长回答只能看两三行。问AI"哪段时间观众反应最热烈",它回了一句"没有消息时间,无法比较时段"。

页面上明明每条评论都标了时间。AI为什么说没有?

页面有时间,不代表AI收到了时间

第一反应是去查代码。翻了存储层,时间字段好好躺在那。翻了页面渲染,时间也正常显示。那AI为什么说没有?

这节课教了一个排查思路:别急着改代码,先沿着数据流走一遍,看信息在哪一步丢的。

一个AI应用从用户提问到拿到回答,中间经过这些环节:

采集与存储 → 程序处理(筛选/截断/统计) → 组装模型请求 → 模型生成 → 页面呈现

每一步负责不同的事,也掌握不同的信息。存储里有时间,不代表模型请求里有时间。页面显示了时间,不代表模型"看到"了时间。

顺着走了一遍,找到了。CommentStore.for_ai_prompt()这个方法,组装发给模型的上下文时,只取了昵称和内容:

yaml 复制代码
存储里的原始记录:

nickname: 示例观众01, content: 大家晚上好, timestamp: 2026-10-12T20:00:05+08:00

实际传给模型的内容:

示例观众01: 大家晚上好

时间字段在上下文组装这一步被扔掉了。模型没收到,当然回答不了时间相关的问题。

信息在哪丢的,就在哪补。

AI应用的调试,跟传统调试不是一回事

以前写前端,debug基本就这些事:打断点、看变量、查DOM、追调用栈。逻辑是确定的,输入A必然得到输出B。

AI应用多了个不确定因素:模型。

模型那一步你控制不了。你能控制的是:传给模型的信息够不够、任务描述清不清楚、输出格式有没有约束。

所以AI应用的调试要分两层:

程序层------信息有没有正确传递。时间字段有没有进请求?筛选逻辑有没有丢数据?截断之后还剩多少条?这些是确定性的,可以写测试。

模型层------信息够了之后,模型的理解和回答质量行不行。给了时间它能正确统计吗?能区分"评论最多"和"情绪最正面"吗?这些不是确定性的,需要评估集。

这两层混在一起查,很容易搞混。程序层的问题用测试修,模型层的问题用评估集调。方向不一样。

"回答更好"不是一个可执行的需求

修完时间字段,你以为完了?没有。

时间补进去了,但"哪段时间反应最热烈"这个问题本身还有歧义。热烈是指评论条数最多?参与人数最多?还是正面情绪最集中?

不同定义对应不同的统计方式。你得先选定一个可以核对的口径。

课里选了这个:按采集时间,以一分钟为窗口,这批记录中哪一分钟评论条数最多。

定清楚之后,"回答更好"才变成了一个可验证的目标。不然你永远不知道模型的回答算对还是算错。

跟AI协作开发,"更好""更智能""更准确"这些词都是无效需求,得翻译成可核对的标准。

修了一个字段,后面还有一串事

时间字段补上了,接着又是一堆问题:

截断问题。 项目设置了上下文长度上限,超了就只保留最近的评论。如果最热闹的时段在被截掉的前半段,模型的答案还是错的。怎么解决?两种思路:要么回答时明确说"基于当前可见记录",要么让程序先做完整统计再把结果给模型。

历史对话污染。 用户问了第一轮,模型回答了。接着问第二轮,历史对话里带着第一轮的结论。如果两轮用的不是同一批评论,模型可能被上一轮的结论带偏。

时间含义。 存的时间戳是采集器的本地采集时间,不是平台原始发送时间。批量接收的消息可能有相同时间戳。这个时间能说明什么、不能说明什么,得写清楚。

一个字段背后,拖着一串约定。每个约定都得明确,不然模型就会在你没想到的地方出错。

评估集:怎么判断AI做得对不对

传统软件测试,输入确定,预期确定,跑一遍就知道过没过。

AI应用没那么简单。同一个问题问三遍,回答可能都不一样。怎么判断"做得对不对"?

课里的做法是建评估集。不是拿一百条随便问,而是精心设计几条有区分力的案例:

  • 单一高峰 vs 并列高峰------能不能正确统计并完整报告
  • 时间缺失 vs 时区跨界------能不能说明限制而不是瞎编
  • 早期高峰被截断------能不能识别输入范围,不冒充完整历史
  • 评论密集但包含质疑------能不能区分"活跃"和"正面"
  • 无评论或新旧记录冲突------能不能处理空输入

每条案例都有"必须成立的事实"和"不能推出的结论"。模型回答完之后,对照这些标准打分。

一次回答正确不代表做得好。用固定案例反复跑,才能看出稳定性。

重构的时候怎么保证不改坏

功能能用之后,总想把代码整理一下。但一改就怕把原来能用的搞坏了。

课里用了一个方法:特征测试(Characterization Test)。

思路很简单:不改代码,先跑一遍,把当前的实际行为记下来作为基线。然后开始重构。每改一步,跑一遍测试,看行为有没有变。变了就停下来查原因。

跟TDD的区别在于:TDD的预期来自"需求应该是什么",特征测试的预期来自"旧代码实际做了什么"。

旧代码的行为可能包含bug,没关系,先记下来。重构的目的是改结构不改行为。bug留着后面单独修。

有个关键动作:挑一个关键断言,故意制造偏差,看测试能不能发现。如果测试发现不了,说明这个测试本身不可靠。

一次修改怎么成为下一次开发的起点

这节课最后讲了一个我之前没想过的概念:维护记录。

改完代码不算完。你得把这次改了什么、为什么改、哪些测试保留、哪些期望被替代、实际验证到了什么范围,全部写下来。

不是为了写文档而写文档。是因为下一次你或者Agent接手的时候,需要知道:

  • 现在实际能做什么(当前行为)
  • 哪些行为必须保持(有效约定)
  • 这次改了什么(本轮变化)
  • 什么有证据,什么还没确认(验证与遗留)

代码写的是"现在长什么样",不写"为什么长这样"。没有变更记录,接手的人只能猜。Agent也一样,读代码能读出结构,读不出决策原因。

所以Spec不是一次性的东西。需求变了Spec跟着变,Spec变了测试跟着变,测试变了实现跟着变。每次变更都要同步更新,保持三份东西一致。

SpecDD在持续维护阶段的价值:不是帮你写出第一版,是让每一版都能接着往下做。

你在维护AI项目的时候,遇到过最头疼的"改了一个地方,另一个地方莫名其妙坏了"的情况是什么?

相关推荐
用户610719266631 小时前
我踩了4个坑才搞懂 AI 视频模型该怎么选
人工智能
doujiaozh1 小时前
从 0 到 1:我用 300 行代码给 AI 记忆加上了“不可篡改的收据”(附开源实现)
人工智能·开源
果霸大叔1 小时前
AI 网关第 9 篇:运行时处理模型——请求进到数据面后底层经历了什么
人工智能·自然语言处理·ai-native
zhidian_AI1 小时前
AI 智能体改 BUG,占满 CPU 内存?3 道资源护栏兜底
人工智能
Morning的Tech1 小时前
LangGraph 的 Swarm 多智能体机制
人工智能·agent
probably1211 小时前
基于水利部技术标准的山洪灾害科学防治体系建设探析
大数据·人工智能
自养Agent1 小时前
Agent Harness 从用到会造:写 MCP server,先写它会怎么坏
人工智能
软件史记1 小时前
免费 GLM 上下文实测:检索在 50 万~60 万失守,80 万仍被服务端接受
人工智能
晨间晓1 小时前
图片里的细小噪点怎么平滑?用华为云码道开发 OpenCV 图像滤波对比工具
人工智能·opencv·华为云