mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-08 22:39:36 +08:00
修复
This commit is contained in:
parent
445d4b879d
commit
e9347337b2
@ -10,35 +10,48 @@ const memoryDataPath = path.join(getConfigDir(), 'memory-data.json')
|
|||||||
|
|
||||||
export class MemoryFileService {
|
export class MemoryFileService {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.ensureMemoryFileExists()
|
|
||||||
this.registerIpcHandlers()
|
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() {
|
private registerIpcHandlers() {
|
||||||
// 读取记忆数据
|
// 读取记忆数据
|
||||||
ipcMain.handle(IpcChannel.Memory_LoadData, async () => {
|
ipcMain.handle(IpcChannel.Memory_LoadData, async () => {
|
||||||
try {
|
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')
|
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) {
|
} catch (error) {
|
||||||
log.error('Failed to load memory data:', error)
|
log.error('Failed to load memory data:', error)
|
||||||
return null
|
return null
|
||||||
@ -48,7 +61,32 @@ export class MemoryFileService {
|
|||||||
// 保存记忆数据
|
// 保存记忆数据
|
||||||
ipcMain.handle(IpcChannel.Memory_SaveData, async (_, data) => {
|
ipcMain.handle(IpcChannel.Memory_SaveData, async (_, data) => {
|
||||||
try {
|
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
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to save memory data:', error)
|
log.error('Failed to save memory data:', error)
|
||||||
|
|||||||
@ -41,8 +41,19 @@ const MemoryProvider: FC<MemoryProviderProps> = ({ children }) => {
|
|||||||
// 在组件挂载时加载记忆数据
|
// 在组件挂载时加载记忆数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[MemoryProvider] Loading memory data from file')
|
console.log('[MemoryProvider] Loading memory data from file')
|
||||||
|
// 使用Redux Thunk加载记忆数据
|
||||||
dispatch(loadMemoryData())
|
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(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -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
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { TopicManager } from '@renderer/hooks/useTopic'
|
import { TopicManager } from '@renderer/hooks/useTopic'
|
||||||
import { analyzeAndAddShortMemories, useMemoryService } from '@renderer/services/MemoryService'
|
import { analyzeAndAddShortMemories, useMemoryService } from '@renderer/services/MemoryService'
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import {
|
import {
|
||||||
addMemory,
|
addMemory,
|
||||||
clearMemories,
|
clearMemories,
|
||||||
@ -41,6 +41,7 @@ import MemoryDeduplicationPanel from './MemoryDeduplicationPanel'
|
|||||||
import MemoryListManager from './MemoryListManager'
|
import MemoryListManager from './MemoryListManager'
|
||||||
import MemoryMindMap from './MemoryMindMap'
|
import MemoryMindMap from './MemoryMindMap'
|
||||||
import PriorityManagementSettings from './PriorityManagementSettings'
|
import PriorityManagementSettings from './PriorityManagementSettings'
|
||||||
|
import ContextualRecommendationSettings from './ContextualRecommendationSettings'
|
||||||
|
|
||||||
const MemorySettings: FC = () => {
|
const MemorySettings: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -255,13 +256,49 @@ const MemorySettings: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理选择长期记忆分析模型
|
// 处理选择长期记忆分析模型
|
||||||
const handleSelectModel = (modelId: string) => {
|
const handleSelectModel = async (modelId: string) => {
|
||||||
dispatch(setAnalyzeModel(modelId))
|
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))
|
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: (
|
children: (
|
||||||
<TabPaneSettingGroup theme={theme}>
|
<TabPaneSettingGroup theme={theme}>
|
||||||
<PriorityManagementSettings />
|
<PriorityManagementSettings />
|
||||||
|
<SettingDivider />
|
||||||
|
<ContextualRecommendationSettings />
|
||||||
</TabPaneSettingGroup>
|
</TabPaneSettingGroup>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
530
src/renderer/src/services/ContextualMemoryService.ts
Normal file
530
src/renderer/src/services/ContextualMemoryService.ts
Normal 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()
|
||||||
@ -16,9 +16,15 @@ import {
|
|||||||
updateMemoryPriorities,
|
updateMemoryPriorities,
|
||||||
accessMemory,
|
accessMemory,
|
||||||
Memory,
|
Memory,
|
||||||
saveMemoryData // <-- 添加 saveMemoryData
|
saveMemoryData,
|
||||||
|
updateCurrentRecommendations,
|
||||||
|
setRecommending,
|
||||||
|
clearCurrentRecommendations,
|
||||||
|
MemoryRecommendation
|
||||||
} from '@renderer/store/memory'
|
} from '@renderer/store/memory'
|
||||||
import { useCallback, useEffect, useRef } from 'react' // Add useRef back
|
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' => {
|
const calculateConversationComplexity = (conversation: string): 'low' | 'medium' | 'high' => {
|
||||||
@ -167,6 +173,162 @@ ${conversation}
|
|||||||
|
|
||||||
// This function definition is a duplicate, removing it.
|
// 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 = () => {
|
export const useMemoryService = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@ -174,6 +336,8 @@ export const useMemoryService = () => {
|
|||||||
const isActive = useAppSelector((state) => state.memory?.isActive || false)
|
const isActive = useAppSelector((state) => state.memory?.isActive || false)
|
||||||
const autoAnalyze = useAppSelector((state) => state.memory?.autoAnalyze || false)
|
const autoAnalyze = useAppSelector((state) => state.memory?.autoAnalyze || false)
|
||||||
const analyzeModel = useAppSelector((state) => state.memory?.analyzeModel || null)
|
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 定义分析函数,但减少依赖项
|
// 使用 useCallback 定义分析函数,但减少依赖项
|
||||||
// 增加可选的 topicId 参数,允许分析指定的话题
|
// 增加可选的 topicId 参数,允许分析指定的话题
|
||||||
@ -559,8 +723,85 @@ ${newConversation}
|
|||||||
// 依赖项只包含决定是否启动定时器的设置
|
// 依赖项只包含决定是否启动定时器的设置
|
||||||
}, [isActive, autoAnalyze, analyzeModel])
|
}, [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
|
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) {
|
while ((match = listItemRegex.exec(result)) !== null) {
|
||||||
if (match[1] && match[1].trim()) {
|
if (match[1] && match[1].trim()) {
|
||||||
extractedLines.push(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
|
const state = store.getState() // Use imported store
|
||||||
// 确保 state.memory 存在,如果不存在则提供默认值
|
// 确保 state.memory 存在,如果不存在则提供默认值
|
||||||
const { isActive, memories, memoryLists, shortMemoryActive, shortMemories, priorityManagementEnabled } = state.memory || {
|
const {
|
||||||
|
isActive,
|
||||||
|
memories,
|
||||||
|
memoryLists,
|
||||||
|
shortMemoryActive,
|
||||||
|
shortMemories,
|
||||||
|
priorityManagementEnabled,
|
||||||
|
contextualRecommendationEnabled,
|
||||||
|
currentRecommendations
|
||||||
|
} = state.memory || {
|
||||||
isActive: false,
|
isActive: false,
|
||||||
memories: [],
|
memories: [],
|
||||||
memoryLists: [],
|
memoryLists: [],
|
||||||
shortMemoryActive: false,
|
shortMemoryActive: false,
|
||||||
shortMemories: [],
|
shortMemories: [],
|
||||||
priorityManagementEnabled: false
|
priorityManagementEnabled: false,
|
||||||
|
contextualRecommendationEnabled: false,
|
||||||
|
currentRecommendations: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前话题ID
|
// 获取当前话题ID
|
||||||
@ -857,6 +1109,50 @@ export const applyMemoriesToPrompt = (systemPrompt: string): string => {
|
|||||||
let result = systemPrompt
|
let result = systemPrompt
|
||||||
let hasContent = false
|
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) {
|
if (shortMemoryActive && shortMemories && shortMemories.length > 0 && currentTopicId) {
|
||||||
// 获取当前话题的短记忆
|
// 获取当前话题的短记忆
|
||||||
|
|||||||
@ -76,6 +76,14 @@ export interface UserInterest {
|
|||||||
lastUpdated: string // 上次更新时间
|
lastUpdated: string // 上次更新时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记忆推荐结果接口
|
||||||
|
export interface MemoryRecommendation {
|
||||||
|
memoryId: string
|
||||||
|
relevanceScore: number
|
||||||
|
source: 'long-term' | 'short-term'
|
||||||
|
matchReason?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface MemoryState {
|
export interface MemoryState {
|
||||||
memoryLists: MemoryList[] // 记忆列表
|
memoryLists: MemoryList[] // 记忆列表
|
||||||
memories: Memory[] // 所有记忆项
|
memories: Memory[] // 所有记忆项
|
||||||
@ -110,6 +118,14 @@ export interface MemoryState {
|
|||||||
freshnessEnabled: boolean // 是否启用记忆鲜度评估
|
freshnessEnabled: boolean // 是否启用记忆鲜度评估
|
||||||
decayRate: number // 记忆衰减速率(0-1)
|
decayRate: number // 记忆衰减速率(0-1)
|
||||||
lastPriorityUpdate: number // 上次优先级更新时间
|
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, // 默认启用记忆衰减功能
|
decayEnabled: true, // 默认启用记忆衰减功能
|
||||||
freshnessEnabled: true, // 默认启用记忆鲜度评估
|
freshnessEnabled: true, // 默认启用记忆鲜度评估
|
||||||
decayRate: 0.05, // 默认衰减速率,每天减少5%
|
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({
|
const memorySlice = createSlice({
|
||||||
@ -685,6 +709,37 @@ const memorySlice = createSlice({
|
|||||||
memory.lastAccessedAt = now
|
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) => {
|
extraReducers: (builder) => {
|
||||||
@ -695,6 +750,23 @@ const memorySlice = createSlice({
|
|||||||
state.memoryLists = action.payload.memoryLists || state.memoryLists
|
state.memoryLists = action.payload.memoryLists || state.memoryLists
|
||||||
state.memories = action.payload.memories || state.memories
|
state.memories = action.payload.memories || state.memories
|
||||||
state.shortMemories = action.payload.shortMemories || state.shortMemories
|
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')
|
log.info('Memory data loaded into state')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -747,7 +819,15 @@ export const {
|
|||||||
setDecayRate,
|
setDecayRate,
|
||||||
updateMemoryPriorities,
|
updateMemoryPriorities,
|
||||||
updateMemoryFreshness,
|
updateMemoryFreshness,
|
||||||
accessMemory
|
accessMemory,
|
||||||
|
|
||||||
|
// 上下文感知记忆推荐相关的action
|
||||||
|
setContextualRecommendationEnabled,
|
||||||
|
setAutoRecommendMemories,
|
||||||
|
setRecommendationThreshold,
|
||||||
|
updateCurrentRecommendations,
|
||||||
|
setRecommending,
|
||||||
|
clearCurrentRecommendations
|
||||||
} = memorySlice.actions
|
} = memorySlice.actions
|
||||||
|
|
||||||
// 加载记忆数据的异步 thunk
|
// 加载记忆数据的异步 thunk
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user