實戰(zhàn):從RAG到Agent的生產(chǎn)級落地路徑與踩坑指南)
最近幫幾個團(tuán)隊評審AI應(yīng)用架構(gòu)發(fā)現(xiàn)一個特別普遍的問題大家把AI全棧開發(fā)當(dāng)成普通全棧開發(fā)來做設(shè)計接口、寫CRUD、接個大模型API、頁面套殼Demo一跑通就以為完事了結(jié)果一上生產(chǎn)就崩。崩的地方不是并發(fā)不是數(shù)據(jù)庫索引而是模型輸出不穩(wěn)定、Token成本失控、評估體系缺失這些傳統(tǒng)開發(fā)里根本不存在的變量。做AI全棧和做傳統(tǒng)全棧底層思維方式完全不同。傳統(tǒng)全棧面對的是確定性系統(tǒng)輸入輸出可預(yù)期AI全棧面對的是概率性系統(tǒng)同樣的Prompt今天和明天可能返回不一樣的結(jié)果。這篇文章不聊空泛的概念直接拆解我這幾年代團(tuán)隊落地AI應(yīng)用的完整實踐路徑從技術(shù)選型到RAG落地從Agent編排到測試評估再到生產(chǎn)環(huán)境里真正燒錢踩坑的地方。適合正在做AI應(yīng)用開發(fā)、準(zhǔn)備從傳統(tǒng)后端轉(zhuǎn)向AI方向、或者團(tuán)隊里需要一個人來扛AI全棧的讀者。1. 先想清楚AI全棧開發(fā)到底在開發(fā)什么1.1 從確定性系統(tǒng)到概率性系統(tǒng)傳統(tǒng)Web應(yīng)用的核心是信息處理用戶提交數(shù)據(jù)、后端校驗、落庫、查出來渲染頁面。每一步都有明確預(yù)期數(shù)據(jù)庫返回多少行、接口返回什么字段都是可以斷言的。測試好寫問題好定位整個系統(tǒng)像一條流水線。AI應(yīng)用不是這樣。大模型本身是個概率系統(tǒng)同樣的輸入、同樣的參數(shù)每次輸出都可能不同。溫度調(diào)到0也不是完全確定只是概率分布更集中。這意味著你做AI全棧開發(fā)時面對的每一個用戶請求都可能產(chǎn)生意料之外的輸出。模型可能答非所問可能編造不存在的事實可能突然拒絕回答甚至可能因為Prompt里一句含糊的表達(dá)就完全跑偏。我見過很多團(tuán)隊在這個問題上栽跟頭。他們用傳統(tǒng)思維設(shè)計AI產(chǎn)品認(rèn)為只要把模型API接進(jìn)來、把知識庫喂進(jìn)去產(chǎn)品就成立了。結(jié)果上線后發(fā)現(xiàn)用戶問法稍微變一下答案質(zhì)量就劇烈波動。這不是模型不夠聰明而是工程上沒有針對概率性輸出做兜底設(shè)計。AI全棧真正的復(fù)雜度不在調(diào)用模型這一步而在如何讓概率性輸出變得可控可用。你需要設(shè)計合理的Prompt結(jié)構(gòu)、建立上下文管理機制、做輸出校驗和兜底、通過評估體系持續(xù)觀測質(zhì)量波動。這些工作才是AI全棧的核心工作量。1.2 一個AI應(yīng)用的最小閉環(huán)我建模的時候習(xí)慣先畫一張閉環(huán)圖把AI應(yīng)用的完整鏈路畫出來再動手寫代碼這樣能避免只見樹木不見森林。一個標(biāo)準(zhǔn)的AI應(yīng)用閉環(huán)包含這幾個環(huán)節(jié)業(yè)務(wù)定義明確這個應(yīng)用到底解決什么問題目標(biāo)用戶是誰成功指標(biāo)是什么。 數(shù)據(jù)準(zhǔn)備收集、清洗、切分、向量化業(yè)務(wù)數(shù)據(jù)建立知識庫。這是RAG類應(yīng)用的地基。 上下文工程設(shè)計Prompt模板、構(gòu)建上下文窗口內(nèi)容決定模型能看到什么。 模型調(diào)用通過網(wǎng)關(guān)統(tǒng)一調(diào)用配置模型參數(shù)和fallback策略而不是在業(yè)務(wù)代碼里直接寫死某個廠商的SDK。 應(yīng)用邏輯包括Agent編排、工具調(diào)用、狀態(tài)流轉(zhuǎn)、業(yè)務(wù)規(guī)則兜底。這是傳統(tǒng)后端開發(fā)者的主場。 評估與觀測建立評測集持續(xù)打分追蹤線上trace監(jiān)控成本和延遲。 迭代反饋根據(jù)線上反饋和評估結(jié)果調(diào)整Prompt、補充知識、優(yōu)化Agent行為。這七個環(huán)節(jié)里第一項業(yè)務(wù)定義和最后兩項評估、觀測是很多從傳統(tǒng)開發(fā)轉(zhuǎn)過來的團(tuán)隊最容易忽略的部分。他們擅長第二到第五項因為那是標(biāo)準(zhǔn)的軟件工程范疇但往往做完第五項就上線了沒有評估閉環(huán)結(jié)果質(zhì)量全靠運氣。傳統(tǒng)全棧和AI全棧的分工差異用一張表能看得很清楚維度傳統(tǒng)全棧AI全棧核心任務(wù)信息處理確定性輸入輸出生成與決策概率性輸出主要復(fù)雜度業(yè)務(wù)邏輯、并發(fā)、數(shù)據(jù)一致性Prompt/上下文工程、模型行為控制、成本評估質(zhì)量保障單元測試、斷言、回歸評測集、LLM as Judge、線上反饋數(shù)據(jù)庫MySQL、PostgreSQL、Redis在原有基礎(chǔ)上增加向量數(shù)據(jù)庫運維關(guān)注點可用性、性能、容量再加Token成本、模型版本、延遲技能要求前后端、數(shù)據(jù)庫、運維傳統(tǒng)技能模型API編排、RAG、Agent、評估為什么會這樣因為模型作為一個外部依賴行為不像數(shù)據(jù)庫那么穩(wěn)定可控你必須圍繞它建立新的工程治理體系。這是AI全棧和傳統(tǒng)全棧最根本的區(qū)別。2. 技術(shù)選型模型網(wǎng)關(guān)、Agent框架與數(shù)據(jù)基礎(chǔ)設(shè)施2.1 模型網(wǎng)關(guān)LiteLLM Proxy的工程價值與最佳實踐很多團(tuán)隊在項目初期直接在前端或后端業(yè)務(wù)代碼里調(diào)用模型SDK比如在Python代碼里寫import openai在Java里直接引入某個廠商的SDK。短期看沒什么問題但做大了就發(fā)現(xiàn)幾個痛點模型換廠牌要改代碼多個模型Key散落各處沒法統(tǒng)一管理沒有統(tǒng)一的成本統(tǒng)計沒有故障轉(zhuǎn)移能力。這時候就需要一個模型網(wǎng)關(guān)。LiteLLM Proxy是當(dāng)前一個非常成熟的開源方案它以O(shè)penAI兼容格式對外提供服務(wù)背后可以代理各家模型平臺包括OpenAI、Anthropic以及國內(nèi)多家廠商的模型服務(wù)。它的核心價值就是把模型調(diào)用收口讓應(yīng)用層只認(rèn)一個base_url。我在項目中落地LiteLLM的基本配置大致是這樣model_list: - model_name: chat-main litellm_params: model: openai/gpt-4o api_key: os.environ[OPENAI_API_KEY] - model_name: chat-main litellm_params: model: deepseek/deepseek-chat api_key: os.environ[DEEPSEEK_API_KEY] - model_name: chat-main litellm_params: model: qwen/qwen-turbo-latest api_key: os.environ[DASHSCOPE_API_KEY] litellm_settings: drop_params: true set_verbose: false general_settings: master_key: sk-your-master-key database_url: postgresql://user:passlocalhost:5432/litellm幾個關(guān)鍵點展開說。第一多個模型共用同一個model_name比如chat-mainLiteLLM會自動做負(fù)載均衡。這不是簡單隨機它會根據(jù)每個模型的歷史調(diào)用延遲和失敗情況動態(tài)調(diào)整權(quán)重某個模型超時率升高就自動少分流量過去。這個機制在實戰(zhàn)場上救過我很多次上游模型抖動時用戶基本無感。第二fallback配置。不同平臺的服務(wù)都會有單點故障的時候我在配置里會給關(guān)鍵模型指定fallbacks參數(shù)主模型掛了自動切換備用模型。配置方式是在litellm_settings里針對某個model_name單獨指定model_list: - model_name: chat-main litellm_params: model: openai/gpt-4o api_key: os.environ[OPENAI_API_KEY] model_info: supports_function_calling: true - model_name: chat-fallback litellm_params: model: qwen/qwen-turbo-latest api_key: os.environ[DASHSCOPE_API_KEY]然后在代碼里用chat-main作為主模型做一層重試和切換邏輯。注意不同模型的function calling支持程度和輸出格式不完全一致切換時要確認(rèn)兼容性不然Agent應(yīng)用會拿到格式錯誤的結(jié)構(gòu)化輸出。第三開啟database_url之后LiteLLM會記錄每次請求的Token消耗、模型名、響應(yīng)時間這能解決AI應(yīng)用成本歸因的問題。我在公司內(nèi)部搭了一套成本面板每個業(yè)務(wù)線每個月消耗多少Token、對應(yīng)多少費用一查就有。沒有這一步AI應(yīng)用的成本就是一個黑盒財務(wù)月底來問的時候只能干瞪眼。最佳實踐方面我的建議是應(yīng)用層永遠(yuǎn)不要直接維護(hù)多家廠商的SDK統(tǒng)一走OpenAI兼容接口網(wǎng)關(guān)獨立部署和應(yīng)用服務(wù)解耦生產(chǎn)環(huán)境必須開啟成本日志和預(yù)算告警。2.2 Agent框架怎么選LangChain/LangGraph、Spring AI還是自研Agent框架是另外一個容易讓人糾結(jié)的點。LangChain鋪得很大生態(tài)全但抽象多有些場景反而被框架拖累。我在初期項目里直接用了LangChain遇到過一次很被動的局面業(yè)務(wù)需要自定義工具的重試和錯誤處理邏輯但在LangChain高層的AgentExecutor里改這部分很別扭后來不得不用LangGraph自己搭狀態(tài)圖才解決。我的選擇建議很簡單按團(tuán)隊基礎(chǔ)和場景復(fù)雜度來如果團(tuán)隊是Python且業(yè)務(wù)邏輯相對簡單比如只有一個工具調(diào)用不需要多輪規(guī)劃直接用原生代碼寫函數(shù)調(diào)用別上大框架。一個while循環(huán)加上tools參數(shù)就夠了沒必要引入幾百個依賴。如果業(yè)務(wù)確實需要多步規(guī)劃、多工具協(xié)作、有復(fù)雜狀態(tài)流轉(zhuǎn)用LangGraph。它比LangChain更接近工程化把Agent的每一步顯式建模成圖的節(jié)點和邊狀態(tài)管理也清晰適合需要精細(xì)控制的業(yè)務(wù)。代價是要花一段時間理解它的State、Node、Edge機制。如果團(tuán)隊是Java背景Spring AI值得認(rèn)真考慮。它對Java開發(fā)者友好很多概念和Spring Boot一脈相承團(tuán)隊上手快。尤其適合企業(yè)內(nèi)部系統(tǒng)集成和現(xiàn)有的Spring生態(tài)無縫結(jié)合。Java團(tuán)隊硬寫Python服務(wù)后續(xù)維護(hù)成本很高。如果業(yè)務(wù)很垂直、對行為有嚴(yán)格控制要求自研一個簡單的Agent編排引擎也是合理選擇。我自己在服務(wù)一個金融客戶時因為對工具調(diào)用有嚴(yán)格的白名單和審計要求最終選擇了自研狀態(tài)機。核心邏輯寫下來其實沒多少行但可控性提升了一個量級。我的判斷標(biāo)準(zhǔn)是不要為了用框架而用框架Agent的編排邏輯本質(zhì)上是業(yè)務(wù)邏輯應(yīng)該由業(yè)務(wù)團(tuán)隊掌控而不是由框架的抽象機制擺布??蚣艿膬r值在于解決通用問題當(dāng)你的業(yè)務(wù)需要在通用邏輯之外做大量定制時就該考慮是不是要自己來了。2.3 數(shù)據(jù)與算力基礎(chǔ)設(shè)施向量庫、推理引擎和部署方式在這輪AI應(yīng)用開發(fā)里基礎(chǔ)設(shè)施層面的變化主要來自數(shù)據(jù)側(cè)和算力側(cè)。數(shù)據(jù)側(cè)最明顯的增量是向量數(shù)據(jù)庫。選型時不要盲目追求功能多的先看自己的場景。我整理過一個表格直接列出來給大家參考方案適用場景優(yōu)點注意事項pgvector已有PostgreSQL數(shù)據(jù)量不大復(fù)用現(xiàn)有數(shù)據(jù)庫事務(wù)一致性好超大規(guī)模檢索性能一般Qdrant獨立向量檢索讀寫性能要求高Rust實現(xiàn)性能好支持過濾需要單獨部署維護(hù)Milvus億級向量、復(fù)雜檢索分布式能力強功能豐富運維成本偏高Elasticsearch需要全文檢索向量混合已有ES團(tuán)隊和經(jīng)驗內(nèi)存消耗大成本高數(shù)據(jù)量小于100萬條向量的場景pgvector完全能頂住不必為了向量數(shù)據(jù)庫單獨引入一個中間件。我在實際項目里很多場景用pgvector就解決了部署簡單備份和事務(wù)都沿用原來的體系。數(shù)據(jù)量上來或需要復(fù)雜的過濾條件和高并發(fā)再考慮獨立向量庫。算力側(cè)則是推理引擎的選型。如果走API路線基本不需要管部署直接通過LiteLLM網(wǎng)關(guān)接入即可。如果需要私有化部署現(xiàn)在基本會考慮vLLM它對主流開源模型支持好吞吐量高而且直接提供OpenAI兼容的接口。一個典型的部署命令vllm serve Qwen/Qwen2.5-14B-Instruct \ --served-model-name my-qwen \ --port 8000 \ --max-model-len 32768 \ --gpu-memory-utilization 0.9 \ --tensor-parallel-size 2量化方面AWQ和GPTQ是當(dāng)前比較成熟的選擇能把參數(shù)量打下來不少顯存占用低很多。GPU資源緊張的團(tuán)隊先量化再部署推理速度通常有提升。更激進(jìn)的方案是把小模型部署到CPU配合量化跑一些簡單分類任務(wù)成本能壓得很低。3. 落地一條AI業(yè)務(wù)鏈路從RAG到Agent再到部署3.1 RAG的工程細(xì)節(jié)切分、召回與重排序RAG是現(xiàn)在AI應(yīng)用里最常用的知識注入方式很多團(tuán)隊一開始就做RAG但做出來的召回質(zhì)量差別很大。差別主要不在Embedding模型選誰而在工程細(xì)節(jié)。文檔切分是第一關(guān)。很多初學(xué)的人直接用固定chunk_size切分比如512個字符一段不做重疊。這樣很容易把一段完整的內(nèi)容從中間切斷導(dǎo)致語義不完整召回時老是丟關(guān)鍵信息。我建議的切分策略是語義邊界優(yōu)先固定長度兜底。用LangChain或LlamaIndex里的遞歸切分器按段落、句子層級逐級切同時設(shè)置重疊區(qū)域from langchain_text_splitters import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size512, chunk_overlap128, separators[\n\n, \n, 。, , , , , , ], keep_separatorTrue, ) chunks splitter.split_text(document)chunk_overlap設(shè)為chunk_size的四分之一比較合適。過小起不到上下文銜接作用過大則產(chǎn)生大量冗余向量浪費存儲和檢索成本。對中文文檔看分隔符的優(yōu)先級把。、、放在比較靠前的位置讓切分盡量落在語義完整的句子邊界。召回環(huán)節(jié)單靠Embedding相似度往往不夠。Embedding模型擅長捕捉語義相似度但對關(guān)鍵詞精確匹配和否定表達(dá)這類信息不太敏感。我現(xiàn)在的標(biāo)準(zhǔn)做法是雙路召回加Rerank一路用向量相似度召回一路用BM25關(guān)鍵詞召回兩路結(jié)果合并后用Cross-Encoder模型重排序把最相關(guān)的Top K排到前面。Rerank模型讀的是query和doc的完整句子對相關(guān)性判斷比單純的向量計算準(zhǔn)確得多。Embedding模型本身的人也要注意。上線后如果想換一個Embedding模型必須把知識庫里的所有向量重新生成一遍否則新舊向量不在同一個空間里召回準(zhǔn)確率直接崩。這個坑我踩過一次上線前跑了幾十萬條向量換模型后沒注意老向量結(jié)果線上召回質(zhì)量下降明顯。3.2 Agent編排的核心循環(huán)從ReAct到可維護(hù)的狀態(tài)機Agent和普通ChatBot的區(qū)別在于能不能使用工具。ChatBot只能基于模型內(nèi)部知識回答問題Agent能查數(shù)據(jù)庫、調(diào)API、發(fā)郵件通過多步推理完成一個相對復(fù)雜的任務(wù)。這背后最基礎(chǔ)的實現(xiàn)就是ReAct模式思考、調(diào)用工具、觀察結(jié)果、再思考。一個最簡Agent循環(huán)用原生代碼可以這樣寫def run_agent(user_message, llm, tools, max_steps8): messages [{role: user, content: user_message}] for step in range(max_steps): response llm.chat( messagesmessages, toolstools, tool_choiceauto, ) if not response.tool_calls: return response.content messages.append(response.message) for tool_call in response.tool_calls: result execute_tool(tool_call.function.name, tool_call.function.arguments) messages.append({ role: tool, tool_call_id: tool_call.id, content: result, }) raise RuntimeError(Agent reached max steps)這個循環(huán)雖然簡單但它抓住了Agent的最核心機制把工具調(diào)用結(jié)果作為新的上下文喂回給模型讓模型基于真實工具結(jié)果繼續(xù)推理。很多Agent框架做的事情本質(zhì)上就是這個循環(huán)只不過加上了更多的狀態(tài)管理、記憶和編排能力。實際開發(fā)里需要特別留意幾個問題。max_steps必須設(shè)置而且不能太大。沒有步數(shù)限制的Agent就是個失控的循環(huán)它會不斷調(diào)用工具、不斷失敗重試把Token消耗放大好幾倍。我在項目里默認(rèn)設(shè)8步超出就終止并返回兜底文案。工具調(diào)用必須有清晰的錯誤返回。工具內(nèi)部報異常時不要把堆棧拋給模型而是捕獲后返回一段結(jié)構(gòu)化的錯誤描述比如{error: USER_NOT_FOUND, message: 用戶ID不存在}好的Agent會根據(jù)錯誤信息自我修正一次但如果錯誤信息是詭異的堆棧模型大概率會亂來。工具參數(shù)校驗要嚴(yán)格。模型生成的JSON參數(shù)偶爾會不合法要么是字段缺失要么是類型錯誤。建議在execute_tool階段做一次JSON Schema校驗非法參數(shù)直接返回校驗錯誤給模型重新生成別讓臟數(shù)據(jù)進(jìn)入業(yè)務(wù)系統(tǒng)。再往后就是多Agent協(xié)作和復(fù)雜狀態(tài)流了。這個階段我一般用LangGraph來管理把每個Agent步驟定義成圖中的節(jié)點節(jié)點間通過State共享數(shù)據(jù)路由邏輯顯式化。雖然多寫了一些樣板代碼但整個流程可讀性強測試也好寫出了問題能定位到具體節(jié)點。3.3 模型部署的兩種路徑托管API與私有化推理模型調(diào)用是走托管API還是私有化部署是每個團(tuán)隊都要做的選擇。我的判斷依據(jù)是數(shù)據(jù)敏感度、調(diào)用量、以及成本結(jié)構(gòu)。數(shù)據(jù)敏感度是第一位的??蛻魯?shù)據(jù)必須留在內(nèi)網(wǎng)那就不用糾結(jié)直接私有化部署。目前開源模型的能力已經(jīng)足夠支撐大量業(yè)務(wù)場景Qwen系列、DeepSeek系列這些模型在很多垂直任務(wù)上并不遜色于閉源API。調(diào)用量大的場景也適合私有化。按量付費API在調(diào)用量大到一定程度以后成本會超過GPU服務(wù)器折舊。我算過一個典型case一個日請求量百萬級別的應(yīng)用如果大部分請求走中等規(guī)模的開源模型兩個月左右的API費用可能就夠買一臺能承載這個負(fù)載的GPU服務(wù)器了。這里還沒算數(shù)據(jù)出網(wǎng)帶來的額外延遲問題。私有化部署的工程要點主要是模型加載、并發(fā)配置、顯存管理。vLLM的--gpu-memory-utilization參數(shù)建議設(shè)在0.85到0.95之間太低浪費顯存太高容易OOM。--max-model-len決定最大輸入長度直接影響顯存占用不要盲目設(shè)大。能開到32K就開32K不需要長上下文任務(wù)的場景開到16K就夠用省下的顯存能換更高的并發(fā)。托管API路徑也有它的優(yōu)勢幾乎沒有運維負(fù)擔(dān)模型版本迭代不用自己管開箱即用。適合快速驗證產(chǎn)品、調(diào)用量不太大的階段。我見過不少團(tuán)隊在早期用API把產(chǎn)品跑起來等用戶量和成本上來了再遷移到私有化部署這個節(jié)奏我認(rèn)為是合理的。不管哪條路徑應(yīng)用層都不要直接連模型服務(wù)統(tǒng)一走LiteLLM網(wǎng)關(guān)。這樣切換API模型到私有化模型時只需要改網(wǎng)關(guān)配置應(yīng)用代碼一行都不用動。4. AI應(yīng)用怎么測試與評估沒有標(biāo)準(zhǔn)答案的驗收難題4.1 為什么傳統(tǒng)的測試思維在AI這里失效傳統(tǒng)軟件開發(fā)里測試有一套成熟的方法論。寫個單元測試斷言輸入輸出跑CI回歸測試一套流程下來質(zhì)量心里有底。但到了AI應(yīng)用這里傳統(tǒng)的斷言體系直接失效。你沒辦法斷言用戶問發(fā)票怎么開模型返回的內(nèi)容是否合格因為合格的答案不是唯一的模型每次生成的答案也不完全一樣。你可能可以斷言返回結(jié)果里包含發(fā)票兩個字但這種斷言太弱了根本保證不了答案質(zhì)量。更麻煩的是你怎么定義相關(guān)性怎么定義回答正確這些在傳統(tǒng)測試?yán)锔緹o法直接表達(dá)。所以我建議團(tuán)隊做AI應(yīng)用測試時觀念要轉(zhuǎn)個彎不要把AI測試當(dāng)作傳統(tǒng)測試一樣追求通過/不通過而是把它當(dāng)作持續(xù)的質(zhì)量評估系統(tǒng)來搭建。核心是建設(shè)評測集定義評分維度用工具化手段持續(xù)打分監(jiān)控質(zhì)量趨勢而不是糾結(jié)單次對錯。4.2 LLM as Judge用評估維度把主觀質(zhì)量變成可量化指標(biāo)LLM as Judge就是用另一個大模型來評估目標(biāo)模型的輸出質(zhì)量。這個概念聽起來有點遞歸但在工程上是有效的因為它解決了誰來打分的問題。人工打分太慢、太貴無法規(guī)模化規(guī)則匹配做不到語義層面的判斷LLM Judge能在很大程度上接近人的判斷。我在實際項目中用LLM as Judge的方式是設(shè)計一個評分Prompt讓Judge模型按維度打分judge_prompt 你是AI應(yīng)用質(zhì)量評估員。請對以下模型回答進(jìn)行評估輸出0到5分。 評估維度 1. 相關(guān)性模型回答是否針對用戶問題有沒有答非所問。 2. 忠實度模型回答是否基于提供的知識庫上下文有沒有編造內(nèi)容。 3. 完整性模型回答是否覆蓋了問題涉及的關(guān)鍵信息點。 用戶問題{question} 知識庫上下文{context} 模型回答{response} 請直接輸出JSON格式如下 {relevance: 0-5, faithfulness: 0-5, completeness: 0-5, reason: 簡要說明} 打分結(jié)果可以匯總成質(zhì)量報告比如按日維度統(tǒng)計平均分、最低分、各個維度的分布。當(dāng)某個維度的分?jǐn)?shù)持續(xù)下降時大概率是知識庫出問題了、Prompt被改壞了或者模型服務(wù)端悄悄換了版本。LLM as Judge也有自己的坑。Judge模型傾向于給更長、更詳細(xì)的答案打高分即使答案冗長且不直接它對數(shù)字和事實的校驗?zāi)芰τ邢奕绻卮鹄锍霈F(xiàn)編造的數(shù)據(jù)Judge不一定能識別出來。所以在事實類場景我會疊加規(guī)則校驗比如從回復(fù)里抽取日期、金額等信息和知識庫做精確比對必要時對接外部分類模型雙重復(fù)核。每次大版本改動后我會抽出一批用戶問題做一次人工抽查把人工評分和LLM Judge評分做一個校準(zhǔn)防止Judge的評價標(biāo)準(zhǔn)和業(yè)務(wù)目標(biāo)漂移。評測集不是一次性的它是一個持續(xù)維護(hù)的資產(chǎn)每發(fā)現(xiàn)一個線上badcase就補充到評測集里。4.3 分層的AI測試策略和工具鏈和傳統(tǒng)軟件測試一樣AI應(yīng)用也需要分層測試只是每層的內(nèi)容要針對AI的特殊性做調(diào)整。層級測什么方法單元測試純函數(shù)、工具函數(shù)、數(shù)據(jù)解析傳統(tǒng)pytest斷言輸入輸出組件測試單步Agent行為、工具調(diào)用正確性mock LLM響應(yīng)驗證工具參數(shù)端到端測試完整用戶場景多輪對話復(fù)雜任務(wù)用評測集跑完整流程LLM Judge打分線上評估真實用戶反饋、線上trace抽樣反饋按鈕、人工抽檢、質(zhì)量看板組件測試?yán)镆粋€實用的做法是用錄制的LLM響應(yīng)來跑回歸。真實調(diào)用模型既慢又貴還不可控我在測試環(huán)境把不同場景的LLM響應(yīng)固化成JSON文件單元測試?yán)镏苯幼x這些錄制文件快速驗證Agent編排邏輯是否正確。只有端到端測試才調(diào)用真實模型。工具鏈上我現(xiàn)在常用的組合是pytest寫單元和集成測試Langfuse記錄線上trace和評估分?jǐn)?shù)配合LiteLLM的成本日志做質(zhì)量與成本的關(guān)聯(lián)分析。如果團(tuán)隊想快速搭建評估體系可以試試PromptFoo或Traceloop這些都是成熟的開源方案比從零開發(fā)省事很多。這里要特別說一句AI應(yīng)用團(tuán)隊的測試工程師角色很重要。這位工程師不只是寫腳本更要定義評估標(biāo)準(zhǔn)、設(shè)計評測集、分析badcase模式。產(chǎn)品迭代過程中的質(zhì)量問題很多都需要測試工程師做根源分析是Prompt問題、知識庫問題、還是模型問題然后再推動修復(fù)。5. 生產(chǎn)環(huán)境里真正燒錢和踩坑的地方5.1 Token消耗失控的四個典型場景與對策AI應(yīng)用最大的隱藏成本不是服務(wù)器是Token。很多團(tuán)隊等月底賬單出來才意識到問題那時候已經(jīng)晚了。我總結(jié)了幾個最典型的Token浪費場景。第一個是System Prompt過長。有些團(tuán)隊為了追求穩(wěn)定把幾百條規(guī)則全部塞進(jìn)System Prompt每次請求都把這些內(nèi)容原樣傳給模型。假設(shè)System Prompt有3000 Token一天一百萬次請求光System Prompt就是30億Token的消耗。對策是精簡System Prompt只保留真正影響全局的規(guī)則業(yè)務(wù)細(xì)節(jié)放到工具描述或知識庫里按需加載。第二個是Agent遞歸調(diào)用失控。Agent在循環(huán)里不斷調(diào)用工具、拿到結(jié)果再問模型每一步都會重復(fù)傳遞歷史上下文。步驟一多Token呈指數(shù)級增長。對策是嚴(yán)格控制max_steps及時清理中間過程只保留和當(dāng)前任務(wù)高相關(guān)的歷史記錄。第三個是重試機制太粗暴。線上模型偶爾會超時或報錯直接重試沒問題但重試時如果不加退避、不考慮成本往往會在模型服務(wù)抖動時瘋狂重試造成大額消耗。對策是重試加指數(shù)退避和熔斷連續(xù)失敗幾次就停止請求切換到fallback模型。第四個是日志全量記錄。為了調(diào)試方便把每次request和response完整打進(jìn)日志長期積累下來日志存儲成本也不小。對策是線上只記錄關(guān)鍵字段比如模型名、Token數(shù)、延遲、響應(yīng)狀態(tài)完整請求內(nèi)容只在小流量環(huán)境記錄。這里給一個成本估算的實例。假設(shè)一個Agent應(yīng)用每輪任務(wù)調(diào)用5次模型每次請求輸入3000 Token、輸出500 Token。一個用戶一天執(zhí)行10個任務(wù)那就是50次模型調(diào)用。如果1000個用戶按當(dāng)前中等規(guī)模API模型的大致價格計算一天的成本可能在兩百元左右一個月就是六千元左右。這個量級還不算大但如果Prompt沒有優(yōu)化、Agent有失控循環(huán)這個數(shù)字翻五倍十倍非??臁?.2 延遲、成本與體驗的平衡AI應(yīng)用的用戶體驗很大程度取決于響應(yīng)速度。一個要等30秒才出結(jié)果的頁面再聰明也沒有用。延遲優(yōu)化有幾個實用手段。首當(dāng)其沖是Streaming輸出不要讓用戶等整個響應(yīng)結(jié)束而是把Token一段一段吐出來用戶第一句話可能兩三秒就出現(xiàn)了體感會好很多。如果你用的框架不支持Streaming建議盡快換。其次是模型分級。不是所有請求都需要最強的模型簡單的意圖識別、文本分類用一個輕量小模型就能完成響應(yīng)快成本低。大模型只處理真正復(fù)雜的任務(wù)。比如客服場景先讓小模型判斷用戶情緒和問題類型一般的售前咨詢直接小模型回答難的問題才轉(zhuǎn)大模型。再次是語義緩存。很多用戶問的問題高度重復(fù)比如怎么退貨客服電話多少。傳統(tǒng)做法是緩存接口響應(yīng)但AI應(yīng)用沒法直接緩存因為問法千變?nèi)f化。語義緩存可以解決這個問題把用戶問題做向量化和緩存里的歷史問題比對相似度超過閾值直接返回上一次的答案。這個方案能省掉大量重復(fù)的模型調(diào)用在公司內(nèi)部客服類應(yīng)用上效果特別明顯。5.3 可觀測性與內(nèi)容安全護(hù)欄傳統(tǒng)應(yīng)用的可觀測性關(guān)注請求量、錯誤率、延遲。AI應(yīng)用在此基礎(chǔ)上要增加Token消耗、模型輸出質(zhì)量、Agent運行軌跡這些新維度。我在項目里用Langfuse記錄每一次LLM調(diào)用的輸入輸出、耗時的Token數(shù)、Agent每一步的工具調(diào)用。出了問題按用戶會話ID一查整條鏈路一目了然這在調(diào)試Agent應(yīng)用時幾乎是剛需。具體做法是給每個會話分配一個trace_id從用戶請求入口貫穿到每一次LLM調(diào)用和工具調(diào)用Langfuse自動把這些信息關(guān)聯(lián)成一條trace。生產(chǎn)環(huán)境的告警我設(shè)置了三個核心指標(biāo)Token消耗日環(huán)比突增、端到端請求延遲P99超過閾值、用戶反饋負(fù)面率上升。這三個指標(biāo)基本能覆蓋AI應(yīng)用主要的線上風(fēng)險。內(nèi)容安全方面這個是繞不開的話題。AI應(yīng)用面向用戶輸出內(nèi)容必須可控合規(guī)。我建議在架構(gòu)上做三道護(hù)欄輸入端做Prompt注入檢測防止用戶通過惡意Prompt讓模型執(zhí)行非預(yù)期指令輸出端做敏感內(nèi)容過濾屏蔽風(fēng)險詞匯和違規(guī)內(nèi)容數(shù)據(jù)側(cè)做脫敏用戶隱私字段在進(jìn)入模型前替換為占位符。這些護(hù)欄和模型能力無關(guān)而是工程上必須做的安全措施也是保障產(chǎn)品長期健康運行的基礎(chǔ)。不做好這一步應(yīng)用上線后隨時可能因為內(nèi)容問題翻車到時候再補成本就高了。6. 一些個人經(jīng)驗和最后的建議AI全棧開發(fā)做到現(xiàn)在我覺得最重要的不是掌握了多少框架而是建立了一套適應(yīng)概率性系統(tǒng)的工程思維。先寫數(shù)據(jù)流圖再寫代碼。AI應(yīng)用的數(shù)據(jù)流向遠(yuǎn)比傳統(tǒng)應(yīng)用復(fù)雜模型調(diào)用、知識檢索、工具執(zhí)行、結(jié)果回填每個環(huán)節(jié)都有數(shù)據(jù)轉(zhuǎn)換。畫清楚數(shù)據(jù)流再動手能省掉后面大量的返工成本。我從一開始就吃過這個虧上來直接寫代碼寫到一半發(fā)現(xiàn)知識庫和Agent的數(shù)據(jù)結(jié)構(gòu)不匹配全部重構(gòu)。Prompt一定要納入版本管理。很多團(tuán)隊用文檔管理Prompt但文檔和代碼是脫節(jié)的。我把Prompt模板放到Git倉庫里和代碼一起管理每次改動都留歷史記錄。Prompt變更導(dǎo)致的質(zhì)量波動可以通過評測集快速定位。沒有版本管理的Prompt線上出問題都不知道改了什么。評測集是團(tuán)隊的公共資產(chǎn)。每發(fā)現(xiàn)一個線上badcase就補進(jìn)評測集持續(xù)沉淀。評測集擴到一定規(guī)模后做Agent的代碼重構(gòu)、模型升級、Prompt優(yōu)化都敢動手因為跑一遍評測集就知道改動有沒有破壞原有的能力。后來我就養(yǎng)成了習(xí)慣任何一個新功能上線先寫評測用例再寫功能代碼。成本賬本要每周看一次。不是月底是每周。Token消耗是非常靈敏的質(zhì)量指標(biāo)某一天消耗突然翻倍往往意味著Agent跑出了一個失控的循環(huán)或者Prompt被改出了bug。每周看一眼Token趨勢很多問題能在早期就發(fā)現(xiàn)省下的錢遠(yuǎn)超花在這幾分鐘上的時間成本。AI輔助編程提效明顯但人要對代碼負(fù)責(zé)。我團(tuán)隊里現(xiàn)在大量使用AI輔助寫代碼效率提升非常明顯但每一段AI生成的代碼都必須經(jīng)過人工審查。AI能幫你寫框架代碼、寫測試用例、解釋復(fù)雜邏輯但它不了解你的業(yè)務(wù)上下文生成的長鏈路模板代碼容易引入隱蔽的邏輯錯誤。AI是很好的結(jié)對編程搭子但最終簽字負(fù)責(zé)的仍然是人。最后想說的是AI全棧開發(fā)還在快速演進(jìn)框架和工具隔幾個月可能就換一批但底層的工程問題不會變?nèi)绾巫尭怕市韵到y(tǒng)穩(wěn)定可用如何把模型能力轉(zhuǎn)化成可度量的業(yè)務(wù)價值如何控制成本和安全風(fēng)險。把這些核心問題想清楚了選型就順其自然。哪怕今天用的框架明天就過時了這些思維方式也依然有效。