这周的工作日志其实挺有意思的。周一还在调一个 agent 的上下文压缩策略,周二被 token 刷新问题折腾到半夜,周三又在评估新模型要不要上生产,周五复盘时发现这一周刚好能串成一句话:更少 token、更强模型、更难管的 agent。这句话不只是本周的观感,也是最近做 AI 工程最真实的体感。
很多刚接触 AI 工程的朋友会以为,模型变强了,工程会变简单。实际恰好相反。模型能力越强,token 成本越敏感,agent 的自主行为越难约束,工程复杂度反而在往上走。这篇文章就把我这周的实操、踩坑和思考完整记录下来,不绕弯子,全是能直接用上的东西。
1. 更少 token:AI 工程的第一道成本题
1.1 token 用量为什么是工程第一痛点
先给不了解背景的朋友补个基础。现在的大模型基本按 token 计费,一个 token 大约是 0.75 个英文单词,中文因为分词方式不同,一个汉字大概对应 1 到 2 个 token。听起来简单,但工程上一算就知道吓人:一个 2000 字的业务文档,光输入就要吃掉三千多 token;如果系统里有 50 份这样的文档,单次请求的输入就可能超过 15 万 token。
更要命的是,token 不只是“花钱”的问题,它直接拖慢响应速度。我在一个 RAG 问答服务里做过压测,上下文从 2 万 token 涨到 8 万 token,首字延迟从 900 毫秒涨到 3.2 秒,翻了三倍多。原因是模型在生成每个新 token 时都要重新处理一遍已有的上下文,长上下文的注意力计算是二次复杂度,数据一多,显存和算力都扛不住。所以“更少 token”不是抠门,是既省钱又保体验的硬指标。
1.2 三条省钱路线:上下文压缩、缓存复用、提示词瘦身
先说上下文压缩。我现在的做法是给长文档建立“摘要层”,文档进来之后先用一个轻量模型抽取关键信息,生成结构化的摘要,真正问答时只把摘要加上相关片段送进大模型。这周测试了一个合同审查场景,原始合同 5 万字,压缩后有效上下文只有 9000 token,回答准确率从 82% 提到 91%。为什么准确率反而提升了?因为无关信息少了,模型注意力更集中,被干扰的概率下降。
第二招是缓存复用。很多服务都是同一套系统提示词加少量动态内容,比如客服机器人,只有用户问题在变。这种情况下可以用前缀缓存,把不变的部分缓存起来,重复请求时直接复用已计算的 KV 状态。实测下来,缓存命中时单次请求的 token 成本能降 50% 到 70%,前提是缓存前缀足够长且顺序稳定。这里有个细节:动态内容要放在缓存区之后,不要穿插在固定提示词中间,否则每次都会触发整段重新计算。
第三招是提示词瘦身。我见过很多人的 prompt 恨不得把所有规则、示例、历史对话全塞进去,结果模型反而抓不住重点。合理的做法是:只保留当前步骤真正需要的约束,示例控制在 2 到 3 个,并且每个示例都明确标注“这是正确做法”和“这是错误做法”的对比。这周我重构了一个审批 agent 的 prompt,从 3200 token 压到 1100 token,工具调用准确率从 89% 升到 94%。删掉的不只是字数,是模型需要“消化”的噪音。
1.3 滑动窗口与上下文水位管理
这周做一个流式日志分析 agent 时,我借用了滑动窗口滤波的思路来管理上下文。滑动窗口滤波是信号处理里的经典方法,只保留最近一段时间的采样数据,平滑噪声同时跟踪趋势。把同样的逻辑搬到 LLM 上下文里:不是所有历史消息都该永远留在上下文里,而是维护一个小窗口,里面放最近几轮对话和当前任务状态,更早的内容要么丢弃,要么压缩成摘要。
实际配置参数可以这样理解:窗口大小决定了“记忆”长度,摘要间隔决定了压缩频率。我做的是 6 轮对话窗口,每 10 轮触发一次摘要合并,把早期对话提炼成一句状态描述。效果很明显,一个连续会话的 token 消耗从线性增长变成平台型曲线,并且模型在长会话末尾的“失忆”问题也缓解了——因为它只关注当前窗口,不会被几个回合前的细节带偏。
2. 更强模型:能力红利与工程反噬
2.1 新模型带来红利,也带来新的麻烦
这周我评估了两款新模型,一个在函数调用上进步明显,一个在长上下文理解上很强。表面看都是利好,但一到工程侧问题就来了:行为漂移。原先针对旧模型精心设计的提示词,换到新模型上反而可能触发多余动作。典型例子是,旧模型按格式返回 JSON 很稳定,新模型可能“太聪明”,自己加字段、改枚举值,导致下游解析崩掉。
所以我的原则是:模型升级不是改个 base_url 就完事,必须配套做回归验证。这周三我搭了一个最小验证集,里面包含 30 个典型请求,覆盖工具调用、格式要求、拒绝回答三类场景,每次切换模型先跑一遍,对比输出结构完整率和语义正确率。没有这个验证集,直接上生产大概率会翻车。
2.2 模型选型:别只看榜单,要看交互模式
很多人选模型喜欢看排行榜,但工程场景里榜单分数参考价值有限。榜单测的是静态问答能力,而生产系统看重的是指令遵循稳定性、上下文窗口利用率、工具调用格式一致性。比如 embedding 模型的选择,排行榜上第一名的模型不一定适合你的检索场景,因为检索质量取决于向量空间与你的文档分布是否匹配。我做过一个测试,同一批技术文档在两个 embedding 模型上召回率差了 8 个百分点,而这两者在榜单上只差 0.2 分。
我的选型维度大致是这样:
| 维度 | 具体关注点 | 我常用的验证方法 |
|---|---|---|
| 工具调用稳定度 | 是否严格按 schema 返回参数 | 连续调用 50 次统计失败率 |
| 上下文利用率 | 长文档中关键信息是否被正确引用 | 构造 10 万字文档做定点查找 |
| 格式一致性 | 输出是否始终是合法 JSON | 批量跑 100 次解析错误率 |
| 延迟与成本 | p95 延迟、单次请求成本 | 用真实流量回放压测 |
这里有个容易忽略的点:长上下文模型不等于“可以随便往里塞东西”。即便模型支持 200k 上下文,超过一定量后注意力会大幅退化,这在 Transformer 架构里是天然存在的现象。所以就算换了强模型,我仍然坚持 1.2 里的上下文管理策略,只是把“窗口”上限调大了,而不是取消窗口。
2.3 提示词工程与上下文工程的边界
最近圈子里的讨论从“提示词工程”转向“上下文工程”,我完全认同。提示词的职责是告诉模型“怎么答”,上下文工程的职责是决定模型“看到什么”。后者对结果的影响往往更大。拿这周做的专利检索辅助小工具来说,初始版本的提示词写得很详细,但效果一直不稳定。后来我把精力放到上下文构建上:先做文档切分,再按检索相关度排序,最后只把 top 5 段落拼进上下文。提示词反而退回简单水平——“基于以下材料回答”。结果准确率从 76% 升到 88%。
这个例子说明,强模型时代提示词的作用在下降,工程重点已经转移到数据的组织方式上。谁的上下文更干净、更相关、更紧凑,谁的效果就好。这也是“更少 token”和“更强模型”两个趋势的交汇点:模型越强,越不需要在 prompt 里啰嗦,把位置让给真正相关的数据就够了。
3. 更难管的 agent:不确定性的治理实验
3.1 agent 为什么到了生产环境就变“刺头”
这周遇到最多的报错来自 agent 执行链路。单独调一个 agent 的某个功能是好的,但把它放进生产流程后,各种意外层出不穷:工具返回了预期外的格式,agent 没有按计划调用下一步,甚至出现循环重试同一个工具直到超时。我在排查一个“agent execution terminated due to error”问题时发现,根因是 agent 在一个外部 API 返回 500 错误后没有退避,而是疯狂重试——这不是 bug,是 agent 自主决策带来的不稳定性。
业内常说 agent 是最难做的工程问题,这话不夸张。普通 API 调用是确定性行为:输入相同、输出必相同。agent 图例的每一步都可能调用不同模型、不同工具,输出分布天然发散。这意味着不能用传统软件工程的“单元测试”思路来保证正确性,只能靠约束、监控和容错机制把它圈在安全边界内。
3.2 受控的 agent:框架选型与工程护栏
做 agent 工程时,我最看重的是“可终止性”和“可控性”,这比模型聪明更重要。框架选择上,我倾向编排式而非完全自主式:编排式把任务流程拆成固定节点,每个节点允许 agent 在限定范围内做决策;完全自主式则把全部决策权交给 agent,看起来美好,但生产失控概率极高。
实操中我给每个 agent 都套上了五层护栏:
- 最大步数限制:比如一个任务最多执行 20 步,超过即终止并报错。
- 工具权限白名单:只暴露这个任务真正需要的工具,避免 agent 链式调用其他外部服务。
- 预算上限:累计消耗 token 超过设定值就强制停止,防止死循环烧钱。
- 关键操作确认:涉及删除、写库、发送消息等操作,必须先返回用户确认。
- 超时熔断:单次工具调用超过 30 秒就标记失败,并切换备用路径。
这里有个教训:护栏不是越多越好。这周二我一度加了 8 层限制,结果 agent 频繁触发防御逻辑,任务完成率反而下降。后来我减到 5 层,把相互冲突的规则合并,完成率从 67% 回升到 86%。护栏的目标是框住风险,而不是把 agent 绑死。
3.3 让 agent 扛住并发:隔离、限流与幂等
关于“AI agent 怎么扛并发”,是我这周被问得最多的问题。先说结论:agent 不适合用传统 API 那种彻底并行的方式直接怼,因为它的链路复杂、耗时长、资源占用不可预测。我给生产环境设计的方案是任务队列加并发池:请求先进入队列,由 worker 逐个领取执行,并发池大小根据底层模型 QPS 和工具服务容量动态调整。
并发池的大小的选择,参考底层 API 的速率限制。如果模型接口限制是每分钟 600 次调用,而单个 agent 任务平均要调 3 次模型接口,那并发池调到 60 就超过限制。实际我把并发设成 40,留出 30% 余量给重试和突发。这个数字不是拍脑袋,是拿真实流量压测出来的:并发 40 时平均任务耗时 28 秒,并发 80 时升到 51 秒,且错误率暴增,因为底层 API 开始限流。
另一个必须处理的点是幂等。agent 因为超时重试时,如果工具调用不是幂等的,就会出现重复扣款、重复建单。这周我踩了一个大坑:某个支付回调工具不支持幂等,agent 超时后自动重试,结果用户被扣了两笔钱。修复方案是给每个工具调用加上全局唯一的请求 ID,服务端记录已处理 ID,重复请求直接返回“已处理”状态。这个机制应该作为 agent 工具层的基本要求,而不是事后补。
3.4 agent 的可观测性:没有 trace 就没有治理
这周排查 agent 问题时,最痛的一点是可观测性不足。传统后端的日志是一条直线的请求链路,agent 的执行过程则是一棵分支树:一次任务会派生出多个子任务、多次工具调用,还可能自我纠错重来。用普通日志根本拼不出完整执行过程。
我现在强制要求所有 agent 动作都打结构化 trace,记录关键字段:任务 ID、当前步骤、目标、输入摘要、调用的工具、返回结果摘要、耗时、token 消耗。集中在类似 trace 面板里查看。这周在定位一个“为什么 agent 总在处理无关信息”的问题时,就是靠 trace 发现它被一个残留的会话上下文干扰,清理后立刻恢复正常。
agent 的可观测性还包含成本审计。每个任务的 token 消耗都要能追溯到具体步骤,否则月末账单出来都不知道钱花哪了。我习惯在 trace 里加一个“cost”字段,由底层调用统一记录,这样能做按任务、按用户、按维度的成本报表。
4. 认证令牌:另一条 token 战线上的坑
4.1 从 LLM token 到认证令牌的思维切换
标题里的“更少 token”,在工程里其实有两条线。一条是上文讲的模型 token 消耗,另一条是系统认证里的 token 治理。这周我被后者折腾得不轻:登录接口报sign-in could not be completed token exchange failed,刷新 token 又返回401,排查到最后发现是 refresh token 生命周期策略和网关配置不一致。
很多 AI 工程师会把认证 token 当成“登录的小事”忽略,但它恰恰是 agent 系统里最致命的一环。agent 要调外部服务、要跨系统交换身份,每一步都在使用 token。一旦 token 提前失效或交换失败,整个链路直接瘫痪,而且错误信息往往只在日志深处出现。
4.2 JWT 失效与续签的工程方案
给不熟悉的朋友简述一下 JWT 的经典模型:登录成功后,服务端签发一个 access token 和一个 refresh token。前者短期有效,用于访问资源;后者长期有效,用于在 access token 过期后换取新的。流程图不做,直接说关键参数。
我目前的生产配置是:access token 有效期 30 分钟,refresh token 有效期 7 天,refresh token 每次使用后轮换,并且只允许使用一次。轮换意味着每次刷新时旧 refresh token 立即作废,签发新的,这样即使某个 refresh token 泄露,它也只能用一次,风险窗口大幅缩小。
这里必须特别注意一个细节:refresh token 轮换后,如果客户端并发请求同时用同一个 refresh token 去刷新,会产生竞态条件——一个请求成功了,另一个请求拿着已经失效的旧 token 会失败。解决方式是在服务端做 token 家族管理:记录同一家族的 refresh token,只要家族内有任意一个 token 被使用且通过了校验,就视为合法,并签发新 token 给当前请求,同时作废整个家族的旧 token。
4.3 本周实际的 token 报错排查
周报里已经有一张表了,我直接复述下表里的高频现象:
| 报错现象 | 常见根因 | 修复方向 |
|---|---|---|
| token exchange failed | 授权码/refresh token 不匹配或已过期 | 检查 token 版本与过期时间,统一时钟源 |
| refresh_token 为空字符串 | 前端或 SDK 未正确存储 refresh token | 核对存储方案,确认是否存在跨域丢 Cookie |
| 403 forbidden | 网关地域策略或来源白名单限制 | 检查网关层 ACL 配置 |
| invalid refresh token | 轮换后使用了旧 token | 前端必须用最新 refresh token,避免并发刷新 |
那个刷新 token 返回空字符串的错误,根因很滑稽:sdk 在刷新前用localStorage.getItem('refreshToken'),而刷新过期后 token 被清理,拿到的是空串,SDK 又没做空值校验就直接发请求。修复只需要加一个前置判断:拿不到 refresh token 时,直接引导用户重新登录,而不是发一个注定失败的请求。
5. 本周问题排查谢实:报错背后的工程真相
5.1 案例一:token 刷新失败导致定时任务链断开
背景是一个定时触发的 AI 分析 agent,它需要周期性地调用本系统的报告 API。某天开始连续报错,错误信息是sign-in could not be completed token exchange failed: token endpoint returned status 403。初看以为是权限问题,查服务端日志后发现,定时任务用的 service account 登录流程里,授权请求走的是旧的 token endpoint 配置,而网关已经升级到新的 endpoint,旧路径被安全策略拦截。
排查顺序是:先看错误发生的组件边界,再对比最近变更记录。最终定位是配置文件里 endpoint 地址没有随服务升级同步更新。这个案例的启示是:token 报错不要第一时间怀疑认证算法,先检查组件交互边界和配置文件版本。
5.2 案例二:agent 并发打满配额后的连环雪崩
现象:某个时段 agent 任务失败率突然从 3% 飙升到 47%,并且所有失败都集中在同一个外部 OCR 服务。排查 trace 发现,并发池配置在高峰期给某个长耗时任务开了后台并行子任务,一下子把 OCR 服务的 QPS 配额打满,后续请求全被限流,然后 agent 在限流后不断重试,形成雪崩。
修复分三步:第一步,在并发池外围加限流器,限制每秒最多 20 个 Agent 任务进入;第二步,给 OCR 调用单独加一层退避策略,收到限流码后等待 1 秒、2 秒、4 秒指数退避;第三步,给长耗时任务设置并行子任务上限,限制最大并行 3 个。修复后失败率回落到 1.8%,而且 OCR 服务恢复正常响应。
这个案例再次印证了一件事:agent 工程里,流量控制不能只依赖底层模型服务的限流,每个外部依赖都要有自己的保护层,因为 agent 会以不可预测的方式发起调用。
6. 观察与体会:工程复杂度正在向上移动
这一整周下来,我的核心感受是:AI 工程的门槛不在“调用模型”,而在“治理不确定性”。更少 token 是成本与性能的治理,更强模型是能力与风险的治理,更难管的 agent 是行为与并发的治理。三者本质上都在做同一件事:把模型的概率行为约束在确定性的工程边界里。
还在老项目里挣扎的时候,我觉得 prompt 写得好就是高手。现在回头看,prompt 只是最外层的手段。真正拉开差距的,是你怎么设计上下文结构、怎么配置缓存、怎么给 agent 套护栏、怎么做 trace 和幂等。每一个词条背后都是一堆细节问题,而这些细节恰恰是传统文档不会写的。
最后分享一个小技巧:这周我把 agent 的每次调用都统一封装成一个入口函数,无论是调模型、调工具还是调外部 API,都会自动做三件事——记录耗时和 token、生成 trace、检查是否超出预算。这个封装让我在排查问题时能直接从统一入口看全局,而不是翻好几份日志。这个习惯我强烈建议每个做 AI 工程的人都尽早养成,因为它省下的是深夜排查问题时最宝贵的睡眠。