跳到主要内容

639 篇博文 含有标签「llm」

查看所有标签

拒绝还是上报:置信度门控 AI 中的双阈值问题

· 阅读需 14 分钟
Tian Pan
Software Engineer

大多数生产环境中的 AI 功能在发布时只带有一个置信度阈值。在阈值之上,模型给出回答;在阈值之下,用户会得到一句生硬的“我不确定”。这个单一的数值同时承担着两个完全不同的任务,这就是为什么即便你对已回答查询的准确率看起来不错,但信任度指标却已经连续两个季度下滑的原因。

正确的设计至少应该有两个切分点。一个“弃权”(abstain)阈值设在低位:低于该值时,模型拒绝回答,因为此时保持沉默比给出任何答案都更有价值。一个“升级”(escalate)阈值设在中间:在两个切分点之间,系统将案例交给人工审核员,而不是直接将其丢弃。将它们合并成一个刻度盘,你发布的产品在出错时和不确定时会让人感到同样无用——在用户只需打开另一个标签页就能找到免费替代品的市场中,这是最糟糕的处境。

这并不是什么新鲜想法。拒绝选项分类器(reject-option classifier)的文献自 20 世纪 70 年代以来就一直在主张拆分阈值,将“歧义”拒绝(输入介于已知类别之间)与“距离”拒绝(输入远离任何训练数据)区分开来。生产环境中的 AI 团队总是在以惨痛的方式重新学习这一教训,通常是在首次发布大约六个月后,当支持队列中挤满了询问“这玩意儿是坏了还是怎么了”的人时。

供应商可迁移性税:为什么“我们可以更换模型”是每季度的成本项,而非一个勾选项

· 阅读需 12 分钟
Tian Pan
Software Engineer

在过去六个月中,我审计过的每一个团队都声称自己是供应商无关的。但实际上没有一个团队能做到。在评估套件中得分最高的系统提示词之所以表现出色,是因为它倾向于单一供应商的分词器行为、JSON 模式协议、拒绝节奏以及停止序列处理 —— 而编写该提示词的团队甚至无法说出其中哪些偏差在起作用。当 CFO 询问为什么采购清单上更便宜的模型不能直接替换时,诚实的回答是:这需要两个工程师季度的提示词重新调优以及对每个评估进行完全的基准重设。这不仅仅是一个勾选项。它是一个季度的成本项。

一直困扰团队的心智模型是将供应商可移植性视为一次性的架构决策。你添加了一个抽象层,在配置中写了一个 model: 字段,以此自我庆贺,然后继续前进。一年后,当供应商提价、发布弃用通知,或者在你关注的某个类别上出现了一周糟糕的拒绝服务时,你发现那个抽象层只是一个围绕特定模型提示词的薄包装。你买到的可移植性是语法上的。而你真正需要的是行为上的可移植性,且行为上的可移植性在你停止支付的那一刻就会衰减。

你的模型更新是一次破坏性变更:你欠集成商的“行为变更日志”

· 阅读需 14 分钟
Tian Pan
Software Engineer

某家厂商在周二下午向模型别名推送了一个“小幅更新”。到了周四,四家客户公司正在进行事件响应。他们本周都没有部署代码。他们的仪表板上没有任何关于延迟、错误率或任何其他基础设施维度的指标退化。改变的是,在他们固定的别名背后的模型开始返回略有不同的句子、略有不同的 JSON 以及略有不同的拒绝——而他们的团队针对旧行为编写的每一个提示词(Prompt)现在都成了一份没人履行的合约。

这种不对称性就是问题的核心。供应商将这次发布视为一次部署:经过内部测试,通过了一些聚合评估,并在维护窗口内逐步推向 100%。而消费端将其视为一次语义化版本(semver)违规:一个依赖项在生产环境中自动升级,却没有更改其版本字符串,随后最终用户的错误报告接踵而至,主题还带着轻快的“我们这边什么都没改”。

推理预算委员会:Token 支出突破七位数时的治理之道

· 阅读需 13 分钟
Tian Pan
Software Engineer

在每月 50,000 美元的水平时,你基础设施账单上的“计算 + Token”这一项只是可以忽略不计的零头。但当每月达到 5,000,000 美元时,它就是一个 CFO 级别的问题。这两个阶段之间的转变并不是渐进的——它是组织讨论模型支出方式的一种“相变”,而大多数工程组织对于随之而来的社会和政治工作都准备不足。账单依然是那简单的一行;但围绕它的对话却不再简单。

