mirror of
https://gitee.com/newgateway/vtj.git
synced 2026-09-03 05:14:03 +08:00
refactor(agent): 优化 architectPlan 解析兼容无 steps 的直接回答
- 移除旧的 tryParsePlan 函数,统一使用 parsePlanOutput 解析内容 - 解析 architectChat.content 时兼容没有 steps 的直接回答场景 - 改进错误处理逻辑,直接使用 parsePlanOutput 返回的错误信息 - 新增测试覆盖无 steps 的直接回答恢复场景,确保行为正确
This commit is contained in:
@@ -35,15 +35,6 @@ function extractIntent(content: string): string {
|
||||
return content.trim();
|
||||
}
|
||||
|
||||
/** 尝试从 content 中解析 JSON 计划 */
|
||||
function tryParsePlan(content: string): PlanResult | null {
|
||||
const parsed = parseJsonObject<PlanResult>(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;
|
||||
|
||||
@@ -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<ConversationRound[]>([]);
|
||||
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('');
|
||||
|
||||
Reference in New Issue
Block a user