Nahida Bot ROADMAP
本路线图仅基于 Python 方案,不包含 Rust 方案。
1. 目标与范围
Nahida Bot 的目标不是做一个普通聊天机器人,而是做一个以 Agent 为核心、以工作空间为中心、可通过插件扩展、可分布式连接 Node 的 Python Bot 框架。
当前仓库已经越过早期骨架阶段,完成了 Core、Agent、Workspace、Plugin、Telegram Channel、多 Provider 与内置命令的主体闭环。因此,这份 ROADMAP 以“稳定当前 MVP,再走向安全加固、分布式执行与可扩展生态”为主线。
2. 技术路线原则
- 语言与运行时:Python 3.12 +
asyncio - Web 与 Gateway:FastAPI + WebSocket
- 类型与配置:Pydantic v2 + 手工分层配置加载(保留显式优先级控制)
- 数据存储:SQLite +
aiosqlite - 包管理:
uv - CLI:
typer+rich - 日志与可观测性:
structlog - 测试:
pytest+pytest-asyncio - 类型检查:
pyright,必要时补充mypy
3. 模块参考映射(可复用思路)
参考原则:学习架构思路与工程组织,不直接复制实现代码;尤其对非公开来源,仅做行为模式和产品交互层面的抽象借鉴。
| 模块 | 主参考 | 可借鉴点 | 落地提醒 |
|---|---|---|---|
core(应用容器、生命周期) | OpenClaw, AstrBot | 统一启动入口、模块化初始化、优雅退出 | 先保证依赖方向干净,再做功能堆叠 |
core.config(配置系统) | AstrBot, Pydantic 官方示例 | 分层配置、环境变量覆盖、显式优先级策略 | 配置模型必须强类型,避免魔法字符串;当前采用手工 merge 保持优先级可控 |
agent.loop(推理回路) | OpenClaw, claude-code(模式层) | 消息拼装、工具调用回填、流式输出链路 | 不复制具体 prompt 或私有实现细节 |
agent.reply_signals(回复信号协议) | OpenClaw(sentinel token 协议) | 哨兵令牌检测、静默回复抑制、心跳确认、尾部剥离 | 令牌检测必须精确,不能误杀正常大写文本 |
agent.context(上下文管理) | claude-code(模式层), LangGraph | 上下文裁剪、历史管理、状态拼接 | 先可用再优化,优先滑窗策略 |
agent.providers(模型抽象) | OpenClaw, LiteLLM, OpenAI SDK 生态 | Provider 统一接口、错误归一化、重试策略 | 首先打通一个 provider,再扩展 |
agent.providers.adapters(响应适配) | LiteLLM, 各厂商 API 文档 | 多后端响应归一化、推理链提取、流式解析 | ⚠️ 必须处理 DeepSeek-R1 和 Claude thinking |
workspace(文件即上下文) | OpenClaw, AstrBot | 指令文件注入、工作区隔离、状态持久化 | 路径安全必须先于易用性 |
workspace.sandbox(文件沙盒) | AstrBot, claude-code(模式层) | 符号链接防护、TOCTOU 防护、多层防御 | ⚠️ 当前实现仅适合可信本地 MVP;开放不可信插件/远程执行前必须加固 |
plugins(声明式扩展) | OpenClaw, nonebot2 插件生态 | 插件发现、生命周期、能力注册 | 不允许绕过权限模型直连核心 |
plugins.permissions(权限系统) | OpenClaw, Android Manifest 思路 | 声明式权限、运行时拦截、审计日志 | 权限粒度从小开始,逐步放开 |
channels(平台接入层) | AstrBot, nonebot2, aiogram | 多平台适配、消息标准化、事件分发 | 平台差异收敛在 adapter 层 |
gateway/node(分布式执行) | OpenClaw | 节点注册、心跳重连、远程执行协议 | 协议一旦对外发布必须版本化 |
cli(运维入口) | OpenClaw, typer 官方最佳实践 | 命令分组、可读输出、诊断命令 | CLI 输出要面向运维,不只面向开发 |
webui(可视化运维) | AstrBot Dashboard, OpenClaw 控制面思路 | 状态面板、配置可视化、节点管理 | 只消费公开 API,不读内部状态 |
| 测试与质量闸门 | nahida-bot 现有规范, OpenClaw CI 思路 | 分层测试、类型检查、覆盖率门禁 | 新模块必须同时交付测试 |
补充可参考项目:
- nonebot2:插件生态与消息适配设计。
- aiogram:Telegram 领域建模和异步处理。
- LangGraph:状态图化 agent 流程组织。
- LiteLLM:多 provider 抽象和错误兼容。
- FastAPI 官方项目模板:服务分层、依赖注入和测试组织。
关于 claude-code:
- 建议仅参考交互流程、任务分解方式、工具调用行为模式。
- 不复制任何疑似私有实现细节、特定 prompt 文本或内部协议定义。
4. 目标架构
Python 方案的核心结构可以概括为五层:
core:应用容器、配置、事件、日志、异常agent:上下文管理、记忆、工具调用、LLM Provider 抽象workspace:工作空间、文件系统沙盒、模板plugins:插件加载、权限、注册表、Hook、内置工具gateway/node/channels:消息入口、远程节点、平台适配
这五层之间要保持稳定的依赖方向:上层依赖下层,内层不反向依赖外层;core 保持中立,不直接绑定具体渠道或插件实现。
5. 阶段规划(细节与勾选合并)
说明:每个阶段都使用可勾选任务清单,既是执行计划也是验收项。
Phase 0 - 项目地基
目标:把仓库从“文档驱动的设想”变成“可持续开发的 Python 工程”。
任务清单:
前置依赖:无。
风险控制:避免先写业务逻辑再补工具链;先把质量闸门立起来。
参考来源:AstrBot(项目初始化)、OpenClaw(模块拆分)、FastAPI 官方模板。
Phase 1 - 核心运行时
目标:建立应用容器、配置系统、日志系统和事件系统。
任务清单:
前置依赖:Phase 0。
风险控制:不要在本阶段引入具体平台逻辑,保证核心层中立。
参考来源:OpenClaw(生命周期)、AstrBot(配置日志)、Pydantic。
事件系统的结构设计、类型约束、依赖注入集成和参考方案,统一维护在 docs/architecture/event-system.md,ROADMAP 仅保留交付目标与勾选状态。
Phase 2 - Agent 与 Workspace 联合阶段
目标:同步打通 Agent 核心回路与 Workspace 上下文能力,形成最小可用智能闭环。
Phase 2.1 - Workspace 基线与安全边界
Phase 2.2 - 指令注入与上下文预算
Phase 2.3 - Agent Loop 与 Provider 打通
Phase 2.4 - Tool Calling 协议闭环
Phase 2.5 - 记忆模型与持久化
⚠️ 待优化(技术债):当前记忆层存在三层间接(
MemoryStore ABC → SQLiteMemoryStore → SQLiteMemoryRepository → DatabaseEngine),对于单一 SQLite 后端而言抽象层数偏多。后续应考虑:
- 评估是否引入轻量 ORM(如 SQLModel)统一 Repository 与模型层,减少手写 SQL 和序列化代码。
- 若确认只使用 SQLite,可考虑合并
SQLiteMemoryStore与SQLiteMemoryRepository为一层。- 关键词检索当前为 jieba 分词+精确匹配,后续可接入向量检索或 BM25 排序提升召回质量。
Phase 2.5b - LLM 增强记忆(规划中)
当前关键词方案(jieba 分词+精确匹配)可解决基础检索,但存在语义理解缺失(近义词无法召回)和长文本摘要丢失问题。本阶段规划 LLM 辅助的记忆管理方案。
Phase 2.6 - 稳定性增强与阶段验收
⚠️ 待优化(技术债):
MetricsCollector当前为纯内存聚合,缺少持久化和导出能力。后续需增加 flush/export 机制(如 log sink、Prometheus exporter),否则进程重启后指标丢失。- 错误回退文案通过
AgentLoopConfig.provider_error_template配置,但尚未接入 i18n 系统。
Phase 2.7 - Workspace Sandbox 安全增强
⚠️ 重要性:当前沙盒实现仅使用简单路径检查,存在符号链接攻击、TOCTOU 等安全风险。为尽快打通可运行 MVP,本阶段未阻塞 Phase 3/4 的可信本地插件与 Telegram 接入;但在开放不可信第三方插件、远程节点执行或更高权限文件工具前,必须完成安全加固。
参考实现:见 docs/architecture/sandbox-security.md。
Phase 2.8 - Provider 响应健壮性与多后端适配
✅ 已完成:Phase 2.8 和 2.8b 全部完成。Provider 现在支持 OpenAI 兼容族和 Anthropic 族的推理链、Extended Thinking、签名回传等高级特性。
支持的响应格式:
| 后端 | 特殊字段 | Provider 类 |
|---|---|---|
| OpenAI 标准 | content | OpenAICompatibleProvider |
| DeepSeek-R1/V4 | reasoning_content + thinking 模式 | DeepSeekProvider |
| GLM/智谱 | (无特殊字段) | GLMProvider |
| Groq | reasoning | GroqProvider |
| Minimax | Anthropic Messages API,thinking 块 | MinimaxProvider(AnthropicProvider) |
| Claude | thinking 块 | AnthropicProvider |
参考实现:见 docs/architecture/provider-architecture.md。
前置依赖:Phase 1。
风险控制:
- 工具协议要尽早固定,后续插件系统将强依赖该协议。
- 先保证安全边界,再做便捷 API。
参考来源:OpenClaw(Agent + Workspace 模式)、claude-code(流程模式层)、AstrBot(运行时文件组织)、LiteLLM/OpenAI SDK。
Phase 2.9 - 图像理解与多模态上下文
当前进度:Phase 2.9 主链路已完成,安全与平台兼容性已加固。MediaResolver/MediaCache/MediaPolicy 已实现;双 Channel 入站 attachment 已完成;Provider 能力配置可注入 ProviderSlot;vision 主模型路径已覆盖;非 vision fallback 三模式(auto/tool/off)已实现;
image_understand工具已能读取当前回合和 session 历史图片;多轮上下文 cache-aware 策略已实现基础版;Memory 持久化已保存 attachment 元数据、缓存路径/描述和 assistant reasoning,并避免持久化平台临时 URL;缓存观测指标已记录;Anthropiccache_control注入已支持;本地单元测试已通过。
设计原则:
- 主模型支持图片输入时,优先原生传图,不先压成文字描述。
- 主模型不支持图片输入时,提供
image_understand能力,并支持自动 fallback 描述,保证“这张图是什么?”这类消息可直接工作。 - 能力判断以显式配置为准,Provider/模型名启发式只作为保守默认,未知模型默认不支持图片输入。
- 多轮上下文采用 cache-aware 策略:短期内可保留稳定的原生图片内容块来争取 Provider prompt/KV cache 命中;长期不持久化 base64 或过期 URL,只保存媒体引用、缓存路径、hash、描述、Provider cache id 和可用性状态。
任务清单:
参考实现:见 docs/architecture/provider-architecture.md。
实现核对/已发现偏差:
docs/architecture/provider-architecture.md曾描述 reasoning 上下文策略已完整实现;实际代码已有字段传播,但ContextBuilder和SessionRunner._load_history()尚未真正应用/恢复 reasoning、signature 和 metadata。这属于架构承诺尚未落地,不是优化性偏移。- Milky 配置已有
cache_media_on_receive,ROADMAP 也提到“收到消息立刻缓存媒体”,但当前实现只注册了milky_get_resource_temp_url,还没有入站缓存下载。这属于 planned 行为未落地。 Milky converter 当前把图片降级为文本并保留 raw event,避免了 Agent 层感知平台结构。这是合理的 MVP 优化,但需要在 Phase 2.9 中升级为第一类 attachment,否则会限制原生多模态能力。已完成:Milky 和 Telegram converter 均已填充InboundMessage.attachments,同时保留文本降级。- 2026-05-05 修复核对:
image_understand曾只注册但无法读取当前回合图片,属于架构实现缺口;现已通过 request context 保存当前 attachments,并从 Memory 恢复历史 attachments。Telegram 图片仅有file_id、无 URL,属于平台能力差异;现通过 channeldownload_media()钩子落成本地文件后再交给MediaResolver。Milky 文本渲染曾暴露temp_url,属于安全偏移;现已移除文本侧临时 URL,并在持久化层默认不保存 attachment URL。
前置依赖:
- Phase 2.8 Provider 抽象、模型切换和上下文构建链路。
- Phase 3.6 内置工具注册与执行闭环。
- Phase 4 Channel 媒体 segment 解析与资源 URL/缓存能力。
风险控制:
- 不要用模型名硬编码替代显式能力配置;模型能力变化必须可由配置覆盖。
- 不把带 token 的临时 URL、base64 图片或本地敏感路径写入日志/长期记忆。
- 不要为了“省历史”无条件删除最近图片。对支持图片缓存的 Provider,删除原生图片可能降低后续追问的 cache 命中率;应按
media_context_policy、预算、TTL 和缓存指标决策。 - fallback 描述是模型生成的二手信息,必须保留
media_id和可用性状态,避免后续多轮把描述误当作原图。
参考来源:OpenAI/Anthropic/Gemini 多模态消息格式和 prompt/context cache 文档、LiteLLM 能力声明思路、现有 Milky/Telegram 媒体处理经验。
Phase 2.10 - 回复信号协议(Reply Signal Protocol)
参考 OpenClaw 的 sentinel token 设计(
NO_REPLY、HEARTBEAT_OK、ANNOUNCE_SKIP、REPLY_SKIP),为 Agent 回复管线引入结构化的回复控制信号。当前 nahida-bot 的回复路径(AgentLoop.run()→SessionRunner.run()→MessageRouter._send_response())没有任何"不回复"语义 — 只要模型返回了文本,就一定会发送给用户。这在多种场景下会造成不必要的噪音。
设计原则:
- 哨兵令牌(sentinel token)是 AI 模型通过文本输出传递的控制信号,不依赖工具调用或元数据通道。
- 令牌必须是模型整个回复的唯一内容(精确匹配),不允许附加在正常文本后面。
- 检测层应覆盖:完整回复检测、流式前缀检测(用于抑制 typing 指示器)、尾部剥离(处理模型意外追加令牌的情况)。
- 哨兵令牌的具体值通过 system prompt 注入,模型在推理时选择输出。令牌字符串本身不硬编码在业务逻辑中,而是通过配置或常量模块集中管理。
令牌定义:
| 令牌 | 作用 | 注入方式 |
|---|---|---|
NO_REPLY | 完全抑制回复,不向用户发送任何消息 | system prompt 静默回复章节 |
HEARTBEAT_OK | 心跳/定时任务轮询确认,抑制空回复 | system prompt 心跳章节(可选) |
ANNOUNCE_SKIP和REPLY_SKIP等 agent 间通信令牌纳入 Phase 3.8 Subagent 编排阶段,不在本阶段实现。
适用场景:
- 工具调用后无需文字补充 — 搜索/天气等工具已直接返回结构化结果,Agent 不需要再说"这是你要的信息"。
- 定时任务/心跳空转 — scheduler 定时触发但无事可报时,用
HEARTBEAT_OK抑制空消息。 - 群聊噪音控制 — 不是每条消息都需要 bot 回应,
NO_REPLY让模型判断是否应该静默。 - 主动消息工具已发送 — 如果 Agent 通过
message工具已直接发送了回复,主回复可以用NO_REPLY避免重复。
System Prompt 章节(参考 OpenClaw 格式,适配 nahida-bot 语境):
## Silent Replies
Use NO_REPLY ONLY when no user-visible reply is required.
Rules:
- Valid cases: silent housekeeping, deliberate no-op, or after a messaging tool already delivered the user-visible reply.
- Never use it to avoid doing requested work or to end an actionable turn early.
- It must be your ENTIRE message - nothing else.
- Never append it to an actual response.
- Never wrap it in markdown or code blocks.## Heartbeats
If you receive a heartbeat/scheduled poll and there is nothing that needs attention, reply exactly:
HEARTBEAT_OK
If something needs attention, do NOT include "HEARTBEAT_OK"; reply with the alert text instead.实现要点:
令牌常量模块(
nahida_bot/agent/reply_signals.py):- 定义
NO_REPLY、HEARTBEAT_OK常量字符串。 is_silent_reply(text) -> bool:精确匹配检测(大小写不敏感,允许前后空白)。is_silent_prefix(text) -> bool:流式前缀检测("NO","NO_","NO_RE"等片段),用于抑制 typing 指示器。strip_trailing_token(text) -> str | None:尾部令牌剥离;剥离后为空则返回None表示静默。is_heartbeat_ack(text) -> bool:心跳确认检测。
- 定义
SessionRunner 集成:
SessionRunner.run()返回后,MessageRouter._dispatch_message()在调用_send_response()前检查 sentinel token。- 如果检测到
NO_REPLY,跳过发送,记录router.reply_suppressed日志。 - 如果检测到
HEARTBEAT_OK,跳过发送,记录router.heartbeat_ack日志。 - 持久化时:
NO_REPLY的 assistant turn 不持久化(避免历史中堆积无意义令牌);HEARTBEAT_OK同理。
System Prompt 注入:
RouterConfig或AgentLoopConfig增加enable_silent_reply开关(默认开启)。- Context Builder 在拼接 system prompt 时,根据开关决定是否追加 Silent Replies 章节。
- Heartbeat 章节仅在 scheduler 触发的运行中注入(通过
source_tag或新参数区分)。
Scheduler 集成:
SchedulerService.fire()调用SessionRunner.run()时传入source_tag="scheduler"或trigger_type="heartbeat"。- SessionRunner 根据触发类型决定是否在 system prompt 中注入 Heartbeat 章节。
检测层设计(参考 OpenClaw 的多层防护):
模型输出文本
↓
1. 精确匹配 → is_silent_reply(text)
整个文本.trim() 匹配 "NO_REPLY"(大小写不敏感)
匹配 → 抑制回复,不持久化 assistant turn
↓ 不匹配
2. JSON 包络检测 → is_silent_envelope(text)
{"action": "NO_REPLY"} 格式也视为静默
↓ 不匹配
3. 尾部剥离 → strip_trailing_token(text)
移除末尾的 NO_REPLY,返回剩余文本
剩余为空 → 抑制回复
剩余非空 → 发送剩余文本
↓
4. 正常发送任务清单:
前置依赖:Phase 2.6(Agent Loop 稳定性增强)。
风险控制:
- 令牌检测必须精确,不能误杀正常文本(如用户消息中恰好包含 "NO_REPLY" 或模型输出的正常大写内容)。OpenClaw 通过大小写检查和下划线约束来防护。
- 流式场景下,前缀检测只应用于 typing 指示器控制,不能在流式中间截断最终回复。
- System prompt 中的令牌指导必须明确告知模型"必须是整个消息",避免模型在正常回复末尾追加令牌。
- 配置开关默认开启,但允许关闭(某些场景可能不需要静默回复能力)。
参考来源:OpenClaw(sentinel token 协议设计、多层检测、system prompt 格式)。
Phase 3 - 插件系统与 Channel 接口定义
目标:建立声明式、可治理的插件系统,并定义 Channel 作为标准插件接口。
本阶段的关键设计决策:Channel 不是独立层,而是通过插件系统接入。这样可以复用权限模型、生命周期管理和能力注册机制。参考 OneBot/NapCat 等协议,定义统一的 Channel 接口,但支持多种底层通信方式(HTTP、WebSocket、SSE)。
任务清单:
Phase 3.1 — Manifest 与 Loader
Phase 3.2 — 事件系统增强
Phase 3.3 — APIBridge 与权限
Phase 3.4 — 异常隔离与生命周期管理
Phase 3.5 — Channel Service 接口与指令系统
Phase 3.6 — 内置工具插件与验证
当前进度:部分完成。内置插件已落地
workspace_read、workspace_write、exec、web_fetch、plan、cron_*;edit/apply_patch、记忆工具、消息发送工具和插件配置体系仍待补齐。
以下工具清单参考 OpenClaw 的 31 个内置工具(
src/agents/tool-catalog.ts),按依赖程度分为两类。 仅收录不需要 Gateway-Node 分布式架构即可独立实现的工具。 需要 Gateway-Node 的工具(nodes、gateway、canvas、browser)纳入 Phase 5 范围。 Subagent 编排和跨会话管理可在本地单进程内实现,纳入 Phase 3.8。
第一类 — 已有基础设施,可直接包装为工具插件:
这些功能已在 Phase 2 中实现,只需通过插件系统的 register_tool 注册为可调用工具。
| 工具 ID | 功能 | 依赖现状 | 备注 |
|---|---|---|---|
read | 读取工作空间文件 | Workspace sandbox 已实现 | 包装 workspace.read_file() |
write | 创建/覆写工作空间文件 | Workspace sandbox 已实现 | 包装 workspace.write_file() |
edit | 精确编辑文件(行级替换) | Workspace sandbox 已实现 | 需实现行级 diff 编辑逻辑 |
apply_patch | 多 hunk 文件补丁 | Workspace sandbox 已实现 | 需实现 unified diff 解析与应用 |
memory_search | 记忆语义检索 | MemoryStore 已实现 | 包装 memory.search(),已有 jieba 分词 |
memory_get | 读取记忆文件 | MemoryStore 已实现 | 包装 memory.get() |
第二类 — 无需 Gateway-Node,需新增实现:
这些工具需要新的实现,但不依赖分布式架构,可在本地独立运行。
| 工具 ID | 功能 | 实现要点 | 优先级 | 备注 |
|---|---|---|---|---|
exec | 执行 shell 命令 | asyncio.create_subprocess_exec + 超时 + 输出截断 | P0 | ⚠️ 需权限控制(命令白名单/黑名单) |
web_search | 网页搜索 | 调用搜索 API(SerpAPI / Bing / DuckDuckGo) | P0 | 可用 duckduckgo-search Python 包零成本起步 |
web_fetch | 获取网页内容 | HTTP GET + HTML→Markdown(readability-lxml + markdownify) | P0 | 需 SSRF 防护(拒绝私有 IP 段) |
message | 跨 Channel 发消息 | 调用已注册 channel service 的 send_message | P1 | 需路由层:target → channel + chat_id |
cron | 定时任务调度 | 本地调度器(APScheduler / asyncio 定时器) | P1 | 本地模式不需要 Gateway |
tts | 文本转语音 | API 调用(edge-tts 免费 / OpenAI TTS) | P2 | 可用 edge-tts 零成本起步 |
image_understand | 图片理解 | 对非 vision 主模型调用 fallback vision Provider 生成描述;vision 主模型优先原生传图 | P2 | 详见 Phase 2.9 |
image_generate | 图片生成 | 调用图片生成 API(DALL-E / Stable Diffusion / Flux) | P2 | 需配置图片 Provider |
code_execution | 沙箱 Python 执行 | subprocess + 资源限制 + 输出截断 | P2 | 可参考 OpenClaw 的远程沙箱模式 |
x_search | 搜索 X/Twitter | 调用 X API v2 | P3 | 需要 X API 凭证 |
music_generate | 音乐生成 | 调用音乐生成 API | P3 | 需要 Provider 支持 |
video_generate | 视频生成 | 调用视频生成 API | P3 | 需要 Provider 支持 |
update_plan | 更新任务计划 | 本地状态管理 | P3 | Agent 内部计划维护 |
需要 Gateway-Node(不在本阶段范围,纳入 Phase 5):
| 工具 ID | 功能 | 依赖原因 |
|---|---|---|
nodes | 发现与操控配对设备 | 需要 Node 注册、心跳、远程执行协议 |
gateway | 网关管理与配置 | 需要 Gateway 服务运行 |
canvas | 驱动 Node Canvas 画布 | 需要 Node 端 Canvas 运行时 |
browser | 浏览器自动化控制 | 需要 Playwright/Chromium 运行时,适合 Node 端;另有官方 Playwright MCP server 可直接对接 |
实施建议:
- P0 工具(
exec、web_search、web_fetch)应优先实现,它们是 Agent 实用性的关键飞跃。 - 第一类工具(文件 I/O、记忆)可快速交付,复用已有基础设施。
exec必须配合严格的权限声明(subprocess权限 + 命令审计),不可跳过权限校验。web_fetch必须实现 SSRF 防护,拒绝127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16等私有网段。message工具可作为 channel service 跨消息路由的基础,先支持同 Channel 回复,再扩展跨 Channel。
任务清单:
Phase 3.7 — SDK 分离(可选前置)
Phase 3.8 — Subagent 编排与跨会话管理
Subagent 编排和跨会话操作不需要分布式架构。nahida-bot 是单进程 asyncio 模型,子 Agent 就是同一进程内独立的
AgentLoop实例,会话数据全在本地 SQLite。因此这些能力可在本地完整实现,无需等待 Gateway-Node。远程执行场景(如 GPU 节点上跑重模型)才需要 Phase 5 的 Gateway-Node。本地编排先落地,远程扩展作为 Phase 5 的增强。
架构文档:见 docs/architecture/agent-orchestration.md。
本阶段将 AgentLoop 上层补齐为可运维的本地编排系统,但设计上保持轻量:
SubagentSpec:一次性子任务说明,由主 Agent 临时提供task、instructions、上下文模式、模型覆盖和工具过滤。AgentRun:描述一次运行,统一主聊天、子 Agent、cron、CLI 和未来远程执行。BackgroundTask:持久化后台工作账本,记录queued -> running -> terminal状态。AgentRegistry:进程内运行时注册表,跟踪父子关系、取消令牌、结果和异常。AgentRunQueue:per-session lane 串行 + main/subagent/cron global lane 并发控制。AgentRunExecutor:很薄的执行器接口;首版只有LocalAgentRunExecutor,Phase 5 再增加RemoteNodeRunExecutor。AgentOrchestrator:统一创建 run、排队、调用 executor、更新任务状态、投递完成事件。
设计收敛:
- 子 Agent 不是长期 profile,不要求为
research/coder/reviewer等角色预先写配置;主 Agent 每次 spawn 时临时写任务提示词。 - 可以后续扩展
AgentProfile,但它只用于长期 persona、channel routing 或默认模型/工具配置,不是 Phase 3.8 MVP 的核心对象。 - 基于现有
session_id系统创建 child session,不另起一套 agent session 管理。 - 固定只支持一层子 Agent:主 Agent 可以 spawn,子 Agent 默认不能再 spawn。
- Gateway-Node 对 Agent 侧透明,只通过
AgentRunExecutor接口预留。 - 权限首版只做粗粒度 hook、配额和工具过滤,不做复杂 policy DSL。
- Agent-as-tool 是 MVP:编排能力通过内置工具暴露给主 Agent。
- A2A /
sessions_send只做最小record_only|enqueue事件接口,不做多轮 ping-pong。
内置工具:
| 工具 ID | 功能 | 首版约束 |
|---|---|---|
agent_spawn | 派生后台子 Agent | 默认 context_mode=isolated,返回 task_id/run_id,不阻塞父 Agent |
agent_yield | 结束当前父 run,等待子任务完成事件后续跑 | 超时不取消子 Agent,只投递当前状态 |
agent_wait | 当前工具调用内等待子 Agent 结果 | 可选;超时只返回当前状态 |
agent_list | 列出当前 session 可见子任务 | 不泄漏其它 session / 用户任务 |
agent_stop | 取消子 Agent | 默认只能取消当前 session 创建的子任务 |
sessions_list | 列出可见会话 | 受权限和会话范围限制 |
sessions_history | 读取安全过滤后的历史 | 不返回 base64、临时 URL、raw_event、reasoning 原文 |
sessions_send | 向目标会话注入消息 | 标记为 agent/system 事件,不伪装成用户消息 |
session_status | 查询会话和 run 状态 | 返回 active run、队列、最近任务摘要 |
任务清单:
ChannelService 接口设计(关键产出物):
Channel 应该以普通 Plugin 的形式暴露运行时服务:
class ChannelService(Protocol):
@property
def channel_id(self) -> str: ...
async def handle_inbound_event(self, event: dict[str, Any]) -> None:
"""来自外部平台的事件回调(触发方式由插件自己决定)。"""
...
async def send_message(self, target: str, message: OutboundMessage) -> str:
"""向外部平台发送消息,返回消息 ID。"""
...普通 Plugin 在 on_load() 中通过 api.register_channel(self) 显式注册自己为 channel service;注册时通过 isinstance(channel, ChannelService) 校验协议满足。
设计收敛说明:
- 宿主不再试图用一组固定的“通信协议标签”描述 Channel 插件的内部实现。
- Channel 插件可以直接使用第三方 SDK、自带 HTTP client、长轮询、webhook、WebSocket 或其它机制;这些属于插件内部实现细节。
- 宿主真正关心的是两个问题:
- 这个插件是否显式注册了一个
ChannelService - 这个插件是否需要宿主额外提供某种扩展点或共享基础设施
- 这个插件是否显式注册了一个
长期规划:宿主扩展点与共享基础设施:
当插件确实需要复用宿主能力时,不再通过 channel_protocols 之类的静态标签声明,而是通过显式的 host service / extension point 暴露:
Web Host 扩展点
- 目标:允许插件挂载 webhook、辅助 route、或少量诊断端点。
- 建议形态:
api.mount_router(...)、api.register_webhook_endpoint(...)或更抽象的WebHostService。 - 设计原则:插件依赖的是“宿主提供可挂载的 Web 入口”,而不是直接依赖“宿主当前用 FastAPI”这一实现细节。
共享 HTTP Client 服务
- 目标:让插件在需要时复用连接池、代理、审计、超时和统一出站策略。
- 建议形态:
api.get_http_client()或HttpClientService。 - 设计原则:插件既可以完全自带 SDK/client,也可以选择使用宿主提供的共享客户端;两者都应被允许。
其它可复用宿主服务
- 候选范围:scheduler、secrets/config、对象存储、审计日志、后台任务执行。
- 原则:只有当宿主提供这些能力能显著降低插件重复实现和运维成本时,才上升为正式扩展点。
约束:
- 插件的“角色”不由 manifest 分类字段决定,而由运行时注册动作决定。
- Channel / Provider / Tool / Command 不是互斥类别;一个普通 Plugin 可以同时注册多种能力。
- 若未来需要展示层分类,优先使用文档或 tags,而不是重新引入驱动运行时语义的
type字段。
前置依赖:Phase 2。
风险控制:
- 不要为了"插件方便"绕过权限检查,channel service plugin 仍需声明所需权限。
- channel service plugin 的 webhook 端点需要鉴权与频率限制,防止欺骗。
- 多 Channel 并存时需要隔离会话上下文,避免消息混淆。
参考来源:OpenClaw(插件 contract)、nonebot2(扩展生态)、OneBot 协议、NapCat 设计、Android Manifest 思路。
Phase 3.9 — Provider Plugin(模型 Provider 插件化)
当前进度:部分完成。运行时 Provider 注册、阶段化插件加载、Provider Registry 扩展和生命周期清理都已落地;后续重点应放在 provider 配置/校验与宿主扩展点,而不是再引入专用
ProviderPlugin基类。
当前状态:
- Manifest 已支持 provider 加载时序 —
PluginManifest.load_phase字段驱动 pre-agent/post-agent 分阶段加载。 - BotAPI/RealBotAPI 已支持 Provider 注册接口 — 插件可通过
register_provider_type()注册运行时 Provider。 - 初始化顺序已调整 —
Application.initialize()先发现插件、加载pre-agent插件,再创建 ProviderManager。 - Provider Registry 已可运行时扩展 — 除静态
_REGISTRY外,已有可卸载的_RUNTIME_REGISTRY。
设计方案:
1. Manifest 扩展(可选,偏配置与展示)
# plugin.yaml 示例
id: "provider-ollama"
name: "Ollama Provider"
load_phase: "pre-agent" # Provider 插件必须在 Agent 初始化前加载
version: "0.1.0"
entrypoint: "plugin:OllamaPlugin"
# 可选 provider metadata:仅用于 host 侧配置描述/展示,
# 不作为运行时“这是 provider 插件”的判定条件
provider:
type_key: "ollama" # 注册到 create_provider() 的 type 名称
config_schema: # JSON Schema:声明此 Provider 接受哪些配置项
type: object
required: ["base_url", "models"]
properties:
base_url: { type: string, default: "http://localhost:11434/v1" }
models:
type: array
items: { type: string }
default: ["llama3"]
timeout: { type: number, default: 60 }2. 普通 Plugin + register_provider_type()
class OllamaPlugin(Plugin):
def create_provider(self, config: dict[str, Any]) -> ChatProvider:
return OllamaProvider(config)
async def on_load(self) -> None:
self.api.register_provider_type(
type_key="ollama",
factory=self.create_provider,
config_schema={
"type": "object",
"required": ["base_url", "model"],
"properties": {
"base_url": {"type": "string"},
"model": {"type": "string"},
},
},
)3. BotAPI 扩展
# BotAPI 协议新增
def register_provider_type(
self,
type_key: str,
factory: Callable[[dict], ChatProvider],
config_schema: dict | None = None,
) -> None:
"""注册一个 Provider 类型,使其可在 YAML 配置中使用。"""
...RealBotAPI 实现将调用委托给 ProviderRegistry.register_runtime()(新增方法,区别于 @register_provider 的 static registration)。
4. 初始化顺序调整(两阶段)
Application.initialize():
1. _init_database() + _init_memory()
2. _init_plugin_manager() # 创建 PluginManager
3. _load_provider_plugins() # ← 新增:发现并加载 load_phase=pre-agent 的插件
4. _init_agent_subsystem() # 现在可用插件注册的 Provider 类型
5. _init_workspace_subsystem()
6. _load_remaining_plugins() # 加载 tool/channel/hook 等插件
7. _init_scheduler()关键变更:将 _init_agent_subsystem() 从当前位置(plugin loading 之前)移到 provider plugin 加载之后。这需要把 _init_plugin_manager() 拆分为两个阶段。
5. Provider Registry 运行时扩展
class ProviderRegistry:
_static: dict[str, ProviderDescriptor] = {} # @register_provider 装饰器填充
_runtime: dict[str, RuntimeProviderDescriptor] = {} # 插件 register_provider_type() 填充
def create_provider(self, type_key: str, **kwargs) -> ChatProvider:
# 优先查 static(内置),fallback 到 runtime(插件)
desc = self._static.get(type_key) or self._runtime.get(type_key)
if desc is None:
raise ValueError(f"Unknown provider type: {type_key}")
return desc.cls(**kwargs) if desc.cls else desc.factory(kwargs)6. 配置集成
插件注册的 Provider 与内置 Provider 在配置中完全等价:
providers:
deepseek-main:
type: deepseek # 内置
api_key: "${DEEPSEEK_API_KEY}"
...
ollama-local:
type: ollama # ← 由 provider-ollama 插件注册
base_url: "http://localhost:11434/v1"
model: "llama3"任务清单:
风险控制:
- Provider 插件注册的
type_key不可与内置类型冲突(deepseek、anthropic、openai-compatible等)。 - Provider 插件的生命周期必须与 AgentLoop 解耦:插件卸载不应中断正在进行的请求。
config_schema校验应在 Provider 创建前执行,错误配置不应导致启动崩溃(应 skip 并记录警告)。- 安全考虑:Provider 插件处理 API key 等敏感信息,权限系统需增加
provider能力声明。
架构反思:加载时序与插件类型泛化
Provider Plugin 的核心难题是加载时序——ProviderManager 必须在插件注册 Provider 类型之后创建,但常规插件的加载又在 ProviderManager 创建之后。这个"先有鸡还是先有蛋"的问题在插件系统中很常见,典型解法有四种:
1. 阶段化加载(Phase-based loading)
为插件 manifest 引入
load_phase字段,定义显式加载阶段(如pre-agent/post-agent)。Loader 按 phase 顺序依次处理。Provider 插件声明load_phase: pre-agent,普通插件默认post-agent。这是最实用的方案——语义清晰,实现简单,且对现有流程改动最小。Application.initialize()只需把_load_plugins()拆成_load_plugins(phase="pre-agent")和_load_plugins(phase="post-agent")两步。2. 依赖声明(Dependency declaration)
插件声明
provides: ["provider:ollama"]和requires: ["subsystem:agent"],Loader 做拓扑排序决定加载顺序。更灵活但更复杂——需要定义服务命名空间、循环依赖检测、缺失依赖处理等。对只有两三个阶段需求的系统来说过度设计。3. 惰性初始化(Lazy initialization)
ProviderManager 不在启动时创建,而是在第一次
SessionRunner.run()被调用时按需构建。这样所有插件都可以在常规阶段加载,register_provider_type()注册的类型在第一次请求时被消费。优点是不需要修改初始化流程;缺点是 Provider 配置错误不会在启动时暴露,而是在第一次对话时才失败,增加了运维排查成本。4. 两遍扫描(Two-pass discovery)
第一遍扫描所有插件目录,收集 manifest 但不加载代码,只提取类型和能力声明。第二遍根据收集到的信息决定加载顺序。优点是可以在不执行插件代码的情况下做全局规划;缺点是实现复杂,且 manifest 必须包含足够的信息来驱动决策(当前的 manifest 设计不完全满足这个需求)。
推荐方案:阶段化加载(方案 1)。理由:实现成本最低、语义最直观、对现有架构改动最小。一个
load_phase字段 +_load_plugins()接受 phase 参数即可。如果未来出现更多阶段需求(如pre-memory、pre-scheduler),phase 枚举自然扩展。关于插件类型的泛化:Channel 和 Provider 的特殊性不应体现在“独立插件基类”或“静态协议标签”上,而应体现在“普通 Plugin 在生命周期里显式注册服务”。普通插件注册回调、核心调用(Tool、Command、Event);channel/provider 插件则额外通过
api.register_channel()/api.register_provider_type()暴露运行时服务。Provider 的时序需求由load_phase建模,Channel 通过运行时协议校验确保注册对象合法,二者都不再要求专门基类。
长期规划:Host 扩展点优先于协议分类。后续如果需要支持 webhook 挂载、共享 HTTP client、后台任务、统一 secrets/config 等能力,优先新增明确的 host service / extension point,而不是重新引入
type、channel_protocols这类静态分类字段。宿主应该表达“我能提供什么能力”,而不是试图推断“插件内部用了什么传输协议或 SDK”。
Phase 4 - 基于 Channel Service Plugin 的 Telegram 接入 + Multi-Provider + 内置命令
目标:实现 Telegram Bot 接入、多 Provider 支持、以及核心命令插件。
Phase 4 已完成。系统现在支持:Telegram 长轮询消息接收、多 LLM Provider 动态切换、 内置命令(/reset, /new, /status, /model, /help)、会话管理、以及完整的资源清理。
任务清单:
Phase 4.1 — Telegram Bot API 基础
Phase 4.2 — 消息标准化与转换
Phase 4.3 — Channel Service 集成
Phase 4.4 — 端到端闭环验证
Phase 4.5 — Multi-Provider 支持与内置命令
/reset清空当前会话历史/new开始新会话/status查看当前会话和模型信息/model列出/切换模型/help列出所有命令
Phase 4.6 — Milky QQ Channel Plugin(临时新增计划项)
目标:基于 Milky 协议和 Lagrange.Milky 实现一个 QQ Channel 插件,复用现有 Plugin + ChannelService 运行时模型,打通 QQ 私聊/群聊消息到 Agent 的完整闭环。
设计决策:不复用
milky-python-sdk的 Client 或 Bot 框架。Nahida Bot 侧直接实现 Milky HTTP API client 和 WebSocket/event事件流;仅参考milky-python-sdk的消息结构、segment 命名和类型建模方式,避免把第三方生命周期、事件分发和命令系统引入 Nahida。
参考事实:Milky 协议端通过
/api/:api接收 HTTP POST API 调用,通过/event推送事件,事件传输支持 SSE、WebSocket、WebHook;Lagrange.Milky 当前实现 WebSocket 和 WebHook,SSE 标记为 wontimpl。因此 Nahida Bot 侧优先实现 WebSocket client 模式,WebHook 等待 Host Web 扩展点成熟后再接入。Milky 与 OneBot 不同,当前没有定义可在同一条 WebSocket 上同时承载 API 请求和事件响应的{action, params, echo}RPC envelope。
Phase 4.6.1 — 目录与依赖基线
Phase 4.6.2 — 配置模型
Phase 4.6.3 — Milky HTTP API Client
Phase 4.6.4 — 消息结构与 Segment 建模
后续实现提醒:入站
forward段只是forward_id引用,真正内容需要 Phase 4.6.3 的 client 支持get_forwarded_messages()后在 Phase 4.6.6 里递归拉取并填充到IncomingForwardSegment.messages。递归必须受max_forward_depth、max_forward_messages和forward_render_max_chars限制。
Phase 4.6.5 — WebSocket Event Stream
Phase 4.6.6 — 入站消息转换
Phase 4.6.7 — 出站消息转换与发送
Phase 4.6.8 — Plugin 生命周期集成
Phase 4.6.9 — 媒体资源工具
注意:Lagrange.milky 的媒体部分可能会有问题,需要在收到消息的时候立刻把其中的媒体文件缓存下来,否则 URL 可能会过期。目前暂不确定这个是 milky-tea 的问题还是 Lagrange.milky 的问题,这里可能需要处理。
注 2:检查了一下 milky 的文档,似乎图片的预期就是一个临时的 URL ,但是 Milky 又确实提供了一个通过 resource_id 获取 temp_url 的 api 端点,这里可能需要考虑一下。
Phase 4.6.10 — 测试与文档
MVP 验收:
前置依赖:
- Phase 3.5
ChannelService接口与消息标准化流程。 - Phase 4 Telegram Channel 的插件生命周期、发送重试和媒体工具经验。
- Phase 5.x
WebHostService(仅 WebHook 模式需要;WebSocket MVP 不阻塞)。
风险控制:
- Milky 协议端本身提供 HTTP 服务,默认只建议连接
127.0.0.1或内网地址,必须支持access_token。 - QQ 消息段比当前
InboundMessage.text更丰富,首版不得丢弃原始事件,所有未完全支持的段必须保存在raw_event并以结构化文本降级。 - Lagrange.Milky 对部分 Milky 能力标记为 wontimpl,插件实现必须以能力探测和错误降级为准,不能假设完整协议覆盖。
- WebHook 模式不要绕过插件宿主扩展点临时开 HTTP server,避免生命周期、鉴权和端口管理分裂。
- 自写 HTTP/WebSocket 客户端需要补齐错误处理和测试,避免把协议细节散落在
plugin.py中。
参考来源:
- Milky 协议文档:
https://milky.ntqqrev.org/ - Lagrange.Milky README:
https://github.com/LagrangeDev/LagrangeV2/blob/main/Lagrange.Milky/README.md - Lagrange.Milky 配置文档:
https://lagrangedev.github.io/Lagrange.Milky.Document/configuration/overview milky-python-sdk:仅参考消息结构设计,不作为运行时 Client 依赖,https://github.com/notnotype/milky-python-sdk
前置依赖:Phase 3(ChannelService 接口)。
风险控制:
- 平台差异统一收敛在 channel service plugin 实现内部,不渗透进 Agent 核心或其他插件。
- 第一个 channel service plugin 的稳定性直接影响用户体验,务必包含充分的测试和监控。
- 多 Channel 并存时,核心层应该透明地支持(会话隔离、上下文管理)。
参考来源:AstrBot、nonebot2、aiogram、OneBot 协议、NapCat、Telegram Bot API 生态。
Phase 5 - Gateway 与 Node
目标:实现 Python 版远程节点控制能力。
任务清单:
前置依赖:Phase 4。
风险控制:协议一旦对外开放,默认只做向后兼容变更。
参考来源:OpenClaw(Gateway-Node 模式)、FastAPI WebSocket 实践。
Phase 5.x — Host Extension Points(长期规划)
这部分不是近期工作,放入长期规划。目标是在不破坏“普通 Plugin + 显式服务注册”模型的前提下,为插件提供少量高价值的宿主能力。
Phase 6 - WebUI 与运维工具
目标:让系统可视化、可配置、可诊断。
任务清单:
前置依赖:Phase 5。
风险控制:WebUI 只消费公开 API,不直接耦合内部模块。
参考来源:AstrBot Dashboard、OpenClaw 控制面思路、typer + rich。
Phase 7 - 稳定性、发布与生态
目标:把项目从“能跑”推进到“能发版、能增长”。
任务清单:
前置依赖:Phase 6。
风险控制:发布阶段不再做核心架构改造,优先稳定与补文档。
参考来源:OpenClaw(发布治理)、AstrBot(文档维护)、PyPA 官方指南。
6. 建议的里程碑顺序
如果按风险和依赖关系排序,建议遵循下面的顺序:
- 项目地基(Phase 0)
- 核心运行时(Phase 1)
- Agent 与 Workspace 联合阶段(Phase 2.1-2.6)
- Workspace Sandbox 安全加固(Phase 2.7) ⚠️ 安全闸门
- Provider 响应健壮性增强(Phase 2.8) ⚠️ 推荐在 Phase 3 前完成
- 回复信号协议(Phase 2.10) — Agent 回复管线的静默/心跳控制
- 插件系统与 Channel 接口定义(Phase 3)
- 基于插件系统的 Channel 实现(Phase 4)
- Subagent 编排与跨会话管理(Phase 3.8,可在 Phase 4 之后或并行推进)
- Provider 插件化(Phase 3.9,可在 Phase 4 之后或并行推进)
- Gateway 与 Node(Phase 5)
- WebUI 与运维工具(Phase 6)
- 稳定性、发布与生态(Phase 7)
这个顺序的核心原因是:
- Phase 0-2.6 建立最小智能闭环(应用容器 -> 核心运行时 -> Agent + Workspace)
- Phase 2.7-2.8 安全与健壮性加固(Phase 2.8 已完成;Phase 2.7 作为开放不可信插件/远程执行前的安全闸门)
- Phase 2.10 回复控制增强(在 Phase 3 之前完善 Agent 输出管线,为后续 scheduler 心跳和群聊噪音控制打基础)
- Phase 3-4 打通插件和 Channel(先定义接口,允许多种通信协议;再实现具体 Channel 作为插件)
- Phase 3.8 本地 Subagent 编排和跨会话管理(不依赖 Gateway-Node,单进程 asyncio 即可实现)
- Phase 3.9 Provider 插件化(扩展插件系统支持第三方 Provider 注册)
- Phase 5-6 扩展分布式与运维(Gateway-Node + WebUI)
- Phase 7 稳定化与商业化(发版、CI/CD、生态)
关键设计点:
- Phase 2.7 是安全闸门:不安全的沙盒会威胁整个系统安全。为快速形成可运行 MVP,可信本地插件和 Telegram 接入可先推进;但开放不可信第三方插件、远程执行、文件写工具扩权前必须修复。
- Phase 2.8 推荐优先:Provider 响应格式差异会直接影响 Agent 能力,尽早适配可减少后续返工。
- Phase 3 中的 Channel 接口设计直接服务于 Phase 4,避免核心层改造。
建议实践方式:
- 主线阶段:按 Phase 0 -> Phase 7 推进。
- 并行事项:测试基建、文档维护、示例维护可全程并行。
- 冻结策略:每个 Phase 结束时冻结接口一次,避免跨阶段大范围返工。
7. MVP 定义
第一版可接受的 MVP 不要求完整生态,但必须包含以下能力:
- 一个可启动的 Python 应用容器
- 一个能工作的 Agent Loop
- 一个可用的 Workspace 目录
- 一个插件加载机制
- 一个 Channel 接入实现
- 一个最小可用的 Gateway 或 Node 通信闭环
- 基础测试与类型检查
如果上述能力都没有完成,项目仍然停留在“设计文档阶段”;如果都完成了,项目就进入“可扩展平台阶段”。
MVP 建议额外约束:
- 必须包含至少一个真实平台消息回路。
- 必须包含至少一个真实 Provider 回路。
- 必须包含最小权限控制,而不是“先全放开”。
8. 风险与约束
- Python 方案的性能上限主要依赖异步 I/O 设计和插件隔离质量,而不是单纯依赖语言性能。
- 插件系统一旦失控,会直接影响安全性和稳定性,因此权限模型必须先于生态扩张落地。
- Workspace 机制是项目的核心资产。当前可在可信本地 MVP 中先保持简单沙盒;任何面向不可信插件、远程节点或高权限文件工具的能力,都必须先补齐文件安全边界。
- Gateway-Node 协议一旦发布,就属于稳定契约,后续只能做兼容性演进。
⚠️ 关键安全风险(开放不可信扩展前必须解决):
8.1 Workspace Sandbox 安全风险
当前 workspace/sandbox.py 实现存在以下已知漏洞:
| 风险类型 | 严重程度 | 状态 |
|---|---|---|
| 符号链接攻击 | 🔴 高 | 待修复(Phase 2.7) |
| TOCTOU 竞态条件 | 🔴 高 | 待修复(Phase 2.7) |
| 硬链接攻击 | 🟡 中 | 待修复(Phase 2.7) |
| Unicode/编码绕过 | 🟡 中 | 待修复(Phase 2.7) |
| 特殊文件系统对象 | 🟡 中 | 待修复(Phase 2.7) |
| 无文件大小限制 | 🟡 中 | 待修复(Phase 2.7) |
缓解措施:在 Phase 2.7 中实现多层防御机制,详见 docs/architecture/sandbox-security.md。
8.2 Provider 响应兼容性风险
当前 Provider 层已支持 OpenAI 兼容族、DeepSeek、Groq、GLM、Minimax 和 Anthropic/Claude thinking 解析;剩余风险集中在流式响应和拒绝语义。
| 风险类型 | 严重程度 | 状态 |
|---|---|---|
| 流式响应不支持 | 🟡 中 | 待规划(Phase 3+) |
| 拒绝标记未处理 | 🟢 低 | 待规划 |
缓解措施:推理链适配和 per-request model override 已完成;后续补齐流式响应和更细的 refusal 语义处理,详见 docs/architecture/provider-architecture.md。
额外风险清单:
- 过早引入多 Provider、多 Channel,可能导致核心抽象失稳。
- 没有回归测试的接口调整会快速积累技术债。
- 插件热加载如果没有隔离与回滚机制,会成为线上稳定性风险点。
建议的质量闸门:
- 每个 Phase 至少新增一组单元测试和一组集成测试。
- 关键协议(消息模型、插件 manifest、Gateway-Node 报文)要有固定示例和回归测试。
- 任何跨模块重构都必须伴随文档更新。
9. 结语
Python 方案的价值不只是“更快写出来”,而是用 Python 的 AI 生态、类型校验和开发体验,把一个 Agent Bot 框架做成真正可持续演进的平台。只要围绕 Agent、Workspace、插件系统和 Gateway-Node 四个核心支柱推进,这个项目的技术方向就不会跑偏。