827 lines
39 KiB
Markdown
827 lines
39 KiB
Markdown
# 6 步法推演问答页面 — 开发说明文档
|
||
|
||
> 所在目录:`src/components/angentchat/`(主页面 `SixStepChatPage.tsx`)
|
||
> 路由地址:`/qa-center/sixstep-chat`
|
||
> 菜单入口:问答中心 → **6bf**(侧栏文案见 `src/components/Sidebar.tsx`)
|
||
> 同模块相关路由:`/qa-center/sixstep-agents`、`/qa-center/sixstep-agent-edit`
|
||
> **最后更新(摘要)**:2026-05-12 — 流式 **`error_log`** / **`errors[]`** 与 `StreamErrorNotice`(后端 skill 失败摘要可含异常片段);`buildLayout` 根节点 **`rootTitle` / `rootDetail`**(已选专家);**`SixStepFlowFullscreen`** 父节点 **收起/展开子树**(过渡动画、缩略图随可见节点);**`/observability`** 独立全站路由。
|
||
|
||
---
|
||
|
||
## 一、功能概述
|
||
|
||
「6bf 推演问答」由 `SixStepChatPage` 对接后端 **SixStep MCP Backend**。主链路通过 **`POST /api/orchestrate/stream`** 统一流式返回:后端先做 **意图识别**(`intent` 事件),再分支为 **查找专家**(`expert_*`)、**6 步法编排**(信息准备 → 目的 → 行为体 → 预期行为与驱动因素 → 叙事 → 最终结果)、**追问**(`follow_up`,`delta` 流式 Markdown)或 **澄清**(`clarify`)。支持 **结构化卡片** 与 **Markdown** 输出切换;结构化模式下可打开 **流程图沙箱 / 全屏流程图**(`SixStepFlowSandbox` / `SixStepFlowFullscreen`),展示编排返回的实体关系。
|
||
|
||
单 MCP 调试仍可直接调用 **`POST /api/agents/{key}/invoke`**(例如 `find_expert`、`create_expert`);6bf 主页面以编排流为主,专家查找也可由流内 `intent` 分支触发。对接细节见同目录 **`INTENT_INTEGRATION.md`**。
|
||
|
||
页面布局为 **顶栏 + 下方主体**:顶栏含页面标题、`SixStepProgressHorizontal`(横向主/子步骤进度)、以及结构化·Markdown、聊天记录、流程图沙箱等控制;主体为 **可收起的会话列表轨**(`SixStepChatHistory`)、**中间对话区与输入**(`SixStepConversation` + `ChatInputComposer`,含专家匹配与 MCP 选择弹窗)、以及 **可选的右侧沙箱**(宽度可拖拽,可关闭)。全屏流程图仍以 `fixed` 遮罩覆盖视口,独立于上述布局。
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ 顶栏:标题 · SixStepProgressHorizontal · 输出格式 · 聊天记录 · 流程图 │
|
||
├───┬────────────────────────────────────────────────────┬─────────┤
|
||
│会话│ SixStepConversation(多轮结构化阶段) │ 流程图 │
|
||
│列表│ + ChatInputComposer │ 沙箱 │
|
||
│轨 │ │(可关闭) │
|
||
└───┴────────────────────────────────────────────────────┴─────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 二、目录结构与各文件职责
|
||
|
||
```
|
||
src/components/angentchat/
|
||
│
|
||
├── types.ts # 数据模型类型(含 phase / intent / rawSteps 等)
|
||
├── mockData.ts # 本地模拟流式控制器(与 api 侧事件对齐,便于离线演示)
|
||
├── api.ts # runIntentOrchestrateStream、runSixStepDeduction、runExpertSearch/Create
|
||
├── flowLayout.ts # SVG 流程图布局
|
||
├── styles.css # 动画与工具类
|
||
│
|
||
├── SixStepChatPage.tsx # 推演主页面(状态中枢)
|
||
├── SixStepProgressHorizontal.tsx# 顶栏横向主/子步骤进度(主页面使用)
|
||
├── SixStepProgress.tsx # 纵向进度面板(组件保留;布局以横向为准)
|
||
├── SixStepChatHistory.tsx # 会话列表轨(展开/收起)
|
||
├── SixStepConversation.tsx # 对话区(阶段卡片 + 专家匹配)
|
||
├── SixStepCards.tsx # 阶段卡片原子组件
|
||
├── ExpertMatchCards.tsx # 专家匹配卡片
|
||
├── McpSelectModal.tsx # MCP 多选确认弹窗
|
||
├── StreamErrorNotice.tsx # 助手消息底部:汇总 SSE error_log / 网络类错误的可折叠提示条
|
||
├── SixStepFlowSandbox.tsx # 右侧流程图沙箱
|
||
├── SixStepFlowFullscreen.tsx # 全屏流程图
|
||
├── AgentCardList.tsx # 路由 sixstep-agents:MCP 卡片列表
|
||
├── AgentEditPage.tsx # 路由 sixstep-agent-edit:单 MCP 调试编辑
|
||
├── AgentChatMessageList.tsx # 智能体对话消息列表(编辑页等)
|
||
├── INTENT_INTEGRATION.md # 意图流与前后端字段说明(对接备忘)
|
||
└── doc.md # 本文档
|
||
```
|
||
|
||
---
|
||
|
||
## 三、数据模型(`types.ts`)
|
||
|
||
所有类型均从此文件导出,是各组件之间传递数据的唯一契约。
|
||
|
||
### 3.1 基础枚举
|
||
|
||
| 类型 | 取值 | 说明 |
|
||
|------|------|------|
|
||
| `MainStepKey` | `'info' \| 'sixstep' \| 'final'` | 三个主步骤的键名 |
|
||
| `SubStepKey` | `'purpose' \| 'actor' \| 'behavior' \| 'narrative'` | 6步法的四个子阶段键名 |
|
||
| `StepStatus` | `'pending' \| 'running' \| 'done'` | 步骤状态,驱动进度面板和动画 |
|
||
| `SixStepPhase` | `'idle'` \| `'intent-detecting'` \| `'expert-match'` \| `'expert-confirm'` \| `'generate'` \| `'paused'` \| `'completed'` \| `'clarifying'` \| `'follow-up'` | 助手消息所处交互阶段(与编排流、停止、追问等配合) |
|
||
| `SelectableMcpKey` | `'purpose' \| 'actor' \| 'behavior_factor' \| 'narrative'` | MCP 弹窗可选子链,会随请求传给后端 `selected_mcps` |
|
||
|
||
`BackendStepResult`:与后端 `step_end` 原始结构对齐,用于 **`continue_analysis`** 时把 `last_steps` 回传编排接口;`SixStepMessage.rawSteps` 在流式过程中累积,`resumedFrom` 对应 **`resume`** 事件中的继续起点。
|
||
|
||
### 3.2 卡片数据结构
|
||
|
||
每类卡片都有独立接口,并通过外键(`purposeId`、`actorId`、`driverId`)建立父子关联,构成树形层级关系。
|
||
|
||
```
|
||
PurposeCard (目的)
|
||
└─ ActorCard.purposeId → PurposeCard.id
|
||
└─ BehaviorDriverCard.actorId → ActorCard.id
|
||
└─ NarrativeCard.driverId → BehaviorDriverCard.id
|
||
```
|
||
|
||
| 接口 | 字段 | 说明 |
|
||
|------|------|------|
|
||
| `PurposeCard` | `id, index, title, reason` | 目的卡片,包含标题和推演理由 |
|
||
| `ActorCard` | `id, index, purposeId, title, content` | 行为体卡片,`purposeId` 关联所属目的 |
|
||
| `BehaviorDriverCard` | `id, index, actorId, expectedBehavior, driver` | 预期行为 + 驱动因素二合一卡片,`actorId` 关联所属行为体 |
|
||
| `NarrativeCard` | `id, index, driverId, title, reason` | 叙事卡片,`driverId` 关联所属驱动因素(`BehaviorDriverCard.id`) |
|
||
|
||
### 3.3 阶段数据结构
|
||
|
||
每个子阶段有独立的 `*StageData` 接口:
|
||
|
||
```typescript
|
||
interface PurposeStageData {
|
||
thinking: string; // 模型思考过程文本(流式填充)
|
||
cards: PurposeCard[]; // 已生成的卡片列表(逐张追加)
|
||
complete: boolean; // 该阶段是否完全结束
|
||
}
|
||
// ActorStageData / BehaviorStageData / NarrativeStageData 结构类似
|
||
```
|
||
|
||
### 3.4 消息总线:`SixStepMessage`
|
||
|
||
这是整个页面最核心的数据结构,一条助手消息包含所有推演状态:
|
||
|
||
```typescript
|
||
interface SixStepMessage {
|
||
id: string;
|
||
role: 'user' | 'assistant';
|
||
content?: string;
|
||
timestamp: Date;
|
||
outputFormat?: 'structured' | 'markdown';
|
||
markdownContent?: string;
|
||
stages?: SixStepStages;
|
||
finalAnswer?: string;
|
||
mainStepStatus?: Record<MainStepKey, StepStatus>;
|
||
subStepStatus?: Record<SubStepKey, StepStatus>;
|
||
currentSubStep?: SubStepKey;
|
||
currentMainStep?: MainStepKey;
|
||
phase?: SixStepPhase;
|
||
expertMatch?: ExpertMatchData;
|
||
selectedExpert?: ExpertCard;
|
||
selectedMcps?: SelectableMcpKey[];
|
||
intent?: { name: string; confidence?: number; reason?: string };
|
||
clarifyQuestion?: string;
|
||
rawSteps?: BackendStepResult[];
|
||
resumedFrom?: string;
|
||
effectiveUserInput?: string;
|
||
errors?: StreamErrorItem[];
|
||
}
|
||
```
|
||
|
||
**`errors` / `StreamErrorItem`**:`runIntentOrchestrateStream` 收到 SSE **`error_log`** 时追加;致命失败仍会走 **`error`** 事件并映射为同类结构。`SixStepConversation` 中 **`StreamErrorNotice`** 读取 `message.errors` 展示摘要(可折叠列表)。
|
||
|
||
**关键设计**:`mainStepStatus` / `subStepStatus` 驱动顶栏 `SixStepProgressHorizontal`;`stages` 驱动中间对话渲染;`currentSubStep` 驱动流程图沙箱蚂蚁线。`phase` / `intent` / `rawSteps` 与编排流、暂停继续、追问分支一致。所有信息集中在一个对象内,组件通过 props 按需取用。
|
||
|
||
---
|
||
|
||
## 四、模拟数据生成器(`mockData.ts`)
|
||
|
||
> **线上主路径**:`SixStepChatPage` 使用 **`runIntentOrchestrateStream`** 消费真实 `/api/orchestrate/stream`。`mockData.ts` 中的 **`runSixStepDeduction`** 仍保留,用于本地演示或与 `api.ts` 中纯编排解析逻辑对齐;新功能(意图、专家 SSE、`resume` 等)以 `runIntentOrchestrateStream` 为准。
|
||
|
||
### 4.1 种子数据
|
||
|
||
文件顶部定义了四组静态种子数组:
|
||
|
||
| 变量 | 长度 | 用途 |
|
||
|------|------|------|
|
||
| `PURPOSE_SEED` | 3 | 目的卡片内容模板 |
|
||
| `ACTOR_SEED` | 7 | 行为体卡片内容模板 |
|
||
| `BEHAVIOR_SEED` | 8 | 预期行为与驱动因素卡片内容模板 |
|
||
| `NARRATIVE_SEED` | 8 | 叙事卡片内容模板 |
|
||
|
||
使用时循环取模(`(counter - 1) % SEED.length`),确保不越界。
|
||
|
||
### 4.2 卡片生成规则
|
||
|
||
| 阶段 | 数量规则 |
|
||
|------|---------|
|
||
| 目的 | 固定 3 张(取自 `PURPOSE_SEED` 全量) |
|
||
| 行为体 | 每个目的对应的数量:第 0 个目的 2 张,其余 1 张,共 4 张 |
|
||
| 预期行为/驱动 | 每个行为体:偶数索引 2 张,奇数索引 1 张,共 6 张 |
|
||
| 叙事 | 每个预期行为:偶数索引 2 张,奇数索引 1 张,共 9 张 |
|
||
|
||
最终流程图节点总数:`1(智能体)+ 3 + 4 + 6×2(预期行为+驱动因素)+ 9 = 29 节点`。
|
||
|
||
### 4.3 流式控制器:`runSixStepDeduction`
|
||
|
||
```typescript
|
||
export function runSixStepDeduction(
|
||
message: SixStepMessage, // 直接在此对象上原地修改字段
|
||
options: {
|
||
onUpdate: (msg: SixStepMessage) => void; // 每次有新内容时回调
|
||
onComplete?: () => void; // 全部完成时回调
|
||
}
|
||
): SixStepMockController
|
||
```
|
||
|
||
返回的 `SixStepMockController` 包含:
|
||
- `message`: 正在被修改的消息对象引用
|
||
- `stop()`: 立即取消所有待执行的 `setTimeout`,中止推演
|
||
- `promise`: 推演完成后 resolve 的 Promise(stop 后不再 resolve)
|
||
|
||
**内部时序**(所有延迟均可通过顶部常量调整):
|
||
|
||
```
|
||
常量 默认值 说明
|
||
TYPE_INTERVAL 28ms 每次打字输出 4 个字符后的等待间隔
|
||
STAGE_GAP 380ms 阶段切换之间的等待时长
|
||
```
|
||
|
||
执行顺序(伪代码):
|
||
|
||
```
|
||
setMain('info', 'running') → emit
|
||
wait(800ms) ← 模拟信息准备耗时
|
||
setMain('info', 'done')
|
||
setMain('sixstep', 'running') → emit
|
||
wait(STAGE_GAP)
|
||
|
||
setSub('purpose', 'running') → emit
|
||
typeInto(PURPOSE_THINKING) ← 逐字打出思考过程
|
||
append cards 逐张(wait 280ms/张)
|
||
setSub('purpose', 'done') → emit
|
||
wait(STAGE_GAP)
|
||
|
||
setSub('actor', 'running') → emit
|
||
typeInto(ACTOR_THINKING)
|
||
append cards 逐张(wait 220ms/张)
|
||
setSub('actor', 'done') → emit
|
||
wait(STAGE_GAP)
|
||
|
||
setSub('behavior', 'running') → emit
|
||
typeInto(BEHAVIOR_THINKING)
|
||
append cards 逐张(wait 200ms/张)
|
||
setSub('behavior', 'done') → emit
|
||
wait(STAGE_GAP)
|
||
|
||
setSub('narrative', 'running') → emit
|
||
typeInto(NARRATIVE_THINKING)
|
||
append cards 逐张(wait 200ms/张)
|
||
setSub('narrative', 'done')
|
||
setMain('sixstep', 'done')
|
||
setMain('final', 'running') → emit
|
||
wait(STAGE_GAP)
|
||
|
||
typeInto(FINAL_ANSWER) ← 逐字打出最终结论
|
||
setMain('final', 'done') → emit
|
||
onComplete()
|
||
```
|
||
|
||
**`emit` 机制**:每次调用 `onUpdate({ ...message, stages: { ...message.stages } })`,将消息对象浅拷贝后传给父组件,触发 React re-render。
|
||
|
||
---
|
||
|
||
## 五、流程图布局工具(`flowLayout.ts`)
|
||
|
||
此文件被沙箱和全屏组件共同引用,避免重复计算。
|
||
|
||
### 5.1 核心函数:`buildLayout`
|
||
|
||
```typescript
|
||
function buildLayout(
|
||
purposes: PurposeCard[],
|
||
actors: ActorCard[],
|
||
behaviors: BehaviorDriverCard[],
|
||
narratives: NarrativeCard[],
|
||
options?: BuildLayoutOptions // 可覆盖节点尺寸和间距,以及根节点文案
|
||
): FlowLayout
|
||
```
|
||
|
||
**`BuildLayoutOptions` 扩展**:除 `nodeWidth`、`nodeHeight`、`levelGap`、`nodeGap`、`paddingX`、`paddingY` 外,可选 **`rootTitle`**、**`rootDetail`**。用于将层级 0 的 `agent-root` 从默认「智能体 / 协同推演…」改为 **当前已选专家**(`SixStepFlowSandbox` / `SixStepFlowFullscreen` 在存在 `message.selectedExpert` 时传入专家姓名与简介摘要)。
|
||
|
||
返回 `FlowLayout`:
|
||
```typescript
|
||
interface FlowLayout {
|
||
nodes: LayoutNode[]; // 已计算好坐标的节点列表
|
||
edges: LayoutEdge[]; // 已确定连接关系和颜色对的边列表
|
||
width: number; // 整体画布宽度(用于 SVG viewBox)
|
||
height: number; // 整体画布高度
|
||
}
|
||
```
|
||
|
||
**布局算法**:改良的"叶节点优先后序遍历":
|
||
|
||
1. 从 agent 根节点开始,递归 DFS 处理子树
|
||
2. 叶节点(无子节点的节点)按从左到右顺序分配 x 坐标,每次递增 `nodeWidth + nodeGap`
|
||
3. 非叶节点的 x = (最左子节点 x + 最右子节点 x) / 2,确保父节点居中于子节点上方
|
||
4. 所有节点的 y = `paddingY + level × (nodeHeight + levelGap)`,level 即树的深度
|
||
|
||
节点层级(`level`)对应关系:
|
||
|
||
| level | variant | 说明 |
|
||
|-------|---------|------|
|
||
| 0 | `agent` | 智能体根节点 |
|
||
| 1 | `purpose` | 目的 |
|
||
| 2 | `actor` | 行为体 |
|
||
| 3 | `behavior` | 预期行为 |
|
||
| 4 | `driver` | 驱动因素 |
|
||
| 5 | `narrative` | 叙事 |
|
||
|
||
注意:`BehaviorDriverCard` 一张卡片会生成两个节点(`behavior` + `driver`),id 分别为 `${card.id}-behavior` 和 `${card.id}-driver`。
|
||
|
||
### 5.2 共享常量
|
||
|
||
| 常量 | 说明 |
|
||
|------|------|
|
||
| `EDGE_COLOR` | 按边类型分别定义深色/浅色主题颜色(5 组) |
|
||
| `EDGE_LABEL` | 每种边类型的中文图例说明 |
|
||
| `CURRENT_PAIR_BY_STAGE` | `SubStepKey → LayoutEdge['pair']` 映射,决定哪组边在哪个阶段展示蚂蚁线 |
|
||
| `STATUS_TITLE` | `SubStepKey → string` 映射,沙箱/全屏顶部标题文字 |
|
||
| `VARIANT_ABBREV` | 节点类型 → 简化缩写(沙箱专用,如 `purpose → 'md'`) |
|
||
| `VARIANT_LABEL` | 节点类型 → 完整中文标签 |
|
||
|
||
### 5.3 辅助函数:`buildEdgePath`
|
||
|
||
生成 SVG 三次贝塞尔曲线路径字符串:
|
||
|
||
```
|
||
M x1 y1 C x1 my, x2 my, x2 y2
|
||
```
|
||
|
||
控制点均取中间 y 坐标(`(y1+y2)/2`),形成平滑 S 型连线,不与节点重叠。
|
||
|
||
---
|
||
|
||
## 六、CSS 动画(`styles.css`)
|
||
|
||
| 类名 | 动画 | 用途 |
|
||
|------|------|------|
|
||
| `.sixstep-ant-line` | `stroke-dasharray: 6 6` + `dashoffset` 循环 | SVG 边上的蚂蚁线流动效果(当阶段正在生成时) |
|
||
| `.sixstep-pulse` | box-shadow 呼吸光晕 | 进度面板当前步骤图标、沙箱标题图标 |
|
||
| `.sixstep-shimmer-text` / `...-light` | 渐变横扫文字 | 进度面板"生成中"文字、沙箱标题生成中状态 |
|
||
| `.sixstep-spin-soft` | 360° 匀速旋转 | loading 图标 |
|
||
| `.sixstep-fade-up` | 从下方淡入 | 子阶段卡片逐步出现 |
|
||
| `.sixstep-node-pop` | scale 弹出 + 高光 | 流程图新增节点的弹出动画(持续 600ms) |
|
||
| `.sixstep-ring-burst` | SVG circle `r` 从 0 扩散到 60,同时 opacity→0 | 节点出现时的扩散光环效果(持续 700ms) |
|
||
| `.sixstep-canvas-grab` / `.sixstep-canvas-grabbing` | `cursor: grab/grabbing` | 流程图画布拖拽手型 |
|
||
| `.sixstep-toolbar-btn` | 基础尺寸 + hover 过渡 | 工具栏图标按钮 |
|
||
| `.sixstep-minimap-rect` | 静态样式 | 缩略图视口矩形指示框 |
|
||
| `.sixstep-resize-handle` | 竖线拖拽手柄 | 沙箱左边缘的宽度调节拖拽条 |
|
||
|
||
---
|
||
|
||
## 七、主页面(`SixStepChatPage.tsx`)
|
||
|
||
### 7.1 职责
|
||
|
||
主页面是整个功能的**状态中枢**,负责:
|
||
|
||
- 多会话列表与 **`/api/conversations`** 持久化(`filter_key` / `filter_value` 标识 6bf 页)。
|
||
- 创建并管理 **`runIntentOrchestrateStream`**(编排 + 意图)与 **`runExpertSearch` / `runExpertCreate`**(仍可用于「换一批专家」「创建专家」等直连 invoke)的控制器生命周期。
|
||
- 将状态与专家相关回调分发给 `SixStepConversation`、`McpSelectModal`、`SixStepFlowSandbox` 等。
|
||
- 管理沙箱宽度拖拽。
|
||
|
||
### 7.2 状态一览(节选)
|
||
|
||
| 状态 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `conversations` / `activeConvId` | 多会话 | 当前选中会话及其 `messages` |
|
||
| `input` | `string` | 输入框文本 |
|
||
| `isGenerating` | `boolean` | 是否正在请求流式接口 |
|
||
| `outputFormat` | `'structured' \| 'markdown'` | 传给后端的 `output_format` |
|
||
| `sandboxOpen` / `sandboxWidth` / `fullscreenOpen` | UI | 流程图沙箱与全屏 |
|
||
| `historyOpen` / `loading` | UI / 加载 | 聊天记录侧栏与首屏拉取 |
|
||
| `activeAssistantMsgId` | `string \| null` | 当前高亮/流式更新的助手消息 |
|
||
| `mcpModal` | `{ open, messageId, expert }` | MCP 多选弹窗状态 |
|
||
|
||
### 7.3 Ref 一览
|
||
|
||
| Ref | 类型 | 用途 |
|
||
|-----|------|------|
|
||
| `controllerRef` | `SixStepMockController \| null` | `runIntentOrchestrateStream` 的 `stop()` |
|
||
| `expertControllerRef` | `ExpertRunController \| null` | 专家搜索/创建流的 `stop()` |
|
||
| `conversationsRef` | 会话数组 | 持久化前读取最新消息 |
|
||
| `containerRef` / `resizeRef` | DOM / 拖拽 | 沙箱宽度拖拽 |
|
||
|
||
### 7.4 核心逻辑
|
||
|
||
**`handleSend`**
|
||
|
||
1. 追加用户消息;创建初始助手消息(`buildInitialAssistantMessage`,`phase` 随流程推进)。
|
||
2. 调用 **`runIntentOrchestrateStream`**,请求体为 **`IntentOrchestrateRequest`**:`user_input`、`output_format`、`intent_mode`(默认 `auto`)、`current_phase`(含上一条助手为 **`paused`** 时传 `paused`)、`selected_expert`、`selected_mcps`、`expert_candidates`、**`last_steps`(来自上一条助手 `rawSteps`)**、`conversation_history` 等。
|
||
- 暂停继续时会继承上一条助手的 `rawSteps` 与 `selectedMcps`(`pausedSelectedMcps`),并显式回传 `selected_mcps` / `last_steps`,避免恢复时丢失步骤选择与历史上下文。
|
||
3. `onUpdate` 内更新当前会话中对应 `id` 的助手消息;结束后 `persistConversation`。
|
||
|
||
**专家与 MCP**
|
||
|
||
- 用户确认专家后 **`handleMcpModalConfirm`**:写入 `selectedExpert` / `selectedMcps`,并可用 **`intent_mode: 'force_orchestrate'`** 立即启动分析(无需用户再次输入)。
|
||
- **`handleRefetchExpert`**:可用 `force_find_expert` 并排除已有候选。
|
||
|
||
**`handleStop`**
|
||
|
||
- `AbortController` 中止流;将未完成步骤从 `running` 收回;助手 **`phase` 置为 `paused`**,便于下次发送走 **`continue_analysis`** / `rerun_from_step` / `revise_input_continue` / `change_steps_continue` / `edit_stage_continue`。
|
||
|
||
**流内关键事件补充**
|
||
|
||
- `resume`:恢复继续分析时,会先把已有 `stages` 中的 `purpose/actor/behavior` 卡片回填到转换缓存,确保后续 `step_end` 的父子映射准确。
|
||
- `rerun`:前端会按 `affected_steps` 清空受影响阶段(`stages`、`subStepStatus`、`rawSteps` 与局部卡片缓存),再接收重跑后的 `step_*`。
|
||
- `change_steps`:按 `removed_steps` 清理被移除阶段与 `rawSteps`,保留未移除阶段并继续执行 `steps_to_run`。
|
||
- `edit_apply`:先更新被编辑卡片内容,再按 `affected_steps` 清理后续阶段并触发重跑。
|
||
- `intent`:包含 `slots`(如 `step`、`new_input`),用于更细粒度前端状态更新。
|
||
|
||
**沙箱宽度拖拽**:逻辑同前(`mousemove` / `mouseup`,保证对话区最小宽度)。
|
||
|
||
**活跃助手消息派生**:
|
||
|
||
```typescript
|
||
const activeAssistantId = /* activeAssistantMsgId 或最后一条 assistant */;
|
||
const activeAssistant = messages.find((m) => m.id === activeAssistantId);
|
||
const liveMainStatus = activeAssistant?.mainStepStatus ?? DEFAULT_MAIN_STATUS;
|
||
const liveSubStatus = activeAssistant?.subStepStatus ?? DEFAULT_SUB_STATUS;
|
||
```
|
||
|
||
`liveMainStatus` / `liveSubStatus` 传给 **`SixStepProgressHorizontal`**;`activeAssistant` 传给沙箱与全屏。
|
||
|
||
### 7.5 Props 传递图
|
||
|
||
```
|
||
SixStepChatPage
|
||
│
|
||
├─► SixStepChatHistory # 会话列表(与顶栏「聊天记录」联动)
|
||
├─► SixStepProgressHorizontal # mainStatus / subStatus / isDark
|
||
├─► SixStepConversation
|
||
│ messages, isGenerating, activeMessageId,
|
||
│ expertHandlers(选专家 / 换一批 / 创建 / 确认)
|
||
├─► ChatInputComposer # onSend → handleSend, onStop → handleStop
|
||
├─► SixStepFlowSandbox # message={activeAssistant}
|
||
├─► SixStepFlowFullscreen
|
||
└─► McpSelectModal # 专家确认后选 MCP
|
||
```
|
||
|
||
---
|
||
|
||
## 八、左侧进度面板(`SixStepProgress.tsx`)
|
||
|
||
> **主页面顶栏**使用的是 **`SixStepProgressHorizontal.tsx`**(横向主/子步骤);本节描述纵向 `SixStepProgress` 的 Props 与交互,组件仍保留供其他布局复用。
|
||
|
||
### 8.1 Props
|
||
|
||
| Prop | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `mainStatus` | `Record<MainStepKey, StepStatus>` | ✓ | 三主步骤状态 |
|
||
| `subStatus` | `Record<SubStepKey, StepStatus>` | ✓ | 四子阶段状态 |
|
||
| `collapsed` | `boolean` | 可选 | 是否折叠,默认 `false` |
|
||
| `onToggleCollapse` | `() => void` | 可选 | 展开/收起回调 |
|
||
|
||
### 8.2 展开视图(`collapsed = false`)
|
||
|
||
从上到下展示三个主步骤卡片,每个卡片包含:
|
||
- 彩色图标(每个步骤有独立配色,running 时呼吸脉冲)
|
||
- 标题 + 进度徽标(`已完成 / 生成中 / 待开始`)
|
||
- 副文本描述(running 时展示 shimmer 动画)
|
||
- 步骤之间用竖线连接(完成后变绿色)
|
||
|
||
当 `sixstep` 阶段进入 running 或 done,在其卡片内部展开四个子阶段列表(`SUB_STEPS`),每个子阶段包含:
|
||
- 彩色图标(与流程图对应色)
|
||
- 标题 + 状态图标
|
||
- 时间线左侧的节点圆点 + 连接竖线
|
||
|
||
右上角收起按钮(`ChevronLeft`)触发 `onToggleCollapse`。
|
||
|
||
### 8.3 折叠视图(`collapsed = true`)
|
||
|
||
渲染轻量的 `CollapsedView` 子组件:
|
||
- 宽度由父容器通过 `w-14` 控制(56px),有 `transition-all duration-300` 过渡动画
|
||
- 顶部 `ChevronRight` 按钮用于展开
|
||
- 竖向排列三个主步骤彩色图标 + 状态圆点
|
||
- 若 `sixstep` 已解锁,在其下方额外展示四个小尺寸子阶段图标(`w-7 h-7`)
|
||
- 所有元素均有 `title` 属性,鼠标悬停显示步骤名称
|
||
|
||
### 8.4 配色方案
|
||
|
||
步骤颜色定义在 `MAIN_STEPS` 和 `SUB_STEPS` 数组的 `color` 字段中:
|
||
|
||
| 步骤 | 深色 | 浅色 |
|
||
|------|------|------|
|
||
| 信息准备 | sky-300 / sky-500 | sky-600 / sky-50 |
|
||
| 6步法推演 | violet-300 / violet-500 | violet-600 / violet-50 |
|
||
| 最终结果 | emerald-300 / emerald-500 | emerald-600 / emerald-50 |
|
||
| 目的 | violet-300 | orange |
|
||
| 行为体 | cyan-300 | red |
|
||
| 预期行为/驱动 | emerald-300 | emerald |
|
||
| 叙事 | amber-300 | amber |
|
||
|
||
---
|
||
|
||
## 九、对话列表(`SixStepConversation.tsx`)
|
||
|
||
### 9.1 Props
|
||
|
||
| Prop | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `messages` | `SixStepMessage[]` | 全部消息 |
|
||
| `isGenerating` | `boolean` | 是否正在生成 |
|
||
| `activeMessageId` | `string \| null` | 当前流式聚焦的助手消息 id |
|
||
| `expertHandlers` | 可选对象 | `onSelectExpert` / `onRefetchExpert` / `onCreateExpert` / `onConfirmExpert` |
|
||
|
||
### 9.2 消息渲染策略
|
||
|
||
每条消息根据 `role` 字段分叉:
|
||
|
||
- `role === 'user'` → 渲染 `UserMessage`:右对齐气泡 + 用户头像
|
||
- `role === 'assistant'` → 渲染 `AssistantMessage`:左对齐 + 机器人头像(生成中时呼吸脉冲 + 星光图标)
|
||
|
||
消息列表容器 `ref` 监听内容变化,每次 `messages` 或 `isGenerating` 变化后自动滚动到底部。
|
||
|
||
### 9.3 助手消息 `AssistantMessage`
|
||
|
||
内部读取 `message.stages` 和 `message.mainStepStatus`、`message.subStepStatus`,根据是否存在各阶段数据来条件渲染对应的阶段块。底部(及部分分支末尾)挂载 **`StreamErrorNotice`**,展示 **`message.errors`**(来自 SSE `error_log` 等)。
|
||
|
||
助手消息结构示意:
|
||
|
||
```
|
||
AssistantMessage
|
||
│
|
||
├─ 信息准备中(仅当 info=running 且无任何 stage)→ shimmer 提示
|
||
│
|
||
├─ PurposeStage (当 stages.purpose 存在时)
|
||
│ ├─ InlineCollapsible (以 StageHeader 为标题)
|
||
│ │ ├─ ThinkingBlock (思考过程)
|
||
│ │ └─ InlineCollapsible (目的卡片列表)
|
||
│ │ └─ PurposeCardItem × N
|
||
│
|
||
├─ ActorStage (当 stages.actor 存在时)
|
||
│ ├─ InlineCollapsible
|
||
│ │ ├─ ThinkingBlock
|
||
│ │ └─ InlineCollapsible (行为体卡片列表)
|
||
│ │ └─ 按 purpose 分组
|
||
│ │ ├─ ParentGroupHeader (所属目的)
|
||
│ │ └─ ActorCardItem × N
|
||
│
|
||
├─ BehaviorStage (当 stages.behavior 存在时)
|
||
│ ├─ InlineCollapsible
|
||
│ │ ├─ ThinkingBlock
|
||
│ │ └─ InlineCollapsible (预期行为/驱动因素卡片列表)
|
||
│ │ └─ 按 actor 分组
|
||
│ │ ├─ ParentGroupHeader (所属行为体)
|
||
│ │ └─ BehaviorCardItem × N(2列网格)
|
||
│
|
||
├─ NarrativeStage (当 stages.narrative 存在时)
|
||
│ ├─ InlineCollapsible
|
||
│ │ ├─ ThinkingBlock
|
||
│ │ └─ InlineCollapsible (叙事卡片列表)
|
||
│ │ └─ 按 driver 分组
|
||
│ │ ├─ ParentGroupHeader (所属驱动因素)
|
||
│ │ └─ NarrativeCardItem × N
|
||
│
|
||
└─ FinalAnswer (当 finalAnswer 存在或 final=running 时)
|
||
│
|
||
└─ StreamErrorNotice (当 message.errors 非空)
|
||
```
|
||
|
||
**分组计算**:`ActorStage`、`BehaviorStage`、`NarrativeStage` 都在内部用 `useMemo` 计算分组,将卡片按父 ID 聚合到 Map,再按父实体列表顺序输出。
|
||
|
||
---
|
||
|
||
## 十、卡片原子组件(`SixStepCards.tsx`)
|
||
|
||
此文件作为"组件库",为对话列表提供所有可复用的卡片和容器组件。
|
||
|
||
### 10.1 `InlineCollapsible`
|
||
|
||
通用折叠面板,思考过程区块、卡片列表区块均复用此组件。
|
||
|
||
| Prop | 说明 |
|
||
|------|------|
|
||
| `title` | 标题区 ReactNode |
|
||
| `children` | 折叠内容 |
|
||
| `defaultOpen` | 默认展开状态(默认 `true`) |
|
||
| `variant` | `'default'`(标准边框背景)或 `'subtle'`(更淡)|
|
||
|
||
展开/收起使用 `grid-template-rows` 动画(`0fr → 1fr`),配合 `opacity` 过渡,无需 JS 计算高度。
|
||
|
||
### 10.2 各卡片组件
|
||
|
||
| 组件 | 卡片类型 | 布局特点 |
|
||
|------|---------|---------|
|
||
| `PurposeCardItem` | 目的 | 紫/橙渐变背景,索引徽标 + Target 图标 + 标题 + 理由 |
|
||
| `ActorCardItem` | 行为体 | 青/红渐变背景,UserCheck 图标 + 标题 + 内容 |
|
||
| `BehaviorCardItem` | 预期行为/驱动因素 | 翠绿渐变背景,上下两部分:TrendingUp 图标+预期行为 / Zap 图标+驱动因素 |
|
||
| `NarrativeCardItem` | 叙事 | 琥珀渐变背景,ScrollText 图标 + 标题 + 理由 |
|
||
|
||
所有卡片均有 `.sixstep-fade-up` 进入动画,`hover` 时边框加深。
|
||
|
||
### 10.3 `ParentGroupHeader`
|
||
|
||
显示某类卡片所属父实体名称的标签行,用于行为体、预期行为/驱动、叙事列表的分组标题。
|
||
|
||
| Prop | 说明 |
|
||
|------|------|
|
||
| `label` | 关系说明(如"所属目的") |
|
||
| `parentTitle` | 父实体名称 |
|
||
| `count` | 该组卡片数量 |
|
||
| `variant` | `'actor' \| 'behavior' \| 'narrative'`,决定图标和配色 |
|
||
|
||
### 10.4 `ThinkingBlock`
|
||
|
||
展示思考过程文本的区块,`loading` 为 `true` 时标题行展示 "生成中" shimmer 文字。
|
||
|
||
### 10.5 `StageHeader`
|
||
|
||
折叠面板标题,包含阶段图标、阶段标题、卡片数量徽标、状态徽标(已完成/生成中)。`loading = true` 时图标呼吸脉冲,副文本展示 shimmer 动画。
|
||
|
||
---
|
||
|
||
## 十一、流程图沙箱(`SixStepFlowSandbox.tsx`)
|
||
|
||
### 11.1 Props
|
||
|
||
| Prop | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `message` | `SixStepMessage \| undefined` | 活跃助手消息,用于提取各阶段卡片数据 |
|
||
| `width` | `number` | 当前沙箱宽度(px),由主页面拖拽控制 |
|
||
| `onResizeStart` | `(e: React.MouseEvent) => void` | 拖拽手柄 mousedown 回调 |
|
||
| `onClose` | `() => void` | 关闭沙箱回调 |
|
||
| `onOpenFullscreen` | `() => void` | 打开全屏回调 |
|
||
|
||
### 11.2 布局参数(`COMPACT_OPTIONS`)
|
||
|
||
沙箱使用缩小版布局,节点小于全屏版:
|
||
|
||
```typescript
|
||
const COMPACT_OPTIONS = {
|
||
nodeWidth: 120, nodeHeight: 60,
|
||
levelGap: 80, nodeGap: 24,
|
||
paddingX: 32, paddingY: 28,
|
||
};
|
||
```
|
||
|
||
### 11.3 简化节点(`CompactNode`)
|
||
|
||
每个节点只显示**彩色图标 + 阶段缩写**(`VARIANT_ABBREV`):
|
||
|
||
| 层级 | 缩写 |
|
||
|------|------|
|
||
| 智能体(或根标题改为已选专家名时仍为 variant `agent`) | AI |
|
||
| 目的 | md |
|
||
| 行为体 | xwt |
|
||
| 预期行为 | yqxw |
|
||
| 驱动因素 | qdys |
|
||
| 叙事 | xs |
|
||
|
||
底部固定图例说明对应关系。
|
||
|
||
### 11.4 新增节点动画
|
||
|
||
通过 `seenIdsRef`(`Set<string>`)记录已渲染过的节点 ID。每次 `layout.nodes` 变化时,找出新增的 ID,将其加入 `appearingIds` 状态,700ms 后自动移除。
|
||
|
||
`CompactNode` 接收 `appearing` prop,为 `true` 时:
|
||
1. 为 `<g>` 添加 `.sixstep-node-pop` CSS 类(scale 弹出)
|
||
2. 在节点中心绘制一个 SVG `<circle>` 并添加 `.sixstep-ring-burst` CSS 类(扩散光环)
|
||
|
||
### 11.5 交互能力
|
||
|
||
**平移(Pan)**:
|
||
- `mousedown` 记录起始坐标和初始 `tx/ty` 到 `panStart.current`
|
||
- 通过 `useEffect` 监听 `isPanning` 状态,动态注册/注销 `window` 的 `mousemove` 和 `mouseup`
|
||
- `mousemove` 计算偏移量 `(e.clientX - startX, e.clientY - startY)` 叠加到初始平移值
|
||
|
||
**缩放(Zoom)**:
|
||
- 通过 `useEffect` 向 `containerRef` 注册原生 `wheel` 事件(`{ passive: false }`),确保 `e.preventDefault()` 生效,不触发浏览器页面缩放
|
||
- 以鼠标位置为缩放中心:`nextTx = mx - (mx - prev.tx) * (nextScale / prev.scale)`
|
||
- 缩放范围:0.3x ~ 2.5x
|
||
|
||
**工具栏**:
|
||
| 按钮 | 功能 |
|
||
|------|------|
|
||
| `+` | 按 1.2 倍放大 |
|
||
| `-` | 按 1.2 倍缩小 |
|
||
| ↺ | 恢复 1:1(translate 0,0 scale 1) |
|
||
| ⊕ | 定位画布中央(保持当前缩放比,调整平移使图形居中) |
|
||
| ⤢ | 触发 `onOpenFullscreen` |
|
||
|
||
首次有内容时自动执行 `fitToView`(等比缩放 + 居中),由 `fitAppliedRef` 控制只执行一次。
|
||
|
||
### 11.6 连线说明
|
||
|
||
SVG 中的 `<defs>` 为每种边类型各定义一个箭头 marker(id 加 `sixstep-sandbox-` 前缀,与全屏版 marker 不冲突)。当 `activePair === edge.pair` 时,该边添加 `.sixstep-ant-line` CSS 类展示蚂蚁线动画。
|
||
|
||
### 11.7 调整宽度
|
||
|
||
左侧有一个 `position: absolute` 的 `.sixstep-resize-handle` 竖线拖拽条,`mousedown` 触发 `onResizeStart`(由主页面处理具体逻辑)。
|
||
|
||
---
|
||
|
||
## 十二、全屏流程图视图(`SixStepFlowFullscreen.tsx`)
|
||
|
||
### 12.1 Props
|
||
|
||
| Prop | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `message` | `SixStepMessage \| undefined` | 与沙箱相同的活跃助手消息 |
|
||
| `onClose` | `() => void` | 关闭全屏回调 |
|
||
|
||
### 12.2 与沙箱的区别
|
||
|
||
| 特性 | 沙箱 | 全屏 |
|
||
|------|------|------|
|
||
| 布局参数 | 紧凑(节点 120×60) | 标准(节点 200×84) |
|
||
| 节点内容 | 缩写 + 图标 | 完整标题 + 详情 + 图标 |
|
||
| 节点出现动画 | 有(pop + ring-burst) | 无 |
|
||
| 子树收起/展开 | 无 | 有(点击**有子节点**的父节点;右下角「-」收起 / 「+」展开;画布左下角有操作提示) |
|
||
| 缩略图 | 无 | 有(右下角;仅绘制当前可见节点) |
|
||
| 下载 | 无 | 有(导出 SVG) |
|
||
| Esc 关闭 | 无 | 有 |
|
||
| 层级 | flex 子元素 | `fixed inset-0 z-50`(全视口遮罩) |
|
||
| 缩放范围 | 0.3x ~ 2.5x | 0.2x ~ 3x |
|
||
|
||
### 12.3 详细节点(`DetailNode`)
|
||
|
||
使用 `foreignObject` 嵌入 HTML,展示:
|
||
- 彩色图标包裹 + 大写类型标签(`VARIANT_LABEL`)
|
||
- 节点标题(`line-clamp-1`)
|
||
- 详情内容(`line-clamp-2`)
|
||
|
||
深色/浅色主题的渐变背景、图标颜色均通过 `VARIANT_TOKEN` 查表获取。
|
||
|
||
**收起子树**:当 `flowLayout` 中节点带 **`children`** 时,卡片右上角显示 **`+` / `-`**,点击节点(或该角标)切换 **`collapsedNodeIds`**:自该节点向下的后代节点与相关边不再纳入 **`targetVisibleNodeSet`**,并以约 **240ms** 的透明度过渡(进入/离开集合合并渲染,避免闪烁)。重新生成布局时会剔除已不存在的节点 id,避免残留收起状态。根节点若被选为专家展示,仍可按树结构收起其下子链。
|
||
|
||
### 12.4 缩略图(`Minimap`)
|
||
|
||
固定 220×150px,渲染在右下角。
|
||
|
||
内部逻辑:
|
||
1. 计算整体布局与 minimap 尺寸的缩放比 `ratio = Math.min((W-10)/layoutW, (H-10)/layoutH)`
|
||
2. 按 `ratio` 缩小绘制每个节点矩形(使用 `MINIMAP_VARIANT_FILL` 颜色,无文字)
|
||
3. 根据当前 `transform`(`tx, ty, scale`)和视口尺寸,计算可见区域在世界坐标的矩形,再乘 `ratio` 映射到 minimap 坐标系,绘制蓝色描边矩形(`.sixstep-minimap-rect`)
|
||
4. 点击 minimap:将点击的 minimap 坐标逆映射到世界坐标,调用 `onJump` 将视口移动到以该点为中心
|
||
|
||
### 12.5 SVG 下载
|
||
|
||
点击下载按钮时:
|
||
1. 克隆 `svgRef.current`(当前全屏 SVG 元素)
|
||
2. 设置 `width/height` 为实际布局尺寸,移除 transform
|
||
3. 用 `XMLSerializer` 序列化为字符串
|
||
4. 创建 `Blob` 和临时 `<a>` 标签,触发下载
|
||
5. 文件名格式:`6步法推演-YYYY-MM-DD-HH-mm-ss.svg`
|
||
|
||
> ⚠️ 注意:由于节点内容在 `foreignObject` 中使用 HTML + CSS 类名,下载的 SVG 打开时若外部查看器不支持 CSS,样式会丢失。可在未来改为纯 SVG 文本节点渲染以提升兼容性。
|
||
|
||
---
|
||
|
||
## 十三、整体数据流图
|
||
|
||
```
|
||
用户输入(回车)
|
||
│
|
||
▼
|
||
SixStepChatPage.handleSend()
|
||
│
|
||
├─► 当前会话追加 UserMessage + 初始 AssistantMessage
|
||
│
|
||
└─► runIntentOrchestrateStream(assistant, { onUpdate, onComplete }, IntentOrchestrateRequest)
|
||
│
|
||
│ readSSE('/api/orchestrate/stream', body)
|
||
│
|
||
│ intent / expert_* / clarify / resume / rerun / change_steps / edit_apply
|
||
│ start / step_* / flow / done / delta(追问或 markdown)
|
||
│ 每次 onUpdate → 更新会话中该 assistant 的 SixStepMessage
|
||
│
|
||
▼ React re-render
|
||
│
|
||
├─► SixStepProgressHorizontal(liveMainStatus / liveSubStatus)
|
||
├─► SixStepConversation(messages、expertHandlers、intent/clarify 等)
|
||
├─► SixStepFlowSandbox / Fullscreen(stages → buildLayout)
|
||
└─► persistConversation(SQLite)
|
||
```
|
||
|
||
---
|
||
|
||
## 十四、路由注册
|
||
|
||
**`src/App.tsx`**:
|
||
```tsx
|
||
// 路由定义
|
||
<Route path="sixstep-chat" element={<SixStepChatPage />} />
|
||
<Route path="/observability" element={<ObservabilityPage />} />
|
||
|
||
// 导航路径映射(handleNavigate 函数)
|
||
else if ([..., 'sixstep-chat'].includes(page)) {
|
||
path = `/qa-center/${page}`;
|
||
}
|
||
```
|
||
|
||
**可观测性页面**(调用耗时 / 错误日志筛选):全站路由 **`/observability`**,侧栏顶级「可观测性」,对接后端 **`GET /api/observability/calls`**、**`/errors`**、**`/options`** 等(详见 `backend/doc.md`)。与 6bf 页面独立,不在 `/qa-center` 前缀下。
|
||
|
||
**`src/components/Sidebar.tsx`**:
|
||
```tsx
|
||
// 菜单配置(问答中心 children)
|
||
{ id: 'sixstep-chat', label: '6bf', icon: Workflow }
|
||
|
||
// qaPages 集合(控制问答中心父菜单高亮)
|
||
const qaPages = new Set([..., 'sixstep-chat']);
|
||
```
|
||
|
||
---
|
||
|
||
## 十五、流式错误与 `error_log`(`api.ts`)
|
||
|
||
编排 SSE 除 **`error`** 外,可能下发 **`error_log`**(步骤失败摘要、skill 降级等)。`runIntentOrchestrateStream` 将其归一为 **`StreamErrorItem`** 并 **`push` 到 `message.errors`**;网络异常也会写入类似结构。后端在可执行 **skill 失败已跳过**时,**`message` 文案**可能附带 **异常原因前若干字符**(与 `backend/app/mcp/base.py` 截断策略一致),便于在气泡内区分超时与其它错误。
|
||
|
||
**`StreamErrorNotice`**:仅在对应助手气泡内展示,支持折叠展开、错误/警告计数摘要;不参与 `showAnalysisContent` 隐藏逻辑(专家阶段亦可显示)。
|
||
|
||
---
|
||
|
||
|
||
## 十六、扩展指南
|
||
|
||
### 接入编排与意图流
|
||
|
||
1. 主入口为 **`runIntentOrchestrateStream`**(`api.ts`),与后端 **`/api/orchestrate/stream`** 事件一一对应;扩展新意图时通常同时改 **`backend/app/intent/detector.py`** 与 **`runIntentOrchestrateStream`** 的分支解析。
|
||
- 当前除 `continue_analysis` 外,还支持 `rerun_from_step`、`revise_input_continue`、`change_steps_continue`、`edit_stage_continue` 分支。
|
||
2. 保持 **`onUpdate(SixStepMessage)`** 契约:`SixStepChatPage` 只需合并助手消息字段。
|
||
3. 纯编排、无意图分支的调试可继续用 **`runSixStepDeduction`**(同文件内,仅 `start` / `step_*` / `flow` / `done`)。
|
||
|
||
详见 **`INTENT_INTEGRATION.md`** 与仓库 **`docs/xvqiu.md`**。
|
||
|
||
### 某子步(如行为体、叙事)体感很慢时
|
||
|
||
前端 **`step_end`** 事件里会带上该步的 **`duration_ms`**(整步总耗时),便于在 UI 或调试时对比各步。
|
||
|
||
更细拆分(skill 耗时 vs 拼 prompt 扩展 vs LLM)在**后端**完成:每次 `POST /api/agents/{key}/invoke` 及编排内对 `MCPAgent.invoke` 的调用,服务端会打 **`mcp_timing`** / **`mcp_skill_timing`** 日志(见 **`backend/doc.md`**「MCP 调用接口」与 **`backend/DEVELOPMENT.md`** §5.2)。排查时在运行 `python -m app.main` 的终端查看即可;无需改前端请求体。
|
||
|
||
### 替换流程图引擎(如 G6 / React Flow)
|
||
|
||
仅需替换 `SixStepFlowSandbox.tsx` 和 `SixStepFlowFullscreen.tsx` 中的 SVG 渲染部分:
|
||
- `buildLayout()` 的 `LayoutNode` 数组可直接转换为 G6 节点
|
||
- `LayoutEdge` 数组可直接转换为 G6 边
|
||
- `CURRENT_PAIR_BY_STAGE` 和 `EDGE_COLOR` 仍适用
|
||
|
||
### 新增推演子阶段
|
||
|
||
1. 在 `types.ts` 的 `SubStepKey` 中新增键名
|
||
2. 在 `SixStepStages` 中新增对应 `*StageData` 字段
|
||
3. 在 `mockData.ts` 的 `runSixStepDeduction` 中新增生成逻辑(若仍需要离线演示)
|
||
4. 在 `SixStepConversation.tsx` 的 `AssistantMessage` 中新增阶段渲染块
|
||
5. 在 `SixStepProgress.tsx` / **`SixStepProgressHorizontal.tsx`** 的 `SUB_STEPS` 中新增步骤项
|
||
6. 若需要流程图新层级,在 `flowLayout.ts` 的 `buildLayout` 中新增节点构建逻辑
|