Files
PromptX/src/lib/core/pouch/commands/RecallCommand.js
sean 041ece9af1 重构:引入统一的DirectoryService以优化目录管理
- 在InitCommand、RecallCommand、RememberCommand和PouchStateMachine中替换了直接路径处理逻辑,改为使用DirectoryService进行目录解析。
- 更新了ProjectDiscovery以使用新的getProjectRoot方法,标记旧方法为已弃用。
- 在executionContext中重构了工作目录获取逻辑,增强了兼容性和可维护性。
- 确保了对用户主目录的避免处理,提升了目录定位的智能性和可靠性。

此改动旨在提升代码的可读性和一致性,同时为未来的扩展打下基础。
2025-06-15 11:23:19 +08:00

199 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const BasePouchCommand = require('../BasePouchCommand')
const fs = require('fs-extra')
const path = require('path')
const { COMMANDS } = require('../../../../constants')
/**
* 记忆检索锦囊命令
* 负责从记忆库中检索相关知识和经验
*/
class RecallCommand extends BasePouchCommand {
constructor () {
super()
}
getPurpose () {
return 'AI主动检索记忆中的专业知识、最佳实践和历史经验'
}
async getContent (args) {
const [query] = args
try {
const memories = await this.getAllMemories(query)
if (memories.length === 0) {
return `🧠 AI记忆体系中暂无内容。
💡 建议:
1. 使用 MCP PromptX remember 工具内化新知识
2. 使用 MCP PromptX learn 工具学习后再内化
3. 开始构建AI的专业知识体系`
}
const formattedMemories = this.formatRetrievedKnowledge(memories, query)
return `🧠 AI记忆体系 ${query ? `检索"${query}"` : '全部记忆'} (${memories.length}条)
${formattedMemories}
💡 记忆运用建议:
1. 结合当前任务场景灵活运用
2. 根据实际情况调整和变通
3. 持续学习和增强记忆能力`
} catch (error) {
return `❌ 检索记忆时出错:${error.message}`
}
}
getPATEOAS (args) {
const [query] = args
const currentState = query ? `recalled-${query}` : 'recall-waiting'
return {
currentState,
availableTransitions: ['hello', 'remember', 'learn', 'recall'],
nextActions: [
{
name: '选择角色',
description: '选择专业角色来应用检索到的知识',
method: 'MCP PromptX hello 工具'
},
{
name: '记忆新知识',
description: '继续内化更多专业知识',
method: 'MCP PromptX remember 工具'
},
{
name: '学习资源',
description: '学习相关专业资源',
method: 'MCP PromptX learn 工具'
},
{
name: '继续检索',
description: '检索其他相关记忆',
method: 'MCP PromptX recall 工具'
}
],
metadata: {
query: query || null,
resultCount: this.lastSearchCount || 0,
searchTime: new Date().toISOString(),
hasResults: (this.lastSearchCount || 0) > 0
}
}
}
/**
* 获取所有记忆(紧凑格式)
*/
async getAllMemories (query) {
this.lastSearchCount = 0
const memories = []
// 读取单一记忆文件
const { getDirectoryService } = require('../../../utils/DirectoryService')
const directoryService = getDirectoryService()
const memoryDir = await directoryService.getMemoryDirectory()
const memoryFile = path.join(memoryDir, 'declarative.md')
try {
if (await fs.pathExists(memoryFile)) {
const content = await fs.readFile(memoryFile, 'utf-8')
const lines = content.split('\n')
for (const line of lines) {
if (line.startsWith('- ')) {
// 解析记忆行
const memory = this.parseMemoryLine(line)
if (memory && (!query || this.matchesMemory(memory, query))) {
memories.push(memory)
}
}
}
}
} catch (error) {
console.error('Error reading memories:', error)
}
this.lastSearchCount = memories.length
return memories
}
/**
* 解析记忆行(紧凑格式)
*/
parseMemoryLine (line) {
// 格式:- 2025/05/31 14:30 内容 #tag1 #tag2 #评分:8 #有效期:长期
const match = line.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.*?) (#.*?)$/)
if (!match) return null
const [, timestamp, content, tagsStr] = match
const tags = tagsStr.split(' ').filter(t => t.startsWith('#'))
return {
timestamp,
content,
tags,
source: 'memory'
}
}
/**
* 检查记忆是否匹配查询
*/
matchesMemory (memory, query) {
const lowerQuery = query.toLowerCase()
return memory.content.toLowerCase().includes(lowerQuery) ||
memory.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
}
matchesQuery (content, query) {
const lowerContent = content.toLowerCase()
const lowerQuery = query.toLowerCase()
const keywords = lowerQuery.split(/\s+/)
return keywords.some(keyword => lowerContent.includes(keyword))
}
/**
* 格式化检索到的记忆(紧凑格式)
*/
formatRetrievedKnowledge (memories, query) {
return memories.map((memory, index) => {
const content = memory.content.length > 120
? memory.content.substring(0, 120) + '...'
: memory.content
return `📝 ${index + 1}. **记忆** (${memory.timestamp})
${content}
${memory.tags.slice(0, 5).join(' ')}
---`
}).join('\n')
}
extractDomain (query) {
const domains = ['copywriter', 'scrum', 'developer', 'test', 'prompt']
const lowerQuery = query.toLowerCase()
return domains.find(domain => lowerQuery.includes(domain)) || null
}
getRelatedQuery (query) {
const relatedMap = {
copywriter: 'marketing',
scrum: 'agile',
frontend: 'ui',
backend: 'api',
test: 'qa'
}
for (const [key, value] of Object.entries(relatedMap)) {
if (query.toLowerCase().includes(key)) {
return value
}
}
return query + '-advanced'
}
}
module.exports = RecallCommand