开发一个简单的帮助请假的agent,用户通过输入请假信息,agent自动完成请假信息补充,发起请假流程
1、安装依赖
shell
go get -u github.com/cloudwego/eino@latest
go get -u github.com/cloudwego/eino-ext/components/model/openai@late
2、新建一个循环agent,用于判断用户输入的请假信息是否完整,如果不完整,询问用户输入相关的信息,核心在于创建一个工具,通过工具中断agent,获取问题用户的回答
go
inferTool, err := utils.InferTool(
"askQuestions",
"询问用户补充信息",
func(ctx context.Context, input InferToolInput) (string, error) {
interrupted, _, storedState := compose.GetInterruptState[string](ctx)
if !interrupted {
return "", compose.StatefulInterrupt(ctx, input, input)
}
flow, data, t := compose.GetResumeContext[string](ctx)
if !flow {
return "", compose.StatefulInterrupt(ctx, input, storedState)
}
fmt.Println("###", t)
if !data || t == "" {
return "", fmt.Errorf("tool resumed without a user answer")
}
return t, nil
})
if err != nil {
panic(err)
}
go
analysisAgent, _ := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
Name: "leaveAnalysisAgent",
Model: chatModel,
Description: "分析缺少请假信息",
Instruction: `你是一个请假流程信息分析专家,根据提供的请假信息,分析以下请假关键信息
1、请假的开始和结束时间
2、请假的类型
3、请假的原因
如果请假的信息已经完整,请输入"EXIT:请假信息已完整" 来结束请假信息补充流程
`,
})
askAgent, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
Name: "completeLeaveInfo",
Model: chatModel,
Description: "根据分析结果询问用户补充请假信息",
Instruction: `根据前面的信息补充问题,使用askQuestions工具,补充请假信息`,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
inferTool,
},
},
},
})
exitAgent, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
Name: "exitController",
Model: chatModel,
Description: "控制优化循环的退出",
Instruction: `检查前面的分析结果,如果请假流程信息分析专家认为请假信息已经完整(包含"EXIT"关键词),
则输出 "TERMINATE" 并生成退出动作来结束循环。否则继续下一轮优化。`,
})
agent, err := adk.NewLoopAgent(context.Background(), &adk.LoopAgentConfig{
Name: "leaveLoop",
Description: "请假信息补充循环:分析缺少请假信息->询问用户补充请假信息->检查退出条件",
SubAgents: []adk.Agent{
analysisAgent,
askAgent,
exitAgent,
},
MaxIterations: 5,
})
if err != nil {
panic(err)
}
leaveApproveTool, err := utils.InferTool[LeaveInfo, string]("leaveApproveTool", "发起请假流程", func(ctx context.Context, input LeaveInfo) (output string, err error) {
log.Println("发起请假入参:", input)
return "success", err
})
modelAgent, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
Name: "leaveApproveAgent",
Model: chatModel,
Description: "发起请假流程",
Instruction: "根据前面的请假信息,使用leaveApproveTool发起请假流程",
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
leaveApproveTool,
},
},
},
})
3、创建一个请假流程发起agent,请假信息补充完成之后,发起请假流程
go
leaveApproveTool, err := utils.InferTool[LeaveInfo, string]("leaveApproveTool", "发起请假流程", func(ctx context.Context, input LeaveInfo) (output string, err error) {
log.Println("发起请假入参:", input)
return "success", err
})
modelAgent, err := adk.NewChatModelAgent(context.Background(), &adk.ChatModelAgentConfig{
Name: "leaveApproveAgent",
Model: chatModel,
Description: "发起请假流程",
Instruction: "根据前面的请假信息,使用leaveApproveTool发起请假流程",
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
leaveApproveTool,
},
},
},
})
4、创建顺序流将整个流程串联起来,运行同时输入我们的信息帮我从今天起请两天假,查看打印agent的思考过程以及结果
go
sequentialAgent, err := adk.NewSequentialAgent(context.Background(), &adk.SequentialAgentConfig{
Name: "leaveSequentialAgent",
Description: "请假工作流",
SubAgents: []adk.Agent{
agent,
modelAgent,
},
})
runner := adk.NewRunner(context.Background(), adk.RunnerConfig{
Agent: sequentialAgent,
CheckPointStore: store.NewInMemoryStore(),
EnableStreaming: true,
})
iter := runner.Query(context.Background(), "帮我从今天起请两天假", adk.WithCheckPointID("deep-analysis-1"))
for {
lastEvent, interrupted := processEvents(iter)
if !interrupted {
break
}
interruptCtx := lastEvent.Action.Interrupted.InterruptContexts[0]
interruptID := interruptCtx.ID
fmt.Println(interruptCtx.Info.(InferToolInput).Question)
fmt.Println("输入你的答案")
var answer string
n, err := fmt.Scanln(&answer)
if n != 1 || err != nil {
panic(err)
}
iter, err = runner.ResumeWithParams(context.Background(), "deep-analysis-1", &adk.ResumeParams{
Targets: map[string]any{
interruptID: answer,
},
})
if err != nil {
panic(err)
}
}
}
func processEvents(iter *adk.AsyncIterator[*adk.AgentEvent]) (*adk.AgentEvent, bool) {
var lastEvent *adk.AgentEvent
for {
event, ok := iter.Next()
if !ok {
break
}
if event.Err != nil {
log.Fatal(event.Err)
}
PrintContent(event)
lastEvent = event
}
if lastEvent == nil {
return nil, false
}
if lastEvent.Action != nil && lastEvent.Action.Interrupted != nil {
return lastEvent, true
}
return lastEvent, false
}
func PrintContent(event *adk.AgentEvent) {
if event.Output != nil && event.Output.MessageOutput != nil {
output := event.Output.MessageOutput
if output.IsStreaming {
stream := output.MessageStream
if stream != nil {
for {
chunk, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
fmt.Printf("error: %v", err)
return
}
if chunk.ReasoningContent != "" {
fmt.Print(chunk.ReasoningContent)
}
if chunk.Content != "" {
fmt.Print(chunk.Content)
}
}
}
} else {
message := output.Message
if message != nil {
if message.Content != "" {
log.Print(message)
}
if message.ReasoningContent != "" {
fmt.Print(message.ReasoningContent)
}
}
}
}
if event.Action != nil {
if event.Action.TransferToAgent != nil {
fmt.Printf("\naction: transfer to %v", event.Action.TransferToAgent.DestAgentName)
}
if event.Action.Interrupted != nil {
for _, ic := range event.Action.Interrupted.InterruptContexts {
str, ok := ic.Info.(fmt.Stringer)
if ok {
fmt.Printf("\n%s", str.String())
} else {
fmt.Printf("\n%v", ic.Info)
}
}
}
if event.Action.Exit {
fmt.Printf("\naction: exit")
}
}
if event.Err != nil {
fmt.Printf("\nerror: %v", event.Err)
}
}
type InferToolInput struct {
Question string `json:"question" jsonschema:"required;description=你需要询问的问题"`
}
type LeaveInfo struct {
StartDate string `json:"startDate" jsonschema:"required;description=请假开始时间"`
EndDate string `json:"endDate" jsonschema:"required;description=请假结束时间"`
LeaveType string `json:"leaveType" jsonschema:"required;description=请假类型"`
LeaveReason string `json:"leaveReason" jsonschema:"required;description=请假原因"`
}
5、查看结果


总结:整体体验不错,agent的中断和恢复,可以灵活的完成各种场景