# Workspace Evidence Backend Orchestration 设计与验收

**Date:** 2026-04-27  
**Status:** Implemented in PR scope; Case 2 E2E PASS

---

## 一图说明

```mermaid
flowchart TD
    U[User asks a direct-query question] --> A[Agent decides what data to query]
    A --> Q[Structured query tool / CLI]
    Q --> R{Tool output has evidence meta?}

    R -- no --> C[Agent can still answer from returned data]
    R -- yes --> ACP[ACP runtime extracts evidence envelope]
    ACP --> RAW[Append raw evidence into workspace-evidence]
    RAW --> META[Update metadata.last_raw_evidence]
    META --> S[Schedule backend post-pass by workspace_id + message_id]
    S --> P[Backend post-pass builds authored analysis patch]
    P --> REPORT[Merge into workspace_evidence_report.json]
    REPORT --> UI[UI renders current Workspace Evidence]

    C --> ANS[Agent answers user]
    REPORT --> ANS
```

核心变化是：agent 不再负责“查完数据后再稳定触发 `evidence-report`”。只要查询工具返回了
evidence meta，runtime/backend 就会完成 raw evidence append 和 report post-pass。

```mermaid
flowchart LR
    subgraph Agent
        A1[Choose query]
        A2[Read structured result]
        A3[Diagnose and answer]
    end

    subgraph Tool_or_CLI[Tool / CLI]
        T1[Query data]
        T2[Return structured output]
        T3[Attach evidence meta when applicable]
    end

    subgraph Backend_Runtime[Backend / Runtime]
        B1[Extract evidence meta]
        B2[Write raw evidence]
        B3[Update last_raw_evidence]
        B4[Run post-pass]
        B5[Merge authored report]
    end

    A1 --> T1 --> T2 --> A2 --> A3
    T3 --> B1 --> B2 --> B3 --> B4 --> B5
```

---

## 核心设计目标

本 PR 的核心目标不是替换 Clinic skill，而是让 direct-query 查到的数据稳定进入
workspace evidence，并让 evidence report 的生成从 agent 行为变成 backend 行为。

此前问题是：agent 能查到 metrics / SQL / logs，也能在回答里完成诊断，但不能稳定触发
`evidence-report` 去更新 report。最终设计改为：

1. **查询结果默认可进入 evidence**
   - metrics / SQL / logs / events / profiles / files 等查询结果带有稳定的 evidence meta。
   - runtime 根据 meta 写入 workspace raw evidence。
   - agent 不需要记住 append API，也不需要手动维护 evidence 文件。

2. **report 更新由 backend 处理**
   - raw evidence append 后记录 `metadata.last_raw_evidence`。
   - backend 根据 `last_raw_evidence` 触发 post-pass。
   - post-pass 生成 authored analysis patch 并更新 report。
   - agent 不再负责稳定触发 `evidence-report`。

3. **agent 只负责拿数据和诊断**
   - agent 使用可用的数据查询入口获取结构化数据。
   - agent 基于数据回答用户问题。
   - evidence append、analysis merge、post-pass 都是 backend/runtime 责任。

4. **`clinic-cli` 是实现手段之一**
   - 本 PR 同时把旧 `clinic-api-cli` 收敛为 `clinic-cli`，让 Clinic 查询输出稳定结构和 evidence meta。
   - 新 workspace 默认启用 `clinic-cli`，旧 `clinic-api` 不再作为默认 enabled skill / source / readiness path。
   - CLI 能力覆盖 cluster、metrics、SQL、logs、events、profiling、diagnostic files。

5. **direct-query evidence 不伪装成 diagnosis run**
   - 普通问答 / direct-query 维护 workspace 级 evidence state。
   - 正式 diagnosis run 仍保留独立产物目录和完整 `collect_evidence.py` 流程。
   - UI 面向用户展示当前 workspace evidence 状态，而不是中间过程。

6. **网络断开不等于 cancel**
   - SSE client disconnect 不取消后端 prompt。
   - 只有显式 stop/cancel API 才取消正在运行的 prompt。

---

## 总体架构

```text
Agent
  |
  | query data through structured tools
  v
Query Tool / CLI
  |
  | structured output + evidence meta
  v
ACP Runtime
  |
  | append raw evidence
  v
Workspace Evidence Store
  |
  | update last_raw_evidence; schedule by workspace_id + message_id
  v
Backend Post-Pass
  |
  | merge authored analysis
  v
workspace_evidence_report.json
```

