feat: AI对话中,系统提示词中支持传递给用户身份

This commit is contained in:
digua committed 2025-12-25 00:58:15 +08:00
1 parent 18138a3563
commit ce436da526
8 files changed
+124 -34

No files matched your search

+23 -8
View File
@@ -6,7 +6,7 @@
import type { ChatMessage, ChatOptions, ChatStreamChunk, ToolCall } from './llm/types'
import { chatStream, chat } from './llm'
import { getAllToolDefinitions, executeToolCalls } from './tools'
import type { ToolContext } from './tools/types'
import type { ToolContext, OwnerInfo } from './tools/types'
import { aiLogger } from './logger'
import { randomUUID } from 'crypto'
@@ -139,8 +139,9 @@ export interface PromptConfig {
/**
* 获取系统锁定部分的提示词(工具说明、时间处理等)
* @param chatType 聊天类型 ('group' | 'private')
* @param ownerInfo Owner 信息(当前用户在对话中的身份)
*/
function getLockedPromptSection(chatType: 'group' | 'private'): string {
function getLockedPromptSection(chatType: 'group' | 'private', ownerInfo?: OwnerInfo): string {
const now = new Date()
const currentDate = now.toLocaleDateString('zh-CN', {
year: 'numeric',
@@ -152,6 +153,15 @@ function getLockedPromptSection(chatType: 'group' | 'private'): string {
const isPrivate = chatType === 'private'
const chatTypeDesc = isPrivate ? '私聊记录' : '群聊记录'
// Owner 说明(当用户设置了"我是谁"时)
const ownerNote = ownerInfo
? `当前用户身份:
- 用户在${isPrivate ? '对话' : '群聊'}中的身份是「${ownerInfo.displayName}」(platformId: ${ownerInfo.platformId})
- 当用户提到"我"、"我的"时,指的就是「${ownerInfo.displayName}」
- 查询"我"的发言时,使用 sender_id 参数筛选该成员
`
: ''
// 成员说明(私聊只有2人)
const memberNote = isPrivate
? `成员查询策略:
@@ -166,7 +176,7 @@ function getLockedPromptSection(chatType: 'group' | 'private'): string {
`
return `当前日期是 ${currentDate}。
${ownerNote}
你可以使用以下工具来获取${chatTypeDesc}数据:
1. search_messages - 根据关键词搜索聊天记录,支持时间筛选和发送者筛选
@@ -216,14 +226,19 @@ function getFallbackResponseRules(): string {
*
* @param chatType 聊天类型 ('group' | 'private')
* @param promptConfig 用户自定义提示词配置(来自前端激活的预设)
* @param ownerInfo Owner 信息(当前用户在对话中的身份)
*/
function buildSystemPrompt(chatType: 'group' | 'private' = 'group', promptConfig?: PromptConfig): string {
function buildSystemPrompt(
chatType: 'group' | 'private' = 'group',
promptConfig?: PromptConfig,
ownerInfo?: OwnerInfo
): string {
// 使用用户配置或 fallback
const roleDefinition = promptConfig?.roleDefinition || getFallbackRoleDefinition(chatType)
const responseRules = promptConfig?.responseRules || getFallbackResponseRules()
// 获取锁定的系统部分(包含动态日期和工具说明)
const lockedSection = getLockedPromptSection(chatType)
// 获取锁定的系统部分(包含动态日期、工具说明和 Owner 信息)
const lockedSection = getLockedPromptSection(chatType, ownerInfo)
// 组合完整提示词
return `${roleDefinition}
@@ -300,7 +315,7 @@ export class Agent {
}
// 初始化消息(包含历史记录)
const systemPrompt = buildSystemPrompt(this.chatType, this.promptConfig)
const systemPrompt = buildSystemPrompt(this.chatType, this.promptConfig, this.context.ownerInfo)
this.messages = [
{ role: 'system', content: systemPrompt },
...this.historyMessages, // 插入历史对话
@@ -405,7 +420,7 @@ export class Agent {
}
// 初始化消息(包含历史记录)
const systemPrompt = buildSystemPrompt(this.chatType, this.promptConfig)
const systemPrompt = buildSystemPrompt(this.chatType, this.promptConfig, this.context.ownerInfo)
this.messages = [
{ role: 'system', content: systemPrompt },
...this.historyMessages, // 插入历史对话
+10
View File
@@ -9,6 +9,14 @@ import type { ToolDefinition } from '../llm/types'
* 工具执行上下文
* 包含执行工具时需要的所有上下文信息
*/
/** Owner 信息(当前用户在对话中的身份) */
export interface OwnerInfo {
/** Owner 的 platformId */
platformId: string
/** Owner 的显示名称 */
displayName: string
}
export interface ToolContext {
/** 当前会话 ID(数据库文件名) */
sessionId: string
@@ -19,6 +27,8 @@ export interface ToolContext {
}
/** 用户配置的消息条数限制(工具获取消息时使用) */
maxMessagesLimit?: number
/** Owner 信息(当前用户在对话中的身份) */
ownerInfo?: OwnerInfo
}
/**
+2
View File
@@ -381,6 +381,8 @@ export interface SearchMessageResult {
id: number
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
+12 -6
View File
@@ -19,12 +19,7 @@ import type {
CheckInAnalysis,
MemberWithStats,
} from '../../src/types/analysis'
import type {
FileParseInfo,
ConflictCheckResult,
MergeParams,
MergeResult,
} from '../../src/types/format'
import type { FileParseInfo, ConflictCheckResult, MergeParams, MergeResult } from '../../src/types/format'
import type { TableSchema, SQLResult } from '../../src/components/analysis/SQLLab/types'
interface TimeFilter {
@@ -107,6 +102,7 @@ interface SearchMessageResult {
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
@@ -322,11 +318,21 @@ interface AgentResult {
totalUsage?: TokenUsage
}
/** Owner 信息(当前用户在对话中的身份) */
interface OwnerInfo {
/** Owner 的 platformId */
platformId: string
/** Owner 的显示名称 */
displayName: string
}
interface ToolContext {
sessionId: string
timeFilter?: { startTs: number; endTs: number }
/** 用户配置:每次发送给 AI 的最大消息条数 */
maxMessagesLimit?: number
/** Owner 信息(当前用户在对话中的身份) */
ownerInfo?: OwnerInfo
}
// 用户自定义提示词配置
+2 -6
View File
@@ -20,12 +20,7 @@ import type {
MemeBattleAnalysis,
MemberWithStats,
} from '../../src/types/analysis'
import type {
FileParseInfo,
ConflictCheckResult,
MergeParams,
MergeResult,
} from '../../src/types/format'
import type { FileParseInfo, ConflictCheckResult, MergeParams, MergeResult } from '../../src/types/format'
// Custom APIs for renderer
const api = {
@@ -439,6 +434,7 @@ interface SearchMessageResult {
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
@@ -52,7 +52,7 @@ const hintText = computed(() => {
if (currentOwner.value) {
return `当前:${getDisplayName(currentOwner.value)}`
}
return props.chatType === 'group' ? '选择你在群聊中的身份,便于数据分析' : '选择你在对话中的身份,便于数据分析'
return '选择你是谁:1. 将会显示在聊天记录查看器中的右侧 2. AI对话中会标识你的身份'
})
// 更新 owner
+53 -9
View File
@@ -6,6 +6,7 @@
import { ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { usePromptStore } from '@/stores/prompt'
import { useSessionStore } from '@/stores/session'
// 工具调用记录
export interface ToolCallRecord {
@@ -82,6 +83,12 @@ const TOOL_DISPLAY_NAMES: Record<string, string> = {
get_time_stats: '获取时间分布',
}
/** Owner 信息类型 */
interface OwnerInfo {
platformId: string
displayName: string
}
export function useAIChat(
sessionId: string,
timeFilter?: { startTs: number; endTs: number },
@@ -89,6 +96,7 @@ export function useAIChat(
) {
// 获取 chat store 中的提示词配置和全局设置
const promptStore = usePromptStore()
const sessionStore = useSessionStore()
const { activeGroupPreset, activePrivatePreset, aiGlobalSettings } = storeToRefs(promptStore)
// 获取当前聊天类型对应的提示词配置
@@ -108,6 +116,9 @@ export function useAIChat(
const isAIThinking = ref(false)
const currentConversationId = ref<string | null>(null)
// Owner 信息(用于告诉 AI 当前用户是谁)
const ownerInfo = ref<OwnerInfo | undefined>(undefined)
// 工具调用状态
const currentToolStatus = ref<ToolStatus | null>(null)
const toolsUsedInCurrentRound = ref<string[]>([])
@@ -115,6 +126,34 @@ export function useAIChat(
// Token 使用量(当前会话累计)
const sessionTokenUsage = ref<TokenUsage>({ promptTokens: 0, completionTokens: 0, totalTokens: 0 })
// 初始化:获取 Owner 信息
async function initOwnerInfo() {
const ownerId = sessionStore.currentSession?.ownerId
if (!ownerId) {
ownerInfo.value = undefined
return
}
try {
// 获取成员列表,找到 owner 的显示名称
const members = await window.chatApi.getMembers(sessionId)
const ownerMember = members.find((m) => m.platformId === ownerId)
if (ownerMember) {
ownerInfo.value = {
platformId: ownerId,
displayName: ownerMember.groupNickname || ownerMember.accountName || ownerId,
}
console.log('[AI] Owner 信息已加载:', ownerInfo.value)
}
} catch (error) {
console.error('[AI] 获取 Owner 信息失败:', error)
ownerInfo.value = undefined
}
}
// 初始化时加载 Owner 信息
initOwnerInfo()
// 中止控制
let isAborted = false
// 当前请求 ID,用于区分不同请求的响应
@@ -248,15 +287,20 @@ export function useAIChat(
try {
// 调用 Agent API
// 注意:ownerInfo 需要深拷贝为普通对象,否则 IPC 克隆会失败
const context = {
sessionId,
timeFilter: timeFilter ? { startTs: timeFilter.startTs, endTs: timeFilter.endTs } : undefined,
maxMessagesLimit: aiGlobalSettings.value.maxMessagesPerRequest,
ownerInfo: ownerInfo.value
? { platformId: ownerInfo.value.platformId, displayName: ownerInfo.value.displayName }
: undefined,
}
console.log('[AI] 构建 context:', {
sessionId,
maxMessagesLimit: context.maxMessagesLimit,
ownerInfo: context.ownerInfo,
aiGlobalSettings: aiGlobalSettings.value,
})
@@ -275,16 +319,12 @@ export function useAIChat(
content: msg.content,
}))
console.log(
'[AI] 调用 Agent API...',
console.log('[AI] 调用 Agent API...', {
context,
'historyLength:',
historyMessages.length,
'chatType:',
historyLength: historyMessages.length,
chatType,
'promptConfig:',
currentPromptConfig.value
)
promptConfig: currentPromptConfig.value,
})
// 获取 requestId 和 promise(传递历史消息、聊天类型和提示词配置)
const { requestId: agentReqId, promise: agentPromise } = window.agentApi.runStream(
@@ -379,7 +419,11 @@ export function useAIChat(
},
historyMessages,
chatType,
currentPromptConfig.value
// 确保传递纯对象,避免 IPC 克隆失败
{
roleDefinition: currentPromptConfig.value.roleDefinition,
responseRules: currentPromptConfig.value.responseRules,
}
)
// 存储 Agent 请求 ID(用于中止)
+21 -4
View File
@@ -251,13 +251,19 @@ export function getOriginalBuiltinPreset(presetId: string): PromptPreset | undef
// ==================== 锁定部分预览(仅用于前端展示) ====================
/** Owner 信息(用于前端预览) */
export interface OwnerInfoPreview {
displayName: string
}
/**
* 获取锁定部分的提示词预览
* 注意:实际执行时由主进程 agent.ts 生成,包含动态日期
*
* @param chatType 聊天类型
* @param ownerInfo Owner 信息(可选,用于预览时显示)
*/
export function getLockedPromptSectionPreview(chatType: 'group' | 'private'): string {
export function getLockedPromptSectionPreview(chatType: 'group' | 'private', ownerInfo?: OwnerInfoPreview): string {
const now = new Date()
const currentDate = now.toLocaleDateString('zh-CN', {
year: 'numeric',
@@ -269,6 +275,15 @@ export function getLockedPromptSectionPreview(chatType: 'group' | 'private'): st
const isPrivate = chatType === 'private'
const chatTypeDesc = isPrivate ? '私聊记录' : '群聊记录'
// Owner 说明(当用户设置了"我是谁"时)
const ownerNote = ownerInfo
? `当前用户身份:
- 用户在${isPrivate ? '对话' : '群聊'}中的身份是「${ownerInfo.displayName}」
- 当用户提到"我"、"我的"时,指的就是「${ownerInfo.displayName}」
- 查询"我"的发言时,使用 sender_id 参数筛选该成员
`
: ''
const memberNote = isPrivate
? `成员查询策略:
- 私聊只有两个人,可以直接获取成员列表
@@ -280,7 +295,7 @@ export function getLockedPromptSectionPreview(chatType: 'group' | 'private'): st
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言`
return `当前日期是 ${currentDate}。
${ownerNote}
你可以使用以下工具来获取${chatTypeDesc}数据:
1. search_messages - 根据关键词搜索聊天记录,支持时间筛选和发送者筛选
@@ -308,13 +323,15 @@ ${memberNote}
* @param roleDefinition 角色定义
* @param responseRules 回答要求
* @param chatType 聊天类型
* @param ownerInfo Owner 信息(可选)
*/
export function buildPromptPreview(
roleDefinition: string,
responseRules: string,
chatType: 'group' | 'private'
chatType: 'group' | 'private',
ownerInfo?: OwnerInfoPreview
): string {
const lockedSection = getLockedPromptSectionPreview(chatType)
const lockedSection = getLockedPromptSectionPreview(chatType, ownerInfo)
return `${roleDefinition}