更新readme

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
ooodc 2026-04-26 17:25:45 +08:00
parent 3045a6b596
commit 5518185868

644
README.md
View File

@ -1,78 +1,330 @@
PicoBot
# PicoBot
Skills (initial implementation)
PicoBot 是一个用 Rust 构建的多通道 Agent 网关。它把消息接入、LLM 调用、工具执行、会话持久化、长期记忆、技能系统和计划任务整合到同一个运行时里,适合做本地 Agent、团队机器人或带长期上下文的自动化助手。
Agent profile injection
当前代码库已经实现以下核心能力:
PicoBot maintains a persistent agent profile file at ~/.picobot/agent/AGENT.md.
- 基于 Gateway 的统一消息入口,支持 WebSocket CLI 与飞书通道
- 面向工具调用的 Agent 循环,支持多轮 tool calling
- SQLite 持久化会话、消息、长期记忆、技能事件和调度任务
- 基于用户维度的长期记忆检索与写入机制
- 基于 SKILL.md 的项目级 / 用户级技能加载与运行时管理
- 定时任务系统,支持延迟、周期、绝对时间和 cron 调度
- 超长上下文压缩与历史摘要
- 持久化 Agent 配置文件注入与周期性重注入
Behavior:
- The directory ~/.picobot/agent is created automatically when needed.
- If AGENT.md does not exist yet, PicoBot creates it with a default profile.
- When the active conversation is empty or has just been reset, AGENT.md is loaded as the first system message in the active history.
- After every configured number of user turns, PicoBot injects the latest AGENT.md content again before the next user message is appended.
## 1. 项目定位
Config:
- Set gateway.agent_prompt_reinject_every in ~/.picobot/config.json.
- Default value is 100.
- Set it to 0 to disable periodic reinjection.
PicoBot 的设计目标不是“只会聊天”的单进程 Bot而是一个可持续运行的 Agent 基础设施:
This profile is persisted in session history, while the skills index system prompt is still injected dynamically by AgentLoop.
- 消息从不同渠道进入统一总线
- SessionManager 负责会话路由、上下文恢复、工具执行和回复生成
- SQLite 作为事实来源保存跨重启状态
- Agent 在每轮推理时可以读取文件、执行命令、发 HTTP 请求、读写记忆、管理技能和调度任务
PicoBot now supports filesystem skills.
如果你需要一个“带长期记忆、可调用工具、可接入飞书、可计划执行任务”的机器人,这个项目的实现面基本够用。
Skill discovery locations:
- Project skills: .picobot/skills/*/SKILL.md
- User skills: ~/.picobot/skills/*/SKILL.md
## 2. 核心架构
Minimal required SKILL.md format:
整体链路可以概括为:
1. Channel 接收外部消息
2. MessageBus 将消息送入统一的 inbound 队列
3. Gateway 启动的 inbound processor 调用 SessionManager 处理消息
4. SessionManager 加载持久化历史、注入系统提示、运行 AgentLoop、执行工具调用
5. 生成的 assistant / tool / system 消息写入 SQLite
6. OutboundDispatcher 将结果投递到目标通道
主要模块如下:
- src/gateway网关入口、HTTP 健康检查、WebSocket 服务、Session 管理
- src/bus消息总线与消息结构定义
- src/agentAgentLoop 与上下文压缩器
- src/providers不同 LLM Provider 的统一抽象,当前支持 openai 和 anthropic
- src/tools内置工具集合
- src/storageSQLite 持久化实现
- src/channels渠道适配层当前已有飞书通道
- src/scheduler数据库驱动的计划任务调度器
- src/skills技能发现、加载与运行时管理
- src/client / src/cli本地 CLI 客户端和交互命令
## 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 对应模型配置里的 token_limit。
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 本身已经过长,会先按 context_summary_max_chars 截断,再交给模型总结。
8. 摘要调用和主对话使用同一个 provider提示词明确要求保留
用户请求、关键标识符、文件路径、URL、工具调用、命令、结果、错误、决策、偏好和当前任务状态。
如果摘要调用失败,会退化为直接截断 transcript而不会中断主流程。
9. 摘要结果会被包装成一条新的 system 消息,并打上 SYSTEM_CONTEXT_HISTORY_COMPACTION 标记,内容前缀为 [Compressed History]。
10. 后台提交阶段不会直接修改旧消息,而是向消息表尾部追加一段“新的活动段”:
依次写入保留的关键 system 消息、压缩摘要消息、最近保留的消息,以及在压缩快照之后新产生的 delta 消息。
11. 提交成功后sessions.reset_cutoff_seq 会被推进到压缩前的最大 seq。
这样旧消息仍然留在数据库里用于审计或全量导出,但默认恢复到运行时上下文时,只会加载新的活动段。
12. 为避免并发覆盖,压缩提交前会检查快照是否过期:
如果 reset_cutoff_seq 已变化,或者压缩期间又有更新导致快照不再匹配,本次压缩会跳过,不会覆盖较新的上下文。
13. 压缩提交成功后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
- 每天本地时间 03:00 运行
这个任务以 internal_event 的方式触发 memory_maintenance 事件。触发后,系统会遍历所有已有用户 scope逐个执行记忆维护并在全部处理完成后统一更新 AGENT.md 中的“用户记忆摘要”托管区块。
## 7. 技能机制
PicoBot 支持基于文件系统的技能系统,用来给 Agent 注入某一类任务的专门说明。
### 7.1 技能发现位置
- 项目级技能:.picobot/skills/*/SKILL.md
- 用户级技能:~/.picobot/skills/*/SKILL.md
### 7.2 最小 SKILL.md 格式
```md
---
description: Summarize code architecture for Rust projects
description: 用于总结 Rust 项目架构
---
Optional detailed instructions go here.
Notes:
- The only required frontmatter field is description.
- If name is missing, the folder name is used as the skill name.
- Invalid skill files are skipped with warning logs.
这里写详细说明。
```
How it is injected:
- AgentLoop adds a system message containing the available skills list (name + description).
- The model can call tool skill_activate with {"name":"<skill-name>"}.
- PicoBot returns the skill body as tool output so the model can follow detailed instructions.
约束:
Skill management tool
- frontmatter 中必须有 description
- name 可省略,省略时使用目录名
- 非法技能文件会被跳过,并记录 warning 日志
PicoBot exposes a built-in tool named skill_manage for runtime skill administration.
PicoBot also exposes a read-only tool named skill_list for listing discovered skills without mutation.
### 7.3 技能注入方式
Supported actions:
- list: List discovered skills
- get: Read one skill by name
- create: Create a skill under project or user scope
- update: Update description and/or body for an existing skill
- delete: Delete a skill directory
- reload: Re-scan skill directories and refresh the in-memory catalog
运行时会发生两件事:
Defaults:
- scope defaults to project
- reload defaults to true for create, update, and delete
- AgentLoop 动态注入“技能索引”系统提示,列出当前可用技能的名称和描述
- 模型可以通过工具读取具体技能内容并按技能说明执行任务
Example payloads:
### 7.4 技能管理工具
skill_list takes no parameters.
内置工具:
{"action":"list"}
{"action":"create","scope":"project","name":"demo-skill","description":"Use when summarizing a Rust crate","body":"Step 1..."}
{"action":"update","scope":"project","name":"demo-skill","description":"Use when reviewing a Rust crate"}
{"action":"delete","scope":"project","name":"demo-skill"}
{"action":"reload"}
- skill_list只读列出技能
- skill_manage运行时创建、更新、删除、读取和重载技能
Config (optional)
skill_manage 支持的 action
Add skills in config.json:
- list
- get
- create
- update
- delete
- reload
skills 配置示例:
```json
{
"skills": {
"enabled": true,
@ -81,21 +333,73 @@ Add skills in config.json:
"max_listed_skills": 32
}
}
```
Scheduler
## 8. 工具机制
PicoBot now includes a DB-backed scheduler for heartbeat, delayed jobs, interval jobs, one-shot absolute-time jobs, and cron jobs.
PicoBot 的 Agent 是围绕工具调用构建的。当前默认注册的工具包括:
Current behavior:
- Scheduler runs as a background loop inside gateway lifecycle.
- Job definitions and runtime state are persisted in SQLite instead of JSON files.
- Supported schedule types: delay, interval, at, cron.
- Supported job kinds: internal_event, outbound_message, agent_task.
- Built-in internal event: session_cleanup, used to clear expired in-memory channel sessions.
- Built-in management tool: scheduler_manage.
- calculator简单数学计算
- file_read读取文件
- file_write写文件
- file_edit编辑文件
- memory_search读取长期记忆
- memory_manage写入 / 更新 / 删除长期记忆
- scheduler_manage管理调度任务
- skill_list列出技能
- skill_manage管理技能
- bash执行 shell 命令
- http_request发起 HTTP 请求
- web_fetch抓取网页正文
Config example:
其中:
- 文件工具适合做代码库和文档操作
- 记忆工具适合维持长期用户画像
- scheduler_manage 允许 Agent 自主创建后续计划任务
- bash / http_request / web_fetch 让 Agent 具备更强的外部交互能力
## 9. 调度器机制
PicoBot 带有一个基于 SQLite 的调度器,而不是纯内存或 JSON 文件驱动的任务系统。
### 9.1 支持的调度类型
- delay延迟执行一次
- interval固定间隔执行
- at某个绝对时间执行一次
- croncron 表达式调度
### 9.2 支持的任务类型
- internal_event内部事件
- outbound_message直接向目标通道发消息
- agent_task构造一次合成用户输入复用完整 Agent 流程执行
agent_task 会复用正常链路中的这些能力:
- 历史加载
- Agent Prompt 注入
- 工具调用
- 会话持久化
- 渠道消息下发
### 9.3 运行时管理
通过 scheduler_manage 可以进行:
- list
- get
- put
- delete
- pause
- resume
任务定义和状态都会写进 SQLite所以重启后仍可恢复。
调度器配置示例:
```json
{
"scheduler": {
"enabled": true,
@ -135,35 +439,199 @@ Config example:
"source": "scheduler"
}
}
},
{
"id": "daily.reminder",
"kind": "outbound_message",
"schedule": {
"type": "cron",
"expression": "0 9 * * *"
},
"target": {
"channel": "feishu",
"chat_id": "oc_xxx"
},
"payload": {
"content": "每日提醒"
}
}
]
}
}
```
Runtime management:
- Use scheduler_manage with action=list|get|put|delete|pause|resume.
- Jobs created by the tool are written into SQLite and picked up by the scheduler loop.
- Config-defined jobs are also synced into SQLite on startup.
- agent_task reuses the normal agent pipeline: it creates a synthetic user turn from payload.prompt and runs tools, persistence, and outbound rendering through SessionManager.
- agent_task payload fields:
- prompt: required, synthetic user input.
- agent: optional, choose which configured agent definition to use. default or any configured agent name.
- fresh_session: optional, when true reset the active chat segment before running.
- system_prompt: optional, append a task-specific system message before the synthetic user turn.
- sender_id: optional, overrides the synthetic sender id used for tool context and memory scoping.
- metadata: optional, attached to outbound messages emitted by this task.
## 10. 渠道与运行方式
### 10.1 当前支持的通道
- WebSocket CLI 客户端
- 飞书通道
### 10.2 Gateway 接口
网关当前暴露:
- /health健康检查
- /wsCLI 客户端连接入口
### 10.3 CLI 使用方式
程序提供两个主命令:
```bash
cargo run -- gateway
cargo run -- agent
```
含义:
- gateway启动网关服务
- agent以 WebSocket 方式连接网关,进入本地 CLI 会话
CLI 中已实现的交互命令包括:
- /new [title]
- /reset
- /sessions
- /use <session>
- /rename <title>
- /archive
- /delete
- /clear
- /quit
## 11. 配置说明
配置默认从以下位置加载:
1. ~/.picobot/config.json
2. 如果不存在,则回退到当前工作目录下的 config.json
配置加载前还会先读取环境变量,并替换配置中的占位符。
最小可运行配置示例:
```json
{
"providers": {
"default": {
"type": "openai",
"base_url": "<OPENAI_BASE_URL>",
"api_key": "<OPENAI_API_KEY>",
"extra_headers": {}
}
},
"models": {
"default": {
"model_id": "<OPENAI_MODEL_NAME>",
"temperature": 0.2
}
},
"agents": {
"default": {
"provider": "default",
"model": "default",
"tool_result_max_chars": 20000,
"context_summary_max_chars": 20000,
"context_tool_result_trim_chars": 2000
}
},
"gateway": {
"host": "0.0.0.0",
"port": 19876,
"agent_prompt_reinject_every": 100
},
"scheduler": {
"enabled": true,
"tick_resolution_ms": 1000,
"misfire_policy": "skip",
"jobs": []
}
}
```
常用配置项:
- providersProvider 连接信息
- models模型参数
- agentsAgent 级别的工具轮次、token 上限和上下文裁剪参数
- gateway监听地址、端口、工具结果展示、会话 TTL、Prompt 重注入策略
- scheduler调度器开关和任务列表
- channels飞书等通道配置
- skills技能来源与索引限制
- time.timezone时区默认应使用 IANA 时区名,例如 Asia/Shanghai
## 12. 快速开始
### 12.1 准备配置
1. 复制并修改 config.json或把配置放到 ~/.picobot/config.json
2. 配置好 Provider 的 base_url、api_key、model_id
3. 如果要接飞书,再补充 channels.feishu 配置
### 12.2 启动网关
```bash
cargo run -- gateway
```
### 12.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
```
### 12.4 检查服务状态
```bash
curl http://127.0.0.1:19876/health
```
## 13. 目录结构
```text
PicoBot/
├── src/
│ ├── agent/ # AgentLoop、上下文压缩
│ ├── bus/ # 消息总线与消息结构
│ ├── channels/ # 渠道适配
│ ├── cli/ # CLI 输入命令
│ ├── client/ # WebSocket CLI 客户端
│ ├── config/ # 配置解析
│ ├── gateway/ # Gateway、SessionManager、WS/HTTP
│ ├── providers/ # OpenAI / Anthropic Provider
│ ├── scheduler/ # 定时任务系统
│ ├── skills/ # 技能运行时
│ ├── storage/ # SQLite 持久化
│ └── tools/ # 内置工具集合
├── docs/
│ ├── IMPLEMENTATION_LOG.md
│ └── PERSISTENCE.md
├── tests/
└── config.json
```
## 14. 测试与维护建议
当前 tests 目录中已经包含 Provider 集成测试和工具调用相关测试,但部分测试依赖外部 API Key需要先准备 tests/test.env。
建议维护时重点关注:
- docs/PERSISTENCE.md持久化结构是否与代码一致
- src/gateway/session.rs消息流、工具注册、记忆维护、会话恢复主逻辑
- src/storage/mod.rsSQLite schema 变更
- src/config/mod.rs配置项变更是否同步到 README
## 15. 总结
PicoBot 当前已经具备一个可长期运行 Agent 系统的关键组件:
- 有入口Gateway + Channel
- 有状态SQLite + Session 恢复
- 有能力:工具调用 + 技能系统
- 有记忆:长期记忆 + 自动维护摘要
- 有计划Scheduler + agent_task
如果后续继续演进,比较自然的方向会是:
- 增加更多渠道
- 增强多模态能力
- 完善记忆检索排序与冲突消解
- 为不同 Agent 提供更清晰的配置和权限隔离