# 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/agent：AgentLoop、上下文压缩器、运行时配置、系统提示构建
- src/bus：消息总线队列与消息结构定义，不包含渠道投递逻辑
- src/channels：渠道适配层，当前已有 CLI、飞书、微信通道
- src/cli：本地 CLI 客户端、输入命令解析
- src/client：WebSocket CLI 客户端实现
- src/command：命令系统，包括处理器、适配器、上下文和响应处理
- src/config：配置解析与默认值定义
- src/domain：领域模型，包含消息和工具定义
- src/gateway：网关生命周期、InboundProcessor、OutboundDispatcher、SessionManager，以及消息执行、调度任务执行、Prompt 注入、历史压缩和记忆维护编排
- src/providers：不同 LLM Provider 的统一抽象，当前支持 openai 和 anthropic
- src/tools：内置工具集合与 ToolRegistry
- src/storage：SQLite 持久化实现
- src/scheduler：数据库驱动的计划任务调度器
- src/skills：技能发现、加载与运行时管理
- src/protocol：WebSocket 入站 / 出站协议结构

## 3. 消息机制

PicoBot 的消息不是简单的“用户文本 + 助手文本”，而是完整的会话事件流。

### 3.1 消息类型

系统中的 ChatMessage 至少包含以下角色：

- user：用户输入
- assistant：模型回复
- system：系统提示、Agent Prompt、调度附加提示、历史压缩摘要
- tool：工具执行结果

此外，assistant 消息还能携带：

- tool_calls：本轮准备调用哪些工具
- reasoning_content：模型的补充推理文本

用户消息也支持附带媒体引用，当前消息结构中已经支持文本与图片 URL 形式的多模态块，以及本地媒体文件路径引用。

### 3.2 消息总线

MessageBus 维护两条异步队列：

- inbound：Channel -> Agent
- outbound：Agent -> Dispatcher -> Channel

这样做的好处是：

- 渠道接入和 Agent 执行解耦
- 可在网关内部统一处理重试、记录、路由和扩展
- 后续增加新渠道时不需要改动核心 Agent 流程

### 3.3 工具调用消息流

当模型发起工具调用时，PicoBot 会按顺序保存：

1. 一条 assistant 消息，记录 tool_calls
2. 一条或多条 tool 消息，记录每个工具返回结果
3. 最终 assistant 汇总回复

这意味着即使进程重启，系统仍然可以恢复：

- 当时模型调用了什么工具
- 工具入参和返回结果是什么
- 最终结论如何形成

## 4. 会话与上下文机制

### 4.1 会话标识

PicoBot 为不同渠道统一维护持久化会话：

- CLI：session_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 下名称为 bash，Windows 下名称为 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：某个绝对时间执行一次
- cron：cron 表达式调度

### 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：健康检查
- /ws：CLI 客户端连接入口
- /：Web UI 前端（已嵌入二进制，无需外部文件）

### 11.3 Web UI

PicoBot 内置 Web 前端，在编译时已打包进二进制文件。启动网关后可直接访问：

```text
http://127.0.0.1:19876/
```

特性：
- 前端代码编译时嵌入 exe，无需携带 `static/` 目录
- 支持 SPA 前端路由
- 开发模式下可通过 `STATIC_DIR` 环境变量使用磁盘文件（支持热更新）

### 11.4 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": []
	}
}
```

常用配置项：

- providers：Provider 连接信息，包含 llm_timeout_secs（LLM 调用超时，默认 120 秒）和 memory_maintenance_timeout_secs（记忆维护超时，默认 600 秒）
- models：模型参数，包括上下文窗口估算所用的 context_window_tokens
- agents：Agent 级别的工具轮次、工具结果裁剪与上下文裁剪参数
- 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 构建与启动

> **Windows 用户注意：** Windows 默认不包含 `make` 命令。你可以通过以下方式之一来构建：
>
> - 直接使用下方 "Windows（PowerShell）" 对应的手动命令（无需额外安装）
> - 安装 make：`choco install make`（Chocolatey）或 `scoop install make`（Scoop）
> - 在 Git Bash / WSL 中运行 `bash build.sh` 或 `make`

**构建（包含前端）：**

Linux / macOS：

```bash
# 使用 Makefile（推荐）
make build

# 或手动构建
cd web && npm install && npm run build
cargo build --release
```

Windows（PowerShell）：

```powershell
# 安装前端依赖并构建
cd web; npm install; npm run build; cd ..

# 构建 Rust 后端
cargo build --release
```

构建产物：
- 前端输出到 `static/` 目录
- 编译时通过 `rust-embed` 嵌入二进制文件
- 最终 exe 约 25MB，包含完整前端

**启动网关：**

Linux / macOS：

```bash
# 生产模式（使用嵌入的前端）
cargo run --release -- gateway

# 开发模式（使用磁盘文件，支持前端热更新）
cd web && npm run dev &
STATIC_DIR=static cargo run -- gateway
```

Windows（PowerShell）：

```powershell
# 生产模式（使用嵌入的前端）
cargo run --release -- gateway

# 开发模式（使用磁盘文件，支持前端热更新，需要开两个终端）
# 终端1：启动前端开发服务器
cd web; npm run dev
# 终端2：启动网关
$env:STATIC_DIR="static"; cargo run -- gateway
```

**常用 Makefile 命令对照：**

| Makefile 命令 | Linux / macOS | Windows（PowerShell） |
|------|------|------|
| `make install` | `cd web && npm install` | `cd web; npm install` |
| `make dev-backend` | `cargo run -- gateway` | `cargo run -- gateway` |
| `make dev-frontend` | `cd web && npm run dev` | `cd web; npm run dev` |
| `make build` | `cd web && npm run build && cargo build --release` | `cd web; npm run build; cd ..; cargo build --release` |
| `make run` | `cargo run --release -- gateway` | `cargo run --release -- gateway` |
| `make check` | `cd web && npm run build && cargo check && cargo clippy` | `cd web; npm run build; cd ..; cargo check; cargo clippy` |

### 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.rs：Agent 执行服务
- src/storage/mod.rs：SQLite 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 包、云服务集成）
