mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-08 06:46:02 +00:00
refactor(agent-runner): simplify event-first entry path
This commit is contained in:
@@ -1,38 +1,27 @@
|
||||
# LangBot AgentRunner Protocol v1
|
||||
|
||||
本文档定义 LangBot Host 与插件 SDK / Runtime / AgentRunner 之间的协议合同。它优先描述”稳定接口应是什么”,不描述具体落地任务。
|
||||
本文档是 LangBot Host 与插件 SDK / Runtime / AgentRunner 之间协议合同的**唯一规范来源(single source of truth)**。
|
||||
|
||||
## 当前状态
|
||||
|
||||
**Protocol v1 已在当前分支落地**:
|
||||
|
||||
- ✅ SDK 定义 `AgentRunnerManifest`、`AgentRunContext`、`AgentRunResult`、`AgentRunAPIProxy`
|
||||
- ✅ Runtime 支持 `LIST_AGENT_RUNNERS` 和 `RUN_AGENT`
|
||||
- ✅ Host 支持 `run_id` session authorization
|
||||
- ✅ Host 能从当前 Pipeline 入口生成 event-first context
|
||||
- ✅ `messages` 降级为 optional bootstrap
|
||||
- ✅ Host 不定义通用历史窗口字段或策略;runner 自己管理 working context
|
||||
- ✅ Proxy 覆盖 model、tool、knowledge、state/storage
|
||||
- ✅ History / Event / Artifact / State API 已落地
|
||||
- ✅ EventLog / Transcript / ArtifactStore / PersistentStateStore 已落地
|
||||
- ✅ `local-agent` 与 Claude Code runner 已通过本地 WebUI smoke,验证 host-infra runner 与外部 harness runner 共享同一协议路径
|
||||
- 本文件描述"稳定接口应是什么",是 normative spec,不混入实现进度。实现状态见 [PROGRESS.md](./PROGRESS.md)。
|
||||
- 本文件之外的任何文档**不得重新定义这里的数据结构**,只能引用,例如"见 PROTOCOL_V1 §4.2"。
|
||||
- Host 内部模型(`AgentEventEnvelope`、`AgentBinding`、Descriptor、各 Store)不属于 SDK 协议,定义在 [HOST_SDK_INFRASTRUCTURE.md](./HOST_SDK_INFRASTRUCTURE.md)。
|
||||
|
||||
## 1. 协议目标
|
||||
|
||||
Protocol v1 要解决四件事:
|
||||
Protocol v1 只解决四件事:
|
||||
|
||||
- LangBot 如何发现插件提供的 AgentRunner。
|
||||
- LangBot 如何把一次事件调用封装成 `AgentRunContext`。
|
||||
- AgentRunner 如何以事件流形式返回运行结果。
|
||||
- AgentRunner 如何通过受限 API 访问 LangBot host 能力。
|
||||
|
||||
Protocol v1 不定义:
|
||||
Protocol v1 **不定义**:
|
||||
|
||||
- LangBot 内部如何持久化 AgentBinding。
|
||||
- AgentRunner 内部如何组装 prompt、压缩历史、管理 memory。
|
||||
- 官方 local-agent 的具体实现。
|
||||
- LangBot 内部如何持久化 `AgentBinding`(见 HOST_SDK)。
|
||||
- AgentRunner 内部如何组装 prompt、压缩历史、管理 memory(见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md))。
|
||||
- 官方 runner 的具体实现(见 [OFFICIAL_RUNNER_PLUGINS.md](./OFFICIAL_RUNNER_PLUGINS.md))。
|
||||
- Pipeline 的长期配置模型。
|
||||
- 发布级安全 hardening 的完整实现;当前只定义 Host 侧资源、权限、状态和审计边界,release gate 见 [SECURITY_HARDENING.md](./SECURITY_HARDENING.md)。
|
||||
- 发布级安全 hardening 的完整实现(见 [SECURITY_HARDENING.md](./SECURITY_HARDENING.md))。
|
||||
|
||||
## 2. 参与方
|
||||
|
||||
@@ -42,26 +31,32 @@ Protocol v1 不定义:
|
||||
| Plugin Runtime | 加载插件,响应 Host 的 runner discovery 和 run 调用。 |
|
||||
| AgentRunner | 插件提供的 agent 执行组件。 |
|
||||
| AgentRunAPIProxy | AgentRunner 访问 Host 能力的受限 API。 |
|
||||
| AgentBinding | Host 内部的事件到 runner 绑定配置,不直接暴露给 SDK。 |
|
||||
| AgentBinding | Host 内部的事件到 runner 绑定配置,不直接暴露给 SDK(见 HOST_SDK §4.2)。 |
|
||||
|
||||
`AgentBinding` 只影响 Host 构造出的 `ctx.config`、`ctx.resources`、`ctx.context` 和 `ctx.delivery`。SDK 不需要知道 binding 的持久化形态。
|
||||
|
||||
外部 harness runner(Claude Code、Codex、Kimi Code 等)仍然是 `AgentRunner`。Protocol v1 只要求它们消费 event-first `AgentRunContext`、返回 `AgentRunResult`,并通过 Host 授权的 state/storage/artifact APIs 保存跨轮次指针。它们内部可以继续使用自己的 session、tool loop、MCP、上下文压缩和权限模型。
|
||||
外部 harness runner(Claude Code、Codex、Kimi Code 等)也是 `AgentRunner`:它们消费 event-first `AgentRunContext`、返回 `AgentRunResult`,并通过 Host 授权的 state/storage/artifact API 保存跨轮次指针。它们内部可以继续使用自己的 session、tool loop、MCP、上下文压缩和权限模型。
|
||||
|
||||
## 3. Discovery 协议
|
||||
## 3. 版本协商
|
||||
|
||||
### 3.1 LIST_AGENT_RUNNERS
|
||||
- `AgentRunnerManifest.protocol_version` 声明 runner 实现的协议大版本,当前为 `"1"`。
|
||||
- `AgentRuntimeContext.protocol_version`(`ctx.runtime.protocol_version`)声明 Host 下发的协议大版本。
|
||||
- Host 发现 runner 时校验 `protocol_version` 兼容性;不兼容的 runner 不进入可用列表,只记 warning。
|
||||
- 字段级演进规则:新增可选字段不提升大版本;删除或改语义需要提升大版本。
|
||||
- 结果流演进:Host **必须忽略未知 result type 并记录 warning**(除非该 type 明确要求强校验)。新增 result type 不提升大版本。
|
||||
|
||||
Host 调用 Plugin Runtime 获取当前插件暴露的 runner 列表。该请求不需要额外 payload。
|
||||
## 4. Discovery 协议
|
||||
|
||||
Runtime 返回:
|
||||
### 4.1 LIST_AGENT_RUNNERS
|
||||
|
||||
Host 调用 Plugin Runtime 获取当前插件暴露的 runner 列表,请求无额外 payload。返回:
|
||||
|
||||
```python
|
||||
class ListAgentRunnersResponse(BaseModel):
|
||||
runners: list[AgentRunnerManifest]
|
||||
```
|
||||
|
||||
### 3.2 AgentRunnerManifest
|
||||
### 4.2 AgentRunnerManifest
|
||||
|
||||
```python
|
||||
class AgentRunnerManifest(BaseModel):
|
||||
@@ -69,6 +64,7 @@ class AgentRunnerManifest(BaseModel):
|
||||
name: str
|
||||
label: I18nObject
|
||||
description: I18nObject | None = None
|
||||
protocol_version: str = "1"
|
||||
capabilities: AgentRunnerCapabilities
|
||||
permissions: AgentRunnerPermissions
|
||||
context: AgentRunnerContextPolicy
|
||||
@@ -76,14 +72,12 @@ class AgentRunnerManifest(BaseModel):
|
||||
metadata: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
字段要求:
|
||||
|
||||
- `id` 必须稳定,推荐 `plugin:author/name/runner`。
|
||||
- `id` 必须稳定,格式 `plugin:author/name/runner`。
|
||||
- `name` 是插件内 runner 名称,例如 `default`。
|
||||
- `config_schema` 只描述绑定配置表单,不代表插件实例状态。
|
||||
- `metadata` 只能放展示、诊断、非稳定扩展信息。
|
||||
- `metadata` 只放展示、诊断、非稳定扩展信息。
|
||||
|
||||
### 3.3 Capabilities
|
||||
### 4.3 Capabilities
|
||||
|
||||
```python
|
||||
class AgentRunnerCapabilities(BaseModel):
|
||||
@@ -101,8 +95,8 @@ class AgentRunnerCapabilities(BaseModel):
|
||||
语义:
|
||||
|
||||
- `streaming`: runner 可以返回 `message.delta`。
|
||||
- `tool_calling`: runner 可能调用 Host tool APIs。
|
||||
- `knowledge_retrieval`: runner 可能调用 Host knowledge APIs。
|
||||
- `tool_calling`: runner 可能调用 Host tool API。
|
||||
- `knowledge_retrieval`: runner 可能调用 Host knowledge API。
|
||||
- `multimodal_input`: runner 可以处理非纯文本 input / artifact。
|
||||
- `event_context`: runner 理解 event-first 输入。
|
||||
- `platform_api`: runner 可能请求平台动作。
|
||||
@@ -110,7 +104,9 @@ class AgentRunnerCapabilities(BaseModel):
|
||||
- `stateful_session`: runner 可能维护跨 run 会话状态。
|
||||
- `self_managed_context`: runner 自己管理 working context,Host 不应默认 inline 历史。
|
||||
|
||||
### 3.4 Permissions
|
||||
> Capabilities 字段全部是 `bool`。runner 是否寄宿 host-owned state **不在 capabilities 表达**,而通过 `permissions.storage` 声明(见 §4.4),避免出现非 bool 取值。
|
||||
|
||||
### 4.4 Permissions
|
||||
|
||||
```python
|
||||
class AgentRunnerPermissions(BaseModel):
|
||||
@@ -125,16 +121,12 @@ class AgentRunnerPermissions(BaseModel):
|
||||
platform_api: list[str] = []
|
||||
```
|
||||
|
||||
Manifest permissions 是 runner 需要的最大能力。实际可用资源还要经过 Host binding policy 和当前 run scope 裁剪。
|
||||
Manifest permissions 是 runner 需要的**最大能力**。实际可用资源还要经过 Host binding policy 和当前 run scope 裁剪(三层裁剪见 HOST_SDK §4.5)。
|
||||
|
||||
### 3.5 Context Policy
|
||||
### 4.5 Context Policy
|
||||
|
||||
```python
|
||||
class AgentRunnerContextPolicy(BaseModel):
|
||||
ownership: Literal["self_managed", "host_bootstrap", "hybrid"] = "self_managed"
|
||||
bootstrap: Literal["none", "current_event", "recent_tail", "summary_tail"] = "current_event"
|
||||
max_inline_events: int = 0
|
||||
max_inline_bytes: int = 0
|
||||
supports_history_pull: bool = True
|
||||
supports_history_search: bool = False
|
||||
supports_artifact_pull: bool = True
|
||||
@@ -147,12 +139,14 @@ Host 不使用该声明给 runner inline 历史窗口。默认原则:
|
||||
- Host 不得默认 inline 全量历史。
|
||||
- Host 只 inline 当前 event / input 和 context handles。
|
||||
- Runner 拥有 working context assembly。
|
||||
- Runner 可在授权后通过 Host history / event / artifact / state APIs 拉取更多上下文。
|
||||
- Runner 可在授权后通过 Host history / event / artifact / state API 拉取更多上下文。
|
||||
- 历史窗口策略不属于 Protocol v1 字段,也不属于 Host 通用语义。
|
||||
|
||||
## 4. Run 协议
|
||||
context 边界的设计理由见 [AGENT_CONTEXT_PROTOCOL.md](./AGENT_CONTEXT_PROTOCOL.md)。
|
||||
|
||||
### 4.1 RUN_AGENT
|
||||
## 5. Run 协议
|
||||
|
||||
### 5.1 RUN_AGENT
|
||||
|
||||
Host 调用 Runtime:
|
||||
|
||||
@@ -163,11 +157,11 @@ class AgentRunRequest(BaseModel):
|
||||
context: AgentRunContext
|
||||
```
|
||||
|
||||
Runtime 返回 `AgentRunResult` 异步流。
|
||||
Runtime 返回 `AgentRunResult` 异步流。底层 transport 可继续用 `plugin_author` / `plugin_name` / `runner_name` 定位组件,但协议语义以 `runner_id` 和 `context` 为准。
|
||||
|
||||
插件运行时可以继续在底层 transport 中使用 `plugin_author`、`plugin_name`、`runner_name` 定位组件,但协议语义以 `runner_id` 和 `context` 为准。
|
||||
### 5.2 AgentRunContext
|
||||
|
||||
### 4.2 AgentRunContext
|
||||
这是 SDK 看到的**唯一权威 context 定义**。
|
||||
|
||||
```python
|
||||
class AgentRunContext(BaseModel):
|
||||
@@ -184,7 +178,6 @@ class AgentRunContext(BaseModel):
|
||||
state: AgentRunState
|
||||
runtime: AgentRuntimeContext
|
||||
config: dict[str, Any] = {}
|
||||
bootstrap: BootstrapContext | None = None
|
||||
adapter: AdapterContext | None = None
|
||||
metadata: dict[str, Any] = {}
|
||||
```
|
||||
@@ -193,29 +186,26 @@ class AgentRunContext(BaseModel):
|
||||
|
||||
- `event` 是必选字段,Protocol v1 是 event-first。
|
||||
- `input` 表示当前事件的主输入,不等于历史消息。
|
||||
- `bootstrap` 是可选字段;LangBot Host 默认不填历史窗口。
|
||||
- `bootstrap` / `messages` **不是协议字段**;Host 不内联历史窗口。
|
||||
- `adapter` 只放入口 adapter 的非核心元数据,runner 不应依赖它做长期能力。
|
||||
- `config` 是 Agent/runner config,不是插件实例状态。
|
||||
|
||||
### 4.3 AgentTrigger
|
||||
### 5.3 AgentTrigger
|
||||
|
||||
```python
|
||||
class AgentTrigger(BaseModel):
|
||||
type: str
|
||||
source: Literal["platform", "webui", "api", "scheduler", "system", "pipeline_adapter"]
|
||||
source: Literal["platform", "webui", "api", "scheduler", "system", "host_adapter"]
|
||||
timestamp: int | None = None
|
||||
```
|
||||
|
||||
`trigger.type` 应与 `event.event_type` 一致或更粗粒度。例如 Pipeline 兼容入口触发消息时:
|
||||
`trigger.type` 应与 `event.event_type` 一致或更粗粒度。例如入口适配器触发消息时:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message.received",
|
||||
"source": "pipeline_adapter"
|
||||
}
|
||||
{ "type": "message.received", "source": "host_adapter" }
|
||||
```
|
||||
|
||||
### 4.4 AgentEventContext
|
||||
### 5.4 AgentEventContext
|
||||
|
||||
```python
|
||||
class AgentEventContext(BaseModel):
|
||||
@@ -228,13 +218,11 @@ class AgentEventContext(BaseModel):
|
||||
data: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- `event_type` 使用 LangBot 稳定协议名,例如 `message.received`。
|
||||
- `event_type` 使用 LangBot 稳定协议名,例如 `message.received`。稳定事件名清单见 [EVENT_BASED_AGENT.md](./EVENT_BASED_AGENT.md)。
|
||||
- 平台原始事件名放入 `source_event_type`。
|
||||
- 大型原始 payload 必须放入 `raw_ref` 或 artifact,不应直接塞入 `data`。
|
||||
|
||||
### 4.5 Actor / Subject / Conversation
|
||||
### 5.5 Conversation / Actor / Subject
|
||||
|
||||
```python
|
||||
class ConversationContext(BaseModel):
|
||||
@@ -263,7 +251,7 @@ class SubjectContext(BaseModel):
|
||||
- 入群事件:actor 是新成员或邀请人,subject 是群/成员关系。
|
||||
- 定时事件:actor 可以是 system,subject 是 schedule。
|
||||
|
||||
### 4.6 AgentInput
|
||||
### 5.6 AgentInput
|
||||
|
||||
```python
|
||||
class AgentInput(BaseModel):
|
||||
@@ -273,13 +261,11 @@ class AgentInput(BaseModel):
|
||||
message_chain: dict[str, Any] | None = None
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 文本、多模态、附件都属于当前 event input。
|
||||
- 大文件、图片、音频、工具大结果应以 artifact ref 传递。
|
||||
- `message_chain` 是平台兼容字段,不应成为长期稳定依赖。
|
||||
|
||||
### 4.7 DeliveryContext
|
||||
### 5.7 DeliveryContext
|
||||
|
||||
```python
|
||||
class DeliveryContext(BaseModel):
|
||||
@@ -292,9 +278,9 @@ class DeliveryContext(BaseModel):
|
||||
platform_capabilities: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
Runner 可以参考 delivery 能力决定返回 `message.delta`、`message.completed` 或 `action.requested`。
|
||||
Runner 可参考 delivery 能力决定返回 `message.delta`、`message.completed` 或 `action.requested`。
|
||||
|
||||
### 4.8 ContextAccess
|
||||
### 5.8 ContextAccess
|
||||
|
||||
```python
|
||||
class ContextAccess(BaseModel):
|
||||
@@ -306,12 +292,7 @@ class ContextAccess(BaseModel):
|
||||
has_history_before: bool = False
|
||||
inline_policy: InlineContextPolicy
|
||||
available_apis: ContextAPICapabilities
|
||||
```
|
||||
|
||||
`ContextAccess` 告诉 runner:Host inline 了什么、没有 inline 什么、如果需要更多上下文应该通过哪些 API 拉取。
|
||||
它不是 Host 的业务上下文编排策略,而是 runner 按需读取上下文的入口说明。
|
||||
|
||||
```python
|
||||
class InlineContextPolicy(BaseModel):
|
||||
mode: Literal["none", "current_event", "recent_tail", "summary_tail"]
|
||||
delivered_count: int = 0
|
||||
@@ -330,28 +311,14 @@ class ContextAPICapabilities(BaseModel):
|
||||
storage: bool = False
|
||||
```
|
||||
|
||||
### 4.9 BootstrapContext
|
||||
`ContextAccess` 告诉 runner:Host inline 了什么、没 inline 什么、需要更多上下文时走哪些 API。它是 runner 按需读取上下文的入口说明,不是 Host 的业务上下文编排策略。
|
||||
|
||||
```python
|
||||
class BootstrapContext(BaseModel):
|
||||
messages: list[Message] = []
|
||||
summary: str | None = None
|
||||
artifacts: list[ArtifactRef] = []
|
||||
metadata: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `bootstrap.messages` 不是 LangBot Host 的默认行为。
|
||||
- 自管 context runner 默认应收到空 bootstrap。
|
||||
- Host 不应为了”帮 agent 更聪明”而自动拼接完整 transcript。
|
||||
- 历史窗口策略由 runner 自己管理,并通过 Host history API 按需拉取历史;new/official runners 不应依赖入口 adapter 下发历史窗口。
|
||||
|
||||
### 4.10 RuntimeContext
|
||||
### 5.9 AgentRuntimeContext
|
||||
|
||||
```python
|
||||
class AgentRuntimeContext(BaseModel):
|
||||
host: str = "langbot"
|
||||
protocol_version: str = "1"
|
||||
langbot_version: str | None = None
|
||||
trace_id: str
|
||||
deadline_at: float | None = None
|
||||
@@ -361,9 +328,9 @@ class AgentRuntimeContext(BaseModel):
|
||||
metadata: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
`static_refs` 用于 KV cache 友好的静态上下文引用,例如 system policy、tool schema、resource manifest 的 hash/version。
|
||||
`static_refs` 用于 KV cache 友好的静态上下文引用(system policy、tool schema、resource manifest 的 hash/version)。理由见 AGENT_CONTEXT_PROTOCOL §6。
|
||||
|
||||
### 4.11 State
|
||||
### 5.10 AgentRunState
|
||||
|
||||
```python
|
||||
class AgentRunState(BaseModel):
|
||||
@@ -375,7 +342,7 @@ class AgentRunState(BaseModel):
|
||||
|
||||
State 是可选 host-owned snapshot。Runner 也可以完全自管状态。
|
||||
|
||||
## 5. Resources
|
||||
## 6. Resources
|
||||
|
||||
```python
|
||||
class AgentResources(BaseModel):
|
||||
@@ -389,9 +356,9 @@ class AgentResources(BaseModel):
|
||||
|
||||
资源列表是本次 run 的授权结果。History / Event / Artifact 访问通过 permissions、`ctx.context.available_apis` 和 Host 侧 run session 校验控制,不作为可枚举 resource list 暴露。Runner 只能通过 `AgentRunAPIProxy` 访问这些能力。
|
||||
|
||||
## 6. Result Stream
|
||||
## 7. Result Stream
|
||||
|
||||
### 6.1 AgentRunResult
|
||||
### 7.1 AgentRunResult
|
||||
|
||||
```python
|
||||
class AgentRunResult(BaseModel):
|
||||
@@ -402,180 +369,78 @@ class AgentRunResult(BaseModel):
|
||||
timestamp: int | None = None
|
||||
```
|
||||
|
||||
### 6.2 稳定 result types
|
||||
### 7.2 稳定 result types
|
||||
|
||||
| type | 说明 |
|
||||
| --- | --- |
|
||||
| `message.delta` | 流式消息片段。 |
|
||||
| `message.completed` | 完整消息。 |
|
||||
| `tool.call.started` | runner 开始工具调用的可观测事件。 |
|
||||
| `tool.call.completed` | runner 完成工具调用的可观测事件。 |
|
||||
| `artifact.created` | runner 生成 artifact。 |
|
||||
| `state.updated` | runner 请求更新 host-owned state。 |
|
||||
| `action.requested` | runner 请求 Host 执行平台动作。 |
|
||||
| `run.completed` | run 正常结束。 |
|
||||
| `run.failed` | run 失败。 |
|
||||
| type | 说明 | 当前消费 |
|
||||
| --- | --- | --- |
|
||||
| `message.delta` | 流式消息片段。 | ✅ |
|
||||
| `message.completed` | 完整消息。 | ✅ |
|
||||
| `tool.call.started` | 工具调用开始的可观测事件。 | telemetry |
|
||||
| `tool.call.completed` | 工具调用完成的可观测事件。 | telemetry |
|
||||
| `artifact.created` | runner 生成 artifact。 | ✅ |
|
||||
| `state.updated` | runner 请求更新 host-owned state。 | ✅ |
|
||||
| `action.requested` | runner 请求 Host 执行平台动作。 | **reserved / 仅 telemetry,不执行** |
|
||||
| `run.completed` | run 正常结束。 | ✅ |
|
||||
| `run.failed` | run 失败。 | ✅ |
|
||||
|
||||
Host 必须忽略未知 result type 并记录 warning,除非该 type 明确要求强校验。
|
||||
`action.requested` 是为 EBA 和 platform API 预留的协议表面:当前阶段 Host 收到后只记 telemetry,**不执行**,runner 作者不应依赖其副作用。执行模型见 EVENT_BASED_AGENT §6。
|
||||
|
||||
### 6.3 message.delta
|
||||
### 7.3 示例
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message.delta",
|
||||
"data": {
|
||||
"chunk": {
|
||||
"role": "assistant",
|
||||
"content": "hel"
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "type": "message.delta", "data": { "chunk": { "role": "assistant", "content": "hel" } } }
|
||||
{ "type": "message.completed", "data": { "message": { "role": "assistant", "content": "hello" } } }
|
||||
{ "type": "state.updated", "data": { "scope": "conversation", "key": "external.session_id", "value": "abc" } }
|
||||
{ "type": "action.requested", "data": { "action": "message.edit", "target": {"message_id": "..."}, "payload": {"text": "..."} } }
|
||||
```
|
||||
|
||||
### 6.4 message.completed
|
||||
Host 必须校验 `state.updated` 的 scope、key、value 大小和 JSON 可序列化性。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message.completed",
|
||||
"data": {
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
## 8. AgentRunAPIProxy
|
||||
|
||||
### 6.5 state.updated
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "state.updated",
|
||||
"data": {
|
||||
"scope": "conversation",
|
||||
"key": "external.session_id",
|
||||
"value": "abc"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Host 必须校验 scope、key、value 大小和 JSON 可序列化性。
|
||||
|
||||
### 6.6 action.requested
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "action.requested",
|
||||
"data": {
|
||||
"action": "message.edit",
|
||||
"target": {"message_id": "..."},
|
||||
"payload": {"text": "..."}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Protocol v1 只定义表达方式。Host 是否执行 action 取决于 platform API 能力、binding policy、审批策略和实现阶段。
|
||||
|
||||
## 7. AgentRunAPIProxy
|
||||
|
||||
所有 proxy action 必须携带 `run_id`。Host 必须校验:
|
||||
|
||||
- active run session 存在。
|
||||
- caller plugin identity 匹配。
|
||||
- resource 在本次 `ctx.resources` 中授权。
|
||||
- scope 不越界。
|
||||
- payload size / rate limit / deadline 合法。
|
||||
|
||||
### 7.1 Model APIs
|
||||
所有 proxy action 必须携带 `run_id`。Host 必须校验:active run session 存在、caller plugin identity 匹配、resource 在本次 `ctx.resources` 中授权、scope 不越界、payload size / rate limit / deadline 合法。
|
||||
|
||||
```python
|
||||
# Model
|
||||
await api.models.invoke(model_id, messages, tools=None, extra_args=None)
|
||||
await api.models.stream(model_id, messages, tools=None, extra_args=None)
|
||||
await api.models.rerank(model_id, query, documents, top_k=None)
|
||||
```
|
||||
|
||||
### 7.2 Tool APIs
|
||||
|
||||
```python
|
||||
# Tool
|
||||
await api.tools.get_detail(tool_name)
|
||||
await api.tools.call(tool_name, parameters)
|
||||
```
|
||||
|
||||
### 7.3 Knowledge APIs
|
||||
|
||||
```python
|
||||
# Knowledge
|
||||
await api.knowledge.retrieve(kb_id, query_text, top_k=5, filters=None)
|
||||
```
|
||||
|
||||
### 7.4 History APIs
|
||||
# History(返回 Transcript projection,不返回原始平台 payload)
|
||||
await api.history.page(conversation_id=None, before_cursor=None, after_cursor=None,
|
||||
limit=50, direction="backward", include_artifacts=False)
|
||||
await api.history.search(query, filters=None, top_k=10)
|
||||
|
||||
```python
|
||||
await api.history.page(
|
||||
conversation_id=None,
|
||||
before_cursor=None,
|
||||
after_cursor=None,
|
||||
limit=50,
|
||||
direction="backward",
|
||||
include_artifacts=False,
|
||||
)
|
||||
|
||||
await api.history.search(
|
||||
query,
|
||||
filters=None,
|
||||
top_k=10,
|
||||
)
|
||||
```
|
||||
|
||||
History API 返回 Transcript projection,不返回原始平台 payload。
|
||||
|
||||
### 7.5 Event APIs
|
||||
|
||||
```python
|
||||
# Event(返回稳定 event envelope 或受限 raw ref,不默认返回大 payload)
|
||||
await api.events.get(event_id)
|
||||
await api.events.page(before_cursor=None, limit=50)
|
||||
```
|
||||
|
||||
Event API 返回稳定 event envelope 或受限 raw ref,不默认返回大 payload。
|
||||
|
||||
### 7.6 Artifact APIs
|
||||
|
||||
```python
|
||||
# Artifact(必须支持大小限制、MIME 校验、过期时间和授权范围)
|
||||
await api.artifacts.metadata(artifact_id)
|
||||
await api.artifacts.read_range(artifact_id, offset=0, length=65536)
|
||||
await api.artifacts.open_stream(artifact_id)
|
||||
```
|
||||
|
||||
Artifact API 必须支持大小限制、MIME 校验、过期时间和授权范围。
|
||||
# State / Storage
|
||||
await api.state.get(scope, key); await api.state.set(scope, key, value); await api.state.delete(scope, key)
|
||||
await api.storage.get(area, key); await api.storage.set(area, key, value)
|
||||
await api.storage.delete(area, key); await api.storage.list(area, prefix=None)
|
||||
|
||||
### 7.7 State / Storage APIs
|
||||
|
||||
```python
|
||||
await api.state.get(scope, key)
|
||||
await api.state.set(scope, key, value)
|
||||
await api.state.delete(scope, key)
|
||||
|
||||
await api.storage.get(area, key)
|
||||
await api.storage.set(area, key, value)
|
||||
await api.storage.delete(area, key)
|
||||
await api.storage.list(area, prefix=None)
|
||||
```
|
||||
|
||||
建议区分:
|
||||
|
||||
- `state`: 小型 JSON 状态,适合 conversation / actor / runner / binding。
|
||||
- `storage`: blob 或较大数据,适合插件私有数据、workspace 数据、checkpoint。
|
||||
|
||||
### 7.8 Platform APIs
|
||||
|
||||
```python
|
||||
# Platform(受限能力,默认不开放,需 manifest + binding policy + 用户审批同时允许)
|
||||
await api.platform.request_action(action, target, payload)
|
||||
```
|
||||
|
||||
平台 API 是受限能力。默认不开放。需要 runner manifest、binding policy、用户审批策略同时允许。
|
||||
`state` 与 `storage` 的建议边界:`state` 放小型 JSON(conversation / actor / runner / binding),`storage` 放 blob 或较大数据(插件私有数据、workspace 数据、checkpoint)。
|
||||
|
||||
## 8. 错误模型
|
||||
返回数据结构(如 `HistoryPage`、artifact metadata)见 AGENT_CONTEXT_PROTOCOL §4。
|
||||
|
||||
Host API 错误统一返回:
|
||||
## 9. 错误模型
|
||||
|
||||
```python
|
||||
class AgentAPIError(BaseModel):
|
||||
@@ -585,8 +450,6 @@ class AgentAPIError(BaseModel):
|
||||
details: dict[str, Any] = {}
|
||||
```
|
||||
|
||||
建议 code:
|
||||
|
||||
| code | 说明 |
|
||||
| --- | --- |
|
||||
| `unauthorized` | 未授权访问资源或 scope。 |
|
||||
@@ -600,96 +463,46 @@ class AgentAPIError(BaseModel):
|
||||
Runner 失败使用 `run.failed`:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "run.failed",
|
||||
"data": {
|
||||
"code": "runner.error",
|
||||
"message": "failed to call external agent",
|
||||
"retryable": false
|
||||
}
|
||||
}
|
||||
{ "type": "run.failed", "data": { "code": "runner.error", "message": "failed to call external agent", "retryable": false } }
|
||||
```
|
||||
|
||||
## 9. Timeout 与 Cancellation
|
||||
## 10. Timeout 与 Cancellation
|
||||
|
||||
Host 在 `ctx.runtime.deadline_at` 中下发总 deadline。SDK proxy 必须用该 deadline 限制单次 action timeout。
|
||||
|
||||
取消语义:
|
||||
|
||||
- Host 可以取消 active run。
|
||||
- Runtime 应尽力中断 runner。
|
||||
- Host 在 `ctx.runtime.deadline_at` 下发总 deadline;SDK proxy 必须用该 deadline 限制单次 action timeout。
|
||||
- Host 可以取消 active run;Runtime 应尽力中断 runner。
|
||||
- Runner 支持中断时应返回或触发 `run.failed`,code 为 `cancelled`。
|
||||
- Host 必须 unregister active run session。
|
||||
|
||||
## 10. Security 与 Guardrail
|
||||
## 11. Security 与 Guardrail(协议层)
|
||||
|
||||
Protocol v1 的安全边界在 Host:
|
||||
|
||||
- Runner 不能直接访问未授权 model/tool/kb/history/artifact/storage。
|
||||
- SDK 本地校验只提升开发体验,不能替代 Host 校验。
|
||||
- 所有 resource id 对 runner 来说都是 opaque。
|
||||
- 默认只能访问当前 conversation / thread 的 history。
|
||||
- 跨会话、workspace 级 history 或 storage 必须额外授权。
|
||||
- 默认只能访问当前 conversation / thread 的 history;跨会话、workspace 级访问必须额外授权。
|
||||
- 大 payload 必须 artifact 化。
|
||||
- Host 必须记录 run_id、runner_id、action、resource、scope、result。
|
||||
|
||||
对外部 harness runner,边界进一步拆分为:
|
||||
Host 不负责业务编排:不拼接全量历史、不替 runner 做 prompt assembly、不内置 agent memory / tool loop / 上下文压缩策略。这些由官方或第三方 AgentRunner 插件实现。
|
||||
|
||||
- Host 在调用前完成 binding/resource policy 裁剪、路径策略、secret 过滤和审计记录。
|
||||
- Runner plugin 把授权后的 context/resource projection 适配为目标 harness 的 context 文件、MCP 配置、skill 目录、环境变量或 CLI 参数。
|
||||
- Claude Code / Codex / Kimi Code 等外部 harness 的 native permission mode、allowed/disallowed tools 和执行隔离策略只是额外执行约束,不能替代 Host 侧授权。
|
||||
- 外部 session id、working directory、checkpoint 等跨轮次指针应作为小型 JSON state 保存,例如 `external.session_id`、`external.working_directory`。
|
||||
对外部 harness runner,Host 在调用前完成 binding/resource policy 裁剪、路径策略、secret 过滤和审计;runner plugin 把授权后的 context/resource projection 适配为目标 harness 的形式;harness 的 native permission mode、allowed/disallowed tools 只是额外执行约束,不能替代 Host 授权。
|
||||
|
||||
完整路径隔离、MCP allowlist、secret redaction、配额、workspace 清理和发布级安全测试不属于当前 Protocol v1 smoke 闭环,详见 [SECURITY_HARDENING.md](./SECURITY_HARDENING.md)。
|
||||
> 发布级路径隔离、MCP allowlist、secret redaction、配额、workspace 清理等**不属于** v1 协议闭环,是生产默认启用前的 release gate,见 [SECURITY_HARDENING.md](./SECURITY_HARDENING.md)。
|
||||
|
||||
Host 不负责业务编排:
|
||||
## 12. Pipeline Adapter 边界
|
||||
|
||||
- 不拼接全量历史。
|
||||
- 不替 runner 做业务 prompt assembly。
|
||||
- 不内置 agent memory 策略。
|
||||
- 不内置 tool loop 业务流程。
|
||||
- 不内置上下文压缩策略。
|
||||
Pipeline 是当前入口 adapter,不是协议中心。Query entry adapter 负责:
|
||||
|
||||
这些能力可以由官方或第三方 AgentRunner 插件实现,并通过公开 Host APIs 消费 LangBot 的状态、历史、存储、artifact、模型、工具和知识库能力。
|
||||
|
||||
## 11. Pipeline Adapter
|
||||
|
||||
Pipeline 是当前入口 adapter,不是协议中心。
|
||||
|
||||
**当前分支已实现**:
|
||||
|
||||
- ✅ `PipelineAdapter.query_to_event(query)` — 从 `Query` 构造 `AgentEventEnvelope`
|
||||
- ✅ `PipelineAdapter.pipeline_config_to_binding(query, runner_id)` — 从 Pipeline config 构造临时 AgentBinding
|
||||
- ✅ `run_from_query()` 委托到 `run(event, binding)`
|
||||
- ✅ Agent/runner config 从当前配置容器透传到 `AgentBinding.runner_config` / `ctx.config`
|
||||
- ✅ Query-only 字段放入 `adapter` context
|
||||
|
||||
Pipeline adapter 负责:
|
||||
|
||||
- 从 `Query` 构造 `AgentEventContext`。
|
||||
- 从当前配置容器构造临时 AgentBinding。
|
||||
- 从 `Query` 构造 `AgentEventContext` 和临时 `AgentBinding`(见 HOST_SDK §4.2)。
|
||||
- 从当前 Agent/runner config 构造 `ctx.config`。
|
||||
- 保留必要的 legacy adapter metadata,但不定义历史窗口、prompt 组装或 agentic context 策略。
|
||||
- 后续若需要传递 preprocessing / hook 后的有效指令,应通过 Host prompt/instruction
|
||||
package pull API 暴露能力位和引用,而不是继续把 prompt 推入 `ctx.adapter.extra`。
|
||||
- 将 Query-only 字段放入 `adapter`。
|
||||
- 将 Query-only 字段放入 `ctx.adapter`,例如 filtered params 放 `ctx.adapter.extra["params"]`。
|
||||
|
||||
Runner 不应长期依赖 `adapter`。新 runner 应只依赖 event-first context 和 Host APIs。
|
||||
约束:
|
||||
|
||||
## 12. 最小 v1 完成标准
|
||||
|
||||
Protocol v1 已在当前分支完成:
|
||||
|
||||
- ✅ SDK 定义 `AgentRunnerManifest`、`AgentRunContext`、`AgentRunResult`、`AgentRunAPIProxy`
|
||||
- ✅ Runtime 支持 `LIST_AGENT_RUNNERS` 和 `RUN_AGENT`
|
||||
- ✅ Host 支持 `run_id` session authorization
|
||||
- ✅ Host 能从当前 Pipeline 入口生成 event-first context
|
||||
- ✅ `messages` 降级为 optional bootstrap
|
||||
- ✅ Host 不定义通用历史窗口字段或策略
|
||||
- ✅ Proxy 至少覆盖 model、tool、knowledge、state/storage
|
||||
- ✅ History / event / artifact API 已落地
|
||||
- ✅ EventLog / Transcript / ArtifactStore / PersistentStateStore 已落地
|
||||
- ✅ 外部 harness runner 最小 smoke 已落地:Claude Code runner 能消费 event-first context、返回消息、写回 `external.session_id` / `external.working_directory`
|
||||
- adapter **不**定义历史窗口、prompt 组装或 agentic context 策略。
|
||||
- preprocessing / hook 后的有效指令不通过 `ctx.adapter.extra` 主动推送;后续应通过 Host prompt/instruction pull API 暴露(占位见 HOST_SDK §4.8)。
|
||||
- 新 runner 不应长期依赖 `adapter`,应只依赖 event-first context 和 Host API。
|
||||
|
||||
## 13. 开放问题
|
||||
|
||||
|
||||
Reference in New Issue
Block a user