尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

语义热力学:用叙事约束把 LLM 的 token 消耗降下来

语义热力学:用叙事约束把 LLM 的 token 消耗降下来 做 LLM 应用的人应该都有过这种经历一个功能看起来很简单但调一次 API 返回的 token 比想象中多一倍做一个 Agent 任务中间来回几轮最后账单比功能复杂度还高。语义热力学Semantic Thermodynamics这个概念正是冲着这个问题来的——在生成内容时引入叙事约束narrative constraints尝试把 LLM 的 token 消耗明显压下来。标题里给出的数字是 79%但先别急着把这个数字当成所有场景的通用结果。它更像一个信号token 优化不一定只能靠换模型、降精度、调并发还可以从输入设计和输出约束入手。这篇文章适合三类人看频繁调用 LLM API 的开发者用本地模型跑批量任务的工程师以及正在做 Agent、RAG、MCP 工具调用的人。最值得关注的核心点也先说清楚叙事约束不是把 prompt 写短而是通过在生成前定义任务边界和输出空间让模型少走弯路。它不改变模型本身的推理能力但能降低无效输出进而降低 token 消耗和时延。下面按我实际落地时会用的顺序拆一遍。1. 语义热力学解决的是什么问题LLM 的 token 浪费从哪来1.1 大多数 token 浪费发生在“探索式生成”上大模型在生成文本时并不会提前知道自己该输出多长、输出什么结构。它只是根据上下文逐步预测下一个 token。当 prompt 没有明确任务边界时模型会在多个方向上“试探”结果就是输出大量与业务无关的内容。最常见的浪费是这样出现的。你让模型“分析一下这段客户反馈”它先来一句“好的我来为您分析”然后把背景重复一遍再给几条普遍性建议最后还加一句“如果需要进一步帮助请随时告诉我”。对于要接入业务系统的请求前三段都是多余 token。我在本地部署模型时也经常看到同样的问题。同样的任务带不带明确的输出约束单次输出 token 差距经常在 30% 到 60% 以上。这不是模型能力不行而是 prompt 给了它太多自由发挥空间。1.2 语义热力学里的“熵”可以理解成生成的不确定性“语义热力学”这个叫法听起来很学术但它更像是从物理里借了一个隐喻。热力学里系统状态越混乱熵越高。放到 LLM 生成里模型的“状态空间”就是它下一步可能输出的词汇范围。约束越少可能的后续 token 分布越宽模型需要在更多分支里做计算输出结果也更难预测。叙事约束做的事情相当于在生成前把状态空间缩小。比如告诉模型“只输出 JSON不要解释”那“好的”“首先”“希望”这些无关 token 的概率就会被压到很低。这不是靠运气而是靠 prompt 让模型进入一个更窄的语义通道。不过要注意这个“熵”不是严格物理定义。我没有把语义热力学当成一套完整理论在用而是把它当成一个优化思路先识别生成里的冗余再用约束去压缩冗余。这个角度在工程上是能落地的。1.3 为什么 token 优化要单独讨论很多团队一开始只关注模型精度和推理框架。精度从 fp32 降到 fp16、bf16确实能省显存、提速度但生成逻辑没变该输出的废话还是照常输出。如果业务任务边界清晰叙事约束可以和精度优化叠加使用互不冲突。另外token 成本在 API 场景里是复利。输入 token 会随着多轮对话变大输出 token 又会成为下一轮的输入。一个 Agent 跑 5 步如果每步都多输出 200 token最后多出来的成本不是 200 乘 5而是每一步的输入都包含之前所有步骤的累积结果实际膨胀更明显。这也是为什么很多人做 Agent 之后才真正意识到 output token 压缩有多重要。2. 叙事约束为什么能把 token 降下来从输出空间和语义冗余拆解2.1 第一类冗余模型在任务边界不明确时的“试探性输出”模型在不确定边界时会倾向于把回答写得更“完整”。所谓完整往往是加背景、加解释、加总结、加下一步建议。对于客服意图识别、信息抽取、工具参数生成这类收敛型任务这些都是噪音。我一般会在实验里放一组对照无约束 prompt请分析以下客户反馈并给出你的处理建议。 客户反馈快递晚了两天客服态度也不好申请退款还被拒绝。有约束 prompt你是一个客服工单分析助手。 输入一段客户反馈文本。 任务抽取反馈中的关键信息。 输出要求只返回 JSONkey 固定为 - sentiment情感倾向只能是 negative/neutral/positive - category问题分类从物流、服务、退款、其他中选择 - keywords关键短语数组 - reply_suggestion建议回复不超过 30 字 禁止不要输出解释、前缀、Markdown 代码块围栏不要输出额外字段。 无法判断时返回 {sentiment: unknown}。无约束版本大概率会输出一段分析比如“从反馈来看客户对物流和服务都不满意尤其是在退款申请被拒绝后情绪比较负面。建议先安抚客户再核实退款进度……”这段文字单独看没问题但落到系统里还需要再解析。有约束版本则直接给结构化字段程序拿过来就能用。在测试里处理这种任务时有约束版本输出 token 通常只有无约束版本的三分之一到一半。这不是因为模型“变聪明了”而是 prompt 把任务从开放式表达变成了字段填充。2.2 第二类冗余重复表述和可见的“思考痕迹”模型有时会重复表述同一个观点。比如先总述“客户很不满意”后面又分析“从语气中可以感受到客户的情绪比较激动”最后再总结“整体来看客户体验较差”。这三句话语义上是重叠的但都被算成 token。更隐蔽的浪费是模型的“思考痕迹”。一些模型在生成最终答案前会把推理过程、判断依据、备选方案都写在输出里。对需要展示推理步骤的场景这可能是优点但对只想拿结果的业务系统这些就是多余 token。叙事约束处理这种冗余比较有效的方式是给输出一个固定结构。比如“先给结论再给不超过两个理由每个理由不超过 20 字。”这样模型不会自我重复太多。有人可能会担心约束太多会不会抑制模型质量确实会。但如果任务本身是抽取、分类、生成工具参数用户要的就是确定性和可解析性而不是文采。2.3 第三类冗余上下文被输出结果“反向撑大”之后的多轮成本这是最容易被忽略的一点。在很多 LLM 框架里上一轮模型输出会被保留到下一轮上下文。也就是说模型上一轮多输出的每个 token下一轮都要重新计算。举例说明。一个 Agent 要调用搜索工具然后基于结果生成总结。如果第一步里模型输出了大段“我先分析一下用户意图然后选择合适的关键词”这些内容会被塞进下一步的上下文。第二步的输入变长成本变高而且这些“分析”并不会给最终结果增加多少价值。在 RAG 流程里也一样。检索出来的文档已经占了大量上下文如果生成阶段还输出无关内容整个链路的 token 消耗会明显膨胀。叙事约束在这个层面的价值不只是省单次调用的钱而是控制整个链路上下文膨胀的速度。每一轮都只输出必要字段后面的每一轮都不必为前面的话买单。2.4 79% 这个数字通常出现在“任务边界很清晰”的场景不是所有任务都能压缩 79%。我的理解是这个数字大概率来自一个原本输出冗余度很高的收敛型任务比如从长文本中抽取字段、日志摘要、工具调用参数生成。这类任务有两个特点原 prompt 没做任何约束模型输出大量解释和客套话。业务真正需要的只是几个字段或一段固定结构。这两个条件同时满足时压缩比例很容易超过 50%。反过来如果是文案创作、头脑风暴、小说生成、情感陪伴这类发散型任务叙事约束不但压不了多少 token还会明显影响输出质量。因为发散型任务的价值就在“不确定性”和“多样性”上你把状态空间压得太窄结果就变得平庸。3. 落地前置条件哪些场景适合哪些环境最容易见效3.1 先判断你的任务是“收敛型”还是“发散型”我建议接到一个 token 优化需求时先别急着调 prompt而是先给任务分类。这个判断决定了叙事约束到底能不能用以及能压到什么程度。表格可以这样列任务类型典型例子是否适合叙事约束预期压缩空间收敛型信息抽取、意图识别、格式化输出、工具参数生成、分类打标适合高半收敛型客服回答、邮件回复、日志摘要、固定结构周报适合但需要保留表达空间中发散型文案创作、故事生成、头脑风暴、情感陪伴谨慎使用低混合型先分析再给出建议、先搜索再总结适合分步约束中到高收敛型任务的核心特点是“结果可以枚举或结构化”。比如意图识别最终答案大概率是“查询订单”“申请退款”“转人工”里的一个工具参数生成最终答案是一个 JSON。这类任务里模型输出的越短越直接越好。发散型任务则相反。给模型加“不要输出多余内容”的约束等于让一个大厨只做一份没有任何装饰的菜能吃但用户可能觉得差点意思。3.2 和模型精度、推理框架的关系token 缩减和模型精度是两个维度的优化可以叠加。很多人在本地跑 LLM 时第一反应是参考那些关于 fp16、fp32、bf16 的精度对比把模型从 fp16 换成 bf16 或 int8把显存降下来。这些优化对推理速度和显存占用有直接帮助但不会改变模型“喜欢说废话”的行为。一套更稳的组合是模型精度保持能满足业务质量的最低精度。如果 bf16 在验证集上和 fp16 没有明显差异可以优先用 bf16。推理框架LLM 框架、llm studio 这类工具主要解决部署和调用问题不解决输出冗余问题。叙事约束负责减少无效生成降低输出 token 和上下文膨胀。比如我在本地用一个大模型跑批量文本分类显存足够速度也不算慢但每条结果都带着一大段理由。这时降精度反而没必要更合适的做法是加一个“只输出分类标签”的约束。输出 token 降低后响应速度明显改善显存压力也小了一些。3.3 落到 API 和本地模型上验证方式有区别如果用的是 LLM APItoken 统计比较方便响应体里通常直接返回 usage包含 prompt token 和 completion token。你可以先把约束前后的 usage 记录成表格再算比例。如果是本地模型需要自己通过日志或框架接口拿 token 数。很多本地推理框架也有 token 统计只是不同工具字段名不同。常见的位置包括服务日志里的 token 信息响应体里的 usage 字段框架内置的 metrics 接口这里最容易踩的坑是只统计输出 token忽略输入 token。在单次调用里输出压缩可能很明显但在多轮 Agent 里输入 token 的膨胀往往更吓人。所以要两个都记。4. 最小可用实验先搭一个带叙事约束的生成流程4.1 实验目标与前置准备做 token 优化实验我建议不要一上来就开最大批量。先选一个任务准备 20 条左右有明确答案的样本跑通对比链路。你要准备的东西不复杂一个能调用的模型可以是 API也可以是本地部署实例。同一批输入样本建议导出成 JSON 或 CSV每条记录带唯一 ID。能记录 token 和耗时的工具。API 一般自带 usage本地框架看日志。最好把 temperature 固定为 0或尽量调低减少随机性对输出长度的影响。样例文件不需要很复杂[ { id: 001, feedback: 快递晚了两天客服态度也不好申请退款还被拒绝。 }, { id: 002, feedback: 包装很结实送货很快整体体验不错。 } ]4.2 设计叙事约束模板叙事约束并不是写一句“请简洁回答”就完事。它至少要包含四类信息角色定位模型以什么身份处理任务。输入对象输入数据长什么样从哪来。输出格式结构、字段、格式、长度上限。禁止项哪些内容绝对不能出现。可以把它做成一个通用模板放到 system prompt 或 prompt 开头。约束模板示例 [角色] 你是一个数据抽取助手。 [任务] 从输入中抽取指定字段。 [输入] 一段客户反馈文本语言不限。 [输出格式] 只返回 JSON字段固定如下 { sentiment: negative/neutral/positive, category: 物流/服务/退款/其他, keywords: [关键词1, 关键词2], reply_suggestion: 不超过30字的建议回复 } [禁止] 不要输出解释。 不要输出前缀或后缀。 不要输出 Markdown 代码块围栏。 不要输出额外字段。 如果无法判断返回 {sentiment: unknown}。和无约束 prompt 放在一起对比时你会发现无约束版本的“自由发挥”内容基本都被挡掉了。这就是叙事约束的直接效果。4.3 跑通单条样例记录 token 和输出质量第一轮实验不要追求压缩比例而是确认链路跑得通。单条成功标准有三个输出能被程序解析成预期格式。必要字段都完整。结果质量不劣于无约束版本。跑完单条之后再跑完整 20 条样本。统计方式可以很简单无约束版本 平均输出 token320 平均输入 token45 平均耗时1.8s JSON 可解析率30% 约束版本 平均输出 token78 平均输入 token120 平均耗时0.9s JSON 可解析率95%注意约束版本的 prompt 变长了它会增加一些输入 token所以单次总 token 不一定会直接减少 79%。但如果任务原本输出就很啰嗦输出 token 的下降通常能覆盖输入 token 的增加。在多轮对话里这个效果会更明显因为长期看输入 token 也会被后续轮次复用。5. 从单条任务到 Agent 批量任务约束模板、输出规范和失败重试5.1 Agent 场景里叙事约束要分成“全局约束”和“单步约束”做 Agent 和单次生成不一样Agent 里模型要经历“理解任务 - 规划步骤 - 调用工具 - 处理结果 - 输出答案”多个阶段。如果只在一开始给一个全局约束后面几步很容易跑偏。我的做法是拆成两层全局约束放在 system prompt 里描述整体角色、任务目标、禁止行为。单步约束放在每一步的提示词或工具调用说明里明确这一步只输出什么。比如一个搜索类 Agent全局约束可以写“你是一个信息整理助手只做必要判断不输出无关解释”。单步约束可以变成“调用搜索工具时只输出搜索关键词和范围最多 5 个关键词不要说明搜索意图”。这样每一轮输出都不会膨胀太多下一轮上下文也不会被塞进大量废话。5.2 批量任务必须考虑输出命名、失败重试和日志单条能跑通不代表批量能跑完。批量任务最容易出的问题不是模型能力而是任务管理和输出管理。以下几点我建议提前做好每条输入带唯一 ID输出文件名或记录里带同一个 ID方便定位问题。输出目录按批次建不要把所有结果堆在一个文件里。日志里记录每次调用的输入 token、输出 token、耗时、状态码。失败重试要有上限。一般重试 1 到 2 次即可不要无限重试。重试时保持相同约束模板不要因为一次失败就临时改 prompt否则结果不可比。如果一批任务里失败率超过 20%别急着加并发先回头检查输入格式、约束模板、模型上下文长度。5.3 结构化输出可以进一步压缩“叙事”叙事约束可以落在 prompt 层也可以落在接口层。现在很多 LLM API 和框架支持结构化输出、JSON Schema、函数调用这类机制能把“输出必须符合某个结构”这件事固化下来比在 prompt 里写“只输出 JSON”更稳定。在 MCP 或 Agent 工具调用场景里结构化输出的意义更大。模型只需要返回工具参数不需要解释为什么选这些参数。这样既省 token又方便程序直接执行。我的习惯是能落在接口约束里的尽量不放到 prompt 里。prompt 里描述得再清楚模型也可能偶尔漏掉接口层的 schema 验证是硬约束不符合就直接拒绝或重试模型很快会学会按格式输出。6. 怎么验证 79% 这个数字指标统计和效果判断6.1 统计哪些指标验证 token 优化不能只看输出 token。输出少了一截但输入变长或者下游解析失败整体收益可能被抵消。建议至少记录四类指标指标统计方法判断标准输入 tokenAPI usage 或本地日志多轮场景重点观察递增速度输出 tokenAPI usage 或本地日志看均值、中位数、P95总耗时单次请求从发起到返回的时间看均值变化不能只看一条解析成功率输出能被 JSON 解析并包含必填字段的比例越高越好通常应高于 90%任务质量人工抽查或下游指标不能因为压缩明显下降如果是 Agent 多轮任务还要统计整条链路的累计 token。很多框架都能导出完整调用链把这个调用链的 token 加总起来对比才看得出真实收益。6.2 一个可复现的对比方案同一批数据同一模型temperature 都设成 0各跑一遍无约束和约束版本。数据量建议至少 50 条太少会受单次波动影响。对比时看几样东西输出 token 均值下降比例总 token输入加输出下降比例解析成功率变化任务质量是否可接受79% 这个数字可以用于观察但不要作为验收目标。它大概率来自一个特定任务、特定模型、特定约束模板。换一个模型压缩比例可能掉到 50%换一个本来就很精简的 prompt可能只剩 20%。真正重要的是找到你自己的基线再在自己的数据上比较多轮结果。6.3 如果压缩后质量下降怎么办压缩 token 之后质量下降通常是三种原因。第一种是约束过强。比如你禁止了所有背景信息但下游业务确实需要模型对输入做一点解释。解决办法不是删掉约束而是在输出格式里加一个“summary”或“reason”字段给理由留出空间。第二种是输出格式装不下有效信息。这种情况在 JSON 字段设计不合理时很常见。比如你把回复建议限制在 10 字以内模型给不出来只能硬挤一句不通顺的话。把长度放宽到 30 到 50 字效果会好很多。第三种是模型本身对结构化输出支持不好。有些小模型或量化模型在 JSON 格式上不稳定经常出现字段名写错、字符串没闭合、输出在结尾被截断的情况。这时可以考虑换更强的模型、减少禁止项、或者在代码里做一次自动修复。我自己的原则是解析成功率优先于 token 数量。一堆无法解析的输出再省 token 也没有价值。7. 容易翻车的地方排查顺序和边界判断7.1 排查顺序现象、输入、环境、参数、约束模板做这类优化时很容易一报错就去怀疑 prompt 写得不够好。但按照我的经验很多问题的根子在更底层。建议按这个顺序排查先看现象是报错、卡住、无输出、输出内容不对还是速度变慢再看输入文件编码、字段名、文本长度、请求格式是否正常。再看环境模型路径、依赖版本、接口地址、上下文长度配置。再看参数temperature、max_tokens、stop、response_format 是否合适。最后才看约束模板是否有禁止项和任务冲突字段是否超出模型能力。比如 JSON 解析失败首先看有没有把 max_tokens 设得太小导致输出被截断。其次看模型版本是不是支持 JSON mode。最后才去怀疑约束模板写得不清楚。7.2 常见边界叙事约束不是“把 prompt 缩短”很多人会把 token 优化理解成“少写点字”。但实际上叙事约束往往会让 prompt 变长因为它要描述角色、输出格式、禁止项。这些新增的输入 token是用于控制输出的“开关成本”。如果为了省 token 把必要的背景信息删掉模型理解不了任务输出质量下降后面反而要花更多轮对话来纠错。结果就是总 token 更高。另外一个边界是结构化输出了不等于所有格式都稳定。JSON mode 在主流模型上比较可靠但在某些本地小模型上仍然会出问题。建议用小样本提前验证不要在正式跑批时才暴露。7.3 什么时候不值得做叙事约束不是万能的下面这些情况我一般不会投入太多时间单次调用量很小优化前后成本差异只有几厘钱。任务是创意写作或开放讨论用户要的就是丰富表达。模型本身已经通过系统 prompt 或接口层强制输出了结构化结果。团队没有足够精力维护约束模板和验证集。下游环节对格式没有严格要求模型输出直接给人看冗余影响不大。如果只是学习默认配置加一套简单约束通常够用。如果要长期使用建议把约束模板、验证样本、日志和输出目录提前整理好否则每换一个任务都要重新做一遍实验。我自己跑过很多轮类似实验后发现语义热力学这个叫法听起来高深落地时其实就是两件事想清楚任务边界再把边界写进提示词或接口约束。真正要盯住的不是 79% 这个数字而是输入格式、输出可解析性和失败重试。先把单条任务跑稳再考虑批量先在本地小样本上验证再投入正式链路。这样出来的结果才不是碰巧好看而是能持续复现的优化。
返回列表