109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"go-tiny-claw/internal/provider"
|
|
"go-tiny-claw/internal/schema"
|
|
"go-tiny-claw/internal/tools"
|
|
)
|
|
|
|
// AgentEngine 是微型 OS 的核心驱动
|
|
type AgentEngine struct {
|
|
provider provider.LLMProvider
|
|
registry tools.Registry
|
|
|
|
// WorkDir (工作区): 借鉴 OpenClaw 的理念,Agent 必须有一个明确的物理边界
|
|
WorkDir string
|
|
}
|
|
|
|
func NewAgentEngine(p provider.LLMProvider, r tools.Registry, workDir string) *AgentEngine {
|
|
return &AgentEngine{
|
|
provider: p,
|
|
registry: r,
|
|
WorkDir: workDir,
|
|
}
|
|
}
|
|
|
|
// Run 启动 Agent 的生命周期
|
|
func (e *AgentEngine) Run(ctx context.Context, userPrompt string) error {
|
|
log.Printf("[Engine] 引擎启动,锁定工作区: %s\n", e.WorkDir)
|
|
|
|
// 1. 初始化会话的 Context (上下文内存)
|
|
// 在真实的场景中,这里会由动态 Prompt 组装器加载 AGENTS.md。目前我们先硬编码。
|
|
contextHistory := []schema.Message{
|
|
{
|
|
Role: schema.RoleSystem,
|
|
Content: "You are go-tiny-claw, an expert coding assistant. You have full access to tools in the workspace.",
|
|
},
|
|
{
|
|
Role: schema.RoleUser,
|
|
Content: userPrompt,
|
|
},
|
|
}
|
|
|
|
turnCount := 0
|
|
|
|
// 2. The Main Loop: 心跳开始 (标准的 ReAct 循环)
|
|
for {
|
|
turnCount++
|
|
log.Printf("========== [Turn %d] 开始 ==========\n", turnCount)
|
|
|
|
// 获取当前挂载的所有工具定义
|
|
availableTools := e.registry.GetAvailableTools()
|
|
|
|
// 向大模型发起推理请求 (包含 Reasoning)
|
|
log.Println("[Engine] 正在思考 (Reasoning)...")
|
|
responseMsg, err := e.provider.Generate(ctx, contextHistory, availableTools)
|
|
if err != nil {
|
|
return fmt.Errorf("模型生成失败: %w", err)
|
|
}
|
|
|
|
// 将模型的响应完整追加到上下文历史中
|
|
contextHistory = append(contextHistory, *responseMsg)
|
|
|
|
// 如果模型回复了纯文本,打印出来 (这通常是它的思考过程,或是最终结果)
|
|
if responseMsg.Content != "" {
|
|
fmt.Printf("🤖 模型: %s\n", responseMsg.Content)
|
|
}
|
|
|
|
// 3. 退出条件判断
|
|
// 如果模型没有请求任何工具调用,说明它认为任务已经完成,跳出循环。
|
|
if len(responseMsg.ToolCalls) == 0 {
|
|
log.Println("[Engine] 任务完成,退出循环。")
|
|
break
|
|
}
|
|
|
|
// 4. 执行行动 (Action) 与 获取观察结果 (Observation)
|
|
log.Printf("[Engine] 模型请求调用 %d 个工具...\n", len(responseMsg.ToolCalls))
|
|
|
|
for _, toolCall := range responseMsg.ToolCalls {
|
|
log.Printf(" -> 🛠️ 执行工具: %s, 参数: %s\n", toolCall.Name, string(toolCall.Arguments))
|
|
|
|
// 通过 Registry 路由并执行底层工具
|
|
result := e.registry.Execute(ctx, toolCall)
|
|
|
|
if result.IsError {
|
|
log.Printf(" -> ❌ 工具执行报错: %s\n", result.Output)
|
|
} else {
|
|
log.Printf(" -> ✅ 工具执行成功 (返回 %d 字节)\n", len(result.Output))
|
|
}
|
|
|
|
// 将工具执行的观察结果 (Observation) 封装为 User Message 追加到上下文中
|
|
// 注意:ToolCallID 必须携带!这是维系大模型推理链条的关键
|
|
observationMsg := schema.Message{
|
|
Role: schema.RoleUser,
|
|
Content: result.Output,
|
|
ToolCallID: toolCall.ID,
|
|
}
|
|
contextHistory = append(contextHistory, observationMsg)
|
|
}
|
|
|
|
// 循环回到开头,模型将带着新加入的 Observation 继续它的下一轮思考...
|
|
}
|
|
|
|
return nil
|
|
}
|