🌟 LangChain 30 天保姆级教程 · Day 14|Callback 机制大揭秘!监听 AI 的每一步,实现日志、监控与审计!

0 阅读4分钟

系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解 Callback 作用 → 掌握自定义 Callback → 实现“调用日志 + 耗时统计 + 敏感词拦截”三大实战功能!


🔍 一、为什么需要 Callback?

当你在生产环境部署 AI 应用时,会面临这些问题:

  • 调试困难:AI 返回错误,但不知道中间哪步出问题
  • 无法监控:不知道 LLM 调用耗时、token 消耗
  • 安全盲区:用户输入了敏感信息,却没被记录
  • 成本失控:不知道哪个功能最“烧 token”

Callback 就是 LangChain 提供的“全链路监听器”  —— 它能在以下时机触发回调:

  • LLM 开始/结束调用
  • Chain 开始/结束执行
  • Tool 被调用
  • Agent 做出决策

✅ 今天,我们就用 Callback 打造一个“可观测、可审计、可拦截”的 AI 系统!


🧱 二、LangChain Callback 核心事件

表格

事件触发时机
on_llm_start / on_llm_endLLM 调用开始/结束
on_chain_start / on_chain_endChain 执行开始/结束
on_tool_start / on_tool_endTool 被调用开始/结束
on_agent_actionAgent 决定调用某个 Tool
on_agent_finishAgent 输出最终答案

💡 所有 Runnable(Chain/Agent/Tool/LLM)都支持 Callback!


🛠️ 三、动手实践 1:基础日志 Callback

步骤 1:创建自定义 Callback Handler

# day14_callbacks.py
import time
from langchain_core.callbacks import BaseCallbackHandler

class SimpleLoggerCallback(BaseCallbackHandler):
    def on_llm_start(self, serialized, prompts, **kwargs):
        print(f"🧠 LLM 调用开始,prompt 长度: {len(prompts[0])}")

    def on_llm_end(self, response, **kwargs):
        tokens = response.llm_output.get("token_usage", {})
        total = tokens.get("total_tokens", "N/A")
        print(f"✅ LLM 调用结束,总 token: {total}")

    def on_chain_start(self, serialized, inputs, **kwargs):
        print(f"🔗 Chain 开始执行,输入: {inputs}")

    def on_chain_end(self, outputs, **kwargs):
        print(f"🔚 Chain 执行结束,输出: {outputs}")

步骤 2:在 Chain 中使用 Callback

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOllama(model="qwen:7b", temperature=0)
prompt = ChatPromptTemplate.from_template("翻译成英文:{text}")
chain = prompt | llm

# 调用时传入 callbacks
response = chain.invoke(
    {"text": "你好,世界!"},
    config={"callbacks": [SimpleLoggerCallback()]}
)
print("最终结果:", response.content)

▶️ 输出示例:

🔗 Chain 开始执行,输入: {'text': '你好,世界!'}
🧠 LLM 调用开始,prompt 长度: 42
✅ LLM 调用结束,总 token: 35
🔚 Chain 执行结束,输出: AIMessage(content='Hello, world!')
最终结果: Hello, world!

✅ 成功捕获全流程!


🛠️ 四、动手实践 2:性能监控 Callback(记录耗时)

class TimingCallback(BaseCallbackHandler):
    def __init__(self):
        self.start_time = None

    def on_chain_start(self, serialized, inputs, **kwargs):
        self.start_time = time.time()
        print("⏱️  开始计时...")

    def on_chain_end(self, outputs, **kwargs):
        elapsed = time.time() - self.start_time
        print(f"⏱️  总耗时: {elapsed:.2f} 秒")

💡 可扩展为 Prometheus 指标上报,用于 Grafana 监控。


🛠️ 五、动手实践 3:安全审计 Callback(敏感词拦截)

class SecurityAuditCallback(BaseCallbackHandler):
    SENSITIVE_WORDS = ["密码", "身份证", "银行卡"]

    def on_chain_start(self, serialized, inputs, **kwargs):
        text = str(inputs)
        for word in self.SENSITIVE_WORDS:
            if word in text:
                raise ValueError(f"⚠️ 检测到敏感词:{word}!请求已拦截。")

    def on_tool_start(self, serialized, input_str, **kwargs):
        # 同样检查 Tool 输入
        for word in self.SENSITIVE_WORDS:
            if word in input_str:
                raise ValueError(f"⚠️ Tool 调用含敏感词:{word}!")

🔒 安全第一:在生产环境中,这是防止数据泄露的关键防线!


🤖 六、在 Agent 中使用 Callback

Agent 的 Callback 使用方式完全相同:

from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools import DuckDuckGoSearchRun

tools = [DuckDuckGoSearchRun()]
agent = create_react_agent(llm, tools, create_react_agent.get_default_prompt())
executor = AgentExecutor(agent=agent, tools=tools, verbose=False)

# 同时注册多个 Callback!
callbacks = [
    SimpleLoggerCallback(),
    TimingCallback(),
    SecurityAuditCallback()
]

try:
    response = executor.invoke(
        {"input": "杭州今天的天气如何?"},
        config={"callbacks": callbacks}
    )
    print("Agent 最终回答:", response["output"])
except ValueError as e:
    print(e)

✅ 所有事件都会被监听,包括 Tool 调用!


📊 七、高级用法:全局 Callback(自动生效)

不想每次手动传 callbacks?可以用 全局回调

from langchain.globals import set_debug, set_verbose
from langchain.callbacks import StdOutCallbackHandler

# 全局打印详细日志(开发用)
set_debug(True)

# 或注册全局 handler
import langchain
langchain.callbacks.manager.tracing_v2_callback_var.set(
    [StdOutCallbackHandler()]
)

⚠️ 注意:全局 Callback 会影响所有 LangChain 调用,生产环境慎用。


⚠️ 八、注意事项 & 最佳实践

表格

场景建议
高并发服务Callback 逻辑要轻量,避免阻塞
日志存储将日志写入文件或 ELK,而非 print
敏感信息脱敏在 on_llm_end 中对 output 做脱敏处理
成本分析从 response.llm_output["token_usage"] 提取 token 数
异常处理Callback 内部异常会中断主流程,务必 try-catch

💡 生产架构建议

  • 开发环境:开启 verbose=True + StdOutCallbackHandler
  • 生产环境:自定义 Callback 写入日志系统 + 监控告警

📦 九、配套代码结构

langchain-30-days/
└── day14/
    └── callback_handlers.py  # 日志 + 耗时 + 安全审计三大 Callback

📝 十、今日小结

  • ✅ 理解了 Callback 在可观测性中的核心价值
  • ✅ 学会了继承 BaseCallbackHandler 自定义监听逻辑
  • ✅ 实现了日志、性能监控、安全审计三大实用功能
  • ✅ 掌握了在 Chain 和 Agent 中使用 Callback 的方法
  • ✅ 知道了全局 Callback 与局部 Callback 的区别

🎯 明日预告:Day 15 —— 异步与流式响应!让 AI 边思考边输出,打造丝滑用户体验!