This commit is contained in:
1600822305 2025-04-13 22:42:26 +08:00
parent bbdcd85014
commit 994ab7362f
7 changed files with 1169 additions and 34 deletions

View File

@ -10,35 +10,48 @@ const memoryDataPath = path.join(getConfigDir(), 'memory-data.json')
export class MemoryFileService {
constructor() {
this.ensureMemoryFileExists()
this.registerIpcHandlers()
}
private async ensureMemoryFileExists() {
try {
const directory = path.dirname(memoryDataPath)
await fs.mkdir(directory, { recursive: true })
try {
await fs.access(memoryDataPath)
} catch (error) {
// 文件不存在,创建一个空文件
await fs.writeFile(memoryDataPath, JSON.stringify({
memoryLists: [],
memories: [],
shortMemories: []
}, null, 2))
}
} catch (error) {
log.error('Failed to ensure memory file exists:', error)
}
}
private registerIpcHandlers() {
// 读取记忆数据
ipcMain.handle(IpcChannel.Memory_LoadData, async () => {
try {
// 确保配置目录存在
const configDir = path.dirname(memoryDataPath)
try {
await fs.mkdir(configDir, { recursive: true })
} catch (mkdirError) {
log.warn('Failed to create config directory, it may already exist:', mkdirError)
}
// 检查文件是否存在
try {
await fs.access(memoryDataPath)
} catch (accessError) {
// 文件不存在,创建默认文件
log.info('Memory data file does not exist, creating default file')
const defaultData = {
memoryLists: [{
id: 'default',
name: '默认列表',
isActive: true
}],
memories: [],
shortMemories: [],
analyzeModel: 'gpt-3.5-turbo',
shortMemoryAnalyzeModel: 'gpt-3.5-turbo',
vectorizeModel: 'gpt-3.5-turbo'
}
await fs.writeFile(memoryDataPath, JSON.stringify(defaultData, null, 2))
return defaultData
}
// 读取文件
const data = await fs.readFile(memoryDataPath, 'utf-8')
return JSON.parse(data)
const parsedData = JSON.parse(data)
log.info('Memory data loaded successfully')
return parsedData
} catch (error) {
log.error('Failed to load memory data:', error)
return null
@ -48,7 +61,32 @@ export class MemoryFileService {
// 保存记忆数据
ipcMain.handle(IpcChannel.Memory_SaveData, async (_, data) => {
try {
await fs.writeFile(memoryDataPath, JSON.stringify(data, null, 2))
// 确保配置目录存在
const configDir = path.dirname(memoryDataPath)
try {
await fs.mkdir(configDir, { recursive: true })
} catch (mkdirError) {
log.warn('Failed to create config directory, it may already exist:', mkdirError)
}
// 尝试读取现有数据并合并
let existingData = {}
try {
await fs.access(memoryDataPath)
const fileContent = await fs.readFile(memoryDataPath, 'utf-8')
existingData = JSON.parse(fileContent)
log.info('Existing memory data loaded for merging')
} catch (readError) {
log.warn('No existing memory data found or failed to read:', readError)
// 如果文件不存在或读取失败,使用空对象
}
// 合并数据,优先使用新数据
const mergedData = { ...existingData, ...data }
// 保存合并后的数据
await fs.writeFile(memoryDataPath, JSON.stringify(mergedData, null, 2))
log.info('Memory data saved successfully')
return true
} catch (error) {
log.error('Failed to save memory data:', error)

View File

@ -41,8 +41,19 @@ const MemoryProvider: FC<MemoryProviderProps> = ({ children }) => {
// 在组件挂载时加载记忆数据
useEffect(() => {
console.log('[MemoryProvider] Loading memory data from file')
// 使用Redux Thunk加载记忆数据
dispatch(loadMemoryData())
}, [])
.then((result) => {
if (result.payload) {
console.log('[MemoryProvider] Memory data loaded successfully via Redux Thunk')
} else {
console.log('[MemoryProvider] No memory data loaded or loading failed')
}
})
.catch(error => {
console.error('[MemoryProvider] Error loading memory data:', error)
})
}, [dispatch])
// 当对话更新时,触发记忆分析
useEffect(() => {

View File

@ -0,0 +1,141 @@
import { InfoCircleOutlined } from '@ant-design/icons'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
setContextualRecommendationEnabled,
setAutoRecommendMemories,
setRecommendationThreshold,
clearCurrentRecommendations
} from '@renderer/store/memory'
import { Button, InputNumber, Slider, Switch, Tooltip } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..'
const SliderContainer = styled.div`
display: flex;
align-items: center;
width: 100%;
max-width: 300px;
margin-right: 16px;
`
const ContextualRecommendationSettings: FC = () => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
// 获取相关状态
const contextualRecommendationEnabled = useAppSelector((state) => state.memory.contextualRecommendationEnabled)
const autoRecommendMemories = useAppSelector((state) => state.memory.autoRecommendMemories)
const recommendationThreshold = useAppSelector((state) => state.memory.recommendationThreshold)
// 处理开关状态变化
const handleContextualRecommendationToggle = (checked: boolean) => {
dispatch(setContextualRecommendationEnabled(checked))
}
const handleAutoRecommendToggle = (checked: boolean) => {
dispatch(setAutoRecommendMemories(checked))
}
// 处理推荐阈值变化
const handleThresholdChange = (value: number | null) => {
if (value !== null) {
dispatch(setRecommendationThreshold(value))
}
}
// 清除当前推荐
const handleClearRecommendations = () => {
dispatch(clearCurrentRecommendations())
}
return (
<SettingGroup>
<SettingTitle>{t('settings.memory.contextualRecommendation.title') || '上下文感知记忆推荐'}</SettingTitle>
<SettingHelpText>
{t('settings.memory.contextualRecommendation.description') ||
'根据当前对话上下文智能推荐相关记忆提高AI回复的相关性和连贯性。'}
</SettingHelpText>
<SettingRow>
<SettingRowTitle>
{t('settings.memory.contextualRecommendation.enable') || '启用上下文感知记忆推荐'}
<Tooltip title={t('settings.memory.contextualRecommendation.enableTip') ||
'启用后,系统将根据当前对话上下文自动推荐相关记忆'}>
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</SettingRowTitle>
<Switch checked={contextualRecommendationEnabled} onChange={handleContextualRecommendationToggle} />
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>
{t('settings.memory.contextualRecommendation.autoRecommend') || '自动推荐记忆'}
<Tooltip title={t('settings.memory.contextualRecommendation.autoRecommendTip') ||
'启用后,系统将定期自动分析当前对话并推荐相关记忆'}>
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</SettingRowTitle>
<Switch
checked={autoRecommendMemories}
onChange={handleAutoRecommendToggle}
disabled={!contextualRecommendationEnabled}
/>
</SettingRow>
<SettingRow>
<SettingRowTitle>
{t('settings.memory.contextualRecommendation.threshold') || '推荐阈值'}
<Tooltip title={t('settings.memory.contextualRecommendation.thresholdTip') ||
'设置记忆推荐的相似度阈值,值越高要求越严格'}>
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</SettingRowTitle>
<div style={{ display: 'flex', alignItems: 'center' }}>
<SliderContainer>
<Slider
min={0.1}
max={0.9}
step={0.05}
value={recommendationThreshold}
onChange={handleThresholdChange}
disabled={!contextualRecommendationEnabled}
style={{ flex: 1 }}
/>
</SliderContainer>
<InputNumber
min={0.1}
max={0.9}
step={0.05}
value={recommendationThreshold}
onChange={handleThresholdChange}
disabled={!contextualRecommendationEnabled}
style={{ width: 70 }}
/>
</div>
</SettingRow>
<SettingRow>
<SettingRowTitle>
{t('settings.memory.contextualRecommendation.clearRecommendations') || '清除当前推荐'}
<Tooltip title={t('settings.memory.contextualRecommendation.clearRecommendationsTip') ||
'清除当前的记忆推荐列表'}>
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</SettingRowTitle>
<Button
onClick={handleClearRecommendations}
disabled={!contextualRecommendationEnabled}
>
{t('settings.memory.contextualRecommendation.clear') || '清除'}
</Button>
</SettingRow>
</SettingGroup>
)
}
export default ContextualRecommendationSettings

View File

@ -9,7 +9,7 @@ import {
import { useTheme } from '@renderer/context/ThemeProvider'
import { TopicManager } from '@renderer/hooks/useTopic'
import { analyzeAndAddShortMemories, useMemoryService } from '@renderer/services/MemoryService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import {
addMemory,
clearMemories,
@ -41,6 +41,7 @@ import MemoryDeduplicationPanel from './MemoryDeduplicationPanel'
import MemoryListManager from './MemoryListManager'
import MemoryMindMap from './MemoryMindMap'
import PriorityManagementSettings from './PriorityManagementSettings'
import ContextualRecommendationSettings from './ContextualRecommendationSettings'
const MemorySettings: FC = () => {
const { t } = useTranslation()
@ -255,13 +256,49 @@ const MemorySettings: FC = () => {
}
// 处理选择长期记忆分析模型
const handleSelectModel = (modelId: string) => {
const handleSelectModel = async (modelId: string) => {
dispatch(setAnalyzeModel(modelId))
console.log('[Memory Settings] Analyze model set:', modelId)
// 手动保存到JSON文件
try {
const state = store.getState().memory
await window.api.memory.saveData({
analyzeModel: modelId,
shortMemoryAnalyzeModel: state.shortMemoryAnalyzeModel,
vectorizeModel: state.vectorizeModel,
// 确保其他必要的数据也被保存
memoryLists: state.memoryLists || [],
memories: state.memories || [],
shortMemories: state.shortMemories || []
})
console.log('[Memory Settings] Analyze model saved to file successfully:', modelId)
} catch (error) {
console.error('[Memory Settings] Failed to save analyze model to file:', error)
}
}
// 处理选择短期记忆分析模型
const handleSelectShortMemoryModel = (modelId: string) => {
const handleSelectShortMemoryModel = async (modelId: string) => {
dispatch(setShortMemoryAnalyzeModel(modelId))
console.log('[Memory Settings] Short memory analyze model set:', modelId)
// 手动保存到JSON文件
try {
const state = store.getState().memory
await window.api.memory.saveData({
analyzeModel: state.analyzeModel,
shortMemoryAnalyzeModel: modelId,
vectorizeModel: state.vectorizeModel,
// 确保其他必要的数据也被保存
memoryLists: state.memoryLists || [],
memories: state.memories || [],
shortMemories: state.shortMemories || []
})
console.log('[Memory Settings] Short memory analyze model saved to file successfully:', modelId)
} catch (error) {
console.error('[Memory Settings] Failed to save short memory analyze model to file:', error)
}
}
// 手动触发分析
@ -571,6 +608,8 @@ const MemorySettings: FC = () => {
children: (
<TabPaneSettingGroup theme={theme}>
<PriorityManagementSettings />
<SettingDivider />
<ContextualRecommendationSettings />
</TabPaneSettingGroup>
)
},

View File

@ -0,0 +1,530 @@
// src/renderer/src/services/ContextualMemoryService.ts
import store from '@renderer/store'
import { vectorService } from './VectorService'
import { addMemoryRetrievalLatency } from '@renderer/store/memory'
import { fetchGenerate } from '@renderer/services/ApiService'
import { Message } from '@renderer/types'
import { TopicManager } from '@renderer/hooks/useTopic'
// 记忆项接口从store/memory.ts导入
interface Memory {
id: string
content: string
createdAt: string
source?: string
category?: string
listId: string
analyzedMessageIds?: string[]
lastMessageId?: string
topicId?: string
vector?: number[]
entities?: string[]
keywords?: string[]
importance?: number
accessCount?: number
lastAccessedAt?: string
decayFactor?: number
freshness?: number
}
interface ShortMemory {
id: string
content: string
createdAt: string
topicId: string
analyzedMessageIds?: string[]
lastMessageId?: string
vector?: number[]
entities?: string[]
keywords?: string[]
importance?: number
accessCount?: number
lastAccessedAt?: string
decayFactor?: number
freshness?: number
}
// 记忆推荐结果接口
export interface MemoryRecommendation {
memory: Memory | ShortMemory
relevanceScore: number
source: 'long-term' | 'short-term'
matchReason?: string
}
/**
* ContextualMemoryService
*/
class ContextualMemoryService {
/**
*
* @param messages -
* @param topicId - ID
* @param limit -
* @returns
*/
async getContextualMemoryRecommendations(
messages: Message[],
topicId: string,
limit: number = 5
): Promise<MemoryRecommendation[]> {
console.log(`[ContextualMemory] Getting contextual memory recommendations for topic ${topicId}`)
const startTime = performance.now()
try {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
if (!memoryState) {
console.log('[ContextualMemory] Memory state not available')
return []
}
// 检查记忆功能是否激活
if (!memoryState.isActive && !memoryState.shortMemoryActive) {
console.log('[ContextualMemory] Memory features are not active')
return []
}
// 获取最近的消息作为上下文
const recentMessages = messages.slice(-5)
if (recentMessages.length === 0) {
console.log('[ContextualMemory] No recent messages available')
return []
}
// 构建上下文查询文本
const contextQuery = this._buildContextQuery(recentMessages)
console.log(`[ContextualMemory] Context query: ${contextQuery}`)
// 并行获取长期记忆和短期记忆的推荐
const [longTermRecommendations, shortTermRecommendations] = await Promise.all([
this._getLongTermMemoryRecommendations(contextQuery, topicId),
this._getShortTermMemoryRecommendations(contextQuery, topicId)
])
// 合并并排序推荐结果
let allRecommendations = [...longTermRecommendations, ...shortTermRecommendations]
// 按相关性分数排序
allRecommendations.sort((a, b) => b.relevanceScore - a.relevanceScore)
// 限制返回数量
const limitedRecommendations = allRecommendations.slice(0, limit)
// 记录性能指标
const endTime = performance.now()
const latency = endTime - startTime
store.dispatch(addMemoryRetrievalLatency(latency))
console.log(`[ContextualMemory] Found ${limitedRecommendations.length} recommendations in ${latency.toFixed(2)}ms`)
return limitedRecommendations
} catch (error) {
console.error('[ContextualMemory] Error getting contextual memory recommendations:', error)
return []
}
}
/**
*
* @param topicId - ID
* @param limit -
* @returns
*/
async getTopicRelatedMemories(topicId: string, limit: number = 10): Promise<MemoryRecommendation[]> {
console.log(`[ContextualMemory] Getting topic-related memories for topic ${topicId}`)
try {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
const messagesState = state.messages
if (!memoryState || !messagesState) {
console.log('[ContextualMemory] Required state not available')
return []
}
// 获取话题信息
// 使用TopicManager获取话题
let topicQuery = ''
try {
const topic = await TopicManager.getTopic(topicId)
if (!topic) {
console.log(`[ContextualMemory] Topic ${topicId} not found`)
return []
}
// 使用话题ID作为查询
// 注意TopicManager.getTopic返回的类型只有id和messages属性
topicQuery = `Topic ${topicId}`
if (!topicQuery.trim()) {
console.log('[ContextualMemory] No topic information available for query')
return []
}
} catch (error) {
console.error(`[ContextualMemory] Error getting topic ${topicId}:`, error)
return []
}
console.log(`[ContextualMemory] Topic query: ${topicQuery}`)
// 并行获取长期记忆和短期记忆的推荐
const [longTermRecommendations, shortTermRecommendations] = await Promise.all([
this._getLongTermMemoryRecommendations(topicQuery, topicId),
this._getShortTermMemoryRecommendations(topicQuery, topicId)
])
// 合并并排序推荐结果
let allRecommendations = [...longTermRecommendations, ...shortTermRecommendations]
// 按相关性分数排序
allRecommendations.sort((a, b) => b.relevanceScore - a.relevanceScore)
// 限制返回数量
const limitedRecommendations = allRecommendations.slice(0, limit)
console.log(`[ContextualMemory] Found ${limitedRecommendations.length} topic-related memories`)
return limitedRecommendations
} catch (error) {
console.error('[ContextualMemory] Error getting topic-related memories:', error)
return []
}
}
/**
* 使
* @param query -
* @param limit -
* @returns
*/
async searchMemoriesBySemantics(query: string, limit: number = 10): Promise<MemoryRecommendation[]> {
console.log(`[ContextualMemory] Semantic search for: ${query}`)
try {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
if (!memoryState) {
console.log('[ContextualMemory] Memory state not available')
return []
}
// 并行获取长期记忆和短期记忆的推荐
const [longTermRecommendations, shortTermRecommendations] = await Promise.all([
this._getLongTermMemoryRecommendations(query),
this._getShortTermMemoryRecommendations(query)
])
// 合并并排序推荐结果
let allRecommendations = [...longTermRecommendations, ...shortTermRecommendations]
// 按相关性分数排序
allRecommendations.sort((a, b) => b.relevanceScore - a.relevanceScore)
// 限制返回数量
const limitedRecommendations = allRecommendations.slice(0, limit)
console.log(`[ContextualMemory] Found ${limitedRecommendations.length} memories matching query`)
return limitedRecommendations
} catch (error) {
console.error('[ContextualMemory] Error searching memories by semantics:', error)
return []
}
}
/**
* 使AI分析当前对话上下文
* @param messages -
* @param limit -
* @returns AI分析的相关记忆推荐
*/
async getAIEnhancedMemoryRecommendations(messages: Message[], limit: number = 5): Promise<MemoryRecommendation[]> {
console.log('[ContextualMemory] Getting AI-enhanced memory recommendations')
try {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
if (!memoryState) {
console.log('[ContextualMemory] Memory state not available')
return []
}
// 获取分析模型
const analyzeModel = memoryState.analyzeModel
if (!analyzeModel) {
console.log('[ContextualMemory] No analyze model set')
return []
}
// 获取最近的消息作为上下文
const recentMessages = messages.slice(-10)
if (recentMessages.length === 0) {
console.log('[ContextualMemory] No recent messages available')
return []
}
// 构建对话内容
const conversation = recentMessages.map(msg => `${msg.role || 'user'}: ${msg.content || ''}`).join('\n')
// 构建提示词
const prompt = `
便
1.
2.
3.
使
:
${conversation}
`
// 调用AI生成文本
console.log('[ContextualMemory] Calling AI for context analysis...')
const result = await fetchGenerate({
prompt: prompt,
content: conversation,
modelId: analyzeModel
})
if (!result || typeof result !== 'string' || result.trim() === '') {
console.log('[ContextualMemory] No valid result from AI analysis')
return []
}
// 提取关键信息
const keyPoints = result
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#') && !line.startsWith('-'))
console.log('[ContextualMemory] Extracted key points:', keyPoints)
// 使用提取的关键信息作为查询
const enhancedQuery = keyPoints.join(' ')
// 获取相关记忆
return await this.searchMemoriesBySemantics(enhancedQuery, limit)
} catch (error) {
console.error('[ContextualMemory] Error getting AI-enhanced memory recommendations:', error)
return []
}
}
/**
*
* @param messages -
* @returns
* @private
*/
private _buildContextQuery(messages: Message[]): string {
// 提取最近消息的内容
const messageContents = messages.map(msg => msg.content || '').filter(content => content.trim() !== '')
// 如果没有有效内容,返回空字符串
if (messageContents.length === 0) {
return ''
}
// 合并消息内容最多使用最近的3条消息
return messageContents.slice(-3).join(' ')
}
/**
*
* @param query -
* @param topicId - ID
* @returns
* @private
*/
private async _getLongTermMemoryRecommendations(
query: string,
topicId?: string
): Promise<MemoryRecommendation[]> {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
// 检查长期记忆功能是否激活
if (!memoryState || !memoryState.isActive) {
return []
}
// 获取所有激活的记忆列表
const activeListIds = memoryState.memoryLists
.filter(list => list.isActive)
.map(list => list.id)
if (activeListIds.length === 0) {
return []
}
// 获取激活列表中的记忆
const memories = memoryState.memories.filter(memory => activeListIds.includes(memory.listId))
if (memories.length === 0) {
return []
}
// 使用向量服务查找相似记忆
const results = await vectorService.findSimilarMemoriesToQuery(
query,
memories,
20, // 获取更多结果,后续会进一步优化排序
0.5 // 降低阈值以获取更多潜在相关记忆
)
// 转换为推荐格式
const recommendations: MemoryRecommendation[] = results.map(result => ({
memory: result.memory as Memory,
relevanceScore: result.similarity,
source: 'long-term',
matchReason: '语义相似'
}))
// 应用高级排序优化
return this._optimizeRelevanceRanking(recommendations, query, topicId)
}
/**
*
* @param query -
* @param topicId - ID
* @returns
* @private
*/
private async _getShortTermMemoryRecommendations(
query: string,
topicId?: string
): Promise<MemoryRecommendation[]> {
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
// 检查短期记忆功能是否激活
if (!memoryState || !memoryState.shortMemoryActive) {
return []
}
// 获取短期记忆
let shortMemories = memoryState.shortMemories
// 如果指定了话题ID只获取该话题的短期记忆
if (topicId) {
shortMemories = shortMemories.filter(memory => memory.topicId === topicId)
}
if (shortMemories.length === 0) {
return []
}
// 使用向量服务查找相似记忆
const results = await vectorService.findSimilarMemoriesToQuery(
query,
shortMemories,
20, // 获取更多结果,后续会进一步优化排序
0.5 // 降低阈值以获取更多潜在相关记忆
)
// 转换为推荐格式
const recommendations: MemoryRecommendation[] = results.map(result => ({
memory: result.memory as ShortMemory,
relevanceScore: result.similarity,
source: 'short-term',
matchReason: '与当前对话相关'
}))
// 应用高级排序优化
return this._optimizeRelevanceRanking(recommendations, query, topicId)
}
/**
*
* @param recommendations -
* @param query -
* @param topicId - ID
* @returns
* @private
*/
private _optimizeRelevanceRanking(
recommendations: MemoryRecommendation[],
query: string,
topicId?: string
): MemoryRecommendation[] {
if (recommendations.length === 0) {
return []
}
// 获取当前状态
const state = store.getState()
const memoryState = state.memory
// 应用多因素排序优化
return recommendations.map(rec => {
const memory = rec.memory
let adjustedScore = rec.relevanceScore
// 1. 考虑记忆的重要性
if (memory.importance && memoryState.priorityManagementEnabled) {
adjustedScore *= (1 + memory.importance * 0.5) // 重要性最多提升50%的分数
}
// 2. 考虑记忆的鲜度
if (memory.freshness && memoryState.freshnessEnabled) {
adjustedScore *= (1 + memory.freshness * 0.3) // 鲜度最多提升30%的分数
}
// 3. 考虑记忆的衰减因子
if (memory.decayFactor && memoryState.decayEnabled) {
adjustedScore *= memory.decayFactor // 直接应用衰减因子
}
// 4. 如果记忆与当前话题相关,提高分数
if (topicId && memory.topicId === topicId) {
adjustedScore *= 1.2 // 提高20%的分数
}
// 5. 考虑访问频率,常用的记忆可能更相关
if (memory.accessCount && memory.accessCount > 0) {
// 访问次数越多,提升越大,但有上限
const accessBoost = Math.min(memory.accessCount / 10, 0.2) // 最多提升20%
adjustedScore *= (1 + accessBoost)
}
// 6. 考虑关键词匹配
if (memory.keywords && memory.keywords.length > 0) {
const queryLower = query.toLowerCase()
const keywordMatches = memory.keywords.filter(keyword =>
queryLower.includes(keyword.toLowerCase())
).length
if (keywordMatches > 0) {
// 关键词匹配越多,提升越大
const keywordBoost = Math.min(keywordMatches * 0.1, 0.3) // 最多提升30%
adjustedScore *= (1 + keywordBoost)
}
}
// 返回调整后的推荐
return {
...rec,
relevanceScore: adjustedScore
}
}).sort((a, b) => b.relevanceScore - a.relevanceScore) // 按调整后的分数重新排序
}
}
// 导出 ContextualMemoryService 的单例
export const contextualMemoryService = new ContextualMemoryService()

View File

@ -16,9 +16,15 @@ import {
updateMemoryPriorities,
accessMemory,
Memory,
saveMemoryData // <-- 添加 saveMemoryData
saveMemoryData,
updateCurrentRecommendations,
setRecommending,
clearCurrentRecommendations,
MemoryRecommendation
} from '@renderer/store/memory'
import { useCallback, useEffect, useRef } from 'react' // Add useRef back
import { contextualMemoryService } from './ContextualMemoryService' // Import contextual memory service
import { Message } from '@renderer/types' // Import Message type
// 计算对话复杂度,用于调整分析深度
const calculateConversationComplexity = (conversation: string): 'low' | 'medium' | 'high' => {
@ -167,6 +173,162 @@ ${conversation}
// This function definition is a duplicate, removing it.
/**
*
* @param messages -
* @param topicId - ID
* @param limit -
* @returns
*/
export const getContextualMemoryRecommendations = async (
messages: Message[],
topicId: string,
limit: number = 5
): Promise<MemoryRecommendation[]> => {
try {
// 获取当前状态
const state = store.getState().memory
// 检查上下文感知记忆推荐是否启用
if (!state?.contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
// 设置推荐状态
store.dispatch(setRecommending(true))
// 调用上下文感知记忆服务获取推荐
const recommendations = await contextualMemoryService.getContextualMemoryRecommendations(
messages,
topicId,
limit
)
// 转换为Redux状态中的推荐格式
const memoryRecommendations: MemoryRecommendation[] = recommendations.map(rec => ({
memoryId: rec.memory.id,
relevanceScore: rec.relevanceScore,
source: rec.source,
matchReason: rec.matchReason
}))
// 更新Redux状态
store.dispatch(updateCurrentRecommendations(memoryRecommendations))
// 重置推荐状态
store.dispatch(setRecommending(false))
return memoryRecommendations
} catch (error) {
console.error('[ContextualMemory] Error getting contextual memory recommendations:', error)
store.dispatch(setRecommending(false))
return []
}
}
/**
*
* @param topicId - ID
* @param limit -
* @returns
*/
export const getTopicRelatedMemories = async (
topicId: string,
limit: number = 10
): Promise<MemoryRecommendation[]> => {
try {
// 获取当前状态
const state = store.getState().memory
// 检查上下文感知记忆推荐是否启用
if (!state?.contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
// 设置推荐状态
store.dispatch(setRecommending(true))
// 调用上下文感知记忆服务获取推荐
const recommendations = await contextualMemoryService.getTopicRelatedMemories(
topicId,
limit
)
// 转换为Redux状态中的推荐格式
const memoryRecommendations: MemoryRecommendation[] = recommendations.map(rec => ({
memoryId: rec.memory.id,
relevanceScore: rec.relevanceScore,
source: rec.source,
matchReason: rec.matchReason
}))
// 更新Redux状态
store.dispatch(updateCurrentRecommendations(memoryRecommendations))
// 重置推荐状态
store.dispatch(setRecommending(false))
return memoryRecommendations
} catch (error) {
console.error('[ContextualMemory] Error getting topic-related memories:', error)
store.dispatch(setRecommending(false))
return []
}
}
/**
* 使AI分析当前对话上下文
* @param messages -
* @param limit -
* @returns AI分析的相关记忆推荐
*/
export const getAIEnhancedMemoryRecommendations = async (
messages: Message[],
limit: number = 5
): Promise<MemoryRecommendation[]> => {
try {
// 获取当前状态
const state = store.getState().memory
// 检查上下文感知记忆推荐是否启用
if (!state?.contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
// 设置推荐状态
store.dispatch(setRecommending(true))
// 调用上下文感知记忆服务获取推荐
const recommendations = await contextualMemoryService.getAIEnhancedMemoryRecommendations(
messages,
limit
)
// 转换为Redux状态中的推荐格式
const memoryRecommendations: MemoryRecommendation[] = recommendations.map(rec => ({
memoryId: rec.memory.id,
relevanceScore: rec.relevanceScore,
source: rec.source,
matchReason: rec.matchReason
}))
// 更新Redux状态
store.dispatch(updateCurrentRecommendations(memoryRecommendations))
// 重置推荐状态
store.dispatch(setRecommending(false))
return memoryRecommendations
} catch (error) {
console.error('[ContextualMemory] Error getting AI-enhanced memory recommendations:', error)
store.dispatch(setRecommending(false))
return []
}
}
// 记忆服务钩子 - 重构版
export const useMemoryService = () => {
const dispatch = useAppDispatch()
@ -174,6 +336,8 @@ export const useMemoryService = () => {
const isActive = useAppSelector((state) => state.memory?.isActive || false)
const autoAnalyze = useAppSelector((state) => state.memory?.autoAnalyze || false)
const analyzeModel = useAppSelector((state) => state.memory?.analyzeModel || null)
const contextualRecommendationEnabled = useAppSelector((state) => state.memory?.contextualRecommendationEnabled || false)
const autoRecommendMemories = useAppSelector((state) => state.memory?.autoRecommendMemories || false)
// 使用 useCallback 定义分析函数,但减少依赖项
// 增加可选的 topicId 参数,允许分析指定的话题
@ -559,8 +723,85 @@ ${newConversation}
// 依赖项只包含决定是否启动定时器的设置
}, [isActive, autoAnalyze, analyzeModel])
// 返回分析函数和记忆访问函数,以便在其他组件中使用
return { analyzeAndAddMemories, recordMemoryAccess }
// 获取上下文感知记忆推荐
const getContextualRecommendations = useCallback(
async (messages: Message[], topicId: string, limit: number = 5) => {
if (!contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
return await getContextualMemoryRecommendations(messages, topicId, limit)
},
[contextualRecommendationEnabled]
)
// 获取主题相关记忆
const getTopicRecommendations = useCallback(
async (topicId: string, limit: number = 10) => {
if (!contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
return await getTopicRelatedMemories(topicId, limit)
},
[contextualRecommendationEnabled]
)
// 获取AI增强记忆推荐
const getAIRecommendations = useCallback(
async (messages: Message[], limit: number = 5) => {
if (!contextualRecommendationEnabled) {
console.log('[ContextualMemory] Contextual recommendation is not enabled')
return []
}
return await getAIEnhancedMemoryRecommendations(messages, limit)
},
[contextualRecommendationEnabled]
)
// 清除当前记忆推荐
const clearRecommendations = useCallback(() => {
dispatch(clearCurrentRecommendations())
}, [dispatch])
// 自动记忆推荐定时器
useEffect(() => {
if (!contextualRecommendationEnabled || !autoRecommendMemories) {
return
}
console.log('[ContextualMemory] Setting up auto recommendation timer...')
// 每5分钟自动推荐一次记忆
const intervalId = setInterval(() => {
const state = store.getState()
const currentTopicId = state.messages.currentTopic?.id
const messages = currentTopicId ? state.messages.messagesByTopic?.[currentTopicId] || [] : []
if (currentTopicId && messages.length > 0) {
console.log('[ContextualMemory] Auto recommendation triggered')
getContextualRecommendations(messages, currentTopicId)
}
}, 5 * 60 * 1000) // 5分钟
return () => {
console.log('[ContextualMemory] Clearing auto recommendation timer')
clearInterval(intervalId)
}
}, [contextualRecommendationEnabled, autoRecommendMemories, getContextualRecommendations])
// 返回分析函数、记忆访问函数和记忆推荐函数,以便在其他组件中使用
return {
analyzeAndAddMemories,
recordMemoryAccess,
getContextualRecommendations,
getTopicRecommendations,
getAIRecommendations,
clearRecommendations
}
}
// 手动添加短记忆
@ -722,7 +963,7 @@ ${newConversation}
// 首先尝试匹配带有数字或短横线的列表项
const listItemRegex = /(?:^|\n)(?:\d+\.\s*|\-\s*)(.+?)(?=\n\d+\.\s*|\n\-\s*|\n\n|$)/gs
let match
let match: RegExpExecArray | null
while ((match = listItemRegex.exec(result)) !== null) {
if (match[1] && match[1].trim()) {
extractedLines.push(match[1].trim())
@ -832,13 +1073,24 @@ export const applyMemoriesToPrompt = (systemPrompt: string): string => {
const state = store.getState() // Use imported store
// 确保 state.memory 存在,如果不存在则提供默认值
const { isActive, memories, memoryLists, shortMemoryActive, shortMemories, priorityManagementEnabled } = state.memory || {
const {
isActive,
memories,
memoryLists,
shortMemoryActive,
shortMemories,
priorityManagementEnabled,
contextualRecommendationEnabled,
currentRecommendations
} = state.memory || {
isActive: false,
memories: [],
memoryLists: [],
shortMemoryActive: false,
shortMemories: [],
priorityManagementEnabled: false
priorityManagementEnabled: false,
contextualRecommendationEnabled: false,
currentRecommendations: []
}
// 获取当前话题ID
@ -857,6 +1109,50 @@ export const applyMemoriesToPrompt = (systemPrompt: string): string => {
let result = systemPrompt
let hasContent = false
// 处理上下文感知记忆推荐
if (contextualRecommendationEnabled && currentRecommendations && currentRecommendations.length > 0) {
// 获取推荐记忆的详细信息
const recommendedMemories: Array<{content: string, source: string, reason: string}> = []
// 处理每个推荐记忆
for (const recommendation of currentRecommendations) {
// 根据来源查找记忆
let memory: any = null
if (recommendation.source === 'long-term') {
memory = memories.find(m => m.id === recommendation.memoryId)
} else if (recommendation.source === 'short-term') {
memory = shortMemories.find(m => m.id === recommendation.memoryId)
}
if (memory) {
recommendedMemories.push({
content: memory.content,
source: recommendation.source === 'long-term' ? '长期记忆' : '短期记忆',
reason: recommendation.matchReason || '与当前对话相关'
})
// 记录访问
store.dispatch(accessMemory({
id: memory.id,
isShortMemory: recommendation.source === 'short-term'
}))
}
}
if (recommendedMemories.length > 0) {
// 构建推荐记忆提示词
const recommendedMemoryPrompt = recommendedMemories
.map((memory, index) => `${index + 1}. ${memory.content} (来源: ${memory.source}, 原因: ${memory.reason})`)
.join('\n')
console.log('[Memory] Contextual memory recommendations:', recommendedMemoryPrompt)
// 添加推荐记忆到提示词
result = `${result}\n\n当前对话的相关记忆(按相关性排序):\n${recommendedMemoryPrompt}`
hasContent = true
}
}
// 处理短记忆
if (shortMemoryActive && shortMemories && shortMemories.length > 0 && currentTopicId) {
// 获取当前话题的短记忆

View File

@ -76,6 +76,14 @@ export interface UserInterest {
lastUpdated: string // 上次更新时间
}
// 记忆推荐结果接口
export interface MemoryRecommendation {
memoryId: string
relevanceScore: number
source: 'long-term' | 'short-term'
matchReason?: string
}
export interface MemoryState {
memoryLists: MemoryList[] // 记忆列表
memories: Memory[] // 所有记忆项
@ -110,6 +118,14 @@ export interface MemoryState {
freshnessEnabled: boolean // 是否启用记忆鲜度评估
decayRate: number // 记忆衰减速率0-1
lastPriorityUpdate: number // 上次优先级更新时间
// 上下文感知记忆推荐相关
contextualRecommendationEnabled: boolean // 是否启用上下文感知记忆推荐
autoRecommendMemories: boolean // 是否自动推荐记忆
recommendationThreshold: number // 推荐阈值0-1
currentRecommendations: MemoryRecommendation[] // 当前的记忆推荐
isRecommending: boolean // 是否正在推荐记忆
lastRecommendTime: number | null // 上次推荐时间
}
// 创建默认记忆列表
@ -167,7 +183,15 @@ const initialState: MemoryState = {
decayEnabled: true, // 默认启用记忆衰减功能
freshnessEnabled: true, // 默认启用记忆鲜度评估
decayRate: 0.05, // 默认衰减速率每天减少5%
lastPriorityUpdate: Date.now() // 初始化为当前时间
lastPriorityUpdate: Date.now(), // 初始化为当前时间
// 上下文感知记忆推荐相关
contextualRecommendationEnabled: true, // 默认启用上下文感知记忆推荐
autoRecommendMemories: true, // 默认自动推荐记忆
recommendationThreshold: 0.7, // 默认推荐阈值
currentRecommendations: [], // 初始化空的推荐列表
isRecommending: false, // 初始化为非推荐状态
lastRecommendTime: null // 初始化为空
}
const memorySlice = createSlice({
@ -685,6 +709,37 @@ const memorySlice = createSlice({
memory.lastAccessedAt = now
}
}
},
// 设置上下文感知记忆推荐是否启用
setContextualRecommendationEnabled: (state, action: PayloadAction<boolean>) => {
state.contextualRecommendationEnabled = action.payload
},
// 设置是否自动推荐记忆
setAutoRecommendMemories: (state, action: PayloadAction<boolean>) => {
state.autoRecommendMemories = action.payload
},
// 设置推荐阈值
setRecommendationThreshold: (state, action: PayloadAction<number>) => {
state.recommendationThreshold = action.payload
},
// 更新当前的记忆推荐
updateCurrentRecommendations: (state, action: PayloadAction<MemoryRecommendation[]>) => {
state.currentRecommendations = action.payload
state.lastRecommendTime = Date.now()
},
// 设置是否正在推荐记忆
setRecommending: (state, action: PayloadAction<boolean>) => {
state.isRecommending = action.payload
},
// 清除当前的记忆推荐
clearCurrentRecommendations: (state) => {
state.currentRecommendations = []
}
},
extraReducers: (builder) => {
@ -695,6 +750,23 @@ const memorySlice = createSlice({
state.memoryLists = action.payload.memoryLists || state.memoryLists
state.memories = action.payload.memories || state.memories
state.shortMemories = action.payload.shortMemories || state.shortMemories
// 更新模型选择
if (action.payload.analyzeModel) {
state.analyzeModel = action.payload.analyzeModel
console.log('[Memory Reducer] Loaded analyze model:', action.payload.analyzeModel)
}
if (action.payload.shortMemoryAnalyzeModel) {
state.shortMemoryAnalyzeModel = action.payload.shortMemoryAnalyzeModel
console.log('[Memory Reducer] Loaded short memory analyze model:', action.payload.shortMemoryAnalyzeModel)
}
if (action.payload.vectorizeModel) {
state.vectorizeModel = action.payload.vectorizeModel
console.log('[Memory Reducer] Loaded vectorize model:', action.payload.vectorizeModel)
}
log.info('Memory data loaded into state')
}
})
@ -747,7 +819,15 @@ export const {
setDecayRate,
updateMemoryPriorities,
updateMemoryFreshness,
accessMemory
accessMemory,
// 上下文感知记忆推荐相关的action
setContextualRecommendationEnabled,
setAutoRecommendMemories,
setRecommendationThreshold,
updateCurrentRecommendations,
setRecommending,
clearCurrentRecommendations
} = memorySlice.actions
// 加载记忆数据的异步 thunk