482 lines
18 KiB
Markdown
482 lines
18 KiB
Markdown
# 报告协作工作台 — 前端对接指南
|
||
|
||
> **适用版本:** RC-BE-000~018 已落地(2026-09-06)
|
||
> **配套文档:**
|
||
> - [前端实施计划](./agentscope-多智能体报告协作工作台-前端实施计划.md)
|
||
> - [后端实施计划](./agentscope-多智能体报告协作工作台-后端实施计划.md)
|
||
> - 模块 README:`frontend-web/src/report-collaboration/README.md`
|
||
|
||
本文说明**前端如何对接真实后端**:环境配置、API 契约、用户流程、状态机、错误处理与联调验收。前端 API 客户端与页面已实现,通常**无需再写一套 HTTP 层**。
|
||
|
||
---
|
||
|
||
## 1. 结论速览
|
||
|
||
| 问题 | 答案 |
|
||
|------|------|
|
||
| 前端还要写接口吗? | **不用**。`src/report-collaboration/api/` 已封装 REST + SSE,页面通过 `getReportCollaborationDriver()` 调用。 |
|
||
| 为什么调不通? | 最常见:`config.yaml` 里 `report_collaboration.enabled: false` → 全部 **503**;或 `worker_enabled: false` → run 不入队执行。 |
|
||
| Mock 和真后端怎么切? | **Mock 已移除**。前端永远请求真实后端(`deerflow.rc-mock` / `VITE_RC_MOCK` 开关已删除);联调需 `enabled: true`。 |
|
||
| 字段会和 MySQL 对不上吗? | 前端只认 JSON 契约(`api/types.ts`),不直连表。缺列表现为后端 500/SQL 错误,部署后需**重启 Gateway** 跑 `create_all` + 列同步。 |
|
||
| 从哪进页面? | `/page/workspace/report-collaboration`(新会话)或 `/page/workspace/report-collaboration/:sessionId`。侧边栏入口默认注释,可直接 URL 访问。 |
|
||
|
||
---
|
||
|
||
## 2. 架构:前端已有什么
|
||
|
||
```
|
||
ReportCollaborationPage
|
||
└─ useReportCollaborationSession(唯一数据入口)
|
||
├─ driver.getSnapshot() ← REST 冷启动
|
||
├─ driver.openRunStream() ← SSE 实时事件
|
||
├─ driver.sendMessage / createCommand / startRun …
|
||
└─ event-reducer ← 事件 → UI 状态
|
||
|
||
ReportConversationPanel
|
||
└─ MessageList(message-adapter 映射 CollaborationMessage → LangGraph Message)
|
||
```
|
||
|
||
### 2.1 目录结构
|
||
|
||
```text
|
||
frontend-web/src/report-collaboration/
|
||
api/
|
||
types.ts # 冻结契约(snake_case,与后端 Pydantic 一致)
|
||
client.ts # rcFetch:鉴权、幂等键、乐观锁、错误映射
|
||
sessions.ts # 会话 CRUD、消息
|
||
plans.ts # 方案请求 / 列表 / 选定
|
||
runs.ts # Run、SSE、命令、节点合同
|
||
reports.ts # 报告版本、改写、恢复
|
||
driver.ts # 真实后端统一门面(Mock 驱动已移除)
|
||
state/
|
||
event-normalizer.ts
|
||
event-reducer.ts
|
||
selectors.ts
|
||
message-adapter.ts
|
||
hooks/
|
||
useReportCollaborationSession.ts # 快照 → SSE → reducer → 动作
|
||
useReportCollaborationStream.ts # 断线重连、after_seq 续接
|
||
useReportCollaborationMessages.ts
|
||
components/ …
|
||
pages/ReportCollaborationPage.tsx
|
||
```
|
||
|
||
### 2.2 事实源原则
|
||
|
||
- **冷启动:** `GET /sessions/{id}` 返回 `SessionSnapshot`(session、messages、plans、run、nodes、agent_runs、commands、report_versions、last_seq)。
|
||
- **运行中:** `GET /runs/{id}/stream?after_seq=N` 订阅 durable SSE。
|
||
- **断线 / 序号缺口:** `GET /runs/{id}/events?after_seq=N` REST 补拉。
|
||
- **澄清 / 规划阶段(尚无 run):** 每 2.5s 轮询快照(后端事件主要在 run 流上)。
|
||
|
||
纯展示状态(预览方案、聚焦成员、面板宽度)**不**写入服务器,仅存前端。
|
||
|
||
---
|
||
|
||
## 3. 后端启用与部署
|
||
|
||
### 3.1 必改配置
|
||
|
||
文件:`offline-backend-20260512/backend/config.yaml`
|
||
|
||
```yaml
|
||
report_collaboration:
|
||
enabled: false # 联调必须改为 true
|
||
worker_enabled: false # 要看成员执行必须 true
|
||
planner_model: null # 建议填可用模型名
|
||
coordinator_model: null
|
||
research_model: null
|
||
writer_model: null
|
||
reviewer_model: null
|
||
# … token_budget、max_concurrent_runs_per_user 等见同文件
|
||
```
|
||
|
||
| 配置 | 现象 |
|
||
|------|------|
|
||
| `enabled: false` | 所有路由 **503**,`code: REPORT_COLLABORATION_DISABLED` |
|
||
| `enabled: true`,`worker_enabled: false` | REST/SSE 可用,run 入队但不自动执行(测 API 可用,无成员流式输出) |
|
||
| 两者 `true` + 模型配置完整 | 完整联调 |
|
||
|
||
### 3.2 MySQL / 表结构
|
||
|
||
生产环境 Alembic 自动升级默认关闭,启动时走 `create_all` + `_ensure_orm_columns_sync`。
|
||
|
||
- 迁移脚本(可选手动跑):`20260905_01`、`20260906_01`、`20260907_01`
|
||
- ORM:`packages/harness/deerflow/persistence/report_collaboration/model.py`(13 张表)
|
||
- **部署后务必重启 Gateway**,观察日志是否有 `Failed to auto-add column report_collaboration_*`
|
||
|
||
已有 RC-003 时代旧表会通过列同步补上 `template_snapshot_json`、`budget_snapshot_json`、`usage_json`、`budget_exhausted_reason` 等可空列,并创建 `rewrite_proposals`、`audits`。
|
||
|
||
### 3.3 前端环境变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `VITE_BACKEND_BASE_URL` | Gateway 地址,默认 `http://127.0.0.1:8001` |
|
||
|
||
请求经 `apiFetch`,自动携带登录 JWT(`credentials: "include"`)。**必须先登录 DeerFlow**。
|
||
|
||
---
|
||
|
||
## 4. API 基址与请求约定
|
||
|
||
### 4.1 Base URL
|
||
|
||
```text
|
||
{VITE_BACKEND_BASE_URL}/api/report-collaboration
|
||
```
|
||
|
||
代码常量:`REPORT_COLLABORATION_BASE()`(`api/client.ts`)。
|
||
|
||
### 4.2 请求头
|
||
|
||
| Header | 何时携带 | 说明 |
|
||
|--------|----------|------|
|
||
| `Authorization` | 全部 | 由 `apiFetch` 注入 |
|
||
| `X-Idempotency-Key` | 写操作 | 格式 `rc-{uuid}`;重复提交返回首次结果 |
|
||
| `X-Expected-Revision` | 选方案、开 run、改会话等 | 会话 `requirement_revision` 或方案 `revision` |
|
||
| `Accept: text/event-stream` | SSE | `openRunStream` |
|
||
|
||
### 4.3 错误响应格式
|
||
|
||
```json
|
||
{
|
||
"code": "REPORT_COLLABORATION_DISABLED",
|
||
"message": "报告协作工作台未启用",
|
||
"detail": { }
|
||
}
|
||
```
|
||
|
||
前端映射(`api/client.ts`):
|
||
|
||
| HTTP | 异常类 | 典型场景 |
|
||
|------|--------|----------|
|
||
| 409 | `ConflictError` | revision 过期、active run 冲突 |
|
||
| 422 | `ValidationError` | 参数/语义错误、缺幂等键 |
|
||
| 429 | `RateLimitError` | 预算耗尽、每用户并发 run 上限 |
|
||
| 5xx | `ServerError` | 可带同一幂等键重试 |
|
||
| 0 | `NetworkError` | fetch 失败 |
|
||
|
||
### 4.4 完整路由清单(与前端 client 对齐)
|
||
|
||
| 方法 | 路径 | 前端模块 |
|
||
|------|------|----------|
|
||
| POST | `/sessions` | `sessions.createSession` |
|
||
| GET | `/sessions` | `sessions.listSessions` |
|
||
| GET | `/sessions/{id}` | `sessions.getSessionSnapshot` |
|
||
| PATCH | `/sessions/{id}` | `sessions.updateSession` |
|
||
| DELETE | `/sessions/{id}` | `sessions.deleteSession` |
|
||
| GET | `/sessions/{id}/messages` | `sessions.listMessages` |
|
||
| POST | `/sessions/{id}/messages` | `sessions.sendMessage` |
|
||
| POST | `/sessions/{id}/plan-requests` | `plans.requestPlans` |
|
||
| GET | `/sessions/{id}/plans` | `plans.listPlans` |
|
||
| POST | `/sessions/{id}/plans/{planId}/select` | `plans.selectPlan` |
|
||
| POST | `/sessions/{id}/runs` | `runs.startRun` |
|
||
| GET | `/sessions/{id}/reports` | `reports.listReportVersions` |
|
||
| POST | `/sessions/{id}/report-rewrites` | `reports.createRewrite` |
|
||
| POST | `/sessions/{id}/report-rewrites/{id}/apply` | `reports.applyRewrite` |
|
||
| POST | `/sessions/{id}/report-versions/{id}/restore` | `reports.restoreReportVersion` |
|
||
| GET | `/runs/{id}` | `runs.getRun` |
|
||
| GET | `/runs/{id}/events` | `runs.listRunEvents` |
|
||
| GET | `/runs/{id}/stream` | `runs.openRunStream` |
|
||
| POST | `/runs/{id}/cancel` | `runs.cancelRun` |
|
||
| POST | `/runs/{id}/commands` | `runs.createCommand` |
|
||
| POST | `/runs/{id}/commands/{id}/confirm` | `runs.confirmCommand` |
|
||
| POST | `/runs/{id}/commands/{id}/cancel` | `runs.cancelCommand` |
|
||
| GET | `/runs/{id}/nodes/{nodeId}/contract` | `runs.getNodeContract` |
|
||
| GET | `/runs/{id}/artifacts` | (后端已暴露,前端按需接) |
|
||
| GET | `/runs/{id}/sources` | (后端已暴露,前端按需接) |
|
||
|
||
---
|
||
|
||
## 5. 用户流程与 API 时序
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as 用户
|
||
participant FE as 前端 RC
|
||
participant API as Gateway
|
||
participant W as Worker
|
||
|
||
U->>FE: 输入需求
|
||
FE->>API: POST /sessions
|
||
FE->>API: POST /sessions/{id}/messages
|
||
API-->>FE: 澄清(快照 messages)
|
||
U->>FE: 回答澄清
|
||
FE->>API: POST /messages
|
||
FE->>API: POST /plan-requests
|
||
API-->>FE: 候选方案(快照 plans)
|
||
U->>FE: 采用方案
|
||
FE->>API: POST /plans/{id}/select
|
||
U->>FE: 开始协作
|
||
FE->>API: POST /runs
|
||
FE->>API: GET /runs/{id}/stream
|
||
W-->>API: 持久化事件
|
||
API-->>FE: SSE
|
||
U->>FE: 运行中干预
|
||
FE->>API: POST /commands → confirm
|
||
```
|
||
|
||
### 5.1 阶段 0:进入页面
|
||
|
||
| 路由 | 行为 |
|
||
|------|------|
|
||
| `/page/workspace/report-collaboration` | 空态;`POST /sessions` 后跳转带 `sessionId` |
|
||
| `/page/workspace/report-collaboration/:sessionId` | 加载快照;有 `location.state.rcInitialMessage` 时自动发首条消息 |
|
||
|
||
路由注册:`frontend-web/src/pages/WorkspaceRoutes.tsx`。
|
||
侧边栏入口已在 `workspace-nav-chat-list.tsx` 中放开(「报告协作」项),也可直接访问 URL。
|
||
|
||
### 5.2 阶段 1:需求澄清(无 run)
|
||
|
||
```http
|
||
POST /api/report-collaboration/sessions
|
||
POST /api/report-collaboration/sessions/{sessionId}/messages
|
||
Content-Type: application/json
|
||
|
||
{ "text": "请写一份…", "target_node_id": null, "agent_run_id": null }
|
||
```
|
||
|
||
- `useReportCollaborationSession.send()`:当 `session.status` 不是 `running` / `awaiting_input` / `reviewing` 时走 `sendMessage`。
|
||
- 会话状态:`empty` → `clarifying` → `planning` → `proposal_ready`。
|
||
- 澄清问题以 `CollaborationMessage` 进入时间线;业务卡通过 `MessageList` 的 `businessCardSlot` 渲染(`additional_kwargs.report_collaboration_card`)。
|
||
|
||
### 5.3 阶段 2:候选方案
|
||
|
||
```http
|
||
POST /api/report-collaboration/sessions/{sessionId}/plan-requests
|
||
POST /api/report-collaboration/sessions/{sessionId}/plans/{planId}/select
|
||
```
|
||
|
||
- **预览方案**(`previewPlanId`):纯前端,不调 API。
|
||
- **采用方案**:`select` 写 `selected_plan_id`,**不**创建 run。
|
||
- 409:方案 revision 过期 → 前端 `refreshSnapshot`,提示用户重选。
|
||
- 右栏 `ReportFlowCanvas` 使用 `PlanNode` / `PlanEdge` 渲染 DAG。
|
||
|
||
### 5.4 阶段 3:开始执行与 SSE
|
||
|
||
```http
|
||
POST /api/report-collaboration/sessions/{sessionId}/runs
|
||
{ "plan_id": "plan-xxx" }
|
||
|
||
GET /api/report-collaboration/runs/{runId}/stream?after_seq=0
|
||
Accept: text/event-stream
|
||
```
|
||
|
||
SSE 每条 `data:` 为 JSON:
|
||
|
||
```typescript
|
||
interface CollaborationEvent {
|
||
eventId: string;
|
||
sessionId: string;
|
||
runId?: string | null;
|
||
seq: number;
|
||
type: CollaborationEventType;
|
||
timestamp: string;
|
||
data: unknown;
|
||
}
|
||
```
|
||
|
||
常用 `type`(完整列表见 `api/types.ts`):
|
||
|
||
| 类型 | 含义 |
|
||
|------|------|
|
||
| `message.created` / `message.delta` / `message.completed` | 对话镜像流式 |
|
||
| `tool_call.*` / `tool_result.*` | 工具调用与返回 |
|
||
| `team_message.*` | 成员 / 协调者广播 |
|
||
| `plan.proposed` / `plan.selected` | 方案生命周期 |
|
||
| `run.status.changed` | Run 状态 |
|
||
| `node.status.changed` / `node.progress` | 节点执行 |
|
||
| `command.confirmation.required` | 需用户确认的干预 |
|
||
| `report.version.created` | 报告版本落盘 |
|
||
| `heartbeat` | 保活 |
|
||
|
||
### 5.5 阶段 4:运行中干预
|
||
|
||
```http
|
||
POST /api/report-collaboration/runs/{runId}/commands
|
||
POST /api/report-collaboration/runs/{runId}/commands/{commandId}/confirm
|
||
POST /api/report-collaboration/runs/{runId}/commands/{commandId}/cancel
|
||
POST /api/report-collaboration/runs/{runId}/cancel
|
||
```
|
||
|
||
- Run 活跃时 `send()` 自动走 `createCommand`(而非 `sendMessage`)。
|
||
- 可从节点抽屉 / `AgentStageSelector` 传入 `target_node_id`、`agent_run_id`。
|
||
- `InterventionCard` 展示 `confirmation_required` 命令;确认后调 `confirmCommand`。
|
||
|
||
`CommandIntent` 枚举(后端分类,前端展示 reason/impact):`answer_question`、`update_requirements`、`research_again`、`rewrite_section`、`rerun_node`、`replan`、`cancel` 等。
|
||
|
||
### 5.6 阶段 5:报告版本与改写
|
||
|
||
```http
|
||
GET /api/report-collaboration/sessions/{sessionId}/reports
|
||
POST /api/report-collaboration/sessions/{sessionId}/report-rewrites
|
||
POST /api/report-collaboration/sessions/{sessionId}/report-rewrites/{rewriteId}/apply
|
||
POST /api/report-collaboration/sessions/{sessionId}/report-versions/{versionId}/restore
|
||
```
|
||
|
||
- `createRewrite` 只生成候选,不直接覆盖正式版本。
|
||
- `applyRewrite` / `restoreReportVersion` 产生新的 head 版本,历史保留。
|
||
- `ReportPreviewDialog` 展示 `ReportVersion.markdown`。
|
||
|
||
---
|
||
|
||
## 6. 前端状态机(接入方须知)
|
||
|
||
**不要**在页面里用零散 REST 字段拼 UI 状态。统一路径:
|
||
|
||
```text
|
||
SessionSnapshot → applySessionSnapshot()
|
||
CollaborationEvent[] → normalizeCollaborationEvent() → applyCollaborationEvents()
|
||
→ selectors(derivePagePhase、selectPlanForCanvas、…)
|
||
```
|
||
|
||
### 6.1 核心 Selector
|
||
|
||
| Selector | 用途 |
|
||
|----------|------|
|
||
| `derivePagePhase` | Composer 占位、方案卡、空态 |
|
||
| `selectPlanForCanvas` | 画布 DAG(含预览方案) |
|
||
| `selectAgentTabs` | 并行成员 Tab |
|
||
| `selectNodeStatusMap` | 节点颜色 / 状态 |
|
||
| `selectPendingConfirmationCommands` | 干预确认卡 |
|
||
| `selectHeadReportVersion` | 报告预览入口 |
|
||
| `selectStreamingAgentRunIds` | 流式成员指示 |
|
||
|
||
### 6.2 SSE 连接策略(`useReportCollaborationStream`)
|
||
|
||
- Run 非终态时连接;`completed` / `failed` / `cancelled` 后关流并 REST 最终补拉。
|
||
- 断线:指数退避重连(1s~30s),`after_seq` 取 reducer 内 `lastSeq`。
|
||
- `hasSeqGap`:暂停 live 应用,先 `listRunEvents` 回填。
|
||
|
||
### 6.3 乐观消息
|
||
|
||
用户发送时插入 `metadata.optimistic: true` 的 pending 消息;服务端镜像带相同 `idempotency_key` 时 reducer 原地替换。
|
||
|
||
---
|
||
|
||
## 7. 消息与 MessageList 复用
|
||
|
||
`useReportCollaborationMessages` 将 `CollaborationMessage[]` 转为 `@langchain/langgraph-sdk` 的 `Message[]`,并构造合成 `BaseStream` 供 `MessageList` 消费。
|
||
|
||
| 来源 | 渲染 |
|
||
|------|------|
|
||
| `human` / `ai` 文本 | 普通气泡 |
|
||
| `tool_calls` + `tool` | 现有工具卡 |
|
||
| `team_message` 事件 | 带 `name` 的成员发言 |
|
||
| 业务卡 | `businessCardSlot`(方案组、命令确认、报告可用) |
|
||
|
||
分组键:`core/messages/utils.ts` 中 `assistant:task-report-import` 同级扩展 `assistant:business-card`,判据 `additional_kwargs.report_collaboration_card`。
|
||
|
||
### 7.1 在其他页面嵌入 RC(最小集)
|
||
|
||
1. `useReportCollaborationSession(sessionId)`
|
||
2. `ThreadContext.Provider` + `MessageList` + `businessCardSlot`
|
||
3. 引入 `report-collaboration/styles/report-collaboration.css`
|
||
|
||
---
|
||
|
||
## 8. 单测
|
||
|
||
Mock 驱动(原 `api/mock.ts`)与 `deerflow.rc-mock` / `VITE_RC_MOCK` 开关已移除,前端只请求真实后端;本地演示请直接联调后端。
|
||
|
||
单测:
|
||
|
||
```bash
|
||
cd frontend-web
|
||
pnpm test:report-collaboration
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 联调验收清单
|
||
|
||
### 9.1 准备
|
||
|
||
- [ ] `report_collaboration.enabled: true`
|
||
- [ ] `report_collaboration.worker_enabled: true`
|
||
- [ ] 五个 `*_model` 已配置且可用
|
||
- [ ] Gateway 已重启(MySQL 表/列就绪)
|
||
- [ ] 前端已登录(JWT 有效)
|
||
|
||
### 9.2 探活
|
||
|
||
```bash
|
||
curl -s -H "Authorization: Bearer <token>" \
|
||
http://127.0.0.1:8001/api/report-collaboration/sessions
|
||
```
|
||
|
||
期望:**200** + `{ "sessions": [...] }`,而非 503。
|
||
|
||
### 9.3 页面流程(浏览器 Network)
|
||
|
||
1. 打开 `/#/page/workspace/report-collaboration`
|
||
2. 输入需求 → `POST .../sessions` **200**
|
||
3. 跳转 → `GET .../sessions/{id}` **200**,响应含 `SessionSnapshot`
|
||
4. 首条消息 → `POST .../messages` **200**
|
||
5. 请求方案 → `POST .../plan-requests` **200**
|
||
6. 采用方案 → `POST .../plans/{id}/select` **200**
|
||
7. 开始协作 → `POST .../runs` **200**
|
||
8. `GET .../runs/{id}/stream` 持续收到 SSE(`message.delta`、`node.status.changed` 等)
|
||
9. 刷新页面 → 消息与 `last_seq` 续接正确
|
||
10. 运行中输入「重新检索」→ `POST .../commands` → 确认卡 → `confirm` **200**
|
||
|
||
### 9.4 常见失败
|
||
|
||
| 现象 | 原因 | 处理 |
|
||
|------|------|------|
|
||
| 全部 503 | `enabled: false` | 改配置并重启 |
|
||
| run 一直 queued | `worker_enabled: false` | 改为 true |
|
||
| 409 选方案 | revision 过期 | 刷新后重选(前端已自动 refresh) |
|
||
| 429 | 预算/并发上限 | 调大配置或取消其他 run |
|
||
| 500 + SQL | 表缺列 | 重启 Gateway,查列同步日志 |
|
||
|
||
---
|
||
|
||
## 10. 契约与类型维护
|
||
|
||
- **单一事实源:** `frontend-web/src/report-collaboration/api/types.ts`
|
||
- **后端镜像:** `offline-backend-20260512/backend/app/report_collaboration/contracts/`
|
||
- 修改字段须同步两份实施计划与本指南,并跑前后端相关测试。
|
||
|
||
`SessionSnapshot` 形状(摘要):
|
||
|
||
```typescript
|
||
interface SessionSnapshot {
|
||
session: ReportSession;
|
||
messages: CollaborationMessage[];
|
||
plans: ReportPlanCandidate[];
|
||
run: ReportRun | null;
|
||
nodes: CollaborationNodeState[];
|
||
agent_runs: AgentRun[];
|
||
commands: CollaborationCommand[];
|
||
report_versions: ReportVersion[];
|
||
last_seq: number;
|
||
}
|
||
```
|
||
|
||
`ReportRun` 可选字段(RC-BE-016):`usage`、`budget_exhausted_reason`。
|
||
|
||
---
|
||
|
||
## 11. 后续工作(非对接阻塞)
|
||
|
||
| 项 | 说明 |
|
||
|----|------|
|
||
| RC-BE-019 | 灰度发布与离线部署文档 |
|
||
| 侧边栏入口 | 已放开(`workspace-nav-chat-list.tsx` 的「报告协作」项) |
|
||
| iframe 嵌入 | RC 页尚未专门适配 `embed=1`,可参考现有深链模式扩展 |
|
||
| 真人盲评 | RC-BE-018 为确定性评测集,上线前仍需计划 §11.4 人工评审 |
|
||
|
||
---
|
||
|
||
## 12. 相关代码索引
|
||
|
||
| 用途 | 路径 |
|
||
|------|------|
|
||
| API 客户端 | `frontend-web/src/report-collaboration/api/` |
|
||
| 会话 Hook | `frontend-web/src/report-collaboration/hooks/useReportCollaborationSession.ts` |
|
||
| 页面 | `frontend-web/src/report-collaboration/pages/ReportCollaborationPage.tsx` |
|
||
| 路由 | `frontend-web/src/pages/WorkspaceRoutes.tsx` |
|
||
| 后端路由 | `offline-backend-20260512/backend/app/gateway/routers/report_collaboration.py` |
|
||
| 配置 | `offline-backend-20260512/backend/config.yaml` → `report_collaboration` |
|
||
| 持久化 | `offline-backend-20260512/backend/packages/harness/deerflow/persistence/report_collaboration/` |
|