跳到主要内容

702 篇博文 含有标签「llm」

查看所有标签

止于供应商边界的链路追踪

· 阅读需 12 分钟
Tian Pan
Software Engineer

你做了追踪(tracing)工作。检索有 span。工具调用有 span。编排循环有 span。Trace ID 贯穿每一个内部跳跃,记录在 W3C 的 traceparent 请求头中,正如 SRE 手册所说。然后请求到达 messages.create,SDK 记录了一个名为 llm.call 的单一 span,接着你流水线中接下来的 2.8 秒在火焰图上变成了一个没有任何内部结构的黑色矩形。首个 token 出现前的 800 毫秒:不透明。之后的 2 秒解码过程:不透明。你的追踪无法得知网络、队列等待、Prefill 或单 token 解码在总耗时中所占的比例。

当客户报告“今天助手感觉很慢”时,你的仪表板可以证实这种缓慢,但无法定位它。你流水线中最昂贵的一分钟——以美元、p95 以及用户感知的延迟来衡量——发生在供应商的数据中心内部,而你签约时接受的合同几乎没有给你任何可见性。你正在为一个黑盒值班(on call)。

不属于你的那次变慢:对话中途的 KV 缓存逐出

· 阅读需 11 分钟
Tian Pan
Software Engineer

一段对话在同一个 Claude 会话里跑了四十分钟。十一轮回合,每轮平均首字延迟(TTFT)800ms,每轮都很便宜——因为那段 28,000 token 的前缀命中了提示词缓存。第十二轮到来,TTFT 飙到 3.4 秒。对话的形态没变,模型没切换,网络也正常。缓存输入 token 从 27,800 掉到 0。下一轮的 prefill 账单从第一个 token 起就全额计费。

你去追踪里找原因,没有任何一条日志写着"另一个租户的突发流量把你逐出了缓存"。对这次毛刺最诚实的解读是:在同一片 GPU 池的某处,另一个客户的 prompt 让调度器认为,丢掉你这段温热的前缀是代价最小的选择。你无法重放这一轮,无法证明那次逐出。那一刻的缓存状态是陌生人流量的函数,而那些流量不在你的追踪里,因为它们本来就不属于你。

无法说出"等一下"的智能体

· 阅读需 10 分钟
Tian Pan
Software Engineer

随便挑一个过去两年里搭建的生产级智能体,清点它在某一轮里实际能做的事。清单很短:发起一次工具调用、给出最终答案,或向用户提一个澄清问题。这就是整个动作词汇表。注意一下缺了什么。没有动词表达"我想在决定之前多花点时间";没有动词表达"我足够不确定,所以希望暂停并重新考虑,而不立刻承诺";没有动词表达"我想在采取任何行动之前先在这件事上多停留一会儿"。智能体在字面意义上无法说出"等一下"。语法里压根没有这个词。

这并不是表面打磨的问题,而是结构性的问题。一旦智能体的全部输出都是动作,任何内部状态都必须经由一个动作来表达。犹豫变成多余的工具调用,怀疑变成自信的承诺。只设计了动作动词的团队,实际上发布了一个唯一的语言就是"做事"的智能体——然后又奇怪它怎么从来不像在思考。

把每个工具都当作 O(1) 的规划器

· 阅读需 9 分钟
Tian Pan
Software Engineer

你的规划器输出了五次工具调用。从纸面上看,这是一个干净的解决方案:lookup_usersearch_documentscall_external_apispawn_sub_agentrequest_human_approval。轨迹优雅、逻辑自洽,智能体最终也会给出正确答案。可在生产环境中,这五个步骤分别耗时 12 毫秒、800 毫秒、4 秒、2 分钟和 6 小时。规划器从未察觉,它这五步计划在成本上跨越了九个数量级。

