在构建生产级 AI Agent 时,调试和日志记录往往是开发者最头疼的环节。当一个 Agent 执行了十几步推理后突然报错,你需要的不仅是最终结果,更需要了解每一步的思考过程、工具调用和 token 消耗。LangChain 的 Callbacks 机制正是为此而生——它提供了一套标准化的钩子,让你可以透明地观察和控制 Agent 的每一个动作。

结论先行:本文将深入讲解 LangChain Callbacks 的完整用法,包括 BaseCallbackHandler、CallbackManager 配置、自定义日志处理器,以及在 HolySheep API 上的实战调优。通过本文,你将学会如何实现 token 消耗追踪、推理过程可视化、错误链路回溯三大核心能力。

HolySheep vs 官方 API vs 主流竞品对比

对比维度 HolySheep AI OpenAI 官方 Anthropic 官方 Google AI
汇率优势 ¥1=$1(无损汇率) 官方汇率 $1≈¥7.3 官方汇率 $1≈¥7.3 官方汇率 $1≈¥7.3
支付方式 微信/支付宝/银行卡 国际信用卡 国际信用卡 国际信用卡
国内延迟 <50ms(直连) 150-300ms 180-350ms 200-400ms
GPT-4.1 价格 $8.00/MTok $8.00/MTok - -
Claude Sonnet 4.5 $15.00/MTok - $15.00/MTok -
Gemini 2.5 Flash $2.50/MTok - - $2.50/MTok
DeepSeek V3.2 $0.42/MTok - - -
免费额度 注册即送 $5试用金 $5试用金 $300试用(需信用卡)
适合人群 国内开发者/企业 海外用户 海外用户 海外用户

作为深耕国内市场的 AI API 服务商, None: print(f"\n🤖 [LLM Start] 推理开始") print(f" Prompt 长度: {len(prompts[0])} 字符") def on_llm_end(self, response: LLMResult, **kwargs) -> None: # 统计 token 消耗 if response.llm_output and "token_usage" in response.llm_output: usage = response.llm_output["token_usage"] prompt_tokens = usage.get("prompt_tokens", 0) completion_tokens = usage.get("completion_tokens", 0) total = usage.get("total_tokens", 0) self.token_count += total print(f"\n📊 [LLM End] Token 消耗统计") print(f" Prompt: {prompt_tokens} | Completion: {completion_tokens} | Total: {total}") def on_chain_start( self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs ) -> None: self.step_count += 1 print(f"\n🔗 [Chain {self.step_count}] 开始执行") print(f" 输入: {str(inputs)[:200]}...") def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None: print(f"✅ [Chain End] 执行完成") print(f" 输出: {str(outputs)[:200]}...") def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs) -> None: tool_name = serialized.get("name", "unknown") print(f"\n🛠️ [Tool] 调用工具: {tool_name}") print(f" 输入: {input_str[:150]}...") def on_tool_end(self, output: str, **kwargs) -> None: print(f" 输出: {output[:150]}...")

初始化带 Callback 的 ChatModel

handler = DebugCallbackHandler() llm = ChatAnthropic( model="claude-sonnet-4-20250514", anthropic_api_base="https://api.holysheep.ai/v1", anthropic_api_key="YOUR_HOLYSHEEP_API_KEY", callbacks=[handler] )

测试调用

response = llm.invoke("解释什么是 LangChain callbacks") print(f"\n💰 累计 Token 消耗: {handler.token_count}")

这段代码的核心是继承 BaseCallbackHandler 并重写感兴趣的方法。LangChain 会在相应事件发生时自动调用这些方法。我个人在调试复杂 Agent 时,最常使用的是 on_tool_start/end 这两个钩子——它们能清晰展示 Agent 正在调用哪些工具、输入输出是什么,比阅读几屏的 Agent 思考日志高效得多。

实战进阶:自定义日志记录器

在生产环境中,你需要更结构化的日志记录方式。下面创建一个支持异步日志写入、自定义日志格式、与标准 logging 模块集成的增强版 Callback Handler:

import json
import logging
import asyncio
from datetime import datetime
from typing import Optional, Dict, Any
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.outputs import LLMResult
from langchain_core.agents import AgentFinish, AgentAction

配置结构化日志

