From 995b752e705e03da0e20a6efaf9646bfede1e388 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Sun, 23 Aug 2026 01:49:05 +0800 Subject: [PATCH] fix(web): keep consultation thinking above one spoken answer Per-domain thinking trees were interleaved with sliced analysis, so a finished reply still looked like unfinished checklists. One collapsed thinking panel and one full body restores the reading order. Co-authored-by: Cursor --- docs/BUG_HISTORY.md | 16 +++ frontend/src/app/globals.css | 38 ++++-- .../src/components/chat-markdown-view.tsx | 4 +- .../src/components/chat-message-content.tsx | 3 +- .../consultation-thinking-report.tsx | 82 +++++-------- .../src/components/thinking-step-tree.tsx | 111 +++++++++++++----- frontend/src/lib/chat-definition-lists.ts | 54 +++++++++ .../src/lib/consultation-thinking-plan.ts | 8 ++ frontend/tests/chat-definition-lists.test.ts | 31 +++++ frontend/tests/chat-stream-layout.test.ts | 6 +- .../consultation-technique-audit.test.ts | 1 + .../tests/consultation-thinking-plan.test.ts | 15 +++ 12 files changed, 274 insertions(+), 95 deletions(-) create mode 100644 frontend/src/lib/chat-definition-lists.ts create mode 100644 frontend/tests/chat-definition-lists.test.ts diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index fa6135d7..9f8f25a8 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -5387,4 +5387,20 @@ - 复发自:BUG-309(validate 跳过 `next build`,publish 才暴露类型错误) - 修复版本:`707327ef` +## BUG-356 | 咨询思考与正文交错,完成步骤仍显示「还有 N 项」 + +- 状态:resolved +- 首次发现:2026-08-23 +- 最近更新:2026-08-23 +- 影响面:咨询页 `ConsultationThinkingReport`、思考步骤树、回答 Markdown 列表、技法审计折叠 +- 用户现象:思考与「分析」看起来像同一段;领域步骤树插在正文中间和文末;已完成步骤仍显示空心圆「还有 N 项」;行运/适合推进等并列项是挤在一起的段落,技法表与「本轮技法」重复。 +- 触发条件:带 `thinking.section` 的本命咨询回答,尤其模型未按领域 H2 切开、或步骤超过 4 项时。 +- 根因:报告按每个 thinking section 交错渲染思考+切片正文。`visibleThinkingSteps` 把第 5 项起收成「还有 N 项」且固定空心圆。模型用「标签:解释」段落而不是列表。正文已贴审计表时仍再叠一层折叠表。 +- 修复:一轮只保留一个可折叠「思考」和一个完整「分析」。结算后未写出的步骤标为完成并展开全部步骤。并列「标签:解释」提升为 Markdown 列表。正文已有完整审计表时不再重复折叠副本。组答要求并列项用 bullet list。 +- 验证:`frontend/tests/consultation-thinking-plan.test.ts`、`frontend/tests/chat-definition-lists.test.ts`、`frontend/tests/chat-stream-layout.test.ts`、`frontend/tests/consultation-technique-audit.test.ts` +- 防复发:咨询思考不得按领域把正文切片后反复插入步骤树。完成态不得用空心圆「还有 N 项」代替未展示步骤。正文已含完整技法表时不得再渲染折叠副本。 +- 相关记录:BUG-354 +- 复发自:BUG-354(按领域交错思考/分析,步骤树截断) +- 修复版本: + diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index ca5e35f5..a29277c4 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -859,22 +859,34 @@ button:disabled { cursor: default; opacity: .45; } } .consultation-thinking-report { display: grid; - gap: var(--space-5); + gap: var(--space-6); } -.consultation-report-block { - display: grid; - gap: var(--space-2); +.consultation-step-tree__group + .consultation-step-tree__group { + margin-top: var(--space-4); } -.consultation-report-analysis__label { - margin: 0 0 var(--space-2); - color: var(--color-ink); - font-size: 13px; - font-weight: 600; - line-height: 1.5; +.consultation-report-analysis { + min-width: 0; } .consultation-report-analysis .message-answer { margin-top: 0; } +.consultation-report-analysis .message-markdown { + color: var(--color-ink-strong); +} +.message-markdown ul.markdown-list, +.message-markdown ol.markdown-list { + display: grid; + gap: var(--space-3); + margin: var(--space-3) 0 var(--space-5); + padding-inline-start: 1.35em; +} +.message-markdown .markdown-list > li { + padding-block: 0; + padding-inline-start: 6px; +} +.message-markdown .markdown-list > li + li { + margin-top: 0; +} .rectification-message-entry { min-width: 0; } .message-actions { display: flex; @@ -1947,7 +1959,11 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .conversation:not(.is-empty):not(.is-rectification) .message-markdown ul, .conversation:not(.is-empty):not(.is-rectification) .message-markdown ol { - margin: 4px 0 var(--space-5); + margin: var(--space-3) 0 var(--space-5); +} + +.conversation:not(.is-empty):not(.is-rectification) .message-markdown .markdown-list { + gap: var(--space-4); } .conversation:not(.is-empty):not(.is-rectification) .markdown-table { diff --git a/frontend/src/components/chat-markdown-view.tsx b/frontend/src/components/chat-markdown-view.tsx index de24c227..e5bc4c37 100644 --- a/frontend/src/components/chat-markdown-view.tsx +++ b/frontend/src/components/chat-markdown-view.tsx @@ -3,6 +3,8 @@ import ReactMarkdown, { type Components } from "react-markdown"; import remarkGfm from "remark-gfm"; +import { promoteDefinitionLists } from "@/lib/chat-definition-lists"; + const markdownComponents: Components = { a: ({ children, href, ...props }) => ( @@ -25,7 +27,7 @@ export function renderChatMarkdown(text: string) { remarkPlugins={[remarkGfm]} skipHtml > - {text} + {promoteDefinitionLists(text)} ); } diff --git a/frontend/src/components/chat-message-content.tsx b/frontend/src/components/chat-message-content.tsx index 6a67593f..bec2ea9d 100644 --- a/frontend/src/components/chat-message-content.tsx +++ b/frontend/src/components/chat-message-content.tsx @@ -54,6 +54,7 @@ export function ChatMessageContent({ const split = splitSpokenAnswerAndTechniqueAudit(text); const rows = resolveTechniqueAuditRows(split, auditRows); const spoken = split.spoken; + const showFoldedAudit = rows.length > 0 && split.rows.length === 0; return (
@@ -67,7 +68,7 @@ export function ChatMessageContent({
) : null} {vargaSentence ?

{vargaSentence}

: null} - {rows.length > 0 ? : null} + {showFoldedAudit ? : null} ); } diff --git a/frontend/src/components/consultation-thinking-report.tsx b/frontend/src/components/consultation-thinking-report.tsx index bd9bea72..ea8bf098 100644 --- a/frontend/src/components/consultation-thinking-report.tsx +++ b/frontend/src/components/consultation-thinking-report.tsx @@ -5,25 +5,9 @@ import { ThinkingStepTree } from "@/components/thinking-step-tree"; import type { TechniqueAuditRow } from "@/lib/consultation-agent-events"; import { applyThinkingSectionProgress, - REPORT_HEADING, - splitAnswerByHeadings, type PublicThinkingSection, } from "@/lib/consultation-thinking-plan"; -function analysisForSection( - section: PublicThinkingSection, - preamble: string, - slices: Record, - isFirst: boolean, -): string { - const own = slices[section.heading] ?? ""; - const wrap = section.id === "close" ? (slices[REPORT_HEADING.wrap] ?? "") : ""; - const lead = isFirst || section.id === "foundation" || section.id === "answer" || section.id === "window" - ? preamble - : ""; - return [lead, own, wrap].filter((part) => part.trim()).join("\n\n"); -} - export function ConsultationThinkingReport({ sections, answer, @@ -43,44 +27,40 @@ export function ConsultationThinkingReport({ auditRows?: readonly TechniqueAuditRow[]; vargaSentence?: string | null; }>) { - const progressed = applyThinkingSectionProgress(sections, answer); - const headings = [ - ...progressed.map((section) => section.heading), - REPORT_HEADING.wrap, - ]; - const { preamble, slices } = splitAnswerByHeadings(answer, headings); + const hasAnswer = Boolean(answer.trim()); + const progressed = applyThinkingSectionProgress(sections, answer, { + settled: !live && hasAnswer, + }); + const activeIndex = progressed.findIndex((section) => ( + section.steps.some((step) => step.status === "active") + )); return (
- {progressed.map((section, index) => { - const active = section.steps.some((step) => step.status === "active"); - const analysis = analysisForSection(section, preamble, slices, index === 0); - const last = index === progressed.length - 1; - return ( -
- - {analysis.trim() ? ( -
-

分析

- -
- ) : null} -
- ); - })} + ({ + id: section.id, + intent: section.title, + steps: section.steps, + live: live && index === activeIndex, + }))} + revealAll + live={live && !hasAnswer} + liveLabel={live && !hasAnswer ? liveLabel : undefined} + liveState={liveState} + startedAt={live && !hasAnswer ? startedAt : undefined} + defaultOpen={live && !hasAnswer} + /> + {hasAnswer ? ( +
+ +
+ ) : null}
); } diff --git a/frontend/src/components/thinking-step-tree.tsx b/frontend/src/components/thinking-step-tree.tsx index c893e1ce..d2eb70ff 100644 --- a/frontend/src/components/thinking-step-tree.tsx +++ b/frontend/src/components/thinking-step-tree.tsx @@ -59,11 +59,68 @@ function StepMarker({ return