化流水線實(shí)戰(zhàn):用Best of N與LLM as Judge構(gòu)建可控AI應(yīng)用)
1. 項(xiàng)目概述從“幻覺(jué)”到“可控”的工程化實(shí)踐在深度使用大語(yǔ)言模型LLM進(jìn)行應(yīng)用開(kāi)發(fā)時(shí)我們幾乎都遇到過(guò)同一個(gè)令人頭疼的問(wèn)題模型的輸出不穩(wěn)定。有時(shí)它妙語(yǔ)連珠精準(zhǔn)地解決了問(wèn)題有時(shí)卻會(huì)一本正經(jīng)地“胡說(shuō)八道”生成一些看似合理但完全錯(cuò)誤或脫離指令的“幻覺(jué)”內(nèi)容。這種不確定性是LLM從實(shí)驗(yàn)室玩具走向生產(chǎn)級(jí)應(yīng)用的最大障礙之一。我們需要的不是一個(gè)偶爾會(huì)“靈光一現(xiàn)”的黑箱而是一個(gè)輸出穩(wěn)定、質(zhì)量可控、能夠持續(xù)自我改進(jìn)的可靠系統(tǒng)。這正是“LLM自優(yōu)化流水線”要解決的核心問(wèn)題。這個(gè)項(xiàng)目我們稱(chēng)之為“Harness”其目標(biāo)就是構(gòu)建一套工程化的框架將LLM的“自由發(fā)揮”納入一個(gè)可觀測(cè)、可評(píng)估、可優(yōu)化的閉環(huán)流程中。它不是一個(gè)單一的算法而是一套組合了多種技術(shù)思想的工程實(shí)踐核心在于利用LLM自身的能力LLM as Judge和系統(tǒng)化的采樣策略如Best of N Sampling來(lái)自動(dòng)化地篩選、評(píng)估并迭代優(yōu)化LLM的生成結(jié)果。簡(jiǎn)單來(lái)說(shuō)就是讓LLM自己當(dāng)裁判從自己生成的多個(gè)答案中選出最好的那個(gè)并從中學(xué)習(xí)如何變得更好。這個(gè)過(guò)程不是一次性的而是一個(gè)可以持續(xù)運(yùn)行的“流水線”每一次交互都是一次微小的優(yōu)化迭代最終目標(biāo)是讓模型的輸出越來(lái)越符合我們的預(yù)期將“幻覺(jué)”的概率降到最低實(shí)現(xiàn)輸出的高度“可控”。這套流水線非常適合那些對(duì)輸出質(zhì)量有嚴(yán)格要求且需要自動(dòng)化處理的場(chǎng)景。比如自動(dòng)生成產(chǎn)品描述、代碼審查注釋、客服話術(shù)質(zhì)檢、報(bào)告摘要等。如果你正在為L(zhǎng)LM輸出的隨機(jī)性而煩惱希望構(gòu)建一個(gè)更健壯、更可靠的AI應(yīng)用那么這個(gè)手把手構(gòu)建Harness的過(guò)程將為你提供一個(gè)清晰的工程藍(lán)圖。2. 自優(yōu)化流水線的核心架構(gòu)與設(shè)計(jì)思路構(gòu)建一個(gè)有效的自優(yōu)化流水線關(guān)鍵在于設(shè)計(jì)一個(gè)能夠自我評(píng)估、自我修正的閉環(huán)系統(tǒng)。我們不能指望單次提示就能得到完美答案而是要通過(guò)“生成-評(píng)估-選擇-反饋”的循環(huán)來(lái)逼近最優(yōu)解。Harness的架構(gòu)正是圍繞這個(gè)循環(huán)展開(kāi)的。2.1 核心組件與工作流拆解一個(gè)完整的Harness流水線通常包含以下幾個(gè)核心組件它們像工廠的流水線一樣協(xié)同工作提示詞工程與任務(wù)分解器這是流水線的起點(diǎn)。它的任務(wù)不僅僅是發(fā)送一個(gè)簡(jiǎn)單的用戶查詢而是將復(fù)雜任務(wù)分解為L(zhǎng)LM更容易理解和執(zhí)行的子任務(wù)并精心設(shè)計(jì)系統(tǒng)提示詞System Prompt來(lái)約束模型的行為范圍減少無(wú)關(guān)“幻覺(jué)”的產(chǎn)生。例如生成SQL的任務(wù)可能會(huì)先分解為“理解自然語(yǔ)言問(wèn)題”、“識(shí)別實(shí)體和關(guān)系”、“映射到數(shù)據(jù)庫(kù)schema”等步驟。候選生成器這是利用LLM生成能力的環(huán)節(jié)。對(duì)于同一個(gè)輸入我們不會(huì)只生成一個(gè)答案。這里會(huì)采用諸如Best of N Sampling、溫度采樣Temperature Sampling、Top-p采樣等策略讓模型基于相同的提示詞生成N個(gè)例如5個(gè)或10個(gè)不同的候選回答。多樣性是后續(xù)優(yōu)化的基礎(chǔ)。評(píng)估器這是流水線的“大腦”和“裁判”通常由另一個(gè)LLM實(shí)例扮演即LLM as Judge。它的職責(zé)是根據(jù)預(yù)設(shè)的、明確的標(biāo)準(zhǔn)如準(zhǔn)確性、相關(guān)性、完整性、安全性、風(fēng)格一致性等對(duì)生成的N個(gè)候選答案進(jìn)行打分或排序。評(píng)估提示詞的設(shè)計(jì)至關(guān)重要它需要將主觀的質(zhì)量要求轉(zhuǎn)化為客觀、可比較的評(píng)分項(xiàng)。選擇與聚合器評(píng)估器給出評(píng)分后這個(gè)組件負(fù)責(zé)根據(jù)分?jǐn)?shù)選出最優(yōu)答案。策略可以很簡(jiǎn)單比如直接選擇最高分也可以很復(fù)雜比如根據(jù)多個(gè)維度分?jǐn)?shù)加權(quán)計(jì)算或者設(shè)置最低閾值僅輸出達(dá)標(biāo)的結(jié)果。反饋與優(yōu)化循環(huán)這是實(shí)現(xiàn)“自優(yōu)化”的關(guān)鍵。被選出的最優(yōu)答案及其生成路徑包括使用的提示詞、中間步驟等可以被記錄下來(lái)形成一個(gè)高質(zhì)量的數(shù)據(jù)對(duì)。這些數(shù)據(jù)對(duì)可以用于后續(xù)的提示詞迭代優(yōu)化如通過(guò)少量示例進(jìn)行提示詞微調(diào)或者在極端情況下作為微調(diào)數(shù)據(jù)集對(duì)模型本身進(jìn)行微調(diào)從而讓模型在下一次類(lèi)似任務(wù)中表現(xiàn)更好。這個(gè)工作流的核心思想是將不確定性前置并系統(tǒng)化處理。與其祈禱單次生成的結(jié)果是好的不如承認(rèn)生成具有隨機(jī)性然后通過(guò)系統(tǒng)化的方法從多個(gè)可能中找出最好的那個(gè)并讓系統(tǒng)從這個(gè)過(guò)程中學(xué)習(xí)。2.2 為什么是“Best of N” “LLM as Judge”這是Harness架構(gòu)中最經(jīng)典也最有效的組合拳其背后的邏輯非常堅(jiān)實(shí)。Best of N Sampling解決了“廣度”問(wèn)題。LLM的生成本質(zhì)上是概率性的單次采樣就像抽獎(jiǎng)可能抽到“頭獎(jiǎng)”完美答案也可能抽到“謝謝惠顧”幻覺(jué)答案。通過(guò)多次獨(dú)立采樣我們極大地提高了抽中“頭獎(jiǎng)”或至少是“二等獎(jiǎng)”的概率。這比單純調(diào)整溫度參數(shù)更直接有效因?yàn)楦邷囟入m然增加多樣性但也可能直接導(dǎo)致語(yǔ)法混亂低溫度雖然穩(wěn)定但可能陷入局部最優(yōu)、缺乏創(chuàng)意。Best of N在保持生成質(zhì)量基線通常用較低溫度的同時(shí)通過(guò)增加嘗試次數(shù)來(lái)覆蓋更多的可能性空間。LLM as Judge解決了“評(píng)估”問(wèn)題。傳統(tǒng)的評(píng)估方法可能需要編寫(xiě)復(fù)雜的規(guī)則引擎或者依賴(lài)人工標(biāo)注前者不夠靈活后者成本高昂且無(wú)法自動(dòng)化。而使用LLM作為評(píng)估者其優(yōu)勢(shì)在于靈活性你可以用自然語(yǔ)言定義復(fù)雜的評(píng)估標(biāo)準(zhǔn)LLM能夠理解。例如“檢查這個(gè)代碼片段是否存在安全漏洞并解釋原因”。上下文感知LLM可以結(jié)合原始問(wèn)題和生成的答案進(jìn)行整體評(píng)估理解答案是否真正解決了問(wèn)題??蓴U(kuò)展性增加一個(gè)新的評(píng)估維度通常只需要修改評(píng)估提示詞而無(wú)需重寫(xiě)整個(gè)評(píng)估系統(tǒng)。將兩者結(jié)合就形成了一個(gè)高效的“生成-篩選”漏斗生成器提供多樣化的候選評(píng)估器用智能的標(biāo)準(zhǔn)進(jìn)行過(guò)濾和排序最終輸出經(jīng)得起檢驗(yàn)的結(jié)果。這個(gè)組合將LLM從一個(gè)“生成器”升級(jí)為一個(gè)具備“批判性思維”和“質(zhì)量控制”能力的系統(tǒng)。注意LLM as Judge并非完美。它自身也可能產(chǎn)生評(píng)估“幻覺(jué)”比如對(duì)某個(gè)細(xì)微錯(cuò)誤過(guò)度懲罰或?qū)α鲿车e(cuò)誤的答案給予高分。因此評(píng)估提示詞需要精心設(shè)計(jì)有時(shí)甚至需要引入多個(gè)LLM進(jìn)行“交叉驗(yàn)證”或者結(jié)合一些確定性規(guī)則如代碼能否通過(guò)編譯、SQL能否執(zhí)行來(lái)增強(qiáng)評(píng)估的可靠性。3. 手把手構(gòu)建Harness從環(huán)境準(zhǔn)備到核心實(shí)現(xiàn)理論講完了我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我將以一個(gè)具體的場(chǎng)景為例手把手搭建一個(gè)簡(jiǎn)化但功能完整的Harness流水線。我們的場(chǎng)景是構(gòu)建一個(gè)“文本轉(zhuǎn)JSON”的自動(dòng)化工具。用戶輸入一段描述性的文本我們需要輸出結(jié)構(gòu)化的JSON數(shù)據(jù)。這是一個(gè)非常典型的需求在數(shù)據(jù)抽取、表單填寫(xiě)、API參數(shù)生成等場(chǎng)景下廣泛應(yīng)用也極易因?yàn)槟P屠斫馄疃a(chǎn)生“幻覺(jué)”比如生成錯(cuò)誤的字段名或值。3.1 環(huán)境準(zhǔn)備與工具選型工欲善其事必先利其器。我們首先需要搭建開(kāi)發(fā)環(huán)境。編程語(yǔ)言與框架Python是目前LLM生態(tài)最豐富的語(yǔ)言。我們將使用openai庫(kù)或兼容OpenAI API的庫(kù)如litellm來(lái)調(diào)用模型使用pydantic來(lái)定義嚴(yán)謹(jǐn)?shù)臄?shù)據(jù)結(jié)構(gòu)使用langchain或llama-index來(lái)構(gòu)建流水線框架會(huì)更方便但為了理解底層原理我們先從基礎(chǔ)實(shí)現(xiàn)開(kāi)始。模型選擇對(duì)于生成和評(píng)估我們都可以使用同一個(gè)強(qiáng)大的模型例如GPT-4 Turbo、Claude 3或者開(kāi)源的DeepSeek-V2、Qwen2.5。考慮到成本和可控性生成器可以使用性價(jià)比高的模型如GPT-3.5-Turbo、DeepSeek-V2而評(píng)估器為了更高的判斷力建議使用能力更強(qiáng)的模型如GPT-4。對(duì)于本地部署可以選擇Qwen2.5-72B-Instruct這類(lèi)高性能開(kāi)源模型。關(guān)鍵庫(kù)安裝pip install openai pydantic tenacityopenai: 用于調(diào)用API。pydantic: 用于數(shù)據(jù)驗(yàn)證和設(shè)置確保輸入輸出的結(jié)構(gòu)。tenacity: 用于實(shí)現(xiàn)API調(diào)用的重試機(jī)制增強(qiáng)流水線的魯棒性。項(xiàng)目結(jié)構(gòu)規(guī)劃harness_project/ ├── config.py # 存放API密鑰、模型配置等 ├── schemas.py # 用Pydantic定義輸入/輸出JSON Schema ├── generator.py # 候選生成器模塊 ├── evaluator.py # LLM評(píng)估器模塊 ├── selector.py # 答案選擇器模塊 ├── pipeline.py # 主流水線串聯(lián)所有組件 └── main.py # 示例運(yùn)行入口3.2 定義任務(wù)與數(shù)據(jù)結(jié)構(gòu)文本轉(zhuǎn)JSON首先我們必須明確任務(wù)邊界。模糊的任務(wù)會(huì)導(dǎo)致模糊的結(jié)果。我們使用Pydantic來(lái)嚴(yán)格定義我們希望輸出的JSON結(jié)構(gòu)。假設(shè)我們要從產(chǎn)品描述中提取信息輸出結(jié)構(gòu)化的產(chǎn)品數(shù)據(jù)。我們?cè)趕chemas.py中定義from pydantic import BaseModel, Field from typing import List, Optional class ProductInfo(BaseModel): 產(chǎn)品信息數(shù)據(jù)結(jié)構(gòu) product_name: str Field(description產(chǎn)品名稱(chēng)) brand: Optional[str] Field(defaultNone, description品牌如未提及則為None) main_category: str Field(description主要分類(lèi)如電子產(chǎn)品、家居用品) price: Optional[float] Field(defaultNone, description價(jià)格單位元如未提及則為None) key_features: List[str] Field(description關(guān)鍵特性列表至少一項(xiàng)) in_stock: bool Field(description是否有庫(kù)存) # 可以添加自定義驗(yàn)證器 # validator(price) # def price_must_be_positive(cls, v): # if v is not None and v 0: # raise ValueError(價(jià)格必須為正數(shù)) # return v這個(gè)ProductInfo類(lèi)就是我們期望的“完美答案”的藍(lán)圖。它明確了每個(gè)字段的名稱(chēng)、類(lèi)型、是否可選以及描述。這個(gè)Schema有兩個(gè)重要作用用于生成我們可以將其描述作為系統(tǒng)提示詞的一部分極大地約束LLM的輸出格式減少格式錯(cuò)誤。用于驗(yàn)證在評(píng)估階段我們可以先嘗試用Pydantic解析LLM生成的JSON字符串如果解析失敗說(shuō)明格式不正確可以直接給予低分或淘汰。這是第一道也是最硬的過(guò)濾網(wǎng)。3.3 實(shí)現(xiàn)候選生成器接下來(lái)我們實(shí)現(xiàn)generator.py中的CandidateGenerator類(lèi)。它的核心是調(diào)用LLM API并利用溫度等參數(shù)進(jìn)行多次采樣。import openai import json from tenacity import retry, stop_after_attempt, wait_random_exponential from schemas import ProductInfo from typing import List import logging logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) class CandidateGenerator: def __init__(self, model: str gpt-3.5-turbo, api_key: str None): self.client openai.OpenAI(api_keyapi_key) self.model model retry(stopstop_after_attempt(3), waitwait_random_exponential(min1, max20)) def _call_llm(self, prompt: str, temperature: float 0.7) - str: 帶重試機(jī)制的LLM調(diào)用 try: response self.client.chat.completions.create( modelself.model, messages[{role: system, content: 你是一個(gè)精準(zhǔn)的信息抽取助手必須嚴(yán)格按照給定的JSON格式輸出。}, {role: user, content: prompt}], temperaturetemperature, response_format{type: json_object} # 強(qiáng)制要求返回JSON ) return response.choices[0].message.content except Exception as e: logger.error(fLLM調(diào)用失敗: {e}) raise def generate_candidates(self, user_input: str, schema: BaseModel, n: int 5) - List[dict]: 生成N個(gè)候選答案 candidates [] # 構(gòu)建系統(tǒng)化的提示詞 schema_description schema.schema_json(indent2) # 獲取JSON Schema描述 system_prompt f 你的任務(wù)是從用戶輸入中提取信息并填充到以下JSON結(jié)構(gòu)中。 請(qǐng)嚴(yán)格遵循此結(jié)構(gòu)只輸出JSON對(duì)象不要有任何額外解釋。 JSON Schema: {schema_description} 示例僅說(shuō)明格式內(nèi)容不一定相關(guān) 輸入“這是一款蘋(píng)果手機(jī)iPhone 15屬于智能手機(jī)售價(jià)5999元特點(diǎn)是超視網(wǎng)膜顯示屏和A16芯片目前有貨?!?輸出{{product_name: iPhone 15, brand: 蘋(píng)果, main_category: 智能手機(jī), price: 5999.0, key_features: [超視網(wǎng)膜顯示屏, A16芯片], in_stock: true}} 現(xiàn)在請(qǐng)?zhí)幚硪韵螺斎? full_prompt system_prompt f\n輸入{user_input} for i in range(n): # 可以微調(diào)溫度讓每次生成略有不同。例如第一次用較低溫度求穩(wěn)后面幾次用稍高溫度探索。 temp 0.3 if i 0 else 0.7 try: raw_output self._call_llm(full_prompt, temperaturetemp) # 嘗試解析為Python字典 parsed_dict json.loads(raw_output) candidates.append({ raw_text: raw_output, parsed_dict: parsed_dict, index: i, temperature_used: temp }) logger.info(f成功生成候選答案 {i1}/{n}) except json.JSONDecodeError as e: logger.warning(f候選 {i} 輸出不是合法JSON已丟棄。錯(cuò)誤: {e}) # 可以選擇記錄這個(gè)錯(cuò)誤答案用于分析但在此不加入候選池 candidates.append({ raw_text: raw_output, parsed_dict: None, index: i, temperature_used: temp, error: str(e) }) except Exception as e: logger.error(f生成候選 {i} 時(shí)發(fā)生未知錯(cuò)誤: {e}) return candidates實(shí)操心得重試機(jī)制是必須的API調(diào)用可能因網(wǎng)絡(luò)、速率限制失敗tenacity庫(kù)能優(yōu)雅地處理間歇性故障。利用response_formatOpenAI等API支持指定返回格式為json_object這能顯著提高模型輸出合規(guī)JSON的概率。溫度策略第一個(gè)候選用較低溫度如0.3確保一個(gè)“穩(wěn)健”的基線答案后續(xù)用較高溫度如0.7探索更多可能性。這是一種簡(jiǎn)單有效的多樣性策略。立即驗(yàn)證JSON在生成環(huán)節(jié)就進(jìn)行初步的JSON語(yǔ)法驗(yàn)證將無(wú)法解析的結(jié)果標(biāo)記出來(lái)避免污染后續(xù)的評(píng)估流程。3.4 實(shí)現(xiàn)LLM評(píng)估器這是Harness最精妙的部分。我們?cè)趀valuator.py中構(gòu)建評(píng)估器。評(píng)估標(biāo)準(zhǔn)需要具體、可操作。我們將從以下幾個(gè)維度打分每個(gè)維度1-5分格式正確性輸出是否為嚴(yán)格符合Schema的JSON此維度可一票否決信息完整性是否提取了輸入文本中所有相關(guān)且Schema要求的字段信息準(zhǔn)確性提取的值是否與輸入文本描述一致有無(wú)虛構(gòu)或曲解邏輯合理性提取的信息在常識(shí)和業(yè)務(wù)邏輯上是否合理例如價(jià)格不會(huì)是負(fù)數(shù)class LLMEvaluator: def __init__(self, judge_model: str gpt-4, api_key: str None): self.client openai.OpenAI(api_keyapi_key) self.judge_model judge_model def create_evaluation_prompt(self, user_input: str, candidate_output: dict, schema: BaseModel) - str: 構(gòu)建評(píng)估提示詞 schema_description schema.schema_json(indent2) evaluation_criteria 請(qǐng)你作為公正的裁判根據(jù)以下標(biāo)準(zhǔn)對(duì)候選答案進(jìn)行評(píng)分1-5分5分為最佳。請(qǐng)先進(jìn)行思考然后給出各維度分?jǐn)?shù)及簡(jiǎn)要理由最后輸出一個(gè)JSON格式的評(píng)分結(jié)果。 評(píng)分維度 1. 格式正確性候選答案是否是一個(gè)完全符合提供之JSON Schema的、無(wú)語(yǔ)法錯(cuò)誤的JSON對(duì)象如果不符合此項(xiàng)直接1分。 2. 信息完整性候選答案是否填充了Schema中所有非可選字段是否盡可能填充了可選字段參考原始輸入 3. 信息準(zhǔn)確性候選答案中每個(gè)字段的值是否嚴(yán)格忠實(shí)于用戶輸入文本有無(wú)添加、刪減或曲解原文信息 4. 邏輯合理性候選答案在常識(shí)和業(yè)務(wù)邏輯上是否合理例如價(jià)格應(yīng)為正數(shù)庫(kù)存狀態(tài)應(yīng)為布爾值 請(qǐng)基于以下信息進(jìn)行評(píng)估 - 用戶輸入{user_input} - 目標(biāo)JSON Schema{schema_description} - 候選答案{candidate_json} 你的輸出必須是且僅是一個(gè)JSON對(duì)象包含以下字段 {{ reasoning: 你的逐步思考過(guò)程, scores: {{ format_correctness: 分?jǐn)?shù), information_completeness: 分?jǐn)?shù), information_accuracy: 分?jǐn)?shù), logical_soundness: 分?jǐn)?shù) }}, overall_score: 平均分, has_critical_error: 布爾值如果格式錯(cuò)誤或嚴(yán)重歪曲事實(shí)則為true }} # 將候選字典轉(zhuǎn)為格式化的JSON字符串用于展示 candidate_json_str json.dumps(candidate_output, ensure_asciiFalse, indent2) if candidate_output else 無(wú)效JSON或?yàn)榭?prompt evaluation_criteria.format( user_inputuser_input, schema_descriptionschema_description, candidate_jsoncandidate_json_str ) return prompt retry(stopstop_after_attempt(2), waitwait_random_exponential(min2, max30)) def evaluate_candidate(self, user_input: str, candidate: dict, schema: BaseModel) - dict: 評(píng)估單個(gè)候選答案 # 檢查1如果生成時(shí)就沒(méi)解析成功直接給最低分 if candidate.get(parsed_dict) is None: return { scores: {format_correctness: 1, information_completeness: 1, information_accuracy: 1, logical_soundness: 1}, overall_score: 1.0, has_critical_error: True, reasoning: 候選答案非有效JSON無(wú)法解析。 } eval_prompt self.create_evaluation_prompt(user_input, candidate[parsed_dict], schema) try: response self.client.chat.completions.create( modelself.judge_model, messages[{role: system, content: 你是一個(gè)嚴(yán)謹(jǐn)、公正的質(zhì)量評(píng)估助手。}, {role: user, content: eval_prompt}], temperature0.0, # 評(píng)估需要確定性溫度設(shè)為0 response_format{type: json_object} ) eval_result json.loads(response.choices[0].message.content) # 將評(píng)估結(jié)果合并到候選信息中 candidate.update({evaluation: eval_result}) return candidate except Exception as e: logger.error(f評(píng)估候選 {candidate.get(index)} 時(shí)失敗: {e}) # 評(píng)估失敗給予一個(gè)保守的中等偏下分?jǐn)?shù) default_eval { scores: {format_correctness: 2, information_completeness: 2, information_accuracy: 2, logical_soundness: 2}, overall_score: 2.0, has_critical_error: False, reasoning: f評(píng)估過(guò)程發(fā)生異常{e} } candidate.update({evaluation: default_eval}) return candidate注意事項(xiàng)評(píng)估模型的選擇評(píng)估器Judge通常需要比生成器更強(qiáng)的推理和理解能力以確保評(píng)估質(zhì)量。GPT-4、Claude 3 Opus是很好的選擇。如果成本敏感可以嘗試讓生成器模型自我評(píng)估但效果會(huì)打折扣。評(píng)估提示詞是核心提示詞必須清晰、無(wú)歧義地定義評(píng)分標(biāo)準(zhǔn)。要求模型先進(jìn)行“思考”reasoning再輸出分?jǐn)?shù)有助于提高評(píng)估的穩(wěn)定性和可解釋性。這種“思維鏈”提示對(duì)評(píng)估任務(wù)非常有效。溫度設(shè)為0評(píng)估需要一致性和客觀性因此應(yīng)將溫度參數(shù)設(shè)為0確保相同的輸入得到相同的評(píng)估輸出。結(jié)構(gòu)化輸出強(qiáng)制要求評(píng)估器返回結(jié)構(gòu)化JSON便于程序自動(dòng)化處理評(píng)分結(jié)果。3.5 實(shí)現(xiàn)選擇器與聚合邏輯評(píng)估完成后selector.py中的Selector類(lèi)需要根據(jù)評(píng)估結(jié)果做出選擇。策略可以多樣化from typing import List, Dict, Any class Selector: staticmethod def select_best_by_score(evaluated_candidates: List[Dict[str, Any]]) - Dict[str, Any]: 根據(jù)綜合得分選擇最佳候選 valid_candidates [c for c in evaluated_candidates if not c.get(evaluation, {}).get(has_critical_error, True)] if not valid_candidates: logger.error(所有候選答案均存在關(guān)鍵錯(cuò)誤無(wú)法選擇。) # 可以返回一個(gè)兜底答案或觸發(fā)人工干預(yù) return {error: No valid candidate found, fallback: evaluated_candidates[0] if evaluated_candidates else None} # 按整體分?jǐn)?shù)排序 sorted_candidates sorted(valid_candidates, keylambda x: x[evaluation][overall_score], reverseTrue) best_candidate sorted_candidates[0] # 記錄選擇理由 best_candidate[selection_reason] f綜合得分最高 ({best_candidate[evaluation][overall_score]:.2f}) return best_candidate staticmethod def select_by_threshold(evaluated_candidates: List[Dict[str, Any]], min_overall: float 3.5, min_accuracy: float 4.0) - List[Dict[str, Any]]: 根據(jù)閾值篩選合格候選可用于多答案輸出或后續(xù)人工復(fù)核 qualified [] for cand in evaluated_candidates: eval_data cand.get(evaluation, {}) if eval_data.get(has_critical_error): continue if (eval_data.get(overall_score, 0) min_overall and eval_data.get(scores, {}).get(information_accuracy, 0) min_accuracy): qualified.append(cand) return qualified選擇策略的考量簡(jiǎn)單最高分最直接的策略適用于大多數(shù)情況。閾值過(guò)濾設(shè)置最低分?jǐn)?shù)線只有達(dá)標(biāo)的結(jié)果才被輸出。如果多個(gè)達(dá)標(biāo)可以全部返回供下游處理或按分?jǐn)?shù)排序。加權(quán)評(píng)分不同維度的分?jǐn)?shù)重要性不同。例如對(duì)于“文本轉(zhuǎn)JSON”“信息準(zhǔn)確性”的權(quán)重可能遠(yuǎn)高于“邏輯合理性”??梢栽谶x擇器中實(shí)現(xiàn)加權(quán)平均計(jì)算。一票否決如果某個(gè)維度如格式正確性得分極低即使總分高也可以淘汰。3.6 組裝完整流水線與運(yùn)行示例最后我們?cè)趐ipeline.py中將所有組件串聯(lián)起來(lái)形成一個(gè)完整的HarnessPipeline類(lèi)。class HarnessPipeline: def __init__(self, generator_model: str, judge_model: str, api_key: str): self.generator CandidateGenerator(modelgenerator_model, api_keyapi_key) self.evaluator LLMEvaluator(judge_modeljudge_model, api_keyapi_key) self.selector Selector() def run(self, user_input: str, output_schema: BaseModel, num_candidates: int 5) - Dict[str, Any]: 運(yùn)行完整自優(yōu)化流水線 logger.info(f開(kāi)始處理輸入: {user_input[:50]}...) # 1. 生成候選 logger.info(f步驟1: 生成 {num_candidates} 個(gè)候選答案...) candidates self.generator.generate_candidates(user_input, output_schema, nnum_candidates) # 2. 評(píng)估候選 logger.info(步驟2: 評(píng)估候選答案...) evaluated_candidates [] for cand in candidates: evaluated self.evaluator.evaluate_candidate(user_input, cand, output_schema) evaluated_candidates.append(evaluated) # 3. 選擇最佳 logger.info(步驟3: 選擇最佳答案...) best_candidate self.selector.select_best_by_score(evaluated_candidates) # 4. 最終驗(yàn)證與格式化輸出 result { original_input: user_input, best_candidate: best_candidate.get(parsed_dict), best_candidate_raw: best_candidate.get(raw_text), best_candidate_score: best_candidate.get(evaluation, {}).get(overall_score), all_candidates_summary: [ { index: c.get(index), score: c.get(evaluation, {}).get(overall_score), has_error: c.get(evaluation, {}).get(has_critical_error, False) } for c in evaluated_candidates ], selection_reason: best_candidate.get(selection_reason, N/A) } # 嘗試用Pydantic Schema做最終驗(yàn)證確保輸出格式絕對(duì)正確 try: if result[best_candidate]: validated_data output_schema(**result[best_candidate]) result[validated_output] validated_data.dict() else: result[validated_output] None except Exception as e: logger.error(f最終輸出驗(yàn)證失敗: {e}) result[validation_error] str(e) result[validated_output] None logger.info(f流水線執(zhí)行完畢。最佳答案得分: {result[best_candidate_score]}) return result現(xiàn)在我們可以在main.py中運(yùn)行一個(gè)示例from pipeline import HarnessPipeline from schemas import ProductInfo import os # 配置API密鑰 api_key os.getenv(OPENAI_API_KEY) pipeline HarnessPipeline( generator_modelgpt-3.5-turbo, judge_modelgpt-4, api_keyapi_key ) # 測(cè)試輸入 test_input 我想買(mǎi)一個(gè)華為的MateBook X Pro筆記本電腦是輕薄本價(jià)格大概在8999元左右店員說(shuō)現(xiàn)在有現(xiàn)貨特點(diǎn)是3.1K觸控屏和超長(zhǎng)續(xù)航。 result pipeline.run( user_inputtest_input, output_schemaProductInfo, num_candidates3 # 演示用3個(gè) ) print(最終輸出:) print(json.dumps(result[validated_output], indent2, ensure_asciiFalse)) print(f\n選擇理由: {result[selection_reason]}) print(f\n所有候選概覽: {result[all_candidates_summary]})運(yùn)行后你可能會(huì)得到類(lèi)似這樣的輸出{ product_name: MateBook X Pro, brand: 華為, main_category: 筆記本電腦, price: 8999.0, key_features: [3.1K觸控屏, 超長(zhǎng)續(xù)航, 輕薄本], in_stock: true }選擇理由: 綜合得分最高 (4.75) 所有候選概覽: [{index: 0, score: 4.5, has_error: false}, {index: 1, score: 4.75, has_error: false}, {index: 2, score: 4.25, has_error: false}]可以看到系統(tǒng)從3個(gè)候選答案中自動(dòng)選出了評(píng)分最高4.75分的一個(gè)作為最終輸出。整個(gè)過(guò)程中生成、評(píng)估、選擇全部自動(dòng)化完成。4. 高級(jí)優(yōu)化與工程化考量基礎(chǔ)流水線搭建完成后我們可以從多個(gè)維度對(duì)其進(jìn)行增強(qiáng)使其更健壯、更高效、更適合生產(chǎn)環(huán)境。4.1 提升評(píng)估的可靠性與效率LLM as Judge的評(píng)估質(zhì)量直接決定流水線的效果。我們可以通過(guò)以下方法提升它多評(píng)委投票引入多個(gè)不同的評(píng)估模型如GPT-4、Claude、DeepSeek-V2對(duì)同一候選進(jìn)行評(píng)分然后取平均分或中位數(shù)可以減少單一模型的偏差和隨機(jī)性。這類(lèi)似于“集成學(xué)習(xí)”的思想。分步評(píng)估與鏈?zhǔn)剿伎紝?fù)雜的評(píng)估任務(wù)分解。例如先讓一個(gè)LLM判斷“格式是否正確”如果正確再交給另一個(gè)LLM判斷“信息是否準(zhǔn)確”?;蛘咭笤u(píng)估LLM必須逐步推理Chain-of-Thought并在提示詞中提供幾個(gè)評(píng)估示例Few-shot能顯著提高評(píng)估的一致性。引入確定性規(guī)則校驗(yàn)對(duì)于可以程序化驗(yàn)證的部分絕不依賴(lài)LLM。例如在“文本轉(zhuǎn)JSON”任務(wù)中我們可以先用Pydantic驗(yàn)證JSON格式和類(lèi)型在“文本轉(zhuǎn)SQL”任務(wù)中可以用一個(gè)輕量級(jí)SQL解析器檢查語(yǔ)法甚至在一個(gè)隔離的測(cè)試數(shù)據(jù)庫(kù)里執(zhí)行EXPLAIN來(lái)驗(yàn)證其是否可運(yùn)行。將規(guī)則校驗(yàn)與LLM評(píng)估結(jié)合形成混合評(píng)估系統(tǒng)。評(píng)估結(jié)果緩存對(duì)于相同的(用戶輸入, 候選答案)對(duì)評(píng)估結(jié)果應(yīng)該是確定的??梢越⒕彺鏅C(jī)制避免重復(fù)調(diào)用昂貴的評(píng)估模型尤其是當(dāng)生成器參數(shù)如溫度固定時(shí)多次運(yùn)行流水線可能產(chǎn)生相同候選。4.2 構(gòu)建反饋循環(huán)與持續(xù)優(yōu)化Harness的終極目標(biāo)是“自優(yōu)化”這意味著它應(yīng)該能從每次運(yùn)行中學(xué)習(xí)。收集高質(zhì)量數(shù)據(jù)對(duì)每次流水線運(yùn)行后將(用戶輸入 被選中的最佳輸出)作為一個(gè)高質(zhì)量的訓(xùn)練數(shù)據(jù)對(duì)保存下來(lái)。特別是當(dāng)最佳答案的評(píng)分很高時(shí)例如4.5分這個(gè)數(shù)據(jù)對(duì)非常寶貴。提示詞迭代優(yōu)化定期分析失敗案例。如果發(fā)現(xiàn)某一類(lèi)錯(cuò)誤頻繁出現(xiàn)例如模型總是漏掉“品牌”字段可以修改生成器的系統(tǒng)提示詞加入針對(duì)性的強(qiáng)調(diào)或示例。甚至可以自動(dòng)化這個(gè)過(guò)程用收集到的高質(zhì)量數(shù)據(jù)對(duì)作為Few-shot示例動(dòng)態(tài)地構(gòu)建更有效的提示詞。模型微調(diào)當(dāng)積累到足夠多例如數(shù)千個(gè)高質(zhì)量數(shù)據(jù)對(duì)時(shí)可以考慮用它們對(duì)生成器模型進(jìn)行監(jiān)督微調(diào)。這能讓模型更直接地學(xué)習(xí)到我們期望的輸入-輸出映射從根本上提升其在特定任務(wù)上的表現(xiàn)和穩(wěn)定性。對(duì)于開(kāi)源模型這是非常可行的路徑。評(píng)估標(biāo)準(zhǔn)進(jìn)化隨著業(yè)務(wù)發(fā)展評(píng)估標(biāo)準(zhǔn)可能需要調(diào)整。Harness系統(tǒng)應(yīng)該允許動(dòng)態(tài)更新評(píng)估提示詞而無(wú)需修改代碼。4.3 性能、成本與監(jiān)控將Harness用于生產(chǎn)必須考慮工程現(xiàn)實(shí)。成本控制Harness的核心成本來(lái)自LLM API調(diào)用尤其是評(píng)估步驟。策略包括候選數(shù)N的權(quán)衡N越大找到好答案的概率越高但成本線性增加。需要通過(guò)實(shí)驗(yàn)找到性價(jià)比最高的N值例如對(duì)于大多數(shù)任務(wù)N3到5可能就足夠了。模型選型生成器用較小/較便宜的模型評(píng)估器用較大/較貴的模型。提前淘汰在完整評(píng)估前先進(jìn)行低成本過(guò)濾。例如先用規(guī)則檢查JSON格式格式錯(cuò)誤的直接淘汰不送評(píng)估。延遲優(yōu)化生成和評(píng)估N個(gè)候選是順序進(jìn)行的會(huì)導(dǎo)致延遲增加。可以考慮并行調(diào)用生成API如果API支持來(lái)減少生成階段的耗時(shí)。評(píng)估階段也可以并行評(píng)估多個(gè)候選。監(jiān)控與可觀測(cè)性必須為流水線添加詳細(xì)的日志和監(jiān)控。記錄每個(gè)環(huán)節(jié)的耗時(shí)、每個(gè)候選的分?jǐn)?shù)分布、最終選擇的原因、失敗案例等。這有助于發(fā)現(xiàn)問(wèn)題如果某天平均分突然下降可能意味著模型服務(wù)異常或提示詞出了問(wèn)題。分析瓶頸了解時(shí)間主要花在生成還是評(píng)估上。持續(xù)改進(jìn)基于監(jiān)控?cái)?shù)據(jù)分析哪些類(lèi)型的輸入容易導(dǎo)致低分從而針對(duì)性優(yōu)化。5. 常見(jiàn)問(wèn)題、故障排查與避坑指南在實(shí)際構(gòu)建和運(yùn)行Harness的過(guò)程中你會(huì)遇到各種各樣的問(wèn)題。下面是我踩過(guò)的一些坑以及解決方案。5.1 評(píng)估器LLM as Judge本身不可靠問(wèn)題表現(xiàn)評(píng)估分?jǐn)?shù)波動(dòng)大或者明顯給出錯(cuò)誤評(píng)判如對(duì)事實(shí)錯(cuò)誤的答案打高分。排查與解決檢查評(píng)估提示詞確保評(píng)分標(biāo)準(zhǔn)清晰、無(wú)歧義。加入具體的評(píng)分示例Few-shot能極大提高一致性。例如“如果答案完全準(zhǔn)確給5分有細(xì)微偏差給4分有主要錯(cuò)誤給2分完全無(wú)關(guān)給1分?!币蟆八季S鏈”在評(píng)估提示詞中明確要求模型“請(qǐng)逐步推理然后給出分?jǐn)?shù)”。這能迫使模型進(jìn)行更深入的思考而不是憑直覺(jué)打分。使用更強(qiáng)的模型如果用的是GPT-3.5做評(píng)估嘗試升級(jí)到GPT-4或Claude 3。評(píng)估通常比生成需要更強(qiáng)的推理能力。多模型投票如前所述使用多個(gè)模型評(píng)估并取綜合結(jié)果。人工校準(zhǔn)定期抽樣一批評(píng)估結(jié)果進(jìn)行人工復(fù)核。如果發(fā)現(xiàn)系統(tǒng)性的評(píng)分偏差調(diào)整提示詞或評(píng)分規(guī)則。5.2 流水線輸出質(zhì)量不穩(wěn)定問(wèn)題表現(xiàn)有時(shí)效果很好有時(shí)很差無(wú)法達(dá)到穩(wěn)定的生產(chǎn)要求。排查與解決增加候選數(shù)量N這是最直接的方法。從N3增加到N5或7找到優(yōu)質(zhì)答案的概率會(huì)顯著提升但成本和延遲也會(huì)增加。優(yōu)化生成提示詞生成器的提示詞是源頭。確保它清晰、具體并包含了輸出格式的明確約束如使用JSON Schema。在提示詞中加入少量高質(zhì)量示例Few-shot learning效果極佳。調(diào)整溫度策略不要對(duì)所有候選使用相同的溫度。嘗試“低溫度高溫度”混合策略確保既有穩(wěn)健輸出也有多樣性探索。引入后處理對(duì)于選出的最佳答案可以增加一個(gè)“潤(rùn)色”或“一致性檢查”步驟。例如讓另一個(gè)LLM快速檢查一下最終答案是否與原始輸入矛盾。5.3 API調(diào)用失敗與速率限制問(wèn)題表現(xiàn)流水線運(yùn)行時(shí)隨機(jī)失敗報(bào)錯(cuò)超時(shí)或額度不足。排查與解決實(shí)現(xiàn)健壯的重試機(jī)制使用tenacity等庫(kù)對(duì)可重試的錯(cuò)誤如網(wǎng)絡(luò)超時(shí)、速率限制進(jìn)行指數(shù)退避重試。設(shè)置合理的超時(shí)時(shí)間根據(jù)模型和任務(wù)復(fù)雜度為API調(diào)用設(shè)置合適的超時(shí)時(shí)間避免無(wú)限等待。監(jiān)控使用量和成本設(shè)置每日預(yù)算和用量告警。對(duì)于評(píng)估這類(lèi)高成本操作可以考慮使用緩存。使用負(fù)載均衡與多API密鑰如果請(qǐng)求量很大可以使用多個(gè)API端點(diǎn)或密鑰并在客戶端實(shí)現(xiàn)簡(jiǎn)單的輪詢或負(fù)載均衡。5.4 如何處理“沒(méi)有合格答案”的情況問(wèn)題表現(xiàn)所有候選答案的評(píng)估分?jǐn)?shù)都很低或者都有關(guān)鍵錯(cuò)誤。解決方案設(shè)置兜底策略在Selector中當(dāng)所有候選都不合格時(shí)可以返回一個(gè)預(yù)定義的錯(cuò)誤信息或者返回分?jǐn)?shù)“相對(duì)最高”的那個(gè)但標(biāo)記上低置信度。觸發(fā)人工審核流程將低置信度的結(jié)果放入一個(gè)待審核隊(duì)列由人工處理。同時(shí)這些案例是優(yōu)化提示詞或模型的寶貴素材。動(dòng)態(tài)回退如果多次嘗試均失敗可以自動(dòng)切換到一個(gè)更簡(jiǎn)單、更保守的生成策略例如使用溫度0、更詳細(xì)的提示詞只生成一個(gè)答案。5.5 針對(duì)特定場(chǎng)景的調(diào)優(yōu)技巧文本轉(zhuǎn)SQL這是“幻覺(jué)”重災(zāi)區(qū)。除了上述通用流程務(wù)必加入SQL語(yǔ)法驗(yàn)證和執(zhí)行計(jì)劃驗(yàn)證。可以在一個(gè)只有Schema沒(méi)有數(shù)據(jù)的測(cè)試庫(kù)中執(zhí)行EXPLAIN確保SQL語(yǔ)法正確且能有效利用索引避免產(chǎn)生笛卡爾積等性能炸彈。評(píng)估標(biāo)準(zhǔn)中要強(qiáng)調(diào)“查詢結(jié)果必須與問(wèn)題意圖匹配”。創(chuàng)意寫(xiě)作對(duì)于寫(xiě)小說(shuō)、營(yíng)銷(xiāo)文案等任務(wù)“準(zhǔn)確性”可能不那么重要而“連貫性”、“創(chuàng)意性”、“風(fēng)格符合度”更重要。需要重新設(shè)計(jì)評(píng)估維度甚至可以讓多個(gè)評(píng)估器分別從不同維度打分。代碼生成必須集成代碼靜態(tài)分析和單元測(cè)試。評(píng)估器不僅要看代碼是否“看起來(lái)對(duì)”更要能通過(guò)編譯和基本的測(cè)試用例。可以將代碼放入沙箱執(zhí)行來(lái)驗(yàn)證其功能。構(gòu)建LLM自優(yōu)化流水線是一個(gè)典型的“用魔法打敗魔法”的工程實(shí)踐。它承認(rèn)當(dāng)前LLM的不完美但通過(guò)系統(tǒng)化的工程方法將這種不穩(wěn)定性控制在一個(gè)可接受、可管理的范圍內(nèi)。從簡(jiǎn)單的Best of N采樣到復(fù)雜的LLM自我評(píng)估與迭代優(yōu)化Harness的理念為我們提供了一條通往可靠AI應(yīng)用的切實(shí)路徑。記住沒(méi)有一勞永逸的銀彈持續(xù)地觀察流水線的輸出分析失敗案例并迭代優(yōu)化你的提示詞、評(píng)估標(biāo)準(zhǔn)和流程才是讓這個(gè)系統(tǒng)越來(lái)越強(qiáng)大的關(guān)鍵。