踐:從Prompt設(shè)計(jì)到Agent開發(fā)與評(píng)測(cè))
1. 從這個(gè)問題開始為什么會(huì)調(diào)接口不等于會(huì)AI工程先說個(gè)我自己觀察到的現(xiàn)象。過去兩年我接觸過不少團(tuán)隊(duì)一說要接入AI能力第一反應(yīng)就是調(diào)個(gè)API而已文檔我看得懂。結(jié)果做出來是什么樣呢功能確實(shí)跑通了demo演示也驚艷但一上生產(chǎn)就露餡用戶隨便換個(gè)說法模型就答非所問明明同一個(gè)Prompt上午和下午輸出能差出十萬八千里模型一本正經(jīng)給你編了個(gè)根本不存在的參數(shù)你還沒法攔。這些問題的根子不在模型而在工程化。我最初踩進(jìn)ai-engineering這個(gè)坑也是因?yàn)楸灰粋€(gè)內(nèi)部工具折磨到崩潰。團(tuán)隊(duì)想做一個(gè)給客服用的知識(shí)庫問答機(jī)器人用的就是市面上常見的大模型對(duì)話接口。我們天真地以為把文檔切片塞進(jìn)向量庫再拿用戶問題去檢索拼接成Prompt就能交付了。結(jié)果真上線后客服同事反饋問退款多久到賬模型能答追問一句那要是用花唄付的呢模型開始胡謅把兩個(gè)政策條款捏在一起說。你能怪模型嗎其實(shí)是我們沒有把工程這兩個(gè)字當(dāng)回事。這里我需要先給AI工程下一個(gè)我自己的定義。它不等同于機(jī)器學(xué)習(xí)建模也不等同于單純的提示詞編寫。AI工程指的是圍繞大語言模型這類非確定性系統(tǒng)設(shè)計(jì)輸入輸出協(xié)議、控制生成過程、建立評(píng)測(cè)閉環(huán)、管理質(zhì)量風(fēng)險(xiǎn)最終讓AI能力以穩(wěn)定可維護(hù)的形式嵌入真實(shí)業(yè)務(wù)流程中的整套實(shí)踐方法。它和傳統(tǒng)軟件開發(fā)最大的區(qū)別在于傳統(tǒng)系統(tǒng)里一切行為是可預(yù)測(cè)的你輸入什么代碼邏輯就給你算出來什么而大模型是概率系統(tǒng)同樣的輸入每次輸出都可能不同而且它的知識(shí)邊界模糊你不知道它什么時(shí)候會(huì)一本正經(jīng)地胡說八道。所以AI工程的核心手段就是用工程手段去約束一個(gè)天生不確定的員工。這篇文章不是我憑空想出來的理論而是我從零開始摸索AI工程的完整記錄。我會(huì)從環(huán)境搭建、Prompt設(shè)計(jì)、Agent開發(fā)、測(cè)試評(píng)估一直到生產(chǎn)部署用一條線串起來講先講我踩過哪些坑再講為什么那樣做是錯(cuò)的最后給出我認(rèn)為從一開始就應(yīng)該采取的工程方案。文章適合誰如果你跟我一樣是個(gè)寫傳統(tǒng)業(yè)務(wù)代碼出身、現(xiàn)在被迫要上AI能力的開發(fā)者或者你已經(jīng)能熟練調(diào)用各家模型的API但總覺得做出來的東西不夠穩(wěn)再或者你帶的團(tuán)隊(duì)正要立項(xiàng)做AI功能你想在動(dòng)工前建立一套少走彎路的整體認(rèn)知。這三類人應(yīng)該都能從里面拿到一些可以直接用的東西。2. 起步階段最容易犯的錯(cuò)先選模型后想問題很多人做AI項(xiàng)目第一步是打開各大模型廠商的官網(wǎng)比較哪個(gè)參數(shù)多、哪個(gè)跑分高、哪個(gè)便宜然后拍腦袋定了一個(gè)。等代碼寫完才發(fā)現(xiàn)模型能力是強(qiáng)但業(yè)務(wù)上用不起來。為什么因?yàn)槟P瓦x型這件事本質(zhì)上是對(duì)業(yè)務(wù)問題的拆解問題不是對(duì)參數(shù)的比較問題。2.1 先把任務(wù)分門別類再談模型參數(shù)我后來整理了一套比較順手的任務(wù)分類思路分享出來給大家參考。接到一個(gè)AI需求先別急著找模型先把任務(wù)切成四類單輪生成類給一段輸入產(chǎn)出一段輸出中間不需要額外信息。例如潤色這段文案把這段日志翻譯成英文總結(jié)這篇文章。這類任務(wù)最簡單對(duì)模型的上下文長度要求不高關(guān)鍵是輸出格式的穩(wěn)定性。多輪對(duì)話類需要記住前文在對(duì)話歷史里找線索。客服機(jī)器人、AI教練、角色扮演都屬于這一類。這類任務(wù)對(duì)上下文管理能力要求很高模型能不能在長對(duì)話里不失憶是核心指標(biāo)。檢索增強(qiáng)類RAG回答的依據(jù)不在模型參數(shù)里而要從外部數(shù)據(jù)庫檢索。例如企業(yè)知識(shí)庫問答、法律政策咨詢。這類任務(wù)真正拼的不是模型聰明不聰明而是檢索鏈路做得好不好以及模型能不能克制住不編造。工具調(diào)用類Agent模型不僅要理解用戶意圖還要決定調(diào)用哪個(gè)工具、傳什么參數(shù)然后根據(jù)工具返回值繼續(xù)推理。這類任務(wù)對(duì)模型的function calling能力要求極高。為什么要做這個(gè)分類因?yàn)檫x型標(biāo)準(zhǔn)完全不同。單輪生成類你可以用輕量模型加嚴(yán)格Prompt模板追求低成本高吞吐多輪對(duì)話類你就得重點(diǎn)關(guān)注上下文輪次的穩(wěn)定性要不要做歷史摘要壓縮檢索增強(qiáng)類模型的理解檢索結(jié)果并忠實(shí)作答能力比它本身的常識(shí)儲(chǔ)備更重要工具調(diào)用類那你必須選function calling做得最成熟的那幾家否則后面會(huì)被各種參數(shù)解析錯(cuò)誤折磨到崩潰。我自己最初就是沒做這個(gè)分類一上來就選了一個(gè)綜合評(píng)分很高的旗艦?zāi)P妥鯮AG問答。后來發(fā)現(xiàn)旗艦?zāi)P偷膬?yōu)勢(shì)根本發(fā)揮不出來反而因?yàn)樗纳娠L(fēng)格太自由經(jīng)常不按我給的格式模板輸出讓我在后處理上花了大量時(shí)間清洗JSON。后來換了一個(gè)更聽話的中型模型配合嚴(yán)格的Prompt約束準(zhǔn)確率和格式合規(guī)率反而上去了成本還降了一大截。2.2 環(huán)境搭建里的隱藏工程點(diǎn)環(huán)境搭建這個(gè)環(huán)節(jié)大多數(shù)教程就是安裝SDK、配好API Key、跑通一個(gè)Hello World。但以我搞生產(chǎn)的經(jīng)驗(yàn)有幾個(gè)隱藏工程點(diǎn)特別容易被忽略第一版本的鎖定與隔離。大模型的API演進(jìn)速度極快今天調(diào)用的參數(shù)明天可能就標(biāo)了deprecated。我見過不止一個(gè)項(xiàng)目因?yàn)镾DK自動(dòng)升級(jí)某個(gè)參數(shù)行為悄悄變了生產(chǎn)環(huán)境直接出問題。所以無論是Python環(huán)境還是Node環(huán)境依賴鎖定是底線。我用的是Python生態(tài)習(xí)慣用pip freeze requirements.txt鎖定全量版本而不是只鎖頂層依賴。第二本地開發(fā)與生產(chǎn)環(huán)境的密鑰管理。千萬別把API Key寫在代碼里或者環(huán)境變量文件里就提交到倉庫。我剛開始犯過這個(gè)錯(cuò)不小心把key提交到了一個(gè)內(nèi)部倉庫雖然沒外泄但被運(yùn)維通報(bào)的時(shí)候冷汗都出來了。后來統(tǒng)一用了.env文件加.gitignore排除生產(chǎn)環(huán)境用密鑰管理服務(wù)本地用direnv之類工具自動(dòng)加載。這個(gè)習(xí)慣一定要在一開始就養(yǎng)成。第三成本與限流的可觀測(cè)性。模型調(diào)用不是免費(fèi)的而且各家都有速率限制RPM、TPM。我建議在封裝API調(diào)用的那一層順手把每次請(qǐng)求的token數(shù)、延遲、花費(fèi)都打到日志里。不用很復(fù)雜一個(gè)簡單的裝飾器就能搞定。早期不記賬等流量上來發(fā)現(xiàn)賬單的時(shí)候那種酸爽我體驗(yàn)過一次不想大家再體驗(yàn)。第四輸入輸出的全量留痕。這個(gè)我要重點(diǎn)強(qiáng)調(diào)。本地調(diào)試跑通不算數(shù)你必須有一個(gè)正式的日志系統(tǒng)記錄每一次線上請(qǐng)求的完整輸入Prompt、模型輸出、消耗token數(shù)、耗時(shí)。原因很簡單當(dāng)你接到用戶投訴你這AI回答得不對(duì)時(shí)如果沒有留痕你根本沒法復(fù)現(xiàn)問題。大模型輸出的概率性意味著復(fù)現(xiàn)本身就很困難留痕是你唯一的追溯手段。實(shí)際落地的時(shí)候我建議封裝一個(gè)統(tǒng)一的模型訪問層把上述的日志、成本統(tǒng)計(jì)、限流重試、熔斷都放進(jìn)去。寧可前期多花兩天寫這個(gè)封裝也別在幾十個(gè)業(yè)務(wù)邏輯里直接散落地調(diào)用model.chat()。這算是我最想穿越回去告訴自己的一條經(jīng)驗(yàn)。注意所有接入大模型的代碼路徑最好都走這個(gè)統(tǒng)一層哪怕是臨時(shí)調(diào)試腳本也不例外。因?yàn)槟阌肋h(yuǎn)不知道哪個(gè)調(diào)試腳本哪一天就被復(fù)制成了正式功能。3. Prompt Engineering非確定性系統(tǒng)里的跟員工溝通藝術(shù)如果非要用一個(gè)類比來說明Prompt的重要性我會(huì)說調(diào)用模型API就像空降了一個(gè)能力很強(qiáng)但性格古怪的外包員工你怎么給他下指令直接決定他活兒干得漂不漂亮。而且這個(gè)員工不會(huì)追問你不說清楚他就自己腦補(bǔ)你給的規(guī)則有歧義他就按他的理解執(zhí)行。3.1 系統(tǒng)提示詞的角色邊界設(shè)計(jì)在我梳理的Prompt體系里系統(tǒng)提示詞System Prompt是最不應(yīng)該偷懶的地方。它的核心價(jià)值不只是告訴模型你是干什么的更是設(shè)定一個(gè)清晰的角色邊界和規(guī)則邊界。怎么才算邊界清晰我給你看一個(gè)我迭代了很多版的知識(shí)庫問答助手系統(tǒng)提示詞框架簡化版你是[公司名稱]智能客服助手只能依據(jù)知識(shí)庫內(nèi)容回答問題。 規(guī)則 1. 當(dāng)知識(shí)庫中有明確答案時(shí)忠實(shí)概括并引用來源。 2. 當(dāng)知識(shí)庫中沒有相關(guān)答案時(shí)必須明確回答抱歉這個(gè)信息我暫時(shí)沒有禁止編造。 3. 禁止將不同政策條款拼接推理嚴(yán)格只做信息檢索與摘要。 4. 用戶詢問與知識(shí)庫無關(guān)的話題時(shí)禮貌引導(dǎo)回到業(yè)務(wù)范圍。 5. 輸出格式結(jié)論優(yōu)先小標(biāo)題分段使用口語化表達(dá)單次回復(fù)不超過200字。注意這個(gè)設(shè)計(jì)里的幾個(gè)要點(diǎn)。第一它明確告訴模型不知道就是不知道把誠實(shí)變成了硬規(guī)則。這能大幅減少幻覺但前提是你的檢索鏈路確實(shí)能支撐判斷知識(shí)庫里有沒有答案這個(gè)動(dòng)作。第二個(gè)要點(diǎn)是禁止拼接推理這是我踩過坑之后加上的。大模型特別擅長把兩段看起來相關(guān)但其實(shí)是針對(duì)不同場(chǎng)景的條款捏成一個(gè)答案看上去邏輯通順實(shí)際上完全錯(cuò)誤。你不明確禁止它就會(huì)干。邊界設(shè)計(jì)還有一個(gè)反面教訓(xùn)不要給模型太大自由。曾有一版提示詞我寫的是如果用戶問題不清晰可以主動(dòng)詢問澄清。出發(fā)點(diǎn)是好的結(jié)果模型在超過一半的對(duì)話里都在追問您能再詳細(xì)描述一下嗎把客服同事惹毛了。后來我把規(guī)則改成了當(dāng)置信度低時(shí)給出最常見情況的答案并提供進(jìn)一步選項(xiàng)效果立竿見影。3.2 結(jié)構(gòu)化輸出讓模型按協(xié)議回答問題有一類問題是幾乎所有新手都會(huì)遇到的跟模型說給我一段JSON它給了但有時(shí)候是純JSON有時(shí)候是好的以下是您需要的JSON后面跟一段有時(shí)候JSON里的字段順序還亂。你要解析吧就得寫一堆容錯(cuò)代碼防御它各種不老實(shí)。工程上解決這個(gè)問題有三個(gè)層次的手段按優(yōu)先級(jí)排列第一層Prompt層面約定。在提示詞里明確給出輸出模板。例如只輸出一個(gè)JSON對(duì)象不要包含任何解釋或markdown代碼塊標(biāo)記格式如下{answer: string, source_doc_id: string, confidence: float}。記住要把format的示例喂給模型模型跟人一樣給一個(gè)具體例子比給一百個(gè)形容詞管用。第二層API參數(shù)層面約束。很多模型服務(wù)提供了response_format參數(shù)可以直接要求模型輸出JSON對(duì)象。這比純靠Prompt約束可靠得多建議能用參數(shù)就不用純文本約定。第三層代碼層面的二次校驗(yàn)。拿到輸出先做JSON解析校驗(yàn)字段缺失或類型不對(duì)就進(jìn)入重試邏輯讓模型再生成一次或者干脆走兜底方案。這一步是保底的保單不可或缺。我見過的最糟糕的寫法是拿到模型返回的字符串直接用json.loads()硬解失敗就報(bào)錯(cuò)返回。問為什么這么寫答模型不是應(yīng)該按我的要求輸出JSON嗎。問題是大模型不是函數(shù)它的輸出只有概率高低沒有必然性。你必須從設(shè)計(jì)上默認(rèn)這次輸出可能是壞的然后去處理這個(gè)壞的情況。3.3 少樣本示例的正確打開方式少樣本學(xué)習(xí)Few-shot Learning是Prompt工程里性價(jià)比很高的一招但大多數(shù)人用得不對(duì)。最常見的錯(cuò)誤是把少樣本示例當(dāng)成展示模型有多么聰明的舞臺(tái)示例繞來繞去又長又復(fù)雜。實(shí)際正確的用法是用示例展示邊界條件和邊緣情況的處理方式。舉個(gè)例子。我做一個(gè)意圖識(shí)別功能把用戶輸入分類為查余額查流水投訴其他。如果僅僅在提示詞里寫請(qǐng)將用戶輸入分類到上述四類模型在邊界情況下就很容易翻車。比如用戶說我剛充的錢怎么沒到賬這算查余額還是投訴模型可能隨機(jī)選一個(gè)。正確的做法是給幾個(gè)精心設(shè)計(jì)的示例用戶輸入我剛充的錢怎么沒到賬 意圖分類投訴 原因用戶表達(dá)了對(duì)服務(wù)異常的負(fù)面情緒應(yīng)優(yōu)先識(shí)別為投訴后續(xù)可通過追問獲取賬號(hào)信息。 用戶輸入我的余額還剩多少 意圖分類查余額 原因直接詢問賬戶剩余金額意圖明確。 用戶輸入上個(gè)月流水幫我導(dǎo)一下 意圖分類查流水 原因請(qǐng)求獲取交易記錄的導(dǎo)出操作。示例的核心不是教會(huì)模型什么是查余額而是告訴它當(dāng)分類邊界模糊時(shí)你的傾向性是什么。這是把業(yè)務(wù)規(guī)則編碼進(jìn)Prompt的有效方式而且比寫一堆如果用戶表達(dá)不滿情緒則優(yōu)先歸為投訴之類的抽象規(guī)則模型理解起來容易得多。順便提一嘴少樣本示例里的原因說明也很關(guān)鍵。只給輸入輸出對(duì)模型可能會(huì)學(xué)到表面模式給原因模型才能理解背后的判斷邏輯。這一點(diǎn)在復(fù)雜任務(wù)上差別尤其明顯。4. 從Prompt到Agent讓模型不再光說不練Prompt玩到一定程度你一定會(huì)遇到一個(gè)瓶頸模型只能生成文字沒法執(zhí)行動(dòng)作。用戶說幫我查一下訂單號(hào)KL2023001的物流狀態(tài)模型就算知道該查物流接口也沒法自己調(diào)接口。讓模型具備動(dòng)手能力的機(jī)制業(yè)界管它叫Function Calling或者更寬泛一點(diǎn)叫Agent模式。4.1 Function Calling的底層邏輯與一次艱難調(diào)試Function Calling的核心機(jī)制其實(shí)不復(fù)雜你在請(qǐng)求里聲明好一系列工具的描述和參數(shù)架構(gòu)通常是JSON Schema格式模型根據(jù)用戶輸入決定要不要調(diào)用工具、調(diào)用哪個(gè)、傳什么參數(shù)但模型本身不執(zhí)行工具它只是輸出一個(gè)結(jié)構(gòu)化的調(diào)用請(qǐng)求真正執(zhí)行還是要你的代碼來做。打個(gè)比方這就像你是老板模型是你的高級(jí)助理。助理不會(huì)自己跑去財(cái)務(wù)室拿數(shù)據(jù)但他會(huì)在你面前說我需要向財(cái)務(wù)部調(diào)取某某報(bào)表請(qǐng)幫我轉(zhuǎn)達(dá)。 你聽懂了就去取報(bào)表然后把結(jié)果交給他他再繼續(xù)回答問題。聽起來挺清晰的對(duì)吧那我當(dāng)初為什么還被折磨了一整天那次做的是一個(gè)訂單管理助手我定義了四個(gè)工具查詢訂單詳情、提交退款申請(qǐng)、修改收貨地址、查詢物流軌跡。運(yùn)行邏輯其實(shí)很常規(guī)用戶提出需求模型返回工具調(diào)用我的代碼執(zhí)行工具把結(jié)果回填給模型模型生成最終回復(fù)。聽起來沒問題。實(shí)際跑起來Log里清晰展示模型決定調(diào)用query_order參數(shù)是{order_id: 3701470}。我的代碼也執(zhí)行成功了返回了正常的訂單詳情。然后模型開始回復(fù)用戶了但它復(fù)述訂單內(nèi)容的時(shí)候把訂單金額里的¥299說成了¥2999。你說這算誰的錯(cuò)工具的返回值是對(duì)的模型自己在總結(jié)的時(shí)候卻嘴瓢了。后來我發(fā)現(xiàn)這種問題在Function Calling場(chǎng)景里其實(shí)相當(dāng)常見。根本原因是模型需要把工具返回結(jié)果和對(duì)話上下文整合進(jìn)新一輪生成里多了一步信息流轉(zhuǎn)就多了一個(gè)出錯(cuò)的機(jī)會(huì)。而且模型對(duì)這種非原生的數(shù)字信息在文本生成過程中很容易產(chǎn)生數(shù)值畸變。怎么解決我的做法有兩條腿。第一條腿在系統(tǒng)提示詞里強(qiáng)制加一條當(dāng)需要引用工具返回的數(shù)據(jù)時(shí)必須原樣照抄該數(shù)據(jù)禁止自行修改變量值。 第二條腿更穩(wěn)妥把關(guān)鍵動(dòng)作比如退款申請(qǐng)?jiān)O(shè)計(jì)成最終確認(rèn)流程用戶意圖識(shí)別到后讓模型生成待確認(rèn)信息等用戶點(diǎn)確認(rèn)按鈕再由代碼直接調(diào)用工具接口。這樣模型只負(fù)責(zé)理解意圖而關(guān)鍵動(dòng)作的執(zhí)行完全繞開模型生成環(huán)節(jié)。這種關(guān)鍵路徑不經(jīng)過模型的設(shè)計(jì)原則我建議每個(gè)AI工程都把它的優(yōu)先級(jí)提到最高。4.2 Agent循環(huán)的骨架搭建拋開Function Calling API的細(xì)枝末節(jié)一個(gè)Agent的大腦循環(huán)本質(zhì)是這個(gè)四步循環(huán)接收用戶輸入連同當(dāng)前上下文、工具描述列表一起提交給模型。模型決定直接生成回答或返回一個(gè)工具調(diào)用請(qǐng)求。如果是工具調(diào)用請(qǐng)求你的代碼執(zhí)行工具把結(jié)果成功、失敗、返回?cái)?shù)據(jù)追加到對(duì)話上下文。拿著新的完整上下文再回到第1步讓模型繼續(xù)推理。一個(gè)基礎(chǔ)的骨架實(shí)現(xiàn)我可以給一個(gè)簡化版的Python偽代碼方便你理解這個(gè)循環(huán)結(jié)構(gòu)def run_agent(user_input, messages, tools): messages.append({role: user, content: user_input}) # 第一步調(diào)用模型 response client.chat.completions.create( modelMODEL_NAME, messagesmessages, toolstools, tool_choiceauto ) message response.choices[0].message # 判斷模型是否請(qǐng)求調(diào)用工具 if message.tool_calls: # 將模型的工具調(diào)用請(qǐng)求追加到消息歷史 messages.append(message) # 第二步遍歷執(zhí)行每個(gè)工具調(diào)用 for tool_call in message.tool_calls: function_name tool_call.function.name arguments json.loads(tool_call.function.arguments) # 第三步執(zhí)行工具獲取結(jié)果 result execute_tool(function_name, arguments) # 把工具結(jié)果作為tool消息追加 messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse) }) # 第四步帶著完整上下文繼續(xù)循環(huán) return run_agent(, messages, tools) else: # 模型沒有請(qǐng)求調(diào)用工具直接返回最終回答 return message.content寫這個(gè)骨架的時(shí)候有幾個(gè)容易忽略的小坑我用自己的血淚教訓(xùn)換來的經(jīng)驗(yàn)?zāi)P头祷氐膖ool_call_id必須和后續(xù)tool消息一一對(duì)應(yīng)一個(gè)都不能漏。有個(gè)版本我偷懶循環(huán)執(zhí)行完工具后重新組裝消息把tool_call_id字段搞丟了模型直接成績異常。還有一個(gè)點(diǎn)是一次模型響應(yīng)里可能同時(shí)包含多個(gè)tool_calls代碼要支持并行或串行執(zhí)行多個(gè)工具并把結(jié)果都追加上去只處理第一個(gè)會(huì)導(dǎo)致后面的結(jié)果丟失。4.3 Agent循環(huán)的無盡循環(huán)與安全護(hù)欄Agent開發(fā)里最經(jīng)典的問題就是模型判斷需要調(diào)用工具的請(qǐng)求一直停不下來或者工具調(diào)用失敗后模型反復(fù)重試同一個(gè)工具陷入無限循環(huán)。這個(gè)問題必須從架構(gòu)上解決。我的方案是引入一個(gè)最大迭代輪數(shù)的限制通常設(shè)3到5輪就夠用了。超過限制后不再把工具結(jié)果喂給模型而是直接要求模型基于已有的信息兜底回答。這里還涉及一個(gè)容易被忽略的問題每輪循環(huán)都有額外的token消耗這些成本要計(jì)入你的預(yù)算計(jì)算中。宣傳一次Agent調(diào)用平均費(fèi)用X元等到線上實(shí)際跑起來發(fā)現(xiàn)是10X元這種情況我見得多了。另外還有一類容易被忽視的安全性問題Agent的任意工具調(diào)用能力意味著一旦Prompt被注入惡意指令模型可能在用戶引導(dǎo)下調(diào)用你系統(tǒng)里本不該調(diào)用的內(nèi)部工具。這絕對(duì)不只是理論風(fēng)險(xiǎn)。我的建議是在execute_tool這個(gè)函數(shù)里一定要有鑒權(quán)與白名單校驗(yàn)不能只依賴模型應(yīng)該不會(huì)調(diào)用這個(gè)工具。工具的真實(shí)執(zhí)行權(quán)限也要做最小化授予比如退款操作一定走雙確認(rèn)。Agent可以被設(shè)計(jì)成最大化自由但底層的權(quán)限閘門必須牢牢握在代碼手里。5. 檢驗(yàn)AI好壞的學(xué)問AI測(cè)試開發(fā)與評(píng)測(cè)閉環(huán)一旦AI功能進(jìn)入開發(fā)階段你馬上會(huì)遇到一個(gè)傳統(tǒng)測(cè)試方法解決不了的問題怎么斷言模型回答是正確的退款多久到賬這個(gè)問題的正確答案不止一種表達(dá)方式你不能拿字符串相等去校驗(yàn)。正因?yàn)槟P洼敵鍪欠谴_定性的AI的測(cè)試與評(píng)估最終要形成一個(gè)閉環(huán)。5.1 從編造正確到可量化的評(píng)測(cè)維度我做AI測(cè)試開發(fā)到現(xiàn)在總結(jié)了一套還算靠譜的評(píng)估維度框架分享給你們參考。不是只有對(duì)不對(duì)這一桿秤而是拆成好幾個(gè)維度評(píng)估維度要解決的問題常見方法準(zhǔn)確率答題命中率核心問題的回答是否正確答案比對(duì)、人工標(biāo)注、用大模型做裁判格式合規(guī)率輸出是否符合設(shè)計(jì)的結(jié)構(gòu)化協(xié)議程序化校驗(yàn)JSON/XML/字段類型幻覺率是否編造了知識(shí)庫中不存在的事實(shí)人工標(biāo)注、交叉檢索校驗(yàn)上下文連貫度多輪對(duì)話中是否自相矛盾人工評(píng)估、對(duì)比Prompt設(shè)計(jì)安全與合規(guī)率是否有違規(guī)或越權(quán)內(nèi)容規(guī)則過濾器加模型分類器延遲與成本用戶體驗(yàn)和資源開銷監(jiān)控系統(tǒng)數(shù)據(jù)我不想在這篇文章里展開每個(gè)維度的評(píng)測(cè)代碼怎么寫那是另一篇長文的內(nèi)容。這里我只想強(qiáng)調(diào)一個(gè)思路問題AI評(píng)測(cè)的核心思想是把定性標(biāo)準(zhǔn)努力轉(zhuǎn)成量化指標(biāo)。比如你們客服機(jī)器人態(tài)度好不好這個(gè)標(biāo)準(zhǔn)沒法上線評(píng)測(cè)。你需要把態(tài)度好拆成可以量化的子項(xiàng)是否包含正面情緒詞是否主動(dòng)致歉是否在結(jié)尾給出下一步建議。這些子項(xiàng)可以規(guī)則匹配也可以用分類模型打分最后合成一個(gè)總分。不要試圖讓評(píng)測(cè)系統(tǒng)直接理解態(tài)度好這種模糊概念要讓評(píng)測(cè)系統(tǒng)去評(píng)估具體可數(shù)的行為。5.2 搭建回歸測(cè)試集鎖住Prompt的剎車從我個(gè)人的實(shí)踐經(jīng)驗(yàn)來看整個(gè)AI工程里回報(bào)率最高的一件事就是建一個(gè)高質(zhì)量的回歸測(cè)試集。這個(gè)測(cè)試集不是隨便找?guī)装贄l問題而是要精心設(shè)計(jì)包含正常的、邊緣的、反例的和對(duì)抗性的樣本每條樣本都要有明確的預(yù)期行為描述。比如我的客服助手回歸測(cè)試集就包含問一個(gè)知識(shí)庫中的標(biāo)準(zhǔn)問題期望給出準(zhǔn)確回答并附來源問一個(gè)超出知識(shí)庫范圍的問題期望回答暫無信息而非編造故意用誘導(dǎo)性語言問敏感問題期望拒絕回答并引導(dǎo)回正軌連續(xù)追問5輪后再問第一個(gè)問題期望模型不混淆上下文。這些測(cè)試集的價(jià)值是每當(dāng)你改Prompt、換模型版本、調(diào)整檢索參數(shù)先跑一遍回歸測(cè)試集看看哪些case從通過變不通過了。這就是你Prompt優(yōu)化的剎車系統(tǒng)沒有它你就是在黑暗中盲調(diào)。5.3 用大模型評(píng)測(cè)大模型的陷阱現(xiàn)在業(yè)界很流行用大模型做裁判LLM-as-a-Judge給候選回答打分。這個(gè)方法效率極高但也有幾個(gè)坑我必須提醒你。裁判模型和被測(cè)模型如果是同一個(gè)裁判會(huì)很自然地偏好和自己風(fēng)格相似的輸出這叫自偏好偏差。我在對(duì)比兩個(gè)模型的回答質(zhì)量時(shí)踩過這個(gè)坑方案A是精調(diào)過的模型風(fēng)格簡潔方案B是通用模型風(fēng)格詳細(xì)。裁判模型輸出偏好的風(fēng)格完全偏向了詳細(xì)的一側(cè)導(dǎo)致我差點(diǎn)換掉其實(shí)表現(xiàn)更好的方案A。還有裁判模型會(huì)被Prompt里的順序影響。相同兩段回答誰放在前面誰就更可能被選為更好。解決辦法是多次運(yùn)行每次隨機(jī)交換次序取統(tǒng)計(jì)結(jié)果。裁判的溫度也要注意設(shè)太高的溫度評(píng)判結(jié)果自己都在隨機(jī)波動(dòng)評(píng)測(cè)本身就不穩(wěn)定了。6. 人人都焦慮的AI編程它到底改變了研發(fā)工作流還是逼瘋研發(fā)標(biāo)題里有個(gè)關(guān)鍵詞是ai編程提示詞這也是當(dāng)前熱度最高的話題之一。我不打算在這篇文章里長篇大論地聊AI編程輔助工具好不好用因?yàn)槲乙呀?jīng)在之前的文章里詳盡地寫過關(guān)于CodeBuddy如何在項(xiàng)目里實(shí)現(xiàn)Harness Engineering的思路。這里我想從一個(gè)更一線的角度去聊一下當(dāng)AI編程進(jìn)入日常研發(fā)流AI工程的方法論反而凸顯出來這兩者之間其實(shí)存在關(guān)系。你有沒有發(fā)現(xiàn)AI編程工具給每個(gè)人最大的變化是產(chǎn)出代碼的速度上去了但產(chǎn)出的代碼質(zhì)量更不可控了。以前你寫代碼編譯器告訴你有錯(cuò)你改了就是?,F(xiàn)在AI幫你生成一段代碼它語法完全正確、邏輯完全自洽但用到你的業(yè)務(wù)場(chǎng)景里邊界情況就是不對(duì)。它像一位來自另一個(gè)星球、熟悉語法但完全不懂你業(yè)務(wù)需求的高效實(shí)習(xí)生。這恰恰說明Prompt Engineering的思維正全面進(jìn)入編程場(chǎng)景。給AI編程助手寫Prompt與你給大模型設(shè)計(jì)輸出協(xié)議本質(zhì)上是同一種能力。明確邊界、給出示例、定義檢查項(xiàng)這些工整的工程手段在兩端都完全可以復(fù)用。所以我的建議始終是不要焦慮AI是不是要替代程序員而要把精力放在我能不能用工程方法把AI的產(chǎn)出納入可控的體系里。做AI工程的人練的恰恰就是這套掌控能力。7. 從零搭建一個(gè)可用的AI應(yīng)用一個(gè)簡化但完整的落地案例理論說了一堆不放一個(gè)自己能跑的最小案例總感覺不落地。我在這一章給你走一個(gè)完整的、可以復(fù)制的從零搭建過程。這里選擇做一個(gè)會(huì)議紀(jì)要助手輸入一段會(huì)議談話記錄文本輸出結(jié)構(gòu)化的會(huì)議紀(jì)要含結(jié)論、待辦事項(xiàng)、負(fù)責(zé)人和截止日期。這個(gè)案例麻雀雖小但正好能覆蓋前面講的幾個(gè)關(guān)鍵點(diǎn)任務(wù)分類、Prompt邊界設(shè)計(jì)、結(jié)構(gòu)化輸出、解析校驗(yàn)、評(píng)測(cè)。7.1 定義輸入輸出協(xié)議第一步不是寫代碼而是定協(xié)議。我們定義輸入為一段原始的會(huì)議文字轉(zhuǎn)寫內(nèi)容字符串。輸出為一個(gè)JSON對(duì)象{ summary: 會(huì)議整體結(jié)論摘要不超過100字, key_points: [重點(diǎn)結(jié)論1, 重點(diǎn)結(jié)論2], action_items: [ {task: 待辦事項(xiàng)描述, owner: 負(fù)責(zé)人, due_date: 截止日期或null, priority: high/medium/low} ], risks: [風(fēng)險(xiǎn)或阻塞項(xiàng)] }協(xié)議一旦定好馬上寫一份樣例這個(gè)就是我們對(duì)齊模型輸出的基準(zhǔn)。沒有樣例后面全亂套。7.2 設(shè)計(jì)系統(tǒng)提示詞系統(tǒng)提示詞按照前面說的邊界設(shè)計(jì)原則你是一個(gè)會(huì)議紀(jì)要整理助手。你的職責(zé)是從原始會(huì)議記錄中提取關(guān)鍵信息生成結(jié)構(gòu)化紀(jì)要。 輸入一段會(huì)議討論的文字轉(zhuǎn)寫。 輸出一個(gè)JSON對(duì)象字段如下 { summary: string, 不超過100字的整體摘要, key_points: [string], action_items: [{task: string, owner: string或null, due_date: string或null, priority: string}], risks: [string] } 規(guī)則 1. 只輸出JSON不添加任何解釋、前綴或Markdown代碼標(biāo)記。 2. 如果原始記錄中沒有明確提及負(fù)責(zé)人owner字段填null。 3. 如果原始記錄中沒有明確提及日期due_date字段填null。 4. 不要推測(cè)和編造原始記錄中不存在的信息。 5. action_items必須從原始記錄中的明確指示或約定提取禁止把泛泛的討論變成待辦事項(xiàng)。 6. 優(yōu)先使用原始記錄中的原話關(guān)鍵詞命名任務(wù)。 示例輸入輸出 輸入張三說下個(gè)版本我們要解決登錄超時(shí)的問題李四你來負(fù)責(zé)要在周五前給出方案。另外王五提到測(cè)試環(huán)境不太穩(wěn)定影響開發(fā)效率。 輸出 {summary: 確認(rèn)下個(gè)版本重點(diǎn)解決登錄超時(shí)問題并著手排查測(cè)試環(huán)境穩(wěn)定性。, key_points: [登錄超時(shí)問題列為下個(gè)版本核心, 測(cè)試環(huán)境不穩(wěn)定已影響開發(fā)效率], action_items: [{task: 給出登錄超時(shí)問題的解決方案, owner: 李四, due_date: 周五, priority: high}], risks: [測(cè)試環(huán)境不穩(wěn)定可能持續(xù)影響效率]}這里有兩個(gè)細(xì)節(jié)值得注意。一是owner/due_date沒有就填null這個(gè)規(guī)則看似簡單但對(duì)減少模型胡編至關(guān)重要。沒有這個(gè)規(guī)則模型會(huì)給每個(gè)action item都編一個(gè)負(fù)責(zé)人出來。二是示例刻意把測(cè)試環(huán)境不穩(wěn)定放進(jìn)了risks而不是action_items這其實(shí)是在教模型區(qū)分明確的行動(dòng)指令和泛泛的討論。李四的任務(wù)是有明確指向的你要負(fù)責(zé)而王五的話只是陳述現(xiàn)狀不代表誰承諾了要去修復(fù)環(huán)境。7.3 代碼實(shí)現(xiàn)與解析兜底調(diào)用代碼我就簡化處理核心邏輯如下import json from openai import OpenAI client OpenAI() SYSTEM_PROMPT 上面設(shè)計(jì)好的system prompt完整內(nèi)容 def generate_minutes(transcript: str) - dict: response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: f會(huì)議記錄\n{transcript}} ], response_format{type: json_object}, # 關(guān)鍵請(qǐng)求結(jié)構(gòu)化JSON temperature0.2, # 低溫度控制生成穩(wěn)定性 ) raw_output response.choices[0].message.content # 解析與校驗(yàn) try: result json.loads(raw_output) except json.JSONDecodeError: # 兜底策略如果解析失敗重試一次 # 如果重試仍失敗返回錯(cuò)誤包裝 ... # 對(duì)輸出做基本校驗(yàn)字段存在性、類型判斷 required_keys [summary, key_points, action_items, risks] for key in required_keys: if key not in result: raise ValueError(fMissing key: {key}) return result這段代碼雖然短但已經(jīng)包含了我前面講的大部分原則協(xié)議先行、低溫度控制、response_format約束、解析失敗兜底、字段校驗(yàn)。最后提一下溫度參數(shù)的選擇。我見過很多人把溫度調(diào)成0覺得這樣最穩(wěn)定。但實(shí)測(cè)中極低的溫度雖然減少了隨機(jī)性但也可能會(huì)讓輸出變得重復(fù)和呆板。會(huì)議紀(jì)要這種任務(wù)temperature0.2到0.3是比較合適的區(qū)間既能抑制胡言亂語又保留了必要的表達(dá)靈活性。7.4 給這個(gè)最小案例套上評(píng)測(cè)閉環(huán)光有代碼還不夠最后一步是建評(píng)測(cè)。對(duì)于這個(gè)會(huì)議紀(jì)要助手我用三個(gè)維度做快速驗(yàn)證字段完整度解析后的JSON是否包含全部4個(gè)頂層字段。action_items提取準(zhǔn)確率人工比對(duì)模型提取的待辦事項(xiàng)和原文中真正明確的指示計(jì)算精確率和召回率。拿上面示例來說如果模型把測(cè)試環(huán)境不穩(wěn)定也提取成了待辦項(xiàng)就算誤報(bào)。拒絕編造率在原始記錄里完全沒有提到負(fù)責(zé)人的情況下owner字段是否正確地填為null。這三條標(biāo)準(zhǔn)我再加幾個(gè)測(cè)試樣本就形成了一個(gè)小小的回歸測(cè)試集。以后無論我怎么調(diào)整提示詞先跑一遍這個(gè)基準(zhǔn)看看評(píng)分有沒有下降。這就從感覺上好像不錯(cuò)變成了數(shù)字上穩(wěn)定可靠。整個(gè)過程雖然樸素但它是AI工程從調(diào)參藝術(shù)走向工程評(píng)測(cè)的關(guān)鍵姿態(tài)。8. 最后說點(diǎn)掏心窩的話寫了這么多最想傳達(dá)的一個(gè)觀念是做AI工程最大的障礙很可能不是技術(shù)而是觀念。傳統(tǒng)開發(fā)講的是需求明確實(shí)現(xiàn)明確驗(yàn)收明確AI開發(fā)從根本上就不滿足這三個(gè)明確我們必須習(xí)慣在概率世界里做確定性系統(tǒng)。我現(xiàn)在的習(xí)慣是接到任何AI需求的第一個(gè)反應(yīng)不是用哪個(gè)模型而是這個(gè)業(yè)務(wù)對(duì)輸出質(zhì)量的容忍底線在哪。付不起幻覺代價(jià)的就把關(guān)鍵路徑用規(guī)則代碼鎖死允許一定程度發(fā)散的任務(wù)才放給模型去生成。然后凡是AI做的必須有日志、有評(píng)測(cè)、有兜底。這三件事做不到就不要上線。還有一個(gè)建議希望你能從一開始就重視建立自己的AI工程工具箱??梢允谴a庫里的一個(gè)包也可以是一個(gè)文檔合集。把好用的Prompt模板、踩過的坑、測(cè)試用例集、評(píng)測(cè)腳本都沉淀下來。別看不起這些土辦法等團(tuán)隊(duì)擴(kuò)張或者換項(xiàng)目的時(shí)候你會(huì)發(fā)現(xiàn)這些東西比任何一個(gè)模型參數(shù)都值錢。最后如果你正打算從零開啟自己的AI工程之路歡迎把第一個(gè)小項(xiàng)目拿給我看。我們一起把這個(gè)不確定性系統(tǒng)的掌控力一點(diǎn)點(diǎn)打磨出來。