Files
LangBot/docs/agent-runner-pluginization/README.md
2026-05-17 11:05:27 +08:00

407 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 等上下文字段。
- 现有内置 `RequestRunner` 最终强制迁移为插件形态,由 LangBot 通过同一套插件化 runner 协议调用。
非目标:
- 不在本阶段实现 EBA EventBus、EventRouter、平台多事件监听或统一平台 API。
- 不改变现有 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]:
...
```
一个插件可以声明多个 `AgentRunner` 组件。每个 runner 使用独立的 component manifest、配置 schema、能力声明和权限声明LangBot 侧以 `plugin:author/name/runner` 作为稳定 ID 区分。插件包可以因此同时提供多个执行策略,例如通用聊天 runner、客服 runner、工单 runner而不需要拆成多个插件。
`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",
"run.completed",
"run.failed",
]
data: dict[str, Any] = {}
```
本阶段 Pipeline 兼容映射:
- `message.delta` -> `MessageChunk`
- `message.completed` -> `Message`
- `run.completed` 且带 `message` -> `Message`
- `run.failed` -> 记录错误并按当前 runner 错误策略返回
`action.requested` 不进入本阶段的必选协议。它表示“Agent 希望 LangBot 执行一个非文本平台动作”,例如未来 EBA 里编辑消息、通过好友请求、踢人等。当前 Agent Runner 仍作为 Pipeline 的一个 stage 执行,输出只需要覆盖消息流、工具调用状态和运行完成/失败;如果实验性 runner 返回 `action.requested`LangBot 只记录 telemetry 并忽略执行。
### 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 配置模型与绑定位置
当前阶段 runner 配置仍跟 Pipeline 绑定,并且仍然作为 Pipeline 的一个 stage 执行。也就是说Bot 收到私聊/群聊消息后仍按现有 Pipeline 流转,只是在 AI runner stage 中选择插件化 Agent Runner。
后续 EBA EventRouter 落地后,同一套 `AgentRunnerDescriptor``AgentRunOrchestrator` 需要支持直接与 Bot 的事件触发器绑定。届时 Bot event handler 可以绕过完整 Pipeline直接选择某个 Agent Runner 处理 `message.received``group.member_joined``friend.request_received` 等事件。
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内置 runner 插件化迁移
- 兼容当前 `plugin:author/name/runner` 字符串 ID。
- 兼容 `runner.runner` 配置键。
- 提供从旧 runner 配置到 `runner.id` / `runner_config` 的迁移。
- 将所有内置 `RequestRunner` 强制迁移为内置插件或官方插件包。
- LangBot 只保留插件 runtime、registry、orchestrator 和兼容迁移逻辑,不再维护独立的内置 runner 执行分支。
### Phase 4为 EBA 接入做预留
- `AgentRunContext.event` 支持 EBA 文档定义的事件 envelope 子集。
- `AgentRunResult.action.requested` 仍只记录,不执行;真正执行平台动作需要等统一平台 API 和事件触发器权限模型完成。
- 等 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. 验收标准
- Pipeline 可以选择一个插件提供的 Agent Runner。
- 插件 runner 能收到结构化上下文,并能流式返回消息。
- 插件 runner 只能看到 LangBot 注入的工具、知识库、模型资源。
- 插件 runner 异常不会中断插件 runtime 或 Pipeline 主流程。
- 旧 Pipeline 配置和旧内置 runner 正常工作。
- 文档明确区分“Agent Runner 插件化”和“未来 EBA 架构”。
## 11. 已确认决策
- 插件可以声明多个 `AgentRunner` 组件,每个组件独立暴露 manifest、配置 schema、能力和权限。
- 本阶段不把 `action.requested` 作为必须实现的运行结果。它只是为未来 EBA 平台动作预留的返回类型;当前 Pipeline stage 中如收到该类型,只记录 telemetry不执行动作。
- 当前 runner 配置先跟 Pipeline 绑定,仍然在 Pipeline 的 AI runner stage 中执行;后续需要支持直接与 Bot 的事件触发器绑定。
- 内置 `RequestRunner` 最终强制迁移为插件形态,避免长期保留“内置 runner 分支”和“插件 runner 分支”两套执行体系。