跳到主要内容

763 篇博文 含有标签「ai-engineering」

查看所有标签

Agent 延迟预算是树而非线 —— 你一直在错误的维度进行调试

· 阅读需 14 分钟
Tian Pan
Software Engineer

用户报告“今天早上助手感觉很慢”。值班工程师调出火焰图,按持续时间降序排列工具调用,找到了最慢的一个——耗时 2.1 秒的向量搜索——将其优化到 900ms,发布修复补丁,并将事件标记为已解决。一周后,同样的投诉再次出现。向量搜索仍然是 900ms,但该查询类型的端到端延迟实际上变得更糟了。火焰图中没有任何内容能解释原因。

这就是当工程师在“线”轴上调试一棵“树”时所发生的情况。Agent 延迟不是一系列顺序步骤的瀑布——它是一个由规划调用、工具子树、并行扇出、重试和递归子 Agent 组成的嵌套树。当预算是结构化的,而工具却将其视为线性的,局部优化就会错过真正的违规点,而违规点存在于时间如何分布在各分支中,而不是任何单个调用耗时多久。你可以让每个叶子节点都变得更快,但交付的 p99 却仍在恶化。

你的 AI 产品在需要另一个模型之前,更需要一名 SRE

· 阅读需 10 分钟
Tian Pan
Software Engineer

我在陷入困境的 AI 团队中看到的最显著模式,是他们复杂的模型栈与原始的运营水平之间的差距。一个团队可能在生产环境中运行三个前沿模型,背后是自定义路由逻辑、包含八个检索阶段的 RAG 流水线,以及一个调用二十个工具的智能体。但与此同时,他们没有轮值制度、没有 SLO、没有运行手册,甚至只有一个 #incidents Slack 频道,在那里的提示词是由当时刚好醒着的某个人进行实时热修复。该产品运行在 2026 年的模型基础设施和 2012 年的运维基础设施之上,而这种差距每周都会导致另一次故障。

当问题出现时,本能反应是去拨动模型杠杆。质量下降了?试试新版本。延迟激增了?换个供应商。生产环境中出现幻觉?再加一个护栏提示词。这些都无法解决根本问题,即没有人将系统的可靠性作为一种专业规范来负责。这些团队真正需要的——通常在他们需要另一位应用科学家之前——是他们的第一位 SRE。

基准测试泄露:你的评估集是如何悄悄加入训练语料库的

· 阅读需 13 分钟
Tian Pan
Software Engineer

你最信任的基准测试极有可能正是在对你撒谎。公开评估是一个闭环:你发布测试,有人抓取它,下一代模型在抓取的数据上进行训练,于是你信任的衡量标准的分数在没人触碰底层能力的情况下提高了 10 分。测量体系保持不动,而它所测量的对象却在下方发生了偏移,“模型在这个基准测试上表现更好”与“模型在这个任务上表现更好”之间的差距每个季度都在拉大。当这种分歧明显到足以引发争论时,该评估已经发布了六次排行榜更新和三个产品路线图,而这些都假设那个数字是有意义的。

这并非一种假设性的失败模式。非公开的预 RLHF 版 GPT-4 基础模型已被证明能够逐字重现 BIG-Bench 的 canary GUID,Claude 3.5 Sonnet 也是如此,这都表明原本应该被隔离的任务数据最终进入了训练。大约 40% 的 HumanEval 样本被确定为已污染,从 GSM8K 中移除污染子集后,测得的准确率下降了约 13 分。SWE-bench Verified 目前显示有记录的数据泄漏率为 10.6%,OpenAI 在 2025 年末其内部审计发现每个主要的前沿模型都能为某些任务逐字复现金标准补丁(gold patches)后,公开停止了对其的报告。我们用来比较模型的数字正越来越多地变成关于记忆力的数字,而不是能力的数字。

对话历史是你的提示词从未承认的负担

· 阅读需 12 分钟
Tian Pan
Software Engineer

下次当用户抱怨“AI 今天变笨了”时,去看看你产品的分析数据。筛选出对话轮次超过 20 轮的会话。你会发现每次都是同样的 U 型曲线:前几轮表现良好,中间几轮表现也不错,而到了后期轮次,质量却直线下跌。提示词没变,模型也没变。变化的是,后期每一轮对话都背负着沉重的载荷:用户的拼写错误、话说到一半的废话、模型的模棱两可、后来被撤销的更正、没人重读的工具输出,以及用户在第四轮就放弃了的目标残余。你的提示词模板把这些“沉积物”当成了信号,模型也是如此。但这不应该。

