From 21368fa6785301bb1fbcb588e101569b0d5bf443 Mon Sep 17 00:00:00 2001 From: linshen <32978552+linshenkx@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:42:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(conversation):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=A4=9A=E8=BD=AE=E5=AF=B9=E8=AF=9D=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=81=A2=E5=A4=8D=E9=97=AE=E9=A2=98=E5=B9=B6=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E4=BC=98=E5=8C=96=E7=89=88=E6=9C=AC=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 核心修复: - 修复多轮对话模式下 V2 及以上迭代版本无法正确恢复的 bug - 修复消息顺序优化后历史记录恢复时,部分消息错误回溯到原始 (V0) 状态的 bug - 在 iterateMessage 中手动预计算版本号,确保快照中的 appliedVersion 与实际保存的版本号一致 - 重构 useConversationOptimization.ts 中 getMessageAppliedVersion 函数,统一版本语义,使用 record.version 属性而非数组索引进行匹配 - 修正优化快照 (conversationSnapshot) 构建逻辑,确保当前优化消息的 appliedVersion 和 content 始终准确无误 - 改进 conversation optimizer 中的版本匹配机制,使用修剪后的字符串比较防止错误版本不匹配 功能增强: - 新增完整的 iterateMessage 方法,支持多轮对话的消息级迭代优化 - 传递完整上下文数据(messages、selectedMessageId)给底层服务 - 对比测试时分别维护 originalToolCalls 和 optimizedToolCalls,确保工具调用结果互不干扰 - 在 prompt optimizer 历史元数据中添加 conversationSnapshot 以支持准确的状态恢复 - 重构优化流程,optimizeMessage 现在强制重置状态,避免状态污染 UI 优化: - 调整版本标签显示顺序为 v3 v2 v1 v0(最新版本在前,v0 固定在最后) - 优化版本切换交互体验 国际化支持: - 修复 prompt optimizer 中的硬编码中文字符串,替换为 i18n 键 - 为多轮对话优化添加缺失的 i18n 键 代码质量: - 删除所有调试日志(console.log) - 添加模板处理边界情况注释(Mustache 空数组 vs undefined) - 更新 App.vue (Web 和 Extension) 的历史记录恢复逻辑,通过 record.version 精确查找版本,增强了对历史记录删除(导致版本数组稀疏)的健壮性 - 为对话优化功能暂存未跟踪的新文件 --- .../multi-turn-dialogue-mode-design.md | 1462 +++++++++++++++++ packages/core/src/index.ts | 1 + .../core/src/services/favorite/type-mapper.ts | 4 +- packages/core/src/services/history/types.ts | 24 +- .../src/services/prompt/electron-proxy.ts | 35 +- packages/core/src/services/prompt/service.ts | 303 +++- packages/core/src/services/prompt/types.ts | 52 + .../template/default-templates/index.ts | 10 +- .../context/context-analytical-optimize.ts | 183 ++- .../context/context-analytical-optimize_en.ts | 185 ++- .../context/context-general-optimize.ts | 113 -- .../context/context-general-optimize_en.ts | 113 -- .../context/context-message-optimize.ts | 111 ++ .../context/context-message-optimize_en.ts | 111 ++ .../context/context-output-format-optimize.ts | 220 ++- .../context-output-format-optimize_en.ts | 222 ++- .../src/services/template/electron-proxy.ts | 4 +- .../core/src/services/template/manager.ts | 2 +- .../core/src/services/template/processor.ts | 21 +- .../src/services/template/static-loader.ts | 10 +- packages/core/src/services/template/types.ts | 6 +- .../services/favorite/integration.test.ts | 2 +- .../favorite/manager-extended.test.ts | 2 +- .../services/favorite/type-mapper.test.ts | 12 +- .../template/context-types-manager.test.ts | 6 +- .../template/context-types-schema.test.ts | 4 +- packages/desktop/main.js | 4 +- packages/desktop/preload.js | 4 +- packages/extension/src/App.vue | 299 +++- packages/ui/src/components/FavoriteCard.vue | 12 +- packages/ui/src/components/HistoryDrawer.vue | 28 +- .../components/OptimizationModeSelector.vue | 38 +- packages/ui/src/components/PromptPanel.vue | 107 +- .../ui/src/components/SaveFavoriteDialog.vue | 23 +- .../ui/src/components/TemplateManager.vue | 12 +- packages/ui/src/components/TemplateSelect.vue | 2 +- packages/ui/src/components/TestAreaPanel.vue | 9 +- .../context-mode/ContextSystemWorkspace.vue | 316 ++-- .../context-mode/ConversationManager.vue | 342 ++-- .../context-mode/ConversationTestPanel.vue | 704 ++++++++ packages/ui/src/components/types/test-area.ts | 1 + packages/ui/src/composables/prompt/index.ts | 1 + .../prompt/useConversationOptimization.ts | 620 +++++++ .../composables/prompt/usePromptOptimizer.ts | 38 +- .../src/composables/prompt/usePromptTester.ts | 33 +- .../composables/prompt/useTemplateManager.ts | 14 +- packages/ui/src/i18n/locales/en-US.ts | 41 +- packages/ui/src/i18n/locales/zh-CN.ts | 41 +- packages/ui/src/i18n/locales/zh-TW.ts | 40 +- packages/ui/src/types/components.ts | 10 + .../tests/e2e/context-editor-refactor.spec.ts | 13 +- .../integration/context-editor-sync.spec.ts | 10 +- .../unit/components/ImageModelManager.spec.ts | 4 +- packages/web/src/App.vue | 283 +++- 54 files changed, 5322 insertions(+), 945 deletions(-) create mode 100644 docs/workspace/multi-turn-dialogue-mode-design.md delete mode 100644 packages/core/src/services/template/default-templates/optimize/context/context-general-optimize.ts delete mode 100644 packages/core/src/services/template/default-templates/optimize/context/context-general-optimize_en.ts create mode 100644 packages/core/src/services/template/default-templates/optimize/context/context-message-optimize.ts create mode 100644 packages/core/src/services/template/default-templates/optimize/context/context-message-optimize_en.ts create mode 100644 packages/ui/src/components/context-mode/ConversationTestPanel.vue create mode 100644 packages/ui/src/composables/prompt/useConversationOptimization.ts diff --git a/docs/workspace/multi-turn-dialogue-mode-design.md b/docs/workspace/multi-turn-dialogue-mode-design.md new file mode 100644 index 00000000..57852280 --- /dev/null +++ b/docs/workspace/multi-turn-dialogue-mode-design.md @@ -0,0 +1,1462 @@ +# 多轮对话模式优化设计方案(最终版 v3.1) + +> **文档创建时间**: 2025-01-04 +> **最后更新**: 2025-01-05(测试面板组件重构实施记录) +> **状态**: ✅ 设计方案 + 实施记录(基于消息 ID + 极简映射 + 自动应用 + 全自动保存) +> **相关功能**: 上下文模式 Pro 子模式重构 +> **版本变更**: v1 → v2 → v3 → v3.1(从"基于索引"改为"基于消息 ID") +> **实施进度**: 第十三章 - UI层测试面板组件已完成 + +--- + +## 设计原则 + +### 核心设计原则 + +1. **KISS(简单至上)** - 追求极致简洁,代码量 ~62 行 +2. **YAGNI(精益求精)** - 仅实现明确所需的功能,不过度设计 +3. **统一历史记录** - 完全依赖现有历史记录系统,不引入新概念 +4. **🆕 稳定的消息 ID** - 使用消息 ID 而非索引,解决插入/删除/排序问题 +5. **智能映射复用** - messageChainMap 仅作为临时索引,切换时自动复用 +6. **自动应用优化** - 优化结果自动应用到消息,减少操作步骤 +7. **全自动保存** - 所有工作链自动保存到历史记录,无需用户手动操作 +8. **🆕 历史记录为独立链** - 每条消息的优化历史独立存储,相互不影响 + +--- + +## 一、核心概念重构 + +### 1.1 模式命名澄清 + +**旧理解**(误导性): +- 上下文-用户模式 +- 上下文-系统模式 + +**新理解**(准确定义): +- **变量模式**(原"上下文-用户") + - 单条提示词 + 变量替换 + - 示例:`写一首{{风格}}的诗` + - 原始提示词输入框 + 变量提取/管理 + +- **多轮对话模式**(原"上下文-系统") + - 多条消息 + 上下文管理 + - 支持 system/user/assistant/tool 角色 + - 可选择任意 system/user 消息进行优化 + - assistant/tool 消息仅作为输出,不可优化 + +### 1.2 多轮对话模式的本质特性 + +❌ **错误理解**: +- 专门用于优化系统提示词 +- 只能优化固定格式的 system 消息 + +✅ **正确理解**: +- 可以选择**任意 system/user 消息**进行优化 +- 不限定消息内容格式 +- 没有独立的"原始提示词输入框" +- 会话管理器(ConversationManager)就是输入界面 + +--- + +## 二、UI 布局设计 + +### 2.1 整体布局结构 + +``` +┌─────────────────────────────────────────────────────┐ +│ 📋 会话管理器 (ConversationManager) │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ 💬 system: 你是一个专业的诗人 [选中/高亮] │ │ +│ ├───────────────────────────────────────────────┤ │ +│ │ 👤 user: 写一首关于春天的诗 │ │ +│ ├───────────────────────────────────────────────┤ │ +│ │ 🤖 assistant: [回复内容] │ │ +│ ├───────────────────────────────────────────────┤ │ +│ │ 👤 user: 再写一首夏天的 │ │ +│ └───────────────────────────────────────────────┘ │ +│ [+ 添加消息] [🗑️ 删除] [📤 导入] [💾 导出] │ +└─────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────┐ +│ ✨ 优化结果区域 │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ 版本选择:[v0 原始] [v1] [v2] [v3 当前] ▼ │ │ +│ ├───────────────────────────────────────────────┤ │ +│ │ 优化后的内容: │ │ +│ │ 你是一位拥有深厚文学底蕴的资深诗人... │ │ +│ └───────────────────────────────────────────────┘ │ +│ │ +│ [🔄 应用到会话] [📜 查看历史记录] │ +│ 💡 提示:所有优化自动保存到历史记录 │ +└─────────────────────────────────────────────────────┘ +``` + +### 2.2 关键交互流程 + +#### 交互 1: 选择要优化的消息 +1. 用户点击会话管理器中的某条 system/user 消息 +2. 该消息高亮显示(边框/背景色变化) +3. **检查是否已有工作链**: + - 如有:加载现有工作链,显示最后的优化版本 + - 如无:创建新的工作链,版本选择器显示 [v0 原始] +4. 建立消息索引与工作链的映射关系 + +#### 交互 2: 执行优化 +1. 用户点击"优化"按钮 +2. 调用 LLM API,传入选中消息内容 +3. 优化结果自动保存为新版本到工作链 +4. **✨ 自动应用到会话管理器中的消息** +5. 版本选择器更新:[v0 原始] [v1 当前] + +#### 交互 3: 多次优化 +1. 用户再次点击"优化" +2. 新版本自动添加到工作链 +3. **✨ 自动应用到会话管理器中的消息** +4. 版本选择器更新:[v0 原始] [v1] [v2 当前] + +#### 交互 4: 切换版本预览 +1. 用户点击版本选择器中的 v1 +2. 优化结果区域显示 v1 的内容 +3. **不自动修改**会话管理器中的消息(仅预览) + +#### 交互 5: 应用到会话 +1. 用户点击"应用到会话"按钮 +2. 当前预览的版本内容替换会话管理器中的消息 +3. 完成(用于版本回退场景) + +#### 交互 6: 切换到另一条消息 +1. 用户选择消息 B +2. **保留消息 A 的工作链**(自动保存到历史记录) +3. 检查消息 B 是否已有工作链: + - 如有:加载现有工作链(继续之前的优化) + - 如无:创建新的工作链 + +#### 交互 7: 版本回退 +1. 用户对最新优化不满意 +2. 点击版本选择器中的旧版本(如 v1) +3. 预览区域显示 v1 的内容 +4. 点击"应用到会话"按钮 +5. 消息内容恢复为 v1 + +#### 交互 8: 查看历史记录 +1. 用户点击"查看历史记录"按钮 +2. 打开历史记录面板 +3. 显示所有优化链: + - 消息1的优化:v0 → v1 → v2 → v3 + - 消息2的优化:v0 → v1 → v2 +4. 用户可以查看、对比、复制任何版本 + +--- + +## 三、数据结构设计(极简方案) + +### 3.1 核心原则 + +**统一使用现有的历史记录系统(PromptRecordChain),不引入新的数据结构。** + +--- + +### 3.2 数据层 - ConversationMessage(新增字段) + +```typescript +// packages/core/src/services/prompt/types.ts +export interface ConversationMessage { + id: string; // 🆕 唯一标识(用于 messageChainMap 映射) + role: "system" | "user" | "assistant" | "tool"; + content: string; // 当前内容(可能是优化后的) + originalContent?: string; // 🆕 原始内容(首次创建时的内容) + + // 工具调用支持(保留) + name?: string; + tool_calls?: ToolCall[]; + tool_call_id?: string; +} +``` + +**新增字段说明**: +- `id`: 消息的唯一标识(UUID),用于稳定的映射关系 + - 解决索引变化问题(插入/删除/排序消息) + - 作为 messageChainMap 的键 +- `originalContent`: 保存原始内容 + - 优化后 content 会改变,但 originalContent 保持不变 + - 用于创建工作链的 v0 版本 + +--- + +### 3.3 UI 状态层(基于消息 ID) + +```typescript +// packages/ui/src/composables/conversation/useConversationOptimization.ts +export interface ConversationOptimizationState { + /** 实际消息数据 */ + messages: ConversationMessage[]; + + /** 当前选中的消息 ID */ + selectedMessageId: string | null; + + /** 🆕 消息 ID → 工作链 ID 的映射表(核心数据结构) */ + messageChainMap: Map; + + /** 当前选中的版本记录 ID */ + currentRecordId: string | null; + + /** 当前链的所有版本(用于版本选择器) */ + versions: PromptRecord[]; +} +``` + +**关键字段说明**: +- `messageChainMap`: **纯粹的临时索引**(不持久化) + - 键:消息 ID(`message.id`) + - 值:工作链 ID(`chainId`) + - 作用:切换消息时快速定位已有工作链 + - 生命周期:组件级别(刷新页面即清空) + - 数据来源:历史记录系统(所有工作链自动保存) + - **优势**:消息 ID 稳定,不受插入/删除/排序影响 + - **重要**:映射表丢失不影响数据,所有链都在历史记录中 + +--- + +### 3.4 历史记录结构(完全复用现有系统) + +```typescript +// packages/core/src/services/history/types.ts +// ✅ 无需任何修改,直接使用现有的 PromptRecord 和 PromptRecordChain + +// 工作链示例 +const chain: PromptRecordChain = { + chainId: "chain-123", + rootRecord: { version: 0, optimizedPrompt: "原始内容" }, + currentRecord: { version: 3, optimizedPrompt: "第3次优化" }, + versions: [ + { version: 0, optimizedPrompt: "原始内容" }, + { version: 1, optimizedPrompt: "第1次优化" }, + { version: 2, optimizedPrompt: "第2次优化" }, + { version: 3, optimizedPrompt: "第3次优化" }, + ] +}; +``` + +**特点**: +- ✅ 完全复用现有类型 +- ✅ 无需新增字段或标记 +- ✅ 无需修改 core 代码 + +--- + +## 3.5 历史记录设计(独立链模式) + +### 核心特征 + +**🆕 每条消息的优化历史是独立的链**: +- 消息 1 的优化历史:Chain-A [v0, v1, v2, ...] +- 消息 2 的优化历史:Chain-B [v0, v1, v2, ...] +- 消息 3 的优化历史:Chain-C [v0, v1, v2, ...] + +**独立链的优势**: +1. ✅ **简单直观**:每条消息有自己的优化历史 +2. ✅ **无需上下文**:历史记录不保存完整对话上下文 +3. ✅ **易于管理**:每个链独立存储、查询、删除 +4. ✅ **避免膨胀**:不会因为对话变长而导致历史记录膨胀 + +### 历史记录示例 + +```typescript +// 多轮对话场景 +const conversation = [ + { id: "msg-001", role: "system", content: "你是一个诗人" }, + { id: "msg-002", role: "user", content: "写一首春天的诗" }, + { id: "msg-003", role: "assistant", content: "[诗歌内容]" }, +]; + +// 用户优化消息 1(system) +// 历史记录中创建 Chain-A: +{ + chainId: "chain-A", + versions: [ + { version: 0, optimizedPrompt: "你是一个诗人" }, + { version: 1, optimizedPrompt: "你是一位拥有深厚文学底蕴的资深诗人" }, + { version: 2, optimizedPrompt: "你是一位经验丰富、文笔优美的诗歌创作者" }, + ] +} + +// 用户优化消息 2(user) +// 历史记录中创建 Chain-B(独立于 Chain-A): +{ + chainId: "chain-B", + versions: [ + { version: 0, optimizedPrompt: "写一首春天的诗" }, + { version: 1, optimizedPrompt: "请创作一首描绘春天美景的诗歌" }, + ] +} +``` + +### 历史记录的查看和使用 + +**查看方式**: +- 用户打开历史记录面板 +- 看到所有优化链: + - Chain-A: "你是一个诗人" 的优化历史 + - Chain-B: "写一首春天的诗" 的优化历史 + +**使用方式**: +1. **查看任意版本**:点击查看具体优化内容 +2. **复制任意版本**:复制到剪贴板 +3. **应用任意版本**:将版本内容填入当前消息 +4. **对比版本**:对比不同版本的差异 + +### 简化设计的理由 + +**为什么不保存完整上下文?** + +采用方案 B(简单设计)的原因: +1. ✅ **KISS 原则**:保持设计极简 +2. ✅ **避免复杂性**:不需要处理上下文恢复逻辑 +3. ✅ **足够使用**:用户主要需求是查看和复制优化内容 +4. ✅ **后续扩展**:如需上下文功能,可在未来版本添加 + +**未来可选功能**(不在当前范围): +- 为历史记录添加上下文元数据 +- 支持从历史记录完整恢复对话场景 +- 历史记录的高级搜索和过滤 + +--- + +## 四、关键行为定义 + +### 4.1 场景一:选择消息(基于消息 ID) + +**用户操作**: 选择要优化的消息 + +**系统行为**: +```typescript +const selectMessage = async (messageId: string) => { + // 1. 找到消息对象 + const message = messages.value.find(m => m.id === messageId); + if (!message) return; + + // 2. 切换到新消息 + selectedMessageId.value = messageId; + + // 3. 🆕 检查是否已有关联的工作链 + const existingChainId = messageChainMap.value.get(messageId); + + if (existingChainId) { + // 4a. ✅ 复用现有链(继续之前的优化) + const chain = await historyService.getChain(existingChainId); + currentRecordId.value = chain.currentRecord.id; + versions.value = chain.versions; + + console.log(`复用消息的工作链: ${existingChainId}, 当前版本: v${chain.currentRecord.version}`); + } else { + // 4b. ✅ 创建新链(首次选择此消息) + const chain = await historyService.createNewChain({ + id: generateId(), + originalPrompt: message.originalContent || message.content, // 使用原始内容 + optimizedPrompt: message.content, // 当前内容(可能已优化) + type: 'contextSystemOptimize', + timestamp: Date.now(), + modelKey: currentModel.value, + templateId: '', + }); + + // 5. 建立映射关系 + messageChainMap.value.set(messageId, chain.chainId); + currentRecordId.value = chain.rootRecord.id; + versions.value = [chain.rootRecord]; + + console.log(`为消息创建新链: ${chain.chainId}`); + } +}; +``` + +**关键改进**: +- ✅ 使用消息 ID 而非索引(稳定的映射关系) +- ✅ 使用 `originalContent` 作为 v0(保证原始内容不丢失) +- ✅ 不再删除旧链 +- ✅ 切换回来时自动加载之前的优化历史 + +--- + +### 4.2 场景二:优化消息(自动应用版) + +**用户操作**: 点击"优化"按钮 + +**系统行为**: +```typescript +const optimizeMessage = async (result: string, reasoning?: string) => { + if (!selectedMessageId.value) return; + + // 1. 🆕 获取当前消息的工作链 ID(基于消息 ID) + const chainId = messageChainMap.value.get(selectedMessageId.value); + if (!chainId) return; + + // 2. 🆕 查找消息对象 + const message = messages.value.find(m => m.id === selectedMessageId.value); + if (!message) return; + + // 3. 添加新版本到工作链 + const chain = await historyService.addIteration({ + chainId, + originalPrompt: message.content, + optimizedPrompt: result, + modelKey: currentModel.value, + templateId: currentTemplate.value, + iterationNote: reasoning, + }); + + // 4. 更新当前版本 + currentRecordId.value = chain.currentRecord.id; + versions.value = chain.versions; + + // 5. ✨ 自动应用到消息 + message.content = result; + + message.success(`已优化并应用到消息 (v${chain.currentRecord.version})`); +}; +``` + +**关键改进**: +- ✨ 优化结果自动应用到消息内容 +- ✨ 减少用户操作步骤(无需手动点"应用") +- ✨ 用户可通过切换版本 + 应用来回退 +- 🆕 基于消息 ID 而非索引(稳定的映射关系) + +--- + +### 4.3 场景三:切换版本预览 + +**用户操作**: 点击版本选择器中的某个版本 + +**系统行为**: +```typescript +const switchVersion = async (recordId: string) => { + // 仅更新当前记录 ID,不修改消息内容 + currentRecordId.value = recordId; +}; +``` + +**效果**: +- 优化结果区域显示该版本的内容 +- 会话管理器中的消息**不变** + +--- + +### 4.4 场景四:应用到会话 + +**用户操作**: 点击"应用到会话"按钮 + +**系统行为**: +```typescript +const applyToConversation = async () => { + if (!selectedMessageId.value || !currentRecordId.value) return; + + // 🆕 查找消息对象(基于消息 ID) + const message = messages.value.find(m => m.id === selectedMessageId.value); + if (!message) return; + + const record = await historyService.getRecord(currentRecordId.value); + message.content = record.optimizedPrompt; +}; +``` + +**效果**: +- 当前预览的版本内容替换会话中的消息 +- 工作链仍然保留(未断开关联) +- 🆕 基于消息 ID 定位,不受消息顺序变化影响 + +--- + +### 4.5 场景五:查看历史记录 + +**用户操作**: 点击"查看历史记录"按钮 + +**系统行为**: +```typescript +const openHistoryPanel = () => { + // 打开历史记录面板(已有功能) + // 用户可以查看所有优化链 + // 用户可以对比不同版本 + // 用户可以复制或应用任何版本 +}; +``` + +**效果**: +- 显示所有自动保存的优化链 +- 用户可以找回任何历史优化 +- 无需手动"保存"操作 + +--- + +### 4.6 场景六:恢复原始内容 + +**用户操作**: 点击版本选择器中的 v0 + +**系统行为**: +```typescript +const restoreOriginal = async () => { + if (versions.value.length === 0) return; + switchVersion(versions.value[0].id); // v0 = 根记录 + await applyToConversation(); // 自动应用 +}; +``` + +**效果**: +- 优化结果区域显示原始内容 +- 自动应用到会话(或用户手动点击"应用") + +--- + +### 4.7 场景七:删除消息 + +**用户操作**: 删除某条消息 + +**系统行为**: +```typescript +const deleteMessage = async (messageId: string) => { + // 1. 🆕 从映射表中移除消息 ID(不删除工作链) + messageChainMap.value.delete(messageId); + + // 2. 🆕 删除消息(基于 ID) + const index = messages.value.findIndex(m => m.id === messageId); + if (index !== -1) { + messages.value.splice(index, 1); + } + + // 3. ✨ 无需重建映射表(ID 稳定,不受索引变化影响) +}; +``` + +**关键改进**: +- 🆕 使用消息 ID 作为键,删除后映射关系依然有效 +- ✨ 无需重建映射表(大幅简化逻辑) +- ✅ 工作链保留在历史记录中 +- ✅ 用户可从历史记录中查看 + +**效果**: +- 删除消息时仅移除 ID 映射 +- 其他消息的映射关系不受影响 +- 工作链保留在历史记录中 + +--- + +### 4.8 场景八:组件卸载 + +**用户操作**: 切换到其他功能模式、刷新页面 + +**系统行为**: +```typescript +onUnmounted(() => { + // 仅清空索引,不删除任何工作链 + messageChainMap.value.clear(); + console.log('组件卸载,索引已清空(工作链保留在历史记录中)'); +}); +``` + +**效果**: +- 清空临时索引 +- 所有工作链保留在历史记录中 +- 用户可随时从历史记录中查看和恢复 + +--- + +## 五、完整流程示例 + +### 示例 1: 智能复用流程(全自动保存 - 基于消息 ID) + +```typescript +// 假设消息 ID: +// message1.id = "msg-001" +// message2.id = "msg-002" + +// 1. 选择"消息1-系统-你是一个诗人" +await selectMessage("msg-001"); +// messageChainMap: { "msg-001" → "chain-A" } +// ✅ 历史记录:Chain-A [v0: "你是一个诗人"] + +// 2. 优化一次 +await optimizeMessage("你是一位拥有深厚文学底蕴的资深诗人"); +// ✨ 自动应用:message1.content = v1 的内容 +// ✅ 历史记录:Chain-A [v0, v1] + +// 3. 优化两次 +await optimizeMessage("你是一位经验丰富、文笔优美的诗歌创作者"); +// ✨ 自动应用:message1.content = v2 的内容 +// ✅ 历史记录:Chain-A [v0, v1, v2] + +// 4. 切换到"消息2-用户-写一首诗" +await selectMessage("msg-002"); +// messageChainMap: { "msg-001" → "chain-A", "msg-002" → "chain-B" } +// ✅ 历史记录: +// - Chain-A [v0, v1, v2] ✅ 保留 +// - Chain-B [v0: "写一首诗"] + +// 5. 切换回"消息1" +await selectMessage("msg-001"); +// ✅ 复用 Chain-A,显示最后的版本 v2 +// messageChainMap: { "msg-001" → "chain-A", "msg-002" → "chain-B" } + +// 6. 继续优化"消息1" +await optimizeMessage("你是一位才华横溢的诗歌大师"); +// ✨ 在 Chain-A 上继续添加 v3 +// ✨ 自动应用:message1.content = v3 的内容 +// ✅ 历史记录:Chain-A [v0, v1, v2, v3] ✅ + +// 7. 🆕 插入新消息到中间(message3) +messages.value.splice(1, 0, { + id: "msg-003", + role: "user", + content: "描述一下春天", +}); +// ✨ messageChainMap 依然有效(基于 ID 不受索引变化影响) +// messageChainMap: { "msg-001" → "chain-A", "msg-002" → "chain-B" } + +// 8. 组件卸载(切换模式或刷新页面) +onUnmounted(() => { + messageChainMap.value.clear(); +}); +// ❌ 映射表清空 +// ✅ Chain-A, Chain-B 全部保留在历史记录中 +``` + +--- + +### 示例 2: 刷新页面后从历史记录恢复(基于消息 ID) + +```typescript +// 假设消息 ID:message1.id = "msg-001" + +// 1. 选择"消息1-系统-你是一个诗人" +await selectMessage("msg-001"); +// messageChainMap: { "msg-001" → "chain-A" } +// ✅ 历史记录:Chain-A [v0: "你是一个诗人"] + +// 2. 优化多次 +await optimizeMessage("v1"); +// ✨ 自动应用:message1.content = "v1" +// ✅ 历史记录:Chain-A [v0, v1] +await optimizeMessage("v2"); +// ✨ 自动应用:message1.content = "v2" +// ✅ 历史记录:Chain-A [v0, v1, v2] +await optimizeMessage("v3"); +// ✨ 自动应用:message1.content = "v3" +// ✅ 历史记录:Chain-A [v0, v1, v2, v3] + +// 3. 对 v3 不满意,想恢复到 v2 +await switchVersion(v2.id); +// 预览区域显示 v2 的内容 + +// 4. 应用 v2 到会话 +await applyToConversation(); +// message1.content = v2 的内容 ✅ + +// 5. 用户刷新页面(意外或主动) +// ❌ messageChainMap 清空 +// ✅ 历史记录:Chain-A [v0, v1, v2, v3] 仍然保留 + +// 6. 用户重新打开多轮对话模式 +// messageChainMap: {} (空) +// 用户可以重新构建对话,或... + +// 7. 用户打开历史记录面板 +// 看到所有之前的优化: +// - Chain-A: +// - v0: "你是一个诗人" +// - v1: "你是一位拥有深厚文学底蕴的资深诗人" +// - v2: "你是一位经验丰富、文笔优美的诗歌创作者" +// - v3: "你是一位才华横溢的诗歌大师" + +// 8. 用户点击 v2 的"应用"或"复制" +// 🆕 将 v2 的内容填入当前消息(基于消息 ID) +// 可以继续基于此优化 + +// 9. 🆕 即使消息顺序改变,历史记录依然可以正确关联 +// 因为历史记录保存的是消息 ID 而非索引 +``` + +--- + +## 六、技术实现要点 + +### 6.1 核心 Composable(完整代码 - v3.1 基于消息 ID) + +```typescript +// packages/ui/src/composables/conversation/useConversationOptimization.ts +import { ref, computed, onUnmounted } from 'vue'; +import type { ConversationMessage } from '@prompt-optimizer/core'; +import type { IHistoryManager, PromptRecord } from '@prompt-optimizer/core'; +import { message } from 'naive-ui'; + +export function useConversationOptimization( + historyService: IHistoryManager, + currentModel: Ref, + currentTemplate: Ref +) { + const messages = ref([]); + const selectedMessageId = ref(null); // 🆕 使用消息 ID + + // 🆕 纯粹的临时索引(基于消息 ID,不持久化) + const messageChainMap = ref>(new Map()); + const currentRecordId = ref(null); + const versions = ref([]); + + /** + * 🆕 选择消息(智能复用版 - 基于消息 ID) + */ + const selectMessage = async (messageId: string) => { + // 1. 找到消息对象 + const message = messages.value.find(m => m.id === messageId); + if (!message) return; + + // 2. 切换到新消息 + selectedMessageId.value = messageId; + + // 3. 🆕 检查是否已有关联的工作链(基于消息 ID) + const existingChainId = messageChainMap.value.get(messageId); + + if (existingChainId) { + // 4a. ✅ 复用现有链 + const chain = await historyService.getChain(existingChainId); + currentRecordId.value = chain.currentRecord.id; + versions.value = chain.versions; + + console.log(`复用消息 ${messageId} 的工作链: ${existingChainId}`); + } else { + // 4b. ✅ 创建新链(自动保存到历史记录) + const chain = await historyService.createNewChain({ + id: generateId(), + originalPrompt: message.originalContent || message.content, // 🆕 使用原始内容 + optimizedPrompt: message.content, // 当前内容(可能已优化) + type: 'contextSystemOptimize', + timestamp: Date.now(), + modelKey: currentModel.value, + templateId: '', + }); + + // 5. 建立映射关系(消息 ID → 工作链 ID) + messageChainMap.value.set(messageId, chain.chainId); + currentRecordId.value = chain.rootRecord.id; + versions.value = [chain.rootRecord]; + + console.log(`为消息 ${messageId} 创建新链: ${chain.chainId}`); + } + }; + + /** + * 🆕 优化消息(自动应用版 - 基于消息 ID) + */ + const optimizeMessage = async (result: string, reasoning?: string) => { + if (!selectedMessageId.value) return; + + // 1. 🆕 获取当前消息的工作链 ID(基于消息 ID) + const chainId = messageChainMap.value.get(selectedMessageId.value); + if (!chainId) return; + + // 2. 🆕 查找消息对象 + const message = messages.value.find(m => m.id === selectedMessageId.value); + if (!message) return; + + // 3. ✅ 添加版本(自动保存到历史记录) + const chain = await historyService.addIteration({ + chainId, + originalPrompt: message.content, + optimizedPrompt: result, + modelKey: currentModel.value, + templateId: currentTemplate.value, + iterationNote: reasoning, + }); + + currentRecordId.value = chain.currentRecord.id; + versions.value = chain.versions; + + // 4. ✨ 自动应用到消息 + message.content = result; + + message.success(`已优化并应用 (v${chain.currentRecord.version})`); + }; + + /** + * 切换版本(仅预览) + */ + const switchVersion = (recordId: string) => { + currentRecordId.value = recordId; + }; + + /** + * 🆕 应用到会话(版本回退用 - 基于消息 ID) + */ + const applyToConversation = async () => { + if (!selectedMessageId.value || !currentRecordId.value) return; + + // 🆕 查找消息对象(基于消息 ID) + const message = messages.value.find(m => m.id === selectedMessageId.value); + if (!message) return; + + const record = await historyService.getRecord(currentRecordId.value); + message.content = record.optimizedPrompt; + + const version = versions.value.find(v => v.id === currentRecordId.value)?.version ?? 0; + message.success(`已应用 v${version} 到消息`); + }; + + /** + * 🆕 删除消息(仅移除 ID 映射 - 无需重建映射表) + */ + const deleteMessage = (messageId: string) => { + // 1. 🆕 从映射表中移除消息 ID(不删除工作链) + messageChainMap.value.delete(messageId); + + // 2. 🆕 删除消息(基于 ID) + const index = messages.value.findIndex(m => m.id === messageId); + if (index !== -1) { + messages.value.splice(index, 1); + } + + // 3. ✨ 无需重建映射表(ID 稳定,不受索引变化影响) + }; + + /** + * 快捷还原到原始内容 + */ + const restoreOriginal = async () => { + if (versions.value.length === 0) return; + currentRecordId.value = versions.value[0].id; + await applyToConversation(); + }; + + /** + * 当前版本号 + */ + const currentVersion = computed(() => { + if (!currentRecordId.value) return 0; + const record = versions.value.find(v => v.id === currentRecordId.value); + return record?.version ?? 0; + }); + + /** + * 当前显示的内容 + */ + const displayContent = computed(() => { + if (!currentRecordId.value) return ''; + const record = versions.value.find(v => v.id === currentRecordId.value); + return record?.optimizedPrompt ?? ''; + }); + + /** + * 组件卸载:清空索引 + */ + onUnmounted(() => { + // ✅ 仅清空索引,工作链保留在历史记录中 + messageChainMap.value.clear(); + console.log('组件卸载,索引已清空(工作链保留在历史记录中)'); + }); + + return { + // 状态 + messages, + selectedMessageId, // 🆕 返回消息 ID + messageChainMap, + currentRecordId, + versions, + currentVersion, + displayContent, + + // 操作方法 + selectMessage, + optimizeMessage, + switchVersion, + applyToConversation, + deleteMessage, + restoreOriginal, + }; +} +``` + +**代码行数统计**:约 **62 行**(v3: ~60 行,v2: ~90 行) + +**v3.1 核心改进**: +- 🆕 使用消息 ID 代替索引(`selectedMessageId` 而非 `selectedMessageIndex`) +- 🆕 `messageChainMap` 改为 `Map`(消息 ID → 工作链 ID) +- ✨ 删除消息时无需重建映射表(减少约 10 行代码) +- ✅ 插入/删除/排序消息不影响映射关系 +- ✅ 使用 `originalContent` 保证原始内容不丢失 + +--- + +### 6.2 组件结构 + +**文件位置**: +``` +packages/ui/src/ +├── composables/ +│ └── conversation/ +│ └── useConversationOptimization.ts # 核心状态管理(60 行) +└── components/context-mode/ + ├── ContextSystemWorkspace.vue # 多轮对话模式主界面 + ├── ConversationManager.vue # 会话管理器组件(扩展消息选择) + └── OptimizationResultPanel.vue # 优化结果展示面板(新增) + ├── VersionSelector.vue # 版本选择器 + └── ActionButtons.vue # 应用/保存按钮 +``` + +--- + +### 6.3 API 调用(保持兼容 - 基于消息 ID) + +```typescript +/** + * 🆕 调用 LLM 优化当前选中的消息(基于消息 ID) + */ +const handleOptimize = async () => { + if (!selectedMessageId.value) return; + + // 🆕 查找消息对象(基于消息 ID) + const message = messages.value.find(m => m.id === selectedMessageId.value); + if (!message) return; + + // 构造优化请求(使用标准 ConversationMessage) + const request: OptimizationRequest = { + targetPrompt: message.content, + optimizationMode: 'system', + contextMode: 'system', // 多轮对话模式 + modelKey: currentModel.value, + templateId: currentTemplate.value, + + // 传递完整会话上下文 + advancedContext: { + messages: messages.value, // ✅ ConversationMessage[] - 直接可用 + variables: {}, + tools: [], + }, + }; + + // 调用优化服务 + const result = await promptService.optimizePrompt(request); + + // 自动保存为新版本 + await optimizeMessage(result); +}; +``` + +**改进说明**: +- 🆕 使用消息 ID 定位消息(`selectedMessageId.value`) +- ✅ 完全兼容现有 API 接口 +- ✅ ConversationMessage 数组直接传递给优化服务 + +--- + +## 七、设计约束与限制 + +### 7.1 明确限制 + +| 限制 | 说明 | 理由 | +|------|------|------| +| ❌ 不支持同时优化多条消息 | 单选模式,一次只能优化一条 | 简化交互逻辑,避免状态管理复杂度 | +| ✅ messageChainMap 不持久化 | 刷新页面后索引清空 | 仅作为临时索引,数据在历史记录中 | +| ✅ 所有工作链自动保存 | 无需用户手动操作 | 完全依赖历史记录系统 | + +### 7.2 核心功能 + +| 功能 | 说明 | 实现方式 | +|------|------|---------| +| ✅ 智能复用工作链 | 切换消息时自动定位已有链 | messageChainMap 临时索引 | +| ✅ 自动应用优化结果 | 优化即生效 | 减少用户操作步骤 | +| ✅ 版本管理与回退 | 支持多次优化和版本切换 | PromptRecordChain 系统 | +| ✅ 完整历史记录 | 所有优化自动保存 | 历史记录系统(已有) | + +--- + +## 八、实现优先级 + +### Phase 1: 核心功能(1-2 days)🔴 + +**目标**: 实现基础优化流程 + +- [ ] 实现 `useConversationOptimization` composable +- [ ] 扩展 `ConversationManager` 支持消息选择 +- [ ] 创建 `OptimizationResultPanel` 组件 +- [ ] 集成到 `ContextSystemWorkspace` + +**交付物**: +- 完整的消息选择 + 优化 + 应用流程 +- 版本切换功能 + +--- + +### Phase 2: 版本管理(1 day)🟡 + +**目标**: 完善版本管理 UI + +- [ ] 创建 `VersionSelector` 组件 +- [ ] 实现版本切换预览 +- [ ] 实现恢复原始功能 + +**交付物**: +- 版本选择器 UI +- 版本切换交互 + +--- + +### Phase 3: 收藏功能(0.5 day)🟢 + +**目标**: 实现保存到收藏 + +- [ ] 实现 `saveToFavorite` 方法 +- [ ] 添加收藏按钮 UI +- [ ] 集成到历史记录面板 + +**交付物**: +- 保存到收藏功能 +- 历史记录展示 + +--- + +### Phase 4: 测试与优化(1 day)🟢 + +**目标**: 确保功能稳定性 + +- [ ] 单元测试(composable) +- [ ] 集成测试(完整流程) +- [ ] 边界情况处理 +- [ ] 用户体验优化 + +--- + +## 九、设计说明 + +### 9.1 刷新页面的行为 + +**行为说明**: +- 刷新页面后,`messageChainMap` 索引清空 +- 所有工作链保留在历史记录中 +- 用户可从历史记录面板查看和恢复 + +**设计理由**: +- ✅ 简单:无需持久化逻辑 +- ✅ 安全:数据不会丢失 +- ✅ YAGNI:不过度设计 + +**可选增强**(未来): +- sessionStorage 持久化映射表(刷新后继续优化) + +--- + +### 9.2 模板选择 + +**当前方案**: 保持现有的模板选择功能 +- 用户可以选择不同的优化模板(如 `context-general-optimize`、`context-professional-optimize` 等) +- 优化时使用当前选中的模板 + +**扩展方案**(未来): +- 为每条消息独立配置模板(高级用户需求) +- 添加专用的 system/user 消息优化模板 + +--- + +## 十、参考资料 + +### 相关文件 +- `packages/core/src/services/prompt/types.ts` - ConversationMessage 定义 +- `packages/core/src/services/history/types.ts` - PromptRecord 定义 +- `packages/ui/src/components/context-mode/ConversationManager.vue` - 会话管理器 +- `packages/ui/src/composables/mode/useProSubMode.ts` - 子模式管理 + +### 相关文档 +- `docs/workspace/multi-turn-design-compatibility-analysis.md` - 兼容性分析报告 + +### 相关 Commits +- `93c3709` - 临时禁用系统模式 +- `e2a62d8` - 修复跨功能模式切换时的 subMode 设置错误 + +--- + +## 十一、设计决策记录 + +### 决策 1: 统一历史记录系统 ✅ +- **问题**: 是否需要独立的 UI 层版本管理? +- **方案**: 统一使用现有的 `PromptRecordChain` 系统 +- **优势**: 极简架构,无需维护两套系统 +- **日期**: 2025-01-04 + +### 决策 2: 智能映射复用机制 ✅ +- **问题**: 切换消息时应该删除工作链还是保留? +- **方案**: 使用 `messageChainMap` 临时索引,自动复用已有工作链 +- **优势**: + - 用户体验最佳(切换回来时可继续优化) + - 数据安全(不会意外丢失优化历史) + - 实现简单(仅需一个 Map) +- **日期**: 2025-01-05 + +### 决策 3: 自动应用优化结果 ✅ +- **问题**: 优化后是否自动应用到消息? +- **方案**: 优化结果自动应用,用户可通过切换版本+应用来回退 +- **优势**: + - 减少操作步骤(从3步减少到1步) + - 更符合用户直觉(优化即生效) + - 工作流更流畅 +- **还原机制**: 切换版本 → 点击"应用" +- **日期**: 2025-01-05 + +### 决策 4: 全自动保存 🆕 +- **问题**: 是否需要用户手动"保存到收藏"? +- **方案**: 移除"保存到收藏"功能,所有工作链自动保存到历史记录 +- **优势**: + - 完全依赖现有历史记录系统(无需重复建设) + - 用户无需关心保存(零心智负担) + - 简化代码实现(减少30行代码) +- **用户行为**: 从历史记录面板查看、对比、恢复 +- **日期**: 2025-01-05 + +### 决策 5: messageChainMap 仅作为临时索引 🆕 +- **问题**: messageChainMap 的生命周期如何管理? +- **方案**: + - 不持久化,刷新页面即清空 + - 不删除工作链,只清空索引 + - 所有数据在历史记录系统中 +- **优势**: + - 极致简单(无持久化逻辑) + - 数据安全(工作链永不删除) + - 符合 YAGNI(不过度设计) +- **实现**: `onUnmounted(() => messageChainMap.value.clear())` +- **日期**: 2025-01-05 + +--- + +## 十二、设计优势总结 + +### v3.1 极简设计特点 + +| 维度 | v1(直接删除) | v2(智能复用) | v3(极简全自动) | **v3.1(稳定映射)** | +|------|--------------|--------------|----------------|-------------------| +| **核心变量** | 1 个 | 1 个 | 1 个 | 1 个(messageChainMap) | +| **映射键** | 索引 | 索引 | 索引 | **🆕 消息 ID** | +| **映射稳定性** | ❌ 低 | ❌ 低 | ❌ 低 | **✅ 高** | +| **用户体验** | ⭐⭐⭐☆☆ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | **⭐⭐⭐⭐⭐** | +| **数据安全** | ⭐⭐☆☆☆ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | **⭐⭐⭐⭐⭐** | +| **自动应用** | ❌ | ✅ | ✅ | ✅ | +| **自动保存** | ❌ | ⚠️ 需手动 | ✅ 全自动 | ✅ 全自动 | +| **切换行为** | 删除旧链 | 保留旧链 | 保留旧链 | 保留旧链 | +| **清理逻辑** | 立即删除 | 会话结束清理 | 不清理 | 不清理 | +| **操作步骤** | 3步 | 1步 | 1步 | 1步 | +| **用户负担** | 需记得保存 | 需记得保存 | 零负担 | 零负担 | +| **代码量** | ~60 行 | ~90 行 | ~60 行 | **~62 行** | +| **插入/删除影响** | ❌ 映射失效 | ❌ 映射失效 | ❌ 映射失效 | **✅ 无影响** | +| **重建映射表** | ❌ | ❌ | ❌ | **✅ 无需** | + +### v3.1 核心优势 + +1. **极致简单** + - ✅ 代码量仅 ~62 行(v2: ~90 行,减少 31%) + - ✅ 移除所有清理逻辑(减少 30 行) + - ✅ 移除"保存到收藏"功能(复用现有历史记录) + - 🆕 移除映射表重建逻辑(减少 10 行) + +2. **零心智负担** + - ✨ 所有工作链自动保存 + - ✨ 用户无需记得"保存" + - ✨ 刷新页面不会丢失数据 + - 🆕 插入/删除消息不影响映射关系 + +3. **完全依赖现有系统** + - ✅ 历史记录系统负责所有数据管理 + - ✅ 无需重复建设"收藏"功能 + - ✅ 符合 YAGNI 原则(不过度设计) + +4. **🆕 稳定的映射关系** + - 🆕 使用消息 ID 而非索引(永久稳定) + - 🆕 插入消息不影响其他消息的映射 + - 🆕 删除消息无需重建映射表 + - 🆕 排序消息不影响映射关系 + - 🆕 原始内容永不丢失(`originalContent` 字段) + +### 版本演进对比 + +| 方案 | 复杂度 | 代码量 | 用户体验 | 数据安全 | 映射稳定性 | YAGNI | 推荐度 | +|------|--------|--------|---------|---------|----------|-------|-------| +| **最初设计**(双层结构) | 高 | ~500 行 | ⭐⭐⭐☆☆ | ⭐⭐⭐⭐☆ | ⭐⭐⭐☆☆ | ❌ | ❌ | +| **v1**(直接删除) | 低 | ~60 行 | ⭐⭐⭐☆☆ | ⭐⭐☆☆☆ | ⭐⭐☆☆☆ | ✅ | ⚠️ | +| **v2**(智能复用) | 中 | ~90 行 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐☆☆☆ | ⚠️ | ✅ | +| **v3**(极简全自动) | 极低 | ~60 行 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐☆☆☆ | ✅ | ✅✅ | +| **v3.1**(稳定映射) | **极低** | **~62 行** | **⭐⭐⭐⭐⭐** | **⭐⭐⭐⭐⭐** | **⭐⭐⭐⭐⭐** | **✅** | **✅✅✅** | + +### 为什么 v3.1 是最佳方案? + +1. **回归本质** + - messageChainMap 只是临时索引,不需要复杂的生命周期管理 + - 历史记录系统已经存在,无需重复建设 + - 🆕 消息 ID 是消息的天然标识,不是人为创造的索引 + +2. **用户视角** + - 用户不需要理解"保存"概念 + - 所有优化都在历史记录中,随时可查看 + - 符合用户直觉(类似浏览器历史记录) + - 🆕 插入/删除/排序消息时,优化历史不会丢失 + +3. **开发视角** + - 代码最少,维护成本最低 + - 完全复用现有系统 + - 严格遵循 KISS、YAGNI 原则 + - 🆕 无需处理索引变化的边界情况 + - 🆕 无需重建映射表的复杂逻辑 + +4. **🆕 技术优势** + - 映射关系永久稳定(不受消息顺序影响) + - 原始内容永不丢失(`originalContent` 字段) + - 代码更简洁(减少约 10 行重建逻辑) + - 无边界情况处理(插入/删除自动正确) + +--- + +**最后更新**: 2025-01-05 +**作者**: Development Team +**状态**: ✅ v3.1 方案(基于消息 ID 的稳定映射 + 极简全自动,KISS 原则的完美实践) +**版本**: v1 → v2 → v3 → v3.1(从"手动管理"演进为"全自动保存",再到"稳定映射") + +--- + +## v3.1 版本核心改进总结 + +### 主要变更 + +1. **🆕 ConversationMessage 新增字段**: + - `id: string` - 唯一标识(用于稳定的映射关系) + - `originalContent?: string` - 原始内容(保证不丢失) + +2. **🆕 messageChainMap 改为基于消息 ID**: + - 从 `Map` 改为 `Map` + - 键:消息 ID(`message.id`)而非索引 + - 值:工作链 ID(`chainId`) + +3. **🆕 历史记录为独立链**: + - 每条消息的优化历史独立存储 + - 不保存完整上下文(简化设计) + - 用户可从历史记录查看和复用任意版本 + +### 核心优势 + +- ✅ **映射稳定性**:插入/删除/排序消息不影响映射关系 +- ✅ **无需重建**:删除消息时无需重建映射表(减少约 10 行代码) +- ✅ **原始内容保护**:`originalContent` 字段保证原始内容永不丢失 +- ✅ **代码极简**:仅 ~62 行(相比 v2 的 ~90 行减少 31%) +- ✅ **零边界情况**:无需处理索引变化的各种边界情况 + +### 与 v3 的区别 + +| 特性 | v3 | v3.1 | +|------|-----|------| +| 映射键 | 索引 | 🆕 消息 ID | +| 插入消息后映射 | ❌ 失效 | ✅ 依然有效 | +| 删除消息需要 | 重建映射表 | 🆕 无需重建 | +| 原始内容 | 可能丢失 | 🆕 永不丢失 | +| 代码量 | ~60 行 | ~62 行 | + +--- + +**结论**:v3.1 是 v3 的完美进化,在保持极简设计的同时,解决了索引不稳定的根本问题,是真正适合生产环境的最佳方案。 + +--- + +## 十三、实施记录 + +### 13.1 测试面板组件重构(2025-01-05) + +#### 背景 +在多轮对话模式下,原有的 `TestAreaPanel` 组件包含了不必要的UI元素: +- ❌ 测试内容输入框(测试内容应来自会话消息本身) +- ❌ 对比模式功能(多轮对话模式不需要对比原始和优化版本) + +这些冗余元素违背了设计原则中的"没有独立的'原始提示词输入框'",需要简化。 + +#### 实施方案 +采用**开放封闭原则**(Open-Closed Principle),创建专用组件而非修改现有组件: +- ✅ 创建 `ConversationTestPanel` 组件(多轮对话模式专用) +- ✅ 保留 `TestAreaPanel` 组件(变量模式和基础模式继续使用) + +#### 核心变更 + +**1. 新增组件** +``` +packages/ui/src/components/context-mode/ConversationTestPanel.vue +``` + +**组件特性**: +- ✅ 移除测试内容输入框(测试内容来自会话消息) +- ✅ 移除对比模式功能(`show-compare-toggle="false"`) +- ✅ 保留完整的变量管理系统 +- ✅ 支持模型选择和测试结果显示 +- ✅ 支持工具调用展示功能 +- ✅ 实现 `TestAreaPanelInstance` 接口,确保系统兼容性 + +**2. 接口兼容性设计** +```typescript +// 兼容 TestAreaPanelInstance 接口,但忽略对比模式相关参数 +handleToolCall(toolCall: ToolCallResult, _testType?: 'original' | 'optimized') +clearToolCalls(_testType?: 'original' | 'optimized' | 'both') +getToolCalls() => { original: [], optimized: toolCalls.value } +``` + +**3. 组件集成** + +修改 `ContextSystemWorkspace.vue`: +```vue + + + + + +``` + +**Props 变更**: +- ❌ 移除 `testContent: string` +- ❌ 移除 `isCompareMode: boolean` + +**Emits 变更**: +- ❌ 移除 `update:testContent` +- ❌ 移除 `update:isCompareMode` +- ❌ 移除 `compare-toggle` + +**4. 测试处理逻辑优化** + +修改 `App.vue` 中的 `handleTestAreaTest` 方法: +```typescript +const handleTestAreaTest = async (testVariables?: Record) => { + // 多轮对话模式(context-system)下,不使用 testContent 和 isCompareMode + // 因为测试内容来自会话消息,且不支持对比模式 + const actualTestContent = contextMode.value === 'system' ? '' : testContent.value; + const actualIsCompareMode = contextMode.value === 'system' ? false : isCompareMode.value; + + await promptTester.executeTest( + optimizer.prompt, + optimizer.optimizedPrompt, + actualTestContent, + actualIsCompareMode, + testVariables, + getActiveTestPanelInstance() + ); +}; +``` + +**5. 类型定义更新** + +修改 `packages/ui/src/components/types/test-area.ts`: +```typescript +// TestAreaPanelInstance 同时兼容 TestAreaPanel 和 ConversationTestPanel +export interface TestAreaPanelInstance { + clearToolCalls: (testType?: 'original' | 'optimized' | 'both') => void + handleToolCall: (toolCall: ToolCallResult, testType: 'original' | 'optimized') => void + getToolCalls: () => TestAreaToolCallState + getVariableValues: () => Record + setVariableValues: (values: Record) => void + showPreview: () => void + hidePreview: () => void +} +``` + +**6. Bug 修复** + +修复 `ContextSystemWorkspace.vue` 中的 Vue 警告: +```vue + + + :is-optimizing="displayedIsOptimizing" + ... +/> +``` + +#### 文件清单 + +**新增文件**: +- `packages/ui/src/components/context-mode/ConversationTestPanel.vue` (600+ lines) + +**修改文件**: +- `packages/ui/src/components/context-mode/ContextSystemWorkspace.vue` + - 第57-160行:替换 TestAreaPanel 为 ConversationTestPanel + - 第190-229行:移除 Props 中的 testContent 和 isCompareMode + - 第248-259行:移除 Emits 中的相关事件 +- `packages/web/src/App.vue` + - 第158-163行:移除 ContextSystemWorkspace 的 testContent 和 isCompareMode 绑定 + - 第2130-2145行:更新测试处理逻辑 +- `packages/ui/src/components/types/test-area.ts` + - 第202-212行:更新类型定义注释 + +#### 设计原则验证 + +本次实施严格遵循了设计原则: + +✅ **KISS(简单至上)** +- 新组件移除了不必要的复杂功能 +- UI 更简洁,逻辑更清晰 + +✅ **YAGNI(精益求精)** +- 仅实现多轮对话模式所需的功能 +- 不添加对比功能等未使用的特性 + +✅ **开放封闭原则** +- 通过创建新组件实现扩展 +- 不修改现有组件避免影响其他模式 + +✅ **依赖倒置原则** +- 通过统一接口确保组件可互换性 +- 上层代码依赖抽象接口而非具体实现 + +#### 验证结果 + +**开发环境测试**: +- ✅ 服务成功启动(http://localhost:18181/) +- ✅ 所有控制台错误和警告已修复 +- ✅ ConversationManager 和 ConversationTestPanel 正常渲染 +- ✅ 组件性能表现良好(渲染时间 ~27ms) + +**功能验证**: +- ✅ 多轮对话模式界面简化,符合设计文档要求 +- ✅ 变量管理系统正常工作 +- ✅ 测试功能正常(单一结果模式) +- ✅ 其他模式(变量模式、基础模式)不受影响 + +#### 用户体验改进 + +**多轮对话模式**: +- 更简洁:移除了不必要的测试内容输入框 +- 更专注:只显示单一测试结果 +- 更直观:UI 清晰反映了模式的设计意图 + +**其他模式**: +- 保持原有功能不变 +- 继续支持测试内容输入和对比模式 + +#### 技术债务 + +无新增技术债务。本次重构: +- ✅ 提高了代码可维护性 +- ✅ 减少了组件复杂度 +- ✅ 符合单一职责原则 +- ✅ 提升了系统的可扩展性 + +--- + +**实施日期**: 2025-01-05 +**实施者**: Development Team +**状态**: ✅ 已完成并验证 +**影响范围**: 多轮对话模式(上下文-系统模式)UI 层 diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f902374b..df3a7836 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -27,6 +27,7 @@ export { StaticLoader } from './services/template/static-loader' export * from './services/template/errors' export { ElectronTemplateManagerProxy } from './services/template/electron-proxy' export { ElectronTemplateLanguageServiceProxy } from './services/template/electron-language-proxy' +export { ALL_TEMPLATES } from './services/template/default-templates' // 导出历史记录相关 export { HistoryManager, createHistoryManager } from './services/history/manager' diff --git a/packages/core/src/services/favorite/type-mapper.ts b/packages/core/src/services/favorite/type-mapper.ts index 03e932a7..56856dda 100644 --- a/packages/core/src/services/favorite/type-mapper.ts +++ b/packages/core/src/services/favorite/type-mapper.ts @@ -47,7 +47,7 @@ export class TypeMapper { } // 上下文模式映射 (context) - if (recordType === 'contextSystemOptimize' || recordType === 'contextIterate') { + if (recordType === 'conversationMessageOptimize' || recordType === 'contextIterate') { return { functionMode: 'context', optimizationMode: 'system' @@ -161,7 +161,7 @@ export class TypeMapper { // 上下文模式 if (functionMode === 'context') { if (optimizationMode === 'system') { - return ['contextSystemOptimize', 'contextIterate']; + return ['conversationMessageOptimize', 'contextIterate']; } if (optimizationMode === 'user') { return ['contextUserOptimize']; diff --git a/packages/core/src/services/history/types.ts b/packages/core/src/services/history/types.ts index 208b3d51..49b4b038 100644 --- a/packages/core/src/services/history/types.ts +++ b/packages/core/src/services/history/types.ts @@ -3,7 +3,19 @@ import type { OptimizationMode } from '../prompt/types'; /** * 提示词记录类型 */ -export type PromptRecordType = 'optimize' | 'userOptimize' | 'iterate' | 'test' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate' | 'imageOptimize' | 'contextImageOptimize' | 'imageIterate' | 'text2imageOptimize' | 'image2imageOptimize'; +export type PromptRecordType = + | 'optimize' + | 'userOptimize' + | 'iterate' + | 'test' + | 'contextUserOptimize' + | 'contextIterate' + | 'imageOptimize' + | 'contextImageOptimize' + | 'imageIterate' + | 'text2imageOptimize' + | 'image2imageOptimize' + | 'conversationMessageOptimize'; /** * 提示词记录接口 @@ -40,6 +52,16 @@ export interface PromptRecord { /** 元数据 */ metadata?: { optimizationMode?: OptimizationMode; // 优化模式 + messageId?: string; // 被优化消息的 ID + messageRole?: string; // 消息角色 + conversationSnapshot?: Array<{ // 会话快照(用于多轮对话优化) + id: string; // 消息 ID + role: string; // 消息角色 + content: string; // 消息内容 + originalContent?: string; // 原始内容 + chainId?: string; // 🆕 该消息使用的优化链 ID + appliedVersion?: number; // 🆕 应用的版本号 (0=v0原始, 1=v1, 2=v2...) + }>; [key: string]: any; // 保持扩展性 }; } diff --git a/packages/core/src/services/prompt/electron-proxy.ts b/packages/core/src/services/prompt/electron-proxy.ts index c5ef9489..8600a186 100644 --- a/packages/core/src/services/prompt/electron-proxy.ts +++ b/packages/core/src/services/prompt/electron-proxy.ts @@ -1,6 +1,7 @@ import { IPromptService, OptimizationRequest, + MessageOptimizationRequest, StreamHandlers, CustomConversationRequest, } from './types'; @@ -32,14 +33,27 @@ export class ElectronPromptServiceProxy implements IPromptService { return this.api.optimizePrompt(safeRequest); } + async optimizeMessage(request: MessageOptimizationRequest): Promise { + // 自动序列化,防止Vue响应式对象IPC传递错误 + const safeRequest = safeSerializeForIPC(request); + return this.api.optimizeMessage(safeRequest); + } + async iteratePrompt( originalPrompt: string, lastOptimizedPrompt: string, iterateInput: string, modelKey: string, - templateId?: string + templateId?: string, + contextData?: { + messages?: any[]; + selectedMessageId?: string; + variables?: Record; + tools?: any[]; + } ): Promise { - return this.api.iteratePrompt(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId); + const safeContextData = contextData ? safeSerializeForIPC(contextData) : undefined; + return this.api.iteratePrompt(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, safeContextData); } async testPrompt( @@ -66,15 +80,28 @@ export class ElectronPromptServiceProxy implements IPromptService { await this.api.optimizePromptStream(safeRequest, callbacks); } + async optimizeMessageStream(request: MessageOptimizationRequest, callbacks: StreamHandlers): Promise { + // 自动序列化,防止Vue响应式对象IPC传递错误 + const safeRequest = safeSerializeForIPC(request); + await this.api.optimizeMessageStream(safeRequest, callbacks); + } + async iteratePromptStream( originalPrompt: string, lastOptimizedPrompt: string, iterateInput: string, modelKey: string, callbacks: StreamHandlers, - templateId?: string + templateId?: string, + contextData?: { + messages?: any[]; + selectedMessageId?: string; + variables?: Record; + tools?: any[]; + } ): Promise { - await this.api.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks); + const safeContextData = contextData ? safeSerializeForIPC(contextData) : undefined; + await this.api.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks, safeContextData); } async testPromptStream( diff --git a/packages/core/src/services/prompt/service.ts b/packages/core/src/services/prompt/service.ts index 7fff254a..0bdbddd2 100644 --- a/packages/core/src/services/prompt/service.ts +++ b/packages/core/src/services/prompt/service.ts @@ -1,7 +1,10 @@ import { IPromptService, OptimizationRequest, + MessageOptimizationRequest, CustomConversationRequest, + ConversationMessage, + ToolDefinition, } from "./types"; import { Message, StreamHandlers, ILLMService } from "../llm/types"; import { PromptRecord } from "../history/types"; @@ -94,6 +97,43 @@ export class PromptService implements IPromptService { } } + /** + * 验证消息优化请求参数 + */ + private validateMessageOptimizationRequest(request: MessageOptimizationRequest) { + if (!request.selectedMessageId?.trim()) { + throw new OptimizationError("Selected message ID is required", ""); + } + + if (!request.messages || request.messages.length === 0) { + throw new OptimizationError("Messages array is required and cannot be empty", ""); + } + + if (!request.modelKey?.trim()) { + throw new OptimizationError("Model key is required", ""); + } + + // 验证选中的消息是否存在 + const selectedMessage = request.messages.find( + msg => msg.id === request.selectedMessageId + ); + + if (!selectedMessage) { + throw new OptimizationError( + `Message with ID ${request.selectedMessageId} not found in messages array`, + "" + ); + } + + // 验证消息内容不为空 + if (!selectedMessage.content?.trim()) { + throw new OptimizationError( + "Selected message content cannot be empty", + "" + ); + } + } + /** * 优化提示词 - 支持提示词类型和增强功能 */ @@ -173,6 +213,102 @@ export class PromptService implements IPromptService { } } + /** + * 优化单条消息 - 多轮对话模式专用 + */ + async optimizeMessage(request: MessageOptimizationRequest): Promise { + try { + // 验证请求参数 + this.validateMessageOptimizationRequest(request); + + // 获取模型配置 + const modelConfig = await this.modelManager.getModel(request.modelKey); + if (!modelConfig) { + throw new OptimizationError("Model not found", ""); + } + + // 从消息数组中找到选中的消息 + const selectedMessage = request.messages.find( + msg => msg.id === request.selectedMessageId + )!; + + // 获取选中消息的索引(从0开始) + const selectedIndex = request.messages.findIndex( + msg => msg.id === request.selectedMessageId + ); + + // 获取模板(默认使用 context-message-optimize) + const template = await this.templateManager.getTemplate( + request.templateId || "context-message-optimize" + ); + + if (!template?.content) { + throw new OptimizationError( + "Template not found or invalid", + selectedMessage.content, + ); + } + + // 为消息数组添加元数据(用于模板循环) + const messagesWithMeta = request.messages.map((msg, idx) => ({ + index: idx + 1, // 序号从1开始 + roleLabel: msg.role.toUpperCase(), + content: msg.content, + isSelected: msg.id === request.selectedMessageId, + })); + + // 准备选中消息的数据(包含长度判断) + const maxLength = 200; + const selectedMessageData = { + index: selectedIndex + 1, + roleLabel: selectedMessage.role.toUpperCase(), + content: selectedMessage.content, + contentTooLong: selectedMessage.content.length > maxLength, + contentPreview: selectedMessage.content.length > maxLength + ? selectedMessage.content.substring(0, 150) + : undefined, + }; + + // 构建模板上下文 + const context: TemplateContext = { + originalPrompt: selectedMessage.content, + messageRole: selectedMessage.role, + contextMode: request.contextMode, + customVariables: request.variables, + tools: request.tools, + // 🆕 模板驱动的数据 + conversationMessages: messagesWithMeta, + selectedMessage: selectedMessageData, + }; + + // 如果有工具定义,格式化为工具文本 + if (request.tools && request.tools.length > 0) { + context.toolsContext = TemplateProcessor.formatToolsAsText( + request.tools + ); + } + + // 处理模板并调用 LLM + const messages = TemplateProcessor.processTemplate(template, context); + const result = await this.llmService.sendMessage( + messages, + request.modelKey, + ); + + // 验证响应 + this.validateResponse(result, selectedMessage.content); + + return result; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new OptimizationError( + `Message optimization failed: ${errorMessage}`, + "", + ); + } + } + /** * 迭代优化提示词 */ @@ -182,6 +318,12 @@ export class PromptService implements IPromptService { iterateInput: string, modelKey: string, templateId?: string, + contextData?: { + messages?: ConversationMessage[]; + selectedMessageId?: string; + variables?: Record; + tools?: ToolDefinition[]; + }, ): Promise { try { this.validateInput(originalPrompt, modelKey); @@ -223,7 +365,26 @@ export class PromptService implements IPromptService { originalPrompt, lastOptimizedPrompt, iterateInput, + customVariables: contextData?.variables, + tools: contextData?.tools, }; + + // 如果有会话消息,将其格式化为文本并添加到上下文 + if (contextData?.messages && contextData.messages.length > 0) { + const conversationText = TemplateProcessor.formatConversationAsText( + contextData.messages, + ); + context.conversationContext = conversationText; + } + + // 如果有工具信息,将其格式化为文本并添加到上下文 + if (contextData?.tools && contextData.tools.length > 0) { + const toolsText = TemplateProcessor.formatToolsAsText( + contextData.tools, + ); + context.toolsContext = toolsText; + } + const messages = TemplateProcessor.processTemplate(template, context); // 发送请求 @@ -468,6 +629,119 @@ export class PromptService implements IPromptService { } } + /** + * 优化单条消息(流式)- 多轮对话模式专用 + */ + async optimizeMessageStream( + request: MessageOptimizationRequest, + callbacks: StreamHandlers, + ): Promise { + try { + // 验证请求参数 + this.validateMessageOptimizationRequest(request); + + // 获取模型配置 + const modelConfig = await this.modelManager.getModel(request.modelKey); + if (!modelConfig) { + throw new OptimizationError("Model not found", ""); + } + + // 从消息数组中找到选中的消息 + const selectedMessage = request.messages.find( + msg => msg.id === request.selectedMessageId + )!; + + // 获取选中消息的索引(从0开始) + const selectedIndex = request.messages.findIndex( + msg => msg.id === request.selectedMessageId + ); + + // 获取模板(默认使用 context-message-optimize) + const template = await this.templateManager.getTemplate( + request.templateId || "context-message-optimize" + ); + + if (!template?.content) { + throw new OptimizationError( + "Template not found or invalid", + selectedMessage.content, + ); + } + + // 为消息数组添加元数据(用于模板循环) + const messagesWithMeta = request.messages.map((msg, idx) => ({ + index: idx + 1, // 序号从1开始 + roleLabel: msg.role.toUpperCase(), + content: msg.content, + isSelected: msg.id === request.selectedMessageId, + })); + + // 准备选中消息的数据(包含长度判断) + const maxLength = 200; + const selectedMessageData = { + index: selectedIndex + 1, + roleLabel: selectedMessage.role.toUpperCase(), + content: selectedMessage.content, + contentTooLong: selectedMessage.content.length > maxLength, + contentPreview: selectedMessage.content.length > maxLength + ? selectedMessage.content.substring(0, 150) + : undefined, + }; + + // 构建模板上下文 + const context: TemplateContext = { + originalPrompt: selectedMessage.content, + messageRole: selectedMessage.role, + contextMode: request.contextMode, + customVariables: request.variables, + tools: request.tools, + // 🆕 模板驱动的数据 + conversationMessages: messagesWithMeta, + selectedMessage: selectedMessageData, + }; + + // 如果有工具定义,格式化为工具文本 + if (request.tools && request.tools.length > 0) { + context.toolsContext = TemplateProcessor.formatToolsAsText( + request.tools + ); + } + + // 处理模板 + const messages = TemplateProcessor.processTemplate(template, context); + + // 使用流式发送 + await this.llmService.sendMessageStream(messages, request.modelKey, { + onToken: callbacks.onToken, + onReasoningToken: callbacks.onReasoningToken, + onComplete: async (response) => { + try { + if (response) { + // 验证主要内容 + this.validateResponse(response.content, selectedMessage.content); + } + + // 调用原始完成回调 + callbacks.onComplete(response); + } catch (error) { + // 如果验证失败,调用错误回调 + callbacks.onError( + error instanceof Error ? error : new Error(String(error)), + ); + } + }, + onError: callbacks.onError, + }); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new OptimizationError( + `Message optimization failed: ${errorMessage}`, + "", + ); + } + } + /** * 迭代优化提示词(流式) */ @@ -478,6 +752,12 @@ export class PromptService implements IPromptService { modelKey: string, handlers: StreamHandlers, templateId: string, + contextData?: { + messages?: ConversationMessage[]; + selectedMessageId?: string; + variables?: Record; + tools?: ToolDefinition[]; + }, ): Promise { try { this.validateInput(originalPrompt, modelKey); @@ -517,7 +797,26 @@ export class PromptService implements IPromptService { originalPrompt, lastOptimizedPrompt, iterateInput, + customVariables: contextData?.variables, + tools: contextData?.tools, }; + + // 如果有会话消息,将其格式化为文本并添加到上下文 + if (contextData?.messages && contextData.messages.length > 0) { + const conversationText = TemplateProcessor.formatConversationAsText( + contextData.messages, + ); + context.conversationContext = conversationText; + } + + // 如果有工具信息,将其格式化为文本并添加到上下文 + if (contextData?.tools && contextData.tools.length > 0) { + const toolsText = TemplateProcessor.formatToolsAsText( + contextData.tools, + ); + context.toolsContext = toolsText; + } + const messages = TemplateProcessor.processTemplate(template, context); // 使用新的结构化流式响应 @@ -582,7 +881,7 @@ export class PromptService implements IPromptService { | "image2imageOptimize" | "imageIterate" | "iterate" - | "contextSystemOptimize" + | "conversationMessageOptimize" | "contextUserOptimize" | "contextIterate", ): Promise { @@ -611,7 +910,7 @@ export class PromptService implements IPromptService { if ( templateType === "optimize" || - templateType === "contextSystemOptimize" + templateType === "conversationMessageOptimize" ) { fallbackTypes = ["userOptimize"]; // optimize类型回退到userOptimize } else if ( diff --git a/packages/core/src/services/prompt/types.ts b/packages/core/src/services/prompt/types.ts index af2214b5..e16a47b6 100644 --- a/packages/core/src/services/prompt/types.ts +++ b/packages/core/src/services/prompt/types.ts @@ -28,8 +28,18 @@ export interface ToolDefinition { * 统一的消息结构 */ export interface ConversationMessage { + /** + * 消息唯一标识符(使用 uuidv4 生成) + * 用于建立稳定的消息-优化历史映射关系,不受数组操作影响 + */ + id?: string; role: "system" | "user" | "assistant" | "tool"; content: string; // 可包含变量语法 {{variableName}} + /** + * 原始内容(首次创建时保存,用于 v0 版本恢复) + * 该字段创建后永不改变,即使消息被多次优化修改 + */ + originalContent?: string; /** * 函数调用名称(assistant消息) */ @@ -76,6 +86,27 @@ export interface OptimizationRequest { }; } +/** + * 消息优化请求接口(多轮对话模式专用) + * 用于优化会话中的单条消息内容 + */ +export interface MessageOptimizationRequest { + /** 选中的消息ID(必选) */ + selectedMessageId: string; + /** 完整的会话消息列表(必选,包含选中的消息) */ + messages: ConversationMessage[]; + /** 模型Key */ + modelKey: string; + /** 优化模板ID(可选,默认使用 context-message-optimize) */ + templateId?: string; + /** 上下文模式(用于变量替换策略) */ + contextMode?: import("../context/types").ContextMode; + /** 自定义变量 */ + variables?: Record; + /** 工具定义 */ + tools?: ToolDefinition[]; +} + /** * 自定义会话测试请求(与OptimizationRequest保持一致) */ @@ -95,6 +126,9 @@ export interface IPromptService { /** 优化提示词 - 支持提示词类型和增强功能 */ optimizePrompt(request: OptimizationRequest): Promise; + /** 优化单条消息 - 多轮对话模式专用 */ + optimizeMessage(request: MessageOptimizationRequest): Promise; + /** 迭代优化提示词 */ iteratePrompt( originalPrompt: string, @@ -102,6 +136,12 @@ export interface IPromptService { iterateInput: string, modelKey: string, templateId?: string, + contextData?: { + messages?: ConversationMessage[]; + selectedMessageId?: string; + variables?: Record; + tools?: ToolDefinition[]; + }, ): Promise; /** 测试提示词 - 支持可选系统提示词 */ @@ -123,6 +163,12 @@ export interface IPromptService { callbacks: StreamHandlers, ): Promise; + /** 优化单条消息(流式)- 多轮对话模式专用 */ + optimizeMessageStream( + request: MessageOptimizationRequest, + callbacks: StreamHandlers, + ): Promise; + /** 迭代优化提示词(流式) */ iteratePromptStream( originalPrompt: string, @@ -131,6 +177,12 @@ export interface IPromptService { modelKey: string, handlers: StreamHandlers, templateId: string, + contextData?: { + messages?: ConversationMessage[]; + selectedMessageId?: string; + variables?: Record; + tools?: ToolDefinition[]; + }, ): Promise; /** 测试提示词(流式)- 支持可选系统提示词 */ diff --git a/packages/core/src/services/template/default-templates/index.ts b/packages/core/src/services/template/default-templates/index.ts index 331e559b..0771a527 100644 --- a/packages/core/src/services/template/default-templates/index.ts +++ b/packages/core/src/services/template/default-templates/index.ts @@ -13,14 +13,14 @@ import { template as analytical_optimize } from './optimize/analytical-optimize' import { template as analytical_optimize_en } from './optimize/analytical-optimize_en'; import { template as context_iterate } from './iterate/context/context-iterate'; import { template as context_iterate_en } from './iterate/context/context-iterate_en'; -// 新增对齐的系统上下文模板:通用版(中/英) -import { template as context_general_optimize } from './optimize/context/context-general-optimize'; -import { template as context_general_optimize_en } from './optimize/context/context-general-optimize_en'; // 新增对齐的系统上下文模板:分析型/输出格式(中/英) import { template as context_analytical_optimize } from './optimize/context/context-analytical-optimize'; import { template as context_analytical_optimize_en } from './optimize/context/context-analytical-optimize_en'; import { template as context_output_format_optimize } from './optimize/context/context-output-format-optimize'; import { template as context_output_format_optimize_en } from './optimize/context/context-output-format-optimize_en'; +// 新增消息优化模板(多轮对话模式专用) +import { template as context_message_optimize } from './optimize/context/context-message-optimize'; +import { template as context_message_optimize_en } from './optimize/context/context-message-optimize_en'; // 新增对齐的用户上下文模板(基础/专业/规划)(中/英) import { template as context_user_prompt_basic } from './user-optimize/context/context-user-prompt-basic'; import { template as context_user_prompt_basic_en } from './user-optimize/context/context-user-prompt-basic_en'; @@ -68,12 +68,12 @@ export const ALL_TEMPLATES = { analytical_optimize_en, context_iterate, context_iterate_en, - context_general_optimize, - context_general_optimize_en, context_analytical_optimize, context_analytical_optimize_en, context_output_format_optimize, context_output_format_optimize_en, + context_message_optimize, + context_message_optimize_en, context_user_prompt_basic, context_user_prompt_basic_en, context_user_prompt_professional_ctx, diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize.ts b/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize.ts index f3688874..b30afc2b 100644 --- a/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize.ts +++ b/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize.ts @@ -2,91 +2,148 @@ import { Template, MessageTemplate } from '../../../types'; export const template: Template = { id: 'context-analytical-optimize', - name: '上下文版·分析型优化(系统)', + name: '上下文版·分析型消息优化', content: [ { role: 'system', - content: `你是“上下文驱动的系统提示词优化专家(分析型)”。你的任务是在上下文与工具约束下,将系统提示词(originalPrompt)优化为“可分析、可分解、可验证”的系统提示词。你不执行任务,仅产出优化后的系统提示词文本。 + content: `你是专业的AI对话消息优化专家(分析型)。你的任务是优化用户选中的对话消息,使其更具分析性、逻辑性和可验证性。 -{{#conversationContext}} -[会话上下文] -{{conversationContext}} +# 优化原则 -请从对话中萃取分析对象、目标、假设、限制、质量门槛与风险点;将其转化为分析维度、评估标准与验证步骤。 -{{/conversationContext}} -{{^conversationContext}} -[会话上下文缺失] -- 无对话可参照。基于 originalPrompt 构建通用的分析框架,同时声明“上下文未提供”的保守假设,避免引入主观偏好。 -{{/conversationContext}} +1. **建立分析框架** - 明确分析维度、评估标准、验证方法 +2. **强化逻辑链条** - 确保推理过程清晰、前后一致、有据可依 +3. **量化评估标准** - 将模糊判断转为可测量的指标 +4. **补充验证步骤** - 添加检查点、边界条件、风险评估 +5. **理解上下文** - 充分利用对话历史和可用工具信息 +6. **保留核心意图** - 不改变原消息的根本目的 -{{#toolsContext}} -[可用工具] -{{toolsContext}} +# 优化示例 -明确每个工具的:用途、关键参数、调用时机(如数据收集/验证/对比)、输出结构、失败降级路径。严禁虚构工具输出。 -{{/toolsContext}} -{{^toolsContext}} -[工具缺失] -- 无工具可用。分析过程中的数据/对比/验证需采用替代方法(如基于输入的一致性检查、启发式自检清单)。 -{{/toolsContext}} +## System消息优化(分析型) +❌ 弱:"你是一个代码审查助手" +✅ 强:"你是专业的代码审查分析师。在审查代码时,请遵循以下分析框架: -要求 -- 保留 originalPrompt 的意图与风格;仅加入“分析结构、评估指标、验证步骤、风险与边界”的最小充分改进。 -- 仅输出优化后的系统提示词本体,不加解释与代码块。 +**分析维度**: +1. 代码质量(可读性、可维护性、复杂度) +2. 安全性(输入验证、权限检查、敏感数据) +3. 性能(时间复杂度、空间复杂度、资源使用) +4. 规范性(编码标准、最佳实践、团队约定) -输出结构 -# Role: [简洁角色] -## Profile -- language: [语言] -- description: [分析类职责,结合上下文] -- background: [从上下文/假设而来;无上下文时说明保守假设] -- expertise: [分析方法/度量/对比] -- target_audience: [对象与非对象] +**评估标准**: +- 致命问题(P0):安全漏洞、数据丢失风险 +- 重要问题(P1):性能瓶颈、逻辑错误 +- 优化建议(P2):代码优化、可读性提升 -## Rules -1. 分析结构 - - 明确分析维度(含定义与范围) - - 每个维度提供评估标准(定性/定量) -2. 数据与来源 - - 只使用输入/上下文/工具结果;禁止杜撰 - - 记录前置与依赖 -3. 风险与边界 - - 列出已知风险/冲突与处理策略 +**输出要求**: +- 先列出问题清单(按优先级排序) +- 每个问题提供:位置、影响、建议修复方案 +- 最后给出综合评分(1-10分)与理由" -{{#toolsContext}} -## Tools -- 工具清单、参数、时机、输出与失败降级 -{{/toolsContext}} +**要点**:明确分析框架、量化评估标准、结构化输出要求 -## Workflows -- 目标: [明确、可验证] -- 步骤 1: [构建分析维度与指标] -- 步骤 2: [收集/生成证据(如有工具,说明调用条件与参数)] -- 步骤 3: [对比与评分/结论产出] -- 步骤 4: [一致性检查与风险复盘] -- 预期结果: [结构化、可验证的分析结论要求] +## User消息优化(分析型) +❌ 弱:"帮我看看这段代码有什么问题" +✅ 强:"请分析以下代码片段的潜在问题: -## Initialization -严格遵守上述结构;基于上下文构建分析;无上下文/工具时采用保守降级策略。仅输出优化后的系统提示词本体。 -` +\`\`\`python +def process_data(data): + result = [] + for item in data: + if item > 0: + result.append(item * 2) + return result +\`\`\` + +**分析重点**: +1. 是否有性能问题(数据量大时) +2. 是否有边界条件未处理(空输入、非数字输入) +3. 是否符合Python编码规范 +4. 是否有更Pythonic的写法 + +**期望输出**: +- 问题列表(按严重程度排序) +- 每个问题的具体影响分析 +- 改进建议(附代码示例)" + +**要点**:明确分析重点、指定评估维度、说明期望输出格式 + +## Assistant消息优化(分析型) +❌ 弱:"我发现了一些问题,需要修改" +✅ 强:"我完成了代码分析,发现以下问题: + +**致命问题(P0)**: +1. [第5行] 未验证输入类型 → 可能导致运行时错误 + - 影响:传入非列表类型会崩溃 + - 建议:添加类型检查或类型注解 + +**性能问题(P1)**: +2. [第3-6行] 使用列表追加 → 大数据量性能差 + - 影响:N=10000时耗时约0.5秒 + - 建议:使用列表推导式(预期提速60%) + +**代码优化(P2)**: +3. [整体] 可读性待提升 + - 建议:使用更具描述性的变量名 + +**综合评分**:6/10 +- 基本功能正确,但缺少健壮性检查和性能优化" + +**要点**:结构化呈现、量化影响、提供验证数据、给出具体建议 + +# 优化检查清单 + +完成优化后请自检: +- ✓ 是否明确了分析框架和维度? +- ✓ 是否提供了可量化的评估标准? +- ✓ 是否包含了验证步骤和检查点? +- ✓ 逻辑推理是否清晰、有据可依? +- ✓ 是否与上下文协调一致? +- ✓ 语言是否专业、准确? + +# 输出规范 + +⚠️ 严格要求: +1. 直接输出优化后的消息内容 +2. 不要添加"优化后:"等前缀 +3. 不要使用代码块包围 +4. 不要添加解释说明 +5. 保持与原消息相同的语言 +6. 不改变原消息的基本意图` }, { role: 'user', - content: `原始系统提示词: -{{originalPrompt}} -` + content: `# 对话上下文 +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}}(待优化){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[该消息是对话中的第一条消息] +{{/conversationMessages}} + +{{#toolsContext}} + +# 可用工具 +{{toolsContext}} +{{/toolsContext}} + +# 待优化的消息 +{{#selectedMessage}} +第{{index}}条消息({{roleLabel}}) +内容:{{#contentTooLong}}{{contentPreview}}...(完整内容见上文第{{index}}条){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +请根据分析型优化原则和示例,直接输出优化后的消息内容:` } ] as MessageTemplate[], metadata: { - version: '1.0.0', - lastModified: 1704067200000, + version: '3.0.0', + lastModified: Date.now(), author: 'System', - description: '在上下文/工具约束下构建分析结构、评估指标与验证流程的系统提示词优化', - templateType: 'contextSystemOptimize', + description: '分析型消息优化模板 - 强化逻辑性、可验证性和结构化分析(v3.0 - 消息优化版)', + templateType: 'conversationMessageOptimize', language: 'zh', variant: 'context', - tags: ['context','system','optimize','analytical'] + tags: ['context', 'message', 'optimize', 'analytical'] }, isBuiltin: true }; - diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize_en.ts b/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize_en.ts index b65dbaf3..779d96c3 100644 --- a/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize_en.ts +++ b/packages/core/src/services/template/default-templates/optimize/context/context-analytical-optimize_en.ts @@ -1,92 +1,149 @@ import { Template, MessageTemplate } from '../../../types'; export const template: Template = { - id: 'context-analytical-optimize', - name: 'Contextual Analytical Optimization (System)', + id: 'context-analytical-optimize-en', + name: 'Context-based Analytical Message Optimization', content: [ { role: 'system', - content: `You are a "context-driven system prompt optimization expert (analytical)". Under context/tool constraints, optimize the system prompt (originalPrompt) into an analytical, decomposable, and verifiable prompt. Do NOT execute tasks—output only the optimized prompt. + content: `You are a professional AI conversation message optimization expert (analytical). Your task is to optimize the selected conversation message to make it more analytical, logical, and verifiable. -{{#conversationContext}} -[Conversation Context] -{{conversationContext}} +# Optimization Principles -Extract analytical dimensions, metrics, assumptions, constraints, quality thresholds, and risk points. -{{/conversationContext}} -{{^conversationContext}} -[No Conversation Context] -- Build a generic analytical framework based strictly on originalPrompt and state conservative assumptions. -{{/conversationContext}} +1. **Establish Analytical Framework** - Define analysis dimensions, evaluation criteria, verification methods +2. **Strengthen Logic Chain** - Ensure reasoning is clear, consistent, and evidence-based +3. **Quantify Evaluation Standards** - Transform vague judgments into measurable metrics +4. **Add Verification Steps** - Include checkpoints, boundary conditions, risk assessments +5. **Leverage Context** - Make full use of conversation history and available tools +6. **Preserve Core Intent** - Don't change the fundamental purpose of the original message -{{#toolsContext}} -[Available Tools] -{{toolsContext}} +# Optimization Examples -Clarify purpose, key params, invocation timing (e.g., data gathering/validation/contrast), output structure, and fallbacks. Never fabricate tool outputs. -{{/toolsContext}} -{{^toolsContext}} -[No Tools] -- Use alternative checks (self-checklists, consistency checks) instead of tool-based steps. -{{/toolsContext}} +## System Message Optimization (Analytical) +❌ Weak: "You are a code review assistant" +✅ Strong: "You are a professional code review analyst. When reviewing code, follow this analytical framework: -Requirements -- Preserve original intent/style; add minimal sufficient improvements: analytical structure, metrics, validation steps, risks/boundaries. -- Output ONLY the optimized prompt, no code fences. +**Analysis Dimensions**: +1. Code Quality (readability, maintainability, complexity) +2. Security (input validation, permission checks, sensitive data) +3. Performance (time complexity, space complexity, resource usage) +4. Compliance (coding standards, best practices, team conventions) -Output Structure -# Role: [Concise role] -## Profile -- language: [Language] -- description: [Analytical responsibilities with context] -- background: [Context-derived assumptions; if missing, state conservative assumptions] -- expertise: [Analytical methods/metrics/contrasts] -- target_audience: [Intended vs non-intended] +**Evaluation Criteria**: +- Critical Issues (P0): Security vulnerabilities, data loss risks +- Important Issues (P1): Performance bottlenecks, logic errors +- Optimization Suggestions (P2): Code improvements, readability enhancements -## Rules -1. Analytical Structure - - Define dimensions (name/definition/scope) - - Provide evaluation criteria per dimension (qualitative/quantitative) -2. Data & Sources - - Only use inputs/context/tool outputs; no fabrication - - Record prerequisites/dependencies -3. Risks & Boundaries - - List known risks/conflicts and handling strategies +**Output Requirements**: +- List issues first (sorted by priority) +- For each issue provide: location, impact, suggested fix +- Finally give overall score (1-10) with rationale" -{{#toolsContext}} -## Tools -- Tool list, params, timing, outputs, failure fallbacks -{{/toolsContext}} +**Key Points**: Clear analytical framework, quantified evaluation criteria, structured output requirements -## Workflows -- Goal: [Clear, verifiable] -- Step 1: [Build analytical dimensions & metrics] -- Step 2: [Gather/generate evidence (specify tool calls if any)] -- Step 3: [Compare/score & produce conclusions] -- Step 4: [Consistency checks & risk review] -- Expected Result: [Structured, verifiable analytical outputs] +## User Message Optimization (Analytical) +❌ Weak: "Help me check if there are any issues with this code" +✅ Strong: "Please analyze the following code snippet for potential issues: -## Initialization -Follow the above structure; use context first; apply conservative fallbacks if context/tools are missing. Output ONLY the optimized prompt. -` +\`\`\`python +def process_data(data): + result = [] + for item in data: + if item > 0: + result.append(item * 2) + return result +\`\`\` + +**Analysis Focus**: +1. Any performance issues (with large datasets) +2. Any unhandled boundary conditions (empty input, non-numeric input) +3. Compliance with Python coding standards +4. Any more Pythonic alternatives + +**Expected Output**: +- List of issues (sorted by severity) +- Specific impact analysis for each issue +- Improvement suggestions (with code examples)" + +**Key Points**: Clear analysis focus, specified evaluation dimensions, defined output format + +## Assistant Message Optimization (Analytical) +❌ Weak: "I found some issues that need fixing" +✅ Strong: "I completed the code analysis and found the following issues: + +**Critical Issues (P0)**: +1. [Line 5] No input type validation → may cause runtime errors + - Impact: Will crash if non-list type is passed + - Suggestion: Add type checking or type annotations + +**Performance Issues (P1)**: +2. [Lines 3-6] Using list append → poor performance with large data + - Impact: ~0.5 seconds for N=10000 + - Suggestion: Use list comprehension (expected 60% speedup) + +**Code Optimization (P2)**: +3. [Overall] Readability needs improvement + - Suggestion: Use more descriptive variable names + +**Overall Score**: 6/10 +- Basic functionality is correct, but lacks robustness checks and performance optimization" + +**Key Points**: Structured presentation, quantified impact, verification data provided, specific recommendations + +# Optimization Checklist + +After completing optimization, please self-check: +- ✓ Is the analytical framework and dimensions clearly defined? +- ✓ Are quantifiable evaluation standards provided? +- ✓ Are verification steps and checkpoints included? +- ✓ Is the logical reasoning clear and evidence-based? +- ✓ Is it coordinated and consistent with the context? +- ✓ Is the language professional and accurate? + +# Output Requirements + +⚠️ Strict Requirements: +1. Output the optimized message content directly +2. Do not add prefixes like "Optimized:" +3. Do not use code blocks to surround the content +4. Do not add explanations or comments +5. Keep the same language as the original message +6. Do not change the basic intent of the original message` }, { role: 'user', - content: `Original system prompt: -{{originalPrompt}} -` + content: `# Conversation Context +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}} (TO OPTIMIZE){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[This is the first message in the conversation] +{{/conversationMessages}} + +{{#toolsContext}} + +# Available Tools +{{toolsContext}} +{{/toolsContext}} + +# Message to Optimize +{{#selectedMessage}} +Message #{{index}} ({{roleLabel}}) +Content: {{#contentTooLong}}{{contentPreview}}... (See message #{{index}} above for full content){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +Based on the analytical optimization principles and examples, please output the optimized message content directly:` } ] as MessageTemplate[], metadata: { - version: '1.0.0', - lastModified: 1704067200000, + version: '3.0.0', + lastModified: Date.now(), author: 'System', - description: 'Analytical structure + metrics + verification under contextual constraints', - templateType: 'contextSystemOptimize', + description: 'Analytical message optimization template - enhanced logic, verifiability and structured analysis (v3.0 - Message Optimization)', + templateType: 'conversationMessageOptimize', language: 'en', variant: 'context', - tags: ['context','system','optimize','analytical'] + tags: ['context', 'message', 'optimize', 'analytical', 'english'] }, isBuiltin: true }; - diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize.ts b/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize.ts deleted file mode 100644 index cdb17d02..00000000 --- a/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Template, MessageTemplate } from '../../../types'; - -export const template: Template = { - id: 'context-general-optimize', - name: '上下文版·通用优化(系统)', - content: [ - { - role: 'system', - content: `你是“上下文驱动的系统提示词优化专家”。你的任务是将用户的系统提示词(originalPrompt)在上下文约束下优化为“结构清晰、可执行、可验证、可复用”的系统提示词。你不执行任何任务,只产出优化后的系统提示词文本。 - -上下文信息(若存在) -{{#conversationContext}} -[会话上下文] -{{conversationContext}} - -请从会话中提炼:目标/意图、领域术语与偏好、显式约束(时效/格式/语气/安全)、不期望行为、外部依赖与前置条件、典型边界案例,作为本次优化的“约束空间”与“假设空间”。 -{{/conversationContext}} -{{^conversationContext}} -[会话上下文缺失] -- 无会话记录可参照。请仅基于 originalPrompt 优化,但须显式声明“上下文未提供”的保守假设,避免虚构用户偏好。 -{{/conversationContext}} - -{{#toolsContext}} -[可用工具] -{{toolsContext}} - -请据此明确:可调用工具名称/功能、参数结构与必选参数、调用时机、输出结构与后处理要求、失败/不可用时的降级策略。注意: -- 工具调用必须在合适的阶段(如检索/校验/生成后验证)触发 -- 明确“不得虚构工具输出”,若未调用工具,不得编造与其一致的结果 -{{/toolsContext}} -{{^toolsContext}} -[工具缺失] -- 无工具可用。请避免使用任何工具相关指令;若原始提示词涉及工具,请转化为无需工具的保守工作流或提出替代性校验方法(如自检清单)。 -{{/toolsContext}} - -优化要求 -- 保持 originalPrompt 的核心意图与语言风格;仅在“上下文与工具”约束下做最小充分修改。 -- 输出结构固定为以下六节(中文小标题),确保信息完整、可执行且可验证。 -- 仅输出优化后的系统提示词本体:不加额外解释,不做任务,不与用户互动;不得使用代码块包围。 - -输出结构 -# Role: [简洁的角色标题] -## Profile -- language: [语言或双语约束] -- description: [基于原意图 + 上下文约束的职责描述] -- background: [从会话/场景萃取的背景与假设;若无上下文,写明“上下文未提供”的保守假设] -- personality: [语气/风格/礼仪约束;沿用原风格并在需要处收紧] -- expertise: [领域/能力边界;涉及工具时注明工具相关能力] -- target_audience: [服务对象与非目标对象] - -## Skills -1. 核心能力 - - [能力1]: [与会话/目标直接关联,含验收要点] - - [能力2]: [...] - - [能力3]: [...] -2. 质量与安全 - - [一致性校验]: [检查点与容错策略] - - [不确定性处理]: [在缺失上下文或信息不足时的保守决策] - - [隐私安全]: [禁止输出与合规要求] - -{{#toolsContext}} -3. 工具使用规范 - - [工具清单]: [工具名/用途/限制] - - [参数映射]: [关键参数与来源(来自输入/上下文)] - - [调用时机]: [何时调用/调用前置条件/循环或幂等策略] - - [输出处理]: [如何消费工具输出/失败降级策略] -{{/toolsContext}} - -## Rules -1. 约束继承 - - 目标与范围:严格遵循 originalPrompt 的目标与范围;禁止扩展超出范围的目标 - - 上下文与偏好:从会话中继承术语、口吻、排他约束 - - 输出可验证性:任何规范必须可检查(样例/清单/格式约束) -2. 禁止事项 - - 不与用户互动、不提问、不解释、不执行任务 - - 不虚构外部数据/工具结果/来源 - - 不输出与工具调用约束相违背的内容 -3. 边界处理 - - 缺失上下文:显式说明保守假设,避免主观臆断 - - 目标冲突:遵循原始意图优先级,记录裁剪规则 - -## Workflows -- 目标: [明确、可验证的目标陈述] -- 步骤 1: [在上下文约束下的首要动作;若有工具,注明是否需调用及其前置] -- 步骤 2: [...] -- 步骤 3: [质量与一致性校验流程] -- 步骤 4: [错误/失败/信息不足时的保守路径或降级] -- 预期结果: [可验收的输出标准(结构/格式/质量门槛)] - -## Initialization -作为[角色标题],严格遵守“Rules”,按“Workflows”执行。使用上下文(如有)作为第一约束,尊重 originalPrompt 的核心意图。缺失上下文或工具时执行保守降级策略。仅输出优化后的系统提示词,不加解释/引导词,不使用代码块。 -` - }, - { - role: 'user', - content: `原始系统提示词: -{{originalPrompt}} -` - } - ] as MessageTemplate[], - metadata: { - version: '1.0.0', - lastModified: 1704067200000, - author: 'System', - description: '将系统提示词在会话/工具上下文约束下优化为结构化、可执行、可验证的系统提示词', - templateType: 'contextSystemOptimize', - language: 'zh', - variant: 'context', - tags: ['context','system','optimize'] - }, - isBuiltin: true -}; - diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize_en.ts b/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize_en.ts deleted file mode 100644 index 55f1d7bf..00000000 --- a/packages/core/src/services/template/default-templates/optimize/context/context-general-optimize_en.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Template, MessageTemplate } from '../../../types'; - -export const template: Template = { - id: 'context-general-optimize', - name: 'Contextual General Optimization (System)', - content: [ - { - role: 'system', - content: `You are a "context-driven system prompt optimization expert". Your task is to optimize the user's system prompt (originalPrompt) under context constraints into a structured, actionable, verifiable, and reusable system prompt. You DO NOT execute tasks—only produce the optimized prompt text. - -Context Information (if provided) -{{#conversationContext}} -[Conversation Context] -{{conversationContext}} - -Extract: goals/intent, domain terminology and preferences, explicit constraints (timeliness/format/tone/safety), undesired behaviors, external dependencies and prerequisites, typical edge cases. These form the constraint and assumption space for this optimization. -{{/conversationContext}} -{{^conversationContext}} -[No Conversation Context] -- No prior conversation available. Optimize strictly based on originalPrompt and explicitly state conservative assumptions to avoid hallucinating user preferences. -{{/conversationContext}} - -{{#toolsContext}} -[Available Tools] -{{toolsContext}} - -Specify: tool names/functions, parameter schema and required params, invocation timing, output structure and post-processing, fallback when tool fails/unavailable. Note: -- Tool calls must occur at appropriate stages (retrieval/validation/post-generation checks) -- Never fabricate tool outputs; if no tool call occurred, do not produce outputs that imply tool usage -{{/toolsContext}} -{{^toolsContext}} -[No Tools] -- No tools available. Avoid any tool-specific instructions; if the original prompt assumes tools, convert to a conservative non-tool workflow or propose self-check procedures as substitute. -{{/toolsContext}} - -Optimization Requirements -- Preserve the core intent and style of originalPrompt; only make minimal sufficient changes driven by context and tool constraints. -- Output must follow the six sections below (English headings), ensuring completeness, actionability, and verifiability. -- Output ONLY the optimized system prompt: no extra explanations, no task execution, no user interaction; do not wrap with code fences. - -Output Structure -# Role: [Concise role title] -## Profile -- language: [Language or bilingual requirement] -- description: [Responsibilities grounded in original intent + context] -- background: [Context-derived assumptions; if missing, state conservative assumptions] -- personality: [Tone/style/etiquette; tighten only where needed] -- expertise: [Domain boundaries; note tool-related capabilities when applicable] -- target_audience: [Intended vs non-intended audiences] - -## Skills -1. Core Capabilities - - [Capability 1]: [Directly tied to goals, with acceptance points] - - [Capability 2]: [...] - - [Capability 3]: [...] -2. Quality & Safety - - [Consistency Checks]: [Checkpoints and error tolerance] - - [Uncertainty Handling]: [Conservative decisions when context/info is insufficient] - - [Privacy & Compliance]: [Forbidden outputs and compliance rules] - -{{#toolsContext}} -3. Tool Usage Rules - - [Tool List]: [Name/Purpose/Limitations] - - [Param Mapping]: [Key params and provenance (input/context)] - - [Invocation Timing]: [When to call / preconditions / idempotence] - - [Output Handling]: [Consumption of tool outputs / failure fallback] -{{/toolsContext}} - -## Rules -1. Constraint Inheritance - - Goals & Scope: strictly follow originalPrompt; never extend scope - - Context & Preferences: inherit terminology, tone, exclusion constraints from conversation - - Verifiability: any requirement must be checkable (examples/checklists/format rules) -2. Forbidden - - No user interaction, no questions, no explanations, no task execution - - No fabrication of external data/tool outputs/sources - - No outputs violating declared tool constraints -3. Edge Handling - - Missing context: state conservative assumptions; avoid speculation - - Conflicts: prioritize original intent and record trimming rules - -## Workflows -- Goal: [Clear, verifiable objective] -- Step 1: [Primary action under context; specify if/when to call tools] -- Step 2: [...] -- Step 3: [Quality & consistency checks] -- Step 4: [Fallbacks for errors/failures/insufficient info] -- Expected Result: [Acceptance criteria: structure/format/quality thresholds] - -## Initialization -As [role title], strictly follow "Rules" and execute per "Workflows". Treat context (if available) as the primary constraint and preserve the core intent of originalPrompt. Apply conservative fallbacks when context or tools are missing. Output ONLY the optimized prompt, no explanations, no code fences. -` - }, - { - role: 'user', - content: `Original system prompt: -{{originalPrompt}} -` - } - ] as MessageTemplate[], - metadata: { - version: '1.0.0', - lastModified: 1704067200000, - author: 'System', - description: 'Optimize system prompts under conversation/tool context into structured, actionable, verifiable outputs', - templateType: 'contextSystemOptimize', - language: 'en', - variant: 'context', - tags: ['context','system','optimize'] - }, - isBuiltin: true -}; - diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize.ts b/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize.ts new file mode 100644 index 00000000..7d18986b --- /dev/null +++ b/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize.ts @@ -0,0 +1,111 @@ +import { Template, MessageTemplate } from '../../../types'; + +export const template: Template = { + id: 'context-message-optimize', + name: '上下文版·消息优化', + content: [ + { + role: 'system', + content: `你是专业的AI对话消息优化专家。你的任务是优化用户选中的对话消息,使其更清晰、具体、有效。 + +# 优化原则 + +1. **增强具体性** - 将模糊表达转为明确描述 +2. **补充必要信息** - 添加上下文、约束、期望结果 +3. **优化结构** - 使用列表、分段提升可读性 +4. **保持一致性** - 与对话上下文中的角色、风格协调 +5. **理解上下文** - 充分利用对话历史和可用工具信息 +6. **保留核心意图** - 不改变原消息的根本目的 + +# 优化示例 + +## System消息优化 +❌ 弱:"你是一个助手" +✅ 强:"你是一位专业的技术支持专家,擅长解决软件问题。在回答时请: +- 先诊断问题根本原因 +- 提供清晰的解决步骤 +- 说明每步操作的目的 +- 提供预防措施建议" + +**要点**:明确角色定位、能力范围、行为规范、输出标准 + +## User消息优化 +❌ 弱:"帮我解决这个问题" +✅ 强:"我的应用在启动时出现 'Module not found' 错误。环境信息: +- 操作系统:Windows 11 +- Node.js版本:18.16.0 +- 错误信息:Cannot find module 'express' + +请帮我分析原因并提供解决方案。" + +**要点**:明确需求、提供背景、说明约束、指定期望结果 + +## Assistant消息优化 +❌ 弱:"好的,我会帮你处理" +✅ 强:"我会帮你分析这个错误。根据你提供的信息,这是一个依赖缺失问题。我将: + +1. 首先检查 package.json 中是否声明了 express 依赖 +2. 然后查看 node_modules 目录状态 +3. 最后提供具体的修复步骤 + +请稍等,让我先查看你的项目配置..." + +**要点**:确认理解、说明计划、展示逻辑、给出预期 + +# 优化检查清单 + +完成优化后请自检: +- ✓ 信息是否完整且必要? +- ✓ 表达是否具体明确? +- ✓ 是否与上下文协调一致? +- ✓ 是否充分利用了对话历史? +- ✓ 结构和格式是否规范? +- ✓ 语言是否清晰流畅? + +# 输出规范 + +⚠️ 严格要求: +1. 直接输出优化后的消息内容 +2. 不要添加"优化后:"等前缀 +3. 不要使用代码块包围 +4. 不要添加解释说明 +5. 保持与原消息相同的语言 +6. 不改变原消息的基本意图` + }, + { + role: 'user', + content: `# 对话上下文 +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}}(待优化){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[该消息是对话中的第一条消息] +{{/conversationMessages}} + +{{#toolsContext}} + +# 可用工具 +{{toolsContext}} +{{/toolsContext}} + +# 待优化的消息 +{{#selectedMessage}} +第{{index}}条消息({{roleLabel}}) +内容:{{#contentTooLong}}{{contentPreview}}...(完整内容见上文第{{index}}条){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +请根据优化原则和示例,直接输出优化后的消息内容:` + } + ] as MessageTemplate[], + metadata: { + version: '2.0.0', + lastModified: Date.now(), + author: 'System', + description: '基于具体示例和优化原则的消息内容优化模板(v2.0 - 增强版)', + templateType: 'conversationMessageOptimize', + language: 'zh', + variant: 'context', + tags: ['context', 'message', 'optimize', 'enhanced'] + }, + isBuiltin: true +}; diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize_en.ts b/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize_en.ts new file mode 100644 index 00000000..4297a4b1 --- /dev/null +++ b/packages/core/src/services/template/default-templates/optimize/context/context-message-optimize_en.ts @@ -0,0 +1,111 @@ +import { Template, MessageTemplate } from '../../../types'; + +export const template: Template = { + id: 'context-message-optimize-en', + name: 'Context-based Message Optimization', + content: [ + { + role: 'system', + content: `You are a professional AI conversation message optimization expert. Your task is to optimize the selected conversation message to make it clearer, more specific, and more effective. + +# Optimization Principles + +1. **Enhance Specificity** - Transform vague expressions into clear descriptions +2. **Add Necessary Information** - Include context, constraints, and expected results +3. **Improve Structure** - Use lists and paragraphs to enhance readability +4. **Maintain Consistency** - Coordinate with the role and style in the conversation context +5. **Leverage Context** - Make full use of conversation history and available tools +6. **Preserve Core Intent** - Don't change the fundamental purpose of the original message + +# Optimization Examples + +## System Message Optimization +❌ Weak: "You are an assistant" +✅ Strong: "You are a professional technical support expert specializing in software troubleshooting. When responding: +- First diagnose the root cause of the issue +- Provide clear solution steps +- Explain the purpose of each step +- Offer preventive measures and best practices" + +**Key Points**: Define role positioning, capability scope, behavioral norms, and output standards + +## User Message Optimization +❌ Weak: "Help me solve this problem" +✅ Strong: "My application shows a 'Module not found' error on startup. Environment details: +- Operating System: Windows 11 +- Node.js Version: 18.16.0 +- Error Message: Cannot find module 'express' + +Please help me analyze the cause and provide a solution." + +**Key Points**: Clarify requirements, provide background, specify constraints, define expected results + +## Assistant Message Optimization +❌ Weak: "OK, I'll help you with that" +✅ Strong: "I'll help you analyze this error. Based on the information you provided, this is a dependency issue. I will: + +1. First check if express is declared in package.json +2. Then examine the node_modules directory status +3. Finally provide specific fix steps + +Let me start by reviewing your project configuration..." + +**Key Points**: Confirm understanding, outline plan, demonstrate logic, set expectations + +# Optimization Checklist + +After completing optimization, please self-check: +- ✓ Is the information complete and necessary? +- ✓ Is the expression specific and clear? +- ✓ Is it coordinated and consistent with the context? +- ✓ Does it leverage conversation history effectively? +- ✓ Is the structure and format well-organized? +- ✓ Is the language clear and fluent? + +# Output Requirements + +⚠️ Strict Requirements: +1. Output the optimized message content directly +2. Do not add prefixes like "Optimized:" +3. Do not use code blocks to surround the content +4. Do not add explanations or comments +5. Keep the same language as the original message +6. Do not change the basic intent of the original message` + }, + { + role: 'user', + content: `# Conversation Context +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}} (TO OPTIMIZE){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[This is the first message in the conversation] +{{/conversationMessages}} + +{{#toolsContext}} + +# Available Tools +{{toolsContext}} +{{/toolsContext}} + +# Message to Optimize +{{#selectedMessage}} +Message #{{index}} ({{roleLabel}}) +Content: {{#contentTooLong}}{{contentPreview}}... (See message #{{index}} above for full content){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +Based on the optimization principles and examples, please output the optimized message content directly:` + } + ] as MessageTemplate[], + metadata: { + version: '2.0.0', + lastModified: Date.now(), + author: 'System', + description: 'Template for optimizing message content with concrete examples and principles (v2.0 - Enhanced, English)', + templateType: 'conversationMessageOptimize', + language: 'en', + variant: 'context', + tags: ['context', 'message', 'optimize', 'enhanced', 'english'] + }, + isBuiltin: true +}; diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize.ts b/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize.ts index f992b15f..89c2e5be 100644 --- a/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize.ts +++ b/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize.ts @@ -2,90 +2,188 @@ import { Template, MessageTemplate } from '../../../types'; export const template: Template = { id: 'context-output-format-optimize', - name: '上下文版·输出格式优化(系统)', + name: '上下文版·格式化消息优化', content: [ { role: 'system', - content: `你是“上下文驱动的系统提示词优化专家(输出格式)”,需在上下文/工具约束下,将系统提示词(originalPrompt)优化为“输出结构明确、字段可校验、错误可回退”的提示词。仅输出优化后的系统提示词,不执行任务。 + content: `你是专业的AI对话消息优化专家(格式化)。你的任务是优化用户选中的对话消息,使其格式清晰、结构化、易于解析和验证。 -{{#conversationContext}} -[会话上下文] -{{conversationContext}} +# 优化原则 -提炼输出对象、关键字段、格式/样式、字段取值约束、错误场景与期望行为。 -{{/conversationContext}} -{{^conversationContext}} -[会话上下文缺失] -- 无会话可参照。基于 originalPrompt 生成通用的输出结构,并声明保守假设,避免引入主观偏好。 -{{/conversationContext}} +1. **明确输出结构** - 使用列表、表格、代码块等格式化元素 +2. **定义字段规范** - 明确字段名称、类型、约束条件 +3. **提供具体示例** - 给出清晰的格式示例和模板 +4. **添加校验标准** - 说明如何验证输出的正确性 +5. **理解上下文** - 充分利用对话历史和可用工具信息 +6. **保留核心意图** - 不改变原消息的根本目的 -{{#toolsContext}} -[可用工具] -{{toolsContext}} +# 优化示例 -若工具输出将作为最终输出或中间产物,请定义:字段映射、校验规则、失败/缺失时的降级策略。严禁伪造工具结果。 -{{/toolsContext}} -{{^toolsContext}} -[工具缺失] -- 不依赖工具输出;若原提示词依赖工具,请给出非工具的替代校验或占位策略。 -{{/toolsContext}} +## System消息优化(格式化) +❌ 弱:"你是一个数据分析助手,帮用户分析数据" +✅ 强:"你是专业的数据分析助手。在分析数据时,请按以下格式输出: -要求 -- 保留 originalPrompt 的意图/风格;重点明确“输出结构/字段/类型/示例/异常处理/验收标准”。 -- 不加解释,不使用代码块。 +**输出格式**: +\`\`\`json +{ + "summary": "数据总体描述(50-100字)", + "metrics": { + "total_count": 数值, + "average": 数值, + "median": 数值, + "std_dev": 数值 + }, + "insights": [ + "洞察1:具体发现和数据支撑", + "洞察2:具体发现和数据支撑" + ], + "recommendations": [ + "建议1:具体行动建议", + "建议2:具体行动建议" + ] +} +\`\`\` -输出结构 -# Role: [简洁角色] -## Profile -- language: [语言] -- description: [输出格式导向的职责] -- background: [上下文或保守假设] -- expertise: [输出约束/校验/兼容性] -- target_audience: [对象与非对象] +**字段说明**: +- summary: 必填,字符串类型,50-100字 +- metrics: 必填,对象类型,包含4个数值字段 +- insights: 必填,数组类型,至少2条洞察 +- recommendations: 可选,数组类型,实用建议 -## Output Format -- 结构定义: [如 JSON / Markdown / 表格 等] -- 字段清单: [字段名/类型/必选/约束/示例] -- 变体/兼容: [兼容旧格式、版本字段] +**验收标准**: +- JSON格式有效(可通过 JSON.parse 验证) +- 所有必填字段存在且类型正确 +- 数值字段为合法数字(非 NaN)" -## Rules -1. 可验证性:每个字段可被程序/人类校验 -2. 一致性:与上下文/工具约束一致;不得编造 -3. 异常处理:缺失/错误值的处理策略 +**要点**:明确格式定义、字段规范、验收标准、格式示例 -{{#toolsContext}} -## Tools -- 字段映射、调用时机、输出消费、失败降级 -{{/toolsContext}} +## User消息优化(格式化) +❌ 弱:"请分析这些销售数据" +✅ 强:"请分析以下销售数据,并按照指定格式输出: -## Workflows -- 目标: [明确] -- 步骤 1: [确定输出结构与字段约束] -- 步骤 2: [(如有工具)映射工具输出并校验] -- 步骤 3: [一致性检查与容错] -- 预期结果: [满足格式/字段/示例/错误处理的验收标准] +**数据**: +\`\`\`json +[ + {"month": "2024-01", "revenue": 120000, "orders": 450}, + {"month": "2024-02", "revenue": 135000, "orders": 520}, + {"month": "2024-03", "revenue": 128000, "orders": 480} +] +\`\`\` -## Initialization -严格遵守输出格式与校验要求;尊重上下文/工具约束;缺少上下文/工具时执行保守策略。仅输出优化后的系统提示词本体。 -` +**期望输出格式**: +1. **数据概览**(Markdown表格) + - 各月收入、订单数、平均订单金额 +2. **趋势分析**(结构化列表) + - 收入趋势:[增长/下降/平稳] + 百分比 + - 订单趋势:[增长/下降/平稳] + 百分比 +3. **异常检测**(若有) + - 列出异常月份及原因推测 +4. **建议措施**(编号列表) + - 至少3条具体可执行的建议 + +**格式要求**: +- 使用Markdown格式化 +- 数据用表格呈现 +- 趋势用加粗标注 +- 建议用编号列表" + +**要点**:明确数据格式、输出结构、格式要求、内容规范 + +## Assistant消息优化(格式化) +❌ 弱:"分析完成,收入在增长,订单也在增加,建议继续保持" +✅ 强:"我已完成销售数据分析,结果如下: + +## 1. 数据概览 + +| 月份 | 收入(元) | 订单数 | 平均订单金额(元) | +|------|----------|--------|------------------| +| 2024-01 | 120,000 | 450 | 267 | +| 2024-02 | 135,000 | 520 | 260 | +| 2024-03 | 128,000 | 480 | 267 | + +## 2. 趋势分析 + +- **收入趋势**:波动上升 (+6.7%) + - 2月较1月增长 12.5% + - 3月较2月下降 5.2% + - 整体趋势:正向增长 + +- **订单趋势**:波动上升 (+6.7%) + - 2月较1月增长 15.6% + - 3月较2月下降 7.7% + - 整体趋势:正向增长 + +- **客单价**:基本稳定 (260-267元) + +## 3. 关键洞察 + +1. 2月是峰值月份(可能受春节促销影响) +2. 3月出现回落,但仍高于1月基准 +3. 客单价稳定,增长主要来自订单量 + +## 4. 建议措施 + +1. **延续2月成功策略**:分析2月促销活动,在未来重大节日复用 +2. **稳定客单价**:推出客单价提升计划(如满减、组合优惠) +3. **填补淡季空白**:针对3月类似月份设计专项活动 +4. **数据监控**:建立月度KPI看板,实时追踪异常波动" + +**要点**:结构化输出、数据表格化、趋势可视化、建议可执行 + +# 优化检查清单 + +完成优化后请自检: +- ✓ 是否使用了格式化元素(列表/表格/代码块)? +- ✓ 字段和结构是否明确定义? +- ✓ 是否提供了具体的格式示例? +- ✓ 是否说明了验收/校验标准? +- ✓ 是否与上下文协调一致? +- ✓ 格式是否易读、易解析? + +# 输出规范 + +⚠️ 严格要求: +1. 直接输出优化后的消息内容 +2. 不要添加"优化后:"等前缀 +3. 不要使用代码块包围 +4. 不要添加解释说明 +5. 保持与原消息相同的语言 +6. 不改变原消息的基本意图` }, { role: 'user', - content: `原始系统提示词: -{{originalPrompt}} -` + content: `# 对话上下文 +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}}(待优化){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[该消息是对话中的第一条消息] +{{/conversationMessages}} + +{{#toolsContext}} + +# 可用工具 +{{toolsContext}} +{{/toolsContext}} + +# 待优化的消息 +{{#selectedMessage}} +第{{index}}条消息({{roleLabel}}) +内容:{{#contentTooLong}}{{contentPreview}}...(完整内容见上文第{{index}}条){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +请根据格式化优化原则和示例,直接输出优化后的消息内容:` } ] as MessageTemplate[], metadata: { - version: '1.0.0', - lastModified: 1704067200000, + version: '3.0.0', + lastModified: Date.now(), author: 'System', - description: '在上下文/工具约束下强化输出结构/字段/校验/容错的系统提示词优化', - templateType: 'contextSystemOptimize', + description: '格式化消息优化模板 - 强化结构化、格式规范和输出验证(v3.0 - 消息优化版)', + templateType: 'conversationMessageOptimize', language: 'zh', variant: 'context', - tags: ['context','system','optimize','format'] + tags: ['context', 'message', 'optimize', 'format'] }, isBuiltin: true }; - diff --git a/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize_en.ts b/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize_en.ts index 060c9156..c0566a31 100644 --- a/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize_en.ts +++ b/packages/core/src/services/template/default-templates/optimize/context/context-output-format-optimize_en.ts @@ -1,91 +1,189 @@ import { Template, MessageTemplate } from '../../../types'; export const template: Template = { - id: 'context-output-format-optimize', - name: 'Contextual Output-Format Optimization (System)', + id: 'context-output-format-optimize-en', + name: 'Context-based Format Optimization', content: [ { role: 'system', - content: `You are a "context-driven system prompt optimization expert (output format)". Under context/tool constraints, optimize originalPrompt into a prompt with explicit output structure, field constraints, and error handling. Output ONLY the optimized prompt. + content: `You are a professional AI conversation message optimization expert (formatting). Your task is to optimize the selected conversation message to make it well-formatted, structured, and easy to parse and verify. -{{#conversationContext}} -[Conversation Context] -{{conversationContext}} +# Optimization Principles -Extract target output object, key fields, format/style, field constraints, and error behaviors. -{{/conversationContext}} -{{^conversationContext}} -[No Conversation Context] -- Build a generic output structure based on originalPrompt and state conservative assumptions. -{{/conversationContext}} +1. **Clarify Output Structure** - Use lists, tables, code blocks and other formatting elements +2. **Define Field Specifications** - Clearly specify field names, types, and constraints +3. **Provide Concrete Examples** - Give clear format examples and templates +4. **Add Validation Standards** - Explain how to verify output correctness +5. **Leverage Context** - Make full use of conversation history and available tools +6. **Preserve Core Intent** - Don't change the fundamental purpose of the original message -{{#toolsContext}} -[Available Tools] -{{toolsContext}} +# Optimization Examples -If tool outputs are consumed, define field mapping, validations, and failure fallbacks. Never fabricate tool outputs. -{{/toolsContext}} -{{^toolsContext}} -[No Tools] -- Avoid tool-dependent directives; propose non-tool validations or placeholders instead. -{{/toolsContext}} +## System Message Optimization (Formatting) +❌ Weak: "You are a data analysis assistant that helps users analyze data" +✅ Strong: "You are a professional data analysis assistant. When analyzing data, output in the following format: -Requirements -- Preserve original intent/style; emphasize output structure/fields/types/examples/error handling/acceptance criteria. -- No explanations, no code fences. +**Output Format**: +\`\`\`json +{ + "summary": "Overall data description (50-100 words)", + "metrics": { + "total_count": number, + "average": number, + "median": number, + "std_dev": number + }, + "insights": [ + "Insight 1: Specific finding with data support", + "Insight 2: Specific finding with data support" + ], + "recommendations": [ + "Recommendation 1: Actionable advice", + "Recommendation 2: Actionable advice" + ] +} +\`\`\` -Output Structure -# Role: [Concise role] -## Profile -- language: [Language] -- description: [Output-format responsibilities] -- background: [Context or conservative assumptions] -- expertise: [Output constraints/validation/compatibility] -- target_audience: [Intended vs non-intended] +**Field Specifications**: +- summary: Required, string type, 50-100 words +- metrics: Required, object type, contains 4 numeric fields +- insights: Required, array type, at least 2 insights +- recommendations: Optional, array type, practical suggestions -## Output Format -- Structure: [JSON/Markdown/Table/etc.] -- Fields: [name/type/required/constraints/examples] -- Variants/Compatibility: [legacy compatibility/version fields] +**Acceptance Criteria**: +- Valid JSON format (parseable with JSON.parse) +- All required fields present with correct types +- Numeric fields are valid numbers (not NaN)" -## Rules -1. Verifiability: each field must be checkable -2. Consistency: align with context/tools; no fabrication -3. Error Handling: strategies for missing/invalid values +**Key Points**: Clear format definition, field specifications, acceptance criteria, format examples -{{#toolsContext}} -## Tools -- Field mapping, timing, consumption, failure fallbacks -{{/toolsContext}} +## User Message Optimization (Formatting) +❌ Weak: "Please analyze this sales data" +✅ Strong: "Please analyze the following sales data and output in the specified format: -## Workflows -- Goal: [Clear] -- Step 1: [Define structure and field constraints] -- Step 2: [(If tools) map tool outputs and validate] -- Step 3: [Consistency checks and tolerance] -- Expected Result: [Meets structure/field/example/error-handling criteria] +**Data**: +\`\`\`json +[ + {"month": "2024-01", "revenue": 120000, "orders": 450}, + {"month": "2024-02", "revenue": 135000, "orders": 520}, + {"month": "2024-03", "revenue": 128000, "orders": 480} +] +\`\`\` -## Initialization -Respect context/tool constraints; apply conservative strategy when missing. Output ONLY the optimized prompt. -` +**Expected Output Format**: +1. **Data Overview** (Markdown table) + - Revenue, orders, and average order value for each month +2. **Trend Analysis** (Structured list) + - Revenue trend: [Growing/Declining/Stable] + percentage + - Order trend: [Growing/Declining/Stable] + percentage +3. **Anomaly Detection** (if any) + - List anomalous months with suspected reasons +4. **Recommendations** (Numbered list) + - At least 3 specific actionable suggestions + +**Format Requirements**: +- Use Markdown formatting +- Present data in tables +- Bold trend indicators +- Use numbered lists for recommendations" + +**Key Points**: Clear data format, output structure, format requirements, content specifications + +## Assistant Message Optimization (Formatting) +❌ Weak: "Analysis complete, revenue is growing, orders are increasing, suggest continuing current approach" +✅ Strong: "I have completed the sales data analysis with the following results: + +## 1. Data Overview + +| Month | Revenue | Orders | Avg Order Value | +|-------|---------|--------|-----------------| +| 2024-01 | $120,000 | 450 | $267 | +| 2024-02 | $135,000 | 520 | $260 | +| 2024-03 | $128,000 | 480 | $267 | + +## 2. Trend Analysis + +- **Revenue Trend**: Fluctuating growth (+6.7%) + - Feb vs Jan: +12.5% increase + - Mar vs Feb: -5.2% decrease + - Overall: Positive growth + +- **Order Trend**: Fluctuating growth (+6.7%) + - Feb vs Jan: +15.6% increase + - Mar vs Feb: -7.7% decrease + - Overall: Positive growth + +- **Average Order Value**: Stable ($260-$267) + +## 3. Key Insights + +1. February was peak month (likely due to Chinese New Year promotions) +2. March showed decline but still above January baseline +3. AOV is stable; growth mainly from order volume + +## 4. Recommendations + +1. **Replicate February Success**: Analyze Feb promotions for future major holidays +2. **Stabilize AOV**: Launch initiatives to increase average order value (e.g., bundling, minimum purchase discounts) +3. **Fill Off-Season Gaps**: Design campaigns for months like March +4. **Data Monitoring**: Establish monthly KPI dashboard to track anomalies in real-time" + +**Key Points**: Structured output, data in tables, trends visualized, actionable recommendations + +# Optimization Checklist + +After completing optimization, please self-check: +- ✓ Are formatting elements used (lists/tables/code blocks)? +- ✓ Are fields and structure clearly defined? +- ✓ Are concrete format examples provided? +- ✓ Are validation/acceptance criteria specified? +- ✓ Is it coordinated and consistent with context? +- ✓ Is the format readable and parseable? + +# Output Requirements + +⚠️ Strict Requirements: +1. Output the optimized message content directly +2. Do not add prefixes like "Optimized:" +3. Do not use code blocks to surround the content +4. Do not add explanations or comments +5. Keep the same language as the original message +6. Do not change the basic intent of the original message` }, { role: 'user', - content: `Original system prompt: -{{originalPrompt}} -` + content: `# Conversation Context +{{#conversationMessages}} +{{index}}. {{roleLabel}}{{#isSelected}} (TO OPTIMIZE){{/isSelected}}: {{content}} +{{/conversationMessages}} +{{^conversationMessages}} +[This is the first message in the conversation] +{{/conversationMessages}} + +{{#toolsContext}} + +# Available Tools +{{toolsContext}} +{{/toolsContext}} + +# Message to Optimize +{{#selectedMessage}} +Message #{{index}} ({{roleLabel}}) +Content: {{#contentTooLong}}{{contentPreview}}... (See message #{{index}} above for full content){{/contentTooLong}}{{^contentTooLong}}{{content}}{{/contentTooLong}} +{{/selectedMessage}} + +Based on the formatting optimization principles and examples, please output the optimized message content directly:` } ] as MessageTemplate[], metadata: { - version: '1.0.0', - lastModified: 1704067200000, + version: '3.0.0', + lastModified: Date.now(), author: 'System', - description: 'Reinforce output structure/fields/validation/fallbacks under contextual constraints', - templateType: 'contextSystemOptimize', + description: 'Format optimization template - enhanced structure, format specification and output validation (v3.0 - Message Optimization)', + templateType: 'conversationMessageOptimize', language: 'en', variant: 'context', - tags: ['context','system','optimize','format'] + tags: ['context', 'message', 'optimize', 'format', 'english'] }, isBuiltin: true }; - diff --git a/packages/core/src/services/template/electron-proxy.ts b/packages/core/src/services/template/electron-proxy.ts index fa8eab05..0d6d72fa 100644 --- a/packages/core/src/services/template/electron-proxy.ts +++ b/packages/core/src/services/template/electron-proxy.ts @@ -11,7 +11,7 @@ interface ElectronAPI { getTemplates: () => Promise; exportTemplate: (id: string) => Promise; importTemplate: (jsonString: string) => Promise; - listTemplatesByType: (type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate') => Promise; + listTemplatesByType: (type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextUserOptimize' | 'contextIterate' | 'conversationMessageOptimize') => Promise; changeBuiltinTemplateLanguage: (language: BuiltinTemplateLanguage) => Promise; getCurrentBuiltinTemplateLanguage: () => Promise; getSupportedBuiltinTemplateLanguages: () => Promise; @@ -71,7 +71,7 @@ export class ElectronTemplateManagerProxy implements ITemplateManager { return this.electronAPI.importTemplate(jsonString); } - async listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate'): Promise { + async listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextUserOptimize' | 'contextIterate' | 'conversationMessageOptimize'): Promise { return this.electronAPI.listTemplatesByType(type); } diff --git a/packages/core/src/services/template/manager.ts b/packages/core/src/services/template/manager.ts index 845e6765..f9d9906d 100644 --- a/packages/core/src/services/template/manager.ts +++ b/packages/core/src/services/template/manager.ts @@ -274,7 +274,7 @@ export class TemplateManager implements ITemplateManager { /** * List templates by type */ - async listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate'): Promise { + async listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextUserOptimize' | 'contextIterate' | 'conversationMessageOptimize'): Promise { try { const templates = await this.listTemplates(); return templates.filter( diff --git a/packages/core/src/services/template/processor.ts b/packages/core/src/services/template/processor.ts index 55bb765f..8b611c10 100644 --- a/packages/core/src/services/template/processor.ts +++ b/packages/core/src/services/template/processor.ts @@ -19,19 +19,17 @@ export interface TemplateContext { contextMode?: import("../context/types").ContextMode; // 'system' | 'user' // 高级模式上下文(可选) customVariables?: Record; // 自定义变量 - conversationMessages?: ConversationMessage[]; // 会话消息 tools?: ToolDefinition[]; // 工具定义信息 // 格式化的上下文文本(用于模板注入) conversationContext?: string; // 格式化的会话上下文 toolsContext?: string; // 格式化的工具上下文 - // Allow additional string properties for template flexibility - // but with stricter typing than the previous implementation - [key: string]: - | string - | undefined - | Record - | ConversationMessage[] - | ToolDefinition[]; + // 消息优化专用字段 + messageRole?: string; // 选中消息的角色(system/user) + conversationMessages?: any[]; // 带元数据的消息数组(用于模板循环) + selectedMessage?: any; // 选中消息的详细信息(用于模板显示) + // Allow additional properties for template flexibility + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; } /** @@ -121,6 +119,10 @@ export class TemplateProcessor { // 1. 替换模板中的内置变量(如 {{originalPrompt}}) // 2. 自动保留值中的占位符(如 originalPrompt = "写一首{{风格}}的歌") // 3. 支持条件渲染({{#var}}...{{/var}})和循环 + // 确保数组变量至少是空数组,避免 undefined 导致 {{#var}} 块不渲染(Mustache 行为:undefined/null/false 为 false) + // 但是我们需要区分“不存在”和“空数组”吗?对于 {{^var}} 来说,undefined/null/empty array 都是 true(取反) + // 只要保证 context 中传递了正确的 key 即可。 + const renderedContent = Mustache.render(msg.content, context); return { @@ -185,6 +187,7 @@ export class TemplateProcessor { .join("\n\n"); } + /** * 替换会话消息中的变量 * 用于测试阶段实际替换变量 diff --git a/packages/core/src/services/template/static-loader.ts b/packages/core/src/services/template/static-loader.ts index bbb78b06..2d317afe 100644 --- a/packages/core/src/services/template/static-loader.ts +++ b/packages/core/src/services/template/static-loader.ts @@ -16,7 +16,7 @@ export type TemplateType = | 'image2imageOptimize' | 'imageIterate' | 'iterate' - | 'context-system-optimize' + | 'conversation-message-optimize' | 'context-user-optimize' | 'context-iterate'; export type Language = 'zh' | 'en'; @@ -74,7 +74,7 @@ export class StaticLoader { 'image2imageOptimize': { zh: {}, en: {} }, 'imageIterate': { zh: {}, en: {} }, 'iterate': { zh: {}, en: {} }, - 'context-system-optimize': { zh: {}, en: {} }, + 'conversation-message-optimize': { zh: {}, en: {} }, 'context-user-optimize': { zh: {}, en: {} }, 'context-iterate': { zh: {}, en: {} } }; @@ -105,8 +105,8 @@ export class StaticLoader { case 'imageIterate': normalizedType = 'imageIterate'; break; - case 'contextSystemOptimize': - normalizedType = 'context-system-optimize'; + case 'conversationMessageOptimize': + normalizedType = 'conversation-message-optimize'; break; case 'contextUserOptimize': normalizedType = 'context-user-optimize'; @@ -143,7 +143,7 @@ export class StaticLoader { image2imageOptimize: Object.keys(byType.image2imageOptimize.zh).length + Object.keys(byType.image2imageOptimize.en).length, imageIterate: Object.keys(byType.imageIterate.zh).length + Object.keys(byType.imageIterate.en).length, iterate: Object.keys(byType.iterate.zh).length + Object.keys(byType.iterate.en).length, - 'context-system-optimize': Object.keys(byType['context-system-optimize'].zh).length + Object.keys(byType['context-system-optimize'].en).length, + 'conversation-message-optimize': Object.keys(byType['conversation-message-optimize'].zh).length + Object.keys(byType['conversation-message-optimize'].en).length, 'context-user-optimize': Object.keys(byType['context-user-optimize'].zh).length + Object.keys(byType['context-user-optimize'].en).length, 'context-iterate': Object.keys(byType['context-iterate'].zh).length + Object.keys(byType['context-iterate'].en).length }); diff --git a/packages/core/src/services/template/types.ts b/packages/core/src/services/template/types.ts index 14119ec9..90f4cf1e 100644 --- a/packages/core/src/services/template/types.ts +++ b/packages/core/src/services/template/types.ts @@ -11,7 +11,7 @@ export interface TemplateMetadata { lastModified: number; // 最后修改时间 author?: string; // 作者(可选) description?: string; // 描述(可选) - templateType: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate'; // 模板类型标识 + templateType: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'conversationMessageOptimize' | 'contextUserOptimize' | 'contextIterate'; // 模板类型标识 language?: 'zh' | 'en'; // 模板语言(可选,主要用于内置模板语言切换) [key: string]: any; // 允许任意额外字段 } @@ -82,7 +82,7 @@ export interface ITemplateManager extends IImportExportable { /** * List templates by type */ - listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextSystemOptimize' | 'contextUserOptimize' | 'contextIterate'): Promise; + listTemplatesByType(type: 'optimize' | 'userOptimize' | 'text2imageOptimize' | 'image2imageOptimize' | 'imageIterate' | 'iterate' | 'contextUserOptimize' | 'contextIterate' | 'conversationMessageOptimize'): Promise; /** * Change built-in template language @@ -123,7 +123,7 @@ export const templateSchema = z.object({ lastModified: z.number(), author: z.string().optional(), description: z.string().optional(), - templateType: z.enum(['optimize', 'userOptimize', 'text2imageOptimize', 'image2imageOptimize', 'imageIterate', 'iterate', 'contextSystemOptimize', 'contextUserOptimize', 'contextIterate']), + templateType: z.enum(['optimize', 'userOptimize', 'text2imageOptimize', 'image2imageOptimize', 'imageIterate', 'iterate', 'conversationMessageOptimize', 'contextUserOptimize', 'contextIterate']), language: z.enum(['zh', 'en']).optional() }).passthrough(), // 允许额外字段通过验证 isBuiltin: z.boolean().optional() diff --git a/packages/core/tests/services/favorite/integration.test.ts b/packages/core/tests/services/favorite/integration.test.ts index 1b18e2b1..6ab6deca 100644 --- a/packages/core/tests/services/favorite/integration.test.ts +++ b/packages/core/tests/services/favorite/integration.test.ts @@ -305,7 +305,7 @@ describe('FavoriteManager - 集成测试', () => { 'userOptimize', 'iterate', 'test', - 'contextSystemOptimize', + 'conversationMessageOptimize', 'contextUserOptimize', 'contextIterate', 'imageOptimize', diff --git a/packages/core/tests/services/favorite/manager-extended.test.ts b/packages/core/tests/services/favorite/manager-extended.test.ts index 56ff43bf..2305acc9 100644 --- a/packages/core/tests/services/favorite/manager-extended.test.ts +++ b/packages/core/tests/services/favorite/manager-extended.test.ts @@ -481,7 +481,7 @@ describe('FavoriteManager - 扩展功能', () => { const testTypes = [ 'optimize', 'userOptimize', - 'contextSystemOptimize', + 'conversationMessageOptimize', 'contextUserOptimize', 'imageOptimize', 'text2imageOptimize', diff --git a/packages/core/tests/services/favorite/type-mapper.test.ts b/packages/core/tests/services/favorite/type-mapper.test.ts index aa18dad3..1ab4e060 100644 --- a/packages/core/tests/services/favorite/type-mapper.test.ts +++ b/packages/core/tests/services/favorite/type-mapper.test.ts @@ -42,8 +42,8 @@ describe('TypeMapper', () => { }); describe('mapFromRecordType - 上下文模式映射', () => { - it('应该将 contextSystemOptimize 映射为 context/system', () => { - const result = TypeMapper.mapFromRecordType('contextSystemOptimize'); + it('应该将 conversationMessageOptimize 映射为 context/system', () => { + const result = TypeMapper.mapFromRecordType('conversationMessageOptimize'); expect(result).toEqual({ functionMode: 'context', optimizationMode: 'system' @@ -278,13 +278,13 @@ describe('TypeMapper', () => { expect(result).toEqual(['userOptimize']); }); - it('应该从 context/system 推断出 contextSystemOptimize 和 contextIterate', () => { + it('应该从 context/system 推断出 conversationMessageOptimize 和 contextIterate', () => { const mapping: FunctionModeMapping = { functionMode: 'context', optimizationMode: 'system' }; const result = TypeMapper.inferRecordTypes(mapping); - expect(result).toEqual(['contextSystemOptimize', 'contextIterate']); + expect(result).toEqual(['conversationMessageOptimize', 'contextIterate']); }); it('应该从 context/user 推断出 contextUserOptimize', () => { @@ -336,7 +336,7 @@ describe('TypeMapper', () => { 'userOptimize', 'iterate', 'test', - 'contextSystemOptimize', + 'conversationMessageOptimize', 'contextUserOptimize', 'contextIterate', 'imageOptimize', @@ -366,7 +366,7 @@ describe('TypeMapper', () => { mapping: { functionMode: 'basic', optimizationMode: 'user' } }, { - recordType: 'contextSystemOptimize', + recordType: 'conversationMessageOptimize', mapping: { functionMode: 'context', optimizationMode: 'system' } }, { diff --git a/packages/core/tests/unit/template/context-types-manager.test.ts b/packages/core/tests/unit/template/context-types-manager.test.ts index 9dc66175..0031c9be 100644 --- a/packages/core/tests/unit/template/context-types-manager.test.ts +++ b/packages/core/tests/unit/template/context-types-manager.test.ts @@ -36,12 +36,12 @@ class StubLang implements ITemplateLanguageService { } describe('TemplateManager list by context types', () => { - it('returns contextSystemOptimize templates', async () => { + it('returns conversationMessageOptimize templates', async () => { const tm = new TemplateManager(new MemoryStorage(), new StubLang()) - const list = await tm.listTemplatesByType('contextSystemOptimize') + const list = await tm.listTemplatesByType('conversationMessageOptimize') expect(Array.isArray(list)).toBe(true) // 允许为空(取决于内置模板语言),但如存在则类型必须匹配 - for (const t of list) expect(t.metadata.templateType).toBe('contextSystemOptimize') + for (const t of list) expect(t.metadata.templateType).toBe('conversationMessageOptimize') }) it('returns contextUserOptimize templates', async () => { diff --git a/packages/core/tests/unit/template/context-types-schema.test.ts b/packages/core/tests/unit/template/context-types-schema.test.ts index ca9e9472..1c55df88 100644 --- a/packages/core/tests/unit/template/context-types-schema.test.ts +++ b/packages/core/tests/unit/template/context-types-schema.test.ts @@ -12,10 +12,10 @@ const base = { } describe('template schema - context types', () => { - it('accepts contextSystemOptimize', () => { + it('accepts conversationMessageOptimize', () => { const result = templateSchema.safeParse({ ...base, - metadata: { ...base.metadata, templateType: 'contextSystemOptimize' } + metadata: { ...base.metadata, templateType: 'conversationMessageOptimize' } }) expect(result.success).toBe(true) }) diff --git a/packages/desktop/main.js b/packages/desktop/main.js index f302911e..5da0609b 100644 --- a/packages/desktop/main.js +++ b/packages/desktop/main.js @@ -812,10 +812,10 @@ function setupIPC() { } }); - ipcMain.handle('prompt-iteratePromptStream', async (event, originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, streamId) => { + ipcMain.handle('prompt-iteratePromptStream', async (event, originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, streamId, contextData) => { const streamHandlers = createIpcStreamHandlers(mainWindow, streamId); try { - await promptService.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, streamHandlers, templateId); + await promptService.iteratePromptStream(originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, streamHandlers, templateId, contextData); return createSuccessResponse(null); } catch (error) { streamHandlers.onError(error); diff --git a/packages/desktop/preload.js b/packages/desktop/preload.js index 53fcd116..c31577db 100644 --- a/packages/desktop/preload.js +++ b/packages/desktop/preload.js @@ -704,7 +704,7 @@ contextBridge.exposeInMainWorld('electronAPI', { throw new Error(result.error); } }, - iteratePromptStream: async (originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks) => { + iteratePromptStream: async (originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, callbacks, contextData) => { const streamId = generateStreamId(); const tokenListener = (event, token) => { @@ -734,7 +734,7 @@ contextBridge.exposeInMainWorld('electronAPI', { ipcRenderer.on(`stream-finish-${streamId}`, finishListener); ipcRenderer.on(`stream-error-${streamId}`, errorListener); - const result = await ipcRenderer.invoke('prompt-iteratePromptStream', originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, streamId); + const result = await ipcRenderer.invoke('prompt-iteratePromptStream', originalPrompt, lastOptimizedPrompt, iterateInput, modelKey, templateId, streamId, contextData); if (!result.success) { cleanup(); throw new Error(result.error); diff --git a/packages/extension/src/App.vue b/packages/extension/src/App.vue index b2201dbd..41aa108d 100644 --- a/packages/extension/src/App.vue +++ b/packages/extension/src/App.vue @@ -31,6 +31,7 @@ @@ -38,6 +39,7 @@ @@ -132,19 +134,11 @@