計eval并hillclimb:從人工打分到自動化評分迭代)
1. 為什么我選擇用 Claude 來做 eval 而不是人工打分第一次接觸 eval 這個概念是在做一個垂直領(lǐng)域的問答助手時。當(dāng)時團隊里三個人輪流看模型輸出每人每天看兩百條看完還要在表格里打分、寫評語。堅持了不到一周所有人都開始敷衍——同一類錯誤早上打 2 分下午可能就打 3 分因為看麻了。更麻煩的是當(dāng)我把模型從 A 版本換到 B 版本想對比到底有沒有變好發(fā)現(xiàn)兩批評分的人不一樣、標(biāo)準(zhǔn)也不一樣數(shù)據(jù)根本沒法比。這就是我后來轉(zhuǎn)向用 Claude 來設(shè)計 eval 的直接原因。eval 的本質(zhì)不是打分而是把什么叫好這件事變成一套可重復(fù)執(zhí)行的判定邏輯。人工打分最大的問題是不可復(fù)現(xiàn)而 Claude 這類模型恰好擅長做給定標(biāo)準(zhǔn)、逐條判斷的活。你只要把評分標(biāo)準(zhǔn)寫清楚它能以幾乎一致的口徑跑幾千條還能把判斷理由寫出來方便你回頭審計。需要先說清楚的是我這里說的 eval 不是指跑個 benchmark 看個準(zhǔn)確率數(shù)字就完事。那種做法在真實項目里價值有限因為公開 benchmark 和你的業(yè)務(wù)場景往往差得很遠。我關(guān)心的是針對自己業(yè)務(wù)場景的定制化 eval輸入是你真實的用戶 query輸出是模型回答判定標(biāo)準(zhǔn)是你自己定義的好答案長什么樣。這套東西搭起來之后你才能做后面那件更有意思的事——hillclimb也就是一輪一輪把分?jǐn)?shù)往上爬。hillclimb 這個詞直譯是爬坡在模型優(yōu)化語境里指的是拿到一個基線分?jǐn)?shù)后通過改 prompt、改檢索、改后處理、換模型等手段每次只動一個變量看分?jǐn)?shù)有沒有漲漲了就保留沒漲就回退如此反復(fù)迭代。它聽起來很樸素但真正做起來難點全在分?jǐn)?shù)這兩個字上——如果你的 eval 本身不可靠那 hillclimb 就是在爬一座會移動的山越爬越迷茫。所以這篇東西的定位很明確給那些已經(jīng)在用 Claude 做實際項目、想系統(tǒng)化提升輸出質(zhì)量的人看。不管你是做客服機器人、內(nèi)容生成、代碼助手還是數(shù)據(jù)分析只要你的產(chǎn)出質(zhì)量可以被打分這套先設(shè)計 eval、再 hillclimb的方法就能用。我會把每一步為什么這么做、參數(shù)怎么定、坑在哪里都講透你可以直接抄作業(yè)。2. 用 Claude 設(shè)計 eval 的整體思路拆解2.1 先想清楚 eval 要回答什么問題很多人一上來就寫評分 prompt結(jié)果寫出來的東西自己都說不清在評什么。我的經(jīng)驗是動手之前先回答三個問題這個 eval 是用來做絕對判斷還是相對比較絕對判斷是這條回答夠不夠好相對比較是A 版本和 B 版本哪個更好。前者適合設(shè)閾值卡質(zhì)量后者適合 hillclimb 時判斷改動是否有效。我通常兩個都做但分開跑。評分維度有幾個能不能拆開一條回答可能同時涉及準(zhǔn)確性、完整性、語氣、格式。如果揉成一個總分你只知道變差了不知道差在哪。拆成多個維度分別打分hillclimb 時才能精準(zhǔn)定位。判定標(biāo)準(zhǔn)能不能用自然語言描述清楚這是能不能交給 Claude 的關(guān)鍵。如果標(biāo)準(zhǔn)是感覺對就行那 Claude 也幫不了你。你必須能寫出如果回答里出現(xiàn)了未經(jīng)檢索支持的具體數(shù)字準(zhǔn)確性扣分這種可執(zhí)行的規(guī)則。我一般會把 eval 拆成 3 到 5 個維度每個維度 1 到 5 分最后加權(quán)成總分。維度太多會讓 Claude 的判斷變模糊太少又定位不到問題。三個維度是我最常用的起點準(zhǔn)確性、完整性、表達質(zhì)量。2.2 為什么用 Claude 當(dāng)裁判而不是規(guī)則腳本有人會問既然標(biāo)準(zhǔn)能寫成規(guī)則為什么不直接寫正則或者關(guān)鍵詞匹配答案是真實回答的多樣性遠超規(guī)則能覆蓋的范圍。比如判斷回答是否切題關(guān)鍵詞匹配只能看有沒有出現(xiàn)某些詞但一條回答可能把所有關(guān)鍵詞都堆上了卻答非所問。這種語義層面的判斷規(guī)則腳本做不了Claude 可以。另一個原因是 Claude 能輸出判斷理由。這一點在調(diào)試時極其重要。當(dāng)分?jǐn)?shù)掉了你不只知道掉了還能看到 Claude 說這條回答在完整性上扣了 2 分因為沒有覆蓋用戶問的第二個子問題。這種可解釋性讓 hillclimb 從盲猜變成了有方向的調(diào)整。當(dāng)然用模型當(dāng)裁判也有代價它本身會有波動會有偏見比如偏好更長的回答。這些后面會專門講怎么壓制。2.3 eval 和 hillclimb 的關(guān)系先有尺子再量身高我見過太多人反過來做先瘋狂調(diào) prompt調(diào)到自己覺得差不多了再想怎么評估。結(jié)果就是沒有基線不知道到底比原來好了還是差了。正確的順序是先把尺子造出來再量身高。具體來說收集一批有代表性的真實輸入我一般用 50 到 200 條太少不具統(tǒng)計意義太多跑起來慢。用 Claude 設(shè)計評分邏輯在這批數(shù)據(jù)上跑出基線分?jǐn)?shù)。人工抽查一部分評分結(jié)果確認 Claude 的判斷和你的直覺一致。確認尺子靠譜后才開始 hillclimb。這個順序不能亂。尺子不準(zhǔn)后面所有迭代都是白費。2.4 數(shù)據(jù)集怎么選才有代表性數(shù)據(jù)集的選擇直接決定 eval 有沒有用。我的做法是從真實日志里采樣而不是自己編。編出來的 case 往往太干凈、太典型跑出來的高分在真實場景里根本復(fù)現(xiàn)不了。采樣時我會刻意覆蓋幾類高頻常規(guī) case占大頭反映日常表現(xiàn)。邊界 case比如超長輸入、空輸入、多語言混雜。已知難點 case之前人工發(fā)現(xiàn)模型容易翻車的那類。對抗 case故意設(shè)計來誘導(dǎo)模型出錯的輸入。這四類按大概 6:1:2:1 的比例混。高頻 case 保證分?jǐn)?shù)反映整體水平難點和對抗 case 保證 eval 有區(qū)分度——如果全是簡單 case所有版本都滿分hillclimb 就沒意義了。3. 核心細節(jié)解析與實操要點3.1 評分 prompt 的寫法把標(biāo)準(zhǔn)寫成可執(zhí)行的規(guī)則評分 prompt 是整個 eval 的心臟。我踩過的最大坑是一開始寫得太籠統(tǒng)比如請判斷這個回答好不好結(jié)果 Claude 給分非常隨機。后來我總結(jié)出一套結(jié)構(gòu)穩(wěn)定很多你是一個嚴(yán)格的評審。請根據(jù)以下標(biāo)準(zhǔn)給回答打分。 【用戶問題】 {question} 【模型回答】 {answer} 【評分維度】 1. 準(zhǔn)確性1-5分 - 5分所有事實陳述都有依據(jù)無編造 - 3分主體正確但有1處細節(jié)存疑 - 1分存在明顯事實錯誤或編造 2. 完整性1-5分 - 5分覆蓋了問題的所有子問題 - 3分覆蓋了主要部分遺漏次要內(nèi)容 - 1分答非所問或嚴(yán)重遺漏 3. 表達質(zhì)量1-5分 - 5分結(jié)構(gòu)清晰無冗余 - 3分能讀懂但有啰嗦或結(jié)構(gòu)混亂 - 1分難以理解 【輸出格式】 先逐維度給出分?jǐn)?shù)和理由最后給出總分三個維度平均。幾個關(guān)鍵點每個分?jǐn)?shù)檔都要有具體描述不能只寫很好一般。Claude 需要錨點才能穩(wěn)定判斷。要求先寫理由再給分。這個順序很重要如果先給分再寫理由Claude 容易先定分再找理由判斷質(zhì)量下降。輸出格式固定方便后面用腳本解析。我一般讓它輸出 JSON解析起來最省事。3.2 用 JSON 輸出讓結(jié)果可解析自然語言的評分結(jié)果沒法直接算平均分所以我會強制 Claude 輸出結(jié)構(gòu)化格式{ accuracy: {score: 4, reason: ...}, completeness: {score: 3, reason: ...}, expression: {score: 5, reason: ...}, total: 4.0 }實測下來只要在 prompt 里給一個明確的 JSON 模板Claude 的格式遵循率能到 95% 以上。偶爾有跑偏的加一句只輸出 JSON不要有其他文字基本能壓住。解析時我會加一層容錯遇到解析失敗的就重跑一次還失敗就標(biāo)記出來人工看。3.3 溫度參數(shù)怎么設(shè)裁判要穩(wěn)不要活這是很多人忽略的一點。當(dāng)裁判的 Claude 應(yīng)該用低溫度我一般設(shè) 0 到 0.2。溫度高了同一條回答兩次評分可能差一分那你的 eval 就不可靠了。我做過對比測試溫度 0.7 時同一批數(shù)據(jù)跑兩遍總分平均差 0.4 分溫度 0 時平均差 0.1 分以內(nèi)。對于 hillclimb 來說0.4 分的波動足以淹沒你辛苦調(diào)出來的真實提升所以低溫度是必須的。另外如果預(yù)算允許我會對同一條數(shù)據(jù)跑 3 次取平均進一步降低波動。雖然成本翻三倍但在關(guān)鍵決策點上值得。3.4 壓制裁判偏見長度偏見和位置偏見Claude 當(dāng)裁判有兩個已知偏見必須主動壓制長度偏見它傾向于給更長的回答更高分哪怕長回答里全是廢話。壓制方法是在評分標(biāo)準(zhǔn)里明確寫冗長但無信息量的內(nèi)容應(yīng)在表達質(zhì)量上扣分并且在完整性維度里強調(diào)覆蓋子問題而不是字?jǐn)?shù)多。位置偏見做 A/B 對比時如果總是把 A 放前面Claude 會略微偏好 A。壓制方法是交換順序跑兩遍A 在前跑一次B 在前跑一次兩次結(jié)果綜合。如果兩次結(jié)論矛盾說明這兩個版本差異不大別急著下結(jié)論。3.5 人工校準(zhǔn)別讓裁判自己說了算Claude 的評分再穩(wěn)也得定期和人工對齊。我的做法是每周抽 20 條自己先盲評再和 Claude 的評分對比。如果某個維度上分歧超過 20%說明評分標(biāo)準(zhǔn)寫得不夠清楚要回去改 prompt。這個校準(zhǔn)環(huán)節(jié)是很多人省掉的但它恰恰是 eval 可信度的來源。沒有校準(zhǔn)你永遠不知道 Claude 的 4 分到底等于你心里的幾分。4. 實操過程與核心環(huán)節(jié)實現(xiàn)4.1 搭一個最小可用的 eval 腳本下面是我常用的骨架用 Python 寫調(diào) Claude 的 API。這里只講結(jié)構(gòu)具體 key 用你自己的。import json import anthropic client anthropic.Anthropic() def score_one(question, answer): prompt f你是一個嚴(yán)格的評審...評分標(biāo)準(zhǔn) resp client.messages.create( modelclaude-sonnet-4-5, max_tokens1024, temperature0, messages[{role: user, content: prompt}] ) text resp.content[0].text try: return json.loads(text) except json.JSONDecodeError: return None # 標(biāo)記失敗后面重跑 def run_eval(dataset): results [] for item in dataset: r score_one(item[question], item[answer]) if r is None: r score_one(item[question], item[answer]) # 重試一次 results.append(r) return results跑完之后算平均分就是你的基線。我一般會把每條明細存成 CSV方便后面分析。4.2 基線分?jǐn)?shù)怎么讀拿到基線分?jǐn)?shù)后別急著看總分。先看分布。如果所有維度都是 4 到 5 分說明你的數(shù)據(jù)集太簡單沒有區(qū)分度得加難點 case。如果某個維度普遍低那就是明確的優(yōu)化方向。我還會看方差。如果同一維度上分?jǐn)?shù)忽高忽低說明模型表現(xiàn)不穩(wěn)定可能對某類輸入特別敏感。這時候要去看那些低分 case 有什么共性。4.3 hillclimb 的迭代節(jié)奏有了基線就可以開始爬坡了。我的節(jié)奏是這樣的一次只改一個變量。改 prompt 就只改 prompt別同時換模型。否則分?jǐn)?shù)變了你不知道是誰的功勞。每次改動跑完整 eval。不要只看幾條 case 就下結(jié)論樣本量不夠會被噪聲騙。記錄每次改動和分?jǐn)?shù)。我用一個表格記列是版本號、改動內(nèi)容、各維度分?jǐn)?shù)、總分。這樣回頭看能發(fā)現(xiàn)哪些改動真的有效。漲了就保留沒漲就回退。聽起來簡單但很多人舍不得回退自己辛苦寫的 prompt結(jié)果越堆越亂。4.4 一個真實的迭代記錄拿我之前做的一個問答助手舉例基線總分 3.2版本改動準(zhǔn)確性完整性表達總分v0基線3.52.83.33.2v1prompt 里加了先列要點再展開3.53.43.53.5v2檢索結(jié)果里加了來源標(biāo)注3.93.43.53.6v3加了不確定就說不確定的指令4.13.33.43.6v4后處理去掉重復(fù)句4.13.33.83.7可以看到v1 主要提完整性v2 主要提準(zhǔn)確性v3 準(zhǔn)確性漲了但完整性略降因為模型更保守了v4 只動后處理就提了表達。每一步都能對應(yīng)到具體維度這就是拆維度評分的好處。4.5 什么時候該停hillclimb 不是無限爬的。當(dāng)連續(xù)兩三次改動總分提升都小于 0.1 分時我就停了。這時候邊際收益已經(jīng)很低繼續(xù)調(diào)不如去優(yōu)化別的環(huán)節(jié)。另外如果某個維度已經(jīng)接近滿分比如 4.8再往上摳意義不大把精力放在低分維度上更劃算。5. 常見問題與排查技巧實錄5.1 評分結(jié)果波動大怎么辦癥狀同一批數(shù)據(jù)跑兩遍總分差 0.3 以上。排查順序先確認溫度是不是設(shè)成了 0。這是最常見的原因。檢查評分標(biāo)準(zhǔn)里有沒有模糊表述比如回答得好這種。有就改成具體規(guī)則??词遣皇悄承?case 本身就有歧義導(dǎo)致 Claude 每次理解不同。這類 case 要么刪掉要么把標(biāo)準(zhǔn)寫得更細。如果以上都排除了考慮跑 3 次取平均。5.2 Claude 總是給高分區(qū)分度不夠癥狀所有版本都是 4 分以上看不出差異。原因評分標(biāo)準(zhǔn)太寬松或者數(shù)據(jù)集太簡單。解決把每個維度的 5 分標(biāo)準(zhǔn)寫得更苛刻比如5 分要求無任何可改進之處。同時往數(shù)據(jù)集里加對抗 case。我還會在 prompt 里加一句請嚴(yán)格打分不要因為回答整體不錯就忽略細節(jié)問題實測能壓下來 0.3 到 0.5 分。5.3 解析 JSON 老失敗癥狀跑一批數(shù)據(jù)有 10% 以上解析不出來。解決prompt 里給完整的 JSON 模板包括字段名和嵌套結(jié)構(gòu)。加一句只輸出 JSON不要 markdown 代碼塊不要解釋。解析前先做清洗去掉可能的前后文字。實在不行用正則從文本里摳出 JSON 部分。我現(xiàn)在的做法是解析失敗就重跑一次重跑還失敗就記下來人工處理。一般重跑能救回大半。5.4 分?jǐn)?shù)漲了但實際體驗沒變好癥狀eval 分?jǐn)?shù)從 3.2 漲到 3.8但用戶反饋還是差不多。原因eval 數(shù)據(jù)集和真實分布不一致或者評分維度和用戶真正在意的點不匹配。解決回去看真實日志找出用戶抱怨最多的那類 case看它們在 eval 里占多少。如果占比很低說明數(shù)據(jù)集采樣有偏。另外重新審視評分維度——也許用戶在意的是響應(yīng)速度或格式而你只評了內(nèi)容質(zhì)量。5.5 常見問題速查表問題可能原因快速排查分?jǐn)?shù)波動大溫度高/標(biāo)準(zhǔn)模糊溫度設(shè) 0細化標(biāo)準(zhǔn)分?jǐn)?shù)普遍偏高標(biāo)準(zhǔn)松/數(shù)據(jù)簡單加嚴(yán)標(biāo)準(zhǔn)加對抗 caseJSON 解析失敗格式約束不夠給模板加只輸出 JSON分?jǐn)?shù)漲體驗沒變數(shù)據(jù)有偏/維度錯位對齊真實日志重審維度某維度一直低該環(huán)節(jié)確實弱針對性改 prompt 或檢索5.6 幾個我踩過的坑坑一一開始就想做完美 eval。我花了三天設(shè)計了一套十幾個維度的評分體系結(jié)果跑起來又慢又亂最后砍到三個維度反而好用。eval 要的是夠用不是完備??佣猛粋€模型既生成又評分。這會有自我偏好模型傾向于給自己的輸出高分。如果條件允許生成和評分用不同模型或者至少不同版本。坑三忽略成本。跑一次 eval 如果幾百條數(shù)據(jù)、每條跑三次token 消耗不小。我一般先用小樣本20 條快速驗證改動方向方向?qū)α嗽倥苋???铀母牧?prompt 不記錄。有次我調(diào)了一周回頭想復(fù)現(xiàn)最好的那版發(fā)現(xiàn)沒記清楚改了啥。從那以后我每次改動都寫進版本表包括改動的原文。6. 把 eval 和 hillclimb 變成日常習(xí)慣這套方法跑順之后我把它變成了固定流程每周一跑基線周中做兩三次迭代周五看一周的分?jǐn)?shù)曲線。分?jǐn)?shù)曲線比單次分?jǐn)?shù)更有信息量——如果它整體向上說明方向?qū)θ绻錾虾鱿抡f明改動太隨意或者 eval 不穩(wěn)。有個細節(jié)值得說別把 eval 分?jǐn)?shù)當(dāng)成唯一目標(biāo)。我見過有人為了刷分把 prompt 調(diào)得特別針對 eval 數(shù)據(jù)集結(jié)果真實場景反而變差。eval 是尺子不是終點。每次分?jǐn)?shù)漲了我都會抽幾條真實 case 人工看一眼確認不是應(yīng)試漲分。另外eval 本身也要迭代。業(yè)務(wù)變了、用戶關(guān)注點變了評分維度和數(shù)據(jù)集都得跟著更新。我大概每個月會重新采樣一次數(shù)據(jù)集把過時的 case 換掉。尺子用久了會鈍得定期磨。最后分享一個我常用的小技巧把每次 hillclimb 的改動和分?jǐn)?shù)做成一張折線圖橫軸是版本縱軸是總分和各維度分。看著曲線往上爬比看一堆數(shù)字有成就感得多也更容易發(fā)現(xiàn)哪個維度是瓶頸。這個圖我一般用 matplotlib 幾行代碼就畫出來了跑完 eval 順手生成復(fù)盤的時候特別直觀。