# picoclaw 技术研究报告 ## 概述 本报告基于对 picoclaw v0.2.6 源代码的研究,详细分析其核心机制。 --- ## 1. 系统架构 ### 1.1 核心模块 ``` picoclaw/ ├── pkg/ │ ├── agent/ # AI 代理核心逻辑 │ ├── providers # LLM 提供者 │ ├── config/ # 配置管理 │ ├── tools/ # 工具注册与执行 │ ├── skills/ # Skill 加载器 │ ├── bus/ # 消息总线 │ └── channels/ # 消息通道 └── web/ # Web 界面 ``` ### 1.2 依赖关系 - **agent**:核心模块,依赖 providers、tools、skills、bus - **providers**:LLM API 调用(OpenAI、Anthropic 等) - **tools**:工具执行(exec、read_file、web_fetch 等) - **skills**:通过 SKILL.md 加载技能定义 --- ## 2. Skill 机制 ### 2.1 加载机制 **位置**:`pkg/skills/loader.go` Skill 存储在三个位置,优先级: 1. `workspace/skills/` - 项目级 2. `~/.picoclaw/skills/` - 全局 3. 内置 skills 目录 每个 skill 必须是 `SKILL.md` 文件,包含: - Frontmatter (YAML/JSON) 定义 `name` 和 `description` - Markdown 主体内容(操作指导) ### 2.2 执行机制 **关键发现**:Skill 本质是 Markdown 文档,不是可执行代码 AI 阅读 SKILL.md 后,调用实际工具执行任务: ``` 加载 SKILL.md → AI 阅读 → 调用工具 → 执行结果 ``` --- ## 3. 工具执行机制 ### 3.1 执行流程 **位置**:`pkg/agent/loop.go:runTurn` (约 2332-2899 行) ```go // 串行执行每个工具调用 for i, tc := range normalizedToolCalls { toolName := tc.Name toolArgs := cloneStringAnyMap(tc.Arguments) // 执行工具 toolResult := ts.agent.Tools.ExecuteWithContext( execCtx, toolName, toolArgs, ts.channel, ts.chatID, asyncCallback, ) // 结果添加到消息历史 toolResultMsg := providers.Message{ Role: "tool", Content: contentForLLM, ToolCallID: toolCallID, } messages = append(messages, toolResultMsg) } ``` ### 3.2 关键特性 | 特性 | 说明 | |------|------| | 执行方式 | **串行执行**,非并行 | | 结果收集 | 通过 `providers.Message` 添加到上下文 | | 用户显示 | `ForUser` 字段非空时发送给用户 | | 静默结果 | `SilentResult()` 不显示给用户,但发给 LLM | ### 3.3 工具结果标志 ```go // Silent: 不发送消息给用户,但发送给 LLM Silent bool `json:"silent"` // ResponseHandled: 工具已处理响应,不生成独立消息 ResponseHandled bool `json:"response_handled,omitempty"` // IsError: 标记为错误结果 IsError bool `json:"is_error,omitempty"` ``` --- ## 4. 消息处理 ### 4.1 消息总线 **位置**:`pkg/bus/bus.go` ``` 用户输入 → AgentLoop.ProcessDirect() → AI 处理 → 工具执行 ← ↓ bus.PublishOutbound() → 输出 ``` ### 4.2 消息类型 | 类型 | 说明 | |------|------| | InboundMessage | 用户输入 | | OutboundMessage | AI 输出 | | ToolResult | 工具结果 | --- ## 5. 异步执行 ### 5.1 AsyncExecutor 接口 **位置**:`pkg/tools/base.go:107` ```go type AsyncExecutor interface { Tool ExecuteAsync(ctx context.Context, args map[string]any, cb AsyncCallback) *ToolResult } ``` ### 5.2 使用方式 ```go // 执行时传入回调函数 toolResult := ts.agent.Tools.ExecuteWithContext( execCtx, toolName, toolArgs, ts.channel, ts.chatID, asyncCallback, // 异步回调 ) ``` --- ## 6. TTS 工具分析 ### 6.1 文件位置 - 主文件:`pkg/tools/tts_send.go` - TTS 提供者:`pkg/audio/tts/tts.go` ### 6.2 实现方式 1. 通过 `SendTTSTool` 执行 TTS 合成 2. 调用 `tts.SynthesizeAndStore()` 生成音频 3. 返回文件路径或直接播放 ### 6.3 已知问题 | 问题 | 原因 | |------|------| | 播放内容不一致 | 异步回调竞争 | | 结果不显示 | 使用了 `SilentResult()` | | 多条记录只显示一条 | 串行执行中的状态竞争 | --- ## 7. 配置系统 ### 7.1 配置文件 - 项目级:`config.yaml` - 用户级:`~/.picoclaw/config.json` - 环境变量:`PICOCLAW_*` 前缀 ### 7.2 配置结构 ```go type Config struct { Agents AgentsConfig `json:"agents"` Tools ToolsConfig `json:"tools"` Channels ChannelsConfig `json:"channels"` Voice VoiceConfig `json:"voice"` } ``` --- ## 8. hxclaw 集成方式 ### 8.1 复用策略 hxclaw 通过 Go replace 机制复用 picoclaw: ```go // go.mod replace github.com/sipeed/picoclaw => ./path/to/picoclaw ``` ### 8.2 核心依赖 - `pkg/agent` - AI 代理核心 - `pkg/providers` - LLM 提供者 - `pkg/config` - 配置加载 - `pkg/bus` - 消息总线 --- ## 9. 总结 | 模块 | 机制 | 阻塞 | |------|------|------| | Skill | Markdown 文档 | 否 | | 工具 | 串行执行 | 是 | | 异步工具 | 回调机制 | 可选 | | 消息总线 | 非阻塞 | 否 | --- ## 10. 建议 1. **避免阻塞**:长时间任务使用 AsyncExecutor 2. **结果显示**:检查 Silent/ResponseHandled 标志 3. **并发控制**:使用 SubTurn 的 concurrencySem