From 5b564cc62f6164fe6b91a200a6264847cdb2011f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cchenhuachun=E2=80=9D?= <“samchen08@163.com”> Date: Fri, 28 Aug 2026 10:28:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor(agent):=20=E4=BC=98=E5=8C=96=20archite?= =?UTF-8?q?ctPlan=20=E8=A7=A3=E6=9E=90=E5=85=BC=E5=AE=B9=E6=97=A0=20steps?= =?UTF-8?q?=20=E7=9A=84=E7=9B=B4=E6=8E=A5=E5=9B=9E=E7=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的 tryParsePlan 函数,统一使用 parsePlanOutput 解析内容 - 解析 architectChat.content 时兼容没有 steps 的直接回答场景 - 改进错误处理逻辑,直接使用 parsePlanOutput 返回的错误信息 - 新增测试覆盖无 steps 的直接回答恢复场景,确保行为正确 --- .../agent/composables/useReplayChat.ts | 19 +++--------- packages/designer/tests/useReplayChat.test.ts | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/designer/src/components/widgets/agent/composables/useReplayChat.ts b/packages/designer/src/components/widgets/agent/composables/useReplayChat.ts index 7904209ac..b2e829707 100644 --- a/packages/designer/src/components/widgets/agent/composables/useReplayChat.ts +++ b/packages/designer/src/components/widgets/agent/composables/useReplayChat.ts @@ -35,15 +35,6 @@ function extractIntent(content: string): string { return content.trim(); } -/** 尝试从 content 中解析 JSON 计划 */ -function tryParsePlan(content: string): PlanResult | null { - const parsed = parseJsonObject(content); - if (parsed?.intent && Array.isArray(parsed.steps)) { - return parsed; - } - return null; -} - /** * 从 editor chat 中解析步骤元数据 * 优先读取持久化的 stepMeta 快照(新记录);旧记录回退到正则解析 prompt @@ -372,8 +363,9 @@ function buildRound(chats: ChatRecord[]): ConversationRound | null { round.attachments = architectChat.files || undefined; round.promptSent = architectChat.prompt || ''; - // 尝试解析 plan - round.architectPlan = tryParsePlan(architectChat.content || ''); + // 与运行时使用相同的解析规则,兼容没有 steps 的直接回答 + const parsedPlan = parsePlanOutput(architectChat.content || ''); + round.architectPlan = parsedPlan.plan; // 若 plan 解析失败,从 editor chats 重建最小 plan if (!round.architectPlan && editorChats.length > 0) { @@ -408,9 +400,8 @@ function buildRound(chats: ChatRecord[]): ConversationRound | null { } else if (editorChats.length === 0 && !round.architectPlan) { const content = architectChat.content || ''; // 模型自报错误({"error": "..."})→ 还原为具体规划失败原因 - const parsed = parsePlanOutput(content); - if (parsed.error) { - round.architectError = parsed.error; + if (parsedPlan.error) { + round.architectError = parsedPlan.error; } else if (content.trim()) { // 无法解析 plan 且无 steps,content 作为直接回答 round.architectAnswer = content; diff --git a/packages/designer/tests/useReplayChat.test.ts b/packages/designer/tests/useReplayChat.test.ts index 0cc397729..df15efe54 100644 --- a/packages/designer/tests/useReplayChat.test.ts +++ b/packages/designer/tests/useReplayChat.test.ts @@ -4,6 +4,37 @@ import { useReplayChat } from '../src/components/widgets/agent/composables/useRe import type { ConversationRound } from '../src/components/widgets/agent/types/agent'; describe('useReplayChat', () => { + it('restores a direct answer without steps', async () => { + const rounds = ref([]); + const answer = 'VTJ 可视化设计器的主要使用流程'; + const content = JSON.stringify({ + intent: '介绍设计器的使用方法', + contextKeys: [], + safety: 'readonly', + answer + }); + const { loadChatHistory } = useReplayChat( + { + getChats: vi.fn(async () => [ + { + id: 'architect', + agentRole: 'architect', + prompt: '设计器如何使用', + content, + status: 'Success' + } + ]) as any, + setStatus: vi.fn() + }, + rounds + ); + + await loadChatHistory('topic'); + + expect(rounds.value[0].architectAnswer).toBe(answer); + expect(rounds.value[0].architectPlan?.steps).toEqual([]); + }); + it('clears the previous conversation before loading an empty topic', async () => { const rounds = ref([{ id: 'old' }] as ConversationRound[]); const statusText = ref('');