# LangBot Host 与 SDK 基础设施设计 本文档描述 LangBot 作为 agent host 的内部能力与分层架构,以及 Host 内部模型。 - SDK ↔ Host 的协议数据结构(`AgentRunContext`、`AgentRunnerManifest`、`AgentRunResult`、`AgentRunAPIProxy` 等)的**唯一定义在** [PROTOCOL_V1.md](./PROTOCOL_V1.md);本文只引用,不重抄。 - 实现进度见 [PROGRESS.md](./PROGRESS.md)。 - 本文定义的 Host 内部模型(`AgentEventEnvelope`、`AgentBinding`、`AgentRunnerDescriptor`)不属于 SDK 协议字段。 ## 1. 目标 LangBot 要转为 agent host,而不是内置 runner 容器: - 接收 IM、WebUI、API 和未来 EventRouter 产生的事件。 - 根据事件、bot、workspace、scope 解析应该调用的 Agent / agent binding。 - 发现、校验和调用插件提供的 AgentRunner。 - 为每次 run 提供受限资源、状态、存储、上下文引用和生命周期控制。 - 接收 AgentRunner 返回的事件流,投递到 IM、WebUI 或其他 output surface。 ## 2. 非目标 - 不把 Pipeline 当作长期架构中心。 - 不要求所有 AgentRunner 依赖 LangBot 的上下文管理。 - 不要求官方 local-agent 的旧行为反向塑造 host 协议。 - 不在 host 中实现通用 agentic prompt assembler。 - 不强制 runner 使用 LangBot state / storage;只提供可选、受控的寄宿能力。 - 不实现 EventGateway:它是 future integration point,由外部 event branch 提供。本分支只定义 host-side envelope/binding models 和 `run(event, binding)` 入口。 ## 3. 分层架构 ```text IM / WebUI / API / EventRouter (future) | v Event Gateway (future - external event branch) | v AgentBindingResolver | v AgentRunOrchestrator |-- AgentRunnerRegistry |-- AgentResourceBuilder |-- AgentContextBuilder |-- AgentRunSessionRegistry |-- PersistentStateStore / EventLogStore / TranscriptStore / ArtifactStore v Plugin Runtime / AgentRunner | v AgentRunResult stream | v Delivery / Renderer / Platform API ``` 目标产品模型、单绑定调度、Agent 复用、插件实例无状态和 fan-out 边界以 [PROTOCOL_V1.md](./PROTOCOL_V1.md) §13 为准。本文只说明 Host 如何把当前入口投影为内部模型。当前 Pipeline 只应接入在 Query entry adapter 位置:它可以继续产生 `message.received` 并投影出临时 `AgentConfig` / `AgentBinding`,但不应再拥有 runner 选择、上下文裁剪和业务 agent 执行的核心语义。EventGateway 由外部 event branch 实现。 ## 4. LangBot 侧能力 ### 4.1 Event Gateway(Future Integration Point) > EventGateway 由外部 event branch 实现,不在本分支范围。本分支只预留 event-first 入口和 envelope/binding models。 Event Gateway 将把入口统一成 host event(IM 平台消息、WebUI debug chat、API 触发、后续非消息事件),输出稳定的 `AgentEventEnvelope`(Host 内部模型): ```python class AgentEventEnvelope(BaseModel): event_id: str event_type: str event_time: int | None source: str bot_id: str | None workspace_id: str | None conversation_id: str | None thread_id: str | None actor: ActorRef | None subject: SubjectRef | None input: AgentInput # 见 PROTOCOL_V1 §5.6 delivery: DeliveryContext # 见 PROTOCOL_V1 §5.7 raw_ref: RawEventRef | None metadata: dict[str, Any] = {} ``` `AgentEventEnvelope` 是 Host 内部入口模型;投影给 runner 的是 `ctx.event`(PROTOCOL_V1 §5.4)。原始平台 payload 存为 raw event 或 artifact ref,不扩散到 runner 协议顶层。 **当前 adapter source**:`QueryEntryAdapter.query_to_event(query)` 从 Query 生成 `AgentEventEnvelope`。 ### 4.2 AgentConfig 与 AgentBinding `AgentConfig` 是迁移期的 Host 内部 Agent 配置投影(不暴露给 SDK)。当前 Query entry adapter 从 Pipeline config 投影出它;未来持久 Agent 也应先投影成这个运行期配置,再由 BindingResolver 结合事件和 scope 解析为 `AgentBinding`。 ```python class AgentConfig(BaseModel): agent_id: str | None = None runner_id: str runner_config: dict[str, Any] = {} resource_policy: ResourcePolicy = ResourcePolicy() state_policy: StatePolicy = StatePolicy() delivery_policy: DeliveryPolicy = DeliveryPolicy() event_types: list[str] = ["message.received"] enabled: bool = True metadata: dict[str, Any] = {} ``` `AgentBinding` 是"什么事件调用哪个 AgentRunner、带什么 Agent 配置"的 Host 内部运行投影(不暴露给 SDK)。它是 EventRouter / 当前 QueryEntryAdapter 在一次运行前解析出的有效绑定。 ```python class AgentBinding(BaseModel): binding_id: str enabled: bool scope: BindingScope event_types: list[str] filters: list[EventFilter] = [] # EBA 阶段使用,见 EVENT_BASED_AGENT runner_id: str runner_config: dict[str, Any] resource_policy: ResourcePolicy state_policy: StatePolicy delivery_policy: DeliveryPolicy ``` BindingResolver 的基数、fan-out 和冲突处理约束见 PROTOCOL_V1 §13;本节只定义 Host 内部投影形态。 **当前 adapter source**:`QueryEntryAdapter.config_to_agent_config(query, runner_id)` 先把 current config 投影为迁移期 `AgentConfig`,再由 `AgentBindingResolver.resolve_one(event, [agent_config])` 解析出唯一 `AgentBinding`。Pipeline 当前只是迁移期 Agent config source(AI runner config → runner_config、extension preference → resource_policy、output settings → delivery_policy),但新设计不再把这些字段命名为 Pipeline 专属概念。 ### 4.3 AgentRunnerRegistry Registry 收集 runner descriptor(来自插件 runtime、开发期本地插件): ```python class AgentRunnerDescriptor(BaseModel): id: str source: Literal["plugin"] label: I18nObject description: I18nObject | None = None protocol_version: str = "1" capabilities: AgentRunnerCapabilities # 见 PROTOCOL_V1 §4.3 permissions: AgentRunnerPermissions # 见 PROTOCOL_V1 §4.4 config_schema: list[DynamicFormItemSchema] plugin: PluginRef | None = None ``` 职责:调用 `plugin_connector.list_agent_runners()` 拉取 runner、校验 manifest(`kind == AgentRunner`、`metadata.name/label` 存在、`protocol_version` 兼容、`spec.*` 类型正确)、输出 descriptor、缓存 discovery 结果并提供 `refresh()`。单个插件 manifest 失败只记 warning,不影响其它 runner。`plugin:author/name/runner` 是稳定 id 格式;插件实例边界见 PROTOCOL_V1 §13。 Host 内置 runner / adapter 不能作为 `AgentRunnerDescriptor.source` 绕过插件 runtime、`run_id`、`ctx.resources` 和 `AgentRunAPIProxy` 权限链。若需要 开发期调试 adapter,应放在 Host 内部测试入口,不进入可选 runner 列表。 刷新触发点:插件安装/卸载/升级/重启后;Pipeline metadata 请求时发现缓存为空;可选 TTL(优先保证正确性)。 ### 4.4 AgentRunOrchestrator Orchestrator 是唯一运行入口: ```text run(event, binding) -> resolve runner descriptor -> build resources -> build context -> register run session -> call plugin runtime -> normalize result stream -> update state -> unregister run session ``` 它负责:`run_id` 生成和生命周期、timeout/deadline/cancellation、插件异常隔离、result schema 校验和大小限制、`state.updated` 处理、delivery backpressure 和 telemetry。 典型 run 时序: ```text QueryEntryAdapter / EventRouter -> AgentRunOrchestrator.run(event, binding) -> AgentRunnerRegistry.resolve(runner_id) -> AgentResourceBuilder.freeze_snapshot(binding, event) -> AgentRunSessionRegistry.register(run_id, runner_id, snapshot) -> AgentContextBuilder.build(event, binding, snapshot) -> PluginRuntimeConnector.run_agent(ctx) -> AgentRunAPIProxy action -> validate active run session + caller identity + snapshot -> Host API / Store <- AgentRunResult stream -> apply state.updated to PersistentStateStore -> write message.completed / artifact.created to Transcript / ArtifactStore -> render delivery or raise RunnerExecutionError -> AgentRunSessionRegistry.unregister(run_id) ``` `run_from_query()` 保留为 Query entry adapter 入口,但内部转换成 event + binding 后走统一 `run()`。约束:`ChatMessageHandler` 不解析 `plugin:*`、不实例化 wrapper、不知道 runner 组件细节;`PipelineService` 从 registry 读取 metadata,不直接访问插件 runtime;跨请求持久化状态必须走授权 storage / 外部服务。 ### 4.5 Resource Authorization(三层裁剪) LangBot 在每次 run 前生成 `ctx.resources`(PROTOCOL_V1 §6),来自三层约束: 1. runner manifest 声明的 `permissions`(最大能力)。 2. binding / resource policy 允许的资源范围。 3. 当前 event / actor / bot / workspace 的实际权限。 这次裁剪结果必须冻结为 run-scoped authorization snapshot,并由 `AgentRunSessionRegistry` 按 `run_id` 保存。`ctx.resources` 是投影给 runner 看的同一份授权结果;运行期每个 proxy action 只依据该 snapshot 校验 active run session、caller plugin identity、resource id、scope、payload size、rate limit 和 deadline。Handler 不应重新执行三层裁剪,否则 build-time 与 runtime 授权逻辑会漂移。 SDK 侧本地校验只用于开发体验,host 侧 run authorization snapshot 才是安全边界。 资源裁剪应通用,不写死 local-agent。selector 与资源的映射示例:`model-fallback-selector` → primary/fallback LLM、`llm-model-selector` → LLM、`rerank-model-selector` → rerank 模型、`knowledge-base-multi-selector` → 知识库;新增 selector 时在 resource builder 中统一扩展。 执行/文件/skill/MCP 等能力的接入方向:先由 Host 封装成普通 tool,再通过 `ctx.resources.tools` 进入 runner;runner 不应识别或硬编码执行环境 provider。 ### 4.6 State / Storage LangBot 可提供 host-owned state 让 runner 寄宿状态(conversation / actor / subject / runner / binding / workspace state),但**不是强制**。Host 只需提供:授权开关、scope key、get/set/list/delete API(见 PROTOCOL_V1 §8)、持久化 backend、审计和清理策略。外部 agent runtime 可维护自己的 session 和 memory。进程内 state store 只能作为过渡实现,不能作为正式生产语义。 ### 4.7 EventLog / Transcript / Artifact(事实源) - `EventLog`: durable append-only,保存原始事件、系统事件、工具调用、投递结果、错误。 - `Transcript`: 从 EventLog 投影出的对话视图,用于 UI、审计和按需历史读取。 - `ArtifactStore`: 保存大文件、多模态输入、工具大结果、平台附件。 三类数据与 working context 的边界、读取约束见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md)。AgentRunner 可读取这些能力,但不被迫使用 LangBot 作为唯一记忆系统。 ### 4.8 Prompt / Instruction Package(占位) 当前 Query 入口不把 preprocessing 后的有效 prompt 放进 adapter metadata。目标形态是 Host 保存或生成一个 run-scoped instruction package,runner 通过 Host API 拉取: - Host 记录静态绑定 prompt、host hook / user plugin 产生的 instruction fragment、来源和审计信息。 - `ctx.context.available_apis` 增加 `prompt_get` 能力位表示拉取是否可用。 - Runner 拉取后仍由自己决定如何与 history、RAG、tool 结果、memory 和当前输入组装最终 prompt。 - Host 不实现通用 agentic prompt assembler,也不把 Query entry adapter prompt 作为长期业务输入契约。 ### 4.9 External harness resource projection Claude Code、Codex、Kimi Code 等外部 harness runner 可能不直接调用 LangBot 的 model/tool loop,而是把 LangBot 事件和授权资源投影到自己的 harness 执行。Host 侧仍保持统一边界:Host 负责构造 event-first context、资源授权、state/storage、EventLog/Transcript/ArtifactStore 和审计;Host 或 binding policy 决定哪些 MCP server、skill、artifact、history/state 句柄可投影给 runner;runner plugin 把 scoped projection 转成目标 harness 可消费形式;外部 harness 负责自己的 native session、tool loop、压缩、权限模式和 resume。 投影的具体形态(context 文件、skill 目录、MCP config、state pointers)见 AGENT_CONTEXT_PROTOCOL §4.5;Claude Code / Codex 当前实现见 OFFICIAL_RUNNER_PLUGINS §7。发布级隔离要求见 SECURITY_HARDENING。 ## 5. SDK 侧协议 SDK 组件入口如下;所有数据结构定义见 PROTOCOL_V1。 ```python class AgentRunner(BaseComponent): __kind__ = "AgentRunner" @classmethod def get_capabilities(cls) -> AgentRunnerCapabilities: ... # PROTOCOL_V1 §4.3 @classmethod def get_config_schema(cls) -> list[dict]: ... async def run(self, ctx: AgentRunContext) -> AsyncGenerator[AgentRunResult, None]: ... # ctx: PROTOCOL_V1 §5.2 ; AgentRunResult: PROTOCOL_V1 §7 ``` - Manifest / capabilities / permissions / context policy:PROTOCOL_V1 §4。 - `AgentRunContext`:PROTOCOL_V1 §5.2。`messages` / `bootstrap` 不是协议字段。 - `AgentRunResult`:PROTOCOL_V1 §7。 - `AgentRunAPIProxy`:PROTOCOL_V1 §8,是 runner 访问 host 能力的唯一入口,所有请求带 `run_id`。