LangChain 教學:Callbacks 與 Log – Tracing 與觀測鏈路
簡介
在 LLM(大型語言模型)應用日益普及的今天,可觀測性 成為開發者必備的能力。當一段對話、搜尋或資料處理流程在 LangChain 中被串成多個 Chain、Agent、Tool 時,若沒有適當的追蹤與記錄,除錯、效能優化、以及合規審計都會變得相當困難。
LangChain 內建的 Callbacks 機制讓我們能在每個節點(如 LLM 呼叫、Prompt 製作、Tool 執行)前後插入自訂程式碼,配合 Log、Tracing 功能即可完整繪製執行鏈路。本文將從概念說明、實作範例、常見陷阱與最佳實踐,帶領初學者到中級開發者快速上手,並展示在真實專案中的應用方式。
核心概念
1. Callback 基礎
LangChain 的 CallbackManager 是一個事件分發中心,支援以下常見事件:
| 事件名稱 | 觸發時機 | 常見用途 |
|---|---|---|
on_chain_start |
Chain 開始執行前 | 記錄輸入、開始時間 |
on_chain_end |
Chain 執行完畢後 | 計算耗時、記錄輸出 |
on_llm_start |
LLM 呼叫前 | 捕捉 Prompt、Token 數 |
on_llm_end |
LLM 回傳後 | 取得回覆、統計成本 |
on_tool_start / on_tool_end |
Tool 執行前後 | 觀測外部 API 呼叫 |
重點:所有 Callback 皆接受
**kwargs,可自行擴充自訂資訊。
2. Tracing 與 Log 的差異
- Log:單向記錄,通常寫入檔案或 console,僅供事後檢視。
- Tracing:結合時間序列與層級關係,形成「樹狀」或「流程圖」視覺化,常與外部平台(如 LangSmith、ELK)整合,支援即時查詢與分析。
LangChain 內建的 ConsoleCallbackHandler 只負責 Log;若需要完整 Tracing,則可使用 LangChainTracer 或自行實作 BaseCallbackHandler。
3. 建立自訂 Callback
自訂 Callback 只要繼承 BaseCallbackHandler,實作想要的事件方法即可。以下範例示範將每次 LLM 呼叫寫入 MongoDB,以便日後分析。
# custom_callback.py
from langchain.callbacks.base import BaseCallbackHandler
from datetime import datetime
import pymongo
class MongoTracer(BaseCallbackHandler):
def __init__(self, uri: str, db_name: str = "langchain_traces"):
client = pymongo.MongoClient(uri)
self.collection = client[db_name]["llm_calls"]
def on_llm_start(self, serialized, prompts, **kwargs):
self._current = {
"timestamp": datetime.utcnow(),
"prompts": prompts,
"metadata": serialized,
}
def on_llm_end(self, response, **kwargs):
self._current["response"] = response
self.collection.insert_one(self._current)
4. 使用內建 Tracer(LangSmith)
LangSmith 是 LangChain 官方提供的雲端 Tracing 平台,只要把 LangChainTracer 加入 CallbackManager,即可自動捕捉所有事件。
from langchain.callbacks.tracers.langchain import LangChainTracer
from langchain import LLMChain, PromptTemplate, OpenAI
tracer = LangChainTracer(project_name="MyChatbot")
callback_mgr = CallbackManager([tracer])
prompt = PromptTemplate.from_template("請把以下文字翻成繁體中文:\n{input}")
chain = LLMChain(
llm=OpenAI(),
prompt=prompt,
callback_manager=callback_mgr,
)
result = chain.run({"input": "Hello, world!"})
print(result) # 觀測結果已於 LangSmith 介面呈現
5. 結合多層級 Chain 的觀測
在實務中,常會把多個 Chain 組成一個大流程(如 RetrievalQA → Summarizer → Formatter)。只要在最外層傳入同一個 CallbackManager,所有子 Chain 會自動共享同一條追蹤線索,形成 層級關係,便於在 UI 上展開/摺疊。
from langchain.chains import RetrievalQA, SummarizationChain
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
# 建立向量資料庫
vectorstore = FAISS.from_texts(
["文件1內容", "文件2內容"], embedding=OpenAIEmbeddings()
)
# 子 Chain
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(),
retriever=vectorstore.as_retriever(),
callback_manager=callback_mgr,
)
summarizer = SummarizationChain(
llm=OpenAI(),
callback_manager=callback_mgr,
)
# 主流程
def answer_question(query: str):
answer = qa_chain.run(query)
summary = summarizer.run(answer)
return summary
print(answer_question("什麼是 LangChain?"))
在上述程式碼中,qa_chain 與 summarizer 的每一次 LLM 呼叫、向量檢索都會被同一個 Tracer 捕捉,最終在 LangSmith 中呈現為一條完整的 執行樹。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解法/最佳實踐 |
|---|---|---|
忘記傳遞 callback_manager |
若只在最外層設定,內部子 Chain 仍會使用預設的 ConsoleCallbackHandler,導致資料斷層。 |
在每個自訂 Chain、Agent、Tool 建構時都顯式傳入同一個 CallbackManager。 |
| 大量 Log 造成效能瓶頸 | 寫入磁碟或遠端 DB 時若同步執行,會拖慢整體回應時間。 | 使用非同步寫入(asyncio)或批次寫入;在開發環境可只開啟 ConsoleCallbackHandler,正式環境再切換至 Tracer。 |
| 敏感資訊外洩 | Prompt、回覆可能包含個人資料或商業機密,直接寫入外部服務會有風險。 | 在 on_llm_start / on_llm_end 中過濾或加密關鍵欄位;或只在測試環境啟用完整 Tracing。 |
| 錯誤未被捕捉 | Callback 本身拋出例外會中斷主流程。 | 在自訂 Callback 中使用 try/except 包住所有程式碼,並在 on_error 事件中記錄。 |
| 過度追蹤導致噪聲 | 記錄每個 token 的細節會產生大量資料,難以分析。 | 只在需要的層級(如 Chain、Tool)啟用細粒度 Tracing,對 LLM 呼叫僅保留 Prompt/Response。 |
最佳實踐小結:
- 統一管理:建立單例
CallbackManager,在整個應用程式的入口處初始化。 - 分層紀錄:根據環境(dev / staging / prod)切換不同的 Handler(Console / File / LangSmith)。
- 安全第一:在寫入前先過濾或脫敏,尤其是涉及 PII(個人可識別資訊)時。
- 效能優化:使用非同步或批次寫入,避免同步 I/O 成為瓶頸。
- 可視化:盡量利用 LangSmith 或自建 Grafana Dashboard,讓非技術夥伴也能快速了解流程。
實際應用場景
| 場景 | 為何需要 Tracing | 典型實作方式 |
|---|---|---|
| 客服聊天機器人 | 需要追蹤用戶問題、模型回覆、以及使用的工具(如資料庫查詢)以做品質分析。 | 在每個 on_chain_end、on_tool_end 中寫入 MongoDB,並利用 Kibana 產生問題熱點圖。 |
| 金融報告自動生成 | 合規要求必須保留所有模型輸入/輸出的完整紀錄,且要能追溯到特定交易。 | 使用 LangSmith + 自訂 on_llm_end 加密儲存敏感欄位,並將 trace_id 與交易編號關聯。 |
| 資料搜尋 + 摘要 | 向量檢索結果與摘要模型的串接容易產生資訊遺失,需要檢查每一步的輸入輸出。 | 以 CallbackManager 包裹 RetrievalQA 與 SummarizationChain,在 UI 上展示「檢索 → 摘要」的階段圖。 |
| A/B 測試多模型 | 同時跑不同 LLM(例如 GPT‑4 與 Claude)比較表現,必須分辨每筆請求屬於哪個模型。 | 在 on_llm_start 中加入 metadata={"model": llm_name},並在分析時依此過濾。 |
| 長期監控與成本預算 | LLM 使用成本與 token 數是關鍵指標,需即時通知超出預算的情況。 | on_llm_end 取得 response["usage"]["total_tokens"],累加至 Redis,若超過門檻即觸發 Slack 通知。 |
總結
Tracing 與 Log 是讓 LangChain 應用從 黑箱 變成 可觀測 的關鍵。透過 CallbackManager 我們可以在每個執行節點插入自訂程式碼,無論是簡單的 console log,還是完整的雲端 Tracing(LangSmith)或自建資料庫,都能輕鬆實現。
本文從概念、實作範例、常見陷阱與最佳實踐,最後列出多種真實場景,提供了一條 從入門到上線 的完整路徑。只要在專案初始化時規劃好 Callback 的使用方式,未來不論是除錯、效能優化、合規審計或是業務分析,都能即時取得清晰的執行鏈路圖,讓開發與運維團隊更有信心面對 LLM 驅動的複雜系統。
祝你在 LangChain 的旅程中,觀測每一步,掌握每一次成功! 🚀