mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-27 17:30:23 +08:00
feat: 增加预设提示词
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user