package agent import ( "context" "encoding/json" "fmt" "time" "github.com/cloudwego/eino/components/model" "github.com/cloudwego/eino/schema" "github.com/gogf/gf/v2/frame/g" ) // ReActAgent 实现 ReAct 模式的智能体 type ReActAgent struct { model model.ChatModel tools []*ToolInfo systemPrompt string maxStep int } // NewReActAgent 创建 ReAct 智能体 func NewReActAgent(ctx context.Context, chatModel model.ChatModel, tools []*ToolInfo, systemPrompt string, maxStep int) *ReActAgent { return &ReActAgent{ model: chatModel, tools: tools, systemPrompt: systemPrompt, maxStep: maxStep, } } // Run 执行 ReAct 循环 // 标准流程: 思考 → 行动(调用工具) → 观察(工具结果) → 重复 → 最终回答 func (a *ReActAgent) Run(ctx context.Context, userInput string) (string, error) { messages := []*schema.Message{ schema.SystemMessage(a.systemPrompt), schema.UserMessage(userInput), } // 构建 toolInfos 传给模型 toolInfos := make([]*schema.ToolInfo, 0, len(a.tools)) for _, t := range a.tools { toolInfos = append(toolInfos, t.ToEinoToolInfo()) } g.Log().Infof(ctx, "ReAct 开始执行,maxStep=%d, tools=%d", a.maxStep, len(a.tools)) for step := 0; step < a.maxStep; step++ { g.Log().Infof(ctx, "ReAct step %d/%d: 调用模型...", step+1, a.maxStep) // 1. LLM 思考并决定行动 startTime := time.Now() result, err := a.model.Generate(ctx, messages, model.WithTools(toolInfos)) elapsed := time.Since(startTime) if err != nil { return "", fmt.Errorf("step %d: 模型调用失败: %w", step, err) } g.Log().Infof(ctx, "ReAct step %d/%d: 模型返回 (耗时 %v), content_len=%d, ToolCalls=%d", step+1, a.maxStep, elapsed, len(result.Content), len(result.ToolCalls)) messages = append(messages, result) // 2. 检查是否有工具调用 if len(result.ToolCalls) == 0 { // 没有工具调用 → 最终回答 g.Log().Infof(ctx, "ReAct step %d/%d: 无工具调用,返回最终结果 (content长度=%d)", step+1, a.maxStep, len(result.Content)) return result.Content, nil } // 3. 执行每个工具调用 for _, tc := range result.ToolCalls { tool := a.findTool(tc.Function.Name) if tool == nil { g.Log().Warningf(ctx, "ReAct step %d: 未知工具: %s", step+1, tc.Function.Name) toolResultMsg := &schema.Message{ Role: schema.Tool, Content: fmt.Sprintf("未知工具: %s", tc.Function.Name), ToolName: tc.Function.Name, ToolCallID: tc.ID, } messages = append(messages, toolResultMsg) continue } // 解析参数 var args map[string]any if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil { g.Log().Warningf(ctx, "ReAct step %d: 参数解析失败: %v", step+1, err) toolResultMsg := &schema.Message{ Role: schema.Tool, Content: fmt.Sprintf("参数解析失败: %v", err), ToolName: tc.Function.Name, ToolCallID: tc.ID, } messages = append(messages, toolResultMsg) continue } g.Log().Infof(ctx, "ReAct step %d: 调用工具 %s, 参数: %s", step+1, tc.Function.Name, tc.Function.Arguments) // 执行工具 toolStart := time.Now() output, err := tool.Func(ctx, args) toolElapsed := time.Since(toolStart) if err != nil { output = fmt.Sprintf("工具执行失败: %v", err) g.Log().Warningf(ctx, "ReAct step %d: 工具 %s 执行失败 (耗时 %v): %v", step+1, tc.Function.Name, toolElapsed, err) } else { truncated := output if len(truncated) > 200 { truncated = truncated[:200] + "..." } g.Log().Infof(ctx, "ReAct step %d: 工具 %s 执行成功 (耗时 %v), 结果长度=%d, 预览: %s", step+1, tc.Function.Name, toolElapsed, len(output), truncated) } // 4. 观察工具结果 toolResultMsg := &schema.Message{ Role: schema.Tool, Content: output, Name: tc.Function.Name, ToolCallID: tc.ID, } messages = append(messages, toolResultMsg) } } g.Log().Errorf(ctx, "ReAct 达到最大步骤数 %d,生成未完成", a.maxStep) return "", fmt.Errorf("达到最大步骤数 %d,生成未完成", a.maxStep) } func (a *ReActAgent) findTool(name string) *ToolInfo { for _, t := range a.tools { if t.Name == name { return t } } return nil }