# 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 - `` - 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 - 切换到指定会话 - /rename - 重命名当前会话 - /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 构建与启动 **构建(包含前端):** ```bash # 使用 Makefile(推荐) make build # 或手动构建 cd web && npm install && npm run build cargo build --release ``` 构建产物: - 前端输出到 `static/` 目录 - 编译时通过 `rust-embed` 嵌入二进制文件 - 最终 exe 约 25MB,包含完整前端 **启动网关:** ```bash # 生产模式(使用嵌入的前端) cargo run --release -- gateway # 开发模式(使用磁盘文件,支持前端热更新) cd web && npm run dev & STATIC_DIR=static 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.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 包、云服务集成)