13 KiB
LangBot Host 与 SDK 基础设施设计
本文档描述 LangBot 作为 agent host 的内部能力与分层架构,以及 Host 内部模型。
- SDK ↔ Host 的协议数据结构(
AgentRunContext、AgentRunnerManifest、AgentRunResult、AgentRunAPIProxy等)的唯一定义在 PROTOCOL_V1.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. 分层架构
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 §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 内部模型):
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。
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 在一次运行前解析出的有效绑定。
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、开发期本地插件):
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 是唯一运行入口:
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 时序:
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),来自三层约束:
- runner manifest 声明的
permissions(最大能力)。 - binding / resource policy 允许的资源范围。
- 当前 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。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。
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。