refactor(agent-runner): tighten protocol v1 runtime boundaries

This commit is contained in:
huanghuoguoguo
2026-05-25 10:34:16 +08:00
parent cc911cc413
commit ab96070036
26 changed files with 548 additions and 3291 deletions

View File

@@ -60,7 +60,7 @@ Delivery / Renderer / Platform API
```
**当前状态**
- `PipelineAdapter` 作为当前 transition adapter将 Pipeline Query 转换为 `AgentEventEnvelope` + `AgentBinding`
- `PipelineAdapter` 作为当前入口 adapter将 Pipeline Query 转换为 `AgentEventEnvelope` + `AgentBinding`
- `run_from_query()` 内部委托到 `run(event, binding)`
- EventLog / Transcript / ArtifactStore / PersistentStateStore 已落地
- EventGateway 由外部 event branch 实现
@@ -99,7 +99,7 @@ class AgentEventEnvelope(BaseModel):
raw_ref: RawEventRef | None
```
**当前 transition source**`PipelineAdapter.query_to_event(query)` 从 Pipeline Query 生成 `AgentEventEnvelope`
**当前 adapter source**`PipelineAdapter.query_to_event(query)` 从 Pipeline Query 生成 `AgentEventEnvelope`
原始平台 payload 可以存为 raw event 或 artifact ref不要把平台私有字段直接扩散到 AgentRunner 顶层协议。
@@ -122,7 +122,7 @@ class AgentBinding(BaseModel):
enabled: bool
```
**当前 transition source**`PipelineAdapter.pipeline_config_to_binding(query, runner_id)` 从 Pipeline config 生成临时 `AgentBinding`
**当前 adapter source**`PipelineAdapter.pipeline_config_to_binding(query, runner_id)` 从 Pipeline config 生成临时 `AgentBinding`
Pipeline 当前可以被迁移为一种 binding source

View File

@@ -150,53 +150,52 @@ class AgentRunnerDescriptor(BaseModel):
- Pipeline metadata 请求时发现缓存为空
- 可选 TTL优先保证正确性
### 3.4 context_builder.py
### 3.4 context_builder.py / pipeline_adapter.py
把当前 Pipeline query 转换成 SDK v1 `AgentRunContext` envelope。这里做协议字段组装、Host-owned 状态快照、授权资源挂载和默认工作窗口 provisioning不承担 Agent 的最终 prompt 组装或长期记忆/压缩策略
`context_builder.py` 只负责从 `AgentEventEnvelope + AgentBinding` 构造 SDK v1 `AgentRunContext`。Pipeline Query 的读取、参数过滤、prompt 提取和 `max-round` bootstrap 映射都属于 `PipelineAdapter`,不再放进 context builder
当前消息 Pipeline 的最小字段
当前消息 Pipeline 进入 agent runner 的路径
```text
Query
-> PipelineAdapter.query_to_event(query)
-> PipelineAdapter.pipeline_config_to_binding(query, runner_id)
-> PipelineAdapter.build_adapter_context(query, binding)
-> AgentRunOrchestrator.run(event, binding, adapter_context=...)
-> AgentRunContextBuilder.build_context_from_event(...)
```
Protocol v1 context 的稳定字段:
- `run_id`: 新 UUID不使用 query id 作为全局 run id
- `trigger.type`: `message.received`
- `conversation`: launchersenderbotpipeline、历史消息
- `event`: message event envelope 子集,`event_type` 使用稳定协议名,平台/SDK 原始事件名放入 `event_data.source_event_type`
- `actor`: sender
- `subject`: 当前消息或 launcher
- `prompt`: 宿主已处理的有效 prompt`query.prompt.messages`
- `messages`: `query.messages` 进入 AgentRunner context packaging 后的历史窗口。插件化 AgentRunner 路径不再由 Pipeline `msgtrun` 截断
- `runtime.metadata.context_packaging`: Host 本次实际下发的历史窗口元数据,例如来源、策略、下发消息数、完整性;未来可扩展 cursor 和 host-side history API
- `input`: 从 `query.user_message``query.message_chain` 构造
- `params`: 过滤后的公开业务变量
- `resources`: 由 `resource_builder` 注入
- `state`: host-managed scoped state snapshot
- `runtime`: host/version/workspace/bot/pipeline/query/trace/deadline
- `config`: 当前 Pipeline 对该 runner id 的绑定配置,即 `ai.runner_config[runner_id]`
- `trigger.type`: 事件触发类型,例如 `message.received`
- `conversation`: conversation/thread/launcher/sender/bot/pipeline 投影
- `event`: 稳定事件上下文
- `actor`: 触发者
- `subject`: 当前消息、群、频道或其它事件主体
- `input`: 当前事件输入,不是历史消息窗口
- `delivery`: 输出 surface 和平台投递能力
- `resources`: 由 `resource_builder` 基于 binding policy 注入
- `state`: `PersistentStateStore` 读取的 host-managed scoped state snapshot
- `runtime`: host/version/workspace/bot/query/trace/deadline
- `config`: 当前 binding 对该 runner id 的配置,即 `runner_config`
- `bootstrap`: 可选小窗口,不是完整历史
- `adapter`: Pipeline 或其它入口 adapter 的元数据
保留 SDK legacy helper 是 SDK 的责任LangBot 不再构造 PoC 的 `query_id/session/messages/user_message/extra_config` 上下文。
Pipeline adapter 的 `prompt` 和公开业务变量不进入顶层协议字段:
`prompt` 的语义必须明确:它不是静态配置 `config["prompt"]`,而是 LangBot PreProcessor 和 `PromptPreProcessing` 插件事件之后的有效 prompt。旧内置 local-agent 请求模型时使用:
- effective prompt -> `ctx.adapter.extra["prompt"]`
- filtered params -> `ctx.adapter.extra["params"]`
- `max-round` working window -> `ctx.bootstrap.messages`
- 同一窗口也可出现在 `ctx.adapter.adapter_messages`,供 adapter 消费方读取
- packaging 元数据 -> `ctx.runtime.metadata.context_packaging`
```python
query.prompt.messages + query.messages + [query.user_message]
```
插件化 runner 要保持行为一致,应消费:
```python
ctx.prompt + ctx.messages + [current_user_message_from_ctx.input]
```
现阶段不要优化裁剪算法,也不要把新的压缩或 token-budget 裁剪塞回 Pipeline stage。
插件化 AgentRunner 路径应跳过 Pipeline `msgtrun` 的破坏性截断,然后由
`AgentContextPackager` 在 AgentRunner 边界执行同一套 max-round user-round 规则。
当前 SDK v1 还没有顶层 context packaging 字段LangBot 先把本次 packaging
元数据放在 `ctx.runtime.metadata.context_packaging`。这是实际下发结果说明,不是 LangBot 侧的长期策略控制面。
后续 LiteLLM 接入后再把真实 context window、token 预算和摘要策略接到这个边界上。
现阶段不要把新的压缩或 token-budget 裁剪塞回 Pipeline stage。Pipeline 只负责入口适配;完整历史和长期上下文由 EventLog / Transcript / pull APIs / future ContextCompressor 支撑。
### 3.4.1 Agentic context plan
本轮只落地 `AgentContextPackager` `max_round` working window不改变 user-round 选择规则。
下面的 `ConversationStore` / `EventLog``ContextCompressor` 和 host history API 仍是设计预留。
本轮只 `PipelineAdapter` 中保留 `max-round` working window不改变 user-round 选择规则。
EventLog / Transcript / Host pull APIs 已落地,`ContextCompressor` 仍是设计预留。
目标是让 Pipeline 逐步退化为入口 adapter让 AgentRunner 层拥有上下文打包职责。
建议最终拆成四个 host-side 服务:
@@ -206,7 +205,7 @@ ConversationStore / EventLog
-> durable append-only raw messages, events, tool results, artifact refs
ConversationProjection
-> converts events into agent-readable conversation history
AgentContextPackager
PipelineAdapter bootstrap policy
-> builds the bounded working context for one run
ContextCompressor
-> creates and updates summaries/checkpoints when thresholds are exceeded
@@ -215,10 +214,10 @@ ContextCompressor
关键原则:
- 完整历史属于 LangBot host不属于插件实例。插件仍是 singleton/stateless。
- `ctx.messages` 是 working context window不是完整 conversation dump。
- `ctx.bootstrap.messages` optional working context window不是完整 conversation dump。
- 每轮不能全量复制/序列化完整历史给插件 runtime否则长会话会产生 O(n) 成本和跨进程 payload 膨胀。
- `max-round` 的 user-round 规则可以先搬到 `AgentContextPackager`,作为 `max_round` adapter 策略。
- LiteLLM 接入后,`AgentContextPackager` 再读取模型 context window升级为 token budget 策略。
- `max-round` 的 user-round 规则只属于 Pipeline adapter 的 bootstrap 策略。
- LiteLLM 接入后,context packaging 应升级为 token budget / summary / pull API 协作策略。
- `ContextCompressor` 生成的是派生 summary/checkpoint不能覆盖或删除 raw history。
- 重启恢复依赖持久化 store 和 summary checkpoint不依赖 `SessionManager` 里的进程内 conversation list。
@@ -252,7 +251,7 @@ page size、总字节数、deadline 和可访问 conversation。
### 3.4.2 Large artifacts and tool collaboration
大文件、多模态输入和工具产物不要内联进 `ctx.messages` 或 tool result。后续统一用
大文件、多模态输入和工具产物不要内联进 bootstrap messages 或 tool result。后续统一用
artifact/resource ref 协作:
- message/content 里只放小文本和必要摘要。
@@ -322,7 +321,7 @@ Platform Adapter
-> ConversationProjection update message/history view when applicable
-> EventRouter resolve binding
-> AgentRunOrchestrator.run_from_event(event_request)
-> AgentContextPackager build working context from projection + state + artifacts
-> Context packager builds working context from projection + state + artifacts
```
这样消息事件、工具事件、群成员事件、好友申请事件可以共用同一套 run/session/state/resource
@@ -481,8 +480,9 @@ async def run_from_query(query: pipeline_query.Query) -> AsyncGenerator[Message
### Step 1补齐宿主上下文
- SDK `AgentRunContext` 增加 `prompt`,并保持向后兼容默认空列表
- LangBot context builder 写入 `ctx.prompt``ctx.input.contents``ctx.runtime.metadata.streaming_supported``ctx.runtime.metadata.remove_think`
- SDK `AgentRunContext` 保持 event-first`event/input/delivery/resources/context/state/runtime/config/bootstrap/adapter`
- LangBot context builder 只从 `AgentEventEnvelope + AgentBinding` 写入稳定协议字段
- Pipeline adapter 把 effective prompt 写入 `ctx.adapter.extra["prompt"]`,把公开业务变量写入 `ctx.adapter.extra["params"]`
- 保持 `ctx.config` 只表达静态绑定配置。
### Step 2增强宿主 AgentRun proxy action
@@ -502,7 +502,7 @@ async def run_from_query(query: pipeline_query.Query) -> AsyncGenerator[Message
### Step 4local-agent parity
- 使用 `ctx.prompt` 而不是重新读取 `ctx.config["prompt"]`
- 使用 `ctx.adapter.extra["prompt"]` 而不是重新读取 `ctx.config["prompt"]`
- 当前 user message 从 `ctx.input.contents` 构造,保留多模态内容。
- RAG 只替换/插入文本部分,不丢图片/文件。
- streaming/non-streaming 默认跟随 `runtime.metadata.streaming_supported`

View File

@@ -88,7 +88,7 @@ Host 侧 agent runner 单测不通过时,不应进入 UI parity QA。
| ID | 场景 | 步骤 | 通过条件 |
| --- | --- | --- | --- |
| P1-LA-01 | 普通文本对话 | 绑定 `plugin:langbot/local-agent/default`,发送普通文本。 | 回复正常生成conversation history 写入用户消息和助手消息。 |
| P1-LA-02 | 有效 prompt | 配置 system prompt并通过 PromptPreProcessing 插件或现有预处理改变 prompt。 | runner 使用 host 处理后的 `ctx.prompt`,不是只读取静态 `ctx.config.prompt`;回复体现有效 prompt。 |
| P1-LA-02 | 有效 prompt | 配置 system prompt并通过 PromptPreProcessing 插件或现有预处理改变 prompt。 | runner 使用 host 处理后的 `ctx.adapter.extra["prompt"]`,不是只读取静态 `ctx.config.prompt`;回复体现有效 prompt。 |
| P1-LA-03 | 历史消息 | 连续多轮对话,第二轮引用第一轮内容。 | 当前兼容路径下 runner 能读到 host 下发的 bootstrap/history目标协议下应通过 history API 或插件自管上下文实现。第二轮能基于上下文回答。 |
| P1-LA-04 | 流式输出 | 使用支持流式的 adapter/WebUI开启流式模型或流式 runner。 | UI 逐步更新;后端接收 `message.delta`;最终没有重复消息或空白卡片。 |
| P1-LA-05 | 非流式输出 | 使用不支持流式或关闭流式的路径。 | 只输出最终消息;不会创建异常流式卡片。 |

View File

@@ -29,7 +29,7 @@ EventGateway 在本文档中描述为 **future integration point**,由外部 e
## 当前状态
**当前 Pipeline 是 transition adapter不再是 agent runner 设计核心。**
**当前 Pipeline 是入口 adapter不再是 agent runner 设计核心。**
当前主入口仍可由 Pipeline 触发,但内部已转换成 event-first path