feat: 增加预设提示词

This commit is contained in:
digua
2025-12-06 13:36:29 +08:00
parent 2048d5d70d
commit 2dafe00bf9
5 changed files with 450 additions and 166 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ defineExpose({
<template>
<div class="flex h-full flex-col">
<!-- Tab 导航 -->
<SubTabs v-model="activeSubTab" :items="subTabs" />
<SubTabs class="hidden" v-model="activeSubTab" :items="subTabs" />
<!-- Tab 内容 -->
<div class="flex-1 min-h-0 overflow-hidden">
@@ -2,6 +2,12 @@
import { ref, computed, watch } from 'vue'
import { useChatStore } from '@/stores/chat'
import type { PromptPreset } from '@/types/chat'
import {
getDefaultRoleDefinition,
getDefaultResponseRules,
getLockedPromptSectionPreview,
getOriginalBuiltinPreset,
} from '@/config/prompts'
// Props
const props = defineProps<{
@@ -31,47 +37,20 @@ const formData = ref({
// 计算属性
const isBuiltIn = computed(() => props.preset?.isBuiltIn ?? false)
const isEditMode = computed(() => props.mode === 'edit')
const isModified = computed(() => {
if (!isBuiltIn.value || !props.preset) return false
return chatStore.isBuiltinPresetModified(props.preset.id)
})
const modalTitle = computed(() => {
if (isBuiltIn.value) return '查看系统提示词'
return isEditMode.value ? '编辑系统提示词' : '添加系统提示词'
if (isBuiltIn.value) return '编辑系统提示词'
return isEditMode.value ? '编辑自定义提示词' : '添加自定义提示词'
})
const canSave = computed(() => {
return formData.value.name.trim() && formData.value.roleDefinition.trim() && formData.value.responseRules.trim()
})
// 获取默认角色定义
function getDefaultRoleDefinition(chatType: 'group' | 'private'): string {
if (chatType === 'private') {
return `你是一个专业的私聊记录分析助手。
你的任务是帮助用户理解和分析他们的私聊记录数据。
注意:这是一个私聊对话,只有两个人参与。你的分析应该关注:
- 两人之间的对话互动
- 谁更主动、谁回复更多
- 对话的主题和内容变化
- 不要使用"群"这个词,使用"对话"或"聊天"`
}
return `你是一个专业的群聊记录分析助手。
你的任务是帮助用户理解和分析他们的群聊记录数据。`
}
// 获取默认回答要求
function getDefaultResponseRules(chatType: 'group' | 'private'): string {
if (chatType === 'private') {
return `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 关注两人之间的互动模式和对话特点`
}
return `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 对于统计数据,可以适当总结趋势和特点`
}
// 监听打开状态,初始化表单
watch(
() => props.open,
@@ -104,10 +83,10 @@ function closeModal() {
}
function handleSave() {
if (!canSave.value || isBuiltIn.value) return
if (!canSave.value) return
if (isEditMode.value && props.preset) {
// 更新现有预设
// 更新现有预设(支持内置和自定义)
chatStore.updatePromptPreset(props.preset.id, {
name: formData.value.name.trim(),
chatType: formData.value.chatType,
@@ -128,60 +107,32 @@ function handleSave() {
closeModal()
}
// 预览完整提示词(始终展示)
// 重置内置预设为原始值
function handleReset() {
if (!props.preset || !isBuiltIn.value) return
// 获取锁定部分的提示词(与 Agent 中的一致)
function getLockedPromptSection(chatType: string): string {
const now = new Date()
const currentDate = now.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
})
const isPrivate = chatType === 'private'
const chatTypeDesc = isPrivate ? '私聊记录' : '群聊记录'
const memberNote = isPrivate
? `成员查询策略:
- 私聊只有两个人,可以直接获取成员列表
- 当用户提到"对方"、"他/她"时,通过 get_group_members 获取另一方信息`
: `成员查询策略:
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
- 群成员有三种名称:accountName(原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言`
return `当前日期是 ${currentDate}
你可以使用以下工具来获取${chatTypeDesc}数据:
1. search_messages - 根据关键词搜索聊天记录,可指定 year/month 筛选时间段,也可指定 sender_id 筛选特定成员的发言
2. get_recent_messages - 获取指定时间段的聊天消息,可指定 year 和 month
3. get_member_stats - 获取成员活跃度统计
4. get_time_stats - 获取时间分布统计
5. get_group_members - 获取成员列表,包括 id、QQ号、账号名称、昵称、别名和消息统计
6. get_member_name_history - 获取成员的昵称变更历史,需要先通过 get_group_members 获取成员 ID
7. get_conversation_between - 获取两个成员之间的对话记录,需要先通过 get_group_members 获取两人的成员 ID
${memberNote}
时间处理要求:
- 如果用户提到"X月"但没有指定年份,默认使用当前年份(${now.getFullYear()}年)
- 如果当前月份还没到用户提到的月份,则使用去年
- 例如:现在是${now.getFullYear()}${now.getMonth() + 1}月,用户问"10月的聊天"应该查询${now.getMonth() + 1 >= 10 ? now.getFullYear() : now.getFullYear() - 1}年10月
根据用户的问题,选择合适的工具获取数据,然后基于数据给出回答。`
const original = getOriginalBuiltinPreset(props.preset.id)
if (original) {
// 重置表单为原始值
formData.value = {
name: original.name,
chatType: original.chatType,
roleDefinition: original.roleDefinition,
responseRules: original.responseRules,
}
// 清除覆盖
chatStore.resetBuiltinPreset(props.preset.id)
}
}
// 完整提示词预览
const previewContent = computed(() => {
const chatType = formData.value.chatType
// 获取锁定的系统部分
const lockedSection = getLockedPromptSection(chatType)
// 获取锁定的系统部分(用于预览)
const lockedSection = getLockedPromptSectionPreview(chatType)
// 组合完整提示词(与 Agent 中的 buildSystemPrompt 保持一致)
// 组合完整提示词
return `${formData.value.roleDefinition}
${lockedSection}
@@ -204,7 +155,10 @@ ${formData.value.responseRules}`
<!-- 内置预设提示 -->
<UAlert v-if="isBuiltIn" color="info" variant="outline" icon="i-heroicons-information-circle" class="mb-4">
<template #title>
<span class="text-sm">内置预设不可编辑你可以复制后修改</span>
<span class="text-sm">
这是内置预设修改后可点击"重置"恢复默认值
<span v-if="isModified" class="text-amber-600 dark:text-amber-400">已修改</span>
</span>
</template>
</UAlert>
@@ -213,7 +167,7 @@ ${formData.value.responseRules}`
<!-- 预设名称 -->
<div>
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">预设名称</label>
<UInput v-model="formData.name" placeholder="为预设起个名字" :disabled="isBuiltIn" class="w-60" />
<UInput v-model="formData.name" placeholder="为预设起个名字" class="w-60" />
</div>
<!-- 适用类型只读显示 -->
@@ -232,7 +186,6 @@ ${formData.value.responseRules}`
v-model="formData.roleDefinition"
:rows="8"
placeholder="定义 AI 助手的角色和任务..."
:disabled="isBuiltIn"
class="font-mono text-sm w-120"
/>
</div>
@@ -247,7 +200,6 @@ ${formData.value.responseRules}`
v-model="formData.responseRules"
:rows="5"
placeholder="定义 AI 回答的格式和要求..."
:disabled="isBuiltIn"
class="font-mono text-sm w-120"
/>
</div>
@@ -266,8 +218,13 @@ ${formData.value.responseRules}`
<!-- Footer -->
<div class="mt-6 flex justify-end gap-2">
<!-- 内置预设显示重置按钮 -->
<UButton v-if="isBuiltIn && isModified" variant="outline" color="warning" @click="handleReset">
<UIcon name="i-heroicons-arrow-path" class="mr-1 h-4 w-4" />
重置为默认
</UButton>
<UButton variant="ghost" @click="closeModal">取消</UButton>
<UButton v-if="!isBuiltIn" color="primary" :disabled="!canSave" @click="handleSave">
<UButton color="primary" :disabled="!canSave" @click="handleSave">
{{ isEditMode ? '保存修改' : '添加预设' }}
</UButton>
</div>
+323
View File
@@ -0,0 +1,323 @@
/**
* AI 提示词统一配置
*
* 本文件集中管理所有 AI 提示词相关的配置:
* - 内置预设定义
* - 默认角色定义/回答要求
* - 锁定部分说明(用于前端预览)
*
* 主进程 (agent.ts) 的锁定部分逻辑需要独立维护,因为包含动态日期
*/
import type { PromptPreset } from '@/types/chat'
// ==================== 预设 ID 常量 ====================
/** 默认群聊预设ID */
export const DEFAULT_GROUP_PRESET_ID = 'builtin-group-default'
/** 默认私聊预设ID */
export const DEFAULT_PRIVATE_PRESET_ID = 'builtin-private-default'
/** 赛博判官群聊预设ID */
export const CYBER_JUDGE_GROUP_PRESET_ID = 'builtin-group-cyber-judge'
/** 赛博判官私聊预设ID */
export const CYBER_JUDGE_PRIVATE_PRESET_ID = 'builtin-private-cyber-judge'
/** 弱智吧群聊预设ID */
export const RUOZHI_GROUP_PRESET_ID = 'builtin-group-ruozhi'
/** 弱智吧私聊预设ID */
export const RUOZHI_PRIVATE_PRESET_ID = 'builtin-private-ruozhi'
// ==================== 默认提示词内容 ====================
/**
* 获取默认角色定义
* @param chatType 聊天类型
* @param style 风格(可选)
*/
export function getDefaultRoleDefinition(
chatType: 'group' | 'private',
style: 'default' | 'cyber-judge' | 'ruozhi' = 'default'
): string {
const chatTypeDesc = chatType === 'private' ? '私聊' : '群聊'
// 赛博判官风格
if (style === 'cyber-judge') {
return `你是一个混迹互联网多年的"赛博判官"和"顶级嘴替"。
你的任务是基于工具提供的${chatTypeDesc}记录数据,对用户进行"降维打击"式的分析。
【人设要求】
1. **拒绝爹味**:不要用"助手"的口吻,要用"吃瓜群众"或"毒舌朋友"的语气。
2. **疯狂玩梗**:熟练使用大陆互联网黑话(如:破防、下头、cpu、画饼、舔狗、纯爱战士、小丑🤡、汗流浃背)。
3. **一针见血**:透过数据看本质。比如发消息字数多但回复少,直接定性为"深情小丑";比如深夜频繁发消息,定性为"网抑云选手"。`
}
// 弱智吧风格
if (style === 'ruozhi') {
return `你是一个来自"弱智吧"的资深吧友,也是一个重度"抽象话"使用者。
你的任务是用最离谱的角度去分析最正常的${chatTypeDesc}记录。
【人设要求】
1. **脑回路清奇**:不要从正常人的角度分析,要关注奇怪的点。比如关注谁发的表情包最土,谁最喜欢在半夜三点不睡觉。
2. **抽象哲学**:金句频出,逻辑自洽但荒谬。
3. **语气**:一本正经的呆萌,或者是那种"大聪明"的感觉。`
}
// 默认风格
if (chatType === 'private') {
return `你是一个专业的私聊记录分析助手。
你的任务是帮助用户理解和分析他们的私聊记录数据。
注意:这是一个私聊对话,只有两个人参与。你的分析应该关注:
- 两人之间的对话互动
- 谁更主动、谁回复更多
- 对话的主题和内容变化
- 不要使用"群"这个词,使用"对话"或"聊天"`
}
return `你是一个专业的群聊记录分析助手。
你的任务是帮助用户理解和分析他们的群聊记录数据。`
}
/**
* 获取默认回答要求
* @param chatType 聊天类型
* @param style 风格(可选)
*/
export function getDefaultResponseRules(
chatType: 'group' | 'private',
style: 'default' | 'cyber-judge' | 'ruozhi' = 'default'
): string {
// 赛博判官风格
if (style === 'cyber-judge') {
const example =
chatType === 'private'
? `【示例风格】
用户问:分析我和女神的聊天。
你回:
👉 **数据审判**:你一共发了 5000 字,她回了 200 字。
🤡 **成分查询**:典型的一厢情愿型"赛博沸羊羊"。
📉 **扎心结论**:她回"嗯"的时候,可能正在跟别人打王者。建议把这股劲头拿去送外卖,三天能买一辆电动车。`
: `【示例风格】
用户问:群里谁最话唠?
你回:
👉 **数据审判**:张三发言 10000 条,平均每天 50 条。
🤡 **成分查询**:典型的"赛博话痨",疑似社交牛逼症晚期。
📉 **扎心结论**:建议去参加脱口秀海选,这嘴皮子不当演员可惜了。`
return `【回答原则】
1. **基于数据(但要过度解读)**:数据必须真实(基于工具返回),但结论可以大胆推测。例如:如果某人经常在工作时间发消息,你可以说他"带薪拉屎时长惊人"。
2. **情绪价值拉满**:要么让人捧腹大笑,要么让人破防扎心。
3. **格式要求**
- 多用 Emoji(🤡💔👉🐷)。
- 结论要短,要有冲击力。
- 引用原文时,要加上你的辣评。
${example}`
}
// 弱智吧风格
if (style === 'ruozhi') {
const example =
chatType === 'private'
? `【示例风格】
用户问:分析一下我俩的聊天。
你回:
经过本 AI 的精密计算:
1. 你发了 3000 条消息,对方发了 500 条。差值刚好是一部《红楼梦》的字数。
2. 对方回复你的平均速度是 3 小时,刚好够看完一部电影。**建议**:下次发消息前先问问自己是不是在给空气表演。
3. **结论**:你俩的聊天记录适合做成睡前故事——给自己催眠用的那种。`
: `【示例风格】
用户问:统计一下群里的摸鱼情况。
你回:
经过本 AI 的精密计算:
1. 群友 A 每天在群里说废话的时间够炸 300 根油条。
2. 群友 B 每次出现都是在饭点,疑似为了掩盖自己是"饭桶"的事实。
3. **结论**:这个群凑不出一个心眼子,建议全员保送幼儿园。`
return `【回答原则】
1. **数据转化**:把枯燥的数据转化为奇怪的度量衡。比如"他撤回的消息连起来能绕地球一圈"。
2. **关注边缘信息**:如果有群昵称、表情包、撤回记录,重点分析这些。
3. **Markdown格式**:要像写"人类观察日记"一样。
${example}`
}
// 默认风格
if (chatType === 'private') {
return `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 关注两人之间的互动模式和对话特点`
}
return `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 对于统计数据,可以适当总结趋势和特点`
}
// ==================== 内置预设定义 ====================
/** 内置群聊预设 - 默认 */
const BUILTIN_GROUP_DEFAULT: PromptPreset = {
id: DEFAULT_GROUP_PRESET_ID,
name: '默认群聊分析',
chatType: 'group',
roleDefinition: getDefaultRoleDefinition('group', 'default'),
responseRules: getDefaultResponseRules('group', 'default'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置群聊预设 - 赛博判官 */
const BUILTIN_GROUP_CYBER_JUDGE: PromptPreset = {
id: CYBER_JUDGE_GROUP_PRESET_ID,
name: '赛博判官',
chatType: 'group',
roleDefinition: getDefaultRoleDefinition('group', 'cyber-judge'),
responseRules: getDefaultResponseRules('group', 'cyber-judge'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置私聊预设 - 默认 */
const BUILTIN_PRIVATE_DEFAULT: PromptPreset = {
id: DEFAULT_PRIVATE_PRESET_ID,
name: '默认私聊分析',
chatType: 'private',
roleDefinition: getDefaultRoleDefinition('private', 'default'),
responseRules: getDefaultResponseRules('private', 'default'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置私聊预设 - 赛博判官 */
const BUILTIN_PRIVATE_CYBER_JUDGE: PromptPreset = {
id: CYBER_JUDGE_PRIVATE_PRESET_ID,
name: '赛博判官',
chatType: 'private',
roleDefinition: getDefaultRoleDefinition('private', 'cyber-judge'),
responseRules: getDefaultResponseRules('private', 'cyber-judge'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置群聊预设 - 弱智吧 */
const BUILTIN_GROUP_RUOZHI: PromptPreset = {
id: RUOZHI_GROUP_PRESET_ID,
name: '弱智吧',
chatType: 'group',
roleDefinition: getDefaultRoleDefinition('group', 'ruozhi'),
responseRules: getDefaultResponseRules('group', 'ruozhi'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置私聊预设 - 弱智吧 */
const BUILTIN_PRIVATE_RUOZHI: PromptPreset = {
id: RUOZHI_PRIVATE_PRESET_ID,
name: '弱智吧',
chatType: 'private',
roleDefinition: getDefaultRoleDefinition('private', 'ruozhi'),
responseRules: getDefaultResponseRules('private', 'ruozhi'),
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 所有内置预设(原始版本,用于重置) */
export const BUILTIN_PRESETS: PromptPreset[] = [
BUILTIN_GROUP_DEFAULT,
BUILTIN_GROUP_CYBER_JUDGE,
BUILTIN_GROUP_RUOZHI,
BUILTIN_PRIVATE_DEFAULT,
BUILTIN_PRIVATE_CYBER_JUDGE,
BUILTIN_PRIVATE_RUOZHI,
]
/**
* 获取内置预设的原始版本(用于重置)
* @param presetId 预设ID
*/
export function getOriginalBuiltinPreset(presetId: string): PromptPreset | undefined {
return BUILTIN_PRESETS.find((p) => p.id === presetId)
}
// ==================== 锁定部分预览(仅用于前端展示) ====================
/**
* 获取锁定部分的提示词预览
* 注意:实际执行时由主进程 agent.ts 生成,包含动态日期
*
* @param chatType 聊天类型
*/
export function getLockedPromptSectionPreview(chatType: 'group' | 'private'): string {
const now = new Date()
const currentDate = now.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
})
const isPrivate = chatType === 'private'
const chatTypeDesc = isPrivate ? '私聊记录' : '群聊记录'
const memberNote = isPrivate
? `成员查询策略:
- 私聊只有两个人,可以直接获取成员列表
- 当用户提到"对方"、"他/她"时,通过 get_group_members 获取另一方信息`
: `成员查询策略:
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
- 群成员有三种名称:accountName(原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言`
return `当前日期是 ${currentDate}
你可以使用以下工具来获取${chatTypeDesc}数据:
1. search_messages - 根据关键词搜索聊天记录,可指定 year/month 筛选时间段,也可指定 sender_id 筛选特定成员的发言
2. get_recent_messages - 获取指定时间段的聊天消息,可指定 year 和 month
3. get_member_stats - 获取成员活跃度统计
4. get_time_stats - 获取时间分布统计
5. get_group_members - 获取成员列表,包括 id、QQ号、账号名称、昵称、别名和消息统计
6. get_member_name_history - 获取成员的昵称变更历史,需要先通过 get_group_members 获取成员 ID
7. get_conversation_between - 获取两个成员之间的对话记录,需要先通过 get_group_members 获取两人的成员 ID
${memberNote}
时间处理要求:
- 如果用户提到"X月"但没有指定年份,默认使用当前年份(${now.getFullYear()}年)
- 如果当前月份还没到用户提到的月份,则使用去年
- 例如:现在是${now.getFullYear()}${now.getMonth() + 1}月,用户问"10月的聊天"应该查询${now.getMonth() + 1 >= 10 ? now.getFullYear() : now.getFullYear() - 1}年10月
根据用户的问题,选择合适的工具获取数据,然后基于数据给出回答。`
}
/**
* 构建完整提示词预览(用于前端展示)
* @param roleDefinition 角色定义
* @param responseRules 回答要求
* @param chatType 聊天类型
*/
export function buildPromptPreview(
roleDefinition: string,
responseRules: string,
chatType: 'group' | 'private'
): string {
const lockedSection = getLockedPromptSectionPreview(chatType)
return `${roleDefinition}
${lockedSection}
回答要求:
${responseRules}`
}
+62 -52
View File
@@ -1,56 +1,15 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { AnalysisSession, ImportProgress, KeywordTemplate, PromptPreset, AIPromptSettings } from '@/types/chat'
import {
BUILTIN_PRESETS,
DEFAULT_GROUP_PRESET_ID,
DEFAULT_PRIVATE_PRESET_ID,
getOriginalBuiltinPreset,
} from '@/config/prompts'
// ==================== 内置提示词预设 ====================
/** 默认群聊预设ID */
export const DEFAULT_GROUP_PRESET_ID = 'builtin-group-default'
/** 默认私聊预设ID */
export const DEFAULT_PRIVATE_PRESET_ID = 'builtin-private-default'
/** 内置群聊预设 */
const BUILTIN_GROUP_PRESET: PromptPreset = {
id: DEFAULT_GROUP_PRESET_ID,
name: '默认群聊分析',
chatType: 'group',
roleDefinition: `你是一个专业的群聊记录分析助手。
你的任务是帮助用户理解和分析他们的群聊记录数据。`,
responseRules: `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 对于统计数据,可以适当总结趋势和特点`,
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 内置私聊预设 */
const BUILTIN_PRIVATE_PRESET: PromptPreset = {
id: DEFAULT_PRIVATE_PRESET_ID,
name: '默认私聊分析',
chatType: 'private',
roleDefinition: `你是一个专业的私聊记录分析助手。
你的任务是帮助用户理解和分析他们的私聊记录数据。
注意:这是一个私聊对话,只有两个人参与。你的分析应该关注:
- 两人之间的对话互动
- 谁更主动、谁回复更多
- 对话的主题和内容变化
- 不要使用"群"这个词,使用"对话"或"聊天"`,
responseRules: `1. 基于工具返回的数据回答,不要编造信息
2. 如果数据不足以回答问题,请说明
3. 回答要简洁明了,使用 Markdown 格式
4. 可以引用具体的发言作为证据
5. 关注两人之间的互动模式和对话特点`,
isBuiltIn: true,
createdAt: Date.now(),
updatedAt: Date.now(),
}
/** 所有内置预设 */
export const BUILTIN_PRESETS: PromptPreset[] = [BUILTIN_GROUP_PRESET, BUILTIN_PRIVATE_PRESET]
// 重新导出常量,保持向后兼容
export { DEFAULT_GROUP_PRESET_ID, DEFAULT_PRIVATE_PRESET_ID, BUILTIN_PRESETS }
export const useChatStore = defineStore(
'chat',
@@ -330,13 +289,30 @@ export const useChatStore = defineStore(
// ==================== AI 提示词预设管理 ====================
const customPromptPresets = ref<PromptPreset[]>([])
/** 内置预设的用户覆盖(key: presetId, value: 覆盖的字段) */
const builtinPresetOverrides = ref<
Record<string, { name?: string; roleDefinition?: string; responseRules?: string; updatedAt?: number }>
>({})
const aiPromptSettings = ref<AIPromptSettings>({
activeGroupPresetId: DEFAULT_GROUP_PRESET_ID,
activePrivatePresetId: DEFAULT_PRIVATE_PRESET_ID,
})
/** 获取所有预设(内置 + 自定义) */
const allPromptPresets = computed(() => [...BUILTIN_PRESETS, ...customPromptPresets.value])
/** 获取所有预设(内置+覆盖 + 自定义) */
const allPromptPresets = computed(() => {
// 合并内置预设和用户覆盖
const mergedBuiltins = BUILTIN_PRESETS.map((preset) => {
const override = builtinPresetOverrides.value[preset.id]
if (override) {
return {
...preset,
...override,
}
}
return preset
})
return [...mergedBuiltins, ...customPromptPresets.value]
})
/** 获取群聊可用的预设 */
const groupPresets = computed(() => allPromptPresets.value.filter((p) => p.chatType === 'group'))
@@ -374,11 +350,26 @@ export const useChatStore = defineStore(
return newPreset.id
}
/** 更新预设 */
/** 更新预设(支持内置和自定义) */
function updatePromptPreset(
presetId: string,
updates: { name?: string; chatType?: PromptPreset['chatType']; roleDefinition?: string; responseRules?: string }
) {
// 检查是否是内置预设
const isBuiltin = BUILTIN_PRESETS.some((p) => p.id === presetId)
if (isBuiltin) {
// 更新内置预设的覆盖
builtinPresetOverrides.value[presetId] = {
...builtinPresetOverrides.value[presetId],
name: updates.name,
roleDefinition: updates.roleDefinition,
responseRules: updates.responseRules,
updatedAt: Date.now(),
}
return
}
// 更新自定义预设
const index = customPromptPresets.value.findIndex((p) => p.id === presetId)
if (index !== -1) {
customPromptPresets.value[index] = {
@@ -389,6 +380,21 @@ export const useChatStore = defineStore(
}
}
/** 重置内置预设为原始值 */
function resetBuiltinPreset(presetId: string): boolean {
const original = getOriginalBuiltinPreset(presetId)
if (!original) return false
// 删除覆盖
delete builtinPresetOverrides.value[presetId]
return true
}
/** 检查内置预设是否被修改过 */
function isBuiltinPresetModified(presetId: string): boolean {
return !!builtinPresetOverrides.value[presetId]
}
/** 删除自定义预设 */
function removePromptPreset(presetId: string) {
const index = customPromptPresets.value.findIndex((p) => p.id === presetId)
@@ -461,6 +467,7 @@ export const useChatStore = defineStore(
customKeywordTemplates,
deletedPresetTemplateIds,
customPromptPresets,
builtinPresetOverrides,
aiPromptSettings,
// Computed
currentSession,
@@ -491,6 +498,8 @@ export const useChatStore = defineStore(
setActiveGroupPreset,
setActivePrivatePreset,
getActivePresetForChatType,
resetBuiltinPreset,
isBuiltinPresetModified,
}
},
{
@@ -507,6 +516,7 @@ export const useChatStore = defineStore(
'deletedPresetTemplateIds',
'aiGlobalSettings',
'customPromptPresets',
'builtinPresetOverrides',
'aiPromptSettings',
],
storage: localStorage,