LangChain Callbacks 与事件驱动架构

LangChain Callbacks 与事件驱动架构

一、概念速查

核心概念

概念 定义
Callback LangChain 执行过程中触发的生命周期事件的回调函数
BaseCallbackHandler 所有回调处理器的抽象基类,定义各事件的接口
CallbackManager 事件分发中心,管理一组 CallbackHandler 的注册与调用
事件驱动架构 执行流程中异步/同步地产生事件,由注册的处理器消费的模式

BaseCallbackHandler 核心接口

事件方法 触发时机 典型场景
on_llm_start LLM 调用开始时 记录请求 prompt、计费
on_llm_end LLM 调用完成时 记录响应 token 数、延迟
on_llm_error LLM 调用抛出异常时 告警、重试逻辑
on_chat_model_start ChatModel 调用开始时 同上,仅 ChatModel 路径
on_chain_start RunnableChain 开始时 追踪链路入口
on_chain_end RunnableChain 完成时 记录链路输出
on_chain_error RunnableChain 异常时 链路级告警
on_tool_start Tool 调用开始时 审计工具参数
on_tool_end Tool 调用完成时 记录工具输出
on_tool_error Tool 抛出异常时 工具级告警
on_retry Retry 机制触发重试时 监控重试频率
on_text 输出文本时 日志记录
on_agent_action Agent 决定执行一个 Action 时 Agent 步骤追踪
on_agent_finish Agent 完成全部步骤时 Agent 最终输出

内置处理器

处理器 作用 依赖
StdOutCallbackHandler 将事件输出到 stdout langchain-core
FileCallbackHandler 将事件写入文件 langchain-core
ConsoleCallbackHandler LangChain CLI 控制台输出 langchain-cli

二、底层原理

2.1 事件分发机制

CallbackManager 内部维护一个 handlers: list[BaseCallbackHandler] 列表。当一个事件被触发时(例如 LLM 开始调用),CallbackManager.on_llm_start() 会遍历所有注册的 handler,依次调用其 on_llm_start() 方法:
#mermaid-svg-RqQmiM3h9RZk0O1n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RqQmiM3h9RZk0O1n .error-icon{fill:#552222;}#mermaid-svg-RqQmiM3h9RZk0O1n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RqQmiM3h9RZk0O1n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .marker.cross{stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RqQmiM3h9RZk0O1n p{margin:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label text{fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label span{color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label span p{background-color:transparent;}#mermaid-svg-RqQmiM3h9RZk0O1n .label text,#mermaid-svg-RqQmiM3h9RZk0O1n span{fill:#333;color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .node rect,#mermaid-svg-RqQmiM3h9RZk0O1n .node circle,#mermaid-svg-RqQmiM3h9RZk0O1n .node ellipse,#mermaid-svg-RqQmiM3h9RZk0O1n .node polygon,#mermaid-svg-RqQmiM3h9RZk0O1n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .rough-node .label text,#mermaid-svg-RqQmiM3h9RZk0O1n .node .label text,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label,#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label{text-anchor:middle;}#mermaid-svg-RqQmiM3h9RZk0O1n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .rough-node .label,#mermaid-svg-RqQmiM3h9RZk0O1n .node .label,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label,#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label{text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .node.clickable{cursor:pointer;}#mermaid-svg-RqQmiM3h9RZk0O1n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .arrowheadPath{fill:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RqQmiM3h9RZk0O1n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster text{fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster span{color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RqQmiM3h9RZk0O1n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n rect.text{fill:none;stroke-width:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape p,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label rect,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RqQmiM3h9RZk0O1n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RqQmiM3h9RZk0O1n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RunnableChain.invoke()
CallbackManager.on_chain_start()
Handler1.on_chain_start()
Handler2.on_chain_start()
Handler3.on_chain_start()
LLM.invoke()
CallbackManager.on_llm_start()
Handler1.on_llm_start()
Handler2.on_llm_start()
Handler3.on_llm_start()
CallbackManager.on_llm_end()
Handler1.on_llm_end()
Handler2.on_llm_end()
Handler3.on_llm_end()
CallbackManager.on_chain_end()
Handler1.on_chain_end()
Handler2.on_chain_end()
Handler3.on_chain_end()

