238 lines
5.1 KiB
Markdown
238 lines
5.1 KiB
Markdown
|
|
# 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
|