对话历史并非免费的上下文。它是一种你每一轮都在付费重新发送的负债;它变得越混乱,就越会损害你向用户收费提供的答案。“聊天”这个隐喻是混乱的根源。聊天界面让用户和工程师习惯于将记录视为神圣不可侵犯的 —— 可滚动的、仅限追加的、从不重置。这种习惯被原封不动地引入了 LLM 应用中,尽管在模型处理上下文的物理机制上并没有这种依据。模型是无状态的。对话记录只是你选择不断拉长的一段字符串。你可以缩减它,而且通常你应该这样做。

演示循环偏见:你的开发流程如何悄然演变为针对“有魅力的失败”进行优化

· 阅读需 12 分钟
Tian Pan
Software Engineer

每个 AI 产品团队都会有一种特定的会议,通常发生在周四。有人共享屏幕,在 notebook 里输入一个 prompt,然后运行三四个例子。房间里的人反应热烈。大家惊叹“哇”。有人截图发到 Slack。决策就这样做出了——上线、更换模型、调整 temperature。没有人记录失败率,因为根本没人去衡量它。

这就是演示循环(demo loop),它带有一种几乎没有团队意识到的结构性偏见:它筛选的不是最佳输出,而是最“易读”的输出。几周或几个月下来,你的 prompt 不断演进,最终生成的是那些能“在会议中镇住场面”的答案——自信、流利、格式整齐、切中主题。至于它们是否正确,则是另一个变量,而你的流程并没有衡量这个变量。

其结果就是我所说的“有魅力的失败”(charismatic failure):输出结果在某些方面是错误的,但由于选择压力,你的演示循环已经被训练得会自动忽略这些错误。

“以后再加评估”的陷阱:测量债务如何产生复利效应

· 阅读需 10 分钟
Tian Pan
Software Engineer

每个在没有评估(evals)的情况下发布 AI 功能的团队都会对自己讲同样的故事:我们会以后再添加衡量标准,等到找到产品与市场契合点(PMF)之后,等到提示词(prompt)稳定之后,等到下一次发布之后。六个月后,提示词已经被四位工程师和两名产品经理修改过,其行为支撑着三个客户集成,团队发现“以后添加评估”意味着要从从未为此目的结构化过的生产日志中重建意图。本应开发新功能的季度变成了考古季度。

这不是规划错误。而是一个复利错误。为了更快发布而跳过评估的团队,正是那个将花费十二周时间从不完整的追踪(traces)中重建评估基础设施、为二月份所谓的“正确”含义争论不休、并悄悄移除没人能证明依然有效的功能的团队。追赶的成本超过了内置的成本——不是一点点,而是随着每一次未经回归检查就发布的提示词修改而倍增。

人机回环 (HITL) 是一个队列,而队列具有动态特性

· 阅读需 13 分钟
Tian Pan
Software Engineer

团队向 AI 工作流添加人工审批的方式,与他们在代码库中添加 if (isDangerous) requireHumanApproval() 的方式如出一辙:将其视为一个二进制开关,在设计时检查一次,然后便将其遗忘。架构图上的指标是“人工监督”旁边的一个绿色对勾。而真正重要的指标——人工花费了多长时间、他们是否阅读了任何内容、在点击批准时该条目是否仍然相关——却很少有对应的仪表板。

将人工审批者视为二进制开关,你就等于在不知不觉中构建了一个队列。而队列具有动态特性:积压量的增长速度超过了你的员工配置速度、陈旧性使昨天的决定变得毫无意义、疲劳让审查变成了“走过场”(rubber-stamping),以及优先级倒置让真正重要的那一个决策被排在三百个无关紧要的决策之后。架构图中看不到这些,但它们都会出现在事故回顾(incident retro)中。

“LLM 即编译器” 是一个你的代码库无法承受的隐喻

· 阅读需 11 分钟
Tian Pan
Software Engineer

这个说法非常诱人:用英语描述行为,模型生成代码,然后交付。提示词(Prompts)变成了源码,产物变成了目标,而 LLM 就像是一个前端界面更友好的 gcc 坐镇其中。如果这个框架成立,那么软件工程的其余部分——评审、重构、架构——都将成为提示词质量的下游产物。但事实并非如此。那些基于这种假设构建的代码库,正以一种现在看来极其乏味的模式走向失败:在大约第六个月,没人能解释为什么某个特定函数长成那样,而且每一次增量变更都会引发一波重复代码。

