Skip to content

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 方案的核心结构可以概括为五层:

  1. core:应用容器、配置、事件、日志、异常
  2. agent:上下文管理、记忆、工具调用、LLM Provider 抽象
  3. workspace:工作空间、文件系统沙盒、模板
  4. plugins:插件加载、权限、注册表、Hook、内置工具
  5. 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,可考虑合并 SQLiteMemoryStoreSQLiteMemoryRepository 为一层。
  • 关键词检索当前为 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 标准contentOpenAICompatibleProvider
DeepSeek-R1/V4reasoning_content + thinking 模式DeepSeekProvider
GLM/智谱(无特殊字段)GLMProvider
GroqreasoningGroqProvider
MinimaxAnthropic Messages API,thinkingMinimaxProvider(AnthropicProvider)
ClaudethinkingAnthropicProvider

参考实现:见 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;缓存观测指标已记录;Anthropic cache_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 上下文策略已完整实现;实际代码已有字段传播,但 ContextBuilderSessionRunner._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,属于平台能力差异;现通过 channel download_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_REPLYHEARTBEAT_OKANNOUNCE_SKIPREPLY_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_SKIPREPLY_SKIP 等 agent 间通信令牌纳入 Phase 3.8 Subagent 编排阶段,不在本阶段实现。

适用场景

  1. 工具调用后无需文字补充 — 搜索/天气等工具已直接返回结构化结果,Agent 不需要再说"这是你要的信息"。
  2. 定时任务/心跳空转 — scheduler 定时触发但无事可报时,用 HEARTBEAT_OK 抑制空消息。
  3. 群聊噪音控制 — 不是每条消息都需要 bot 回应,NO_REPLY 让模型判断是否应该静默。
  4. 主动消息工具已发送 — 如果 Agent 通过 message 工具已直接发送了回复,主回复可以用 NO_REPLY 避免重复。

System Prompt 章节(参考 OpenClaw 格式,适配 nahida-bot 语境):

markdown
## 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.
markdown
## 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.

实现要点

  1. 令牌常量模块nahida_bot/agent/reply_signals.py):

    • 定义 NO_REPLYHEARTBEAT_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:心跳确认检测。
  2. 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 同理。
  3. System Prompt 注入

    • RouterConfigAgentLoopConfig 增加 enable_silent_reply 开关(默认开启)。
    • Context Builder 在拼接 system prompt 时,根据开关决定是否追加 Silent Replies 章节。
    • Heartbeat 章节仅在 scheduler 触发的运行中注入(通过 source_tag 或新参数区分)。
  4. Scheduler 集成

    • SchedulerService.fire() 调用 SessionRunner.run() 时传入 source_tag="scheduler"trigger_type="heartbeat"
    • SessionRunner 根据触发类型决定是否在 system prompt 中注入 Heartbeat 章节。

检测层设计(参考 OpenClaw 的多层防护)

text
模型输出文本

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_readworkspace_writeexecweb_fetchplancron_*edit/apply_patch、记忆工具、消息发送工具和插件配置体系仍待补齐。

以下工具清单参考 OpenClaw 的 31 个内置工具(src/agents/tool-catalog.ts),按依赖程度分为两类。 仅收录不需要 Gateway-Node 分布式架构即可独立实现的工具。 需要 Gateway-Node 的工具(nodesgatewaycanvasbrowser)纳入 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 + markdownifyP0需 SSRF 防护(拒绝私有 IP 段)
message跨 Channel 发消息调用已注册 channel service 的 send_messageP1需路由层: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 v2P3需要 X API 凭证
music_generate音乐生成调用音乐生成 APIP3需要 Provider 支持
video_generate视频生成调用视频生成 APIP3需要 Provider 支持
update_plan更新任务计划本地状态管理P3Agent 内部计划维护

需要 Gateway-Node(不在本阶段范围,纳入 Phase 5):

工具 ID功能依赖原因
nodes发现与操控配对设备需要 Node 注册、心跳、远程执行协议
gateway网关管理与配置需要 Gateway 服务运行
canvas驱动 Node Canvas 画布需要 Node 端 Canvas 运行时
browser浏览器自动化控制需要 Playwright/Chromium 运行时,适合 Node 端;另有官方 Playwright MCP server 可直接对接