核心分工：

- query tool / CLI：返回稳定结构化数据和 evidence meta。
- ACP runtime：从 tool result 中提取协议化 evidence。
- workspace evidence store：维护 raw evidence、metadata、report 文件。
- backend post-pass：把 raw evidence 转成 authored evidence。
- agent：读取数据并给出诊断结论。

---

## Clinic CLI 能力

`clinic-cli` 是本 PR 为 Clinic direct-query 落地 evidence meta 的实现载体，不是产品目标本身。

入口：

```bash
python3 .opencode/skills/builtin/clinic-cli/scripts/clinic_cli.py <subcommand> ...
```

已实现 subcommands：

- `cluster-metadata`
- `metrics`
- `sql`
- `logs`
- `activity-events`
- `continuous-profiling`
- `continuous-profiling-download`
- `diagnostic-files`
- `diagnostic-file-download`

能力对齐原则：

- 常用 `clinic-api` 诊断能力都应在 `clinic-cli` 中有对应入口。
- shell wrapper 不再保留，代码集中在 `clinic_cli.py`。
- 下载类能力由 CLI 执行下载，并在 output 中返回下载路径。
- cluster metadata 是结构化上下文，不写入 evidence。
- 新增数据类型如果不属于既有 evidence kind，不强行塞进旧 kind；只有进入协议白名单的 kind 才写 evidence。

---

## Evidence Kind 与文件映射

workspace evidence 目录：

```text
<workspace>/workspace-evidence/
  metrics.json
  queries.json
  logs.json
  events.json
  profiles.json
  files.json
  links.json
  gate_checks.json
  downloads/
  analysis_patch.json
  workspace_evidence_report.json
```

当前 kind 映射：

| kind | 文件 | 说明 |
| --- | --- | --- |
| `metric` | `metrics.json` | PromQL / metrics query 结果 |
| `query` | `queries.json` | SQL / slow query / TopSQL 结果 |
| `log` | `logs.json` | Loki / log summary 结果 |
| `event` | `events.json` | Activity Hub / cluster events |
| `profile` | `profiles.json` | continuous profiling metadata / downloads |
| `file` | `files.json` | diagnostic files metadata / downloads |
| `link` | `links.json` | Clinic / dashboard / deep link |
| `gate_check` | `gate_checks.json` | gate / validation result |

`cluster_meta` 不属于 workspace evidence kind。它可以作为 CLI output 的结构化上下文返回，但 backend 不把它写入 evidence。

---

## Workspace Evidence 语义

普通问答和 direct-query 场景维护一份当前 workspace evidence state：

- raw evidence 默认宽进：成功且结构化的 Clinic 查询结果可以进入 raw evidence。
- authored evidence 严格精选：只有能支撑当前诊断结论的数据进入 `key_evidence` 或 `supporting_evidence`。
- 多轮追问继续 append / merge 到同一个 workspace state。
- frontend 不从 chat text 或 tool output 推断 evidence，只消费 backend 维护的 workspace evidence。

正式 diagnosis run 保留独立语义：

```text
clinic-downloads/<date>/<run-id>/...
```

只用于完整诊断流程，不作为 direct-query evidence 的默认落点。

---

## Backend Post-Pass

post-pass 解决的问题是：agent 可能已经查询到数据并给出诊断，但不应要求 agent 自己补写 evidence report。

当前行为：

- raw evidence append 后触发。
- 以 `workspace_id + message_id` 去重。
- debounce 后执行，避免同一 turn 多次消耗。
- 不复用主 ACP session，不污染主 agent context。
- 不依赖 chat complete；raw evidence append 后即可调度。
- 优先 direct OpenAI；未配置 `INVESTIBOOK_OPENAI_API_KEY` 时 fallback 到 deterministic metric patch。

写入结果：

- `workspace-evidence/analysis_patch.json`
- `workspace-evidence/workspace_evidence_report.json`
- `metadata.last_analysis_update`

---

## Cancel 与并发语义

当前 cancel 语义：

- SSE client disconnect / browser refresh / test client timeout 不触发 `cancel_prompt()`。
- 显式 stop/cancel API 触发 `cancel_prompt()`。
- post-pass 与主 ACP prompt 解耦，不阻塞主 prompt，也不污染主 context。