改变的是谁有资格问“为什么”。当三个产品团队共享一个 API Key 和一个预留容量时,每一个配额争论的结构都是相同的:某人正以牺牲他人的利益为代价获胜,而没有中立方来主持公道。当一个团队的发布第一次因为另一个团队上线了一个“话痨”智能体(agent)而受到限制时,整个工程组织会立刻感受到治理机构缺失带来的痛苦。在压力之下召开会议并凭空发明流程,是设计流程最糟糕的时机。

Prompt 迭代中的“局部最大值”陷阱:如何判断你调错了地方

· 阅读需 12 分钟
Tian Pan
Software Engineer

在一个严肃的 LLM 项目进行到第六周时,总会有那么一个时刻,Prompt 迭代日志开始变得像一本心理治疗日志。每一次微调都是在用一种失败模式交换另一种。增加一个更严格的 “do not”(不要)条款,模型在以前能处理的情况下就开始回避。放软语气,另一类幻觉又回来了。评测得分板在三四个点的范围内徘徊,拒绝突破。有人说,“让我再试一次重新排序,”于是又是半天时间烟消云散。

这就是局部最优陷阱(local-maximum trap)。团队正在爬山,但这山头已经到顶了。残忍的是,这座山是真的——每一次 Prompt 的改动确实会在某些案例子集上产生可衡量的变化,而这正是让每个人持续微调的信号。被忽略的是:上方的天花板根本不是 Prompt 的天花板。

主权崩塌:记录你的 Prompt 究竟去了哪里

· 阅读需 11 分钟
Tian Pan
Software Engineer

监管机构问了一个简单的问题:“对于上周二 UTC 时间 14:32 提交的这个特定用户 Prompt,请证明该请求及其派生状态经过了哪些管辖区。”

你的应用日志显示 model=claude-sonnet-4-5, region=eu-west-1, latency=2.1s。你的网关日志也显示同样的内容。供应商的发票确认了请求确实发生了。但这些都无法回答上述问题。该请求进入了一个由欧盟托管的网关,被转发到美国区域的主端点,但在一次区域性故障期间故障转移到了新加坡,并预热了一个第三方 GPU 池上的 KV 缓存,而该 GPU 池的数据驻留声明仅存在于供应商的脚注中。你所需要的审计追踪存在于一个你的团队并不掌握的层级中。

这就是主权崩溃:即你的合同中关于数据位置的承诺与你的运行时在事后能实际证明的情况之间的差距。合规主张的强度取决于链路中最薄弱的那行日志。

RAG 流水线中被你忽略的查询重写层

· 阅读需 12 分钟
Tian Pan
Software Engineer

当 RAG 系统回答错误时,大多数团队的第一反应是归咎于编码器(encoder)。更换更大的嵌入模型(embedding model)。尝试针对特定领域微调过的模型。增加维度。三个迭代周期(sprint)后,召回率曲线只提升了几个百分点,而用户的投诉看起来还是老样子。

诊断错了。大多数检索失败并非嵌入失败。它们是查询形状(query-shape)失败——在编码器运行之前就存在的词汇不匹配,无论如何调整向量都无法修复。

用户输入“如何取消”。相关的文档标题却是“订阅生命周期管理”,并使用了“终止”、“计费周期结束”和“服务停用”等词汇。世界上没有任何编码器能靠词汇运气将这两个字符串拉入同一个邻域。余弦相似度(cosine similarity)的差距是真实存在的,它存在于输入中,而非模型中。位于检索之前的查询重写层是大多数流水线跳过的步骤,随后他们却要花一个季度的时间试图在下游进行补偿。

Agent 的链路追踪采样:每日千万级 Span 中哪些值得保留

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个 Web 服务请求在繁忙时段产生 5 个 Span。而一个现代的 Agent 会话产生 50 个,如果 Planner 决定递归,有时甚至会产生 1000 个。你们平台团队从微服务时代复制粘贴过来的 1% 均匀采样器,从定义上就会丢弃你真正关心的稀有故障——因为故障是稀有的,而均匀采样对稀有性没有任何判断力。

“我们对 Agent 拥有完全的可观测性”的真实版本听起来与营销版本不同。它听起来应该是:我们保留重要的 Trace,丢弃不重要的,并且我们预先知道哪些是哪些。这句话中的每一个词都至关重要,而那些在账单寄来之前一直忽视采样设计的平台团队,现在正被迫反向学习这一学科——在成本压力下,以及在经历了一个季度的故障之后,这些故障本应“在数据中”,但在有人查看之前就被剔除了。