编译器隐喻才是根本原因,而不是“氛围感编程”(vibe coding)、模型质量或提示词技巧。这是一个范畴错误,它悄无声息地让团队逃避了那些能保持代码库长年连贯性的工作。当你认为模型是编译器时,生成的代码就变成了实现细节,就像汇编语言是 C 程序的实现细节一样。而当你实际在带领一个由非确定性、上下文受限的协作成员组成的团队时,生成的代码才是资产——而提示词比起源码,更像是 Slack 消息。

LLM-as-Judge 漂移:当你的评估器升级导致所有数据变动时

· 阅读需 14 分钟
Tian Pan
Software Engineer

一个在提示词未发生任何更改的情况下,测试结果由绿转红的回归测试套件,通常源于以下三种情况之一:测试框架损坏、检索库不稳定,或者评估模型(Judge)在周末学习了“新口味”。第三种情况最常见,也最难调试,因为你的代码仓库里没有任何提交记录与之相关。评分模型在静默状态下完成了质量更新,你与上个月仪表盘对比的所有分数,现在都已经是在用另一种“货币”计价了。

这是 LLM-as-judge 令人不安的地方:你有两个处于变动中的模型,而不仅仅是一个。待测模型(Candidate Model)是你发布的产物;评估模型(Judge Model)则是告诉你待测模型表现如何的工具。当两者独立演进时,分数的变化量(Score Deltas)不再具有以往的含义,你家产品经理每天早上刷新的仪表盘正在悄悄地撒谎。

Markdown 优于 JSON:你正在支付却未察觉的输出格式税

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数团队在上线当天开启 JSON 模式,却从未衡量这背后的代价。这种假设合情合理:结构化输出能保证正确性,为什么不选它呢?答案是,严格的 JSON 模式约束解码通常会使数学、符号和多步分析任务的推理准确度降低 5–15%,而由于评估是在开启格式标志之前运行的,或者评估衡量的是可解析性而非质量,因此没人注意到这一点。

输出格式是一种解码时的约束,正如所有约束一样,它会扭曲模型的概率分布。当你查看日志时,这种扭曲是不可见的:JSON 有效,Schema 匹配,字段类型也对得上。你在日志中看不到的是模型本可以用散文形式产出的推理过程,但由于无法塞进你给定的语法中而消失了。“格式税”是真实存在的,在文献中已有详尽记录,但在生产环境中几乎普遍未被衡量。

这篇文章将探讨何时该支付这笔费用,如何在不必支付时及时止损,以及对于既想要结构化输出又想要准确性的工程师来说,格式选择的决策树究竟是什么样的。

飞行中转向:无需重启即可重定向长时运行的智能体

· 阅读需 11 分钟
Tian Pan
Software Engineer

观察一个开发者使用代理型 IDE 二十分钟,你会看到同样的小剧场上演三次。代理开始了一个长任务。在两次工具调用后,用户意识到他们想要一个函数式组件而不是类,或者想要 v2 接口而不是 v1,亦或是想用 Vitest 而不是 Jest 编写测试。他们手中只有一个杠杆:红色的停止按钮。他们按了下去。代理在编辑中途阵亡。他们复制并粘贴上一个提示词,加上修正,然后为前八分钟的工作支付了两次费用。

中止按钮是错误的交互设计。它将“我想调整计划”和“我想丢弃这次运行”视为同一种动作。在实践中,它们就像方向盘和弹射座椅一样迥异,而将两者混为一谈,正是为什么许多代理产品在任务耗时超过一屏输出时就显得脆弱不堪的原因。

评估通过,但工具全是 Mock 的:为什么你的 Agent 最棘手的生产故障从未进入测试框架

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的智能体在评估测试集上达到了 94% 的准确率。然而你的轮值告警却响个不停。房间里没人撒谎,这两个数字都是真实的。实际情况是,测试框架(harness)在测试提示词(prompt),而生产环境在测试智能体(agent),这是两个完全不同的产物,只是恰好共享了权重。

Mock 工具的评估通常是产生这种差距的原因。你用预设的 JSON 存根(stub)了 search_orderscharge_cardsend_email,给模型输入一个用户回合,并对最终响应进行断言。这种运行方式成本低、具有确定性且可复现——这些都是 CI 系统喜欢的特性。但它对工具选择、延迟、速率限制(rate limits)、部分失败和重试行为保持沉默,也就是说,它忽略了那些在事故回顾中占主导地位的失败因素。