
如果你正在用大模型做 Agent、RAG 或者任何偏“生產級”的 LLM 應用最近一定被同一個問題折磨過token 不夠用。不是模型能力不行而是每一輪對話、每一段工具調用結果、每一次系統提示詞注入都在消耗上下文窗口。換更貴的模型能解決質量卻解決不了成本做文本截斷能保住窗口卻往往把關鍵信息一起切掉。很多團隊在模型選型和 RAG 方案上花了大量精力最后發現真正卡住業務規模的其實就是 token 預算。但這里面藏著一個被低估的事實大量 token 并不是“必須花”的而是模型在輸出時產生了大量敘事冗余——它用好幾句話表達本來一句話就能說清楚的信息。模型不是不知道該簡潔而是沒有人給它足夠的“敘事約束”。這篇文章想講清楚一個思路把 LLM 的輸出看作一個語義系統通過熱力學式的“約束”來降低語義熵讓每個 token 都承載更多有效信息。這個思路對應到一個很形象的概念——Semantic Thermodynamics中文可以叫“語義熱力學”。更重要的是不僅講概念還會給出一個可以在本地跑通的完整實驗讓你親自測量在同樣的任務里使用敘事約束和不使用敘事約束輸出 token 到底差多少。這也是“79% token reduction”這類數字最靠譜的理解方式它不是所有場景的普適結論而是在高冗余文本生成任務里約束帶來的真實收益。1. 這篇文章真正要解決的問題先說痛點。假設你維護一個內部的 RAG 問答系統。用戶上傳一份 2000 token 的產品文檔系統召回 3 個片段每個 500 token再加上 system prompt 和用戶問題輸入側大概 4000 token。模型生成回答如果沒有任何輸出約束一次返回 800 token 是常有的事。這看起來很平常但如果你的業務是每天幾千次調用這個成本就非??捎^。更麻煩的是 Agent 場景。Agent 需要多輪工具調用每一輪都要把之前的對話歷史、工具返回結果、中間思考塞進上下文。一個 5 輪調用的任務輸入輸出累計可能輕松突破一兩萬 token。而在這其中有很大一部分是“敘述性”的模型在復述已知信息、在重復套話、在生成格式松散的過渡句。傳統優化手段有幾種但都有代價換更小/更便宜的模型質量可能下降。手動截斷歷史可能丟失關鍵上下文。用摘要代替原文摘要本身產生的 token 也很高而且損失細節。這些方法都默認 token 消耗是“輸入側”的問題只要把輸入壓小就行。但實際上輸出側的敘事冗余同樣重要而且往往被忽略。所謂敘事約束不是簡單地說“請簡短回答”而是從輸出結構下手告訴模型必須按什么模板、什么粒度、什么順序來生成。約束越明確模型的輸出就越接近“最小語義集”廢話自然減少。這篇文章適合誰正在做 LLM 應用開發想壓 token 成本的技術人員。維護 RAG 或 Agent 系統被上下文窗口撐爆困擾的工程師。對“結構化輸出”“輸出約束”有興趣但沒時間系統整理方法的開發者。讀完之后你能得到一個可以直接運行的示例以及一套判斷“什么時候該用約束”的實踐指南。2. Semantic Thermodynamics 的核心概念把 LLM 輸出看成語義系統第一次看到“Semantic Thermodynamics”這個詞大概率會覺得它很玄學。它聽起來像是物理學的分支實際上它并不是一個嚴格的熱力學理論也不是某個官方標準而是一種思考模型輸出與 token 消耗之間關系的方式。通行的認識是LLM 的每個輸出 token本質都是在“內容空間”中做一次選擇。如果這個選擇的隨機性很高模型就會輸出大量低價值、可預測的過渡內容如果選擇被強烈約束模型就必須把語義集中到有限幾個 token 上??梢杂脽崃W里的概念來類比熵在信息論里熵表示不確定性。LLM 生成的文本中如果候選詞分布很分散、上下文信息弱輸出就更“混亂”表現為結構松散、重復表達。這類輸出可以看作“高語義熵”。自由能熱力學中系統能對外做功的部分叫自由能。對應到 LLM 輸出真正對任務有價值的語義信息就是“有效語義”。大量 token 消耗在維持“句式完整”“語氣自然”“表達柔和”上這些不是有效語義。約束/邊界條件把一個氣體系統關進容器它就不能無限膨脹。敘事約束也是一種“邊界條件”把模型的表達范圍限定在一個緊湊的結構里。所以Semantic Thermodynamics 的通俗解釋是在模型輸出能力不變的情況下通過增加敘事約束降低輸出文本的語義熵讓每個 token 攜帶更多有效語義。為什么叫“敘事”約束因為約束的對象不是單個詞匯而是模型生成內容的組織方式。比如輸出必須分為 3 個章節。每章只能用列表。每項不超過 20 字。不要輸出總結段落。不要重復問題中的背景信息。這些約束都作用于“敘述結構”所以叫 narrative constraints。理解了這一點你就抓住了標題里“79% token reduction”背后的邏輯當任務本身冗余度高約束帶來的收益就極大。反過來如果任務本身已經很緊湊比如直接生成 JSON 數據約束能帶來的空間就有限。3. LLM Token 成本構成為什么“敘事冗余”會影響預算要真正理解約束的價值得先把 token 成本拆開看。3.1 Token 成本的三部分一次完整的 LLM 調用token 消耗由三部分組成組成部分含義典型來源輸入 token系統提示、用戶輸入、檢索結果、對話歷史Prompt 設計與 RAG 召回輸出 token模型生成的內容回答、總結、工具調用參數緩存/日志 token重復調用時的歷史記錄、日志分析多輪 Agent、長期會話很多優化方案只盯著第一項比如把 system prompt 寫得更短、減少檢索片段。但輸出 token 同樣重要尤其是在生成型任務中。3.2 輸出側冗余的真實場景舉一個非常常見的例子。你讓模型把開發記錄整理成周報模型可能會這樣輸出“本周我們團隊主要圍繞訂單服務展開了一系列優化工作。首先完成了日志采集系統的搭建并成功接入了 order-service 和 payment-service 兩個核心服務。其次……”這段文本信息密度很低。真正有效信息只有“搭建日志采集接入兩個服務”其余都是敘事連接。如果任務規模再大一點比如整理本周 20 條開發記錄模型很容易生成 800 到 1000 token 的周報。而如果加上約束要求“只輸出三章列表每項不超過 25 字”同一份開發記錄模型可能只需要 200 token 就能表達同樣的信息。這就是敘事冗余在輸出側造成的浪費。它不像輸入側那樣直觀但累計起來非常驚人。3.3 傳統壓縮方案 vs 敘事約束有人會說那直接在 Prompt 里加一句“請簡短回答”不就行了區別很大。方法原理問題直接要求“簡短”模型自行判斷壓縮程度效果不穩定容易丟失關鍵信息或仍然冗長截斷輸出切斷長文本破壞語義完整性摘要/壓縮中間結果用另一輪 LLM 調用壓縮內容增加額外 token 消耗且摘要也可能冗余敘事約束固定輸出結構限制粒度需要設計模板但效果可預測、可復用敘事約束的優勢在于“可預期”。你規定模型輸出 3 章、每章 3 條列表它就會接近這個結構。你可以提前估算輸出 token 上限這對成本控制是非常有價值的。4. 敘事約束的四種落地方式敘事約束不是一個單一技巧而是一類方法的合集。在實際工程中常見的有四種落地方式4.1 輸出結構約束這是最直接的一種。在 system prompt 里明確告訴模型輸出必須包含哪些章節、章節的順序是什么、每個章節用什么樣的排版。示例請將下面的會議紀要整理為項目周報并嚴格遵守以下約束 1. 只輸出三個章節# 本周進展、# 問題與風險、# 下周計劃。 2. 每個章節使用無序列表不允許出現段落式描述。 3. 不要輸出任何總結、建議或客套話。結構約束的本質是“定義模板”。模型在模板內填充內容時不會浪費 token 在過渡句上。4.2 輸出格式約束把輸出格式限定為 JSON、CSV、YAML 等機器可讀格式。這不僅能壓縮 token還能讓下游程序直接解析減少額外 JSON 解析和清洗成本。示例請輸出 JSON 格式字段為 { summary: 不超過50字的一句話總結, items: [不超過20字的關鍵進展], risk: 無則填null }需要注意JSON 的字段名本身也會消耗 token所以字段名盡量短。但可讀性也不能太差團隊內部需要約定一套通用的短字段命名。4.3 思維鏈壓縮推理任務中思維鏈Chain of Thought能提升效果但也會帶來大量中間 token。敘事約束在這里的作用是保留關鍵推理步驟刪除重復推理過程??梢赃@樣約束請分步驟給出推理過程但每個步驟不超過一行并且只保留與結論直接相關的推理。這屬于“輕量約束”保留了思維鏈的能力但限制了它的膨脹。4.4 多輪上下文壓縮這個更適合 Agent 場景。當對話歷史超過一定長度時用一次帶敘事約束的 LLM 調用把歷史壓縮成“關鍵事實列表”而不是用原始文本繼續拼接。示例壓縮指令請將以下多輪對話壓縮為事實清單 1. 每行一個事實不超過20字。 2. 只保留對后續任務有影響的決定、結論和未解決問題。 3. 不要輸出對話過程的描述。這樣在后續調用中輸入歷史從幾千 token 降到幾百 token。壓縮本身雖然消耗一次調用但總體上通常是節省的而且上下文精度更高。5. 完整示例用敘事約束降低 LLM Token 消耗前面講了概念和方法這一節用一個可以實際運行的腳本驗證敘事約束對 token 的影響。5.1 環境準備建議使用 Python 3.9。需要安裝兩個依賴pip install openai tiktoken如果你還沒有設置 API Key先設置環境變量export OPENAI_API_KEY你的_API_KeyWindows 下使用set OPENAI_API_KEY你的_API_Key文中下面的示例使用 OpenAI 接口風格模型選擇上建議使用你實際可用的模型比如gpt-4o-mini。腳本里通過變量統一配置方便替換。5.2 完整實驗腳本場景把一段開發記錄整理成技術周報。對比“無約束”和“敘事約束”兩種模式下輸出 token 的差異。# 文件路徑narrative_constraint_demo.py import os import textwrap import tiktoken from openai import OpenAI client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) encoder tiktoken.get_encoding(cl100k_base) # 未約束版本 FREESTYLE_SYSTEM ( 你是技術團隊負責人請根據開發記錄整理一份技術周報。 要求表達自然、內容完整、邏輯通順。 ) # 敘事約束版本 CONSTRAINED_SYSTEM textwrap.dedent(\ 請把開發記錄整理成技術周報并嚴格遵守以下敘事約束 1. 只輸出三個章節# 本周進展、# 問題與風險、# 下周計劃 2. 每個章節使用無序列表每項不超過 25 字 3. 不輸出問候語、總結段落、補充解釋 4. 總列表項不超過 8 個。 ) DEV_RECORDS textwrap.dedent(\ 周一搭建日志采集接入 order-service 與 payment-service。 周二完成訂單超時取消任務覆蓋測試通過。 周三修復支付回調偶發超時根因是數據庫連接池不足。 周四訂單服務 QPS 壓測由 800 提升到 1200。 周五數據看板聯調完成導出按鈕樣式待調。 ) def call_and_count(system_prompt: str, user_content: str, model: str gpt-4o-mini): 調用模型并返回生成文本和 token 使用量 resp client.chat.completions.create( modelmodel, messages[ {role: system, content: system_prompt}, {role: user, content: user_content}, ], temperature0.3, ) output_text resp.choices[0].message.content return output_text, resp.usage def local_prompt_token_count(): 本地統計兩類 prompt 的輸入 token方便不調用 API 也能對比 free_prompt FREESTYLE_SYSTEM \n DEV_RECORDS con_prompt CONSTRAINED_SYSTEM \n DEV_RECORDS return len(encoder.encode(free_prompt)), len(encoder.encode(con_prompt)) if __name__ __main__: free_prompt_tk, con_prompt_tk local_prompt_token_count() print(f未約束 prompt token: {free_prompt_tk}) print(f約束 prompt token: {con_prompt_tk}) print(\n 未約束版本 ) free_text, free_usage call_and_count(FREESTYLE_SYSTEM, DEV_RECORDS) print(free_text) print(f\n[token] prompt{free_usage.prompt_tokens}, fcompletion{free_usage.completion_tokens}, ftotal{free_usage.total_tokens}) print(\n 敘事約束版本 ) con_text, con_usage call_and_count(CONSTRAINED_SYSTEM, DEV_RECORDS) print(con_text) print(f\n[token] prompt{con_usage.prompt_tokens}, fcompletion{con_usage.completion_tokens}, ftotal{con_usage.total_tokens}) if con_usage.completion_tokens and free_usage.completion_tokens: reduction (1 - con_usage.completion_tokens / free_usage.completion_tokens) * 100 print(f\n輸出 token 下降比例: {reduction:.1f}%)5.3 腳本關鍵邏輯說明腳本分為三部分本地計算 prompt 的 token 數。通過 tiktoken 估算輸入側的差異。在這個例子里約束版 system prompt 會更長一些這是因為它包含了詳細規則但這部分成本是一次性且可復用的。使用同一個開發記錄分別調用兩次模型。一次“自由發揮”一次“敘事約束”。輸出文本本身打印出來可以直觀感受兩者差異。通過 API 返回的usage字段精確獲得輸出 token 數并計算下降比例。這里有一個容易被忽略的細節如果你在系統提示詞中寫了一套敘事約束它會被復用到后續所有調用中所以 prompt 本身多消耗的 token 是值得的。只要約束能讓每次輸出節省 200 token調用 10 次就節省了 2000 token遠大于 prompt 增加的部分。5.4 運行與驗證運行命令python narrative_constraint_demo.py如果一切正常你會看到兩類輸出文本和 token 統計。判斷實驗是否成功的標準兩類版本都生成了周報內容。約束版的輸出結構嚴格符合 3 章列表要求。約束版輸出 token 明顯少于未約束版。約束版的正文仍然覆蓋了開發記錄中的關鍵事實日志接入、超時取消、支付回調修復、QPS 提升、數據看板聯調。這最后一點最重要。Token 減少的代價不能是信息損失。約束后的輸出應該更精煉而不是更殘缺。6. 運行結果與效果驗證以示例中的開發記錄和模型表現來看典型結果大致如下模式輸出 token 范圍優點風險未約束700 - 1100表達自然閱讀體驗好存在大量過渡句和重復描述敘事約束150 - 250token 少結構可預期便于程序解析需要人工確認信息覆蓋度如果你運行后的下降比例恰好是 70% 左右說明任務冗余度較高如果只有 30%說明這個任務本身已經足夠緊湊比如輸入本身是高度結構化數據。79% 這個數字之所以會被用來做標題通常對應的是最典型的“周報/紀要/文檔摘要”類任務。不要把它當作所有場景都能復現的結果。如何更嚴謹地驗證約束是否有效建議做一個 20 條樣本的小測試準備 20 個不同的輸入文本。每條分別用自由模式和約束模式生成。記錄輸出 token。人工檢查每條輸出是否覆蓋關鍵信息點。如果平均下降比例在 40% 以上且 90% 的樣本沒有丟失關鍵信息說明這套約束模板適合你的業務可以放到生產環境里復用。如果某些樣本丟失信息就要調整約束規則的粒度比如把“每項不超過 25 字”放寬到“不超過 40 字”。7. 常見問題與排查方法在實際工程中敘事約束并不是加上就有效果。下面幾個問題非常典型問題現象可能原因排查方式解決方案模型輸出不符合指定結構約束規則模糊或相互沖突檢查 system prompt是否出現“要簡潔又要完整”這類矛盾表述將規則拆成可執行的序號條款并配一個輸出示例信息丟失關鍵點沒寫出來約束過強列表項數量限制過嚴對比約束前后輸出的信息點覆蓋數量適當放寬字數上限或允許“超出部分單獨用小字補充”模型仍然輸出大段解釋約束只寫了“不要輸出解釋”但沒有給出替代結構檢查是否定義了“如果必須解釋應放在哪個字段”增加一個comment字段把必須的解釋集中放在末尾本地 token 統計與 API 計費不一致tiktoken encoding 選擇與模型不匹配使用模型對應的 encoding以 API 返回 usage 為準計費與優化決策以 API 返回數據為準本地統計只做估算用了約束后質量下降temperature 設置過高輸出隨機性增加在約束場景下調低 temperature 到 0.2 - 0.4生產環境建議 temperature 固定不用默認值結構化輸出偶爾不是合法 JSON模型沒有穩定遵循 JSON 格式開啟 response_formatjson_object 等結構化輸出能力同時做 JSON 解析失敗重試不要把格式正確性完全交給模型7.1 一個容易踩坑的案例有一種錯誤做法是把敘事約束寫在用戶消息里而不是系統提示詞里。當約束出現在用戶消息中時模型可能把約束和業務輸入混在一起理解優先級沒有 system prompt 高。生產環境建議把穩定的敘事約束模板放在 system prompt 中把每次變化的文本放在 user message 中。這樣既保證約束穩定生效又方便統一更新模板。8. 最佳實踐與工程建議敘事約束是一把雙刃劍。約束太強模型像在“填表格”句子生硬約束太弱token 節省不明顯。以下是長期實踐下來比較有效的經驗。8.1 設計約束的五個原則原則一結構優先于措辭。先規定章節和列表再規定每項字數不要顛倒。原則二給正例比給反例更有效。與其說“不要寫總結”不如直接說“最后一個章節必須是下周計劃”。原則三控制規則數量。超過 5 條時模型容易顧此失彼建議合并同類項。原則四每條規則都要可驗證。比如“每項不超過 25 字”是可客觀判斷的“語言要流暢”不是。原則五預留一個自由字段。完全不放行會導致信息損失一個comment字段能避免模型把沒說清楚的話強行塞進別處。8.2 敘事約束與結構化輸出的配合如果你的項目已經使用了 structured output 或 function calling敘事約束依然有位置。它不是替代結構化輸出而是專門針對“非結構化生成內容”的壓縮手段。當模型必須返回一段可讀文本時結構化輸出無法約束文本內部的表達方式敘事約束會補上這個空缺。所以兩者是互補關系不是競爭關系。8.3 生產環境部署建議在生產環境上token 優化不能只靠“改了 prompt 就上線”。建議做三件事把約束模板作為配置項而不是硬編碼在代碼里。這樣調整字數限制或者章節名時不用改代碼重新發布。對每次調用記錄prompt_tokens和completion_tokens形成消耗曲線。當改版后 token 不降反升可以及時回溯。定期抽查 5% 的約束輸出確認信息覆蓋度沒有悄悄下降。模型版本升級后行為可能變化同一套約束模板在新模型上不一定同樣好用。8.4 安全與合規提醒在 prompt 中加入約束時不要將業務敏感信息直接寫在 system prompt 里。尤其是約束模板是要被復用的它可能被記錄在日志中也要被團隊成員評審。敏感字段應該通過變量注入到 user message 或專門的上下文字段中。另外如果你的約束模板變成了團隊內部的“標準做法”記得維護一份變更記錄。你新增一個字段可能影響下游解析邏輯需要同步通知調用方。9. 總結與后續學習方向敘事約束的核心價值不是讓你把所有 LLM 輸出都壓成“電報體”而是給你一種可預期、可量化的 token 控制手段。Semantic Thermodynamics 這個方向表面上是借用了熱力學的概念本質上是在提醒你一件事面對 LLM約束不是創新的枷鎖它是信息密度的杠桿。一句“請簡短回答”是模糊的意圖而一套“三章結構 列表 字數上限”的敘事約束才是真正能落到工程里的方案。建議你接下來做三件事用本文的腳本把你業務中最高頻的一個生成場景測一遍拿到真實 token 下降數據。根據業務特點設計一套專屬約束模板先在小流量下驗證信息覆蓋度。把約束模板配置化并接上 token 消耗監控觀察長期收益。如果你的任務本身就是高度結構化的比如生成 JSON 或者調用工具參數敘事約束的收益會小一些。但如果你在做周報生成、會議摘要、Agent 多輪壓縮、文檔整理這類“高冗余文本”任務這套方法很可能是目前成本收益比最高的優化方式。