Files
LangBot/docs/agent-runner-pluginization/AGENT_RUNNER_QA_GUIDE.md
T

14 KiB
Raw Blame History

Agent Runner QA 指南

本文档是 agent-runner 插件化下一轮测试的唯一 QA 入口。它合并并取代旧的 Phase 1 验收矩阵与 2026-05-18 / 2026-05-29 两份本地 QA 报告。

目标不是保留完整历史流水账,而是指导测试 agent 用最小但高价值的路径判断当前分支是否仍然健康。

1. 测试边界

当前主线验证的是 AgentRunner Protocol v1

event -> binding -> runner.run(ctx) -> result stream

本指南验证:

  • Host 能通过当前 Query entry adapter 进入 event-first run(event, binding) 主链路。
  • Runner 来自插件 registry,而不是旧内置 runner 分支。
  • local-agent 能消费 Host 模型、工具、知识库、history、state、sandbox 文件等基础设施。
  • 外部 harness runnerACP / Claude Code / Codex 等直接 runner 插件)能消费 event-first context,并把外部 session 指针写回 host-owned state。
  • 错误、权限裁剪、无输出、timeout 等路径不会破坏主聊天流程。

本指南不验证:

  • 完整外部 harness daemon 管控和分布式业务队列;已实现的 run ledger / heartbeat / claim 原语仍应执行定向回归。
  • 尚未实现的通用事件订阅和定时自动化;已集成的 Bot 路由、独立 Agent 和 Pipeline 必须纳入当前产品验收。
  • 发布级 path isolation、secret filtering、MCP allowlist、资源配额和 workspace cleanup。
  • 所有外部服务 runner 的真实凭据联调。

这些属于后续能力或发布门槛,分别见 RUNTIME_CONTROL_PLANE_V2.mdSECURITY_HARDENING.md

2. 状态定义

测试报告只使用以下状态:

状态 含义
PASS 按步骤执行,用户可见行为和日志证据都满足通过条件。
FAIL 环境可用,但行为不满足通过条件。
BLOCKED 凭据、CLI、外部服务、测试数据或本地配置缺失导致无法执行。必须写清阻塞原因。
N/A 当前 runner 或平台明确不支持该能力。必须引用 manifest、文档或配置说明。

不能使用“看起来正常”“大概通过”“基本没问题”等模糊状态。

3. 执行顺序

2026-09-05 更新:先记录 Core/SDK/Runner 提交、发行版元数据与实际 import 路径,区分 editable 工作区和正式包安装。当前定向测试及前端未通过断言见 STATUS.md。本指南的步骤不是已执行记录。

本轮发布验收至少覆盖:

  • 空白实例从市场安装 Runner,创建 Agent 与 Pipeline,并绑定到 Bot。
  • 同一 Bot 的消息事件走 Pipeline,非消息事件走独立 Agent;dry-run 与保存后的路由结果一致。
  • event_* 使用冻结目标;未授权 platform_*、额外参数和失效机器人调用被拒绝;SDK/Python 与 MCP gateway 均走 Host 工具授权。
  • interaction.requested 回调恢复原处理器,重复/过期/跨作用域提交被拒绝。
  • 分别记录合成事件、mock provider、真实平台和真实 provider 的结果,不能相互替代。

推荐按以下顺序执行,前一层失败时不要继续扩大测试面:

  1. Host / SDK / runner 单测。
  2. WebUI 登录与 Pipeline Debug Chat 基础 smoke。
  3. local-agent 高价值场景。
  4. 外部 code-agent harness smoke。
  5. 权限和错误路径补充检查。
  6. 汇总 PASS / FAIL / BLOCKED,并给出下一步建议。

用户可见流程必须通过 WebUI 或真实消息平台验证。API / curl 只能作为诊断证据,不能单独让 UI case PASS。

4. 必跑基线

4.1 单测基线

在 LangBot 仓库运行:

uv run --frozen pytest tests/unit_tests/agent

如果本次改动只触及默认配置或 API service,也至少补跑相关目标测试,例如:

uv run pytest tests/unit_tests/api/test_pipeline_service_defaults.py

通过条件:

  • agent 单测全 PASS,或失败项已确认与本次 agent-runner 路径无关。
  • 若失败来自 context_builderorchestratorsession_registryresource_builderplugin/handler.py 的 run action 权限路径,不应进入 UI smoke。

4.2 环境基线

langbot-skills 做环境检查:

cd "$LANGBOT_SKILLS_REPO"
bin/lbs env doctor
bin/lbs case list

LANGBOT_SKILLS_REPO 指向当前工作区里的 langbot-skills 仓库。优先使用已有 case,而不是临时发明测试路径。

