模型应用的预算治理

📅 2026/8/21 20:11:34
模型应用的预算治理
模型应用的预算治理预算有限时应先拆分单次请求的 Token 构成再决定是精简 Prompt 还是调整 RAG 的 Top-K。若几千字 Prompt 叠加多条召回文档成本往往主要来自上下文而不是用户问题本身。1. 月底 API 账单超预算入口层 Token 堆积是罪魁祸首抓取最近一周的日志分析发现平均每次请求消耗 4800 个 Token。仔细拆解这 4800 个 Token 的构成背景 System Prompt 占了 1200 个RAG 检索出来的上下文占了 3200 个真正来自用户提问的只有不到 100 个 Token。这种结构非常危险。当业务并发量上来后大部分资金都在给冗余的上下文埋单。更糟糕的是上下文过长直接拖慢了首包响应时间TTFT导致延迟飙升。[原始请求 Token 分配] System Prompt: ██████████ 1200 Tokens RAG Context: ██████████████████████████████ 3200 Tokens User Query: ██ 100 Tokens Output Limit: ████ 300 Tokens面对这种情况很多人第一反应是更换便宜的小模型。但直接换小模型会导致复杂推理准确率骤降。真正的切入点应当是入口层的 Token 治理优先降低无用上下文的侵占。2. Prompt 压缩与模版清理把 2000 Token 减到 400 Token仔细检查系统内部的 Prompt 模版里面充斥着大量的陈述句和多余的 Few-Shot 示例。比如“请你作为一个专业的金融分析专家遵循以下 15 条严格的规则来回答问题”光是这套角色设定就占用了数百 Token。应通过对照实验确认大模型对格式化 instructions 的理解效率高于自然语言废话。通过将冗长自然语言转化为 Markdown 标记和结构化 JSON 规范Prompt 长度直接被压缩了 65%。压缩前 请你仔细阅读以下背景材料并严格根据材料内容回答用户的提问。如果材料中没有提及请明确回答“不知道”不要自己瞎编答案。回答时请保持客观公正的语气字数控制在 200 字以内。 压缩后 [约束] 1. 仅依据 context 回答无相关信息输出 未知。 2. 保持客观限制 200 字内。仅这一项修改System Prompt 就从 1200 Token 降到了 350 Token。模型不仅没有因为提示词减少而变傻反而因为干扰信息降低回答命中率提升了 4%。3. RAG 检索链路砍刀Top-K 从 10 降到 3精度真的下降了吗RAG 链路中的文本块Chunk召回是 Token 消耗的另一个常见薄弱点。之前的配置是直接取 Vector DB 的 Top-10 结果每个 Chunk 设定为 300 Token单次请求光是 context 就硬生生灌塞进去 3000 Token。为了验证 Top-K 对回答质量的影响我们在测试集上跑了对比实验。结果显示Top-10 与 Top-3 在答案召回覆盖率上的差异极其微小但在 Token 消耗和延迟上有着天壤之别。引入轻量级重排模型Re-Ranker后先粗召回 20 条再由 Cross-Encoder 筛选出相关度最高的 Top-3 送入 LLM。RAG Context 的 Token 消耗瞬间从 3000 降到了 900。4. 面向生产环境的 Token 预算控制闸门代码为了防止某次异常请求装载过大文本打爆预算必须在调用 API 前设置确定性的 Token 预算闸门Budget Gate。如果上下文超出安全阈值直接进行强制截断或降级处理而不是无脑发给供应商 API。下面是基于tiktoken实现的面向生产环境的 Token 预算控制管理器支持动态截断与降级兜底。import tiktoken from typing import List, Dict, Any, Tuple class TokenBudgetManager: def __init__(self, model_name: str gpt-4o-mini, max_total_tokens: int 2048): self.model_name model_name self.max_total_tokens max_total_tokens try: self.encoder tiktoken.encoding_for_model(model_name) except KeyError: self.encoder tiktoken.get_encoding(cl100k_base) def count_tokens(self, text: str) - int: if not text: return 0 return len(self.encoder.encode(text)) def truncate_text(self, text: str, max_tokens: int) - str: tokens self.encoder.encode(text) if len(tokens) max_tokens: return text return self.encoder.decode(tokens[:max_tokens]) def fit_context_to_budget( self, system_prompt: str, user_query: str, retrieved_chunks: List[str], max_output_tokens: int 500 ) - Tuple[str, List[str], Dict[str, Any]]: 按照优先级计算并装配上下文防止 Token 溢出 优先级: User Query System Prompt RAG Chunks system_tokens self.count_tokens(system_prompt) query_tokens self.count_tokens(user_query) # 计算留给 RAG 的剩余预算 reserved_tokens system_tokens query_tokens max_output_tokens available_for_rag self.max_total_tokens - reserved_tokens if available_for_rag 0: # 极限情况降级用户问题过长截断用户问题抛弃 RAG 上下文 truncated_query self.truncate_text(user_query, self.max_total_tokens - system_tokens - max_output_tokens) meta {rag_used: 0, status: degraded_no_rag, total_estimated: self.max_total_tokens} return system_prompt, [], meta selected_chunks [] used_rag_tokens 0 for chunk in retrieved_chunks: chunk_tokens self.count_tokens(chunk) if used_rag_tokens chunk_tokens available_for_rag: selected_chunks.append(chunk) used_rag_tokens chunk_tokens else: # 尝试放入被截断的最后一个 chunk remaining_rag_budget available_for_rag - used_rag_tokens if remaining_rag_budget 50: # 过小的 chunk 丢弃 truncated_chunk self.truncate_text(chunk, remaining_rag_budget) selected_chunks.append(truncated_chunk) used_rag_tokens remaining_rag_budget break total_estimated system_tokens query_tokens used_rag_tokens max_output_tokens meta { rag_used_chunks: len(selected_chunks), used_rag_tokens: used_rag_tokens, status: normal, total_estimated: total_estimated } return system_prompt, selected_chunks, meta if __name__ __main__: manager TokenBudgetManager(max_total_tokens1000) sys_p [约束]\n1. 仅依据 context 回答。 query 请分析过去三个季度公司的供应链风险点。 chunks [ 第一季度数据供应链由于芯片短缺导致产能下降 12%主要影响了 A 系列产品的交付周期。 * 5, 第二季度数据物流成本上升 8%海运集装箱紧缺情况有所缓解但欧洲线路延迟依旧存在。 * 5, 第三季度数据原材料采购价格上涨 5%供应商集中度过高的风险开始显现。 * 5 ] clean_sys, fit_chunks, report manager.fit_context_to_budget(sys_p, query, chunks) print(f装配状态: {report[status]}) print(f选用 Chunk 数: {report[rag_used_chunks]}) print(f预计占用 Token: {report[total_estimated]})5. 性能与成本评测基线优化前后的延迟与花费对比对线上真实压测环境跑了 500 次并发请求收集优化前后的关键度量指标。结果证明在预算有限的限制下优先优化 Prompt 结构与 RAG 召回链路收益远大于直接替换底层模型。优化后单次调用的平均 Token 消耗从 4800 下降到 1350。日均 API 费用直接缩减了 71%同时由于 Context 变小LLM 推理的首包延迟TTFT从 1.4 秒降低至 0.45 秒。[压测结果对比] 指标 | 优化前 | 优化后 | 变化幅度 ----------------|--------------|--------------|---------- 平均 Token / 次 | 4800 Tokens | 1350 Tokens | -71.8% 首包延迟 TTFT | 1.40 秒 | 0.45 秒 | -67.8% 准确率 (Eval Set)| 87.2% | 88.5% | 1.3%先把多余的文字水分抽掉再谈模型微调和选型。确定性的工程闸门比盲目赌模型迭代靠谱得多。