实施建议:

  • P0 工具(execweb_searchweb_fetch)应优先实现,它们是 Agent 实用性的关键飞跃。
  • 第一类工具(文件 I/O、记忆)可快速交付,复用已有基础设施。
  • exec 必须配合严格的权限声明(subprocess 权限 + 命令审计),不可跳过权限校验。
  • web_fetch 必须实现 SSRF 防护,拒绝 127.0.0.0/810.0.0.0/8172.16.0.0/12192.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 临时提供 taskinstructions、上下文模式、模型覆盖和工具过滤。
  • 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 的形式暴露运行时服务:

python
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。"""
        ...

普通 Pluginon_load() 中通过 api.register_channel(self) 显式注册自己为 channel service;注册时通过 isinstance(channel, ChannelService) 校验协议满足。

设计收敛说明

  • 宿主不再试图用一组固定的“通信协议标签”描述 Channel 插件的内部实现。
  • Channel 插件可以直接使用第三方 SDK、自带 HTTP client、长轮询、webhook、WebSocket 或其它机制;这些属于插件内部实现细节。
  • 宿主真正关心的是两个问题:
    1. 这个插件是否显式注册了一个 ChannelService
    2. 这个插件是否需要宿主额外提供某种扩展点或共享基础设施

长期规划:宿主扩展点与共享基础设施

当插件确实需要复用宿主能力时,不再通过 channel_protocols 之类的静态标签声明,而是通过显式的 host service / extension point 暴露:

  1. Web Host 扩展点

    • 目标:允许插件挂载 webhook、辅助 route、或少量诊断端点。
    • 建议形态:api.mount_router(...)api.register_webhook_endpoint(...) 或更抽象的 WebHostService
    • 设计原则:插件依赖的是“宿主提供可挂载的 Web 入口”,而不是直接依赖“宿主当前用 FastAPI”这一实现细节。
  2. 共享 HTTP Client 服务

    • 目标:让插件在需要时复用连接池、代理、审计、超时和统一出站策略。
    • 建议形态:api.get_http_client()HttpClientService
    • 设计原则:插件既可以完全自带 SDK/client,也可以选择使用宿主提供的共享客户端;两者都应被允许。
  3. 其它可复用宿主服务

    • 候选范围: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 基类。

当前状态

  1. Manifest 已支持 provider 加载时序PluginManifest.load_phase 字段驱动 pre-agent/post-agent 分阶段加载。
  2. BotAPI/RealBotAPI 已支持 Provider 注册接口 — 插件可通过 register_provider_type() 注册运行时 Provider。
  3. 初始化顺序已调整Application.initialize() 先发现插件、加载 pre-agent 插件,再创建 ProviderManager。
  4. Provider Registry 已可运行时扩展 — 除静态 _REGISTRY 外,已有可卸载的 _RUNTIME_REGISTRY

设计方案

1. Manifest 扩展(可选,偏配置与展示)

yaml
# 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()

python
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 扩展

python
# 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. 初始化顺序调整(两阶段)

text
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 运行时扩展

python
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 在配置中完全等价:

yaml
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 不可与内置类型冲突(deepseekanthropicopenai-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-memorypre-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,而不是重新引入 typechannel_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_depthmax_forward_messagesforward_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. 建议的里程碑顺序

如果按风险和依赖关系排序,建议遵循下面的顺序:

  1. 项目地基(Phase 0)
  2. 核心运行时(Phase 1)
  3. Agent 与 Workspace 联合阶段(Phase 2.1-2.6)
  4. Workspace Sandbox 安全加固(Phase 2.7) ⚠️ 安全闸门
  5. Provider 响应健壮性增强(Phase 2.8) ⚠️ 推荐在 Phase 3 前完成
  6. 回复信号协议(Phase 2.10) — Agent 回复管线的静默/心跳控制
  7. 插件系统与 Channel 接口定义(Phase 3)
  8. 基于插件系统的 Channel 实现(Phase 4)
  9. Subagent 编排与跨会话管理(Phase 3.8,可在 Phase 4 之后或并行推进)
  10. Provider 插件化(Phase 3.9,可在 Phase 4 之后或并行推进)
  11. Gateway 与 Node(Phase 5)
  12. WebUI 与运维工具(Phase 6)
  13. 稳定性、发布与生态(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 解析;剩余风险集中在流式响应和拒绝语义。

风险类型严重程度状态
同 Provider 多模型切换未真正覆盖请求 model🟡 中已完成
流式响应不支持🟡 中待规划(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 四个核心支柱推进,这个项目的技术方向就不会跑偏。