每个 Runnable(Chain、LLM、Tool、Retriever 等)都持有自己的 CallbackManager 实例。当外层 Runnable 调用内层 Runnable 时,callback 继承链会自动传递------子 Runnable 会继承父 Runnable 的 callback handlers,同时可以追加自己的 handler。

2.2 继承链机制

LangChain 通过 RunnableConfig 中的 callbacks 字段传递 handler。当一个 chain 包含一个 LLM 调用时:

python 复制代码
# langchain>=0.3.0
from langchain_core.runnables import RunnableConfig

config = RunnableConfig(callbacks=[my_handler])
chain.invoke({"input": "hello"}, config=config)
# 内部 LLM 自动继承 config 中的 callbacks

继承链的实现路径:

  1. Runnableinvoke()RunnableConfig 提取 callbacks
  2. 如果 RunnableConfig 没有显式传 callbacks,从 parent run 继承
  3. CallbackManager 新建时合并 parent 的 handlers 与当前配置的 handlers
  4. 事件冒泡:内层事件先通知本地 handler,再通知 parent handler

#mermaid-svg-h6jIuDYyQBMtPoSa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h6jIuDYyQBMtPoSa .error-icon{fill:#552222;}#mermaid-svg-h6jIuDYyQBMtPoSa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h6jIuDYyQBMtPoSa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .marker.cross{stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h6jIuDYyQBMtPoSa p{margin:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label text{fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label span{color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label span p{background-color:transparent;}#mermaid-svg-h6jIuDYyQBMtPoSa .label text,#mermaid-svg-h6jIuDYyQBMtPoSa span{fill:#333;color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .node rect,#mermaid-svg-h6jIuDYyQBMtPoSa .node circle,#mermaid-svg-h6jIuDYyQBMtPoSa .node ellipse,#mermaid-svg-h6jIuDYyQBMtPoSa .node polygon,#mermaid-svg-h6jIuDYyQBMtPoSa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .rough-node .label text,#mermaid-svg-h6jIuDYyQBMtPoSa .node .label text,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label,#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label{text-anchor:middle;}#mermaid-svg-h6jIuDYyQBMtPoSa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .rough-node .label,#mermaid-svg-h6jIuDYyQBMtPoSa .node .label,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label,#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label{text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .node.clickable{cursor:pointer;}#mermaid-svg-h6jIuDYyQBMtPoSa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .arrowheadPath{fill:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h6jIuDYyQBMtPoSa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster text{fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster span{color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h6jIuDYyQBMtPoSa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa rect.text{fill:none;stroke-width:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape p,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label rect,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h6jIuDYyQBMtPoSa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h6jIuDYyQBMtPoSa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} parent_config.callbacks
Chain CallbackManager
LLM CallbackManager
LLM.on_llm_start
本地Handler
继承自Chain的Handler
Chain.on_chain_start
继承自...的Handler

2.3 同步与异步双通道

BaseCallbackHandler 同时定义同步和异步接口:

python 复制代码
# langchain>=0.3.0
from langchain_core.callbacks import BaseCallbackHandler

class MyHandler(BaseCallbackHandler):
    def on_llm_start(self, serialized, prompts, **kwargs):
        # 同步路径
        pass

    async def on_llm_start(self, serialized, prompts, **kwargs):
        # 异步路径
        pass

CallbackManager 会在同步执行链(invoke)中只调用同步方法,在异步执行链(ainvoke)中只调用异步方法。如果处理器只实现了同步方法,异步路径会自动降级为同步调用。

2.4 构造函数传参 vs 运行时传参

传参方式有两种:

python 复制代码
# langchain>=0.3.0
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import StdOutCallbackHandler

# 方式一:构造时传入(生命周期绑定)
model = ChatOpenAI(
    model="gpt-4o-mini",
    callbacks=[StdOutCallbackHandler()],
)

# 方式二:运行时传入(每次调用独立)
model.invoke("你好", config={"callbacks": [StdOutCallbackHandler()]})

方式一适用于固定 logger,方式二适用于请求级别的追踪 ID 注入。

三、架构设计原则

