性實(shí)戰(zhàn):讓 AI Agent 從黑盒到透明)
1. 為什么“能跑通”的 Agent 項(xiàng)目上線三天就變成了黑盒我最早接觸 AI Agent 是在一個(gè)內(nèi)部知識(shí)庫(kù)問答項(xiàng)目上。當(dāng)時(shí)用 LangChain 把檢索、工具調(diào)用、生成串起來本地跑得挺順回答質(zhì)量也還行。上線第一周用戶反饋開始變多“同一個(gè)問題昨天答得對(duì)今天答錯(cuò)了”“有時(shí)候轉(zhuǎn)圈十幾秒才回”“它到底查了哪些文檔能不能給我看看”。我打開日志看到的只有一行AgentExecutor finished中間發(fā)生了什么完全靠猜。這就是大多數(shù) Agent 項(xiàng)目從 Demo 走向生產(chǎn)時(shí)撞上的第一堵墻調(diào)用鏈?zhǔn)呛诤?。一次用戶提問背后可能?jīng)歷了意圖識(shí)別、多輪工具調(diào)用、向量檢索、重排、大模型生成、結(jié)果校驗(yàn)等七八個(gè)環(huán)節(jié)任何一個(gè)環(huán)節(jié)的參數(shù)變化、超時(shí)、返回異常都會(huì)讓最終結(jié)果面目全非。而傳統(tǒng)日志只能記錄“入口”和“出口”中間過程要么沒打要么打了一堆沒法關(guān)聯(lián)的散點(diǎn)。Langfuse 解決的正是這個(gè)問題。它把自己定位成 LLM 應(yīng)用的可觀測(cè)性平臺(tái)核心能力可以概括成三件事Trace追蹤把一次完整請(qǐng)求的所有環(huán)節(jié)串成一條時(shí)間線Span跨度記錄每個(gè)子步驟的輸入輸出、耗時(shí)、模型參數(shù)Score評(píng)分把人工反饋或自動(dòng)評(píng)估的結(jié)果掛到具體的 Trace 上。這三件事組合起來Agent 的每一次“思考”都變得可回放、可對(duì)比、可量化。這篇文章適合兩類人看一類是已經(jīng)把 Agent 跑起來、但被線上問題搞得焦頭爛額的工程師另一類是正準(zhǔn)備搭 Agent 系統(tǒng)、想從一開始就把可觀測(cè)性設(shè)計(jì)進(jìn)去的開發(fā)者。我會(huì)從接入方式、數(shù)據(jù)模型、評(píng)測(cè)體系、并發(fā)與成本、踩坑經(jīng)驗(yàn)幾個(gè)角度把 Langfuse 在真實(shí) Agent 項(xiàng)目里的用法講透。文中涉及的具體參數(shù)和配置一部分來自官方文檔一部分是我在實(shí)際項(xiàng)目中反復(fù)調(diào)試后總結(jié)的實(shí)踐值你可以直接參考但建議結(jié)合自己的業(yè)務(wù)量級(jí)做調(diào)整。2. Langfuse 的數(shù)據(jù)模型Trace、Span、Generation 到底怎么擺很多人第一次看 Langfuse 的界面會(huì)覺得信息很多不知道從哪看起。其實(shí)它的數(shù)據(jù)模型非常清晰理解了這個(gè)模型后面接入和排查都會(huì)順很多。2.1 一次請(qǐng)求就是一條 TraceTrace 是 Langfuse 里最大的容器單位對(duì)應(yīng)“一次完整的用戶請(qǐng)求”。比如用戶在對(duì)話框里問了一句“幫我查一下上個(gè)月的銷售數(shù)據(jù)”從這句話進(jìn)入系統(tǒng)到最終答案返回整個(gè)過程就是一條 Trace。Trace 有一個(gè)唯一的trace_id你可以自己生成也可以讓 SDK 自動(dòng)生成。我習(xí)慣在請(qǐng)求入口處手動(dòng)生成并透?jìng)鬟@樣即使跨服務(wù)調(diào)用也能把同一條 Trace 的各個(gè)部分關(guān)聯(lián)起來。Trace 上可以掛很多元數(shù)據(jù)用戶 ID、會(huì)話 ID、標(biāo)簽、環(huán)境production/staging、版本號(hào)。這些字段看起來不起眼但在排查問題時(shí)極其有用。比如你可以按user_id過濾看某個(gè)用戶最近的所有請(qǐng)求也可以按version對(duì)比新舊版本的表現(xiàn)差異。2.2 Span 是 Trace 里的一個(gè)步驟Span 代表 Trace 內(nèi)部的一個(gè)操作單元。在 Agent 場(chǎng)景里一次工具調(diào)用、一次向量檢索、一次重排都可以是一個(gè) Span。Span 可以嵌套形成樹狀結(jié)構(gòu)。比如“工具調(diào)用”這個(gè) Span 下面可以再掛“參數(shù)構(gòu)造”和“HTTP 請(qǐng)求”兩個(gè)子 Span。Span 最核心的價(jià)值是耗時(shí)歸因。當(dāng)用戶抱怨“怎么這么慢”時(shí)你打開 Trace 一看如果檢索 Span 花了 3 秒生成 Span 花了 8 秒那優(yōu)化重點(diǎn)就很明確了。我見過一個(gè)項(xiàng)目排查了半天以為是模型慢結(jié)果發(fā)現(xiàn)是重排服務(wù)在高峰期排隊(duì)Span 一拉出來問題一目了然。2.3 Generation 是專門給大模型調(diào)用用的 SpanGeneration 是 Span 的一種特殊類型專門用來記錄大模型調(diào)用。它比普通 Span 多了幾個(gè)關(guān)鍵字段model模型名稱、model_parameters溫度、top_p 等、prompt輸入提示詞、completion模型輸出、usagetoken 消耗。這些字段是后續(xù)做成本分析和質(zhì)量評(píng)估的基礎(chǔ)。我特別想強(qiáng)調(diào)usage字段。很多團(tuán)隊(duì)做 Agent 時(shí)只關(guān)心“能不能答對(duì)”不關(guān)心“花了多少錢”。等到月底賬單出來才發(fā)現(xiàn)某個(gè)工具調(diào)用因?yàn)樘崾驹~寫得太啰嗦每次都要消耗幾千 token。Langfuse 會(huì)把每次 Generation 的 token 數(shù)記錄下來你可以在儀表盤上按模型、按天、按用戶維度看消耗趨勢(shì)。這個(gè)數(shù)據(jù)對(duì)于控制成本是剛需。2.4 三者關(guān)系用一張表說清楚層級(jí)對(duì)應(yīng)概念關(guān)鍵字段典型用途Trace一次完整請(qǐng)求trace_id, user_id, session_id, tags全鏈路回放、用戶行為分析Span請(qǐng)求內(nèi)的一個(gè)步驟name, start_time, end_time, input, output耗時(shí)歸因、步驟排查Generation一次模型調(diào)用model, prompt, completion, usage成本分析、提示詞優(yōu)化、質(zhì)量評(píng)估理解這個(gè)模型之后接入 Langfuse 就變成了“在合適的位置埋點(diǎn)”的問題。埋點(diǎn)位置的選擇直接決定了你后續(xù)能看到什么。3. 接入實(shí)戰(zhàn)在 Agent 的關(guān)鍵路徑上埋點(diǎn)Langfuse 提供了 Python 和 JavaScript 的 SDK也支持通過 API 直接寫入。對(duì)于大多數(shù) Agent 項(xiàng)目我推薦用 SDK 的裝飾器或上下文管理器方式接入侵入性小維護(hù)成本低。3.1 環(huán)境準(zhǔn)備與初始化先裝 SDKpip install langfuse然后在項(xiàng)目啟動(dòng)時(shí)初始化客戶端。這里有個(gè)細(xì)節(jié)不要在每次請(qǐng)求里都 new 一個(gè)客戶端那樣會(huì)反復(fù)建立連接既慢又浪費(fèi)資源。正確做法是在應(yīng)用啟動(dòng)時(shí)初始化一個(gè)全局客戶端通過依賴注入或模塊級(jí)變量共享。from langfuse import Langfuse langfuse Langfuse( public_keypk-lf-..., secret_keysk-lf-..., hosthttps://your-langfuse-host, releaseagent-v1.2.0, environmentproduction )release和environment這兩個(gè)參數(shù)建議一定要填。release可以填 Git commit 短哈?;虬姹咎?hào)environment區(qū)分生產(chǎn)和測(cè)試。后面做版本對(duì)比時(shí)這兩個(gè)字段就是篩選依據(jù)。3.2 用裝飾器給函數(shù)自動(dòng)埋點(diǎn)Langfuse 提供了observe()裝飾器加在函數(shù)上就能自動(dòng)生成 Span。這是最省事的接入方式。from langfuse.decorators import observe observe() def retrieve_documents(query: str): # 向量檢索邏輯 return docs observe() def call_llm(prompt: str): # 模型調(diào)用邏輯 return response裝飾器會(huì)自動(dòng)記錄函數(shù)的入?yún)ⅰ⒎祷刂?、?zhí)行耗時(shí)。如果函數(shù)內(nèi)部拋異常異常信息也會(huì)被記錄到 Span 上。這一點(diǎn)在排查線上問題時(shí)特別有用你能直接看到是哪一步炸了、炸的時(shí)候輸入是什么。不過裝飾器有個(gè)局限它記錄的是函數(shù)的輸入輸出但如果你想把模型調(diào)用的 token 數(shù)、模型名稱這些信息也記下來需要配合update_current_generation或update_current_span手動(dòng)補(bǔ)充。3.3 手動(dòng)控制 Trace 的粒度對(duì)于 Agent 這種多步驟場(chǎng)景我建議在入口處手動(dòng)創(chuàng)建 Trace然后在關(guān)鍵步驟手動(dòng)創(chuàng)建 Span。這樣粒度更可控。from langfuse import Langfuse langfuse Langfuse(...) def handle_user_query(user_id: str, query: str): trace langfuse.trace( nameagent-query, user_iduser_id, input{query: query}, metadata{channel: web} ) # 檢索步驟 retrieval_span trace.span(nameretrieval, input{query: query}) docs retrieve_documents(query) retrieval_span.end(output{doc_count: len(docs)}) # 生成步驟 generation trace.generation( nameanswer-generation, modelgpt-4o, inputbuild_prompt(query, docs) ) answer call_llm(...) generation.end( outputanswer, usage{input: 1200, output: 350} ) trace.update(output{answer: answer}) return answer這種寫法的好處是你能精確控制每個(gè) Span 的邊界和記錄內(nèi)容。比如檢索 Span 里你可以把召回的文檔 ID 列表記進(jìn)去后面排查“為什么答錯(cuò)了”時(shí)就能直接看到當(dāng)時(shí)檢索到了什么。3.4 在 LangChain 和 LangGraph 里的接入方式如果你的 Agent 是基于 LangChain 或 LangGraph 搭的Langfuse 有現(xiàn)成的 CallbackHandler接入成本很低。from langfuse.callback import CallbackHandler handler CallbackHandler( public_keypk-lf-..., secret_keysk-lf-..., hosthttps://your-langfuse-host, session_iduser-session-123, user_iduser-456 ) chain.invoke({input: query}, config{callbacks: [handler]})LangGraph 的接入類似把 handler 傳進(jìn)config即可。它會(huì)自動(dòng)把每個(gè)節(jié)點(diǎn)執(zhí)行、每次模型調(diào)用都記錄成 Span 和 Generation。我實(shí)測(cè)下來LangGraph 的節(jié)點(diǎn)名稱會(huì)自動(dòng)成為 Span 名稱所以給節(jié)點(diǎn)起個(gè)好名字很重要?jiǎng)e用node_1、node_2這種后面看 Trace 時(shí)會(huì)很痛苦。提示CallbackHandler 在高并發(fā)場(chǎng)景下會(huì)頻繁創(chuàng)建對(duì)象建議復(fù)用或使用連接池。如果 QPS 很高可以考慮異步寫入模式避免阻塞主流程。4. 評(píng)測(cè)體系讓 Agent 的回答質(zhì)量從“感覺還行”變成“有數(shù)可查”可觀測(cè)性解決的是“看得見”的問題但看得見不等于管得住。Agent 的回答質(zhì)量到底怎么樣需要一套評(píng)測(cè)體系來量化。Langfuse 的 Score 功能就是干這個(gè)的。4.1 人工評(píng)分最直接但最貴的方式最簡(jiǎn)單的做法是在 Trace 上掛人工評(píng)分。Langfuse 支持在界面上直接給某條 Trace 打分也支持通過 API 寫入。langfuse.score( trace_idtrace.id, nameuser-feedback, value1, # 1 表示好評(píng)0 表示差評(píng) comment回答準(zhǔn)確引用了正確的文檔 )人工評(píng)分的優(yōu)點(diǎn)是準(zhǔn)確缺點(diǎn)是貴且慢。我的經(jīng)驗(yàn)是不要對(duì)所有請(qǐng)求都做人工評(píng)分而是抽樣。按用戶反饋點(diǎn)贊/點(diǎn)踩自動(dòng)掛分再定期人工復(fù)核一部分。這樣成本可控?cái)?shù)據(jù)也有代表性。4.2 自動(dòng)評(píng)估用模型給模型打分對(duì)于需要規(guī)?;u(píng)估的場(chǎng)景可以用另一個(gè)模型來做自動(dòng)評(píng)分。Langfuse 本身不提供評(píng)估模型但你可以自己寫評(píng)估邏輯然后把結(jié)果寫回 Score。常見的自動(dòng)評(píng)估維度有幾個(gè)相關(guān)性回答是否切題有沒有答非所問忠實(shí)度回答是否基于檢索到的文檔有沒有編造完整性是否覆蓋了問題的所有方面格式合規(guī)是否滿足輸出格式要求比如 JSON 結(jié)構(gòu)def evaluate_answer(query, answer, docs): eval_prompt f 請(qǐng)?jiān)u估以下回答的質(zhì)量從相關(guān)性、忠實(shí)度、完整性三個(gè)維度打分1-5分。 問題{query} 檢索文檔{docs} 回答{answer} 請(qǐng)以 JSON 格式輸出評(píng)分和理由。 result call_llm(eval_prompt) scores parse_json(result) for dim, score in scores.items(): langfuse.score( trace_idtrace.id, namefauto-{dim}, valuescore[value], commentscore[reason] )這里有個(gè)坑要注意評(píng)估模型和被評(píng)估模型不要用同一個(gè)。用同一個(gè)模型評(píng)估自己容易產(chǎn)生“自我偏好”分?jǐn)?shù)虛高。我一般用能力更強(qiáng)的模型做評(píng)估或者用不同廠商的模型交叉評(píng)估。4.3 用數(shù)據(jù)集做回歸測(cè)試Agent 系統(tǒng)最怕的是“改了一個(gè)提示詞修好了 A 問題弄壞了 B 問題”。Langfuse 的 Dataset 功能可以幫你做回歸測(cè)試。做法是把一批有代表性的問題包括邊界 case整理成數(shù)據(jù)集每次發(fā)版前跑一遍對(duì)比新舊版本的評(píng)分。如果某個(gè)維度的分?jǐn)?shù)明顯下降就說明這次改動(dòng)有問題。dataset langfuse.get_dataset(agent-regression-v1) for item in dataset.items: # 用新版本跑一遍 output run_agent(item.input) # 掛到數(shù)據(jù)集條目上 item.link( trace_or_observationtrace, run_namev1.3.0-release )跑完之后在 Langfuse 界面上可以按run_name對(duì)比不同版本的評(píng)分。這個(gè)流程跑順了之后發(fā)版心里就有底了。4.4 評(píng)測(cè)頻率與成本控制自動(dòng)評(píng)估是要花錢的每次評(píng)估都是一次模型調(diào)用。我的建議是場(chǎng)景評(píng)估頻率評(píng)估方式日常線上抽樣 5%-10%自動(dòng)評(píng)估 用戶反饋發(fā)版前全量數(shù)據(jù)集自動(dòng)評(píng)估 人工抽檢重大改動(dòng)全量數(shù)據(jù)集 線上灰度自動(dòng)評(píng)估 人工全檢這樣既能控制成本又能在關(guān)鍵節(jié)點(diǎn)拿到足夠的質(zhì)量信號(hào)。5. 高并發(fā)下的 Langfuse別讓可觀測(cè)性拖垮主流程Agent 系統(tǒng)本身就比普通接口重一次請(qǐng)求可能涉及多次模型調(diào)用和工具調(diào)用延遲本來就高。如果可觀測(cè)性的埋點(diǎn)再同步阻塞主流程用戶體驗(yàn)會(huì)更差。這一節(jié)講幾個(gè)高并發(fā)場(chǎng)景下的實(shí)踐要點(diǎn)。5.1 異步寫入與批量上報(bào)Langfuse SDK 默認(rèn)是同步發(fā)送數(shù)據(jù)的也就是說每次trace.span()、generation.end()都會(huì)觸發(fā)一次網(wǎng)絡(luò)請(qǐng)求。在低 QPS 下沒問題但 QPS 一高這些請(qǐng)求會(huì)堆積拖慢主流程。解決辦法是開啟異步模式。Langfuse 的 Python SDK 支持通過環(huán)境變量或初始化參數(shù)配置異步langfuse Langfuse( public_key..., secret_key..., host..., flush_at100, # 攢夠 100 條再發(fā) flush_interval1.0, # 或者每 1 秒發(fā)一次 threads4 # 后臺(tái)發(fā)送線程數(shù) )flush_at和flush_interval是兩個(gè)關(guān)鍵參數(shù)。flush_at控制批量大小flush_interval控制最大等待時(shí)間。兩者是“或”的關(guān)系滿足任一條件就發(fā)送。我一般設(shè)flush_at50、flush_interval2.0在延遲和數(shù)據(jù)實(shí)時(shí)性之間取平衡。注意異步模式下如果應(yīng)用崩潰緩沖區(qū)里沒發(fā)出去的數(shù)據(jù)會(huì)丟失。對(duì)于關(guān)鍵業(yè)務(wù)建議在請(qǐng)求結(jié)束時(shí)手動(dòng)調(diào)用langfuse.flush()確保數(shù)據(jù)落庫(kù)。5.2 采樣不是所有請(qǐng)求都值得全量記錄高并發(fā)場(chǎng)景下全量記錄 Trace 的成本很高包括存儲(chǔ)成本、網(wǎng)絡(luò)成本、以及 Langfuse 服務(wù)端的處理成本。這時(shí)候需要采樣。采樣的策略有幾種固定比例采樣比如只記錄 20% 的請(qǐng)求。實(shí)現(xiàn)簡(jiǎn)單但可能漏掉關(guān)鍵 case。基于錯(cuò)誤采樣正常請(qǐng)求少記出錯(cuò)請(qǐng)求全記。這個(gè)策略最實(shí)用因?yàn)榕挪閱栴}主要看出錯(cuò)的?;谟脩舨蓸訉?duì) VIP 用戶全量記錄普通用戶抽樣。基于延遲采樣慢請(qǐng)求全記快請(qǐng)求抽樣。我通常組合使用錯(cuò)誤請(qǐng)求 100% 記錄慢請(qǐng)求超過 P95100% 記錄正常請(qǐng)求按 10% 采樣。這樣既控制了數(shù)據(jù)量又保證了問題排查時(shí)有足夠的信息。import random def should_sample(trace_metadata): if trace_metadata.get(has_error): return True if trace_metadata.get(latency_ms, 0) 5000: return True return random.random() 0.15.3 敏感信息脫敏Agent 系統(tǒng)處理的往往是用戶真實(shí)數(shù)據(jù)直接把這些數(shù)據(jù)寫到 Langfuse 上是有合規(guī)風(fēng)險(xiǎn)的。Langfuse 支持在 SDK 層面做脫敏。from langfuse import Langfuse def mask_sensitive(data): # 對(duì)手機(jī)號(hào)、郵箱、身份證號(hào)做脫敏 if isinstance(data, str): data re.sub(r\d{11}, ***********, data) data re.sub(r[\w\.-][\w\.-], ******, data) return data langfuse Langfuse( ..., maskmask_sensitive )mask函數(shù)會(huì)在數(shù)據(jù)發(fā)送前被調(diào)用你可以在這里做各種脫敏處理。這一步千萬別省尤其是涉及用戶隱私的業(yè)務(wù)。5.4 并發(fā)壓測(cè)下的表現(xiàn)我在一個(gè) QPS 200 左右的 Agent 服務(wù)上做過壓測(cè)對(duì)比開啟和關(guān)閉 Langfuse 的延遲差異。結(jié)論是開啟異步寫入后P99 延遲增加在 5% 以內(nèi)如果不開異步P99 延遲會(huì)增加 30% 以上因?yàn)橥骄W(wǎng)絡(luò)請(qǐng)求在高峰期會(huì)排隊(duì)。所以結(jié)論很明確生產(chǎn)環(huán)境一定要開異步并且做好采樣和脫敏??捎^測(cè)性是為了讓系統(tǒng)更可控不能反過來成為系統(tǒng)的負(fù)擔(dān)。6. 踩坑記錄那些文檔里不會(huì)寫的細(xì)節(jié)這一節(jié)記錄幾個(gè)我在實(shí)際項(xiàng)目中踩過的坑都是文檔里不會(huì)寫、但實(shí)際會(huì)遇到的。6.1 Trace 丟失請(qǐng)求還沒結(jié)束進(jìn)程就退出了Agent 服務(wù)如果用 Serverless 或短生命周期容器部署請(qǐng)求處理完進(jìn)程可能立刻退出導(dǎo)致 Langfuse 緩沖區(qū)里的數(shù)據(jù)還沒發(fā)出去就丟了。表現(xiàn)就是 Langfuse 上只能看到部分 Trace或者干脆看不到。解決辦法是在請(qǐng)求處理結(jié)束時(shí)顯式 flushtry: result handle_user_query(user_id, query) finally: langfuse.flush()flush()會(huì)阻塞直到緩沖區(qū)清空。雖然會(huì)增加一點(diǎn)延遲但能保證數(shù)據(jù)不丟。如果對(duì)延遲極其敏感可以把這個(gè) flush 放到后臺(tái)線程里做。6.2 Span 嵌套層級(jí)過深界面加載慢Agent 如果步驟很多Span 嵌套層級(jí)可能達(dá)到十幾層。Langfuse 界面在渲染深層嵌套時(shí)會(huì)變慢排查問題時(shí)展開也很費(fèi)勁。我的做法是控制嵌套深度一般不超過 5 層。對(duì)于特別復(fù)雜的流程把一些細(xì)節(jié)步驟合并成一個(gè) Span只在需要時(shí)展開。比如“工具調(diào)用”這個(gè) Span 下面不需要把參數(shù)構(gòu)造、序列化、HTTP 請(qǐng)求都拆開合并成一個(gè) Span 記錄關(guān)鍵信息就夠了。6.3 時(shí)間戳不一致導(dǎo)致 Trace 順序錯(cuò)亂Langfuse 依賴時(shí)間戳來排序 Span。如果 Agent 服務(wù)部署在多臺(tái)機(jī)器上機(jī)器時(shí)間不同步Span 的順序就會(huì)亂看起來像是“后面的步驟先執(zhí)行了”。解決辦法是確保所有機(jī)器開啟 NTP 時(shí)間同步。另外Langfuse SDK 支持傳入自定義時(shí)間戳如果確實(shí)有時(shí)序問題可以在埋點(diǎn)時(shí)手動(dòng)指定。6.4 評(píng)分?jǐn)?shù)據(jù)寫入失敗但沒報(bào)錯(cuò)Langfuse 的score()方法在寫入失敗時(shí)默認(rèn)不拋異常只是靜默失敗。這導(dǎo)致我以為評(píng)分寫進(jìn)去了實(shí)際上沒有。排查方法是看 SDK 的日志。把日志級(jí)別調(diào)到 DEBUG能看到每次寫入的結(jié)果。如果發(fā)現(xiàn)失敗檢查網(wǎng)絡(luò)、認(rèn)證信息、以及 trace_id 是否正確。6.5 模型名稱不規(guī)范導(dǎo)致統(tǒng)計(jì)混亂generation.end()里的model字段如果寫法不統(tǒng)一比如有時(shí)寫gpt-4o有時(shí)寫gpt-4o-2024-08-06統(tǒng)計(jì)時(shí)會(huì)被當(dāng)成兩個(gè)模型成本分析就不準(zhǔn)了。建議在項(xiàng)目里維護(hù)一個(gè)模型名稱常量表所有埋點(diǎn)統(tǒng)一引用。這樣統(tǒng)計(jì)維度才干凈。7. 從可觀測(cè)性到持續(xù)優(yōu)化把數(shù)據(jù)用起來埋點(diǎn)、評(píng)測(cè)、排查都做完之后最后一步是把這些數(shù)據(jù)轉(zhuǎn)化成實(shí)際的優(yōu)化動(dòng)作。否則可觀測(cè)性就只是“看個(gè)熱鬧”。7.1 用 Trace 對(duì)比找出提示詞退化每次改提示詞后用同一批測(cè)試問題跑一遍然后在 Langfuse 上按release篩選對(duì)比新舊版本的評(píng)分和輸出。我遇到過好幾次“改了一個(gè)詞整體分?jǐn)?shù)沒變但某類問題的準(zhǔn)確率掉了 20%”的情況全靠這個(gè)對(duì)比發(fā)現(xiàn)。7.2 用耗時(shí)分布定位性能瓶頸在 Langfuse 儀表盤上看 Span 的耗時(shí)分布找出 P95 最高的幾個(gè) Span。這些就是優(yōu)化重點(diǎn)。常見的瓶頸包括向量檢索沒有加緩存、重排服務(wù)并發(fā)度不夠、模型調(diào)用沒有做流式輸出等。7.3 用用戶反饋驅(qū)動(dòng)數(shù)據(jù)集迭代用戶點(diǎn)踩的 Trace 是最寶貴的數(shù)據(jù)。定期把這些 Trace 整理出來補(bǔ)充到回歸測(cè)試數(shù)據(jù)集里。這樣數(shù)據(jù)集會(huì)越來越貼近真實(shí)場(chǎng)景回歸測(cè)試的有效性也會(huì)越來越高。7.4 成本優(yōu)化的幾個(gè)切入點(diǎn)通過 Langfuse 的 token 統(tǒng)計(jì)我總結(jié)出幾個(gè)成本優(yōu)化的方向提示詞精簡(jiǎn)很多提示詞里有大量冗余說明精簡(jiǎn)后 token 數(shù)能降 30% 以上緩存復(fù)用相同或相似的查詢結(jié)果緩存減少重復(fù)模型調(diào)用模型分級(jí)簡(jiǎn)單任務(wù)用小模型復(fù)雜任務(wù)用大模型通過路由分發(fā)輸出長(zhǎng)度控制設(shè)置合理的max_tokens避免模型“話癆”這些優(yōu)化做完成本能降一半以上而質(zhì)量通過評(píng)測(cè)體系監(jiān)控不會(huì)明顯下降。8. 寫在最后可觀測(cè)性是 Agent 工程化的入場(chǎng)券我從最早用print調(diào)試 Agent到后來用日志系統(tǒng)再到接入 Langfuse最大的感受是Agent 系統(tǒng)的復(fù)雜度不在于“能不能跑”而在于“跑得好不好、為什么好、為什么不好”。沒有可觀測(cè)性這些問題全靠猜有了可觀測(cè)性每個(gè)問題都能定位到具體的 Trace、具體的 Span、具體的參數(shù)。Langfuse 不是唯一的選擇但它在這個(gè)方向上的數(shù)據(jù)模型設(shè)計(jì)得比較合理接入成本也低。如果你正在搭 Agent 系統(tǒng)我的建議是從第一天就把可觀測(cè)性設(shè)計(jì)進(jìn)去別等到線上出問題了再補(bǔ)。補(bǔ)的時(shí)候你會(huì)發(fā)現(xiàn)很多關(guān)鍵信息當(dāng)時(shí)沒記事后根本查不到。最后分享一個(gè)我自己的習(xí)慣每次發(fā)版前除了跑回歸測(cè)試我還會(huì)隨機(jī)抽 20 條線上 Trace 人工看一遍。這個(gè)動(dòng)作花不了多少時(shí)間但經(jīng)常能發(fā)現(xiàn)一些自動(dòng)化指標(biāo)覆蓋不到的問題比如回答語氣不對(duì)、格式雖然合規(guī)但可讀性差、某些邊界 case 處理得生硬。這些細(xì)節(jié)才是 Agent 從“能用”到“好用”的關(guān)鍵。