导读:本期聚焦于缓存小熊猫创作的《Agent调试怎么做?日志记录与中间状态查看的完整实践指南》,敬请观看详情。智能体Agent运行出错时往往难以定位问题,因为其执行链路涉及模型调用、工具执行、记忆读写等多个环节。本文系统讲解Agent调试的两大核心手段:一是日志体系搭建,包括结构化日志设计、分层记录模型输入输出与工具调用参数;二是中间状态查看,涵盖state dump、断点跟踪、replay回放等技巧。文中给出Python可运行的代码示例,演示如何为自研Agent或基于LangChain的Agent加日志、抓状态,并对比print、logging、回调等方案的优缺点,帮助开发者在复杂决策链路中快速找到出错环节。

Agent系统与传统单次调用的程序不同,它的执行过程是一条动态决策链:模型思考、选择工具、执行工具、观察结果、再思考。这条链路上任何一环出错,都可能导致最终答案偏离预期。而更麻烦的是,Agent的中间过程往往被框架封装在内部,只暴露最终输出,一旦结果不对,你很难知道是模型推理错了、工具参数传错了,还是上下文被截断了。因此,日志记录与中间状态查看是Agent开发中不可或缺的两大调试利器。

Agent调试怎么做?日志记录与中间状态查看的完整实践指南

一、为什么Agent调试比普通程序更难

普通程序的执行路径是确定的,设个断点就能看到每一步的变量值。而Agent的执行路径由大模型在运行时决定,同一段代码每次运行可能走完全不同的分支:第一次调用搜索工具,第二次直接回答,第三次陷入循环调用。这种不确定性使得传统的断点调试效率低下。

此外,Agent出错的表现形式往往具有很强的迷惑性。例如模型明明拿到了正确的历史消息,却给出了错误的工具调用参数;或者工具返回了正确结果,但模型在下一轮推理中忽略了它。这类问题不查看中间状态根本无从判断。常见的问题类型包括:

  • 上下文丢失:历史消息过长被截断,导致模型忘记任务目标
  • 参数幻觉:模型编造了工具不存在的参数名或传入了错误格式的值
  • 循环调用:模型反复调用同一个工具,没有从结果中提取有效信息
  • 提示词污染:工具返回的内容干扰了模型的推理方向

要定位这些问题,必须让Agent的每一步都留下痕迹。这就要靠日志体系和状态快照两个手段。

二、搭建Agent结构化日志体系

最直接的方式是使用Python标准库logging,但Agent场景对日志有特殊要求:你需要同时记录模型输入输出、工具调用细节和最终结果,并且最好以结构化格式存储,方便后续过滤分析。下面是一个完整的示例,展示如何为一个自研ReAct风格Agent加上分层日志。

import logging
import json

