调试Agent比普通程序复杂,因自然语言输入、LLM推理不可见、多节点编排及流式输出。三种调试方法:日志记录LLM调用、工具调用与循环轮次;eino-extdevops交互调试器提供节点SSE状态与耗时;Mermaid可视化生成Graph图。不同问题可选对应姿势。
调试Agent的过程与调试普通程序存在显著差异。普通程序逻辑固定,输入输出可预期,断点一处即可清晰查看状态。而Agent的调试则复杂得多,涉及自然语言输入、LLM内部推理不可见、多节点编排以及流式输出等挑战。
普通程序:函数入参固定,输出确定,断点打下去就能看到状态。
长期稳定更新的攒劲资源: >>>点此立即查看<<<

Agent:
下文将介绍三种应对上述问题的调试方法,逐一说明。
虽然方法略显基础,但它是直接且有效的调试手段,适用于初期阶段或尚未使用编排框架的场景。
至少记录以下三类信息:
// 1. 每次 LLM 调用的输入和输出
log.Printf("[llm] input: %v", messages)
resp, err := chatModel.Generate(ctx, messages)
log.Printf("[llm] output: %s, finish_reason: %s", resp.Content, resp.ResponseMeta.FinishReason)
// 2. 工具调用
log.Printf("[tool] call: %s, args: %s", toolName, argsJSON)
result, err := tool.Invoke(ctx, args)
log.Printf("[tool] result: %s", result)
// 3. 循环轮次(ReAct 循环最容易死循环)
log.Printf("[react] turn=%d, action=%s", turn, action)
Token用量建议记录在INFO级别:
usage := resp.ResponseMeta.Usage
log.Printf("[token] prompt=%d, completion=%d, total=%d", usage.PromptTokens, usage.CompletionTokens, usage.TotalTokens)
无需记录的内容:
在E31中,Agent直接调用agent.Stream(ctx, messages)。如需添加日志但不修改主流程,可通过callback实现:
import "github.com/cloudwego/eino/callbacks"
type DebugCallback struct{}
func (d *DebugCallback) OnStart(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
log.Printf("[%s] start: %T", info.Name, input)
return ctx
}
func (d *DebugCallback) OnEnd(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
log.Printf("[%s] end: %T", info.Name, output)
return ctx
}
func (d *DebugCallback) OnError(ctx context.Context, info *callbacks.RunInfo, err error) context.Context {
log.Printf("[%s] error: %v", info.Name, err)
return ctx
}
随后将callback注册到context:
ctx = callbacks.CtxWithHandlers(ctx, []callbacks.Handler{&DebugCallback{}})
sr, err := agent.Stream(ctx, messages)
这样无需修改Agent主流程,所有节点的生命周期事件均会触发日志记录。
自行添加日志只能查看已设定的点,且无法观察节点间的数据流转。eino-ext/devops提供了一个本地HTTP调试服务器,每个节点执行后推送一条SSE事件,包含输入、输出、耗时和Token数。
启动调试服务器:
import "github.com/cloudwego/eino-ext/devops"
func main() {
ctx := context.Background()
// 启动调试 HTTP 服务器(默认 127.0.0.1:52538)
if err := devops.Init(ctx); err != nil {
log.Fatal(err)
}
// 正常编译和运行你的 Agent
// ...
}
devops.Init会在全局注册一个GraphCompileCallback,后续所有编译的Graph/Chain均会被自动捕获。
在浏览器中进行调试:
访问http://127.0.0.1:52538(或使用Eino官方IDE插件),可查看:
NodeDebugState:输入、输出、Token数、耗时通过命令行查看SSE流(无需浏览器):
# 创建调试线程
curl -X POST http://127.0.0.1:52538/eino/devops/debug/v1/graphs/{graph_id}/threads
# 从指定节点开始,SSE 流式返回每节点状态
curl -N -X POST http://127.0.0.1:52538/eino/devops/debug/v1/graphs/{graph_id}/threads/{tid}/stream -H 'Content-Type: application/json' -d '{"from_node": "ChatModel", "input": {...}}'
适用场景:
tool_callsNodeDebugState包含毫秒级耗时注意:devops服务器仅用于开发环境,请勿在生产部署中启动它。
Agent执行的路径是怎样的?通过可视化工具直接绘制Graph。
import "github.com/cloudwego/eino/devops"
// graph 编译前,生成 Mermaid 图
diagram, err := devops.Visualize(graph)
if err != nil {
log.Fatal(err)
}
fmt.Println(diagram)
输出为标准Mermaid文本:
graph TD
__start__ --> ChatTemplate
ChatTemplate --> ChatModel
ChatModel --> |tool_calls != nil| ToolsNode
ChatModel --> |finish_reason == stop| __end__
ToolsNode --> ChatModel
将这段文本粘贴到mermaid.live,即可查看图形:
[start] → [ChatTemplate] → [ChatModel] (有工具调用) [ToolsNode] → [ChatModel]
(stop)[end]
适用场景:
在HTTP handler中暴露调试端点(开发环境):
// 开发环境加一个 /debug/graph 端点
http.HandleFunc("/debug/graph", func(w http.ResponseWriter, r *http.Request) {
diagram, _ := devops.Visualize(graph)
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, diagram)
})
| 问题 | 推荐姿势 |
|---|---|
| Agent 未按预期调用工具 | 姿势二(devops),查看ChatModel节点输出是否包含tool_call |
| 工具返回了什么内容 | 姿势二(devops),查看ToolsNode节点输出 |
| 某次请求消耗了多少Token | 姿势一(日志),在OnEnd callback中记录usage |
| 新接手Agent,不了解其结构 | 姿势三(Mermaid),先画图再阅读代码 |
| 某个节点报错但缺少上下文 | 姿势二(devops)+ 姿势一(OnError callback) |
| 分支路由配置错误,走了错误路径 | 姿势三(Mermaid)查看图 + 姿势二确认实际路径 |
假设你的Agent询问天气问题,却没有调用get_weather工具,直接回复“我不知道”:
第一步:启动devops,重现问题
devops.Init(ctx)
// 加这一行,启动本地调试服务器
访问http://127.0.0.1:52538,选择你的Graph,创建调试线程,发送问题。
查看ChatModel节点的输出:
{"role": "assistant", "content": "我不知道北京今天的天气"}
没有tool_calls字段——LLM未调用工具。
第二步:查看ChatModel的输入
[{"role": "system", "content": "你是一个助手"}, {"role": "user", "content": "北京今天天气怎么样?"}]
Prompt中未提及工具,LLM不知道有工具可用。
根因:工具未绑定到ChatModel。修复方法:
// 错误:直接用 chatModel
runner := chain.AppendChatModel(chatModel)
// 正确:绑定工具
chatModelWithTools, _ := chatModel.WithTools([]*schema.ToolInfo{weatherToolInfo})
runner := chain.AppendChatModel(chatModelWithTools)
三种调试姿势:
Agent调试的思路与传统程序一致:缩小范围,找到第一个行为偏离预期的节点。WithDebug是快速缩小范围的有效工具。
下一篇E36将拆解compose.WithDebug的源码,分析一行代码背后如何在每个节点插入记录逻辑。
代码参考:eino-examples/quickstart · Eino devops/visualize
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述