From 3c58b9141b4c803f92967785843a155a5ee5ba2d Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Fri, 8 May 2026 17:45:17 +0800 Subject: [PATCH] docs: design agent runner pluginization --- docs/agent-runner-pluginization/README.md | 400 ++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 docs/agent-runner-pluginization/README.md diff --git a/docs/agent-runner-pluginization/README.md b/docs/agent-runner-pluginization/README.md new file mode 100644 index 00000000..566f7950 --- /dev/null +++ b/docs/agent-runner-pluginization/README.md @@ -0,0 +1,400 @@ +# Agent Runner 插件化设计 + +## 1. 背景 + +当前 `feat/agent-runner-plugin` 分支已经验证了一个最小路径:SDK 增加 `AgentRunner` 组件,LangBot 在 Pipeline 的 `runner` 配置项中动态列出插件提供的 runner,并在 `ChatMessageHandler` 中通过 `plugin_connector.run_agent()` 调用插件实现。 + +这个方向能把内置 `RequestRunner` 之外的 Agent 实现放到插件中,但它仍然沿用“私聊/群聊消息进入 Pipeline,再由 runner 产出回复”的旧模型。它没有解决后续 Agent 需要面对复杂上下文的问题,也没有为 EBA 计划里的事件驱动能力留下足够清晰的扩展面。 + +本设计只聚焦 Agent Runner 插件化。EBA 文档中的事件体系、平台 API、事件路由只作为接口预留和未来兼容参考,不纳入本阶段实现范围。 + +## 2. 目标与非目标 + +目标: + +- 将 Agent Runner 从 LangBot 内置 runner 列表中解耦,允许插件提供新的 Agent 执行器。 +- 保持当前聊天 Pipeline 可用,并允许现有消息场景选择插件 Agent Runner。 +- 设计新的 Agent 上下文模型,使 runner 不只依赖 `query.messages` 和 `user_message`,还能承载事件、会话、资源、工具、知识库、平台能力和业务状态。 +- SDK 提供稳定的 `AgentRunner` 组件接口、上下文实体、返回实体、配置 schema 和运行期 API。 +- LangBot 负责 runner 发现、配置装配、权限校验、运行调度、流式结果转换、错误隔离和兼容层。 +- 为未来 EBA 的非消息事件接入预留 `event`、`actor`、`subject`、`platform_capabilities` 等上下文字段。 + +非目标: + +- 不在本阶段实现 EBA EventBus、EventRouter、平台多事件监听或统一平台 API。 +- 不要求所有内置 `RequestRunner` 立即改写成插件。 +- 不改变现有 Pipeline 的阶段链和私聊/群聊入口。 +- 不引入插件内自定义长驻调度器;Agent Runner 仍由 LangBot 显式调用。 + +## 3. 当前分支问题 + +当前分支的实现可以作为 PoC,但需要调整: + +- `AgentRunContext` 仍是 query 视角,字段包括 `query_id`、`session`、`messages`、`user_message`、`use_funcs`、`extra_config`,对非消息事件和复杂任务上下文表达不足。 +- runner 标识使用 `plugin:author/plugin_name/runner_name` 字符串拼接,缺少结构化 ID、版本、能力和权限信息。 +- LangBot 在 `PipelineService.get_pipeline_metadata()` 中直接把插件配置 schema 拼进 AI metadata,缺少缓存、失败隔离和 schema 兼容验证。 +- `ChatMessageHandler` 内部直接解析插件 runner 名称并调用 wrapper,调度逻辑和消息处理逻辑耦合。 +- SDK 的 `AgentRunner.run()` 只接受单一上下文,没有生命周期 hooks、能力声明、配置 schema 分层和运行结果协议版本。 +- 工具调用、知识检索、LLM 调用目前依赖零散 proxy action,缺少 Agent 运行期明确的 capability set。 + +## 4. 总体架构 + +建议引入三层结构: + +```text +Pipeline / future Event Router + | + v +AgentRunnerRegistry + | discovers built-in runners and plugin runners + v +AgentRunOrchestrator + | builds context, validates permissions, invokes runner + v +Built-in RequestRunner adapter / Plugin AgentRunner component + | + v +AgentRunResult stream +``` + +### 4.1 AgentRunnerRegistry + +职责: + +- 从内置 runner 和插件运行时收集 runner manifest。 +- 输出统一的 `AgentRunnerDescriptor`,而不是散落在 UI metadata 中的字符串 option。 +- 对插件 runner manifest 做基础校验:组件类型、配置 schema、权限声明、协议版本。 +- 提供缓存和刷新机制,插件安装、卸载、重启后刷新。 + +建议结构: + +```python +class AgentRunnerDescriptor(BaseModel): + id: str # builtin:local-agent 或 plugin:author/name/runner + source: Literal["builtin", "plugin"] + label: I18nObject + description: I18nObject | None = None + config_schema: list[DynamicFormItemSchema] = [] + capabilities: AgentRunnerCapabilities + plugin: PluginRef | None = None + protocol_version: str = "1" +``` + +### 4.2 AgentRunOrchestrator + +职责: + +- 根据 pipeline 配置选择 runner。 +- 将当前 query 或未来事件输入转换为 `AgentRunRequest`。 +- 注入可用工具、模型、知识库、会话、权限、平台能力摘要。 +- 统一处理超时、异常、流式返回、取消、中断和 telemetry。 +- 将插件返回的 `AgentRunResult` 转换回当前 Pipeline 能消费的 `Message` / `MessageChunk`。 + +LangBot 当前 `ChatMessageHandler` 里的插件 wrapper 应下沉到 orchestrator,避免消息处理器知道插件 runner 的细节。 + +## 5. SDK 设计 + +### 5.1 AgentRunner 组件 + +SDK 保留当前分支新增的组件方向,但需要补齐能力声明: + +```python +class AgentRunner(BaseComponent): + __kind__ = "AgentRunner" + __protocol_version__ = "1" + + @classmethod + def get_capabilities(cls) -> AgentRunnerCapabilities: + return AgentRunnerCapabilities() + + @classmethod + def get_config_schema(cls) -> list[dict]: + return [] + + async def run(self, ctx: AgentRunContext) -> AsyncGenerator[AgentRunResult, None]: + ... +``` + +`get_capabilities()` 用来告诉 LangBot 这个 runner 是否支持: + +- `streaming` +- `tool_calling` +- `knowledge_retrieval` +- `multimodal_input` +- `event_context` +- `platform_api` +- `interrupt` +- `stateful_session` + +本阶段可以先实现 `streaming`、`tool_calling`、`knowledge_retrieval` 三项,其他字段只作为声明和预留。 + +### 5.2 上下文模型 + +当前 `AgentRunContext` 应升级为更通用的运行上下文: + +```python +class AgentRunContext(BaseModel): + run_id: str + trigger: AgentTrigger + conversation: ConversationContext | None = None + event: AgentEventContext | None = None + actor: ActorContext | None = None + subject: SubjectContext | None = None + messages: list[Message] = [] + input: AgentInput + resources: AgentResources + runtime: AgentRuntimeContext + config: dict[str, Any] = {} +``` + +关键点: + +- `trigger` 标明触发来源。当前消息 Pipeline 使用 `message.received`,未来 EBA 可使用 `group.member_joined`、`friend.request_received` 等。 +- `conversation` 承载会话历史、launcher、sender、bot 等聊天语义。 +- `event` 是未来 EBA 的预留封装,本阶段可以由 query 生成一个最小 message event。 +- `actor` 表示触发者,`subject` 表示事件作用对象,例如被邀请用户、被撤回消息、被操作群组。 +- `input` 是 runner 的主输入,不再强制等同于纯文本消息。 +- `resources` 列出 LangBot 已授权给 runner 的工具、知识库、模型、文件等。 +- `runtime` 提供 host 信息、workspace/bot/pipeline 标识、trace id、deadline 等。 +- `config` 是当前 runner 的实例配置,替代当前 `extra_config`。 + +为了兼容现有实现,SDK 可提供: + +```python +ctx.input.to_text() +ctx.conversation.to_legacy_session() +ctx.to_legacy_query_context() +``` + +### 5.3 返回协议 + +当前 `AgentRunReturn.type` 建议规范化为事件流: + +```python +class AgentRunResult(BaseModel): + type: Literal[ + "message.delta", + "message.completed", + "tool.call.started", + "tool.call.completed", + "state.updated", + "action.requested", + "run.completed", + "run.failed", + ] + data: dict[str, Any] = {} +``` + +本阶段 Pipeline 兼容映射: + +- `message.delta` -> `MessageChunk` +- `message.completed` -> `Message` +- `run.completed` 且带 `message` -> `Message` +- `run.failed` -> 记录错误并按当前 runner 错误策略返回 + +未来 EBA 可使用 `action.requested` 表达平台动作,例如编辑消息、通过好友请求、踢人等。本阶段只保留类型,不执行平台动作。 + +### 5.4 LangBotAPIProxy + +Agent Runner 插件需要使用 LangBot 能力,但这些能力必须通过显式授权暴露: + +- 模型:`invoke_llm`、`invoke_llm_stream`、embedding。 +- 工具:`list_tools`、`get_tool_detail`、`call_tool`。 +- 知识:`list_knowledge_bases`、`retrieve_knowledge`。 +- 存储:plugin storage、workspace storage。 +- 文件:配置文件读取、知识文件读取。 + +SDK 应把这些能力按 capability 分组。LangBot 在调用 runner 前根据 runner manifest、pipeline 配置、插件绑定范围生成 `resources`,插件不能绕过资源列表调用未授权对象。 + +## 6. LangBot 设计 + +### 6.1 runner 发现 + +在 LangBot 增加 `AgentRunnerRegistry`: + +- 内置 runner 由 `runner_module.preregistered_runners` 注册为 `builtin:*`。 +- 插件 runner 通过 `PluginRuntimeConnector.list_agent_runners()` 获取。 +- manifest 中必须包含 `metadata`、`spec.config`、`spec.capabilities`。 +- 发现失败只影响对应插件 runner,不影响 Pipeline metadata 返回。 + +当前 `PipelineService.get_pipeline_metadata()` 可以继续作为 UI 入口,但应改为读取 registry,而不是直接拼插件列表。 + +### 6.2 配置模型 + +Pipeline AI 配置建议从: + +```json +{ + "runner": { + "runner": "local-agent" + }, + "local-agent": {} +} +``` + +演进为: + +```json +{ + "runner": { + "id": "plugin:author/name/runner" + }, + "runner_config": { + "plugin:author/name/runner": {} + } +} +``` + +为了兼容现有配置: + +- 读取时同时支持 `runner.runner` 和 `runner.id`。 +- 写入时可以先继续写 `runner.runner`,等前端完成迁移后再切到 `runner.id`。 +- 旧的内置 runner config key 保持可用。 + +### 6.3 运行调度 + +`ChatMessageHandler` 不应直接构造 `PluginAgentRunnerWrapper`。建议路径: + +```text +ChatMessageHandler + -> AgentRunOrchestrator.run_from_query(query) + -> resolve runner descriptor + -> build AgentRunContext + -> invoke built-in adapter or plugin connector + -> normalize AgentRunResult stream +``` + +内置 `RequestRunner` 可以由 adapter 包一层,统一成 `AgentRunnerDescriptor`,但不要求现在改写内置 runner。 + +### 6.4 插件调用协议 + +LangBot 到 SDK runtime 需要以下 action: + +- `LIST_AGENT_RUNNERS` +- `RUN_AGENT` + +`RUN_AGENT` 输入: + +```json +{ + "plugin_author": "...", + "plugin_name": "...", + "runner_name": "...", + "context": {} +} +``` + +`RUN_AGENT` 输出为流式 `AgentRunResult`。LangBot 必须校验每个结果: + +- 未知 `type` 记录 warning 后忽略。 +- 单次 result 大小限制,避免插件输出过大。 +- `message.delta` 和 `message.completed` 做 provider message schema 校验。 +- `run.failed` 进入统一错误处理。 + +### 6.5 权限与隔离 + +插件 runner 的权限不能只靠插件安装即全量开放。建议 manifest 增加: + +```yaml +spec: + capabilities: + streaming: true + tool_calling: true + knowledge_retrieval: true + permissions: + models: ["invoke"] + tools: ["call"] + knowledge_bases: ["retrieve"] + platform_api: [] +``` + +LangBot 执行前做三层裁剪: + +- 插件 manifest 声明的权限。 +- Pipeline 或 Bot 绑定的扩展范围。 +- 用户在 runner 配置中选择的资源范围。 + +最终写入 `ctx.resources`,并在 proxy action 里再次校验。 + +## 7. 与 EBA 的边界 + +本阶段只使用 EBA 文档中的以下思想: + +- 统一事件命名,例如当前消息 query 可映射为 `message.received`。 +- Agent 不应假设输入一定是用户文本消息。 +- Agent 返回不应只限于文本回复,未来可表达动作请求。 +- 插件 SDK 的事件和 API 应向后兼容。 + +本阶段不实现: + +- EventBus。 +- EventRouter。 +- 新平台适配器目录结构。 +- 群组、好友、Bot 状态等非消息事件监听。 +- 统一平台 API 的实际执行。 + +因此文档和代码命名应避免把当前任务称为 EBA 实现。推荐使用 `agent-runner-pluginization`、`AgentRunContext`、`AgentRunResult` 等命名。 + +## 8. 分阶段落地 + +### Phase 1:整理当前分支 + +- 保留 SDK `AgentRunner` 组件。 +- 调整 `AgentRunContext` / `AgentRunReturn` 为协议 v1 的命名和字段。 +- LangBot 增加 `AgentRunnerRegistry` 和 `AgentRunOrchestrator`。 +- `ChatMessageHandler` 改为调用 orchestrator。 +- Pipeline metadata 从 registry 读取 runner 列表。 + +### Phase 2:能力和权限 + +- runner manifest 增加 `capabilities`、`permissions`、`config_schema`。 +- LangBot 对工具、知识库、模型资源做注入和裁剪。 +- proxy action 做二次校验。 +- 增加超时、取消、错误隔离和 telemetry。 + +### Phase 3:兼容和迁移 + +- 兼容当前 `plugin:author/name/runner` 字符串 ID。 +- 兼容 `runner.runner` 配置键。 +- 提供从旧 runner 配置到 `runner.id` / `runner_config` 的迁移。 +- 将内置 `RequestRunner` 包装成 registry descriptor。 + +### Phase 4:为 EBA 接入做预留 + +- `AgentRunContext.event` 支持 EBA 文档定义的事件 envelope 子集。 +- `AgentRunResult.action.requested` 只记录,不执行。 +- 等 EBA EventRouter 落地后,由 EventRouter 直接调用 orchestrator。 + +## 9. 需要修改的代码范围 + +LangBot: + +- `src/langbot/pkg/pipeline/process/handlers/chat.py`:移除插件 runner 解析细节,改为 orchestrator。 +- `src/langbot/pkg/api/http/service/pipeline.py`:从 registry 获取 runner metadata。 +- `src/langbot/pkg/plugin/connector.py`:保留 `list_agent_runners()` / `run_agent()`,增加协议校验。 +- `src/langbot/pkg/plugin/handler.py`:整理 Agent 运行期可调用的 proxy action。 +- 新增 `src/langbot/pkg/provider/agent_runner/` 或 `src/langbot/pkg/agent/runner/`:registry、orchestrator、context builder、result normalizer。 + +SDK: + +- `src/langbot_plugin/api/definition/components/agent_runner/runner.py`:补 capabilities、config schema、协议版本。 +- `src/langbot_plugin/api/entities/builtin/agent_runner/context.py`:升级上下文和返回协议。 +- `src/langbot_plugin/runtime/io/handlers/control.py`:保留 `LIST_AGENT_RUNNERS` / `RUN_AGENT`。 +- `src/langbot_plugin/runtime/plugin/mgr.py`:runner 发现、调用、异常隔离。 +- `src/langbot_plugin/api/proxies/langbot_api.py`:补齐 Agent 运行期需要的 host capability proxy。 + +## 10. 验收标准 + +- 在不修改现有内置 runner 的前提下,Pipeline 可以选择一个插件提供的 Agent Runner。 +- 插件 runner 能收到结构化上下文,并能流式返回消息。 +- 插件 runner 只能看到 LangBot 注入的工具、知识库、模型资源。 +- 插件 runner 异常不会中断插件 runtime 或 Pipeline 主流程。 +- 旧 Pipeline 配置和旧内置 runner 正常工作。 +- 文档明确区分“Agent Runner 插件化”和“未来 EBA 架构”。 + +## 11. 待确认问题 + +- 插件 Agent Runner 是否允许一个插件声明多个 runner。当前分支注释写“一个插件只能提供一个”,但 manifest 和 UI 其实可以支持多个;建议允许多个。 +- `AgentRunResult.action.requested` 在 EBA 前是否只做 telemetry,还是允许有限的 `reply_message` 动作。 +- runner 配置是否跟 Pipeline 绑定,还是未来跟 Bot/Event handler 绑定。当前阶段建议仍跟 Pipeline 绑定。 +- 内置 runner 是否最终迁移到 SDK 插件形式。建议短期只包装,长期再拆。