# 配置结构化日志
logger = logging.getLogger("agent")
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler("agent_debug.log", encoding="utf-8")
formatter = logging.Formatter(
    '%(asctime)s | %(levelname)s | %(name)s | %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)

def log_json(event: str, **kwargs):
    """以JSON格式记录事件,便于后续用脚本分析"""
    payload = {"event": event}
    payload.update(kwargs)
    logger.info(json.dumps(payload, ensure_ascii=False, default=str))

class DebuggableAgent:
    def __init__(self, llm, tools):
        self.llm = llm
        self.tools = {t.name: t for t in tools}
        self.messages = []

    def run(self, user_input: str, max_steps: int = 8):
        self.messages.append({"role": "user", "content": user_input})
        log_json("user_input", text=user_input)

        for step in range(max_steps):
            # 记录发送给模型的完整上下文
            log_json("llm_request", step=step, messages=self.messages)
            response = self.llm.chat(self.messages)
            # 记录模型的原始返回,这是排查问题的关键
            log_json("llm_response", step=step, raw=response)

            action = self.parse_action(response)
            if action is None:
                log_json("final_answer", step=step, answer=response)
                return response

            tool_name = action["tool"]
            tool_args = action["args"]
            if tool_name not in self.tools:
                log_json("tool_not_found", step=step, tool=tool_name)
                observation = f"错误:工具 {tool_name} 不存在"
            else:
                # 记录工具入参,参数幻觉问题在这里现形
                log_json("tool_call", step=step, tool=tool_name, args=tool_args)
                try:
                    observation = self.tools[tool_name].run(**tool_args)
                except Exception as e:
                    log_json("tool_error", step=step, tool=tool_name,
                             error=str(e))
                    observation = f"工具执行失败:{e}"

            # 记录工具返回结果
            log_json("tool_result", step=step, tool=tool_name,
                     observation=observation)
            self.messages.append(
                {"role": "user", "content": f"观察结果:{observation}"}
            )

        log_json("max_steps_reached", max_steps=max_steps)
        return "已达最大步数,任务未完成"

这套日志体系的核心设计在于三个记录点:llm_request记录发送给模型的完整上下文,llm_response记录模型的原始输出,tool_call与tool_result记录工具的入参和返回。其中llm_request尤其重要,它能让你直观看到上下文是如何一步步增长的,从而发现消息被截断或格式混乱的问题。

相比直接使用print,logging方案有三个显著优势:一是支持日志级别,调试期开DEBUG、上线后只保留WARNING以上;二是可以同时输出到文件和控制台,便于长期留存排查;三是JSON格式可以用简单的Python脚本做统计分析,例如统计某个工具被调用的频率,快速定位循环调用问题。

三、中间状态查看与回放调试

日志是流式的,适合看过程;而中间状态是快照式的,适合看某一时刻Agent的完整内部情况。对于复杂的Stateful Agent,建议在每个关键节点dump一次状态,保存为一个可回放的轨迹文件。

import json
from datetime import datetime

class StateRecorder:
    """记录Agent每一步的完整中间状态,支持回放"""

    def __init__(self, session_id: str):
        self.session_id = session_id
        self.trajectory = []

    def capture(self, step: int, phase: str, state: dict):
        snapshot = {
            "session": self.session_id,
            "time": datetime.now().isoformat(),
            "step": step,
            "phase": phase,  # thinking / tool_calling / observing
            "state": {
                "messages": state.get("messages", []),
                "memory": state.get("memory", {}),
                "plan": state.get("plan", ""),
                "pending_actions": state.get("pending_actions", []),
            },
        }
        self.trajectory.append(snapshot)

    def save(self, filename: str):
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(self.trajectory, f, ensure_ascii=False, indent=2)

    @classmethod
    def replay(cls, filename: str):
        """回放轨迹,逐步检查中间状态"""
        with open(filename, encoding="utf-8") as f:
            trajectory = json.load(f)
        for snap in trajectory:
            print(f"--- 第{snap['step']}步 [{snap['phase']}] ---")
            print(json.dumps(snap["state"], ensure_ascii=False, indent=2))

使用时在Agent的每个阶段调用capture方法,任务结束后save保存轨迹。当结果不符合预期时,用replay逐步检查:先看thinking阶段模型的计划和推理是否合理,再看tool_calling阶段传入的参数是否正确,最后看observing阶段模型是否正确消化了工具返回。这种逐帧回放的方式,几乎可以覆盖所有类型的Agent问题定位。

对于多Agent协作系统,状态记录还要注意保存Agent之间的消息传递。可以在消息总线层面统一拦截,为每条消息打上发送方、接收方和时间戳,这样当子Agent给出错误结论时,可以追溯到它收到了哪些上游信息。

四、基于框架回调机制的调试方案

如果使用LangChain或LangGraph这类框架,不必自己造轮子,它们提供了回调机制可以直接接入日志体系。以LangChain为例,自定义一个回调处理器即可捕获所有模型调用和工具执行事件。

from langchain_core.callbacks import BaseCallbackHandler

class AgentDebugHandler(BaseCallbackHandler):
    """捕获Agent执行过程中的所有关键事件"""

    def on_llm_start(self, serialized, prompts, **kwargs):
        print(f"[LLM开始] 输入提示:{prompts}")

    def on_llm_end(self, response, **kwargs):
        print(f"[LLM结束] 输出内容:{response.generations}")

    def on_tool_start(self, serialized, input_str, **kwargs):
        print(f"[工具开始] 名称:{serialized.get('name')},"
              f"参数:{input_str}")

    def on_tool_end(self, output, **kwargs):
        print(f"[工具结束] 返回:{output}")

    def on_tool_error(self, error, **kwargs):
        print(f"[工具异常] {error}")

# 使用方式:传入callbacks参数
# agent.invoke(
#     {"input": "查询北京天气"},
#     config={"callbacks": [AgentDebugHandler()]}
# )

回调方案的优点是与业务代码完全解耦,不需要改动Agent核心逻辑,加一行配置即可开启。LangGraph还提供了更强大的get_state方法,可以在执行的任意节点读取图的当前状态,配合中断机制,实现类似断点调试的效果。

另外值得推荐的是LangSmith这类可视化追踪平台,它能自动记录每一步的输入输出并生成依赖图,适合团队协作场景。不过对于个人开发或对数据隐私敏感的场景,本地日志加状态快照的组合已经足够应对绝大多数调试需求。

五、调试实践中的经验总结

综合来看,高效的Agent调试需要日志、状态、回放三层手段配合:日志保证过程可追溯,状态快照保证内部可见,回放机制保证问题可复现。落地时有几点经验值得注意。第一,日志要记录模型的原始返回而非解析后的结果,很多问题恰恰出在解析环节。第二,工具调用的参数务必完整记录,参数幻觉是最高频的问题类型。第三,给每次会话分配唯一ID贯穿所有日志,避免多个请求的日志混在一起。第四,长上下文场景下要定期检查llm_request中的消息列表长度,防止静默截断。

最后提醒一点:调试日志本身也可能成为性能负担和信息泄露风险。生产环境应控制日志级别,对用户敏感信息脱敏,并设置合理的日志保留周期。开发期详细、上线后精简,才是日志体系的正确打开方式。

Agent调试Agent日志中间状态修改时间:2026-08-31 16:15:06

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。