推荐首批 case

  • webui-login-state
  • pipeline-debug-chat
  • local-agent-basic-debug-chat
  • local-agent-rag-debug-chat(改动涉及 RAG / knowledge
  • local-agent-plugin-tool-call-debug-chat(改动涉及 tool / resource policy

5. WebUI 主链路 Smoke

5.1 Runner registry

步骤:

  1. 打开 WebUI Pipeline 配置页。
  2. 查看 AI runner 下拉列表。
  3. 选择 plugin:langbot-team/LocalAgent/default
  4. 保存并刷新页面。

通过条件:

  • runner 选项来自插件 registry。
  • 保存后配置仍为 ai.runner.id + ai.runner_config[id]
  • runner_config 表示 Agent/runner config,不表示插件实例状态。
  • 不读取或回写旧 ai.runner.runner 字段。
  • 不出现旧内置 runner stage 名(例如裸 local-agent)作为当前选中项或配置 surface。
  • 插件没有循环重启或 metadata 加载失败。

5.2 主聊天路径

步骤:

  1. 使用绑定 plugin:langbot-team/LocalAgent/default 的 Pipeline。
  2. 在 Debug Chat 发送确定性普通文本。
  3. 查看 WebUI 回复和后端日志。

通过条件:

  • 用户可见回复正常。
  • 后端日志显示走 AgentRunOrchestrator / RUN_AGENT
  • 不走旧内置 local-agent 主执行分支。
  • conversation transcript 写入用户消息和助手消息。

6. local-agent 高价值测试

只保留最能覆盖架构边界的场景。

ID 场景 操作 通过条件
LA-01 绑定 prompt 配置 system prompt 后发送文本。 runner 使用 ctx.config.prompt,不读取 ctx.adapter.extra["prompt"];回复体现绑定 prompt。
LA-02 history API 连续两轮对话,第二轮引用第一轮 marker。 runner 通过 Host history API 或自管上下文读取历史,不依赖 inline history window。
LA-03 流式 / 非流式 分别用支持流式和关闭流式的路径发送文本。 流式 UI 不重复、不空白;非流式只输出最终消息。
LA-04 工具调用 绑定测试工具,发送会触发工具的 prompt。 ctx.resources.tools 只包含授权工具;工具调用 started/completed;最终回复包含工具结果。
LA-05 RAG 绑定测试知识库,发送命中文档的 prompt。 ctx.resources.knowledge_bases 包含所选知识库;runner 通过授权 API 检索;回复使用检索内容。
LA-06 多模态 发送图片输入。 ctx.input.contents 保留图片;支持视觉模型时正常处理,不支持时受控失败。
LA-07 fallback / 错误 模拟 primary 模型失败或 runner 抛错。 fallback 或 run.failed 行为受控;后续请求不受影响。
LA-08 无输出保护 测试 runner 完成但不产出消息。 不产生空白成功回复;按受控失败或明确缺陷处理。
LA-09 steering / 运行中追加消息 使用支持 steering 的 runner,第一条消息触发长 runrun 未结束时在同 conversation 追加第二条消息。 第二条消息被 active run claim,不启动并发 runrunner 通过 steering_pull 看到追加输入;EventLog 有 queued -> steering.injected,若未消费则有 steering.dropped 终态;后续普通消息仍可处理。

Rerank、remove-think、文件输入等场景只在本次改动直接涉及时补测,不作为每轮必跑项。

7. Code-agent Harness Smoke

这些测试用于验证 ACP、Claude Code、Codex 这类自管 runtime 能走同一条 Host 协议路径。若目标 harness 没有 CLI/daemon、登录态、代理配置或远端 workspace,标记 BLOCKED,不要伪造 PASS。

Smoke 前应优先保留一层轻量单测或 fixture 测试:session 创建/复用、消息发送、结果解析、run_id 注入和 LangBot MCP gateway 必须有稳定测试覆盖。WebUI smoke 证明真实链路可用,但不能替代转换层和错误映射测试。

7.1 外部 harness runner

步骤:

  1. 确认目标 harness(例如 ACP daemon、Claude Code 或 Codex)在对应机器上可执行且已登录。
  2. 绑定目标 runner,例如 plugin:langbot-team/ACPAgentRunner/defaultplugin:langbot-team/ClaudeCodeAgent/defaultplugin:langbot-team/CodexAgent/default
  3. 配置 runner 必要字段,例如 remote target、workspace、provider、startup timeout、reuse session 等。
  4. 在 Debug Chat 执行一次确定性真实 smoke。
  5. 检查 LangBot MCP gateway、run_id 回填和 host-owned state。

通过条件:

  • WebUI 可见回复包含预期 sentinel。
  • 发送给 harness 的消息包含当前 LangBot run_id 和可访问资源摘要。
  • Harness 通过 gateway 调用 langbot_history_pagelangbot_retrieve_knowledgelangbot_call_tool 时必须携带正确 run_id;错误 run id 被拒绝。
  • external.session_id 写入 host-owned state。
  • 外部 harness 错误、timeout、empty output 都转成受控 run.failed
  • resume 到同一 external session 时,全局锁边界符合 PROTOCOL_V1 §13。

7.2 API 型外部 runner

Dify、n8n、Coze、DashScope、Langflow、Tbox 等外部服务 runner 不作为每轮必跑项。只有在本次改动触及对应 runner 或凭据已经可用时执行 smoke。

通过条件:

  • runner 可选,配置可保存。
  • 请求成功,或外部服务错误被清晰返回。
  • 外部服务凭据缺失时标记 BLOCKED,并记录缺失项。

8. 权限与隔离补充

以下优先用单测 / targeted fixture 覆盖,不要求每次通过 UI 人工构造恶意 runner。

场景 推荐证据
未授权模型调用被拒绝 plugin/handler.py run action 权限测试或目标单测。
未授权工具调用被拒绝 ctx.resources.tools 与 host action 拒绝日志。
未授权知识库检索被拒绝 ctx.resources.knowledge_bases 与 host action 拒绝日志。
run_id 结束后复用被拒绝 session registry 注销测试。
插件身份不匹配被拒绝 caller_plugin_identity mismatch 测试。
绑定插件身份的 run_id 省略 caller identity 被拒绝 _validate_run_authorization(..., caller_plugin_identity=None) 返回错误。
未注册 Runtime 连接伪造插件身份被剥离 SDK runtime forwarding 测试:请求自带 caller_plugin_identity 时,未注册连接转发前必须 pop,已注册连接必须覆盖为真实插件身份。
storage/state scope 越权被拒绝 state/storage proxy 单测。
steering claim 异常不杀 consumer loop controller 单测:无效 runner / registry 异常只让当前消息回到普通 session 槽位路径,消息消费循环继续。
steering queue 未消费有终态 session registry / orchestrator 单测:队列有上限;run unregister 时未 pull 项写 steering.dropped 审计。

如果这些单测失败,不能用 WebUI 正常回复替代。

9. 证据要求

每轮测试报告至少记录:

  • LangBot commit、SDK commit、相关 runner 插件 commit。
  • Pipeline UUID/name、runner id、关键 runner config 摘要。
  • WebUI 截图或 Playwright 操作记录。
  • 后端日志中对应 query id / run id 的关键行。
  • langbot-skills case/report 路径。
  • 外部 harness runner 的 context 文件、session id、working directory、CLI 错误摘要。
  • FAIL/BLOCKED 的复现步骤和归属仓库建议。

报告结论必须回答:

  • 是否建议继续进入下一阶段测试。
  • 是否存在主聊天路径阻塞。
  • 是否只是凭据 / 外部服务 / 本机 CLI 缺失导致 BLOCKED。
  • 是否需要进入 SECURITY_HARDENING.md 的发布级验收。

10. 历史高价值记录

历史高价值记录与当前 runner 验收状态见 STATUS.md。本指南只保留可重复执行的测试步骤和证据要求。

Event debug execution trace

The Agent workbench uses POST /api/v1/agents/{uuid}/debug/stream (NDJSON, runtime.operate). Verify incremental text and provider-returned reasoning, tool call arguments/results in execution order, automatic scrolling, and preservation of partial output on errors. Thinking is only shown when returned by the runner. Disconnecting cancels the debug task. The existing /debug endpoint and MCP debug_agent return final text and a bounded execution_events snapshot; they are not live transports.

Platform tools now use Host-owned mock adapters in synthetic debug runs. The model really invokes the authorized tool, validates its parameters, and receives a result; the Host does not call a live platform adapter. Event tools retain their frozen targets and platform tools retain explicit targets. Native, plugin and MCP tools continue to execute normally. A successful mock platform result is completion of that action in the debug run; runners must communicate that context to the model to avoid repeated attempts at real delivery.

The optional mock payload field (also editable under Mock 场景(JSON) supports:

{
  "errors": {"event_reply": "Simulated permission denied"},
  "results": {"event_get_actor": {"id": "user-42", "nickname": "Fixture User"}},
  "unsupported_apis": ["delete_message"]
}

errors and results cannot both override the same tool. Unknown tools/APIs and malformed options are rejected before model execution. Default read fixtures follow SDK User, UserGroup, UserGroupMember and MessageReceivedEvent structures; list reads return a synthetic fixture list (override with [] to test emptiness). unsupported_apis participates in the normal capability intersection, so unsupported tools are not offered to the model. Event payloads should include the intended group/member/request IDs; the built-in presets supply examples.

Verify a welcome event shows event_reply, the expected text and frozen target, and 模拟执行成功 · Mock. Plain text alone must not count as a successful platform action. A simulated failure must show 模拟执行失败 · Mock. Also verify 停止调试 retains the partial trace and allows the next run, and that entering a new draft while streaming does not erase it on completion.

Full event/Mock regression evidence: 2026-09-05 follow-up.