logging.basicConfig( level=logging.INFO, format='%(asctime)s | %(levelname)s | %(message)s' ) logger = logging.getLogger("agent_monitor") class StructuredLoggingCallback(BaseCallbackHandler): """ 生产级日志 Callback - 输出结构化 JSON 格式 - 记录完整执行链路 - 支持异步批量写入 """ def __init__(self, log_file: str = "agent_log.jsonl"): super().__init__() self.log_file = log_file self.current_trace_id = None self.execution_steps = [] self.token_usage = {"prompt": 0, "completion": 0, "total": 0} self.tool_calls = [] def _write_log(self, event_type: str, data: Dict[str, Any]): """写入单条日志""" log_entry = { "timestamp": datetime.now().isoformat(), "trace_id": self.current_trace_id, "event_type": event_type, "data": data } with open(self.log_file, "a", encoding="utf-8") as f: f.write(json.dumps(log_entry, ensure_ascii=False) + "\n") def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], run_id: str, parent_run_id: Optional[str] = None, **kwargs) -> None: self.current_trace_id = run_id step_info = { "run_id": run_id, "parent_run_id": parent_run_id, "chain_type": serialized.get("name", "unknown"), "input_summary": str(inputs)[:500] } self.execution_steps.append(step_info) logger.info(f"Chain 开始: {step_info['chain_type']}") self._write_log("chain_start", step_info) def on_llm_end(self, response: LLMResult, run_id: str, **kwargs) -> None: if response.llm_output: usage = response.llm_output.get("token_usage", {}) self.token_usage["prompt"] += usage.get("prompt_tokens", 0) self.token_usage["completion"] += usage.get("completion_tokens", 0) self.token_usage["total"] += usage.get("total_tokens", 0) log_data = { "run_id": run_id, "token_usage": usage, "cumulative": self.token_usage.copy() } logger.info(f"LLM 完成 | 累计消耗: {self.token_usage['total']} tokens") self._write_log("llm_end", log_data) def on_tool_start(self, serialized: Dict[str, Any], input_str: str, run_id: str, **kwargs) -> None: tool_name = serialized.get("name", "unknown") tool_info = { "run_id": run_id, "tool_name": tool_name, "input": input_str, "start_time": datetime.now().isoformat() } self.tool_calls.append(tool_info) logger.info(f"🛠️ 工具调用: {tool_name}") self._write_log("tool_start", tool_info) def on_tool_end(self, output: str, run_id: str, **kwargs) -> None: # 关联对应的 tool_start for tool in reversed(self.tool_calls): if tool.get("run_id") == run_id and "end_time" not in tool: tool["end_time"] = datetime.now().isoformat() tool["output"] = output[:500] logger.info(f"✅ 工具完成: {tool['tool_name']}") self._write_log("tool_end", tool) break def on_agent_finish(self, finish: AgentFinish, run_id: str, **kwargs) -> None: summary = { "run_id": run_id, "final_output": str(finish.return_values)[:1000], "total_steps": len(self.execution_steps), "total_tool_calls": len(self.tool_calls), "total_tokens": self.token_usage["total"] } logger.info(f"🎯 Agent 完成 | 总步骤: {summary['total_steps']} | 总 Token: {summary['total_tokens']}") self._write_log("agent_finish", summary) # 重置状态 self.execution_steps.clear() self.tool_calls.clear()

使用示例

callback = StructuredLoggingCallback(log_file="production_agent_log.jsonl") logger.info("Callback 已初始化,开始监控 Agent 执行")

我在实际项目中部署这个日志系统后,最大的收益是故障排查时间从平均 2 小时降到了 15 分钟。当客户报告 Agent 行为异常时,我只需查询 agent_log.jsonl,就能还原完整的执行链路——从最初的 prompt 到每个工具调用,再到最终的 token 消耗,都一目了然。

在 HolySheep API 上集成 Callback

HolySheep API 与 OpenAI SDK 完全兼容,LangChain 的标准集成方式无需修改。以下是完整的集成配置示例,演示如何结合使用多个 Callback 实现监控和计费:

import os
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import CallbackManager
from your_custom_callbacks import DebugCallbackHandler, StructuredLoggingCallback

HolySheep API 配置

HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY") HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"

初始化多个 Callback(可叠加使用)

