项目结构分析
This commit is contained in:
parent
30155c1c59
commit
84a52428b5
1 changed files with 901 additions and 0 deletions
901
docs/architecture-flow.md
Normal file
901
docs/architecture-flow.md
Normal file
|
|
@ -0,0 +1,901 @@
|
|||
# PicoClaw 代码执行流程分析
|
||||
|
||||
## 项目概述
|
||||
|
||||
PicoClaw 是一个用 Go 编写的超轻量级个人 AI 助手,由 Sipeed 发起。核心内存占用 <10MB,可在 $10 硬件上运行。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
picoclaw/
|
||||
├── cmd/
|
||||
│ ├── picoclaw/ # 主 CLI 入口
|
||||
│ │ ├── main.go # 程序入口,Cobra 命令注册
|
||||
│ │ └── internal/ # CLI 子命令
|
||||
│ │ ├── agent/ # picoclaw agent(交互式/单次对话)
|
||||
│ │ ├── gateway/ # picoclaw gateway(长期运行网关)
|
||||
│ │ ├── onboard/ # picoclaw onboard(初始化配置)
|
||||
│ │ ├── cron/ # picoclaw cron(定时任务管理)
|
||||
│ │ ├── skills/ # picoclaw skills(技能管理)
|
||||
│ │ ├── auth/ # picoclaw auth(Provider 认证)
|
||||
│ │ ├── migrate/ # picoclaw migrate(配置迁移)
|
||||
│ │ ├── model/ # picoclaw model(模型管理)
|
||||
│ │ ├── status/ # picoclaw status(状态查看)
|
||||
│ │ └── version/ # picoclaw version
|
||||
│ └── picoclaw-launcher-tui/ # TUI 启动器(终端 UI + WebUI)
|
||||
├── pkg/ # 核心库
|
||||
│ ├── agent/ # Agent 运行时(核心中的核心)
|
||||
│ │ ├── loop.go # AgentLoop 主循环
|
||||
│ │ ├── turn.go # 单轮对话状态管理
|
||||
│ │ ├── instance.go # AgentInstance 实例创建
|
||||
│ │ ├── context.go # 上下文构建(系统提示词)
|
||||
│ │ ├── hooks.go # Hook 系统
|
||||
│ │ ├── steering.go # 消息注入/转向
|
||||
│ │ ├── subturn.go # 子 Agent 并发执行
|
||||
│ │ ├── eventbus.go # 事件总线
|
||||
│ │ ├── registry.go # Agent 注册表
|
||||
│ │ ├── definition.go # AGENT.md/SOUL.md 解析
|
||||
│ │ ├── memory.go # 记忆存储
|
||||
│ │ └── model_resolution.go # 模型解析
|
||||
│ ├── bus/ # 消息总线
|
||||
│ │ ├── types.go # InboundMessage/OutboundMessage
|
||||
│ │ └── bus.go # 消息传递
|
||||
│ ├── channels/ # 聊天平台集成(17+)
|
||||
│ │ ├── manager.go # ChannelManager 统一管理
|
||||
│ │ ├── telegram/
|
||||
│ │ ├── discord/
|
||||
│ │ ├── weixin/ # 微信
|
||||
│ │ ├── wecom/ # 企业微信
|
||||
│ │ ├── qq/
|
||||
│ │ ├── feishu/ # 飞书
|
||||
│ │ ├── slack/
|
||||
│ │ ├── whatsapp/
|
||||
│ │ └── ... # 更多平台
|
||||
│ ├── providers/ # LLM Provider 适配器(30+)
|
||||
│ │ ├── types.go # LLMProvider 接口定义
|
||||
│ │ ├── fallback.go # 降级链
|
||||
│ │ ├── cooldown.go # 冷却追踪
|
||||
│ │ ├── openai/
|
||||
│ │ ├── anthropic/
|
||||
│ │ ├── gemini/
|
||||
│ │ ├── deepseek/
|
||||
│ │ └── ... # 更多 Provider
|
||||
│ ├── gateway/ # HTTP 网关服务
|
||||
│ │ └── gateway.go # Gateway 启动与服务编排
|
||||
│ ├── config/ # 配置管理
|
||||
│ │ └── config.go # Config 结构与加载
|
||||
│ ├── tools/ # 工具注册与执行
|
||||
│ ├── session/ # 会话持久化(JSONL)
|
||||
│ ├── memory/ # 对话记忆
|
||||
│ ├── skills/ # 技能系统
|
||||
│ ├── routing/ # 模型路由
|
||||
│ ├── cron/ # 定时任务服务
|
||||
│ ├── heartbeat/ # 心跳服务
|
||||
│ ├── media/ # 媒体文件存储
|
||||
│ ├── voice/ # 语音转录
|
||||
│ ├── health/ # 健康检查 HTTP 服务
|
||||
│ └── commands/ # 斜杠命令系统
|
||||
├── docker/ # Docker 部署
|
||||
│ ├── docker-compose.yml # 三种 profile 部署
|
||||
│ ├── entrypoint.sh # 容器入口脚本
|
||||
│ └── Dockerfile # 多种构建方式
|
||||
├── workspace/ # 默认工作空间
|
||||
│ ├── AGENT.md # Agent 身份与工具定义
|
||||
│ ├── SOUL.md # Agent 人格设定
|
||||
│ └── USER.md # 用户偏好
|
||||
└── docs/ # 文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 一、程序启动流程
|
||||
|
||||
### 1.1 CLI 入口 (`cmd/picoclaw/main.go`)
|
||||
|
||||
```
|
||||
main()
|
||||
│
|
||||
├── 打印 ASCII Banner
|
||||
├── 创建 Cobra Root Command
|
||||
├── 注册子命令:
|
||||
│ ├── onboard → 初始化 ~/.picoclaw/ 目录与配置
|
||||
│ ├── agent → 单次/交互式对话
|
||||
│ ├── gateway → 启动长期运行网关
|
||||
│ ├── cron → 管理定时任务
|
||||
│ ├── skills → 安装/搜索技能
|
||||
│ ├── auth → Provider OAuth 认证
|
||||
│ ├── migrate → 配置版本迁移
|
||||
│ ├── model → 模型管理
|
||||
│ ├── status → 运行状态查看
|
||||
│ └── version → 版本信息
|
||||
└── cmd.Execute()
|
||||
```
|
||||
|
||||
### 1.2 Gateway 启动流程 (`pkg/gateway/gateway.go:Run()`)
|
||||
|
||||
这是最核心的运行模式,完整启动流程如下:
|
||||
|
||||
```
|
||||
gateway.Run(debug, homePath, configPath, allowEmptyStartup)
|
||||
│
|
||||
├── 1. 初始化日志系统
|
||||
│ ├── logger.InitPanic() → Panic 日志
|
||||
│ └── logger.EnableFileLogging() → 文件日志
|
||||
│
|
||||
├── 2. 加载配置
|
||||
│ └── config.LoadConfig(configPath) → ~/.picoclaw/config.json
|
||||
│
|
||||
├── 3. 创建 LLM Provider
|
||||
│ └── createStartupProvider(cfg) → OpenAI/Anthropic/Gemini 等
|
||||
│
|
||||
├── 4. 创建 MessageBus
|
||||
│ └── bus.NewMessageBus() → 消息总线(Inbound/Outbound 通道)
|
||||
│
|
||||
├── 5. 创建 AgentLoop
|
||||
│ └── agent.NewAgentLoop(cfg, msgBus, provider)
|
||||
│ ├── NewAgentRegistry() → 创建 Agent 注册表
|
||||
│ ├── NewCooldownTracker() → 冷却追踪器
|
||||
│ ├── NewFallbackChain() → 降级链
|
||||
│ ├── NewEventManager() → 事件总线
|
||||
│ ├── NewHookManager() → Hook 管理器
|
||||
│ ├── configureHookManager() → 从配置加载 Hooks
|
||||
│ └── registerSharedTools() → 注册共享工具(web/message/spawn 等)
|
||||
│
|
||||
├── 6. 启动后台服务
|
||||
│ └── setupAndStartServices()
|
||||
│ ├── CronService.Start() → 定时任务服务
|
||||
│ ├── HeartbeatService.Start() → 心跳监控
|
||||
│ ├── MediaStore.Start() → 媒体文件管理(含清理)
|
||||
│ ├── ChannelManager.Start() → 聊天平台管理器
|
||||
│ ├── DeviceService → 设备管理
|
||||
│ └── HealthServer.Start() → HTTP 健康检查
|
||||
│
|
||||
├── 7. 启动 AgentLoop(goroutine)
|
||||
│ └── go agentLoop.Run(ctx)
|
||||
│
|
||||
├── 8. 配置热重载(可选)
|
||||
│ └── setupConfigWatcherPolling() → 监听 config.json 变化
|
||||
│
|
||||
└── 9. 主循环等待信号
|
||||
├── <-sigChan (Ctrl+C) → 优雅关闭
|
||||
├── <-configReloadChan → 配置热重载
|
||||
└── <-manualReloadChan → /reload 端点触发
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、核心数据结构
|
||||
|
||||
### 2.1 AgentLoop(`pkg/agent/loop.go`)
|
||||
|
||||
AgentLoop 是整个系统的核心调度器:
|
||||
|
||||
```go
|
||||
type AgentLoop struct {
|
||||
bus *bus.MessageBus // 消息总线
|
||||
cfg *config.Config // 全局配置
|
||||
registry *AgentRegistry // Agent 注册表(多 Agent 支持)
|
||||
state *state.Manager // 状态管理
|
||||
eventBus *EventBus // 事件总线
|
||||
hooks *HookManager // Hook 管理器
|
||||
fallback *providers.FallbackChain // LLM 降级链
|
||||
channelManager *channels.Manager // 聊天平台管理器
|
||||
mediaStore media.MediaStore // 媒体存储
|
||||
transcriber voice.Transcriber // 语音转录
|
||||
cmdRegistry *commands.Registry // 斜杠命令注册表
|
||||
mcp mcpRuntime // MCP 协议运行时
|
||||
steering *steeringQueue // 消息转向队列
|
||||
activeTurnStates sync.Map // 活跃 Turn 状态表
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 AgentInstance(`pkg/agent/instance.go`)
|
||||
|
||||
每个 Agent 的完整配置实例:
|
||||
|
||||
```go
|
||||
type AgentInstance struct {
|
||||
ID string // Agent 标识
|
||||
Name string // Agent 名称
|
||||
Model string // 主模型
|
||||
Fallbacks []string // 降级模型列表
|
||||
Workspace string // 工作目录
|
||||
MaxIterations int // 最大工具调用迭代次数
|
||||
MaxTokens int // 最大输出 Token
|
||||
ContextWindow int // 上下文窗口大小
|
||||
Provider providers.LLMProvider // LLM Provider 实例
|
||||
Sessions session.SessionStore // 会话存储
|
||||
ContextBuilder *ContextBuilder // 上下文构建器
|
||||
Tools *tools.ToolRegistry // 工具注册表
|
||||
Router *routing.Router // 模型路由器
|
||||
LightProvider providers.LLMProvider // 轻量模型 Provider
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 TurnState(`pkg/agent/turn.go`)
|
||||
|
||||
单轮对话的完整状态:
|
||||
|
||||
```go
|
||||
type TurnPhase string
|
||||
const (
|
||||
TurnPhaseSetup = "setup" // 初始化阶段
|
||||
TurnPhaseRunning = "running" // LLM 调用中
|
||||
TurnPhaseTools = "tools" // 工具执行中
|
||||
TurnPhaseFinalizing = "finalizing" // 收尾阶段
|
||||
TurnPhaseCompleted = "completed" // 已完成
|
||||
TurnPhaseAborted = "aborted" // 已中止
|
||||
)
|
||||
|
||||
type turnState struct {
|
||||
turnID string
|
||||
agent *AgentInstance
|
||||
phase TurnPhase
|
||||
iteration int // 当前迭代次数
|
||||
userMessage string // 用户消息
|
||||
media []string // 媒体附件
|
||||
finalContent string // 最终回复
|
||||
// 中断控制
|
||||
gracefulInterrupt bool
|
||||
hardAbort bool
|
||||
providerCancel context.CancelFunc
|
||||
// SubTurn 支持
|
||||
depth int
|
||||
parentTurnState *turnState
|
||||
childTurnIDs []string
|
||||
pendingResults chan *ToolResult
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 消息结构(`pkg/bus/types.go`)
|
||||
|
||||
```go
|
||||
type InboundMessage struct {
|
||||
Channel string // 平台: "telegram", "discord", "cli" 等
|
||||
SenderID string // 发送者 ID
|
||||
Sender SenderInfo // 结构化发送者信息
|
||||
ChatID string // 会话 ID
|
||||
Content string // 消息文本
|
||||
Media []string // 媒体附件 (media:// refs)
|
||||
Peer Peer // 路由对等体 (direct/group/channel)
|
||||
SessionKey string // 会话标识
|
||||
Metadata map[string]string // 自定义元数据
|
||||
}
|
||||
|
||||
type OutboundMessage struct {
|
||||
Channel string // 平台
|
||||
ChatID string // 会话 ID
|
||||
Content string // 回复文本
|
||||
ReplyToMessageID string // 引用回复的消息 ID
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 LLM Provider 接口(`pkg/providers/types.go`)
|
||||
|
||||
```go
|
||||
type LLMProvider interface {
|
||||
Chat(ctx context.Context, messages []Message, tools []ToolDefinition,
|
||||
model string, options map[string]any) (*LLMResponse, error)
|
||||
GetDefaultModel() string
|
||||
}
|
||||
|
||||
// 可选接口
|
||||
type StreamingProvider interface {
|
||||
ChatStream(ctx context.Context, ..., onChunk func(accumulated string)) (*LLMResponse, error)
|
||||
}
|
||||
|
||||
type ThinkingCapable interface {
|
||||
SupportsThinking() bool
|
||||
}
|
||||
|
||||
type NativeSearchCapable interface {
|
||||
SupportsNativeSearch() bool
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、消息处理完整流程
|
||||
|
||||
### 3.1 AgentLoop 主循环
|
||||
|
||||
```
|
||||
AgentLoop.Run(ctx) [loop.go:380]
|
||||
│
|
||||
├── ensureHooksInitialized() → 初始化 Hook 系统
|
||||
├── ensureMCPInitialized() → 初始化 MCP 运行时
|
||||
│
|
||||
└── for { select {
|
||||
│
|
||||
├── <-ctx.Done() → 退出
|
||||
│
|
||||
└── msg := <-bus.InboundChan() → 收到入站消息
|
||||
│
|
||||
├── resolveSteeringTarget() → 检查是否有活跃 Turn 需要转向
|
||||
│ └── 若有 → drainBusToSteering() → 排水到转向队列
|
||||
│
|
||||
├── processMessage(msg) → 处理消息(核心)
|
||||
│
|
||||
├── buildContinuationTarget() → 构建延续目标
|
||||
│
|
||||
└── 处理转向队列中的排队消息
|
||||
└── Continue() → 继续对话
|
||||
}}
|
||||
```
|
||||
|
||||
### 3.2 processMessage 详细流程
|
||||
|
||||
```
|
||||
processMessage(ctx, msg) [loop.go:1255]
|
||||
│
|
||||
├── 1. 日志记录
|
||||
│
|
||||
├── 2. 语音转录(如有音频)
|
||||
│ └── transcribeAudioInMessage()
|
||||
│
|
||||
├── 3. 系统消息路由
|
||||
│ └── if channel == "system" → processSystemMessage()
|
||||
│
|
||||
├── 4. 消息路由解析
|
||||
│ └── resolveMessageRoute(msg) → 确定 Agent 和会话
|
||||
│ ├── registry.ResolveRoute() → 根据 channel/peer/guild 路由
|
||||
│ └── 返回 (route, agentInstance, error)
|
||||
│
|
||||
├── 5. 重置 message 工具状态
|
||||
│
|
||||
├── 6. 解析 SessionKey
|
||||
│ └── resolveScopeKey(route, sessionKey)
|
||||
│
|
||||
├── 7. 构建 processOptions
|
||||
│ ├── SessionKey
|
||||
│ ├── Channel / ChatID / SenderID
|
||||
│ ├── UserMessage
|
||||
│ ├── Media
|
||||
│ └── DefaultResponse
|
||||
│
|
||||
├── 8. 斜杠命令检查
|
||||
│ └── handleCommand() → 如匹配则直接返回
|
||||
│
|
||||
├── 9. 挂起技能检查
|
||||
│ └── takePendingSkills() → 应用技能覆盖
|
||||
│
|
||||
└── 10. runAgentLoop(ctx, agent, opts) → 执行 Agent 循环
|
||||
```
|
||||
|
||||
### 3.3 runAgentLoop → runTurn(核心执行循环)
|
||||
|
||||
```
|
||||
runAgentLoop(ctx, agent, opts) [loop.go:1467]
|
||||
│
|
||||
├── RecordLastChannel() → 记录最后活跃频道
|
||||
├── newTurnState(agent, opts, scope) → 创建 Turn 状态
|
||||
└── runTurn(ctx, ts) → 执行 Turn
|
||||
│
|
||||
├── 注册活跃 Turn → registerActiveTurn()
|
||||
├── 发射 TurnStart 事件
|
||||
│
|
||||
├── 加载会话历史
|
||||
│ ├── Sessions.GetHistory(sessionKey)
|
||||
│ └── Sessions.GetSummary(sessionKey)
|
||||
│
|
||||
├── 构建消息列表
|
||||
│ └── ContextBuilder.BuildMessages(history, summary, userMessage, ...)
|
||||
│ ├── 构建 System Prompt(含缓存)
|
||||
│ │ ├── AGENT.md → Agent 身份、工具、技能
|
||||
│ │ ├── SOUL.md → Agent 人格
|
||||
│ │ ├── USER.md → 用户偏好
|
||||
│ │ ├── Skills → 已安装技能
|
||||
│ │ └── 动态上下文(时间、会话信息)
|
||||
│ ├── 拼接对话历史
|
||||
│ └── 添加当前用户消息 + 媒体
|
||||
│
|
||||
├── 解析媒体引用
|
||||
│ └── resolveMediaRefs(messages, mediaStore)
|
||||
│
|
||||
├── 上下文预算检查
|
||||
│ └── isOverContextBudget() → 若超限则 forceCompression()
|
||||
│
|
||||
├── 保存用户消息到会话
|
||||
│
|
||||
├── 模型选择
|
||||
│ └── selectCandidates(agent, userMessage) → 主模型/轻量模型
|
||||
│
|
||||
└── ═══════════════════════════════════════════
|
||||
║ Turn 迭代循环 ║
|
||||
═══════════════════════════════════════════
|
||||
for iteration < maxIterations {
|
||||
│
|
||||
├── 检查硬中止 → hardAbortRequested()
|
||||
├── 检查父 Turn 状态(SubTurn)
|
||||
├── 轮询 SubTurn 结果
|
||||
├── 注入 steering 消息
|
||||
│
|
||||
├── 调用 LLM ────────────────────────
|
||||
│ ├── 流式: provider.ChatStream()
|
||||
│ │ └── onChunk → 实时推送到 Channel
|
||||
│ └── 非流式: provider.Chat()
|
||||
│
|
||||
├── Hook: Observer 回调
|
||||
├── Hook: Interceptor 拦截
|
||||
├── Hook: Approval 审批
|
||||
│
|
||||
├── 解析 LLM 响应
|
||||
│ ├── 提取 finalContent(文本回复)
|
||||
│ └── 提取 toolCalls(工具调用)
|
||||
│
|
||||
├── if 无 toolCalls → 跳出循环
|
||||
│
|
||||
├── 执行工具调用
|
||||
│ for _, toolCall := range toolCalls {
|
||||
│ │
|
||||
│ ├── 工具查找 → Tools.Get(toolName)
|
||||
│ ├── 工具执行 → tool.Execute(params)
|
||||
│ ├── 结果添加到 messages
|
||||
│ └── Hook 回调
|
||||
│ }
|
||||
│
|
||||
├── 模型降级检查
|
||||
│ └── fallbackChain.Execute() → 如失败则切换 Provider
|
||||
│
|
||||
└── 继续迭代 → 再次调用 LLM
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 Turn 完成后的处理
|
||||
|
||||
```
|
||||
runTurn() 返回 turnResult
|
||||
│
|
||||
├── 保存 assistant 回复到会话
|
||||
├── 发射 TurnEnd 事件
|
||||
│
|
||||
└── 回到 runAgentLoop()
|
||||
├── 发布 followUp 消息
|
||||
├── 若 SendResponse → 发送到 OutboundMessage
|
||||
└── 返回 finalContent
|
||||
|
||||
回到 AgentLoop.Run() 的消息处理
|
||||
│
|
||||
├── publishResponseIfNeeded() → 通过 Bus 发送回复
|
||||
│ └── bus.PublishOutbound()
|
||||
│ └── ChannelManager 接收
|
||||
│ └── channelWorker 发送到平台
|
||||
│
|
||||
├── 处理排队的 steering 消息
|
||||
│ └── Continue() → 新一轮 Turn
|
||||
│
|
||||
└── 最终回复发布
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、Channel 系统架构
|
||||
|
||||
### 4.1 ChannelManager(`pkg/channels/manager.go`)
|
||||
|
||||
```
|
||||
ChannelManager
|
||||
├── channels map[string]Channel → 已注册的平台通道
|
||||
├── workers map[string]*channelWorker → 每个 Channel 一个 Worker
|
||||
├── mediaStore → 媒体存储
|
||||
│
|
||||
├── 启动流程:
|
||||
│ ├── 遍历配置中的 Channel
|
||||
│ ├── 创建 Channel 实例
|
||||
│ ├── channel.Start(ctx) → 启动平台连接
|
||||
│ └── 启动 channelWorker goroutine
|
||||
│
|
||||
├── 入站流程:
|
||||
│ Channel 收到消息
|
||||
│ └── 转换为 InboundMessage
|
||||
│ └── bus.PublishInbound() → AgentLoop 消费
|
||||
│
|
||||
└── 出站流程:
|
||||
channelWorker 监听 queue chan
|
||||
└── 收到 OutboundMessage
|
||||
└── channel.SendMessage()
|
||||
```
|
||||
|
||||
### 4.2 支持的 Channel 平台
|
||||
|
||||
| Channel | 包名 | 协议 |
|
||||
|---------|------|------|
|
||||
| Telegram | `telegram` | Long Polling / Webhook |
|
||||
| Discord | `discord` | WebSocket Gateway |
|
||||
| 微信 | `weixin` | iLink API |
|
||||
| 企业微信 | `wecom` | WebSocket |
|
||||
| QQ | `qq` | WebSocket |
|
||||
| 飞书 | `feishu` | WebSocket / SDK |
|
||||
| Slack | `slack` | Socket Mode |
|
||||
| WhatsApp | `whatsapp` | Bridge 协议 |
|
||||
| WhatsApp Native | `whatsapp_native` | 原生协议 |
|
||||
| 钉钉 | `dingtalk` | Stream Mode |
|
||||
| Matrix | (内置) | Sync API |
|
||||
| IRC | `irc` | IRC 协议 |
|
||||
| LINE | `line` | Messaging API |
|
||||
| MaixCam | `maixcam` | 设备集成 |
|
||||
| OneBot | `onebot` | OneBot 协议 |
|
||||
| Pico | `pico` | 硬件设备 |
|
||||
|
||||
### 4.3 Channel 扩展接口
|
||||
|
||||
```go
|
||||
type Channel interface {
|
||||
Start(ctx context.Context) error
|
||||
HandleMessage(ctx context.Context, msg InboundMessage) error
|
||||
Stop(ctx context.Context) error
|
||||
}
|
||||
|
||||
// 可选能力接口
|
||||
type TypingCapable interface {
|
||||
StartTyping(ctx context.Context, chatID string) (stop func(), error)
|
||||
}
|
||||
|
||||
type StreamingCapable interface {
|
||||
BeginStream(ctx context.Context, chatID string) (Streamer, error)
|
||||
}
|
||||
|
||||
type PlaceholderCapable interface {
|
||||
SendPlaceholder(ctx context.Context, chatID string) error
|
||||
}
|
||||
|
||||
type MessageEditor interface {
|
||||
EditMessage(ctx context.Context, chatID, msgID, newContent string) error
|
||||
DeleteMessage(ctx context.Context, chatID, msgID string) error
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、LLM Provider 系统
|
||||
|
||||
### 5.1 Provider 架构
|
||||
|
||||
```
|
||||
LLMProvider 接口
|
||||
│
|
||||
├── OpenAI (gpt-4, gpt-4o, o1, o3...)
|
||||
├── Anthropic (Claude 系列)
|
||||
├── Google (Gemini)
|
||||
├── DeepSeek
|
||||
├── Zhipu (GLM)
|
||||
├── OpenRouter
|
||||
├── Azure OpenAI
|
||||
├── AWS Bedrock
|
||||
├── xAI (Grok)
|
||||
├── Ollama (本地模型)
|
||||
└── 30+ 更多 Provider
|
||||
```
|
||||
|
||||
### 5.2 降级链(Fallback Chain)
|
||||
|
||||
```
|
||||
selectCandidates(agent, userMessage)
|
||||
│
|
||||
├── 模型路由(如启用)
|
||||
│ ├── Router.Score(message) → 复杂度评分
|
||||
│ ├── 低复杂度 → LightProvider(轻量模型)
|
||||
│ └── 高复杂度 → 主 Provider
|
||||
│
|
||||
└── 降级策略
|
||||
├── 主 Provider 调用失败
|
||||
├── FailoverError 分类:
|
||||
│ ├── auth → 认证错误
|
||||
│ ├── rate_limit → 限流
|
||||
│ ├── billing → 计费问题
|
||||
│ ├── timeout → 超时
|
||||
│ └── context_overflow → 上下文溢出
|
||||
├── CooldownTracker 记录失败 Provider
|
||||
├── FallbackChain 尝试下一个候选
|
||||
└── 所有候选失败 → 返回错误
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、工具系统
|
||||
|
||||
### 6.1 内置工具
|
||||
|
||||
| 工具名 | 功能 | 说明 |
|
||||
|--------|------|------|
|
||||
| `read_file` | 读取文件 | 支持行号范围,有大小限制 |
|
||||
| `write_file` | 写入文件 | 创建或覆盖文件 |
|
||||
| `edit_file` | 编辑文件 | 字符串替换 |
|
||||
| `append_file` | 追加文件 | 追加内容到文件末尾 |
|
||||
| `list_dir` | 列出目录 | 目录内容浏览 |
|
||||
| `exec` | 执行命令 | 带沙箱限制的命令执行 |
|
||||
| `message` | 发送消息 | 通过 Bus 发送到其他 Channel |
|
||||
| `send_file` | 发送文件 | 文件附件发送 |
|
||||
| `web_search` | 网页搜索 | Brave/Tavily/DuckDuckGo/Perplexity/SearXNG/百度 |
|
||||
| `web_fetch` | 网页抓取 | HTTP 请求获取网页内容 |
|
||||
| `spawn` | 子 Agent | 创建并发 SubTurn |
|
||||
| `skills_search` | 搜索技能 | 从技能仓库搜索 |
|
||||
| `skills_install` | 安装技能 | 安装技能到工作空间 |
|
||||
| `mcp_tool` | MCP 工具 | MCP 协议工具调用 |
|
||||
|
||||
### 6.2 工具注册流程
|
||||
|
||||
```
|
||||
NewAgentInstance()
|
||||
│
|
||||
├── 创建 ToolRegistry
|
||||
│
|
||||
├── 注册基础工具(根据配置开关):
|
||||
│ ├── read_file → NewReadFileTool(workspace, restrict, maxSize, allowPaths)
|
||||
│ ├── write_file → NewWriteFileTool(workspace, restrict, allowPaths)
|
||||
│ ├── edit_file → NewEditFileTool(workspace, restrict)
|
||||
│ ├── append_file→ NewAppendFileTool(workspace, restrict)
|
||||
│ ├── list_dir → NewListDirTool(workspace, restrict)
|
||||
│ └── exec → NewExecTool(workspace, restrict, timeout)
|
||||
│
|
||||
└── registerSharedTools() (AgentLoop 级别):
|
||||
├── web_search → NewWebSearchTool(brave, tavily, duckduckgo, ...)
|
||||
├── web_fetch → NewWebFetchTool()
|
||||
├── message → NewMessageTool(bus)
|
||||
├── send_file → NewSendFileTool(bus)
|
||||
└── spawn → NewSpawnTool()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、上下文构建与缓存
|
||||
|
||||
### 7.1 ContextBuilder(`pkg/agent/context.go`)
|
||||
|
||||
```
|
||||
ContextBuilder
|
||||
│
|
||||
├── workspace → 工作目录
|
||||
├── skillsLoader → 技能加载器
|
||||
├── memory → 记忆存储
|
||||
│
|
||||
├── 系统提示词缓存:
|
||||
│ ├── cachedSystemPrompt → 缓存的完整 System Prompt
|
||||
│ ├── cachedAt → 缓存构建时各文件的 mtime
|
||||
│ ├── existedAtCache → 缓存时存在的文件路径集合
|
||||
│ └── skillFilesAtCache → 缓存时技能文件的 mtime 快照
|
||||
│
|
||||
├── BuildMessages():
|
||||
│ ├── 构建 System Prompt(有缓存则复用)
|
||||
│ │ ├── AGENT.md 内容
|
||||
│ │ ├── SOUL.md 内容
|
||||
│ │ ├── USER.md 内容
|
||||
│ │ ├── 已激活技能描述
|
||||
│ │ ├── 工具发现提示(BM25/Regex)
|
||||
│ │ ├── 动态上下文(当前时间、会话信息)
|
||||
│ │ └── 记忆上下文
|
||||
│ │
|
||||
│ ├── 拼接 System Message
|
||||
│ ├── 拼接对话历史(如有摘要则先放摘要)
|
||||
│ └── 拼接当前用户消息(含媒体)
|
||||
│
|
||||
└── 缓存失效检测:
|
||||
├── sourceFilesChanged() → 检查 mtime 变化
|
||||
├── 检查文件新增/删除
|
||||
└── 检查技能文件变化
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、Hook 与事件系统
|
||||
|
||||
### 8.1 事件类型
|
||||
|
||||
```
|
||||
EventBus
|
||||
│
|
||||
├── EventKindTurnStart → Turn 开始
|
||||
├── EventKindTurnEnd → Turn 结束
|
||||
├── EventKindToolCall → 工具调用
|
||||
├── EventKindToolResult → 工具结果
|
||||
├── EventKindLLMRequest → LLM 请求
|
||||
├── EventKindLLMResponse → LLM 响应
|
||||
├── EventKindSteeringInjected → Steering 消息注入
|
||||
├── EventKindContextCompress → 上下文压缩
|
||||
└── EventKindError → 错误
|
||||
```
|
||||
|
||||
### 8.2 Hook 类型
|
||||
|
||||
```
|
||||
HookRegistration
|
||||
│
|
||||
├── Observer Hook(观察者)
|
||||
│ └── 只读回调,不影响执行流程
|
||||
│
|
||||
├── Interceptor Hook(拦截器)
|
||||
│ └── 可修改消息内容
|
||||
│
|
||||
└── Approval Hook(审批)
|
||||
└── 可阻断执行,等待人工审批
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、SubTurn 并发子 Agent
|
||||
|
||||
### 9.1 SubTurn 流程
|
||||
|
||||
```
|
||||
spawn 工具被调用
|
||||
│
|
||||
├── 创建子 turnState
|
||||
│ ├── depth = parent.depth + 1
|
||||
│ ├── parentTurnState = 当前 Turn
|
||||
│ ├── pendingResults channel
|
||||
│ └── concurrencySem 信号量(控制并发数)
|
||||
│
|
||||
├── goroutine 启动子 Turn
|
||||
│ └── runTurn(ctx, childTurnState)
|
||||
│ └── 独立的 LLM 调用循环
|
||||
│
|
||||
├── 结果通过 pendingResults 传回父 Turn
|
||||
│
|
||||
└── 父 Turn 在每次迭代开始时轮询结果
|
||||
└── select { case result := <-pendingResults }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、会话与记忆管理
|
||||
|
||||
### 10.1 会话存储(JSONL)
|
||||
|
||||
```
|
||||
SessionStore (JSONL 后端)
|
||||
│
|
||||
├── AddMessage(sessionKey, role, content)
|
||||
├── AddFullMessage(sessionKey, message) → 含媒体的消息
|
||||
├── GetHistory(sessionKey) []Message
|
||||
├── GetSummary(sessionKey) string
|
||||
│
|
||||
├── 上下文压缩:
|
||||
│ ├── 检查触发条件:
|
||||
│ │ ├── 消息数 > summarizeMessageThreshold
|
||||
│ │ └── Token 使用 > contextWindow * summarizeTokenPercent%
|
||||
│ ├── 调用 LLM 生成摘要
|
||||
│ ├── 替换历史为摘要 + 最近消息
|
||||
│ └── 发射 ContextCompress 事件
|
||||
│
|
||||
└── 主动压缩:
|
||||
└── forceCompression() → 在 LLM 调用前检查预算
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、配置管理
|
||||
|
||||
### 11.1 配置文件路径
|
||||
|
||||
```
|
||||
~/.picoclaw/
|
||||
├── config.json → 主配置文件
|
||||
├── workspace/
|
||||
│ ├── AGENT.md → Agent 定义
|
||||
│ ├── SOUL.md → Agent 人格
|
||||
│ └── USER.md → 用户偏好
|
||||
├── skills/ → 全局技能目录
|
||||
├── logs/ → 日志目录
|
||||
└── .security.yml → 安全过滤配置
|
||||
```
|
||||
|
||||
### 11.2 关键配置项
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model_name": "gpt-4o",
|
||||
"max_tool_iterations": 20,
|
||||
"max_tokens": 8192,
|
||||
"context_window": 32768,
|
||||
"steering_mode": "async",
|
||||
"restrict_to_workspace": true
|
||||
},
|
||||
"list": [{ "id": "main", "workspace": "~/.picoclaw/workspace" }]
|
||||
},
|
||||
"model_list": [
|
||||
{ "model_name": "gpt-4o", "model": "openai/gpt-4o", "api_key": "sk-..." }
|
||||
],
|
||||
"gateway": { "host": "127.0.0.1", "port": 18790 },
|
||||
"routing": { "enabled": true, "light_model": "gpt-4o-mini" },
|
||||
"tools": { "web": { "brave": { "enabled": true } }, "mcp": { "enabled": true } }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、端到端消息流示例
|
||||
|
||||
以 Telegram 用户发送消息为例:
|
||||
|
||||
```
|
||||
1. Telegram Bot API → telegram channel 收到 Update
|
||||
↓
|
||||
2. telegram 转换为 InboundMessage:
|
||||
{Channel:"telegram", ChatID:"123", SenderID:"alice", Content:"今天天气怎样?"}
|
||||
↓
|
||||
3. ChannelManager → bus.PublishInbound()
|
||||
↓
|
||||
4. AgentLoop.Run() 从 bus.InboundChan() 收到消息
|
||||
↓
|
||||
5. processMessage()
|
||||
├── transcribeAudioInMessage() → 无音频,跳过
|
||||
├── resolveMessageRoute() → 路由到 "main" Agent
|
||||
└── runAgentLoop()
|
||||
↓
|
||||
6. runTurn()
|
||||
├── 加载会话历史
|
||||
├── ContextBuilder.BuildMessages()
|
||||
│ ├── 读取 AGENT.md + SOUL.md + USER.md
|
||||
│ ├── 生成 System Prompt
|
||||
│ └── 拼接历史 + 当前消息
|
||||
↓
|
||||
7. selectCandidates() → 选择主模型 gpt-4o
|
||||
↓
|
||||
8. provider.Chat(messages, toolDefs) → 调用 OpenAI API
|
||||
│
|
||||
返回: { toolCalls: [{name: "web_search", params: {query: "今天天气"}}] }
|
||||
↓
|
||||
9. 执行 web_search 工具 → 获取搜索结果
|
||||
↓
|
||||
10. 将工具结果添加到 messages → 再次调用 LLM
|
||||
│
|
||||
返回: { content: "根据搜索结果,今天北京晴,气温15°C..." }
|
||||
↓
|
||||
11. 保存 assistant 回复到会话
|
||||
↓
|
||||
12. runTurn() 返回 → runAgentLoop() 返回
|
||||
↓
|
||||
13. publishResponseIfNeeded()
|
||||
└── bus.PublishOutbound({Channel:"telegram", ChatID:"123", Content:"..."})
|
||||
↓
|
||||
14. ChannelManager 分发给 telegram channelWorker
|
||||
↓
|
||||
15. telegram channel 发送回复消息给用户
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十三、Docker 部署
|
||||
|
||||
### 13.1 三种运行模式
|
||||
|
||||
| Profile | 命令 | 用途 |
|
||||
|---------|------|------|
|
||||
| `agent` | `docker compose run --rm picoclaw-agent -m "Hello"` | 单次查询 |
|
||||
| `gateway` | `docker compose --profile gateway up` | 长期运行网关(无 WebUI) |
|
||||
| `launcher` | `docker compose --profile launcher up` | 网关 + WebUI |
|
||||
|
||||
### 13.2 容器内流程
|
||||
|
||||
```
|
||||
entrypoint.sh
|
||||
│
|
||||
├── 检查 ~/.picoclaw/ 是否存在
|
||||
│ └── 不存在 → picoclaw onboard(自动初始化)
|
||||
│
|
||||
├── 检查 config.json 是否存在
|
||||
│ └── 不存在 → 从环境变量生成配置
|
||||
│
|
||||
└── 执行传入的命令
|
||||
├── picoclaw gateway → 启动网关
|
||||
└── picoclaw agent -m "..." → 单次对话
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十四、关键设计亮点
|
||||
|
||||
1. **系统提示词缓存**: 通过 mtime 检测文件变化,避免每次请求重建完整 System Prompt
|
||||
2. **JSONL 会话存储**: 比 JSON 更高效的日志式存储,适合追加写入
|
||||
3. **Steering 消息注入**: 允许在 Turn 执行过程中动态注入新消息,实现对话中续
|
||||
4. **SubTurn 并发**: 通过信号量控制并发子 Agent 数量,结果异步传回父 Turn
|
||||
5. **Fallback 降级链**: 自动分类错误原因,智能切换备用 Provider
|
||||
6. **模型路由**: 根据消息复杂度动态选择轻量/重量模型,优化成本
|
||||
7. **插件化架构**: Channel 和 Provider 均通过接口实现,支持热插拔
|
||||
8. **配置热重载**: 支持不重启服务更新配置
|
||||
Loading…
Add table
Reference in a new issue