3.1 处理器应当无状态或幂等

Callback handler 可能在多线程环境下被共享。如果 handler 持有状态(如累加 token 计数器),需要确保线程安全或使用 run_inline 隔离。

推荐模式:每个请求创建一个新的 handler 实例,在 on_chain_start 中注入 run_id

python 复制代码
# langchain>=0.3.0
import json
from uuid import UUID
from pathlib import Path
from langchain_core.callbacks import BaseCallbackHandler

class AuditHandler(BaseCallbackHandler):
    """审计日志处理器:记录每次 LLM 调用的入参和出参"""

    def __init__(self, log_dir: str = "audit_logs"):
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(exist_ok=True)

    def on_llm_start(self, serialized, prompts, **kwargs):
        run_id = kwargs.get("run_id")
        entry = {"event": "llm_start", "run_id": str(run_id), "prompts": prompts}
        (self.log_dir / f"{run_id}.jsonl").write_text(
            json.dumps(entry, ensure_ascii=False) + "\n", encoding="utf-8"
        )

    def on_llm_end(self, response, **kwargs):
        run_id = kwargs.get("run_id")
        entry = {"event": "llm_end", "run_id": str(run_id), "llm_output": response.llm_output}
        with open(self.log_dir / f"{run_id}.jsonl", "a", encoding="utf-8") as f:
            f.write(json.dumps(entry, ensure_ascii=False) + "\n")

    def on_llm_error(self, error, **kwargs):
        run_id = kwargs.get("run_id")
        entry = {"event": "llm_error", "run_id": str(run_id), "error": repr(error)}
        with open(self.log_dir / f"{run_id}.jsonl", "a", encoding="utf-8") as f:
            f.write(json.dumps(entry, ensure_ascii=False) + "\n")

3.2 不要阻塞主流程

Callback handler 与主执行流程是同步关系------handler 的执行时间直接累加到 invoke() 的总耗时。如果需要异步持久化(写入数据库、发送 HTTP 请求),使用队列解耦:

python 复制代码
# langchain>=0.3.0
import threading
from queue import Queue
from langchain_core.callbacks import BaseCallbackHandler

class AsyncLogHandler(BaseCallbackHandler):
    """写日志到远端,通过队列解耦不阻塞主流程"""

    def __init__(self):
        self.queue = Queue()
        self._worker = threading.Thread(target=self._consumer, daemon=True)
        self._worker.start()

    def on_llm_start(self, serialized, prompts, **kwargs):
        self.queue.put(("llm_start", serialized, prompts, kwargs.get("run_id")))

    def on_llm_end(self, response, **kwargs):
        self.queue.put(("llm_end", response.llm_output, kwargs.get("run_id")))

    def _consumer(self):
        while True:
            event = self.queue.get()
            # 实际写入远端存储
            ...

3.3 使用 tags 细粒度过滤

在生产环境中可能注册多个 handler,每个 handler 只关心特定链路:

python 复制代码
# langchain>=0.3.0
chain = prompt | model | parser
chain.invoke(
    {"input": "hello"},
    config={
        "tags": ["critical", "user-facing"],
        "callbacks": [alert_handler],
    },
)

在 handler 内检查 tags:

python 复制代码
# langchain>=0.3.0
class AlertHandler(BaseCallbackHandler):
    def on_chain_error(self, error, **kwargs):
        tags = kwargs.get("tags", [])
        if "critical" in tags:
            send_alert(error)  # 仅关键链路触发告警

3.4 典型场景组合

场景 推荐处理器 关键事件
调试开发 StdOutCallbackHandler 全部事件
生产日志 自定义 JsonLogHandler on_llm_start/end/error
合规审计 自定义 AuditHandler on_tool_start/end(记录工具参数+输出)
性能监控 自定义 MetricHandler on_llm_end(计算耗时 + token 数)
异常告警 自定义 AlertHandler on_llm_error, on_chain_error, on_tool_error
调用追踪 LangSmithTracer(远端 handler) 全部事件

3.5 与 LangSmith 的关系