智能体无法察觉的死锁:生成计划中的循环工具依赖

· 阅读需 13 分钟
Tian Pan
Software Engineer

一个规划器智能体输出了七个步骤。每一个看起来都很合理。编排器分发了这些步骤,前三个返回了值,第四个在等待第五个,第五个在等待第七个,而第七个——埋藏在规划器散文般描述的第三行里——正静静地等待着第四个。没有任何东西被锁定。没有触发过任何 EDEADLK。智能体消耗了 40,000 个 token 来推理为什么第四步“花费的时间比预期长”,最终以一个温和、合理的道歉向用户宣告放弃。

这就是你的智能体无法察觉的死锁。它不是操作系统课程中的那种经典死锁——这里没有互斥锁(mutex),没有内核可以内省的资源图,也没有你的技术栈中任何人能识别的持有者或等待者。依赖关系存在于规划器生成的英语句子中,循环形成于潜在语义而非任何数据结构中,而故障模式看起来与“模型正在努力思考”无异。经典的死锁检测在这里毫无用处,但代价是相同的:工作流停滞,token 蒸发,而你的 trace 什么也不会告诉你。

冷启动评估:如何在零生产环境追踪的情况下发布 AI 功能

· 阅读需 11 分钟
Tian Pan
Software Engineer

每个 AI 功能上线前都有一个同样的静默时刻:在第一个用户看到它之前,团队中的某个人会问“我们怎么知道这个东西好不好?”,而诚实的回答是“我们现在还不知道”。你没有追踪记录 (traces),因为你还没有用户。你没有用户,因为你还没有发布。这是一个真实的死循环,而它产生的两种失败模式都是致命的——要么盲目发布,让第一周的线上问题 (escalations) 成为你的评估数据集;要么等待“真实数据”,眼睁睁地看着产品路线图推迟一个季度,而竞争对手却发布了演示视频。

摆脱困境的方法不是假装冷启动评估与发布后的评估是同一个问题(只是样本量较小)。事实并非如此。你不是在对分布进行采样,而是在构建先验 (prior)。上线首日的每一个信号都是你所做选择的产物——关于衡量什么、模拟谁的行为以及关注哪些失败的选择。能够出色发布 AI 功能的团队会将发布前的评估栈 (eval stack) 视为一等交付物——它不是在准入审查前一晚匆忙拼凑的电子表格,而是一个由内部试用 (dogfooding)、模拟、专家标注和对抗性探测 (adversarial probes) 组成的层级化系统,每一层都提供不同类型的信号,并伴随着关于它能告诉你什么以及不能告诉你什么的明确说明。

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

· 阅读需 12 分钟
Tian Pan
Software Engineer

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

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

按功能计费,而非按 Token 计费:AI 预算分配中的缺口

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的财务团队可以准确告诉你,上个月你在 Anthropic 和 OpenAI 上花了多少钱。你的产品团队可以告诉你,哪些功能的用户点击量最高。但公司里没人能告诉你 Draft-Email 是否盈利,Summarize-Thread 是否应该保留在免费层级,或者新的 Rewrite-Tone 功能是否在单用户成本上蚕食了 Draft-Email 的利润。你拥有两个声称追踪同一笔支出的仪表盘,但它们都无法回答那个真正驱动产品决策的问题。

这就是分配缺口。你按端点(endpoint)测量 Token 支出,因为这是供应商 API 提供的数据。但 /chat 端点服务于 12 个刚好共享同一个提示词模板的功能,“按端点”统计将这 12 个功能全部合并到了同一个细目中。在有人完成将 Token 成本导回至产生成本的功能这一底层工作之前,定价层级、功能权限管理、弃用决策以及“我们要不要发布这个功能?”的讨论,全都只能靠直觉。

这项底层工作并不光鲜。它是请求级标记(request-level tagging)、追踪与遥测数据的关联(trace-to-telemetry joins),以及一种坚决的态度:如果不带成本标签,就不发布任何 AI 功能。将此视为基础设施投资的团队,最终会获得按用户群细分的单功能利润报告。而将其推迟到下季度的团队,最终在 18 个月里只能凭感觉做定价决策,并在事后发现,某个单一客户群在负利润的情况下消耗了一半的推理账单。