raw evidence append 的 post-pass 调度使用 `workspace_id + message_id` 做去重，避免同一轮多次 tool result 导致重复 post-pass。

---

## Skill 文档原则

`clinic-cli` skill 文档只回答 agent 需要知道的内容：

- 有哪些能力。
- 如何通过 `--help` 看参数。
- 何时用哪个子命令。
- output 是结构化 JSON，下载路径通过 output 返回。

skill 文档不写：

- backend evidence 内部落盘规则。
- 哪些命令默认写 workspace evidence。
- 不要使用某些底层工具的实现约束。
- post-pass / analysis merge 细节。

这些是 backend/runtime contract，不是 agent 的诊断知识。

---

## 本地 E2E 验收

当前 Case 2 验证的是 `clinic-cli` direct-query 轻路径：

1. 创建 workspace。
2. prepare `O11Y-2447801` fixture。
3. agent 使用 `clinic-cli` 查询 QPS。
4. ACP auto-append raw metrics evidence。
5. backend post-pass 自动生成 authored analysis。
6. UI 可读取 `workspace-evidence` 产物。

命令：

```bash
RUN_CASE1=0 RUN_CASE2=1 BACKEND_PORT=8001 CHAT_TIMEOUT_S=300 bash tests/ft/test_workspace_evidence_local.sh
```

最新结果：

```text
workspace_id: ws-1777355708247-6f1bd20e0
result: analysis_ready
```

关键产物：

```text
workspace-evidence/metrics.json
workspace-evidence/analysis_patch.json
workspace-evidence/workspace_evidence_report.json
workspace-evidence/events.json
workspace-evidence/files.json
workspace-evidence/gate_checks.json
workspace-evidence/links.json
workspace-evidence/logs.json
workspace-evidence/profiles.json
workspace-evidence/queries.json
```

关键 metadata：

```json
{
  "last_raw_evidence": {
    "message_id": "assistant-1777355761584",
    "source": "clinic-cli:metrics",
    "kind": "metric"
  },
  "last_analysis_update": {
    "message_id": "assistant-1777355761584",
    "source": "backend-postpass",
    "kind": "analysis"
  }
}
```

关键 post-pass 日志：

```text
workspace_evidence_postpass start workspace_id=ws-1777355708247-6f1bd20e0 message_id=assistant-1777355761584
workspace_evidence_postpass complete workspace_id=ws-1777355708247-6f1bd20e0 message_id=assistant-1777355761584 analysis_fields=['add_key_evidence']
```

验证结论：

- agent 成功调用 `clinic-cli`。
- metrics raw evidence 成功 append。
- backend post-pass 在主 agent 仍运行时已完成 analysis merge。
- post-pass 未污染或阻塞主 ACP context。

---

## 代码级验证

已通过：

```bash
cd backend && uv run pytest -q \
  tests/test_chat_workspace_evidence_postpass.py \
  tests/test_acp_workspace_evidence.py \
  tests/test_workspace_evidence_store.py \
  ../agentkit/skills/builtin/clinic-cli/tests/test_clinic_cli.py
```

结果：

```text
26 passed
```

也通过：

```bash
python3 -m py_compile \
  backend/investibook/api/chat.py \
  backend/investibook/core/acp.py \
  backend/investibook/core/workspace_evidence_postpass.py \
  backend/investibook/core/workspace_evidence_store.py \
  agentkit/skills/builtin/clinic-cli/scripts/clinic_cli.py
```

---

## 前端查看

本地 E2E workspace 可用以下方式查看：

1. 用 E2E 数据目录启动 backend：

```bash
cd backend
DATA_DIR=/tmp/investibook-local-data uv run uvicorn investibook.main:app --reload --host 0.0.0.0 --port 8000
```

2. 启动前端：

```bash
make web
```

3. 登录：

```text
dev@localhost
dev12345
```

4. 打开：

```text
http://localhost:3000/workspace/ws-1777355708247-6f1bd20e0
```

---

## 剩余关注点

1. 继续确认旧 `clinic-api` 中真实使用过的边缘能力是否都已覆盖到 `clinic-cli`。
2. 确认 UI 对 `event` / `profile` / `file` 的展示是否需要更细 renderer。
3. 如果需要更高质量 authored analysis，应配置 `INVESTIBOOK_OPENAI_API_KEY`，让 post-pass 走 direct LLM；未配置时 fallback 只生成最小 metric evidence。
