做 AI 应用接近一年了,最开始以为就是把 API 调一调,很快发现坑多得超出想象。模型本身的能力已经很强了,真正难的是工程部分:怎么让它在生产环境里稳定、准确、成本可控地运行。整理一些实际遇到的问题和解法。
上下文窗口的管理
现在主流模型的 context window:GPT-4o 是 128K tokens,Claude 3.5 Sonnet 是 200K,Gemini 1.5 Pro 甚至到了 1M。看起来很大,但生产场景里用起来并不是”有多少塞多少”这么简单。
token 成本问题
以 GPT-4o 为例(截至写这篇文章),input $5/1M tokens,output $15/1M tokens。一个对话如果每轮都带着全部历史,50 轮对话下来 input token 消耗是个等差数列求和,成本会爆。
长上下文的”迷失”问题
论文 “Lost in the Middle” 表明,当上下文很长时,模型对中间位置的内容注意力明显下降,首尾的内容处理得更好。所以简单地把所有文档塞满 context 并不能保证质量。
实际的上下文管理策略:
1 | 会话历史压缩: |
幻觉(Hallucination)的工程解法
模型会一本正经地说错话,这不会彻底消失,只能通过工程手段减少。
结构化输出 + 验证
让模型返回 JSON 而不是自然语言,然后在应用层验证:
1 | // 用 Zod 定义期望的输出结构 |
GPT-4o 支持 response_format: { type: 'json_schema', json_schema: { ... } } 直接传 schema,更进一步约束输出格式。
事实性内容靠 RAG + citation
对于需要准确性的场景(客服问答、知识库检索),不让模型”自由发挥”,而是:
- 从知识库检索相关内容
- 让模型只基于检索到的内容回答
- 要求模型给出引用来源
1 | system: 你只能基于提供的参考文档回答问题,不能使用文档以外的知识。 |
Self-consistency 抽样
对关键问题让模型回答多次(temperature > 0),取多次答案中的”众数”。一次回答可能幻觉,但多次回答如果大多数一致,可信度更高。成本翻几倍,适合精度要求极高的场景。
成本控制
LLM API 的成本不容忽视,特别是高流量场景。
缓存语义相似的请求
完全相同的 prompt 可以用 Redis 精确匹配缓存。更进一步,对语义相似的查询也可以缓存:
1 | async function cachedLLMCall(prompt: string) { |
模型路由(Model Routing)
不同复杂度的任务用不同模型:
1 | function selectModel(taskType: string, complexity: 'low' | 'medium' | 'high') { |
gpt-4o-mini 比 gpt-4o 便宜约 15 倍,能处理的任务比想象中多。先用小模型,不行再 fallback 到大模型,能省不少钱。
流式响应减少超时和体感延迟
LLM 生成长响应时间长,用 streaming 让用户看到逐步生成:
1 | const stream = await openai.chat.completions.create({ |
流式输出让用户感觉”快”很多,即使总时长相同。
观测性(Observability)
生产环境里,LLM 调用黑盒化非常危险——你不知道它什么时候开始退化、哪类问题准确率低、哪个 prompt 版本效果更好。
最基本的要记录:
1 | interface LLMCallLog { |
有了这些数据,才能做 A/B 测试 prompt、分析成本趋势、及时发现模型性能退化。
LLM 应用的工程复杂度超出了很多人的预期。模型调通只是开始,上下文管理、幻觉控制、成本优化、监控这些才是真正决定产品质量的地方。
评论加载中…