
之前在做 AI 智能體項目時我一直被一個問題困擾模型一旦訓練完成能力邊界似乎就固定了。遇到模型答不出來的題要不重新訓練要不微調(diào)成本高周期長。后來接觸到一個思路——不訓練模型而是在推理階段投入更多計算讓模型自己“多想幾步”“多試幾次”效果竟然出奇地好。這個思路就是測試時計算Test-Time Compute也是斯坦福 CS329A《自我改進 AI 智能體》第二講的核心內(nèi)容。這篇文章就基于這門課第二講的知識脈絡結(jié)合并行采樣與驗證機制完整梳理測試時計算的核心原理、實現(xiàn)方式和工程落地注意事項。無論你在做 AI 智能體開發(fā)還是想提升大模型在推理任務上的表現(xiàn)這篇文章都值得收藏。1. 背景與核心概念1.1 為什么“不訓練也能讓 AI 變強”我們先想一個場景。你用大模型解一道競賽數(shù)學題模型直接給出一個答案。這個答案可能是對的也可能是錯的但模型通常表現(xiàn)得非常自信。更麻煩的是你讓它重新算一遍它可能給出完全不同的答案而且依然自信。這說明什么說明單次推理只是一個“采樣”過程。大模型本質(zhì)上是根據(jù)輸入概率分布來生成文本的同樣的 prompt不同的隨機種子、不同的采樣參數(shù)得到的結(jié)果可能不一樣。既然單次采樣不靠譜我們能不能讓模型多采樣幾次然后從多個結(jié)果中選出最可靠的那個這正是測試時計算要做的事。它的核心思想是在模型推理階段通過增加計算量來提升輸出質(zhì)量而不是修改模型權重。換句話說模型還是那個模型但我們在使用方式上做文章。在很多測試集上這種“推理時多想幾步”的方式往往能讓模型在數(shù)學推理、代碼生成、復雜規(guī)劃等任務上的表現(xiàn)明顯提升。相比訓練一個新模型這種方式成本低、見效快且不需要額外準備訓練數(shù)據(jù)。1.2 測試時計算是什么定義與常見形式測試時計算Test-Time Compute通常指在模型推理inference階段投入額外的計算資源通過多次采樣、搜索、驗證、反思、修正等策略獲得比單次推理更高質(zhì)量的輸出。它的常見形式包括并行采樣Parallel Sampling讓模型對同一個問題生成多個候選答案。多數(shù)投票/自一致性Self-Consistency對多個答案進行投票選出現(xiàn)次數(shù)最多的結(jié)果。驗證器Verifier訓練或利用一個評分模型對候選答案打分選出分數(shù)最高的結(jié)果。思維搜索Search over Thoughts在思維鏈的每一步做多種可能性的搜索類似樹搜索。自我反思與修正Self-Refinement讓模型自己檢查錯誤并根據(jù)反饋重新生成答案。這篇文章重點展開并行采樣與驗證這是最容易落地、也最容易被忽視的兩塊內(nèi)容。1.3 適用場景與局限測試時計算并非萬能。它最適用的場景是那些存在“標準答案”或者“結(jié)果可驗證”的任務比如數(shù)學題、邏輯推理、代碼單元測試、SQL 查詢等。這類任務的共同特點是候選答案很多但我們可以通過某種方式判斷哪個答案更好。相反如果任務本身是開放式的比如“寫一首詩”“總結(jié)一下這篇文章的風格”多個答案沒有絕對的對錯測試時計算的價值就相對有限因為驗證環(huán)節(jié)很難設計。另外還要注意測試時計算是以更多算力開銷換質(zhì)量提升。在實際工程中需要評估延遲和成本是否能接受。2. 訓練階段計算與測試時計算的對比2.1 訓練階段計算離線、批量、權重更新先看傳統(tǒng)方式。訓練階段計算發(fā)生在模型上線之前通過大量樣本計算梯度并更新模型權重。這個過程的特點是離線進行時間跨度長。一次性投入大量 GPU 算力。結(jié)果是一組固定的權重。模型能力在訓練結(jié)束后基本定型。訓練階段計算解決的是“模型學會多少知識”的問題。模型不會做某類題通常是因為訓練數(shù)據(jù)里沒見過或者模型容量不夠。這種情況下只能通過重新訓練或微調(diào)來改變。2.2 測試時計算在線、采樣、選擇測試時計算則完全不同。模型權重保持不動我們在推理階段做額外的計算讓模型生成多個候選結(jié)果。對候選結(jié)果進行驗證或篩選。選出一個最終答案或者綜合多個答案。這個過程是“在線”的每次請求都可能產(chǎn)生不同的計算路徑。它解決的是“模型其實會但單次沒發(fā)揮好”的問題。這兩類計算方式并不互斥而是可以疊加。訓練階段決定了模型能力的上限測試時計算則盡量逼近這個上限。2.3 兩者如何配合使用在實際項目中合理的做法是先用訓練階段的計算把模型能力提到足夠高。對于仍然不穩(wěn)定的任務在推理階段引入測試時計算。當一個任務經(jīng)過測試時計算仍然無法解決時才考慮重新訓練或微調(diào)。這樣做的好處非常明顯訓練一次模型成本很高而測試時計算按量付費。對于低頻但高價值的任務測試時計算幾乎是性價比最高的優(yōu)化手段。3. 核心機制一并行采樣3.1 并行的含義多個獨立推理并行采樣的思路很直接同一個 prompt同時讓模型生成 N 個答案而不是只生成一個。這里的“并行”既可以是真正的多路同時推理也可以是同一模型多次順序采樣只要每次采樣保持隨機性效果類似。打個比方你問一個聰明但偶爾粗心的人一道題他答一次可能失誤你讓他答 10 次然后統(tǒng)計出現(xiàn)次數(shù)最多的答案正確率通常會高很多。在代碼實現(xiàn)上并行采樣通常有兩種方式顯式循環(huán)在代碼中調(diào)用 N 次模型接口。批量輸入構造 N 條相同的 prompt 一起請求。3.2 溫度參數(shù)與多樣性并行采樣要發(fā)揮作用關鍵在于“樣本之間的多樣性”。如果采樣出來的 N 個答案一模一樣那投票和驗證都沒有意義。控制多樣性的核心參數(shù)是溫度temperature。溫度越高模型生成時的隨機性越大候選答案越多樣但同時單條答案的質(zhì)量可能下降溫度越低輸出越確定但多樣性不足。實際使用中常見做法是把采樣溫度設置在 0.7 到 1.0 之間具體需要根據(jù)任務調(diào)節(jié)。數(shù)學推理類任務建議溫度略低比如 0.7 左右創(chuàng)意生成類任務可以高一點比如 0.9 以上。另一個注意點是 top-p核采樣它控制候選 token 的累積概率。一般情況下并行采樣時適當調(diào)低 top-p 可以減少低質(zhì)量候選但不要在并行采樣場景把 top-p 設得過低否則多樣性會受影響。3.3 并行采樣的參數(shù)組合一個典型的并行采樣配置看起來像這樣參數(shù)說明建議值n采樣次數(shù)4 到 16temperature隨機性0.7 到 1.0top_p核采樣概率0.9 到 1.0max_tokens最大生成長度根據(jù)任務調(diào)整stop停止符按需配置下面是一個調(diào)用 OpenAI 兼容接口進行并行采樣的 Python 示例。示例思路可用于任意兼容接口實際運行需要你配置自己的 API Key 和服務地址。# 文件路徑parallel_sample.py import openai client openai.OpenAI( api_keyyour-api-key, base_urlyour-base-url ) prompt 一個三角形的三個內(nèi)角分別是 2x、3x 和 4x求 x 的值。 def parallel_sample(prompt: str, n: int 5) - list[str]: responses [] for i in range(n): try: resp client.chat.completions.create( modelyour-model-name, messages[ {role: system, content: 你是一個嚴謹?shù)臄?shù)學解題助手。}, {role: user, content: prompt} ], temperature0.7, top_p0.9, max_tokens500 ) responses.append(resp.choices[0].message.content) except Exception as e: print(f第 {i1} 次采樣失敗: {e}) return responses if __name__ __main__: answers parallel_sample(prompt, n5) for idx, ans in enumerate(answers, 1): print(f候選 {idx}:\n{ans}\n)這里有幾個值得注意的地方每次采樣都是獨立請求互不影響。采樣過程中的異常需要單獨捕獲避免單次失敗導致整個流程中斷。在實際工程中可以把循環(huán)改成線程池或異步任務提高吞吐。4. 核心機制二驗證器與選擇策略并行采樣生成了很多候選答案但哪個更好這時候就需要驗證器。4.1 為什么要驗證采樣只是產(chǎn)生候選采樣只解決“有沒有更多備選”的問題并沒有解決“哪個備選更好”的問題。直接隨機選一個候選正確率并不會提升多少。所以必須有一個選擇策略。選擇策略大致分兩類無需額外訓練的比如多數(shù)投票、啟發(fā)式規(guī)則。需要訓練的比如訓練一個驗證器模型對候選結(jié)果進行打分。兩者各有優(yōu)劣。無需訓練的方案上手快適用面廣訓練驗證器則需要額外數(shù)據(jù)標注和訓練成本但通常效果更好。4.2 不需要訓練的驗證方案多數(shù)投票與自一致性先看多數(shù)投票Majority Voting也叫自一致性Self-Consistency。核心邏輯是如果多個獨立采樣得到同一個答案那么這個答案正確的概率更大。舉個例子。5 次采樣結(jié)果中有 3 次答案是 20 度1 次是 25 度1 次是 30 度。按照多數(shù)投票規(guī)則最終答案是 20 度。但這里有一個關鍵細節(jié)不能對完整文本做簡單去重因為模型的措辭可能不同。更合理的做法是提取“最終答案”部分參與投票。下面是一個簡單的多數(shù)投票實現(xiàn)# 文件路徑majority_vote.py import re from collections import Counter def extract_answer(text: str) - str: # 簡化版抽取規(guī)則實際項目需要根據(jù)任務定制 patterns [ r答案是[:]\s*([^\n。]), r最終答案[:]\s*([^\n。]), r\bx\s*\s*([^\n。]) ] for pattern in patterns: match re.search(pattern, text) if match: return match.group(1).strip() return text.strip() def majority_vote(answers: list[str]) - tuple[str, int]: extracted [extract_answer(ans) for ans in answers] counter Counter(extracted) best_answer, count counter.most_common(1)[0] return best_answer, count if __name__ __main__: sample_answers [ 設三個角分別為 2x、3x、4x三角形內(nèi)角和為 180 度。9x 180所以 x 20。答案是 20。, 2x 3x 4x 1809x 180x 20。最終答案20。, x 25。, 內(nèi)角和為 180 度。2x3x4x9x180最終 x 20。, 這道題中 x 20 度。 ] result, cnt majority_vote(sample_answers) print(f多數(shù)投票結(jié)果: {result}出現(xiàn)次數(shù): {cnt})多數(shù)投票的適用前提是任務有明確的答案形式且模型大部分情況下能給出正確答案。如果模型本身能力較弱正確率低于隨機水平投票也救不回來。4.3 需要訓練的驗證器結(jié)果驗證器與過程驗證器當任務答案形式復雜或者候選答案難以自動抽取時可以考慮訓練一個驗證器。驗證器本質(zhì)上是一個二分類模型或打分模型輸入是“問題 候選答案”輸出是一個分數(shù)表示這個答案的可信度。常見做法有兩種結(jié)果驗證器Outcome Reward Model, ORM只看最終答案是否正確給最終結(jié)果打分。過程驗證器Process Reward Model, PRM在一步步推理中逐步打分能定位到錯誤步驟但訓練成本更高。結(jié)果驗證器適合大多數(shù)工程場景過程驗證器更適合推理鏈路長、需要定位錯誤位置的任務。驗證器的訓練數(shù)據(jù)怎么來常見思路是用大模型為同一個問題生成多個候選答案然后通過人工標注或者用權威答案自動比對給每個候選打上正確/錯誤標簽再用排序損失訓練一個打分模型。4.4 用大模型自身做驗證如果不方便訓練驗證器還有一種折中方案讓大模型充當驗證器。做法是把問題、候選答案一起給模型要求它判斷這個答案是否正確或者對多個答案進行排序。這種方式無需訓練效果取決于大模型自身的判斷能力。示例 prompt請判斷以下解題過程是否正確。如果正確請回答“正確”如果不正確請指出錯誤原因。 題目一個三角形的三個內(nèi)角分別是 2x、3x 和 4x求 x 的值。 候選解答 2x 3x 4x 180 9x 180 x 20 請給出你的判斷。這種方式的好處是靈活缺點是會額外消耗 token而且大模型可能“看不出”錯誤。在工程中可以先做字符串規(guī)則抽答案再用 LLM 驗證沒有抽到答案的候選這樣成本更可控。5. 完整實戰(zhàn)案例為數(shù)學推理任務加入采樣與驗證流程現(xiàn)在把前面講的內(nèi)容串起來實現(xiàn)一個“采樣 → 投票/驗證 → 最終輸出”的完整流程。示例任務仍然是數(shù)學題但這套流程可以擴展到代碼生成、SQL 生成、日志分析等任務。5.1 流程設計整體流程分四步接收用戶問題。并行采樣生成 N 個候選答案。對候選答案做歸一化和抽取。用多數(shù)投票篩選最終答案若投票無法收斂則用 LLM 驗證。5.2 完整代碼# 文件路徑test_time_pipeline.py import re from collections import Counter import openai client openai.OpenAI( api_keyyour-api-key, base_urlyour-base-url ) SYSTEM_PROMPT 你是一個嚴謹?shù)臄?shù)學解題助手。請分步推理并在最后單獨一行輸出最終答案。 def generate_answer(question: str, temperature: float 0.7) - str: resp client.chat.completions.create( modelyour-model-name, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: question} ], temperaturetemperature, max_tokens500 ) return resp.choices[0].message.content def extract_answer(text: str) - str: # 優(yōu)先抽取“最終答案”后面的內(nèi)容 patterns [ r最終答案[:]\s*([^\n。]), r答案是[:]\s*([^\n。]), rx\s*\s*([^\n。]), r([-]?\d(?:\.\d)?) ] for pattern in patterns: match re.search(pattern, text) if match: return match.group(1).strip() return text.strip() def majority_vote(answers: list[str]) - tuple[str | None, int]: extracted [extract_answer(ans) for ans in answers] counter Counter(extracted) answer, count counter.most_common(1)[0] if count 1: return None, count return answer, count def llm_verify(question: str, answer: str) - bool: verify_prompt f 請判斷下面的最終答案是否正確。如果正確請回答“正確”否則回答“不正確”。 題目{question} 最終答案{answer} 你的判斷 resp client.chat.completions.create( modelyour-model-name, messages[ {role: user, content: verify_prompt} ], temperature0, max_tokens100 ) content resp.choices[0].message.content return 正確 in content def solve_with_test_time_compute(question: str, n: int 5) - str: # Step 1: 并行采樣 candidates [generate_answer(question) for _ in range(n)] # Step 2: 多數(shù)投票 best_answer, count majority_vote(candidates) if best_answer is not None: return f多數(shù)投票結(jié)果: {best_answer}獲得 {count}/{n} 票 # Step 3: 投票不收斂時用 LLM 驗證 for idx, cand in enumerate(candidates): extracted extract_answer(cand) if extracted and llm_verify(question, extracted): return fLLM 驗證通過: {extracted}來自第 {idx1} 個候選 # Step 4: 兜底返回第一個候選 return f驗證未通過返回第一個候選: {extract_answer(candidates[0])} if __name__ __main__: q 一個三角形的三個內(nèi)角分別是 2x、3x 和 4x求 x 的值。 result solve_with_test_time_compute(q, n5) print(result)5.3 運行結(jié)果說明假設模型本身具備基礎數(shù)學能力那么 5 次采樣中通常會出現(xiàn) 3 到 4 次答案一致的情況程序會直接走“多數(shù)投票”分支并輸出結(jié)果。如果模型能力偏弱5 次采樣各自給出不同答案程序會進入 LLM 驗證分支逐一驗證候選答案直到找到模型自身認可的答案。如果所有候選都無法通過驗證程序返回第一個候選作為兜底避免接口無輸出。這種設計保證了整個流程在工程上是健壯的不會因為某一次采樣異?;蝌炞C異常導致整體失敗。5.4 工程改造建議上面的代碼是一個串行演示版本。在實際服務中建議做以下改造用 asyncio 或線程池并發(fā)發(fā)起采樣請求把 5 次采樣時間從“5 倍單次延遲”壓縮到“接近 1 倍單次延遲”。對候選答案做緩存相同 prompt 在短時間內(nèi)的采樣結(jié)果可以復用。把投票和驗證邏輯封裝成獨立服務方便不同模塊復用。增加超時和重試機制防止某個采樣請求一直阻塞。6. 常見問題與排查思路測試時計算在實踐中會遇到不少問題下面整理幾個高頻場景。問題現(xiàn)象常見原因解決思路采樣結(jié)果千篇一律投票沒有意義溫度參數(shù)過低模型輸出趨于確定調(diào)高 temperature適當降低 top_p多數(shù)投票選出的答案是錯的模型本身能力不足大多數(shù)采樣都錯考慮換更強的模型或訓練任務專用驗證器答案抽取不準確投票失效正則規(guī)則沒覆蓋到模型的表述習慣先觀察 20 條真實輸出再完善抽取規(guī)則并行采樣導致接口延遲過高所有請求串行執(zhí)行用異步或線程池并發(fā)調(diào)用驗證結(jié)果不穩(wěn)定同樣的輸入有時通過有時不通過LLM 驗證時溫度過高隨機性大驗證請求設置 temperature0多次驗證取多數(shù)算力成本暴增采樣數(shù)量 N 設置過大先測 N4 的效果再逐步增加到 8 或 16采樣結(jié)果長度過長token 消耗大沒有限制 max_tokens模型輸出冗長設置合理的 max_tokens并要求“只輸出最終答案”這里重點說一下答案抽取。很多人做多數(shù)投票時直接對完整文本去重結(jié)果發(fā)現(xiàn)同樣的答案因為表達方式不同被當成不同結(jié)果。比較好的辦法是先讓模型在 prompt 中按固定格式輸出比如“最終答案xxx”。代碼里先按固定標記抽取。抽不到再走正則兜底。最后才考慮用 LLM 抽取。這樣做的核心原則是能用規(guī)則解決的事情不要用模型能一次抽準的事情不要二次加戲。7. 最佳實踐與工程建議7.1 采樣數(shù)量 N 不是越大越好N 越大效果通常會更好但收益遞減非常明顯。從實際項目經(jīng)驗看N4 到 N8 時性價比最高N 超過 16 后正確率提升趨于平緩成本和延遲卻線性增長。建議上線前先做小規(guī)模實驗畫出“N 值-正確率”曲線再決定線上參數(shù)。7.2 溫度參數(shù)要與任務匹配數(shù)學推理、代碼生成建議 temperature0.7top_p0.9。開放對話、創(chuàng)意寫作建議 temperature0.9 以上。驗證環(huán)節(jié)建議 temperature0。同一套并行采樣邏輯用在不同任務上需要單獨調(diào)參不要追一個參數(shù)走天下。7.3 驗證器要關注“錯誤接受率”用 LLM 做驗證時最容易出問題的不是把好答案拒掉而是把錯誤答案放進來。比如模型對錯誤答案也說“正確”這時候驗證就失去了意義。在工程上可以引入“多次驗證取多數(shù)”或者“驗證規(guī)則雙重校驗”來降低錯誤接受率。在安全敏感的領域比如代碼生成、數(shù)據(jù)庫 SQL 生成建議加上沙箱測試而不是只靠 LLM 判斷。7.4 并行采樣的資源控制并行采樣會放大對底層模型的調(diào)用壓力。建議在代碼中加入并發(fā)數(shù)限制例如用信號量控制最大并發(fā)請求數(shù)避免短時間大量請求打爆服務。下面是一個簡單的并發(fā)控制示例# 文件路徑concurrency_control.py import asyncio import openai client openai.OpenAI( api_keyyour-api-key, base_urlyour-base-url ) semaphore asyncio.Semaphore(3) async def guarded_generate(prompt: str) - str: async with semaphore: loop asyncio.get_event_loop() def sync_call(): resp client.chat.completions.create( modelyour-model-name, messages[{role: user, content: prompt}], temperature0.7, max_tokens300 ) return resp.choices[0].message.content return await loop.run_in_executor(None, sync_call) async def parallel_generate(prompt: str, n: int 5) - list[str]: tasks [guarded_generate(prompt) for _ in range(n)] return await asyncio.gather(*tasks, return_exceptionsTrue) if __name__ __main__: results asyncio.run(parallel_generate(解釋一下什么是測試時計算。, n5)) for idx, res in enumerate(results, 1): if isinstance(res, Exception): print(f候選 {idx} 異常: {res}) else: print(f候選 {idx}: {res})7.5 日志與可復現(xiàn)性測試時計算引入了隨機性會導致同一個請求在不同時間得到不同的結(jié)果。這在調(diào)試時非常痛苦。建議在日志中記錄每個請求的prompt 版本溫度和 top_p 參數(shù)采樣次數(shù)每個候選答案及其抽取結(jié)果最終采用的候選編號同時在采樣時為每次請求生成一個隨機種子方便復現(xiàn)問題。7.6 安全與權限邊界在真實項目中如果測試時計算用于生產(chǎn)環(huán)境涉及 SQL 生成、命令執(zhí)行、代碼生成等場景必須嚴格限制最終結(jié)果的執(zhí)行權限。建議所有生成結(jié)果默認不自動執(zhí)行先經(jīng)過人工確認。代碼生成結(jié)果必須在沙箱環(huán)境中進行測試。數(shù)據(jù)庫 SQL 生成結(jié)果必須經(jīng)過只讀賬號驗證并在測試庫執(zhí)行。在安全敏感操作中加入審批流程。測試時計算只是提升模型輸出質(zhì)量的手段不應該繞過任何已有的安全邊界。7.7 評估線上效果時要注意評估測試時計算的效果不能只看正確率。需要同時觀察延遲P95 延遲是否可接受。成本單次請求的 token 消耗和 API 費用。錯誤模式是多數(shù)投票能解決還是必須上驗證器?;赝藱C制驗證不通過時是否有兜底策略。建議先在離線數(shù)據(jù)集上跑通全流程再小流量上線對比。8. 總結(jié)與后續(xù)學習方向測試時計算的核心邏輯并不復雜既然單次采樣不可靠那就多做幾次再引入驗證機制來篩選。它把“訓練時多花算力”變成了“推理時多花算力”在沒有改變模型權重的前提下提升了模型在推理任務上的可靠性。本文講清楚了幾件事什么是測試時計算以及它與訓練階段計算的本質(zhì)區(qū)別。并行采樣如何通過溫度等參數(shù)控制多樣性。驗證器的兩種路線無需訓練的多數(shù)投票以及需要訓練的驗證器模型。一個完整的采樣投票LLM 驗證流程及其代碼實現(xiàn)。工程落地時關于延遲、成本、護欄和評估的注意事項。下一步建議你在自己的任務上先跑一遍并行采樣基線記錄 N1、N4、N8 時的正確率和成本曲線。確認收益后再考慮是否引入訓練驗證器。如果對思維搜索和過程驗證器感興趣還可以進一步看樹搜索在推理任務中的應用。如果你在做 AI 智能體開發(fā)或者正在搭建模型評估流程建議把文中的 pipeline 代碼改成你自己的任務格式跑一遍。測試時計算是一個“投入小、見效快”的優(yōu)化方向值得花一個下午做實驗看看在你的任務上能提升多少。