更新文档
This commit is contained in:
parent
18f1e47f77
commit
901b622b40
66
AGENTS.md
66
AGENTS.md
@ -1,5 +1,7 @@
|
|||||||
# PicoBot
|
# PicoBot
|
||||||
|
|
||||||
|
This file is the operational contract for coding agents working in this repository. Read [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) before making architectural or lifecycle changes. Code and tests are the final source of truth; update the document when an architectural invariant changes.
|
||||||
|
|
||||||
## Build & Run
|
## Build & Run
|
||||||
|
|
||||||
- `cargo build` — build the binary
|
- `cargo build` — build the binary
|
||||||
@ -8,16 +10,18 @@
|
|||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
- Config load order: `~/.picobot/config.json` then fallback to `./config.json` (`src/config/mod.rs:237-267`)
|
- Config load order: `~/.picobot/config.json` then fallback to `./config.json` (`Config::load_default` in `src/config/mod.rs`)
|
||||||
- `.env` (cwd) is loaded with a custom parser, not via dotenv crate; env var placeholders `<VAR_NAME>` in config JSON are substituted
|
- `.env` (cwd) is loaded with a custom parser, not via dotenv crate; env var placeholders `<VAR_NAME>` in config JSON are substituted
|
||||||
- Config example: `resources/templates/config.example.json` (released to `~/.picobot/` on first run)
|
- Config example: `resources/templates/config.example.json` (released to `~/.picobot/` on first run)
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
- `cargo test --lib` — run unit tests (runs all `#[test]` in `src/`)
|
- `cargo test --lib` — run unit tests (runs all `#[test]` in `src/`)
|
||||||
- `cargo test --test test_integration -- --ignored` — run integration tests (also `test_tool_calling`, `test_request_format`)
|
- `cargo clippy --all-targets --all-features -- -D warnings` — required for Rust changes
|
||||||
- **All** integration tests require `tests/test.env` with real API keys; copy from `tests/test.env.example` and fill in keys
|
- `cargo test --test test_scheduler` and `cargo test --test test_request_format` — offline integration/protocol tests
|
||||||
- Integration tests are `#[ignore]` by default; use `-- --ignored` to run them
|
- `cargo test --test test_integration -- --ignored` and `cargo test --test test_tool_calling -- --ignored` — model API integration tests
|
||||||
|
- API-backed tests require `tests/test.env` with real API keys; copy from `tests/test.env.example` and fill in keys
|
||||||
|
- API-backed tests are `#[ignore]` by default; use `-- --ignored` to run them
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
@ -33,9 +37,12 @@
|
|||||||
### Core Data Flow
|
### Core Data Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionManager → MessageBus → OutboundDispatcher → Channel
|
Channel → MessageBus.inbound → Gateway processor → SessionManager → per-session worker → AgentLoop
|
||||||
↑
|
↑ │
|
||||||
ControlChannel ──→ SessionManager (dialog ops: create/switch/archive/delete)
|
└── Channel ← OutboundDispatcher ← per-conversation lane ← MessageBus.outbound
|
||||||
|
|
||||||
|
WebSocket/Channel → MessageBus.control → Gateway processor → SessionManager (dialog operations)
|
||||||
|
Scheduler → SessionManager.handle_cron_message → AgentLoop → send_message tool
|
||||||
```
|
```
|
||||||
|
|
||||||
### Modules
|
### Modules
|
||||||
@ -45,8 +52,8 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
|
|||||||
| `gateway` | Server lifecycle, HTTP/WS endpoints, owns `GatewayState` | `GatewayState`, `run()` |
|
| `gateway` | Server lifecycle, HTTP/WS endpoints, owns `GatewayState` | `GatewayState`, `run()` |
|
||||||
| `client` | TUI rendering, WebSocket client for CLI chat | `App`, `run()` |
|
| `client` | TUI rendering, WebSocket client for CLI chat | `App`, `run()` |
|
||||||
| `channels` | External integrations (Feishu, CLI chat) | `ChannelManager`, `Channel` trait |
|
| `channels` | External integrations (Feishu, CLI chat) | `ChannelManager`, `Channel` trait |
|
||||||
| `bus` | Async message queue (inbound/outbound/control channels) | `MessageBus`, `InboundMessage`, `OutboundMessage`, `ControlMessage` |
|
| `bus` | Bounded async queues and ordered outbound delivery lanes | `MessageBus`, `OutboundDispatcher`, `InboundMessage`, `OutboundMessage`, `ControlMessage` |
|
||||||
| `session` | Conversation session lifecycle, dialog operations | `SessionManager`, `Session` |
|
| `session` | Conversation lifecycle, dialog operations, per-session serialization, persistence coordination | `SessionManager`, `Session` |
|
||||||
| `agent` | LLM call loop, tool execution, context compression | `AgentLoop` |
|
| `agent` | LLM call loop, tool execution, context compression | `AgentLoop` |
|
||||||
| `providers` | LLM API clients (OpenAI-compatible, Anthropic) | `LLMProvider` trait, factory `create_provider()` |
|
| `providers` | LLM API clients (OpenAI-compatible, Anthropic) | `LLMProvider` trait, factory `create_provider()` |
|
||||||
| `tools` | Agent tools (bash, file ops, http, web, get_skill) | `ToolRegistry`, `Tool` trait |
|
| `tools` | Agent tools (bash, file ops, http, web, get_skill) | `ToolRegistry`, `Tool` trait |
|
||||||
@ -57,22 +64,51 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
|
|||||||
| `protocol` | WebSocket protocol message types | `WsInbound`, `WsOutbound`, `SessionSummary` |
|
| `protocol` | WebSocket protocol message types | `WsInbound`, `WsOutbound`, `SessionSummary` |
|
||||||
| `config` | Config loading, env substitution, path resolution | `Config`, `LLMProviderConfig` |
|
| `config` | Config loading, env substitution, path resolution | `Config`, `LLMProviderConfig` |
|
||||||
| `logging` | Tracing initialization with file rotation | `init_logging()`, `init_logging_console_only()` |
|
| `logging` | Tracing initialization with file rotation | `init_logging()`, `init_logging_console_only()` |
|
||||||
|
| `task_supervisor` | Owns, cancels, and boundedly joins gateway background tasks | `TaskSupervisor` |
|
||||||
|
|
||||||
### Functional Boundaries
|
### Functional Boundaries
|
||||||
|
|
||||||
- **Channels** only send/receive messages via `MessageBus`; they know nothing about sessions or LLM
|
- **Channels** only send/receive messages via `MessageBus`; they know nothing about sessions or LLM
|
||||||
- **MessageBus** is a pure async queue; it routes nothing, just passes messages
|
- **MessageBus** owns bounded inbound/outbound/control queues; outbound routing and retries belong to `OutboundDispatcher`, not the queue
|
||||||
- **SessionManager** owns session state and dialog operations; it does NOT call LLM directly
|
- **SessionManager** owns session state, dialog operations, context construction, per-session work queues, and persistence coordination
|
||||||
- SessionManager is responsible for injecting skills prompt into conversation history
|
- **AgentLoop** is stateless across turns; it receives prepared history, calls LLM providers, executes tools, and returns one result
|
||||||
- **AgentLoop** receives dialog events from `SessionManager`, calls LLM via `providers`, executes tools, returns text responses
|
|
||||||
- AgentLoop is stateless; all state is managed by Session/SessionManager
|
|
||||||
- **Providers** are pure HTTP clients; no bus/session/channel awareness
|
- **Providers** are pure HTTP clients; no bus/session/channel awareness
|
||||||
- **Tools** are executed by `AgentLoop`; they receive raw arguments and return string results
|
- **Tools** are executed by `AgentLoop`; they receive raw arguments and return string results
|
||||||
|
|
||||||
|
### Concurrency and Lifecycle Invariants
|
||||||
|
|
||||||
|
- Messages in one session are processed serially through a bounded queue; different sessions may run concurrently
|
||||||
|
- Outbound messages are ordered per `(channel, chat_id)`; a slow destination must not block unrelated destinations
|
||||||
|
- Never hold a Session mutex across model, network, or database I/O unless a documented invariant requires it
|
||||||
|
- Slow work derived from session state must validate `worker_generation`/`state_version` before committing results
|
||||||
|
- Related durable mutations use Storage transaction APIs; persistence failure must not leave silent memory/database divergence
|
||||||
|
- Long-lived gateway tasks must be owned by `TaskSupervisor`; connection-local tasks must be explicitly joined or aborted by their owner
|
||||||
|
- Connection, retry sleep, queue wait, and shutdown join paths must observe cancellation and have hard time bounds
|
||||||
|
- Retry only errors explicitly classified as transient; permanent failures must surface immediately
|
||||||
|
- Never log secrets, authorization headers, or full connection URLs containing temporary credentials
|
||||||
|
- The workspace cwd is not a filesystem sandbox: default file tools accept absolute paths and Bash inherits process permissions; add explicit canonical-path/process isolation when a hard boundary is required
|
||||||
|
|
||||||
### Key Constraints
|
### Key Constraints
|
||||||
|
|
||||||
- Gateway **changes working directory** to workspace on startup (`src/gateway/mod.rs:31`)
|
- Gateway **changes working directory** to workspace in `GatewayState::new` (`src/gateway/mod.rs`)
|
||||||
- Session/message persistence uses SQLite via `sqlx`; DB stored in workspace as `picobot.db` by default
|
- Session/message persistence uses SQLite via `sqlx`; DB stored in workspace as `picobot.db` by default
|
||||||
- `ChannelManager` owns the `MessageBus` and all channel instances
|
- `ChannelManager` owns the `MessageBus` and all channel instances
|
||||||
- `OutboundDispatcher` routes outbound messages to the correct channel via `ChannelManager`
|
- `OutboundDispatcher` routes outbound messages to the correct channel via `ChannelManager`
|
||||||
- Config `.env` loading uses `unsafe { env::set_var(...) }` — don't refactor to safer patterns without understanding side effects
|
- Config `.env` loading uses `unsafe { env::set_var(...) }` — don't refactor to safer patterns without understanding side effects
|
||||||
|
|
||||||
|
## Change Workflow
|
||||||
|
|
||||||
|
1. Inspect the relevant implementation, tests, and `docs/ARCHITECTURE.md`; do not rely on names or old reports alone.
|
||||||
|
2. Search `reference/` only for comparison. Never edit it or copy behavior without checking PicoBot's boundaries.
|
||||||
|
3. Preserve unrelated user changes in a dirty worktree. Use `rg` for search and `apply_patch` for edits.
|
||||||
|
4. Add regression tests for bugs, especially cancellation, timeout, queue saturation, stale state, persistence failure, and retry classification.
|
||||||
|
5. For Rust changes run targeted tests, `cargo test --lib`, Clippy with warnings denied, and `cargo build`. Integration tests require real credentials.
|
||||||
|
6. For documentation-only changes verify links, commands, paths, and `git diff --check`.
|
||||||
|
7. Update README, this file, and the architecture document together when public behavior or an architectural invariant changes.
|
||||||
|
|
||||||
|
## Documentation Roles
|
||||||
|
|
||||||
|
- `README.md` — user-facing overview, setup, capabilities, and navigation
|
||||||
|
- `docs/ARCHITECTURE.md` — maintainer-facing runtime design, invariants, lifecycle, and extension guidance
|
||||||
|
- `AGENTS.md` — concise operational rules for repository agents
|
||||||
|
- `resources/skills/about-picobot/references/` — runtime knowledge shipped to PicoBot; update it only when the assistant's built-in product knowledge must change
|
||||||
|
|||||||
27
README.md
27
README.md
@ -4,7 +4,7 @@ PicoBot 是一个用 Rust 编写的个人 AI 助手运行时。它在本地启
|
|||||||
|
|
||||||
它更像一个可扩展的“个人助手操作系统”:渠道负责收发消息,SessionManager 负责会话和上下文,AgentLoop 负责模型与工具循环,Storage 负责可靠落盘。
|
它更像一个可扩展的“个人助手操作系统”:渠道负责收发消息,SessionManager 负责会话和上下文,AgentLoop 负责模型与工具循环,Storage 负责可靠落盘。
|
||||||
|
|
||||||

