全鏈路觀測:基于LangSmith的調(diào)試與優(yōu)化實(shí)踐)
1. 為什么 RAG 系統(tǒng)需要一臺“心電監(jiān)護(hù)儀”做過 RAG 應(yīng)用的人大概都有過這種體驗(yàn)用戶反饋“回答不準(zhǔn)”你打開日志一看檢索回來的文檔看起來沒問題生成的內(nèi)容卻答非所問或者明明知識庫里有的內(nèi)容模型就是視而不見。更讓人頭疼的是這類問題往往無法穩(wěn)定復(fù)現(xiàn)——同一個(gè)問題問兩遍一次答對一次答錯(cuò)你連從哪里下手排查都不知道。這就是 RAG 系統(tǒng)最典型的困境鏈路太長黑盒太多。一個(gè)完整的 RAG 流程至少包含查詢改寫、向量化、向量檢索、重排序、上下文拼接、Prompt 組裝、大模型生成這六七個(gè)環(huán)節(jié)任何一個(gè)環(huán)節(jié)出問題最終都會表現(xiàn)為“回答不好”。但“回答不好”這四個(gè)字根本無法定位到底是哪一環(huán)出了故障。打個(gè)比方這就像醫(yī)生看病。病人說“我不舒服”你不可能直接開藥你得先量體溫、測血壓、做心電圖把模糊的主訴轉(zhuǎn)化成一個(gè)個(gè)可觀測的指標(biāo)才能判斷問題出在心臟、肺還是別的地方。RAG 系統(tǒng)也一樣它需要的不是“感覺回答不好”這種主觀判斷而是一套能記錄每個(gè)環(huán)節(jié)輸入輸出、耗時(shí)、token 消耗、中間狀態(tài)的全鏈路觀測能力。LangSmith 就是干這個(gè)的。它本質(zhì)上是一個(gè)面向大模型應(yīng)用的追蹤與評估平臺能把你 RAG 鏈路里的每一次調(diào)用——不管是檢索器的調(diào)用、LLM 的調(diào)用還是自定義函數(shù)的執(zhí)行——都記錄下來形成一棵可視化的調(diào)用樹。你可以把它理解成給 RAG 系統(tǒng)裝了一臺心電監(jiān)護(hù)儀每一次心跳調(diào)用都被記錄波形輸入輸出都被保存一旦出現(xiàn)異常你能精確回放到出問題的那一次。這篇文章主要面向已經(jīng)用 LangChain 或類似框架搭過 RAG 應(yīng)用、但被調(diào)試問題折磨過的開發(fā)者。如果你還在用 print 大法排查 RAG 問題或者你的日志系統(tǒng)只能告訴你“這次請求失敗了”卻說不清失敗在哪一步那這套觀測方案值得你花時(shí)間搭起來。我會從整體設(shè)計(jì)思路講到具體的接入步驟、參數(shù)配置、常見坑盡量讓你看完就能在自己的項(xiàng)目里復(fù)現(xiàn)。2. 全鏈路觀測的整體設(shè)計(jì)思路拆解2.1 觀測粒度從“請求級”下沉到“節(jié)點(diǎn)級”大部分人的第一版 RAG 日志是這樣的記錄用戶問了什么、最終答了什么、耗時(shí)多少。這是請求級觀測粒度太粗。當(dāng)回答出錯(cuò)時(shí)你只知道“這次請求失敗了”但檢索到的文檔是什么、重排序后的順序是什么、最終塞進(jìn) Prompt 的上下文長什么樣全都不知道。LangSmith 的核心價(jià)值在于把觀測粒度下沉到節(jié)點(diǎn)級。它通過 Trace追蹤和 Run運(yùn)行單元兩級結(jié)構(gòu)來組織數(shù)據(jù)一次完整的用戶請求對應(yīng)一個(gè) TraceTrace 內(nèi)部的每一個(gè)環(huán)節(jié)——檢索、重排、生成——都是一個(gè) Run。每個(gè) Run 都記錄了輸入、輸出、開始時(shí)間、結(jié)束時(shí)間、耗時(shí)、token 用量、錯(cuò)誤信息。這樣你排查問題時(shí)可以逐層展開看到每個(gè)節(jié)點(diǎn)的真實(shí)數(shù)據(jù)。我個(gè)人的經(jīng)驗(yàn)是觀測粒度至少要下沉到“能獨(dú)立判斷對錯(cuò)”的最小單元。比如檢索節(jié)點(diǎn)它的輸出是一組文檔你能判斷這組文檔是否相關(guān)生成節(jié)點(diǎn)它的輸出是文本你能判斷是否答非所問。而像“向量化”這種中間步驟如果它只是把文本轉(zhuǎn)成向量你肉眼也看不出對錯(cuò)那就不必單獨(dú)建 Run合并到檢索節(jié)點(diǎn)里即可。粒度太細(xì)會讓 Trace 樹變得臃腫反而影響排查效率。2.2 追蹤方式選型自動追蹤 vs 手動埋點(diǎn)LangSmith 提供兩種追蹤方式。自動追蹤是指你用了 LangChain 的組件比如RetrievalQA、VectorStoreRetriever只要配置好環(huán)境變量框架會自動幫你上報(bào) Trace幾乎零代碼改動。手動埋點(diǎn)是指你用traceable裝飾器或trace上下文管理器手動標(biāo)記自定義函數(shù)的執(zhí)行。那到底該用哪種我的建議是混合使用??蚣軆?nèi)置的組件用自動追蹤省事且不會漏你自己寫的業(yè)務(wù)邏輯——比如查詢改寫、自定義的重排序算法、結(jié)果后處理——用手動埋點(diǎn)。因?yàn)樽詣幼粉欀荒芨采w框架認(rèn)識的組件你寫在chain外面的那些函數(shù)框架根本不知道它們的存在自然也不會記錄。這里有個(gè)容易踩的坑很多人以為配了環(huán)境變量就萬事大吉結(jié)果發(fā)現(xiàn)自己寫的rerank函數(shù)在 Trace 里完全看不到排查問題時(shí)缺了最關(guān)鍵的一環(huán)。所以接入前先梳理一遍你的 RAG 鏈路把每個(gè)環(huán)節(jié)標(biāo)注為“框架組件”還是“自定義邏輯”前者自動追蹤后者手動埋點(diǎn)一個(gè)都別漏。2.3 數(shù)據(jù)采集的邊界記什么不記什么全鏈路觀測不等于“什么都記”。LangSmith 會把每次 Run 的輸入輸出完整上傳如果你的 RAG 系統(tǒng)處理的是用戶隱私數(shù)據(jù)、內(nèi)部文檔、商業(yè)機(jī)密那這些內(nèi)容會一并上傳到平臺。這是接入前必須想清楚的問題。我的處理原則是觀測數(shù)據(jù)只保留排查問題必需的最小集。具體來說檢索到的文檔內(nèi)容、最終生成的回答、Prompt 模板這些是排查必需的必須記但用戶的原始身份信息、文檔里的敏感字段可以在上報(bào)前做脫敏處理。LangSmith 支持通過環(huán)境變量控制是否記錄完整內(nèi)容也支持在代碼里對輸入輸出做預(yù)處理。如果你對數(shù)據(jù)合規(guī)有要求建議在接入前先和團(tuán)隊(duì)確認(rèn)數(shù)據(jù)邊界別等出了問題再補(bǔ)救。另外觀測本身是有成本的。每次 Run 上報(bào)都會產(chǎn)生網(wǎng)絡(luò)請求和存儲開銷如果你的 RAG 系統(tǒng) QPS 很高全量上報(bào)可能會帶來可觀的費(fèi)用和延遲。實(shí)踐中可以采取采樣策略正常請求按比例采樣比如 10%錯(cuò)誤請求和慢請求全量上報(bào)。這樣既控制了成本又保證了問題樣本不丟失。3. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)3.1 環(huán)境準(zhǔn)備與關(guān)鍵配置項(xiàng)接入 LangSmith 的第一步是拿到 API Key 并配置環(huán)境變量。這一步看起來簡單但有幾個(gè)配置項(xiàng)直接決定了追蹤能不能正常工作我逐個(gè)說明。export LANGCHAIN_TRACING_V2true export LANGCHAIN_ENDPOINThttps://api.smith.langchain.com export LANGCHAIN_API_KEY你的API Key export LANGCHAIN_PROJECTrag-observability-demoLANGCHAIN_TRACING_V2是總開關(guān)必須設(shè)為true否則后面配了也白搭。LANGCHAIN_PROJECT是項(xiàng)目名同一個(gè)項(xiàng)目下的 Trace 會聚合在一起建議按環(huán)境區(qū)分比如rag-dev、rag-prod避免測試數(shù)據(jù)污染生產(chǎn)觀測。LANGCHAIN_ENDPOINT一般用默認(rèn)值即可除非你有自建的服務(wù)地址。注意環(huán)境變量必須在導(dǎo)入 LangChain 相關(guān)模塊之前設(shè)置好。我見過有人把os.environ的設(shè)置寫在 import 之后結(jié)果追蹤死活不生效排查了半天才發(fā)現(xiàn)是加載順序問題。如果你在代碼里設(shè)置環(huán)境變量務(wù)必放在所有 LangChain import 的最前面。還有一個(gè)容易被忽略的點(diǎn)如果你用的是異步調(diào)用ainvoke、astream要確認(rèn)你的 LangSmith SDK 版本支持異步追蹤。早期版本對異步的支持不完善會出現(xiàn) Trace 丟失或 Run 不完整的情況。建議把langsmith和langchain都升到較新的版本避免這類兼容性問題。3.2 自動追蹤的接入與驗(yàn)證自動追蹤的接入成本極低配置好環(huán)境變量后你原有的 LangChain 代碼基本不用改。但“不用改”不代表“不用驗(yàn)證”接入后第一件事是確認(rèn) Trace 真的上報(bào)成功了。from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate llm ChatOpenAI(modelgpt-4o-mini) prompt ChatPromptTemplate.from_template(用一句話解釋{concept}) chain prompt | llm result chain.invoke({concept: 向量檢索}) print(result.content)跑完這段代碼去 LangSmith 的項(xiàng)目頁面看應(yīng)該能看到一條 Trace展開后有兩個(gè) Run一個(gè)是 Prompt 的格式化一個(gè)是 LLM 的調(diào)用。如果只看到一個(gè) Run或者干脆什么都沒有那就要檢查環(huán)境變量和版本了。驗(yàn)證的時(shí)候有個(gè)小技巧故意制造一個(gè)錯(cuò)誤比如把 API Key 改錯(cuò)看 Trace 里是否記錄了錯(cuò)誤信息。一個(gè)合格的觀測系統(tǒng)不僅要記錄成功調(diào)用更要記錄失敗調(diào)用因?yàn)槭⊥攀悄闩挪閱栴}的起點(diǎn)。如果錯(cuò)誤沒有被捕獲那這套觀測的價(jià)值就打了對折。3.3 手動埋點(diǎn)給自定義邏輯裝上探頭框架組件之外的邏輯需要手動埋點(diǎn)。LangSmith 提供了兩種方式我分別說說適用場景。第一種是traceable裝飾器適合裝飾獨(dú)立的函數(shù)from langsmith import traceable traceable(namecustom_rerank, run_typetool) def custom_rerank(query: str, docs: list) - list: # 你的重排序邏輯 scored [(doc, compute_score(query, doc)) for doc in docs] scored.sort(keylambda x: x[1], reverseTrue) return [doc for doc, _ in scored]name參數(shù)是顯示在 Trace 里的節(jié)點(diǎn)名建議起得有意義比如custom_rerank比func_1強(qiáng)太多。run_type用來標(biāo)記節(jié)點(diǎn)類型常見的有l(wèi)lm、chain、tool、retriever正確設(shè)置類型能讓 Trace 的可視化更清晰。第二種是trace上下文管理器適合包裹一段代碼塊from langsmith import trace def process_query(query: str): with trace(namequery_rewrite, run_typechain) as run: rewritten rewrite_query(query) run.end(outputs{rewritten_query: rewritten}) return rewritten上下文管理器的好處是可以在代碼塊內(nèi)部動態(tài)設(shè)置輸入輸出適合那些輸入輸出不是函數(shù)參數(shù)的場景。比如查詢改寫輸入是原始 query輸出是改寫后的 query用上下文管理器能精確控制記錄的內(nèi)容。實(shí)操心得手動埋點(diǎn)時(shí)輸入輸出要記“有意義的數(shù)據(jù)”而不是“全部數(shù)據(jù)”。我見過有人把整個(gè)文檔列表的 embedding 向量都記進(jìn)去結(jié)果 Trace 頁面加載慢得要死排查時(shí)也根本看不過來。記住觀測數(shù)據(jù)是給人看的不是給機(jī)器看的記那些你能一眼判斷對錯(cuò)的信息就夠了。3.4 元數(shù)據(jù)與標(biāo)簽讓 Trace 可檢索Trace 多了以后怎么快速找到你要排查的那一條靠元數(shù)據(jù)metadata和標(biāo)簽tags。LangSmith 支持在 Run 上附加任意鍵值對你可以把用戶 ID、會話 ID、模型版本、檢索策略等維度都打上去。chain.invoke( {concept: 向量檢索}, config{ metadata: {user_id: u_123, strategy: hybrid_search}, tags: [prod, v2] } )這樣當(dāng)你想排查“使用 hybrid_search 策略的所有失敗請求”時(shí)直接按 metadata 過濾就行不用一條條翻。標(biāo)簽適合做粗粒度的分類比如按環(huán)境prod/dev、按版本v1/v2、按優(yōu)先級high/low打標(biāo)元數(shù)據(jù)適合做細(xì)粒度的檢索比如具體的用戶、會話、參數(shù)組合。我的習(xí)慣是至少打三個(gè)維度的標(biāo)簽環(huán)境、版本、鏈路類型。環(huán)境區(qū)分測試和生產(chǎn)版本區(qū)分不同迭代鏈路類型區(qū)分是問答、摘要還是別的場景。這三個(gè)維度基本能覆蓋 80% 的排查場景剩下的用元數(shù)據(jù)補(bǔ)充。4. 實(shí)操過程與核心環(huán)節(jié)實(shí)現(xiàn)4.1 搭建一個(gè)可觀測的最小 RAG 鏈路光講理論不夠我?guī)愦钜粋€(gè)最小可運(yùn)行的 RAG 鏈路把觀測能力完整接進(jìn)去。這個(gè)鏈路包含四個(gè)節(jié)點(diǎn)查詢改寫、向量檢索、重排序、生成。每個(gè)節(jié)點(diǎn)都做埋點(diǎn)形成一個(gè)完整的 Trace 樹。先準(zhǔn)備基礎(chǔ)組件import os os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_API_KEY] 你的API Key os.environ[LANGCHAIN_PROJECT] rag-observability-demo from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_core.vectorstores import InMemoryVectorStore from langchain_core.prompts import ChatPromptTemplate from langsmith import traceable llm ChatOpenAI(modelgpt-4o-mini, temperature0) embeddings OpenAIEmbeddings(modeltext-embedding-3-small)這里用內(nèi)存向量庫做演示實(shí)際項(xiàng)目里換成你用的向量數(shù)據(jù)庫即可。關(guān)鍵是每個(gè)節(jié)點(diǎn)都要能被獨(dú)立觀測。4.2 查詢改寫節(jié)點(diǎn)的實(shí)現(xiàn)與埋點(diǎn)查詢改寫是 RAG 里最容易被忽略、但對效果影響很大的環(huán)節(jié)。用戶的問題往往口語化、有指代、信息不全直接拿去檢索效果很差。改寫節(jié)點(diǎn)負(fù)責(zé)把原始問題轉(zhuǎn)成更適合檢索的形式。traceable(namequery_rewrite, run_typechain) def rewrite_query(original_query: str) - str: prompt ChatPromptTemplate.from_template( 把下面的問題改寫成適合向量檢索的查詢保留關(guān)鍵實(shí)體去掉口語化表達(dá)。 只輸出改寫后的查詢不要解釋。\n\n原問題{q} ) chain prompt | llm result chain.invoke({q: original_query}) return result.content.strip()這個(gè)節(jié)點(diǎn)用traceable裝飾后它的輸入原始問題和輸出改寫后的問題都會被記錄。排查時(shí)你能清楚看到模型到底把問題改成了什么。我遇到過改寫節(jié)點(diǎn)把“它多少錢”改寫成“價(jià)格”這種丟失上下文的情況如果沒有觀測你根本不知道檢索失敗是因?yàn)楦膶懗隽藛栴}。注意改寫節(jié)點(diǎn)本身也是一次 LLM 調(diào)用會產(chǎn)生 token 消耗和延遲。如果你的場景對延遲敏感可以考慮用規(guī)則改寫替代或者只在檢測到查詢質(zhì)量低時(shí)才觸發(fā)改寫。觀測數(shù)據(jù)能幫你判斷改寫的收益是否值得這個(gè)成本。4.3 檢索與重排序節(jié)點(diǎn)的埋點(diǎn)檢索節(jié)點(diǎn)是 RAG 的核心也是最需要觀測的環(huán)節(jié)。我們要記錄檢索到的原始文檔、相似度分?jǐn)?shù)、重排序后的順序。traceable(namevector_retrieve, run_typeretriever) def vector_retrieve(query: str, k: int 10) - list: docs vectorstore.similarity_search_with_score(query, kk) return [{content: d.page_content, score: s} for d, s in docs] traceable(namecustom_rerank, run_typetool) def custom_rerank(query: str, docs: list, top_n: int 3) - list: # 這里用簡單的關(guān)鍵詞重疊度做演示實(shí)際項(xiàng)目可換成 Cross-Encoder def overlap_score(doc): return len(set(query) set(doc[content])) / max(len(set(query)), 1) ranked sorted(docs, keyoverlap_score, reverseTrue) return ranked[:top_n]檢索節(jié)點(diǎn)記錄的是“召回”重排序節(jié)點(diǎn)記錄的是“精排”。這兩個(gè)節(jié)點(diǎn)的輸出都要完整記錄因?yàn)榕挪闀r(shí)你需要對比是召回階段就沒找到相關(guān)文檔還是召回了但重排序排錯(cuò)了這兩種情況的優(yōu)化方向完全不同——前者要調(diào)檢索策略后者要調(diào)重排序模型。4.4 生成節(jié)點(diǎn)與完整鏈路串聯(lián)最后是生成節(jié)點(diǎn)把重排序后的文檔拼進(jìn) Prompt交給大模型生成回答。traceable(namegenerate_answer, run_typellm) def generate_answer(query: str, docs: list) - str: context \n\n.join([d[content] for d in docs]) prompt ChatPromptTemplate.from_template( 根據(jù)以下資料回答問題資料中沒有的信息不要編造。\n\n 資料\n{context}\n\n問題{q}\n\n回答 ) chain prompt | llm return chain.invoke({context: context, q: query}).content traceable(namerag_pipeline, run_typechain) def rag_pipeline(query: str) - str: rewritten rewrite_query(query) docs vector_retrieve(rewritten, k10) reranked custom_rerank(rewritten, docs, top_n3) answer generate_answer(query, reranked) return answer把整個(gè)鏈路包在rag_pipeline這個(gè) Trace 里所有子節(jié)點(diǎn)都會掛在它下面形成一棵完整的調(diào)用樹。跑一次rag_pipeline(你們的退貨政策是什么)去 LangSmith 看你會看到類似這樣的結(jié)構(gòu)rag_pipeline ├── query_rewrite ├── vector_retrieve ├── custom_rerank └── generate_answer每個(gè)節(jié)點(diǎn)都能展開看輸入輸出。這時(shí)候如果回答不對你可以逐層往下看改寫后的查詢對不對檢索到的文檔相關(guān)嗎重排序后的前三篇是不是最相關(guān)的Prompt 里的上下文完整嗎問題定位從“猜”變成了“看”。4.5 參數(shù)計(jì)算與選擇過程觀測接入后你會發(fā)現(xiàn)很多之前拍腦袋定的參數(shù)其實(shí)可以用數(shù)據(jù)來優(yōu)化。舉幾個(gè)我實(shí)際調(diào)過的例子。檢索的 k 值。一開始我設(shè) k10覺得召回越多越好。但觀測數(shù)據(jù)顯示重排序后真正被用到的文檔通常只有 2-3 篇k10 意味著有 7 篇是浪費(fèi)的還增加了重排序的延遲。后來我把 k 降到 6召回質(zhì)量基本沒降延遲降了約 30%。這個(gè)結(jié)論不是拍腦袋來的是看了幾十條 Trace 的檢索結(jié)果分布后得出的。重排序的 top_n。top_n 決定了塞進(jìn) Prompt 的文檔數(shù)量。設(shè)太小可能漏掉關(guān)鍵信息設(shè)太大Prompt 變長token 成本上升還可能引入噪聲干擾生成。我的做法是看觀測數(shù)據(jù)里“正確答案所在的文檔排名分布”如果 90% 的正確答案都在前 3 篇那 top_n3 就夠了如果分布很散說明重排序效果不好得先優(yōu)化重排序。改寫是否觸發(fā)。改寫節(jié)點(diǎn)有成本不是每次都需要。我通過觀測數(shù)據(jù)統(tǒng)計(jì)了“改寫前后檢索命中率的變化”發(fā)現(xiàn)對于短查詢少于 5 個(gè)字改寫收益明顯對于長查詢收益很小。于是加了個(gè)規(guī)則只有查詢長度小于閾值時(shí)才觸發(fā)改寫。這個(gè)優(yōu)化讓整體延遲降了不少效果還沒受影響。5. 常見問題與排查技巧實(shí)錄5.1 Trace 不顯示或顯示不全這是接入初期最常見的問題。按我的排查順序通常是這幾個(gè)原因現(xiàn)象可能原因排查方法完全沒有 Trace環(huán)境變量未生效檢查LANGCHAIN_TRACING_V2是否為true且設(shè)置在 import 之前只有部分節(jié)點(diǎn)自定義函數(shù)未埋點(diǎn)檢查鏈路里哪些是自定義邏輯補(bǔ)上traceable異步調(diào)用丟失SDK 版本過舊升級langsmith和langchain到較新版本上報(bào)延遲網(wǎng)絡(luò)或采樣配置檢查網(wǎng)絡(luò)連通性確認(rèn)采樣率設(shè)置我踩過最深的一個(gè)坑是環(huán)境變量在 Jupyter Notebook 里設(shè)置后因?yàn)閮?nèi)核已經(jīng)加載過 LangChain導(dǎo)致設(shè)置不生效。解決辦法是設(shè)置完環(huán)境變量后重啟內(nèi)核或者用importlib.reload重新加載模塊。這個(gè)坑排查了我快一個(gè)小時(shí)因?yàn)榇a看起來完全沒問題。5.2 觀測數(shù)據(jù)太多找不到重點(diǎn)Trace 多了以后頁面會變得很亂。這時(shí)候標(biāo)簽和元數(shù)據(jù)就派上用場了。我的做法是建立一套命名規(guī)范項(xiàng)目名按環(huán)境分標(biāo)簽按版本和鏈路類型打元數(shù)據(jù)里帶上關(guān)鍵參數(shù)。這樣排查時(shí)先按標(biāo)簽過濾再按元數(shù)據(jù)精確定位效率高很多。另外LangSmith 支持對 Trace 做篩選和排序比如“只看耗時(shí)超過 5 秒的”“只看報(bào)錯(cuò)的”“只看某個(gè)用戶的”。善用這些篩選條件能幫你快速從海量 Trace 里撈出問題樣本。我通常會先按“錯(cuò)誤”篩一遍再按“耗時(shí)”排個(gè)序基本就能找到需要優(yōu)化的點(diǎn)。5.3 觀測帶來的性能開銷全量上報(bào)確實(shí)會帶來延遲。我實(shí)測下來每次 Run 上報(bào)大約增加 10-50ms 的網(wǎng)絡(luò)開銷鏈路節(jié)點(diǎn)多的話累積起來不可忽視。如果你的應(yīng)用對延遲敏感幾個(gè)優(yōu)化方向采樣上報(bào)正常請求按比例采樣錯(cuò)誤和慢請求全量上報(bào)異步上報(bào)確認(rèn) SDK 用的是異步上報(bào)模式不阻塞主流程精簡數(shù)據(jù)只上報(bào)排查必需的內(nèi)容別把大段文檔全文都傳上去實(shí)操心得我一般會在開發(fā)環(huán)境開全量上報(bào)方便調(diào)試生產(chǎn)環(huán)境開采樣上報(bào)采樣率根據(jù)流量調(diào)整。這樣既保證了排查能力又控制了開銷。采樣率不是拍腦袋定的而是根據(jù)“每天需要排查的問題數(shù)量”倒推——如果每天大概有 10 個(gè)問題需要排查那采樣率保證每天有幾百條 Trace 就夠了。5.4 排查思路速查表最后整理一個(gè)我常用的排查思路表遇到 RAG 回答問題時(shí)按這個(gè)順序過一遍排查步驟看什么判斷標(biāo)準(zhǔn)1. 看改寫改寫后的查詢是否保留了關(guān)鍵實(shí)體是否丟失上下文2. 看召回檢索到的文檔列表相關(guān)文檔是否在列表里排名如何3. 看重排重排后的順序最相關(guān)的文檔是否排在前列4. 看上下文塞進(jìn) Prompt 的內(nèi)容是否包含回答所需的信息5. 看生成模型輸出是否忠實(shí)于上下文有無編造這個(gè)順序是從鏈路前端往后端走因?yàn)榍岸说膯栴}會傳導(dǎo)到后端。如果召回階段就沒找到相關(guān)文檔那后面重排和生成再優(yōu)化也沒用。先定位問題出在哪一環(huán)再針對性優(yōu)化比盲目調(diào)參高效得多。這套觀測方案搭起來之后我最大的感受是RAG 調(diào)優(yōu)從玄學(xué)變成了工程。以前改個(gè)參數(shù)心里沒底現(xiàn)在改完看數(shù)據(jù)好沒好一目了然。當(dāng)然觀測只是手段不是目的最終還是要落到效果提升上。下一篇我會聊聊怎么基于這些觀測數(shù)據(jù)做系統(tǒng)性的評估和優(yōu)化把“能看見問題”變成“能解決問題”。