LangSmith 的追踪功能本质上是注册了一个远端 CallbackHandler。当设置 LANGCHAIN_TRACING_V2=true 时,LangChain 自动将 LangSmithTracer(继承自 BaseCallbackHandler)注入到 CallbackManager 中:
#mermaid-svg-wAOXh3YD73NqjYqM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wAOXh3YD73NqjYqM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wAOXh3YD73NqjYqM .error-icon{fill:#552222;}#mermaid-svg-wAOXh3YD73NqjYqM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wAOXh3YD73NqjYqM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .marker.cross{stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wAOXh3YD73NqjYqM p{margin:0;}#mermaid-svg-wAOXh3YD73NqjYqM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label text{fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label span{color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label span p{background-color:transparent;}#mermaid-svg-wAOXh3YD73NqjYqM .label text,#mermaid-svg-wAOXh3YD73NqjYqM span{fill:#333;color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .node rect,#mermaid-svg-wAOXh3YD73NqjYqM .node circle,#mermaid-svg-wAOXh3YD73NqjYqM .node ellipse,#mermaid-svg-wAOXh3YD73NqjYqM .node polygon,#mermaid-svg-wAOXh3YD73NqjYqM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .rough-node .label text,#mermaid-svg-wAOXh3YD73NqjYqM .node .label text,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label,#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label{text-anchor:middle;}#mermaid-svg-wAOXh3YD73NqjYqM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .rough-node .label,#mermaid-svg-wAOXh3YD73NqjYqM .node .label,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label,#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label{text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .node.clickable{cursor:pointer;}#mermaid-svg-wAOXh3YD73NqjYqM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .arrowheadPath{fill:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wAOXh3YD73NqjYqM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wAOXh3YD73NqjYqM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster text{fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster span{color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wAOXh3YD73NqjYqM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM rect.text{fill:none;stroke-width:0;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape p,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label rect,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wAOXh3YD73NqjYqM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wAOXh3YD73NqjYqM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LangChain 应用
CallbackManager
StdOutCallbackHandler
自定义AuditHandler
LangSmithTracer

(远端CallbackHandler)
序列化 + POST
LangSmith API

(云端存储/可视化)

LangSmithTracer 在各事件回调中收集运行数据(prompt、response、token 计数、延迟、错误),序列化为 protobuf 或 JSON,异步 POST 到 LangSmith 后端。用户可在 LangSmith Web UI 中查看 trace 树、对比实验、分析成本。本质上,LangSmith 就是一个运行在远端的、自带存储和可视化能力的超级 CallbackHandler。

四、快速参考

完整导入路径

python 复制代码
from langchain_core.callbacks import BaseCallbackHandler  # langchain>=0.3.0
from langchain_core.callbacks import CallbackManager      # langchain>=0.3.0
from langchain_core.callbacks import StdOutCallbackHandler # langchain>=0.3.0
from langchain_core.callbacks import FileCallbackHandler   # langchain>=0.3.0
from langchain_core.callbacks import CallbackManagerForLLMRun
from langchain_core.runnables import RunnableConfig

最小自定义 handler 模板

python 复制代码
# langchain>=0.3.0
from langchain_core.callbacks import BaseCallbackHandler

class MyHandler(BaseCallbackHandler):
    def on_llm_start(self, serialized, prompts, **kwargs):
        # serialized: dict, LLM 配置的序列化表示
        # prompts: list[str], 传入的 prompt 列表
        # kwargs: run_id, parent_run_id, tags, metadata 等
        pass

    def on_llm_end(self, response, **kwargs):
        # response: LLMResult, 包含 generations 和 llm_output
        # llm_output 中通常包含 token_usage
        pass

    def on_tool_start(self, serialized, input_str, **kwargs):
        # input_str: str, 工具的输入(已转为字符串)
        pass

    def on_tool_end(self, output, **kwargs):
        # output: str, 工具的输出
        pass

注意事项

  • Handler 中的异常不会中断主流程(LangChain 默认 raise_error=False),但可以通过配置 CallbackManager(raise_error=True) 改变
  • 同一个 handler 实例不要在多线程 invoke 中共享可变状态
  • on_retry 事件在重试发生前触发,可用来记录重试原因和次数
  • run_idUUID 类型,可作为链路追踪的全局标识符