|
完整的组件边界、并发不变量、启动/关停顺序和扩展指南见 [架构文档](docs/ARCHITECTURE.md)。
|
||||||
|
|
||||||
## 适合做什么
|
## 适合做什么
|
||||||
|
|
||||||
@ -95,7 +95,9 @@ CLI 默认连接 `ws://127.0.0.1:19876/ws`。如需指定地址,可使用 `--g
|
|||||||
|
|
||||||
用户消息进入 PicoBot 后,会被转换为统一的 inbound message,经由 MessageBus 交给 SessionManager。SessionManager 选择当前 dialog、组装上下文、调用 AgentLoop;AgentLoop 调用模型和工具,最终响应通过 outbound bus 回到原渠道。
|
用户消息进入 PicoBot 后,会被转换为统一的 inbound message,经由 MessageBus 交给 SessionManager。SessionManager 选择当前 dialog、组装上下文、调用 AgentLoop;AgentLoop 调用模型和工具,最终响应通过 outbound bus 回到原渠道。
|
||||||
|
|
||||||

|
详细时序和失败语义见 [架构文档:消息与控制数据流](docs/ARCHITECTURE.md#4-消息与控制数据流)。
|
||||||
|
|
||||||
|
同一 session 的普通消息由专属有界队列串行处理,不同 session 可以并发;出站消息按 `(channel, chat_id)` 分 lane 保序,慢渠道不会阻塞其他目标。Gateway 的长生命周期任务统一由 `TaskSupervisor` 取消和限时回收。
|
||||||
|
|
||||||
核心边界:
|
核心边界:
|
||||||
|
|
||||||
@ -111,6 +113,7 @@ CLI 默认连接 `ws://127.0.0.1:19876/ws`。如需指定地址,可使用 `--g
|
|||||||
| `scheduler` | 轮询 Cron 任务并把任务 prompt 送入目标会话 |
|
| `scheduler` | 轮询 Cron 任务并把任务 prompt 送入目标会话 |
|
||||||
| `skills` | 加载 Skill,并把 Skill 指南注入系统提示 |
|
| `skills` | 加载 Skill,并把 Skill 指南注入系统提示 |
|
||||||
| `mcp` | 连接 MCP Server,将远端工具包装成普通 Tool |
|
| `mcp` | 连接 MCP Server,将远端工具包装成普通 Tool |
|
||||||
|
| `task_supervisor` | 统一管理 Gateway 后台任务的取消和有界关停 |
|
||||||
|
|
||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
@ -156,7 +159,7 @@ PicoBot 有两类记忆:
|
|||||||
| Knowledge | 偏好、事实、项目规则、长期可复用信息 | 长期保留,手动删除 |
|
| Knowledge | 偏好、事实、项目规则、长期可复用信息 | 长期保留,手动删除 |
|
||||||
| Timeline | 长对话压缩后的历史摘要 | 默认保留 90 天 |
|
| Timeline | 长对话压缩后的历史摘要 | 默认保留 90 天 |
|
||||||
|
|
||||||
每轮处理用户消息时,MemoryManager 会按用户输入召回最多 `memory.recall_limit` 条 Knowledge,并注入系统提示。上下文压缩产生的摘要会保存为 Timeline,后续可通过 `timeline_recall` 工具检索。
|
每轮处理用户消息时,MemoryManager 会按用户输入召回 Knowledge,并作为运行时上下文附加到本轮用户消息。当前召回上限固定为 5;`memory.recall_limit` 已支持解析但尚未接入 worker。上下文压缩产生的摘要会保存为 Timeline,后续可通过 `timeline_recall` 工具检索。
|
||||||
|
|
||||||
### 工具
|
### 工具
|
||||||
|
|
||||||
@ -214,7 +217,7 @@ Skill 是包含 `SKILL.md` 的目录。加载优先级从高到低:
|
|||||||
| `gateway.max_concurrent_background_tasks` | `10` |
|
| `gateway.max_concurrent_background_tasks` | `10` |
|
||||||
| `gateway.scheduler.enabled` | `true` |
|
| `gateway.scheduler.enabled` | `true` |
|
||||||
| `client.gateway_url` | `ws://127.0.0.1:19876/ws` |
|
| `client.gateway_url` | `ws://127.0.0.1:19876/ws` |
|
||||||
| `memory.recall_limit` | `5` |
|
| `memory.recall_limit` | `5`(当前运行时固定为 5) |
|
||||||
| `memory.timeline_retention_days` | `90` |
|
| `memory.timeline_retention_days` | `90` |
|
||||||
| `mcp.tool_timeout_secs` | `180` |
|
| `mcp.tool_timeout_secs` | `180` |
|
||||||
| `browser.enabled` | `false` |
|
| `browser.enabled` | `false` |
|
||||||
@ -253,14 +256,17 @@ Outbound 消息类型包括 `assistant_response`、`error`、`session_establishe
|
|||||||
# 单元测试
|
# 单元测试
|
||||||
cargo test --lib
|
cargo test --lib
|
||||||
|
|
||||||
# 集成测试需要 tests/test.env 中有真实 API key
|
# 离线集成/协议测试
|
||||||
|
cargo test --test test_scheduler
|
||||||
|
cargo test --test test_request_format
|
||||||
|
|
||||||
|
# 模型 API 集成测试需要 tests/test.env 中有真实 API key
|
||||||
cp tests/test.env.example tests/test.env
|
cp tests/test.env.example tests/test.env
|
||||||
cargo test --test test_integration -- --ignored
|
cargo test --test test_integration -- --ignored
|
||||||
cargo test --test test_tool_calling -- --ignored
|
cargo test --test test_tool_calling -- --ignored
|
||||||
cargo test --test test_request_format -- --ignored
|
|
||||||
```
|
```
|
||||||
|
|
||||||
集成测试默认 `#[ignore]`,因为它们会真实调用模型 API。
|
会真实调用模型 API 的测试标记为 `#[ignore]`;离线集成测试默认执行。
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
@ -281,11 +287,12 @@ src/
|
|||||||
skills/ Skill 加载和内置 Skill 安装
|
skills/ Skill 加载和内置 Skill 安装
|
||||||
storage/ SQLite schema 和 CRUD
|
storage/ SQLite schema 和 CRUD
|
||||||
tools/ Agent 工具实现
|
tools/ Agent 工具实现
|
||||||
|
task_supervisor.rs Gateway 后台任务的生命周期管理
|
||||||
resources/
|
resources/
|
||||||
skills/ 构建时嵌入的内置 Skills
|
skills/ 构建时嵌入的内置 Skills
|
||||||
templates/ 首次运行释放的配置和用户模板
|
templates/ 首次运行释放的配置和用户模板
|
||||||
tests/ 单元测试和 ignored 集成测试
|
tests/ 单元测试和 ignored 集成测试
|
||||||
docs/ 分析报告、文档插图和补充资料
|
docs/ 面向维护者和 Agent 的架构与开发文档
|
||||||
```
|
```
|
||||||
|
|
||||||
## 关键依赖
|
## 关键依赖
|
||||||
@ -304,9 +311,9 @@ docs/ 分析报告、文档插图和补充资料
|
|||||||
|
|
||||||
## 进一步阅读
|
## 进一步阅读
|
||||||
|
|
||||||
- [架构机制](resources/skills/about-picobot/references/architecture.md)
|
- [维护者架构文档](docs/ARCHITECTURE.md)
|
||||||
|
- [内置 Skill:架构机制](resources/skills/about-picobot/references/architecture.md)
|
||||||
- [配置说明](resources/skills/about-picobot/references/config.md)
|
- [配置说明](resources/skills/about-picobot/references/config.md)
|
||||||
- [命令说明](resources/skills/about-picobot/references/commands.md)
|
- [命令说明](resources/skills/about-picobot/references/commands.md)
|
||||||
- [工具说明](resources/skills/about-picobot/references/tools.md)
|
- [工具说明](resources/skills/about-picobot/references/tools.md)
|
||||||
- [数据库结构](resources/skills/about-picobot/references/db-schema.md)
|
- [数据库结构](resources/skills/about-picobot/references/db-schema.md)
|
||||||
- [代码质量分析](docs/CODE_QUALITY_ANALYSIS.md)
|
|
||||||
|
|||||||
281
docs/ARCHITECTURE.md
Normal file
281
docs/ARCHITECTURE.md
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
# PicoBot 架构
|
||||||
|
|
||||||
|
本文档描述 PicoBot 当前实现的运行时边界、数据流、并发模型和演进约束。它面向维护者和后续参与改进的 Agent,是代码架构的主入口;行为细节仍以代码和测试为最终依据。
|
||||||
|
|
||||||
|
## 1. 设计目标
|
||||||
|
|
||||||
|
PicoBot 是一个单进程、异步、可扩展的个人 AI 助手运行时。核心目标是:
|
||||||
|
|
||||||
|
- 用统一消息模型接入不同聊天渠道。
|
||||||
|
- 隔离渠道、会话、模型、工具和持久化职责。
|
||||||
|
- 同一会话内保持消息顺序,不同会话之间允许并发。
|
||||||
|
- 让外部 I/O、后台任务和程序关停都有明确边界。
|
||||||
|
- 通过 SQLite 保存会话、消息、记忆、定时任务及后台任务状态。
|
||||||
|
|
||||||
|
当前不是分布式系统。除调度任务使用数据库租约防止重复领取外,运行时会话状态由单个 Gateway 进程持有。
|
||||||
|
|
||||||
|
## 2. 运行模式与进程边界
|
||||||
|
|
||||||
|
PicoBot 只有一个二进制,提供两种模式:
|
||||||
|
|
||||||
|
| 模式 | 入口 | 职责 |
|
||||||
|
|------|------|------|
|
||||||
|
| Gateway | `cargo run -- gateway` | 组装服务、监听 HTTP/WebSocket、运行渠道、会话、调度器和后台任务 |
|
||||||
|
| CLI client | `cargo run -- chat` | 运行 Ratatui UI,通过 WebSocket 使用 Gateway,不持有业务状态 |
|
||||||
|
|
||||||
|
Gateway 启动时会切换进程工作目录到 `workspace_dir`。因此所有相对路径都应按 workspace 解释,不能假设仍位于源码仓库。
|
||||||
|
|
||||||
|
## 3. 组件关系
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
External[CLI / Feishu] --> Channels[channels]
|
||||||
|
Channels -->|InboundMessage| Bus[MessageBus]
|
||||||
|
Bus --> Processor[Gateway message processor]
|
||||||
|
Processor --> Sessions[SessionManager]
|
||||||
|
Sessions --> Agent[AgentLoop]
|
||||||
|
Agent --> Providers[LLM providers]
|
||||||
|
Agent --> Tools[ToolRegistry / MCP]
|
||||||
|
Sessions <--> Storage[(SQLite)]
|
||||||
|
Sessions -->|OutboundMessage| Bus
|
||||||
|
Scheduler[Scheduler] --> Sessions
|
||||||
|
Bus --> Dispatcher[OutboundDispatcher]
|
||||||
|
Dispatcher --> Channels
|
||||||
|
Supervisor[TaskSupervisor] -. lifecycle .-> Processor
|
||||||
|
Supervisor -. lifecycle .-> Dispatcher
|
||||||
|
Supervisor -. lifecycle .-> Scheduler
|
||||||
|
Supervisor -. lifecycle .-> Sessions
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块职责
|
||||||
|
|
||||||
|
| 模块 | 拥有的职责 | 不应承担的职责 |
|
||||||
|
|------|------------|----------------|
|
||||||
|
| `gateway` | 依赖装配、HTTP/WS 入口、启动和关停顺序 | 业务规则、渠道协议细节 |
|
||||||
|
| `channels` | 外部协议适配、权限检查、媒体收发 | 会话选择、LLM 调用 |
|
||||||
|
| `bus` | 三条有界异步队列与出站投递协调 | 会话路由、业务状态 |
|
||||||
|
| `session` | dialog 路由、会话状态、串行工作队列、上下文和持久化协调 | 外部渠道协议 |
|
||||||
|
| `agent` | 单次无状态模型/工具循环、上下文压缩、子 Agent | 持有 dialog 生命周期 |
|
||||||
|
| `providers` | 把统一请求映射到模型 API | Session、Bus 或 Channel 感知 |
|
||||||
|
| `tools` / `mcp` | 工具定义、注册和执行适配 | 隐式修改会话路由 |
|
||||||
|
| `storage` | SQLite schema、迁移、原子 CRUD | 运行时调度策略 |
|
||||||
|
| `memory` | Knowledge/Timeline 的存取与召回 | 直接驱动消息发送 |
|
||||||
|
| `scheduler` | 领取到期任务、限并发执行、原子记录结果 | 复用聊天会话历史 |
|
||||||
|
| `task_supervisor` | 后台任务注册、取消、限时回收 | 业务级重试和结果语义 |
|
||||||
|
|
||||||
|
## 4. 消息与控制数据流
|
||||||
|
|
||||||
|
`MessageBus` 包含三条容量相同的 Tokio MPSC 队列:
|
||||||
|
|
||||||
|
- `inbound`:Channel → Gateway message processor。
|
||||||
|
- `outbound`:Session/Tool → `OutboundDispatcher`。
|
||||||
|
- `control`:WebSocket/Channel → Gateway message processor,用于 dialog 操作。
|
||||||
|
|
||||||
|
### 普通消息
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant C as Channel
|
||||||
|
participant B as MessageBus
|
||||||
|
participant G as Message processor
|
||||||
|
participant S as SessionManager
|
||||||
|
participant W as Per-session worker
|
||||||
|
participant A as AgentLoop
|
||||||
|
participant D as OutboundDispatcher
|
||||||
|
|
||||||
|
C->>B: publish InboundMessage
|
||||||
|
B->>G: consume inbound
|
||||||
|
G->>S: handle_message
|
||||||
|
S->>W: try_send AgentTask
|
||||||
|
S-->>G: AgentProcessing
|
||||||
|
W->>A: process(history)
|
||||||
|
A-->>W: final response
|
||||||
|
W->>B: publish OutboundMessage
|
||||||
|
B->>D: consume outbound
|
||||||
|
D->>C: Channel::send
|
||||||
|
```
|
||||||
|
|
||||||
|
关键语义:
|
||||||
|
|
||||||
|
- Gateway 的主消息处理循环不等待模型完成;普通消息进入对应 session worker 后立即返回 `AgentProcessing`。
|
||||||
|
- 每个 session 有一条容量为 32 的队列,同一 session 串行处理,不同 session 的 worker 可并发执行。
|
||||||
|
- 队列满时明确拒绝新消息,不允许无界积压。
|
||||||
|
- Slash command 不进入 Agent 队列,由 `SessionManager` 直接执行,因此 `/stop` 等控制操作不会排在长模型调用之后。
|
||||||
|
|
||||||
|
### 出站投递
|
||||||
|
|
||||||
|
`OutboundDispatcher` 按 `(channel, chat_id)` 建立独立 lane:
|
||||||
|
|
||||||
|
- 同一目标的消息保持顺序。
|
||||||
|
- 慢目标不会阻塞其他目标。
|
||||||
|
- 每条 lane 容量为 64,空闲 300 秒后退出。
|
||||||
|
- 单次发送超时 30 秒;最多尝试 3 次,前两次失败后分别等待 1/2 秒。只有 `ConnectionError` 和 `SendError` 会重试。
|
||||||
|
- `deliver_outbound` 可等待渠道真实投递结果,等待上限 120 秒;普通 `publish_outbound` 只保证成功入队。
|
||||||
|
|
||||||
|
不要把“已进入 Bus”误认为“外部渠道已收到”。需要确认语义时必须使用 `deliver_outbound`。
|
||||||
|
|
||||||
|
### Control 消息
|
||||||
|
|
||||||
|
WebSocket dialog 操作通过 `ControlMessage` 携带一次性回复通道。Gateway 在统一 message processor 中调用 `SessionManager`,再将 `SessionEvent` 回传给发起者。Bus 只承载消息,不解释操作。
|
||||||
|
|
||||||
|
## 5. 会话模型与并发不变量
|
||||||
|
|
||||||
|
Session ID 格式为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<channel>:<chat_id>:<dialog_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
`SessionManagerInner` 保存:
|
||||||
|
|
||||||
|
- `sessions`:完整 Session ID 到内存 Session 的映射。
|
||||||
|
- `current_sessions`:`channel:chat_id` 到当前 dialog 的映射。
|
||||||
|
|
||||||
|
必须维护以下不变量:
|
||||||
|
|
||||||
|
1. 同一 Session 的 Agent 工作由一个 generation 对应的 worker 串行执行。
|
||||||
|
2. `/stop` 或 worker 替换会递增 `worker_generation`;旧 worker 不得再提交结果。
|
||||||
|
3. 慢操作(模型、记忆召回、压缩、SQLite I/O)不能长期持有 Session mutex。
|
||||||
|
4. 慢操作开始前记录 `state_version`,提交前重新验证,防止旧快照覆盖 `/clear`、`/delete` 等并发修改。
|
||||||
|
5. 持久化写入由 `persistence_lock` 串行化;多条相关记录应使用 Storage 的原子接口。
|
||||||
|
6. 内存先变更但持久化失败时,必须回滚精确匹配的消息后缀,不能删除无关的新状态。
|
||||||
|
|
||||||
|
SessionManager 负责组装会话上下文:系统提示、Skills、召回的 Knowledge、压缩后的 Timeline 和当前消息历史。`AgentLoop` 接收完整输入执行一次模型/工具循环,本身不拥有会话状态。
|
||||||
|
|
||||||
|
## 6. 持久化
|
||||||
|
|
||||||
|
`Storage` 使用 SQLx + SQLite,默认数据库为 `{workspace_dir}/picobot.db`。连接启用:
|
||||||
|
|
||||||
|
- WAL journal mode。
|
||||||
|
- foreign keys。
|
||||||
|
- 5 秒 busy timeout。
|
||||||
|
- schema version 迁移。
|
||||||
|
|
||||||
|
持久化范围包括 sessions、messages、memories、scheduled jobs、job runs 和 background tasks。修改 schema 时应:
|
||||||
|
|
||||||
|
1. 更新集中式 schema/迁移逻辑。
|
||||||
|
2. 保留已有数据库的升级路径。
|
||||||
|
3. 为新库初始化和旧库迁移分别增加测试。
|
||||||
|
4. 对“状态更新 + 执行记录”等复合写入使用事务。
|
||||||
|
|
||||||
|
### 安全边界
|
||||||
|
|
||||||
|
- API Key 和渠道凭据只来自配置占位符、`.env` 或进程环境,不得写入仓库。
|
||||||
|
- 日志不得输出 token、secret、Authorization header,或包含临时凭据的完整 URL;应记录脱敏后的 host/path 和必要诊断字段。
|
||||||
|
- Gateway 把 cwd 切到 workspace,因此相对文件路径和 Shell 默认从 workspace 开始;这不是硬沙箱。当前内置文件工具接受绝对路径,Bash 也可访问进程权限允许的位置。若某场景需要硬边界,必须显式配置/实现 allowed directory 和进程隔离。
|
||||||
|
- `http_request` 和 `web_fetch` 的私网/回环地址校验属于 SSRF 防线,重构网络层时不能绕过。
|
||||||
|
- 外部内容、Tool 输出和 MCP 响应均是不可信输入;解析错误应返回结构化失败,不能 panic。
|
||||||
|
|
||||||
|
## 7. 后台任务与生命周期
|
||||||
|
|
||||||
|
`TaskSupervisor` 是 Gateway 内部后台任务的统一所有者。message processor、outbound dispatcher、scheduler、session workers、outbound lanes、通知消费者和子 Agent 后台任务都应通过它注册。
|
||||||
|
|
||||||
|
两种注册方式:
|
||||||
|
|
||||||
|
- `spawn`:收到全局取消后直接丢弃任务 future,适合无需异步清理的任务。
|
||||||
|
- `spawn_graceful`:任务自己观察 cancellation token 并清理,Supervisor 在总宽限期结束后再强制 abort。
|
||||||
|
|
||||||
|
新增长生命周期任务时必须满足:
|
||||||
|
|
||||||
|
- 有明确 owner,禁止无法回收的裸 `tokio::spawn`。
|
||||||
|
- 能响应取消;外部连接、重试 sleep 和阻塞式等待也要纳入取消分支。
|
||||||
|
- 等待任务退出必须有硬超时,超时后 abort 并回收 JoinHandle。
|
||||||
|
- 任务 panic、超时和永久错误要可观测,且不能阻止其他组件清理。
|
||||||
|
|
||||||
|
WebSocket 每个客户端的 writer task 是连接局部任务,由连接 handler 自己限时回收;它不跨越连接生命周期。
|
||||||
|
|
||||||
|
## 8. 启动与关停顺序
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
1. 加载配置和 `.env`,解析 workspace。
|
||||||
|
2. 创建并切换到 workspace。
|
||||||
|
3. 初始化 SQLite、MemoryManager、MessageBus 和 SessionManager。
|
||||||
|
4. 注册内置工具、渠道、MCP 工具和 Cron 工具。
|
||||||
|
5. 启动所有 Channel。
|
||||||
|
6. 通过 TaskSupervisor 启动 message processor、dispatcher 和 scheduler。
|
||||||
|
7. 绑定 Axum listener,开始接收请求。
|
||||||
|
|
||||||
|
### 关停
|
||||||
|
|
||||||
|
1. `Ctrl-C` 触发 Axum graceful shutdown,并取消所有 WebSocket 连接。
|
||||||
|
2. `ChannelManager::stop_all` 先停止外部消息入口并注销渠道。
|
||||||
|
3. 取消 TaskSupervisor,停止接受新后台任务。
|
||||||
|
4. 在共享的 10 秒总宽限期内等待任务退出,之后 abort 剩余任务。
|
||||||
|
|
||||||
|
渠道自己的 `stop()` 也必须有界。以飞书为例,端点请求、WebSocket 建连、重试等待和已连接循环共享 CancellationToken,另有 5 秒强制回收兜底。
|
||||||
|
|
||||||
|
## 9. 扩展指南
|
||||||
|
|
||||||
|
### 新增 Channel
|
||||||
|
|
||||||
|
1. 实现 `Channel` trait,仅处理外部协议和统一消息转换。
|
||||||
|
2. 在 `ChannelManager::init` 注册,并通过同一个 MessageBus 收发。
|
||||||
|
3. 将可重试错误表示为 `ConnectionError`/`SendError`,永久错误使用其他类型。
|
||||||
|
4. 为 start/stop 幂等性、取消建连、投递失败和媒体边界增加测试。
|
||||||
|
5. 不要从 Channel 直接调用 SessionManager 或 Provider。
|
||||||
|
|
||||||
|
### 新增 Tool
|
||||||
|
|
||||||
|
1. 实现 `Tool`,在集中注册点加入 `ToolRegistry`。
|
||||||
|
2. 参数 schema 必须明确;返回值保持可供模型消费的字符串协议。
|
||||||
|
3. 明确工具需要“workspace 默认目录”还是“不可逃逸的硬边界”;后者必须显式校验 canonical path,不能只依赖 cwd。
|
||||||
|
4. 网络工具必须保留 SSRF/私网地址校验。
|
||||||
|
5. 长操作应有超时;后台执行应交给 SubAgentManager/TaskSupervisor。
|
||||||
|
|
||||||
|
### 新增 Provider
|
||||||
|
|
||||||
|
1. 实现 `LLMProvider`,保持其为纯 HTTP/API 适配器。
|
||||||
|
2. 统一映射文本、媒体、tool calls、usage 和错误。
|
||||||
|
3. 不在 Provider 中访问 Session、Bus 或 Channel。
|
||||||
|
4. 为请求序列化和响应兼容性编写离线单元测试。
|
||||||
|
|
||||||
|
### 修改 Session
|
||||||
|
|
||||||
|
先画出锁、慢 I/O、版本检查和持久化顺序。任何跨 `await` 的 Session 锁都需要特别审查;任何由旧快照产生的结果都必须在提交前验证 generation/state version。
|
||||||
|
|
||||||
|
## 10. 验证策略
|
||||||
|
|
||||||
|
按改动范围选择最小但充分的验证:
|
||||||
|
|
||||||
|
| 改动 | 至少执行 |
|
||||||
|
|------|----------|
|
||||||
|
| 文档 | 检查链接、命令和源码路径;`git diff --check` |
|
||||||
|
| Rust 实现 | 相关定向测试、`cargo test --lib`、`cargo clippy --all-targets --all-features -- -D warnings` |
|
||||||
|
| 构建/依赖 | 上述检查加 `cargo build` |
|
||||||
|
| Provider/真实渠道 | 离线测试;有凭据时再运行 ignored integration tests |
|
||||||
|
| SQLite schema | 新库测试、迁移测试、原子性测试 |
|
||||||
|
| 生命周期/并发 | 成功、失败、超时、取消、队列满和重复 start/stop 测试 |
|
||||||
|
|
||||||
|
模型 API 集成测试需要 `tests/test.env` 中的真实 API Key,并默认 ignored;`test_scheduler` 和 `test_request_format` 是可直接运行的离线测试。不应在无凭据时假装已经验证真实 Provider。
|
||||||
|
|
||||||
|
## 11. 演进决策清单
|
||||||
|
|
||||||
|
提交架构性修改前,逐项确认:
|
||||||
|
|
||||||
|
- 是否仍遵守 Channel → Bus → Session → Agent 的依赖方向?
|
||||||
|
- 是否引入第二个 owner、重复状态或绕过统一注册点?
|
||||||
|
- 队列、并发、重试和等待是否全部有界?
|
||||||
|
- 取消信号能否覆盖建连、sleep、I/O 和清理阶段?
|
||||||
|
- 是否在持锁时执行了网络、模型或数据库慢操作?
|
||||||
|
- 内存与 SQLite 失败时能否保持一致?
|
||||||
|
- 错误是否区分瞬态和永久语义?
|
||||||
|
- 是否有测试覆盖正常路径与最危险的失败路径?
|
||||||
|
- README、AGENTS.md 和本文档是否需要同步更新?
|
||||||
|
|
||||||
|
## 12. 代码导航
|
||||||
|
|
||||||
|
| 主题 | 入口 |
|
||||||
|
|------|------|
|
||||||
|
| Gateway 装配和关停 | `src/gateway/mod.rs` |
|
||||||
|
| WebSocket 生命周期 | `src/gateway/ws.rs` |
|
||||||
|
| Bus 和消息类型 | `src/bus/mod.rs`, `src/bus/message.rs` |
|
||||||
|
| 出站并发与重试 | `src/bus/dispatcher.rs` |
|
||||||
|
| Channel 接口与注册 | `src/channels/base.rs`, `src/channels/manager.rs` |
|
||||||
|
| Session 核心 | `src/session/session.rs` |
|
||||||
|
| Session 命令/事件 | `src/session/commands.rs`, `src/session/events.rs` |
|
||||||
|
| Agent loop | `src/agent/agent_loop.rs` |
|
||||||
|
| 后台任务监督 | `src/task_supervisor.rs` |
|
||||||
|
| SQLite 初始化和迁移 | `src/storage/mod.rs` |
|
||||||
|
| Scheduler | `src/scheduler/mod.rs` |
|
||||||
|
| 配置加载 | `src/config/mod.rs` |
|
||||||
@ -14,10 +14,11 @@ PicoBot 是一个基于 Rust 的个人 AI 助手运行时,包含本地 Gateway
|
|||||||
| 文件 | 内容 |
|
| 文件 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `references/config.md` | 配置字段详解:providers、models、agents、gateway、client、channels、memory、mcp、browser |
|
| `references/config.md` | 配置字段详解:providers、models、agents、gateway、client、channels、memory、mcp、browser |
|
||||||
| `references/db-schema.md` | 数据库表结构:sessions、messages、memories、scheduled_jobs、llm_calls、background_tasks |
|
| `references/db-schema.md` | 数据库表结构与运行约束:sessions、messages、memories、scheduled_jobs、job_runs、llm_calls、background_tasks |
|
||||||
| `references/architecture.md` | 核心架构:数据流、会话系统、上下文压缩、记忆系统、Skill 优先级、MCP、子 Agent |
|
| `references/architecture.md` | 核心架构:消息并发、会话系统、持久化、生命周期、上下文压缩、记忆、MCP、子 Agent |
|
||||||
| `references/faq.md` | 常见问题:模型切换、渠道添加、Skill 安装、历史查询、定时任务、MCP 等 |
|
| `references/faq.md` | 常见问题:模型切换、渠道添加、Skill 安装、历史查询、定时任务、MCP 等 |
|
||||||
| `references/commands.md` | 常用命令:编译、启动网关、启动客户端、运行测试 |
|
| `references/commands.md` | 常用命令:编译、启动网关、启动客户端、运行测试 |
|
||||||
|
| `references/tools.md` | 内置工具名称、参数和重要使用约束 |
|
||||||
| `assets/config.example.json` | config.json 完整示例 |
|
| `assets/config.example.json` | config.json 完整示例 |
|
||||||
|
|
||||||
Skill 根目录路径见上方 **Skill Root Directory**。
|
Skill 根目录路径见上方 **Skill Root Directory**。
|
||||||
|
|||||||
@ -3,9 +3,12 @@
|
|||||||
## 核心数据流
|
## 核心数据流
|
||||||
|
|
||||||
```
|
```
|
||||||
Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionManager → MessageBus → OutboundDispatcher → Channel
|
Channel → MessageBus.inbound → Gateway processor → SessionManager → per-session worker → AgentLoop
|
||||||
↑
|
↑ │
|
||||||
ControlChannel → SessionManager (dialog 操作: 创建/切换/归档/删除)
|
└── Channel ← OutboundDispatcher ← per-conversation lane ← MessageBus.outbound
|
||||||
|
|
||||||
|
WebSocket/Channel → MessageBus.control → Gateway processor → SessionManager (dialog 操作)
|
||||||
|
Scheduler → SessionManager.handle_cron_message → AgentLoop → send_message
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模块职责
|
## 模块职责
|
||||||
@ -15,8 +18,8 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
|
|||||||
| `gateway` | HTTP/WebSocket 服务器,持有 GatewayState |
|
| `gateway` | HTTP/WebSocket 服务器,持有 GatewayState |
|
||||||
| `client` | TUI 聊天客户端 |
|
| `client` | TUI 聊天客户端 |
|
||||||
| `channels` | 外部集成(飞书、CLI),仅收发消息 |
|
| `channels` | 外部集成(飞书、CLI),仅收发消息 |
|
||||||
| `bus` | 异步消息队列,纯队列不路由 |
|
| `bus` | 有界 inbound/outbound/control 队列;出站 dispatcher 与分目标 lane |
|
||||||
| `session` | 会话生命周期管理、dialog 操作 |
|
| `session` | 会话生命周期、dialog 操作、每 session 串行队列、上下文与持久化协调 |
|
||||||
| `agent` | LLM 调用循环、工具执行、上下文压缩、媒体处理、子 Agent |
|
| `agent` | LLM 调用循环、工具执行、上下文压缩、媒体处理、子 Agent |
|
||||||
| `providers` | LLM API 客户端(OpenAI 兼容、Anthropic) |
|
| `providers` | LLM API 客户端(OpenAI 兼容、Anthropic) |
|
||||||
| `tools` | Agent 工具(bash、文件操作、搜索、HTTP、web、browser、memory、delegate 等) |
|
| `tools` | Agent 工具(bash、文件操作、搜索、HTTP、web、browser、memory、delegate 等) |
|
||||||
@ -28,13 +31,14 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
|
|||||||
| `config` | 配置加载、环境变量替换、路径解析 |
|
| `config` | 配置加载、环境变量替换、路径解析 |
|
||||||
| `memory` | 长期记忆存储与检索 |
|
| `memory` | 长期记忆存储与检索 |
|
||||||
| `mcp` | MCP(Model Context Protocol)工具集成 |
|
| `mcp` | MCP(Model Context Protocol)工具集成 |
|
||||||
|
| `task_supervisor` | Gateway 后台任务注册、取消、限时等待和强制回收 |
|
||||||
|
|
||||||
## 功能边界
|
## 功能边界
|
||||||
|
|
||||||
- Channels 仅收发消息,不感知 session 或 LLM
|
- Channels 仅收发消息,不感知 session 或 LLM
|
||||||
- MessageBus 是纯异步队列,不路由
|
- MessageBus 本体持有三条有界队列;出站路由、顺序和重试由 `OutboundDispatcher` 负责
|
||||||
- SessionManager 拥有 session 状态,不直接调 LLM;负责注入 skills prompt
|
- SessionManager 拥有 session 状态、dialog 路由、上下文构建和每 session worker,并通过 worker 创建 AgentLoop
|
||||||
- AgentLoop 无状态,接收 dialog 事件调用 LLM、执行工具
|
- AgentLoop 跨轮无状态,接收已准备的 history 调用 LLM、执行工具并返回一次结果
|
||||||
- Providers 是纯 HTTP 客户端,无 bus/session/channel 感知
|
- Providers 是纯 HTTP 客户端,无 bus/session/channel 感知
|
||||||
- Tools 接收原始参数,返回字符串结果
|
- Tools 接收原始参数,返回字符串结果
|
||||||
- MCP 工具在 Gateway 初始化时连接服务器、发现工具,并包装成普通 Tool 注册到 ToolRegistry
|
- MCP 工具在 Gateway 初始化时连接服务器、发现工具,并包装成普通 Tool 注册到 ToolRegistry
|
||||||
@ -48,6 +52,11 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
|
|||||||
- OutboundDispatcher 通过 ChannelManager 路由出站消息
|
- OutboundDispatcher 通过 ChannelManager 路由出站消息
|
||||||
- Config `.env` 加载使用 `unsafe { env::set_var(...) }`
|
- Config `.env` 加载使用 `unsafe { env::set_var(...) }`
|
||||||
- `browser` 工具只有在 `browser.enabled=true` 时注册,依赖 Chrome/Chromium 与 WebDriver
|
- `browser` 工具只有在 `browser.enabled=true` 时注册,依赖 Chrome/Chromium 与 WebDriver
|
||||||
|
- 同一 session 的普通消息串行处理,不同 session 可并发;session 队列容量为 32,满时明确拒绝
|
||||||
|
- 出站消息按 `(channel, chat_id)` 分 lane 保序;lane 容量为 64,慢目标不阻塞其他目标
|
||||||
|
- 长生命周期后台任务由 TaskSupervisor 管理;连接局部任务由其 owner 限时 join 或 abort
|
||||||
|
- 外部建连、重试等待和关停 join 必须可取消且有硬超时
|
||||||
|
- 不得记录 API Key、Authorization header 或包含临时凭据的完整连接 URL
|
||||||
|
|
||||||
## 上下文压缩
|
## 上下文压缩
|
||||||
|
|
||||||
@ -101,7 +110,7 @@ create → 存入 Storage → 载入 memory → 设为当前 dialog
|
|||||||
| `list_dialogs` | 列出 `channel:chat_id` 下最近 10 个 session |
|
| `list_dialogs` | 列出 `channel:chat_id` 下最近 10 个 session |
|
||||||
| `rename` | 更新标题,内存 + Storage 同步 |
|
| `rename` | 更新标题,内存 + Storage 同步 |
|
||||||
| `delete` | 软删除(设 deleted_at),从内存移除 |
|
| `delete` | 软删除(设 deleted_at),从内存移除 |
|
||||||
| `archive` | 当前为空操作 |
|
| `archive` | 设置 archived_at,从内存和当前 dialog 追踪中移除;可通过 include_archived 查询 |
|
||||||
|
|
||||||
### SessionManager 数据结构
|
### SessionManager 数据结构
|
||||||
|
|
||||||
@ -112,13 +121,17 @@ create → 存入 Storage → 载入 memory → 设为当前 dialog
|
|||||||
|
|
||||||
消息到达时 `resolve_dialog_id()` 按顺序确定接收 session:当前 session → Storage 最近活跃 session → 新建。
|
消息到达时 `resolve_dialog_id()` 按顺序确定接收 session:当前 session → Storage 最近活跃 session → 新建。
|
||||||
|
|
||||||
### 消息处理三阶段
|
### 消息处理与并发
|
||||||
|
|
||||||
**阶段 1(持锁)**:斜杠命令检测 → 用户消息入库 → 提取记忆上下文 → 构建系统提示(skills + memory_context)→ 上下文压缩 → 创建 AgentLoop
|
普通消息先 `try_send` 到该 session 的有界 worker 队列,Gateway 主 processor 随即返回 `AgentProcessing`。Slash command 直接执行,不进入此队列,因此 `/stop` 不会排在长模型调用后。
|
||||||
|
|
||||||
**阶段 2(无锁)**:`agent.process(history)` → LLM 调用 + 工具执行。上下文溢出时自动重新压缩重试
|
Worker 的处理原则:
|
||||||
|
|
||||||
**阶段 3(持锁)**:持久化 agent 响应消息 → 自动生成标题(消息数 ≥ 5 且标题为"新对话"时)
|
1. 短暂持 Session 锁抓取快照并记录 `worker_generation`/`state_version`。
|
||||||
|
2. 释放锁后执行消息持久化、记忆召回、上下文压缩、LLM 和工具等慢操作。
|
||||||
|
3. 提交由旧快照产生的结果前重新验证 generation/version,防止 `/stop`、`/clear` 或 `/delete` 后写回陈旧状态。
|
||||||
|
4. Session 持久化由独立 `persistence_lock` 串行化;批量消息使用原子写入,失败时精确回滚内存后缀。
|
||||||
|
5. 上下文溢出时按 Provider 返回的真实限制重新压缩并重试。
|
||||||
|
|
||||||
### 会话恢复
|
### 会话恢复
|
||||||
|
|
||||||
@ -136,7 +149,7 @@ create → 存入 Storage → 载入 memory → 设为当前 dialog
|
|||||||
| 类别 | 用途 | 生命周期 | 检索方式 |
|
| 类别 | 用途 | 生命周期 | 检索方式 |
|
||||||
|------|------|----------|----------|
|
|------|------|----------|----------|
|
||||||
| **Knowledge** | 事实、偏好、模式、洞察 | 长期保留,手动删除 | 每轮注入系统提示,关键词匹配 |
|
| **Knowledge** | 事实、偏好、模式、洞察 | 长期保留,手动删除 | 每轮注入系统提示,关键词匹配 |
|
||||||
| **Timeline** | 历史会话摘要 | 自动清理(默认 90 天) | `timeline_recall` 工具按需检索 |
|
| **Timeline** | 历史会话摘要 | 配置预期保留 90 天;当前尚无自动清理循环 | `timeline_recall` 工具按需检索 |
|
||||||
|
|
||||||
### MemoryEntry 字段
|
### MemoryEntry 字段
|
||||||
|
|
||||||
@ -163,7 +176,7 @@ create → 存入 Storage → 载入 memory → 设为当前 dialog
|
|||||||
→ MemoryManager::recall(content, 5, Knowledge)
|
→ MemoryManager::recall(content, 5, Knowledge)
|
||||||
返回最多 5 条匹配的知识记忆(按 importance DESC)
|
返回最多 5 条匹配的知识记忆(按 importance DESC)
|
||||||
→ 格式化为 "- key: content"
|
→ 格式化为 "- key: content"
|
||||||
→ 注入系统提示的 "记忆上下文" 部分
|
→ 作为运行时上下文附加到本轮 user message
|
||||||
→ LLM 可见,辅助回答
|
→ LLM 可见,辅助回答
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -191,10 +204,12 @@ LLM 对话上下文接近 token 限制 (默认 128K × 70%) 时自动触发压
|
|||||||
| 时机 | 操作 |
|
| 时机 | 操作 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 每次消息处理 | `memory_manager.recall()` 提取 Knowledge 上下文 |
|
| 每次消息处理 | `memory_manager.recall()` 提取 Knowledge 上下文 |
|
||||||
| 系统提示构建 | `MemorySection` 渲染记忆指南 + 匹配的记忆 |
|
| 系统提示构建 | `MemorySection` 渲染记忆工具指南;匹配的 Knowledge 附加到本轮 user message |
|
||||||
| 有压缩历史时 | `HistorySection` 提示 LLM 使用 `timeline_recall` |
|
| 有压缩历史时 | `HistorySection` 提示 LLM 使用 `timeline_recall` |
|
||||||
| 压缩完成后 | 摘要自动存储为 Timeline 记忆 |
|
| 压缩完成后 | 摘要自动存储为 Timeline 记忆 |
|
||||||
| 空闲时 | 可配置自动 consolidation(`idle_consolidation_minutes`) |
|
| 会话恢复 | 加载最近 Timeline 和压缩边界后的原始消息 |
|
||||||
|
|
||||||
|
`memory.recall_limit`、`idle_consolidation_minutes`、`timeline_retention_days` 和 `max_failures_before_degrade` 当前会被配置解析;其中每轮 Knowledge 召回在 worker 中仍固定为 5,其余自动维护策略尚未接入运行循环。不要把“配置可解析”误认为“行为已生效”。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -223,6 +238,23 @@ Gateway 初始化时读取 `config.mcp.servers`:
|
|||||||
|
|
||||||
默认工具集是只读工具:`file_read`、`file_search`、`content_search`、`web_fetch`、`http_request`、`calculator`。调用时可通过 `allowed_tools` 显式放开其他工具。后台任务会写入 `background_tasks` 表,默认 24 小时后清理。
|
默认工具集是只读工具:`file_read`、`file_search`、`content_search`、`web_fetch`、`http_request`、`calculator`。调用时可通过 `allowed_tools` 显式放开其他工具。后台任务会写入 `background_tasks` 表,默认 24 小时后清理。
|
||||||
|
|
||||||
|
后台子 Agent 通过 `TaskSupervisor::spawn_graceful` 注册,受 `gateway.max_concurrent_background_tasks` 限制;Gateway 关停时先收到取消信号,再在总宽限期内清理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 出站投递与关停
|
||||||
|
|
||||||
|
`OutboundDispatcher` 对每个 `(channel, chat_id)` 创建独立 lane:同一目标保持顺序,单次发送超时 30 秒,最多尝试 3 次。只有 `ConnectionError` 和 `SendError` 被视为瞬态错误;永久错误不重试。`deliver_outbound` 等待真实渠道投递结果,`publish_outbound` 只表示成功入队。
|
||||||
|
|
||||||
|
Gateway 关停顺序:
|
||||||
|
|
||||||
|
1. Ctrl-C 停止 Axum 接入并取消 WebSocket 连接。
|
||||||
|
2. `ChannelManager::stop_all` 停止外部渠道并注销它们。
|
||||||
|
3. 取消 TaskSupervisor,并在共享的 10 秒宽限期内等待后台任务。
|
||||||
|
4. 超时后 abort 剩余任务并回收 JoinHandle。
|
||||||
|
|
||||||
|
渠道 `stop()` 也必须有界。飞书端点请求、WebSocket 建连、重试 sleep 和连接循环共享 CancellationToken,另有 5 秒强制终止兜底。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 当前斜杠命令
|
## 当前斜杠命令
|
||||||
|
|||||||
@ -13,6 +13,16 @@ cargo run -- chat
|
|||||||
# 运行单元测试
|
# 运行单元测试
|
||||||
cargo test --lib
|
cargo test --lib
|
||||||
|
|
||||||
# 运行集成测试 (需配置 tests/test.env)
|
# 运行离线集成/协议测试
|
||||||
|
cargo test --test test_scheduler
|
||||||
|
cargo test --test test_request_format
|
||||||
|
|
||||||
|
# 运行代码检查(Rust 修改必跑)
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
# 运行模型 API 集成测试(需配置 tests/test.env)
|
||||||
cargo test --test test_integration -- --ignored
|
cargo test --test test_integration -- --ignored
|
||||||
|
cargo test --test test_tool_calling -- --ignored
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`test_scheduler` 和 `test_request_format` 不需要 API Key,也没有标记 `#[ignore]`。只有会真实调用 Provider 的测试需要从 `tests/test.env.example` 创建 `tests/test.env` 后使用 `-- --ignored`。
|
||||||
|
|||||||
@ -55,9 +55,9 @@
|
|||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `host` | string | 127.0.0.1 | 监听地址 |
|
| `host` | string | 127.0.0.1 | 监听地址 |
|
||||||
| `port` | int | 19876 | 监听端口 |
|
| `port` | int | 19876 | 监听端口 |
|
||||||
| `session_ttl_hours` | int | - | 会话过期小时数 |
|
| `session_ttl_hours` | int | - | 兼容/预留字段;当前没有会话 TTL 清理循环 |
|
||||||
| `session_db_path` | string | - | SQLite 数据库路径,默认在 workspace 下 |
|
| `session_db_path` | string | - | SQLite 数据库路径,默认在 workspace 下 |
|
||||||
| `cleanup_interval_minutes` | int | - | 清理间隔 |
|
| `cleanup_interval_minutes` | int | - | 兼容/预留字段;当前没有按此间隔运行的 session 清理任务 |
|
||||||
| `max_concurrent_background_tasks` | int | 10 | delegate 后台子任务最大并发数 |
|
| `max_concurrent_background_tasks` | int | 10 | delegate 后台子任务最大并发数 |
|
||||||
| `scheduler` | object | - | 调度器配置 |
|
| `scheduler` | object | - | 调度器配置 |
|
||||||
|
|
||||||
@ -67,18 +67,21 @@
|
|||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `enabled` | bool | true | 是否启动调度器并注册 cron 工具 |
|
| `enabled` | bool | true | 是否启动调度器并注册 cron 工具 |
|
||||||
| `poll_interval_secs` | int | 60 | 检查到期任务的轮询间隔 |
|
| `poll_interval_secs` | int | 60 | 检查到期任务的轮询间隔 |
|
||||||
| `max_concurrent` | int | 1 | 最大并发任务数,当前实现预留 |
|
| `max_concurrent` | int | 1 | 每批到期任务的最大并发数,运行时限制在 1–256 |
|
||||||
|
| `execution_timeout_secs` | int | 900 | 单个定时任务 Agent 执行的硬超时;租约会额外增加 30 秒 |
|
||||||
|
|
||||||
## memory 字段
|
## memory 字段
|
||||||
|
|
||||||
| 字段 | 类型 | 默认 | 说明 |
|
| 字段 | 类型 | 默认 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `consolidation_provider` | string | - | 记忆归并 LLM 提供商 |
|
| `consolidation_provider` | string | 主 Agent provider | 当前记录在 MemoryManager 中供后续归并使用;压缩摘要仍使用 Session provider |
|
||||||
| `consolidation_model` | string | - | 记忆归并 LLM 模型 |
|
| `consolidation_model` | string | 主 Agent model | 当前记录在 MemoryManager 中供后续归并使用;压缩摘要仍使用 Session model |
|
||||||
| `recall_limit` | int | 5 | 每轮注入的知识记忆条数 |
|
| `recall_limit` | int | 5 | 预期的每轮知识召回上限;当前 worker 固定使用 5 |
|
||||||
| `idle_consolidation_minutes` | int | 10 | 空闲后触发归并的分钟数 |
|
| `idle_consolidation_minutes` | int | 10 | 预留的空闲归并阈值;当前无对应循环 |
|
||||||
| `timeline_retention_days` | int | 90 | 时间线记忆保留天数 |
|
| `timeline_retention_days` | int | 90 | 预留的 Timeline 保留期;当前无自动清理循环 |
|
||||||
| `max_failures_before_degrade` | int | 3 | 归并失败次数阈值 |
|
| `max_failures_before_degrade` | int | 3 | 预留的归并失败阈值;当前无失败降级循环 |
|
||||||
|
|
||||||
|
注意:这些字段都会被解析,但当前 worker 的 Knowledge 召回数量仍固定为 5;idle consolidation、Timeline 自动清理和失败降级循环尚未接入。配置存在不等于对应后台行为已经生效。
|
||||||
|
|
||||||
## channels.feishu 字段
|
## channels.feishu 字段
|
||||||
|
|
||||||
@ -89,7 +92,7 @@
|
|||||||
| `app_secret` | string | - | 飞书应用密钥 |
|
| `app_secret` | string | - | 飞书应用密钥 |
|
||||||
| `allow_from` | []string | ["*"] | 允许交互的用户列表 |
|
| `allow_from` | []string | ["*"] | 允许交互的用户列表 |
|
||||||
| `agent` | string | - | 使用的 agent 名称 |
|
| `agent` | string | - | 使用的 agent 名称 |
|
||||||
| `media_dir` | string | ~/.picobot/media/feishu | 媒体存储目录 |
|
| `media_dir` | string | ~/.picobot/media/feishu | 配置默认值;Gateway 注册渠道时会覆盖为 `{workspace}/media/feishu` |
|
||||||
| `reaction_emoji` | string | "Typing" | 回复意向表达的表情 |
|
| `reaction_emoji` | string | "Typing" | 回复意向表达的表情 |
|
||||||
|
|
||||||
## mcp 字段
|
## mcp 字段
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
数据库为 SQLite,默认位于 workspace 下的 `picobot.db`。
|
数据库为 SQLite,默认位于 workspace 下的 `picobot.db`。
|
||||||
|
|
||||||
|
连接启用 WAL、`synchronous=NORMAL`、foreign keys、5 秒 busy timeout,连接池最多 8 个连接。当前 `PRAGMA user_version=1`;启动时会在事务内补齐旧库字段和索引,遇到比程序更新的 schema version 会拒绝启动。
|
||||||
|
|
||||||
## sessions 表
|
## sessions 表
|
||||||
|
|
||||||
会话表,一个 session 对应一个 (channel, chat_id, dialog_id) 组合。
|
会话表,一个 session 对应一个 (channel, chat_id, dialog_id) 组合。
|
||||||
@ -13,13 +15,16 @@
|
|||||||
| `chat_id` | TEXT | 聊天/群组标识 |
|
| `chat_id` | TEXT | 聊天/群组标识 |
|
||||||
| `dialog_id` | TEXT | 对话标识 |
|
| `dialog_id` | TEXT | 对话标识 |
|
||||||
| `title` | TEXT | 会话标题(默认 "新对话") |
|
| `title` | TEXT | 会话标题(默认 "新对话") |
|
||||||
| `created_at` | INTEGER | 创建时间(unix 秒) |
|
| `created_at` | INTEGER | 创建时间(Unix 毫秒) |
|
||||||
| `last_active_at` | INTEGER | 最后活跃时间 |
|
| `last_active_at` | INTEGER | 最后活跃时间(Unix 毫秒) |
|
||||||
| `message_count` | INTEGER | 消息计数 |
|
| `message_count` | INTEGER | 消息计数 |
|
||||||
| `routing_info` | TEXT | 路由信息 |
|
| `routing_info` | TEXT | 路由信息 |
|
||||||
|
| `archived_at` | INTEGER | 归档时间(Unix 毫秒),NULL 表示未归档 |
|
||||||
| `deleted_at` | INTEGER | 软删除时间戳 |
|
| `deleted_at` | INTEGER | 软删除时间戳 |
|
||||||
| `last_consolidated_at` | INTEGER | 上次记忆归并时间 |
|
| `last_consolidated_at` | INTEGER | 上次记忆归并时间 |
|
||||||
| `last_compressed_message_at` | INTEGER | 上次上下文压缩消息序号 |
|
| `last_compressed_message_at` | INTEGER | 上次上下文压缩边界时间戳 |
|
||||||
|
|
||||||
|
`(channel, chat_id, dialog_id)` 唯一。普通列表排除 `deleted_at`;是否包含归档记录由查询参数决定。
|
||||||
|
|
||||||
## messages 表
|
## messages 表
|
||||||
|
|
||||||
@ -35,9 +40,11 @@
|
|||||||
| `tool_name` | TEXT | 工具名称 |
|
| `tool_name` | TEXT | 工具名称 |
|
||||||
| `tool_calls` | TEXT | 工具调用参数 JSON |
|
| `tool_calls` | TEXT | 工具调用参数 JSON |
|
||||||
| `source` | TEXT | 消息来源(跨会话消息时标记来源 session_id) |
|
| `source` | TEXT | 消息来源(跨会话消息时标记来源 session_id) |
|
||||||
| `created_at` | INTEGER | 创建时间(unix 秒) |
|
| `created_at` | INTEGER | 创建时间(Unix 毫秒) |
|
||||||
| `reasoning_content` | TEXT | provider 返回的推理内容(如有) |
|
| `reasoning_content` | TEXT | provider 返回的推理内容(如有) |
|
||||||
|
|
||||||
|
`(session_id, seq)` 有唯一索引,防止并发写入重复序号。删除 session 会通过外键级联删除 messages。
|
||||||
|
|
||||||
## background_tasks 表
|
## background_tasks 表
|
||||||
|
|
||||||
delegate 后台子任务表。`session_id` 不使用数据库外键,因为 session 使用软删除,关联关系由应用层维护。
|
delegate 后台子任务表。`session_id` 不使用数据库外键,因为 session 使用软删除,关联关系由应用层维护。
|
||||||
@ -82,17 +89,24 @@ delegate 后台子任务表。`session_id` 不使用数据库外键,因为 ses
|
|||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `id` | TEXT PK | 任务 UUID |
|
| `id` | TEXT PK | 任务 UUID |
|
||||||
| `name` | TEXT | 任务名称 |
|
| `name` | TEXT | 任务名称 |
|
||||||
| `schedule` | TEXT | 调度规则 JSON(once/every/cron) |
|
| `schedule` | TEXT | 调度规则 JSON(at/every/cron) |
|
||||||
| `prompt` | TEXT | 任务提示词 |
|
| `prompt` | TEXT | 任务提示词 |
|
||||||
| `channel` | TEXT | 执行渠道 |
|
| `channel` | TEXT | 执行渠道 |
|
||||||
| `chat_id` | TEXT | 目标对话 |
|
| `chat_id` | TEXT | 目标对话 |
|
||||||
| `model` | TEXT | 使用的模型(可选) |
|
| `model` | TEXT | 可选模型标记;当前会存储/展示,但 Scheduler 执行仍使用默认 Agent 模型 |
|
||||||
| `enabled` | INTEGER | 是否启用 (1/0) |
|
| `enabled` | INTEGER | 是否启用 (1/0) |
|
||||||
| `delete_after_run` | INTEGER | 执行后自动删除 (1/0) |
|
| `delete_after_run` | INTEGER | 执行后自动删除 (1/0) |
|
||||||
| `next_run_at` | INTEGER | 下次执行时间 |
|
| `next_run_at` | INTEGER | 下次执行时间 |
|
||||||
| `last_run_at` | INTEGER | 上次执行时间 |
|
| `last_run_at` | INTEGER | 上次执行时间 |
|
||||||
| `last_status` | TEXT | 上次执行状态 |
|
| `last_status` | TEXT | 上次执行状态 |
|
||||||
| `last_error` | TEXT | 上次错误信息 |
|
| `last_error` | TEXT | 上次错误信息 |
|
||||||
|
| `locked_at` | INTEGER | 本次领取时间 |
|
||||||
|
| `lock_owner` | TEXT | 领取任务的 Scheduler owner UUID |
|
||||||
|
| `lease_until` | INTEGER | 租约到期时间;进程崩溃后允许其他实例重新领取 |
|
||||||
|
| `created_at` | INTEGER | 创建时间(Unix 毫秒) |
|
||||||
|
| `updated_at` | INTEGER | 更新时间(Unix 毫秒) |
|
||||||
|
|
||||||
|
Scheduler 使用原子 `UPDATE ... RETURNING` 领取到期任务。任务结果、下次运行时间和租约释放在同一事务中提交,并校验 owner,防止过期 worker 覆盖已恢复的任务。
|
||||||
|
|
||||||
## job_runs 表
|
## job_runs 表
|
||||||
|
|
||||||
@ -121,3 +135,5 @@ delegate 后台子任务表。`session_id` 不使用数据库外键,因为 ses
|
|||||||
| `response_body` | TEXT | 响应体 JSON |
|
| `response_body` | TEXT | 响应体 JSON |
|
||||||
| `error` | TEXT | 错误信息 |
|
| `error` | TEXT | 错误信息 |
|
||||||
| `duration_ms` | INTEGER | 耗时(毫秒) |
|
| `duration_ms` | INTEGER | 耗时(毫秒) |
|
||||||
|
|
||||||
|
`request_body`/`response_body` 可能包含用户内容,排障和导出数据库时应按敏感数据处理。
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
`~/.picobot/skills/about-picobot/` 下,SKILL.md 为索引,references/ 下为各详细文档,assets/ 下为 config 示例。如被删除,重启程序自动重新安装。
|
`~/.picobot/skills/about-picobot/` 下,SKILL.md 为索引,references/ 下为各详细文档,assets/ 下为 config 示例。如被删除,重启程序自动重新安装。
|
||||||
|
|
||||||
|
内置 Skill 只在目标目录不存在时释放,不会覆盖已安装目录。升级 PicoBot 后如需获取新版内置文档,应先备份自己的修改,再删除旧的 `~/.picobot/skills/about-picobot/` 并重启。也可把定制版放在 `{workspace}/skills/about-picobot/`,它的优先级更高。
|
||||||
|
|
||||||
## Q: 数据库文件在哪里?
|
## Q: 数据库文件在哪里?
|
||||||
|
|
||||||
默认 `{workspace}/picobot.db`,workspace 默认 `~/.picobot/workspace/`。
|
默认 `{workspace}/picobot.db`,workspace 默认 `~/.picobot/workspace/`。
|
||||||
@ -30,7 +32,7 @@
|
|||||||
|
|
||||||
## Q: 如何创建定时任务?
|
## Q: 如何创建定时任务?
|
||||||
|
|
||||||
使用 `cron` 工具,支持一次性 (`once`)、周期性 (`every`) 和 cron 表达式调度。
|
使用 `cron_add` 创建,`cron_list` 查看,`cron_update`、`cron_enable`、`cron_disable`、`cron_remove` 管理。Schedule 类型为 `at`(Unix 毫秒时间戳)、`every`(毫秒间隔)或 6 段 `cron` 表达式,可指定 IANA 时区。
|
||||||
|
|
||||||
## Q: 上下文压缩是什么意思?
|
## Q: 上下文压缩是什么意思?
|
||||||
|
|
||||||
@ -43,3 +45,13 @@
|
|||||||
## Q: 如何查看 LLM 调用日志?
|
## Q: 如何查看 LLM 调用日志?
|
||||||
|
|
||||||
LLM 调用记录存储在 `llm_calls` 表中。可通过 SQLite 客户端直接查询,或在代码中通过 storage 模块访问。
|
LLM 调用记录存储在 `llm_calls` 表中。可通过 SQLite 客户端直接查询,或在代码中通过 storage 模块访问。
|
||||||
|
|
||||||
|
该表可能包含完整用户消息、工具参数和模型响应,按敏感数据处理;不要直接上传或粘贴到公开 issue。
|
||||||
|
|
||||||
|
## Q: 为什么修改了某些 memory 配置却没有看到行为变化?
|
||||||
|
|
||||||
|
当前 `recall_limit`、`idle_consolidation_minutes`、`timeline_retention_days` 和 `max_failures_before_degrade` 都能被配置解析,但每轮 Knowledge 召回仍固定为 5,自动 idle consolidation、Timeline 清理和失败降级循环尚未接入。以当前代码行为为准。
|
||||||
|
|
||||||
|
## Q: Gateway 为什么无法立即退出?
|
||||||
|
|
||||||
|
正常关停会先停止渠道,再取消受 TaskSupervisor 管理的后台任务,并最多等待 10 秒。飞书自身另有 5 秒连接任务兜底。如果持续超过这些上限,应检查是否新增了未受监督的 `tokio::spawn`、不可取消的外部 I/O,或没有 timeout 的 JoinHandle 等待。
|
||||||
|
|||||||
@ -12,16 +12,8 @@
|
|||||||
| `content` | 是 | 消息文本内容 |
|
| `content` | 是 | 消息文本内容 |
|
||||||
| `files` | 否 | 文件路径列表 |
|
| `files` | 否 | 文件路径列表 |
|
||||||
| `origin` | 否 | 消息来源标识,不填则自动使用当前 session_id |
|
| `origin` | 否 | 消息来源标识,不填则自动使用当前 session_id |
|
||||||
| `file_types` | 否 | 指定文件发送类型,`{"路径": "audio"|"file"}`。未指定则自动判断 |
|
|
||||||
|
|
||||||
### file_types 说明
|
`files` 支持绝对路径和 workspace 相对路径,媒体类型由文件扩展名/MIME 自动判断。目前 schema 不支持手工指定 `file_types`。
|
||||||
|
|
||||||
控制文件以何种消息类型发送,主要用于飞书渠道:
|
|
||||||
|
|
||||||
- `"audio"`:作为语音消息发送(仅 opus 格式支持)
|
|
||||||
- `"file"`:作为文件附件发送
|
|
||||||
|
|
||||||
飞书渠道限制:上传类型和消息类型必须一致。opus 文件以 `"audio"` 发送,其他音频(mp3、wav 等)只能以 `"file"` 发送。
|
|
||||||
|
|
||||||
### 示例
|
### 示例
|
||||||
|
|
||||||
@ -29,11 +21,12 @@
|
|||||||
{
|
{
|
||||||
"target_chat_id": "feishu:oc_abc123",
|
"target_chat_id": "feishu:oc_abc123",
|
||||||
"content": "这是生成的音乐文件",
|
"content": "这是生成的音乐文件",
|
||||||
"files": ["/workspace/music.mp3"],
|
"files": ["/workspace/music.mp3"]
|
||||||
"file_types": {"/workspace/music.mp3": "file"}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
发送流程会先把消息写入目标 Session,再调用 `MessageBus::deliver_outbound` 等待真实渠道投递结果(上限 120 秒)。投递失败会作为工具失败返回,不应把“已入队”报告成“已送达”。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## chat_manager — 会话管理
|
## chat_manager — 会话管理
|
||||||
@ -53,20 +46,28 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## cron — 定时任务管理
|
## Cron 定时任务工具
|
||||||
|
|
||||||
管理 cron 定时任务。
|
Cron 不是一个带 `action` 的统一工具,而是六个独立工具;仅在 `gateway.scheduler.enabled=true` 时注册。
|
||||||
|
|
||||||
### 参数
|
| 工具 | 主要参数 | 说明 |
|
||||||
|
|------|----------|------|
|
||||||
|
| `cron_add` | `schedule`, `prompt`, `channel`, `chat_id`; 可选 `name`, `model` | 创建任务 |
|
||||||
|
| `cron_list` | 可选 `status=all|enabled|disabled` | 列出任务 |
|
||||||
|
| `cron_update` | `job_id`; 可选 `prompt`, `schedule`, `channel`, `chat_id`, `model` | 更新指定字段 |
|
||||||
|
| `cron_remove` | `job_id` | 永久删除任务和关联 job runs |
|
||||||
|
| `cron_enable` | `job_id` | 启用并重新计算下次运行时间 |
|
||||||
|
| `cron_disable` | `job_id` | 禁用但保留任务 |
|
||||||
|
|
||||||
| 参数 | 必填 | 说明 |
|
`schedule` 支持:
|
||||||
|------|------|------|
|
|
||||||
| `action` | 是 | 操作: `add`, `list`, `update`, `remove`, `enable`, `disable` |
|
```json
|
||||||
| `name` | add必须 | 任务名称 |
|
{"type":"at","at":1750000000000}
|
||||||
| `schedule` | add需要 | 调度规则: `once`(时间戳), `every`(间隔秒), `cron`(表达式) |
|
{"type":"every","every_ms":3600000}
|
||||||
| `prompt` | add必须 | 任务提示词 |
|
{"type":"cron","expr":"0 0 9 * * *","tz":"Asia/Shanghai"}
|
||||||
| `channel` | add必须 | 执行渠道 |
|
```
|
||||||
| `chat_id` | add必须 | 目标对话 |
|
|
||||||
|
时间戳和间隔单位为毫秒;Cron 表达式为 6 段(秒、分、时、日、月、周)。定时 Agent 不复用聊天历史,`prompt` 必须包含完整上下文,并由 Agent 使用 `send_message` 投递结果。`model` 当前会持久化和展示,但执行仍使用默认 Agent Provider/Model,不能依赖它实现模型覆盖。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -168,15 +169,19 @@
|
|||||||
|
|
||||||
## file_read / file_write / file_edit / file_search / content_search — 文件操作和搜索
|
## file_read / file_write / file_edit / file_search / content_search — 文件操作和搜索
|
||||||
|
|
||||||
工作目录内的文件读写编辑、文件名搜索和内容搜索。详细的参数定义见各工具的 parameters_schema。
|
文件读写编辑、文件名搜索和内容搜索。相对路径从 workspace cwd 解析;默认注册的文件工具也接受绝对路径,因此 workspace 不是硬沙箱。详细参数以各工具的 `parameters_schema` 为准。
|
||||||
|
|
||||||
## bash — 执行命令
|
## bash — 执行命令
|
||||||
|
|
||||||
在本地环境执行 bash 命令,有超时限制和安全检查(阻止 rm -rf /、fork bomb 等)。
|
默认从 workspace cwd 执行 bash 命令。参数为 `command` 和可选 `timeout`;默认 60 秒、最大 600 秒,输出最多约 50,000 字符。命令继承 Gateway 进程权限,可以访问 workspace 外路径;危险模式拦截不是完整沙箱。
|
||||||
|
|
||||||
|
## pty — 持久终端会话
|
||||||
|
|
||||||
|
用于交互式程序和需要保持状态的长运行命令。`action` 支持 `spawn`、`write`、`read`、`kill`、`list`;`write/read/kill` 需要 `session_id`。Gateway 进程退出时 PTY manager 会清理子进程。
|
||||||
|
|
||||||
## http_request / web_fetch — HTTP 和 Web 工具
|
## http_request / web_fetch — HTTP 和 Web 工具
|
||||||
|
|
||||||
发送 HTTP 请求和获取网页内容,有 URL 安全校验(阻止内网/本地访问)。
|
`http_request` 支持 GET/POST/PUT/DELETE/PATCH、headers 和字符串 body;`web_fetch` 提取 HTML/JSON 的可读文本。两者校验 URL 与 DNS 解析结果,阻止回环、私网、link-local 和本地域名,并禁用自动重定向,以降低 SSRF 风险。
|
||||||
|
|
||||||
## calculator — 计算器
|
## calculator — 计算器
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user