PicoBot/README.md
oudecheng 86d48a3ec0 feat: 实现自定义子代理加载功能
- 添加 SubagentCatalog::discover() 方法,支持从文件系统加载自定义子代理
- 支持 ~/.picobot/subagents/ 和 ./.picobot/subagents/ 两个目录
- 项目级定义可覆盖用户级定义
- 支持 YAML frontmatter + body 格式解析
- 修复 Windows 换行符兼容性问题
- 移除未使用的 read_only 字段
- 实现 TaskTool 动态 schema,子代理类型列表从运行时获取

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:56:44 +08:00

1084 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PicoBot
PicoBot 是一个用 Rust 构建的多通道 Agent 网关。它把消息接入、LLM 调用、工具执行、会话持久化、长期记忆、技能系统和计划任务整合到同一个运行时里,适合做本地 Agent、团队机器人或带长期上下文的自动化助手。
当前代码库已经实现以下核心能力:
- 基于 Gateway 的统一消息入口,支持 WebSocket CLI、飞书通道和微信通道
- 面向工具调用的 Agent 循环,支持多轮 tool calling
- SQLite 持久化会话、消息、长期记忆、技能事件和调度任务
- 基于用户维度的长期记忆检索与写入机制
- 基于 SKILL.md 的项目级 / 用户级 / OpenClaw 级技能加载与运行时管理
- 定时任务系统,支持延迟、周期、绝对时间和 cron 调度
- 超长上下文压缩与历史摘要
- 持久化 Agent 配置文件注入与周期性重注入
- 会话管理支持按通道查询和切换
- MCP (Model Context Protocol) 集成,支持 Stdio 和 HTTP 两种传输方式
## 1. 项目定位
PicoBot 的设计目标不是“只会聊天”的单进程 Bot而是一个可持续运行的 Agent 基础设施:
- 消息从不同渠道进入统一总线
- SessionManager 负责会话路由和运行时服务编排AgentExecutionService 负责上下文准备、AgentLoop 执行、结果持久化和回复生成
- SQLite 作为事实来源保存跨重启状态
- Agent 在每轮推理时可以读取文件、执行命令、发 HTTP 请求、读写记忆、管理技能和调度任务
如果你需要一个“带长期记忆、可调用工具、可接入飞书、可计划执行任务”的机器人,这个项目的实现面基本够用。
## 2. 核心架构
整体链路可以概括为:
1. Channel 接收外部消息
2. MessageBus 将消息送入统一的 inbound 队列
3. Gateway 启动的 InboundProcessor 调用 SessionManager 定位目标 Session
4. AgentExecutionService 准备上下文、运行 AgentLoop、执行工具调用并收集结果
5. 生成的 user / assistant / tool / system 消息按真实顺序写入 SQLite
6. OutboundDispatcher 将结果投递到目标通道
### 2.1 消息流转图
```text
用户消息输入
┌─────────────┐
│ Channel │ ◄── WebSocket CLI / 飞书等
└──────┬──────┘
│ publish_inbound
┌─────────────┐
│ MessageBus │ ◄── 统一消息总线
└──────┬──────┘
│ consume_inbound
┌──────────────────┐
│ InboundProcessor │ ◄── 入站处理器
└────────┬─────────┘
│ handle_message
┌────────────────┐
│ SessionManager │ ◄── 会话管理器
└────────┬───────┘
│ 定位/创建 Session
┌────────────────────────┐
│ AgentExecutionService │ ◄── 准备上下文、执行 Agent
└────────┬───────────────┘
┌────┴────┐
▼ ▼
┌───────┐ ┌──────────┐
│SQLite │ │ AgentLoop│ ◄── 调用 LLM、工具执行
└───────┘ └────┬─────┘
▲ │
│ ▼
│ ┌──────────┐
└────┤ToolRegistry│ ◄── 工具注册表
└──────────┘
┌─────────────────┐
│ OutboundDispatcher │ ◄── 出站分发
└────────┬────────┘
│ dispatch
┌─────────────────┐
│ Channel │
└────────┬────────┘
│ 最终回复
用户
关键步骤说明:
1. Channel 接收外部消息 → 2. MessageBus 统一路由 → 3. InboundProcessor 处理
4. SessionManager 定位 Session → 5. AgentExecutionService 执行
6. 消息持久化到 SQLite → 7. AgentLoop 推理/工具调用
8. 结果经 OutboundDispatcher 返回 Channel
```
### 2.2 项目架构图
```text
接入层 (Edge)
├── CLI Client / WebSocket ──┐
├── Feishu Channel ─────────┼──► 网关与运行时编排 (Gateway)
└── HTTP Health / WS ────────┘ │
├── ChannelManager ◄──┐
├── MessageBus ────────┼── 双向通信
├── InboundProcessor ──┘
├── OutboundDispatcher
├── SessionManager
├── SessionLifecycle / Message / ScheduledTask Services
└── AgentExecutionService ◄── 调用 Agent 执行层
Agent 执行层 (Agent) ◄─────────────────────────────────────────┘
├── AgentLoop ──► ToolRegistry (工具调用)
├── ContextCompressor (上下文压缩)
├── SkillRuntime (技能系统)
└── LLM Providers (OpenAI / Anthropic)
持久化与后台能力 (Runtime)
├── SessionStore / SQLite (会话/消息/记忆存储)
├── Scheduler (定时任务调度)
└── Memory Maintenance (记忆维护)
数据流向:
- 接入层 ◄──► 网关 ◄──► Agent 执行层
- 网关 ◄──► 持久化层 (SQLite)
- Scheduler ◄──► 总线 ◄──► SessionManager
```
主要模块如下:
- src/agentAgentLoop、上下文压缩器、运行时配置、系统提示构建
- src/bus消息总线队列与消息结构定义不包含渠道投递逻辑
- src/channels渠道适配层当前已有 CLI、飞书、微信通道
- src/cli本地 CLI 客户端、输入命令解析
- src/clientWebSocket CLI 客户端实现
- src/command命令系统包括处理器、适配器、上下文和响应处理
- src/config配置解析与默认值定义
- src/domain领域模型包含消息和工具定义
- src/gateway网关生命周期、InboundProcessor、OutboundDispatcher、SessionManager以及消息执行、调度任务执行、Prompt 注入、历史压缩和记忆维护编排
- src/providers不同 LLM Provider 的统一抽象,当前支持 openai 和 anthropic
- src/tools内置工具集合与 ToolRegistry
- src/storageSQLite 持久化实现
- src/scheduler数据库驱动的计划任务调度器
- src/skills技能发现、加载与运行时管理
- src/protocolWebSocket 入站 / 出站协议结构
## 3. 消息机制
PicoBot 的消息不是简单的“用户文本 + 助手文本”,而是完整的会话事件流。
### 3.1 消息类型
系统中的 ChatMessage 至少包含以下角色:
- user用户输入
- assistant模型回复
- system系统提示、Agent Prompt、调度附加提示、历史压缩摘要
- tool工具执行结果
此外assistant 消息还能携带:
- tool_calls本轮准备调用哪些工具
- reasoning_content模型的补充推理文本
用户消息也支持附带媒体引用,当前消息结构中已经支持文本与图片 URL 形式的多模态块,以及本地媒体文件路径引用。
### 3.2 消息总线
MessageBus 维护两条异步队列:
- inboundChannel -> Agent
- outboundAgent -> Dispatcher -> Channel
这样做的好处是:
- 渠道接入和 Agent 执行解耦
- 可在网关内部统一处理重试、记录、路由和扩展
- 后续增加新渠道时不需要改动核心 Agent 流程
### 3.3 工具调用消息流
当模型发起工具调用时PicoBot 会按顺序保存:
1. 一条 assistant 消息,记录 tool_calls
2. 一条或多条 tool 消息,记录每个工具返回结果
3. 最终 assistant 汇总回复
这意味着即使进程重启,系统仍然可以恢复:
- 当时模型调用了什么工具
- 工具入参和返回结果是什么
- 最终结论如何形成
## 4. 会话与上下文机制
### 4.1 会话标识
PicoBot 为不同渠道统一维护持久化会话:
- CLIsession_id 等于 chat_id
- 非 CLI 渠道session_id = channel_name:chat_id
这样可以让:
- CLI 显式创建、切换和管理多个会话
- 飞书等渠道把同一个 chat 稳定映射到同一条持久化会话
### 4.2 Agent Prompt 注入
PicoBot 会在 ~/.picobot/agent/AGENT.md 维护一份持久化 Agent 画像文件。
行为规则:
- 如果目录不存在会自动创建
- 如果文件不存在会自动写入默认内容
- 当前活动对话为空或刚 reset 时,会先把 AGENT.md 作为第一条 system 消息注入
- 每经过指定数量的 user 轮次,会在下一条用户消息进入前重新注入最新 AGENT.md
相关配置:
- gateway.agent_prompt_reinject_every默认 100
- 设为 0 可关闭周期性重注入
### 4.3 上下文压缩
上下文压缩不是在每次请求前同步执行而是在一轮消息处理完成、assistant / tool 消息已经按真实顺序落库之后,再异步安排后台压缩任务。这样可以先保证当前回复链路完成,再处理历史瘦身。
完整机制如下:
1. 系统先对当前活动历史做一个近似 token 估算。
估算规则不是调用 tokenizer而是按“约每 4 个字符约等于 1 token并再乘以 1.2 安全系数”计算。
2. 当估算结果超过模型上下文窗口的 50% 时,压缩器才认为“需要压缩”。
这里的上下文窗口来自 agent 对应模型配置里的 context_window_tokens未配置时按 128000 估算。
3. 即使超过阈值,如果当前历史里的 user turn 数量不超过保留阈值,也不会压缩。
当前默认会完整保留最近 3 个 user turn。
4. 一旦满足条件,压缩器会先按 user 消息切分 turn再确定“旧历史”和“最近保留段”的分界点。
5. 在旧历史里,不是所有 system 消息都会被折叠:
带有 agent_prompt 和 scheduled_system_prompt 标记的 system 消息会被原样保留,避免丢失 Agent 基本设定和调度任务附加约束。
6. 除这些必须保留的 system 消息外,分界点之前的其余消息会被整理成一段待摘要 transcript。
transcript 会保留角色信息tool 消息还会带上工具名,方便摘要时保留关键操作和结果。
7. 如果 transcript 本身已经过长,会先按当前模型上下文窗口推导出的摘要字符预算截断,再交给模型总结。
8. 摘要调用和主对话使用同一个 provider提示词明确要求保留
用户请求、关键标识符、文件路径、URL、工具调用、命令、结果、错误、决策、偏好和当前任务状态。
如果摘要调用失败,会退化为直接截断 transcript而不会中断主流程。
9. 摘要结果会被包装成一条新的 system 消息,并打上 SYSTEM_CONTEXT_HISTORY_COMPACTION 标记,内容前缀为 [Compressed History]。
10. 后台提交阶段会删除旧消息,并追加新的活动段:
依次写入保留的关键 system 消息、压缩摘要消息、最近保留的消息。
11. 压缩提交成功后Session 会重新加载当前 chat 的活动历史,后续轮次看到的就是"关键 system 消息 + 压缩摘要 + 最近若干完整 turn"的新上下文。
这套机制的目标不是简单删历史,而是把“远端历史变成可恢复摘要”,同时保证:
- Agent Prompt 和调度提示不丢
- 最近对话细节仍然完整
- 工具调用形成的重要事实被保留下来
- 数据库里仍有完整原始消息流水
## 5. 持久化与存储机制
PicoBot 目前使用 SQLite 作为主要持久化后端,默认数据库路径为:
- ~/.picobot/storage/sessions.db
数据库会保存以下核心实体:
- sessions会话元数据
- messages消息流水
- memories长期记忆
- skill_events技能发现与使用事件
- scheduler_jobs计划任务定义与运行状态
设计原则是:
- 内存对象负责运行时性能与临时状态
- SQLite 负责跨重启、跨进程恢复
详细表结构和持久化说明可参考 docs/PERSISTENCE.md。
## 6. 长期记忆机制
长期记忆是 PicoBot 和普通聊天机器人最不一样的部分之一。
### 6.1 记忆存储模型
每条记忆至少包含这些维度:
- scope_kind当前实现为 user
- scope_key由 channel_name:sender_id 组成
- namespace记忆命名空间例如 profile、preferences、tasks
- memory_key命名空间内的键
- content记忆正文
- source_session_id / source_message_id / source_message_seq来源追踪信息
这意味着记忆默认按“渠道中的某个用户”隔离,而不是按单个 chat 隔离。
### 6.2 记忆读取与写入工具
内置了两类记忆工具:
- memory_search只读检索支持 search / get / list
- memory_manage写操作支持 put / update / delete
推荐模式是:
1. 先用 memory_search 回忆用户长期信息
2. 只在确实识别到高价值长期信息时,再用 memory_manage 写入或更新
### 6.3 记忆机制的时机
长期记忆不会像会话历史那样在每轮请求开始时自动拼进上下文;它的使用时机是显式、按需、分阶段发生的。
完整时序如下:
1. 用户新消息到达后,系统先恢复当前 chat 的活动历史、system 消息和 Agent Prompt。
这一步恢复的是会话上下文,不会自动把 memories 表里的所有长期记忆直接注入本轮请求。
2. AgentLoop 开始推理后,如果模型判断当前问题依赖用户历史,例如语言偏好、长期任务、身份事实、历史决策或稳定工作方式,就应主动调用 memory_search。
因此“读记忆”的时机通常发生在本轮推理前段或中段,而不是网关层强制预加载。
3. memory_search 的作用域不是当前 chat而是当前 channel_name:sender_id 对应的用户范围。
这意味着同一个用户在同一渠道下的不同 chat可以共享长期记忆。
4. 当模型已经确认某条信息值得跨会话保留时,才调用 memory_manage。
因此“写记忆”的时机通常发生在本轮推理中后段:先理解当前问题,再决定是否沉淀为长期记忆。
5. memory_manage 写入时会记录当前工具上下文中的 session_id、message_id、message_seq、channel_name 和 chat_id。
所以一旦写入成功,这条记忆会立刻持久化到 SQLite并能追溯来源。
6. 已写入的记忆不会自动回流到“当前这一轮”已经构建好的提示上下文。
它主要影响后续轮次中模型再次调用 memory_search 的结果,以及后续记忆维护后生成的托管摘要。
可以把两类上下文区分为:
- 会话历史:默认参与当前 chat 的直接推理
- 长期记忆:只有模型主动检索时才进入推理过程
### 6.4 记忆维护机制
PicoBot 不只是“把记忆存进去”,还内置了记忆整理逻辑。
系统会把长期记忆粗分为几类:
- 用户事实
- 偏好
- 行为模式
- 其他信息
随后通过记忆维护流程:
- 去重低价值记忆
- 合并重复或可归并的记忆项
- 识别冲突信息
- 生成一份受控的“用户记忆摘要” Markdown
维护时机和生效方式是:
1. SessionManager 会先按 scope_key 取出该用户范围下的全部长期记忆。
2. 然后把候选记忆整理成结构化计划,交给模型做归纳和裁剪。
3. 模型输出会包含用户事实、偏好、行为模式、需要合并的条目、低价值条目,以及一份 managed_markdown 摘要。
4. 系统据此回写 memories 表:
合并项会重新 put_memory低价值项会 delete_memory自动整理产生的记录会标记 source_type = memory_maintenance。
5. 所有 scope 的 managed_markdown 会再被合并,并写入 ~/.picobot/agent/AGENT.md 的托管记忆区块。
这意味着记忆维护不是为了立刻改变当前一轮回复,而是为了影响后续新对话、后续 reset 后的新活动段,以及未来每次重新注入的 Agent Prompt。
### 6.5 定时记忆维护
Scheduler 默认内置一个任务:
- builtin.memory_maintenance_daily
- 默认按本地时区每 4 小时运行一次cron: 0 */4 * * *
这个任务以 internal_event 的方式触发 memory_maintenance 事件。触发后,系统会遍历所有已有用户 scope逐个执行记忆维护并在全部处理完成后统一更新 AGENT.md 中的“用户记忆摘要”托管区块。
## 7. 技能机制
PicoBot 支持基于文件系统的技能系统,用来给 Agent 注入某一类任务的专门说明。
### 7.1 技能发现位置
当前技能运行时按从低到高优先级合并多个来源,后加载来源可覆盖同名技能:
- 用户级技能:~/.picobot/skills/*/SKILL.md
- 用户 Agent 级技能:~/.agents/skills/*/SKILL.md
- 用户 OpenClaw 级技能:~/.openclaw/skills/*/SKILL.md
- 项目级技能:.picobot/skills/*/SKILL.md
- 项目 Agent 级技能:.agents/skills/*/SKILL.md
- 项目 OpenClaw 级技能:.openclaw/skills/*/SKILL.md
### 7.2 最小 SKILL.md 格式
```md
---
description: 用于总结 Rust 项目架构
---
这里写详细说明。
```
约束:
- frontmatter 中必须有 description
- name 可省略,省略时使用目录名
- 非法技能文件会被跳过,并记录 warning 日志
### 7.3 技能注入方式
运行时会发生两件事:
- AgentLoop 动态注入“技能索引”系统提示,列出当前可用技能的名称和描述
- 模型可以通过工具读取具体技能内容并按技能说明执行任务
### 7.4 技能管理工具
内置工具:
- skill_list只读列出技能
- skill_manage运行时创建、更新、删除、批量禁用、读取和重载技能
skill_manage 支持的 action
- list
- get
- create
- update
- delete
- disable
- reload
skill 的启用/禁用状态不会写入 config.json而是写入独立状态文件
- 用户级状态:~/.picobot/skill-state.json
- 项目级状态:.picobot/skill-state.json
状态文件当前使用最小 JSON 结构:
```json
{
"disabled_skills": ["example-skill"]
}
```
说明:
- disable 默认写入项目级状态文件,可通过 tool 参数中的 scope 指定 user 或 project
- disable 只接受 names 数组;即使只禁用 1 个 skill也需要传单元素数组
- 一次 disable 调用会批量处理 names 里的所有 skill并只做一次 reload
- 用户级与项目级状态同时生效,项目运行时会同时读取两者
- 某个 skill 只要在任一层状态文件中被禁用,就不会出现在 skill_list、skill_activate 和技能索引提示里
批量示例:
```json
{
"action": "disable",
"scope": "project",
"names": ["lark-calendar", "lark-vc", "lark-minutes"]
}
```
skills 配置示例:
```json
{
"skills": {
"enabled": true,
"sources": ["user", "user_agent", "user_openclaw", "project", "project_agent", "project_openclaw"],
"max_index_chars": 4000,
"max_listed_skills": 32
}
}
```
tools 配置示例:
```json
{
"tools": {
"disabled": ["bash", "http_request", "web_fetch"]
}
}
```
可用工具名称:
- calculator - 数学计算器
- get_time - 获取当前时间
- read - 读取文件
- write - 写入文件
- edit - 编辑文件
- memory_search - 搜索长期记忆
- memory_manage - 管理长期记忆
- send_session_message - 发送会话消息
- scheduler_manage - 管理定时任务
- skill_activate - 激活技能
- skill_manage - 管理技能(含 list 功能)
- bash - 执行 shell 命令Unix/Linux/macOS
- shell - 执行 shell 命令Windows PowerShell/Cmd
- http_request - HTTP 请求
- web_fetch - 网页抓取
- task - 创建和管理子代理支持内置类型general/explore和用户自定义类型
注意bash 和 shell 是同一个工具在不同平台上的名称,运行时自动检测。
## 8. 子代理系统
PicoBot 支持通过 `task` 工具创建子代理来处理复杂多步骤任务。子代理在一个独立的执行上下文中运行,拥有独立的会话历史和工具权限。
### 8.1 内置子代理类型
- **general**通用型子代理适合处理复杂多步骤任务。可以使用读写文件、执行命令、HTTP 请求等完整工具集。
- **explore**:探索型子代理,用于代码库探索和信息收集。只使用只读工具,禁止任何写操作。
### 8.2 自定义子代理
用户可以在文件系统上定义新的子代理类型,类似于技能的加载方式。子代理定义文件采用 YAML frontmatter + body 的格式。
#### 定义位置
按从低到高优先级合并,后加载来源可覆盖同名定义:
- 用户级:`~/.picobot/subagents/*/SUBAGENT.md`
- 项目级:`.picobot/subagents/*/SUBAGENT.md`
#### SUBAGENT.md 格式
```md
---
name: code-review # 可选,省略时使用目录名
description: 代码审查代理,用于检查代码质量和安全问题
prompt_template: |
你是一个专业的代码审查代理。
任务描述: {{description}}
你应该:
1. 检查代码质量和潜在问题
2. 提出改进建议
3. 完成后给出简洁的总结
注意: 你是一个只读代理,禁止执行任何修改操作。
allowed_tools: [read, bash, web_fetch] # 可选,覆盖默认工具白名单
max_execution_secs: 600 # 可选,覆盖默认执行时间
---
请重点关注:
- SQL 注入风险
- XSS 漏洞
- 权限校验缺失
```
#### 字段说明
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 子代理名称,默认取目录名 |
| `description` | string | 是 | 简短描述,用于 agent 选择 |
| `prompt_template` | string | 是 | 提示词模板,支持变量插值 |
| `allowed_tools` | array | 否 | 工具白名单,不指定时使用默认列表 |
| `max_execution_secs` | integer | 否 | 最大执行时间(秒) |
#### 模板变量
`prompt_template` 支持以下变量插值:
- `{{description}}`:任务描述(来自 `task` 工具的 description 参数)
- `{{prompt}}`:详细指令(来自 `task` 工具的 prompt 参数)
文件 body 部分frontmatter 之后的内容)会追加到提示词模板末尾,用于提供更详细的补充指令。
#### 使用方式
创建子代理后,通过 `task` 工具的 `subagent_type` 参数指定类型:
```json
{
"description": "审查 src/auth 目录下的代码",
"prompt": "检查安全漏洞和错误处理",
"subagent_type": "code-review"
}
```
如果指定的子代理类型不存在,系统会自动回退到 `general` 类型。
### 8.3 子代理配置
```json
{
"subagents": {
"enabled": true,
"sources": ["user", "project"]
}
}
```
| 字段 | 默认值 | 说明 |
|------|--------|------|
| `enabled` | `true` | 是否启用自定义子代理发现 |
| `sources` | `["user", "project"]` | 定义来源优先级 |
## 9. 工具机制
PicoBot 的 Agent 是围绕工具调用构建的。当前默认注册的工具包括:
- calculator简单数学计算
- get_time获取当前时间与时区上下文
- read读取文件
- write写文件
- edit编辑文件
- memory_search读取长期记忆
- memory_manage写入 / 更新 / 删除长期记忆
- send_session_message发送会话消息
- scheduler_manage管理调度任务
- skill_activate读取并激活某个技能内容
- skill_manage管理技能支持 list, get, create, update, delete, disable, reload
- bash / shell执行 shell 命令同一工具Unix 下名称为 bashWindows 下名称为 shell
- http_request发起 HTTP 请求
- web_fetch抓取网页正文
- task创建和管理子代理支持内置类型general/explore和用户自定义类型
其中:
- read / write / edit 文件工具适合做代码库和文档操作
- 记忆工具适合维持长期用户画像
- scheduler_manage 允许 Agent 自主创建后续计划任务
- skill_activate 负责把具体技能正文注入当前任务上下文
- skill_manage 整合了技能列出与管理功能,支持运行时创建、更新、删除和批量禁用
- bash / shell / http_request / web_fetch 让 Agent 具备更强的外部交互能力bash 和 shell 是同一工具在不同平台的名称)
- task 允许 Agent 创建独立上下文的子代理来处理复杂多步骤任务支持内置类型general/explore和用户自定义类型
### 9.1 MCP 工具集成
PicoBot 支持通过 MCP (Model Context Protocol) 扩展工具能力,可以连接外部 MCP servers 并自动发现其提供的工具。配置格式兼容 Claude Desktop / Cursor。
**支持的 Transport 类型:**
| Transport | type 值 | 说明 | 适用场景 |
|-----------|---------|------|----------|
| **Stdio** | `stdio` | 启动子进程,通过 stdin/stdout 通信 | 本地 MCP servers如 npm 包) |
| **HTTP** | `streamableHttp``http` | 通过 HTTP/SSE 连接远程服务器 | 远程 MCP servers、云服务 |
**配置示例:**
```json
{
"mcpServers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],
"isActive": true
},
"WebSearch": {
"type": "streamableHttp",
"baseUrl": "https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/mcp",
"headers": {
"Authorization": "Bearer ${DASHSCOPE_API_KEY}"
},
"isActive": true,
"name": "AliyunBailianMCP_WebSearch"
}
}
}
```
**配置字段说明:**
| 字段 | 说明 | 必填 |
|------|------|------|
| `type` | Transport 类型:`stdio``streamableHttp`/`http` | 是 |
| `isActive` | 是否启用此 server默认 `true` | 否 |
| `name` | Server 显示名称(默认使用 map key | 否 |
| `command` | Stdio: 要执行的命令 | Stdio 必填 |
| `args` | Stdio: 命令参数 | 否 |
| `env` | Stdio: 环境变量 | 否 |
| `baseUrl` | HTTP: MCP server URL | HTTP 必填 |
| `headers` | HTTP: 自定义请求头(支持 `${ENV_VAR}` 占位符) | 否 |
**环境变量占位符:**
配置中支持两种占位符语法:
- `${ENV_VAR}` - Claude Desktop 风格,推荐用于 MCP headers
- `<ENV_VAR>` - PicoBot 原有风格,用于其他配置项
```json
{
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
```
**工具命名规则:**
MCP 工具会自动注册到 ToolRegistry命名格式为 `mcp_{server_key}_{tool_name}`
- `mcp_filesystem_read_file` - server key 为 "filesystem"
- `mcp_filesystem_write_file`
- `mcp_WebSearch_search` - server key 为 "WebSearch"
**架构特点:**
- MCP 模块完全解耦,默认禁用时不影响原有系统
- 异步初始化,不阻塞 Gateway 启动
- 通过 Tool trait 适配器接入,无需修改核心代码
- 连接失败不影响 Gateway 运行
## 10. 调度器机制
PicoBot 带有一个基于 SQLite 的调度器,而不是纯内存或 JSON 文件驱动的任务系统。
### 10.1 支持的调度类型
- delay延迟执行一次
- interval固定间隔执行
- at某个绝对时间执行一次
- croncron 表达式调度
### 10.2 支持的任务类型
- internal_event内部事件
- outbound_message直接向目标通道发消息
- agent_task构造一次合成用户输入复用完整 Agent 流程执行
- silent_agent_task在独立后台会话中执行 Agent 流程,成功不推送,失败回主 chat 通知
agent_task 会复用正常链路中的这些能力:
- 历史加载
- Agent Prompt 注入
- 工具调用
- 会话持久化
- 渠道消息下发
silent_agent_task 和 agent_task 使用同一套 Agent 执行能力,但路由语义不同:
- target.chat_id 仍表示用户通知目标
- target.session_chat_id 表示后台任务实际写入的会话;如果省略,会稳定派生为 scheduler/{job_id}
- 成功执行后不会向用户发送正常结果
- 执行失败时会向主 chat 发送一条失败通知,便于用户感知异常
- 后台任务的历史、压缩和会话内上下文会留在独立会话中,不污染主会话
### 10.3 运行时管理
通过 scheduler_manage 可以进行:
- list
- get
- put
- delete
- pause
- resume
任务定义和状态都会写进 SQLite所以重启后仍可恢复。
当前内置任务只默认写入记忆维护任务;`session_cleanup` 作为 `internal_event` 仍然受支持,但如果需要启用,需在配置中显式声明。
调度器配置示例:
```json
{
"scheduler": {
"enabled": true,
"tick_resolution_ms": 1000,
"misfire_policy": "skip",
"jobs": [
{
"id": "session.cleanup",
"kind": "internal_event",
"schedule": {
"type": "interval",
"seconds": 300
},
"payload": {
"event": "session_cleanup"
}
},
{
"id": "agent.daily_summary",
"kind": "agent_task",
"schedule": {
"type": "cron",
"expression": "30 18 * * *"
},
"target": {
"channel": "feishu",
"chat_id": "oc_xxx"
},
"payload": {
"prompt": "请总结今天的项目进展,并列出明天的优先事项",
"agent": "default",
"fresh_session": true,
"system_prompt": "你是日报助手,输出时先给摘要,再给待办。",
"sender_id": "scheduler-daily-summary",
"metadata": {
"job_type": "daily_summary",
"source": "scheduler"
}
}
},
{
"id": "agent.weekly_report.background",
"kind": "silent_agent_task",
"schedule": {
"type": "cron",
"expression": "0 8 * * 1"
},
"target": {
"channel": "feishu",
"chat_id": "oc_xxx",
"session_chat_id": "scheduler/agent.weekly_report.background"
},
"payload": {
"prompt": "请后台整理上周项目进展,输出结构化周报草稿,重点标出风险、阻塞项和下周优先级。",
"agent": "default",
"fresh_session": false,
"system_prompt": "你是周报助手,只在后台整理,不要面向用户寒暄。",
"sender_id": "scheduler-weekly-report",
"metadata": {
"job_type": "weekly_report_background",
"source": "scheduler"
}
}
}
}
]
}
}
```
推荐场景:
- agent_task用户需要直接收到结果例如日报提醒、定时播报、定时外发通知
- silent_agent_task任务需要长期积累独立上下文或后台整理材料但不应污染主会话例如周报草稿整理、周期性资料汇总、后台分析任务
## 11. 渠道与运行方式
### 11.1 当前支持的通道
- WebSocket CLI 客户端
- 飞书通道
- 微信通道
### 11.2 Gateway 接口
网关当前暴露:
- /health健康检查
- /wsCLI 客户端连接入口
### 11.3 CLI 使用方式
程序提供两个主命令:
```bash
cargo run -- gateway
cargo run -- agent
```
含义:
- gateway启动网关服务
- agent以 WebSocket 方式连接网关,进入本地 CLI 会话
CLI 中已实现的交互命令包括:
- /new [title] - 创建新会话
- /sessions - 列出当前通道的所有会话(支持跨通道隔离)
- /use <session> - 切换到指定会话
- /rename <title> - 重命名当前会话
- /archive - 归档当前会话
- /delete - 删除指定会话
- /clear - 清屏
- /quit - 退出 CLI
- /clear
- /quit
## 12. 配置说明
配置默认从以下位置加载:
1. ~/.picobot/config.json
2. 如果不存在,则回退到当前工作目录下的 config.json
配置加载前还会先读取环境变量,并替换配置中的占位符。
最小可运行配置示例:
```json
{
"providers": {
"default": {
"type": "openai",
"base_url": "<OPENAI_BASE_URL>",
"api_key": "<OPENAI_API_KEY>",
"extra_headers": {},
"llm_timeout_secs": 120,
"memory_maintenance_timeout_secs": 600
}
},
"models": {
"default": {
"model_id": "<OPENAI_MODEL_NAME>",
"temperature": 0.2,
"context_window_tokens": 128000
}
},
"agents": {
"default": {
"provider": "default",
"model": "default",
"max_tool_iterations": 100,
"tool_result_max_chars": 20000,
"context_tool_result_trim_chars": 2000
}
},
"gateway": {
"host": "0.0.0.0",
"port": 19876,
"show_tool_results": false,
"agent_prompt_reinject_every": 100
},
"scheduler": {
"enabled": true,
"tick_resolution_ms": 1000,
"worker_queue_capacity": 128,
"misfire_policy": "skip",
"jobs": []
}
}
```
常用配置项:
- providersProvider 连接信息,包含 llm_timeout_secsLLM 调用超时,默认 120 秒)和 memory_maintenance_timeout_secs记忆维护超时默认 600 秒)
- models模型参数包括上下文窗口估算所用的 context_window_tokens
- agentsAgent 级别的工具轮次、工具结果裁剪与上下文裁剪参数
- gateway监听地址、端口、工具结果展示、会话 TTL、Prompt 重注入策略
- scheduler调度器开关、worker 队列容量、误触发策略和任务列表
- channels飞书、微信等通道配置
- skills技能来源与索引限制
- tools工具启用/禁用配置(通过 disabled 列表指定禁用的工具)
- time.timezone时区默认应使用 IANA 时区名,例如 Asia/Shanghai
## 13. 快速开始
### 13.1 准备配置
1. 复制并修改 config.json或把配置放到 ~/.picobot/config.json
2. 配置好 Provider 的 base_url、api_key、model_id
3. 如果要接飞书,再补充 channels.feishu 配置
4. 如果要接微信,再补充 channels.wechat 配置
飞书通道配置示例:
```json
{
"channels": {
"feishu": {
"type": "feishu",
"app_id": "<FEISHU_APP_ID>",
"app_secret": "<FEISHU_APP_SECRET>",
"enabled": true,
"allow_from": ["*"],
"agent": "default",
"media_dir": "~/.picobot/media/feishu",
"reaction_emoji": "Typing",
"max_message_chars": 20000,
"reply_context_max_chars": 20000
}
}
}
```
微信通道配置示例:
```json
{
"channels": {
"wechat": {
"type": "wechat",
"enabled": true,
"allow_from": ["*"],
"agent": "default",
"base_url": "https://ilinkai.weixin.qq.com",
"cred_path": "~/.picobot/wechat/credentials.json",
"force_login": false
}
}
}
```
### 13.2 启动网关
```bash
cargo run -- gateway
```
### 13.3 启动本地 CLI
```bash
cargo run -- agent
```
默认会连接:
```text
ws://127.0.0.1:19876/ws
```
也可以手动指定:
```bash
cargo run -- agent --gateway-url ws://127.0.0.1:19876/ws
```
### 13.4 检查服务状态
```bash
curl http://127.0.0.1:19876/health
```
## 14. 目录结构
```text
PicoBot/
├── src/
│ ├── agent/ # AgentLoop、上下文压缩、运行时配置、系统提示
│ ├── bus/ # 消息总线与消息结构
│ ├── channels/ # CLI / 飞书 / 微信等渠道适配
│ ├── cli/ # CLI 输入命令与通道实现
│ ├── client/ # WebSocket CLI 客户端
│ ├── command/ # 命令系统(处理器、适配器、上下文)
│ ├── config/ # 配置解析
│ ├── domain/ # 领域模型(消息、工具定义)
│ ├── gateway/ # Gateway、Session 编排、WS/HTTP 控制面、执行服务
│ ├── logging/ # 日志配置
│ ├── mcp/ # MCP 集成(客户端管理、工具适配器、配置)
│ ├── observability/ # 可观测性支持
│ ├── platform/ # 平台抽象
│ ├── providers/ # OpenAI / Anthropic Provider
│ ├── scheduler/ # 定时任务系统
│ ├── skills/ # 技能运行时
│ ├── storage/ # SQLite 持久化(存储、端口、记录、错误)
│ ├── text/ # 文本处理工具
│ └── tools/ # 内置工具集合(含 task 子代理系统)
├── docs/
│ ├── IMPLEMENTATION_LOG.md
│ └── PERSISTENCE.md
├── tests/
└── config.json
```
## 15. 测试与维护建议
当前 tests 目录中已经包含 Provider 集成测试和工具调用相关测试,但部分测试依赖外部 API Key需要先准备 tests/test.env。
建议维护时重点关注:
- docs/PERSISTENCE.md持久化结构是否与代码一致
- src/gateway/session.rs会话状态、会话路由和运行时服务编排
- src/gateway/execution.rsAgent 执行服务
- src/storage/mod.rsSQLite schema 变更
- src/config/mod.rs配置项变更是否同步到 README
- src/bus/message.rs消息结构变更如 OutboundMessage 新增 session_id
- src/command/handlers/:命令处理器实现
## 16. 总结
PicoBot 当前已经具备一个可长期运行 Agent 系统的关键组件:
- 有入口Gateway + Channel
- 有状态SQLite + Session 恢复
- 有能力:工具调用 + 技能系统 + MCP 扩展 + 可自定义子代理
- 有记忆:长期记忆 + 自动维护摘要
- 有计划Scheduler + agent_task
如果后续继续演进,比较自然的方向会是:
- 增加更多渠道
- 增强多模态能力
- 完善记忆检索排序与冲突消解
- 为不同 Agent 提供更清晰的配置和权限隔离
- 扩展 MCP servers 支持(更多 npm 包、云服务集成)