回调函数调试Agent
2026年4月15日 · 576 字 · 3 分钟
如何使用回调函数调试Agent,无侵入式地监控和调试AI Agent的运行过程 回调函数是Eino框架提供的一种切面能力,允许开发者在不修改原有代码的情况下,无侵入式地注入日志、追踪、指标等功能。这在调试AI Agent时特别有用,可以帮助我们: 在 这是最基础的回调实现,记录所有组件的所有事件: 使用方式: 这个回调会打印所有组件(ChatModel、Tool等)的输入输出,适合初步调试时使用。 如果你只想关注开始和结束事件,可以使用Builder模式构建: 使用方式: 使用Builder模式的好处是:你可以自由组合需要关注的事件类型(OnStart、OnEnd、OnError等)。 如果你只想监控ChatModel组件,可以使用 输出示例: 使用方式: 类似地,你可以只监控工具调用: 输出示例: 使用方式: 下面我们来看如何在 通过观察回调输出,你可以清楚地了解Agent的整个思考过程。什么是回调函数(Callback)
回调函数的实现方式
callback_tool/callback.go中,我们实现了多种回调函数,满足不同的调试需求。1. 全量日志回调 - LoggerCallbacks
type LoggerCallbacks struct{}
func (l *LoggerCallbacks) OnStart(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
log.Printf("[INPUT] name: %v, type: %v, component: %v, input: %v", info.Name, info.Type, info.Component, input)
return ctx
}
func (l *LoggerCallbacks) OnEnd(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
log.Printf("[OUTPUT] name: %v, type: %v, component: %v, output: %v", info.Name, info.Type, info.Component, output)
return ctx
}
func (l *LoggerCallbacks) OnError(ctx context.Context, info *callbacks.RunInfo, err error) context.Context {
log.Printf("[ERROR] name: %v, type: %v, component: %v, error: %v", info.Name, info.Type, info.Component, err)
return ctx
}
func (l *LoggerCallbacks) OnStartWithStreamInput(ctx context.Context, info *callbacks.RunInfo, input *schema.StreamReader[callbacks.CallbackInput]) context.Context {
return ctx
}
func (l *LoggerCallbacks) OnEndWithStreamOutput(ctx context.Context, info *callbacks.RunInfo, output *schema.StreamReader[callbacks.CallbackOutput]) context.Context {
return ctx
}
callbacks.AppendGlobalHandlers(new(LoggerCallbacks))
2. 选择性回调 - GetStartAndEndCallback
func GetStartAndEndCallback() callbacks.Handler {
return callbacks.NewHandlerBuilder().
OnStartFn(func(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
log.Printf("[INPUT] name: %v, type: %v, component: %v, input: %v", info.Name, info.Type, info.Component, input)
return ctx
}).
OnEndFn(func(ctx context.Context, info *callbacks.RunInfo, output callbacks.CallbackOutput) context.Context {
log.Printf("[OUTPUT] name: %v, type: %v, component: %v, output: %v", info.Name, info.Type, info.Component, output)
return ctx
}).
Build()
}
callbacks.AppendGlobalHandlers(GetStartAndEndCallback())
3. 特定组件回调 - ChatModel专用回调
NewHandlerHelper来过滤:func GetChatModelInputCallback() callbacks.Handler {
return cbutils.NewHandlerHelper().ChatModel(&cbutils.ModelCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *model.CallbackInput) context.Context {
fmt.Printf("\n[ChatModel Input] component: %s\n", info.Name)
for i, msg := range input.Messages {
fmt.Printf(" Message %d [%s]: %s\n", i+1, msg.Role, msg.Content)
if len(msg.ToolCalls) > 0 {
fmt.Printf(" Tool Calls: %d\n", len(msg.ToolCalls))
for j, tc := range msg.ToolCalls {
fmt.Printf(" %d. %s: %s\n", j+1, tc.Function.Name, tc.Function.Arguments)
}
}
}
return ctx
},
}).Handler()
}
[ChatModel Input] component: trip_plan
Message 1 [system]: 请帮我完成旅行规划,包括什么时间去什么景点,并列出详细的高铁车次和出发时间
Message 2 [user]: 我想去上海旅游3天(从明天开始),请帮我做一份旅游攻略
Message 3 [assistant]: 我来帮您规划上海3天的旅游行程...
Message 4 [tool]: ...
callbacks.AppendGlobalHandlers(callbacktool.GetChatModelInputCallback())
4. Tool专用回调
func GetToolInputCallback() callbacks.Handler {
return cbutils.NewHandlerHelper().Tool(&cbutils.ToolCallbackHandler{
OnStart: func(ctx context.Context, info *callbacks.RunInfo, input *tool.CallbackInput) context.Context {
fmt.Printf("\n[Tool Input] component: %s, args: %s\n", info.Name, input.ArgumentsInJSON)
return ctx
},
}).Handler()
}
[Tool Input] component: search_tool, args: {"query":"上海旅游景点推荐"}
[Tool Input] component: crawl_tool, args: {"url":"https://..."}
callbacks.AppendGlobalHandlers(callbacktool.GetToolInputCallback())
实战案例:旅行规划Agent调试
trip_agent.go中使用这些回调函数进行调试。代码结构
package agent
import (
"context"
callbacktool "eino_study/callback_tool"
"eino_study/config"
"eino_study/model"
"eino_study/tools"
"fmt"
"log"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/callbacks"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
)
func TripAgent() {
ctx := context.Background()
// ========== 注入回调函数 ==========
// 根据调试需求,选择合适的回调方式:
// 方式1:开启所有组件的所有回调(输出最详细)
// callbacks.AppendGlobalHandlers(new(callbacktool.LoggerCallbacks))
// 方式2:只关注开始和结束事件
// callbacks.AppendGlobalHandlers(callbacktool.GetStartAndEndCallback())
// 方式3:只关注ChatModel的输入(推荐)
callbacks.AppendGlobalHandlers(callbacktool.GetChatModelInputCallback())
// 方式4:只关注Tool的调用
callbacks.AppendGlobalHandlers(callbacktool.GetToolInputCallback())
// 可以组合使用多个回调
// callbacks.AppendGlobalHandlers(callbacktool.GetChatModelInputCallback())
// callbacks.AppendGlobalHandlers(callbacktool.GetToolInputCallback())
// ========== 创建Agent ==========
model := model.NewChatModel(config.NewConfig("../config/config.json"))
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Model: model.OpenaiChatModel,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
tools.SearchTool(),
tools.CrawlTool(),
},
},
},
Name: "trip_plan",
Description: "出行规划",
Instruction: "请帮我完成旅行规划,包括什么时间去什么景点,并列出详细的高铁车次和出发时间",
})
if err != nil {
log.Fatal(err)
}
// ========== 运行Agent ==========
runner := adk.NewRunner(ctx, adk.RunnerConfig{
Agent: agent,
})
iter := runner.Query(ctx, "我想去上海旅游3天(从明天开始),请帮我做一份旅游攻略")
var lastMsg adk.Message
for {
event, ok := iter.Next()
if !ok {
break
}
if event.Err != nil {
log.Fatal(event.Err)
}
msg, err := event.Output.MessageOutput.GetMessage()
if err != nil {
log.Fatal(err)
}
// 调试时可以打印每一步的结果
// fmt.Println(msg.Role, msg.Content)
lastMsg = msg
}
fmt.Println("【最终答案】")
if lastMsg.Role == schema.Assistant && len(lastMsg.Content) > 0 {
fmt.Println(lastMsg.Content)
}
}
调试输出分析
第一步:ChatModel接收初始消息
[ChatModel Input] component: trip_plan
Message 1 [system]: 请帮我完成旅行规划,包括什么时间去什么景点...
Message 2 [user]: 我想去上海旅游3天(从明天开始),请帮我做一份旅游攻略
第二步:Agent决定调用工具
[ChatModel Input] component: trip_plan
Message 1 [system]: 请帮我完成旅行规划...
Message 2 [user]: 我想去上海旅游3天...
Message 3 [assistant]: 我需要先搜索上海的相关信息
Tool Calls: 1
1. search_tool: {"query":"上海旅游景点推荐 三日游攻略"}
第三步:工具执行
[Tool Input] component: search_tool, args: {"query":"上海旅游景点推荐 三日游攻略"}
第四步:ChatModel继续处理
[ChatModel Input] component: trip_plan
Message 1 [system]: 请帮我完成旅行规划...
Message 2 [user]: 我想去上海旅游3天...
Message 3 [assistant]: [包含tool call]
Message 4 [tool]: {"result": "搜索结果..."}
第五步:最终回答