39 KiB
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 接口:
interface PurposeStageData {
thinking: string; // 模型思考过程文本(流式填充)
cards: PurposeCard[]; // 已生成的卡片列表(逐张追加)
complete: boolean; // 该阶段是否完全结束
}
// ActorStageData / BehaviorStageData / NarrativeStageData 结构类似
3.4 消息总线:SixStepMessage
这是整个页面最核心的数据结构,一条助手消息包含所有推演状态:
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
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
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:
interface FlowLayout {
nodes: LayoutNode[]; // 已计算好坐标的节点列表
edges: LayoutEdge[]; // 已确定连接关系和颜色对的边列表
width: number; // 整体画布宽度(用于 SVG viewBox)
height: number; // 整体画布高度
}
布局算法:改良的"叶节点优先后序遍历":
- 从 agent 根节点开始,递归 DFS 处理子树
- 叶节点(无子节点的节点)按从左到右顺序分配 x 坐标,每次递增
nodeWidth + nodeGap - 非叶节点的 x = (最左子节点 x + 最右子节点 x) / 2,确保父节点居中于子节点上方
- 所有节点的 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
- 追加用户消息;创建初始助手消息(
buildInitialAssistantMessage,phase随流程推进)。 - 调用
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,避免恢复时丢失步骤选择与历史上下文。
- 暂停继续时会继承上一条助手的
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,保证对话区最小宽度)。
活跃助手消息派生:
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)
沙箱使用缩小版布局,节点小于全屏版:
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 时:
- 为
<g>添加.sixstep-node-popCSS 类(scale 弹出) - 在节点中心绘制一个 SVG
<circle>并添加.sixstep-ring-burstCSS 类(扩散光环)
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,渲染在右下角。
内部逻辑:
- 计算整体布局与 minimap 尺寸的缩放比
ratio = Math.min((W-10)/layoutW, (H-10)/layoutH) - 按
ratio缩小绘制每个节点矩形(使用MINIMAP_VARIANT_FILL颜色,无文字) - 根据当前
transform(tx, ty, scale)和视口尺寸,计算可见区域在世界坐标的矩形,再乘ratio映射到 minimap 坐标系,绘制蓝色描边矩形(.sixstep-minimap-rect) - 点击 minimap:将点击的 minimap 坐标逆映射到世界坐标,调用
onJump将视口移动到以该点为中心
12.5 SVG 下载
点击下载按钮时:
- 克隆
svgRef.current(当前全屏 SVG 元素) - 设置
width/height为实际布局尺寸,移除 transform - 用
XMLSerializer序列化为字符串 - 创建
Blob和临时<a>标签,触发下载 - 文件名格式:
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:
// 路由定义
<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:
// 菜单配置(问答中心 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 隐藏逻辑(专家阶段亦可显示)。
十六、扩展指南
接入编排与意图流
- 主入口为
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分支。
- 当前除
- 保持
onUpdate(SixStepMessage)契约:SixStepChatPage只需合并助手消息字段。 - 纯编排、无意图分支的调试可继续用
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仍适用
新增推演子阶段
- 在
types.ts的SubStepKey中新增键名 - 在
SixStepStages中新增对应*StageData字段 - 在
mockData.ts的runSixStepDeduction中新增生成逻辑(若仍需要离线演示) - 在
SixStepConversation.tsx的AssistantMessage中新增阶段渲染块 - 在
SixStepProgress.tsx/SixStepProgressHorizontal.tsx的SUB_STEPS中新增步骤项 - 若需要流程图新层级,在
flowLayout.ts的buildLayout中新增节点构建逻辑