![](https://opengraph-image.blockeden.xyz/api/og-tianpan-co?title=%E6%8A%8A%E6%AF%8F%E4%B8%AA%E5%B7%A5%E5%85%B7%E9%83%BD%E5%BD%93%E4%BD%9C%20O(1%29%20%E7%9A%84%E8%A7%84%E5%88%92%E5%99%A8)

这并不是幻觉。模型选对了工具,顺序也合理。它做不到的——工具模式根本没给它做这件事的途径——是去推理:计划的最后一步在性质上和第一步完全不同。在规划器眼里,工具就是工具,计划图中每个节点的权重都是 1。

无法收敛的验证器循环

· 阅读需 12 分钟
Tian Pan
Software Engineer

代理系统里最贵的 bug 是那种没有任何报错的 bug。Worker 提出一个草稿。Verifier 用一段反馈把它驳回。Worker 修改。Verifier 再次驳回。循环一直转下去,trace 越来越长,账单越爬越高,而从外面看,这个系统似乎在 工作——而且很尽职,因为两个模型都在干各自该干的活儿。没有人定价进去的是:验证器的接受标准在不同调用之间并不固定。worker 在追的那个目标本身在动,而循环没有任何收敛保证。

你以为自己交付的是"迭代到满意为止",其实你交付的是一次对极值可能根本不存在的空间的搜索。

长出胳膊和腿的缓存提示词前缀

· 阅读需 11 分钟
Tian Pan
Software Engineer

六个月前,你的提示词前缀是 4,000 tokens。它稳定、缓存预热,几乎可以摊销到不计成本——系统指令的每次调用附加费,相比每次响应的成本,只是一个舍入误差。今天那个前缀变成了 11,000 tokens,你的缓存命中率从 92% 滑到了 31%,你的推理账单上升了 4 倍。团队里没有人能指出是哪个 PR 干的。没有一条 commit message 写着"将提示词增加 7,000 tokens"。每一次修改都很小,每一次修改都有理有据,每一次修改都干干净净地合入了。

提示词前缀长出胳膊和腿,就像地下室积攒纸箱一样。一个团队需要注入用户的订阅等级,这样 agent 才能解释套餐限制。另一个团队需要用户时区的今天日期,这样"明天提醒我"才能工作。第三个团队把当前 A/B 变体名硬塞进去,这样 eval traces 才能切片。市场团队加进了当前促销 banner,这样 agent 才能适时提及它。合规团队加进了功能标志清单,这样模型才能拒绝那些不在灰度名单里的用户访问 beta 功能。每一条都是一行的添加。每一条单独看都站得住脚。但加起来摧毁了你的缓存。

那位通过反复试验摸清你 Prompt 的高级用户

· 阅读需 10 分钟
Tian Pan
Software Engineer

此刻,你的产品里有一位用户,体验远好于中位数用户。不是因为他付得更多,不是因为他在不同的订阅层,也不是因为他被分到了某个特殊的实验组。他通过耐心地试探,发现这个 AI 功能只要换种方式提问,就会给出漂亮的回答。他知道哪些动词能触发结构化输出。他知道一个词的追问会得到精简版本,一整句话则会得到展开版本。他知道某些话题如果不包装成假设性问题,助手就会戒备起来。这一切,你的网站上没有一处写过。他是逆向工程出来的。

有意思的不是这种用户存在,而是这种用户现在成了你的产品文档。你的 AI 功能跟用户之间签了一份契约——一份没公开的契约,完全编码在系统 prompt 里——而了解这份契约的唯一方式就是反复试验。只有一小部分用户有耐心去做这些试验。其余人拿到的是一个更糟糕的产品。

你的 Agent 读不懂的生产日志

· 阅读需 10 分钟
Tian Pan
Software Engineer

你把事故响应 agent 接入了 Splunk。你在系统提示里给了它查询语法,给了它执行 SPL 的工具,还有一个新鲜的 API token。第一次真正处理告警时,它拉了错的日志,总结了错的服务,信誓旦旦地报了错的客户。集成做得完美无缺,agent 却一文不值。

你忘了什么。十五年的日志惯例、没文档的字段名、跨越三次重组从 ERR 漂移到 error 再到 ERROR 的告警级别字符串、把 customer_id 在认证服务里变成 cust_id_v2_actual、在计费服务里变成 tenant.user.id 的团队特定后缀——这些东西没有一条出现在 prompt 里。你给了 agent 对 API 的访问权,但你没有给它访问那些让 API 变得有用的机构知识的权力。

这种失败的形状比 Splunk 大得多。任何把查询语言暴露给 agent、而底层语料是团队手工塑造了十年的工具,都会撞上这堵墙。Agent 拿到了动词,没拿到名词。

当评估指标全看“感觉”时,你的 A/B 测试无法区分两个模型

· 阅读需 9 分钟
Tian Pan
Software Engineer

你在实验中上线了一个模型替换。两周过去了,控制面板只变动了 0.1%,读数显示“无显著差异”。你得出结论,新模型与旧模型基本相同,然后继续进行其他工作。

它们并不相同。你的指标从未敏感到足以区分它们。

提示词 Diff 隐藏了自身的爆炸半径

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个 PR(合并请求)进入了你的评审队列。Diff 显示系统提示词(system prompt)中修改了三个词:Output strictly valid JSON 变成了 Always respond using clean, parseable JSON。这看起来就像是一次文案润色。你快速浏览了一下,CI 检查勾标是绿色的,于是你点击了批准。总耗时:90 秒。

六个小时后,下游解析器开始拒绝带有尾随逗号和缺失字段的响应。结构化输出的错误率从接近零飙升至两位数,一个创收工作流陷入停滞。Diff 中没有任何迹象预示到这一点。Diff 中也不 可能 预示到这一点,因为 Diff 衡量的是错误的东西。

这就是评审提示词变更的核心问题:提示词 Diff 的大小完全无法说明其影响范围的大小。三五个词的修改与三段话的重写都只是文本,而文本 Diff 以相同的视觉权重呈现它们,就像对待任何其他编辑一样。但提示词并不是 描述 行为的文本 —— 它是 导致 行为的文本,而一次编辑所产生的因果爆炸半径在你评审的产物中是不可见的。

用户终将学会忽略的置信度评分

· 阅读需 12 分钟
Tian Pan
Software Engineer

你想要表现得诚实。你在智能体给出的每个答案旁边都标上了一个小小的 92%。当智能体第三次以 92% 的置信度给出错误答案后,你的用户就不再看那个数字了。他们并没有因此生气。他们只是学会了——就像人类在面对失灵信号时总会学到的那样——仪表盘上的指针并没有连接到引擎。数字还在那里。生成它需要消耗你的 token。但它不再能为任何人的决策提供参考。

这种失败模式是置信度校准(calibration)UX 研究不断重现的:呈现概率是一种信任承诺,而且这种承诺是单向的。一旦数字在用户的使用体验中被证明与正确性无关,这个分数就失去了意义——你为了展示它而投入的信任也随之崩塌。你无法在事后通过修正数字来挽回局面。这个数字现在只是个装饰品。

填充式工具调用:当智能体在表演勤奋而不是真正干活

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开任何一个生产环境智能体的 trace,看一看在用户提问和第一个真正有用的动作之间到底跑了哪些工具调用。你会看到一个 get_user_profile 返回了一个根本没人用的名字、一个 check_status 返回绿色然后再也没被引用、一个 list_recent_orders 的结果被总结成"ok"然后直接丢掉。这些调用没有一个改变了最终答案。但每一个都花了真金白银的钱、真实的延迟,以及在 trace 里真实占一行。你的智能体已经学会了表演勤奋——而表演勤奋如今是你最大的单一浪费来源。

这就是填充式工具调用:智能体发出一个动作,不是因为它需要那个结果,而是因为"先想一想,再行动"的整体模式在训练时被反复奖励,多到模型现在会把"显得周全"当作回答任何问题的副作用来执行。这是一个 LLM 版本的初级分析师,故意打开五个根本不会读的标签页,好让坐在对面的高级同事看到自己很忙。区别只在于:初级分析师会累。智能体永远不会。