소스 검색

feat: 问数运行逐步落盘(每步思考/工具完成即持久化,中途重启可续看)+ 工具步骤加「查询/提取/计算」阶段徽标

KenZ1117 2 주 전
부모
커밋
047c5d5ffd
3개의 변경된 파일과 14개의 추가작업 그리고 1개의 파일을 삭제
  1. 7 1
      public/ask.js
  2. 4 0
      public/style.css
  3. 3 0
      server/agent/loop.js

+ 7 - 1
public/ask.js

@@ -490,6 +490,12 @@
       cross_entity: '跨主体对比', aitable_verify: '湖云比对', build_report: '分析报告', metric_query: '指标查询',
       detect_anomalies: '异常检测', attribution: '归因分析',
     };
+    // 阶段徽标:让「数据查询 → 提取 → 计算」的推进在时间线上一眼可见
+    const TOOL_PHASE = {
+      lake_overview: '查询', query_report: '查询', compare_periods: '查询', metric_query: '查询', cross_entity: '查询', peer_benchmark: '查询',
+      raw_inspect: '提取', aitable_verify: '提取',
+      data_quality: '计算', cross_check: '计算', detect_anomalies: '计算', attribution: '计算', build_report: '计算',
+    };
     const VERDICT_WORD = { ok: '通过', warn: '警告', fail: '不一致' };
     const skillName = rec.skill ? ((state.skills.find((s) => s.key === rec.skill) || {}).name || rec.skill) : '';
 
@@ -525,7 +531,7 @@
         const bits = [fmtDuration(step.ms), fmtBytes(step.bytes)];
         if (step.rows != null) bits.push(`${step.rows} 行`);
         steps.push(`<div class="ask-step">
-          <div class="ask-step-head"><span class="ask-dot ${step.ok ? 'done' : 'failed'}"></span><b>${esc(label)}</b></div>
+          <div class="ask-step-head"><span class="ask-dot ${step.ok ? 'done' : 'failed'}"></span><span class="ask-phase phase-${TOOL_PHASE[step.tool] || 'query'}">${esc(TOOL_PHASE[step.tool] || '查询')}</span><b>${esc(label)}</b></div>
           <div class="ask-step-meta">${bits.filter(Boolean).map((b) => `<span class="ask-chip">${esc(String(b))}</span>`).join('')}</div>
           <details class="ask-tool"><summary>参数与结果</summary>
             <pre class="ask-pre">参数:${esc(JSON.stringify(step.args || {}))}

+ 4 - 0
public/style.css

@@ -1171,3 +1171,7 @@ main.content { padding: var(--page-padding-y) var(--page-padding-x) var(--page-p
 .diagnose-raw { margin-top: 12px; font-size: 12.5px; }
 .diagnose-raw summary { cursor: pointer; color: var(--text-2); }
 .diagnose-raw pre { max-height: 300px; overflow: auto; margin-top: 8px; padding: 10px; border-radius: 8px; background: var(--border-light); white-space: pre-wrap; word-break: break-all; font-size: 12px; }
+.ask-phase { flex: 0 0 auto; padding: 1px 7px; border-radius: 999px; font-size: 10.5px; line-height: 1.7; }
+.ask-phase.phase-查询 { background: var(--info-bg); color: var(--info); }
+.ask-phase.phase-提取 { background: var(--warn-bg); color: var(--warn); }
+.ask-phase.phase-计算 { background: var(--ok-bg); color: var(--ok); }

+ 3 - 0
server/agent/loop.js

@@ -147,6 +147,7 @@ async function runAgent(o) {
     } finally {
       step.ms = Date.now() - t0;
       trace.pushEvent(rec, 'tool.done', { tool: toolName, ok: step.ok, ms: step.ms, error: step.error });
+      trace.persistRun(rec); // 逐步落盘:每个工具(查询/提取/计算)完成即对轮询可见
     }
     return step;
   };
@@ -165,6 +166,7 @@ async function runAgent(o) {
   if (o.plan && Array.isArray(o.plan.steps) && o.plan.steps.length) {
     rec.replayFrom = o.plan.question;
     rec.steps.push({ type: 'thought', text: `命中已验证打法「${o.plan.question}」,直接复用其查询序列(数据按当前湖重算)`, ms: 0 });
+    trace.persistRun(rec);
     for (const ps of o.plan.steps) {
       if (rec.cancelRequested) throw cancelledError();
       const tool = registry.get(ps.tool);
@@ -223,6 +225,7 @@ async function runAgent(o) {
       tokensIn: turnMeta.usage ? turnMeta.usage.promptTokens : null,
       tokensOut: turnMeta.usage ? turnMeta.usage.completionTokens : null,
     });
+    trace.persistRun(rec); // 逐步落盘:前端轮询实时看到思考/查询/计算推进
     messages.push({ role: 'assistant', content: String(reply).slice(0, 4000) });
 
     if (parsed.type === 'final') {