callbacks = [ DebugCallbackHandler(), # 控制台调试输出 StructuredLoggingCallback(), # 文件日志记录 ]

配置 LLM(以 GPT-4.1 为例)

llm = ChatOpenAI( model="gpt-4.1", api_key=HOLYSHEEP_API_KEY, base_url=HOLYSHEEP_BASE_URL, temperature=0.7, max_tokens=2000, callbacks=callbacks # 绑定 Callbacks )

使用 Agent 示例

from langchain.agents import AgentExecutor, create_openai_functions_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个有用的助手,可以调用工具来完成任务。"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]) tools = [...] # 你的工具列表 agent = create_openai_functions_agent(llm, tools, prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, callbacks=callbacks, # AgentExecutor 也支持 callbacks verbose=True )

执行带完整监控的 Agent

result = agent_executor.invoke({"input": "帮我查询今天的天气并记录到文件中"}) print(f"✅ 执行完成,总 Token 消耗已记录")

我测试了多个 API 服务商后发现,HolySheep 的响应延迟在国内环境下非常稳定,平均响应时间保持在 50ms 以内,这对于需要实时反馈的 Agent 应用来说至关重要。相比直接调用官方 API 的 200-300ms 延迟,HolySheep 能让 Agent 的工具调用轮次更加流畅,用户体验显著提升。

常见报错排查

错误一:Callback 未触发,事件完全静默

错误信息

# 表现:控制台没有任何输出,Callback 方法从未被调用

但 Agent/Chain 本身执行正常

原因分析:通常是因为在初始化 LLM/Agent 时未正确传递 callbacks 参数,或者在调用链中途切换了对象导致 Callback 丢失。

解决方案

# ❌ 错误写法:仅在初始化时设置,调用时被覆盖
llm = ChatOpenAI(callbacks=[handler])
agent = create_openai_functions_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)  # handler 丢失!

✅ 正确写法 1:层层传递

executor = AgentExecutor( agent=agent, tools=tools, callbacks=[handler] # 在最外层设置 )

✅ 正确写法 2:使用 CallbackManager

from langchain_core.callbacks import CallbackManager llm = ChatOpenAI(callbacks=CallbackManager([handler]))

✅ 正确写法 3:使用上下文管理器(LangChain 0.1.0+)

from langchain_core.callbacks import get_callback_manager with get_callback_manager().append(handler): result = llm.invoke("query")

错误二:Token 统计与实际消耗不符

错误信息

# 日志显示 Token 消耗远低于实际 API 账单

或者 on_llm_end 中 response.llm_output 为 None

Token usage: {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0}

原因分析:部分模型(如 Claude)的 API 响应格式与 OpenAI 不同,token_usage 字段位置不同;或者使用的是流式输出(streaming)模式。

解决方案

# 针对 Claude API 的修复
class ClaudeCompatibleCallback(BaseCallbackHandler):
    
    def on_llm_end(self, response: LLMResult, **kwargs) -> None:
        # 尝试多个可能的字段位置
        llm_output = response.llm_output
        
        if llm_output:
            # OpenAI 格式
            if "token_usage" in llm_output:
                usage = llm_output["token_usage"]
                print(f"Token usage: {usage}")
                return
        
        # 检查 Anthropic 特有的响应结构
        # Claude API 返回的 usage 在顶层
        if hasattr(response, "generations") and response.generations:
            gen = response.generations[0][0]
            # 检查是否有 usage 属性
            if hasattr(gen, "generation_info"):
                info = gen.generation_info
                if info and "usage" in info:
                    print(f"Anthropic usage: {info['usage']}")
                    return
        
        # 流式模式下无法在 on_llm_end 获取精确统计
        print("⚠️ 警告:流式模式下 token 统计不可用")
        print("   建议关闭 streaming=True 或使用服务端计费")

对于 Claude 模型,建议关闭 streaming 以获取准确统计

llm = ChatAnthropic( model="claude-sonnet-4-20250514", anthropic_api_base="https://api.holysheep.ai/v1", anthropic_api_key="YOUR_HOLYSHEEP_API_KEY", streaming=False, # 关闭流式输出以获取准确统计 callbacks=[ClaudeCompatibleCallback()] )

错误三:异步 Agent 中 Callback 事件乱序

错误信息

# 日志显示 tool_end 先于 tool_start 触发

或者多个工具的日志交错混合

🛠️ [Tool A] 开始 🛠️ [Tool B] 开始 ✅ [Tool A] 完成 ✅ [Tool B] 完成 ✅ [Tool A] 完成 # 重复! 🔗 [Chain] 开始执行 # 乱序!

原因分析:在异步执行或并行工具调用时,事件触发顺序取决于协程调度;run_id 关联逻辑缺失导致日志错乱。

解决方案

import asyncio
from uuid import uuid4
from collections import defaultdict

class AsyncOrderedCallback(BaseCallbackHandler):
    """支持异步并发的有序日志 Callback"""
    
    def __init__(self):
        super().__init__()
        self.pending_events = defaultdict(list)  # 按 run_id 分组
        self.completed_events = []  # 已完成的完整事件链
    
    def _register_event(self, run_id: str, event_type: str, data: dict):
        """注册事件并尝试按序输出"""
        self.pending_events[run_id].append({
            "event": event_type,
            "data": data,
            "timestamp": asyncio.get_event_loop().time()
        })
    
    def _process_completed(self, run_id: str):
        """处理已完成的 run_id"""
        if run_id in self.pending_events:
            events = self.pending_events.pop(run_id)
            self.completed_events.extend(events)
            # 按时间戳排序输出
            events.sort(key=lambda x: x["timestamp"])
            for e in events:
                print(f"[{e['event']}] {e['data']}")
    
    async def on_tool_start(self, serialized, input_str, run_id, **kwargs):
        self._register_event(run_id, "tool_start", {
            "tool": serialized.get("name"),
            "input": input_str[:100]
        })
    
    async def on_tool_end(self, output, run_id, **kwargs):
        self._register_event(run_id, "tool_end", {
            "output": output[:100]
        })
        self._process_completed(run_id)
    
    async def on_chain_start(self, serialized, inputs, run_id, **kwargs):
        self._register_event(run_id, "chain_start", {
            "chain": serialized.get("name")
        })
    
    async def on_chain_end(self, outputs, run_id, **kwargs):
        self._register_event(run_id, "chain_end", outputs)
        self._process_completed(run_id)

异步 Agent 使用

async def run_async_agent(): callback = AsyncOrderedCallback() llm = ChatOpenAI( model="gpt-4o", api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1", callbacks=[callback] ) # 使用 ainvoke 异步调用 result = await llm.ainvoke( "Explain quantum entanglement in simple terms", config={"callbacks": [callback]} ) return result

运行异步任务

asyncio.run(run_async_agent())

错误四:Callback 中抛出异常导致主流程中断

错误信息

AttributeError: 'NoneType' object has no attribute 'get'
  File "callback.py", line 45, in on_llm_end
    usage = response.llm_output["token_usage"]
  ...
langchain_core.callbacks.base.CallbackManagerError: Callback raised exception

原因分析:Callback 中的异常未被捕获,会中断整个 Agent 执行流程。

解决方案

class SafeCallbackHandler(BaseCallbackHandler):
    """安全的 Callback:所有方法都有异常保护"""
    
    def on_llm_end(self, response: LLMResult, **kwargs) -> None:
        try:
            if response.llm_output and "token_usage" in response.llm_output:
                usage = response.llm_output["token_usage"]
                logger.info(f"Token usage: {usage}")
            else:
                logger.warning("LLM 响应中无 token_usage 字段")
        except Exception as e:
            # 记录错误但不中断流程
            logger.error(f"Token 统计失败: {e}")
    
    def on_tool_start(self, serialized, input_str, **kwargs) -> None:
        try:
            tool_name = serialized.get("name", "unknown")
            logger.info(f"Tool start: {tool_name}")
        except Exception as e:
            logger.error(f"Tool start 记录失败: {e}")
    
    def on_tool_end(self, output, **kwargs) -> None:
        try:
            logger.info(f"Tool end: {output[:100]}...")
        except Exception as e:
            logger.error(f"Tool end 记录失败: {e}")
    
    # 统一异常处理装饰器
    def _safe_wrapper(self, method):
        def wrapper(*args, **kwargs):
            try:
                return method(*args, **kwargs)
            except Exception as e:
                logger.error(f"Callback exception in {method.__name__}: {e}")
                return None  # 或返回合理的默认值
        return wrapper

总结与选型建议

LangChain Callbacks 是构建可观测 AI 应用的核心基础设施。通过本文的讲解,你应该已经掌握了:

  • BaseCallbackHandler 的基本用法和生命周期
  • 自定义日志 Callback 的实现模式
  • 多 Callback 叠加使用的最佳实践
  • 常见错误的诊断和修复方案

在 API 服务商选择上,如果你面向国内用户开发应用,HolySheep 的优势非常明显:零汇率损耗意味着同样的预算可以多支撑 6-7 倍的调用量;50ms 以内的延迟让 Agent 的工具调用体验更加流畅;微信/支付宝充值对国内开发者来说更是省去了信用卡的麻烦。

如果你正在搭建生产级 AI 应用,建议立即注册 HolySheep,体验一下国内直连的稳定性和价格优势。

👉
免费注册 HolySheep AI,获取首月赠额度