优化角色注册,发现,nuwa 角色的提示词等

This commit is contained in:
sean
2025-06-11 18:03:55 +08:00
parent 821df44104
commit 283374bf09
32 changed files with 3582 additions and 2643 deletions

View File

@ -3,6 +3,9 @@ const fs = require('fs-extra')
const path = require('path')
const { COMMANDS, buildCommand } = require('../../../../constants')
const ResourceManager = require('../../resource/resourceManager')
const DPMLContentParser = require('../../resource/DPMLContentParser')
const SemanticRenderer = require('../../resource/SemanticRenderer')
const logger = require('../../../utils/logger')
/**
* 角色激活锦囊命令
@ -14,6 +17,8 @@ class ActionCommand extends BasePouchCommand {
// 获取HelloCommand的角色注册表
this.helloCommand = null
this.resourceManager = new ResourceManager()
this.dpmlParser = new DPMLContentParser()
this.semanticRenderer = new SemanticRenderer()
}
getPurpose () {
@ -38,9 +43,14 @@ ${COMMANDS.HELLO}
}
try {
logger.debug(`[ActionCommand] 开始激活角色: ${roleId}`)
// 1. 获取角色信息
const roleInfo = await this.getRoleInfo(roleId)
logger.debug(`[ActionCommand] getRoleInfo结果:`, roleInfo)
if (!roleInfo) {
logger.warn(`[ActionCommand] 角色 "${roleId}" 不存在!`)
return `❌ 角色 "${roleId}" 不存在!
🔍 请使用以下命令查看可用角色:
@ -71,17 +81,24 @@ ${COMMANDS.HELLO}
* 获取角色信息从HelloCommand
*/
async getRoleInfo (roleId) {
logger.debug(`[ActionCommand] getRoleInfo调用角色ID: ${roleId}`)
// 懒加载HelloCommand实例
if (!this.helloCommand) {
logger.debug(`[ActionCommand] 创建新的HelloCommand实例`)
const HelloCommand = require('./HelloCommand')
this.helloCommand = new HelloCommand()
} else {
logger.debug(`[ActionCommand] 复用现有HelloCommand实例`)
}
return await this.helloCommand.getRoleInfo(roleId)
const result = await this.helloCommand.getRoleInfo(roleId)
logger.debug(`[ActionCommand] HelloCommand.getRoleInfo返回:`, result)
return result
}
/**
* 分析角色文件,提取thought和execution依赖
* 分析角色文件,提取完整的角色语义(@引用 + 直接内容)
*/
async analyzeRoleDependencies (roleInfo) {
try {
@ -99,32 +116,47 @@ ${COMMANDS.HELLO}
// 读取角色文件内容
const roleContent = await fs.readFile(filePath, 'utf-8')
// 提取所有资源引用
const resourceRegex = /@([!?]?)([a-zA-Z][a-zA-Z0-9_-]*):\/\/([a-zA-Z0-9_\/.,-]+?)(?=[\s\)\],]|$)/g
const matches = Array.from(roleContent.matchAll(resourceRegex))
const dependencies = {
thoughts: new Set(),
executions: new Set(),
knowledge: [roleInfo.id] // 角色自身的knowledge
}
// 分类依赖
matches.forEach(match => {
const [fullMatch, priority, protocol, resource] = match
if (protocol === 'thought') {
dependencies.thoughts.add(resource)
} else if (protocol === 'execution') {
dependencies.executions.add(resource)
// 使用DPMLContentParser解析完整的角色语义
const roleSemantics = this.dpmlParser.parseRoleDocument(roleContent)
// 提取@引用依赖(保持兼容性)
// 注意:对于包含语义内容的角色,引用已在语义渲染中处理,无需重复加载
const thoughts = new Set()
const executions = new Set()
// 从所有标签中提取thought和execution引用
// 但排除已在语义内容中处理的引用
Object.values(roleSemantics).forEach(tagSemantics => {
if (tagSemantics && tagSemantics.references) {
tagSemantics.references.forEach(ref => {
// 跳过已在语义内容中处理的引用
if (tagSemantics.fullSemantics) {
// 如果标签有完整语义内容,其引用将在语义渲染中处理,无需独立加载
return
}
if (ref.protocol === 'thought') {
thoughts.add(ref.resource)
} else if (ref.protocol === 'execution') {
executions.add(ref.resource)
}
})
}
})
return {
thoughts: dependencies.thoughts,
executions: dependencies.executions,
knowledge: dependencies.knowledge
// 保持原有结构(兼容性)
thoughts,
executions,
knowledge: [roleInfo.id],
// 新增:完整的角色语义结构
roleSemantics: {
personality: roleSemantics.personality || null,
principle: roleSemantics.principle || null,
knowledge: roleSemantics.knowledge || null
}
}
} catch (error) {
console.error('Error analyzing role dependencies:', error)
@ -132,7 +164,12 @@ ${COMMANDS.HELLO}
return {
thoughts: [],
executions: [],
knowledge: [roleInfo.id]
knowledge: [roleInfo.id],
roleSemantics: {
personality: null,
principle: null,
knowledge: null
}
}
}
}
@ -267,37 +304,85 @@ ${result.content}
}
/**
* 生成学习计划并直接加载所有内容
* 生成学习计划并直接加载所有内容(包含完整的角色语义)
*/
async generateLearningPlan (roleId, dependencies) {
const { thoughts, executions } = dependencies
const { thoughts, executions, roleSemantics } = dependencies
let content = `🎭 **角色激活完成:${roleId}** - 所有技能已自动加载\n`
// 加载思维模式
// 加载思维模式技能仅包含独立的thought引用
if (thoughts.size > 0) {
content += `# 🧠 思维模式技能 (${thoughts.size}个)\n`
// 加载引用的思维资源
for (const thought of Array.from(thoughts)) {
content += await this.loadLearnContent(`thought://${thought}`)
}
}
// 加载执行技能
// 添加角色人格特征(支持@引用占位符语义渲染)
if (roleSemantics.personality && roleSemantics.personality.fullSemantics) {
content += `# 👤 角色人格特征\n`
content += `## ✅ 👤 人格特征:${roleId}\n`
const personalityContent = await this.semanticRenderer.renderSemanticContent(
roleSemantics.personality,
this.resourceManager
)
content += `${personalityContent}\n`
content += `---\n`
}
// 加载执行技能仅包含独立的execution引用
if (executions.size > 0) {
content += `# ⚡ 执行技能 (${executions.size}个)\n`
// 加载引用的执行资源
for (const execution of Array.from(executions)) {
content += await this.loadLearnContent(`execution://${execution}`)
}
}
// 添加角色行为原则(支持@引用占位符语义渲染)
if (roleSemantics.principle && roleSemantics.principle.fullSemantics) {
content += `# ⚖️ 角色行为原则\n`
content += `## ✅ ⚖️ 行为原则:${roleId}\n`
const principleContent = await this.semanticRenderer.renderSemanticContent(
roleSemantics.principle,
this.resourceManager
)
content += `${principleContent}\n`
content += `---\n`
}
// 添加语义渲染的知识体系(支持@引用占位符)
if (roleSemantics.knowledge && roleSemantics.knowledge.fullSemantics) {
content += `# 📚 专业知识体系\n`
content += `## ✅ 📚 知识体系:${roleId}-knowledge\n`
const knowledgeContent = await this.semanticRenderer.renderSemanticContent(
roleSemantics.knowledge,
this.resourceManager
)
content += `${knowledgeContent}\n`
content += `---\n`
}
// 激活总结
content += `# 🎯 角色激活总结\n`
content += `✅ **${roleId} 角色已完全激活!**\n`
content += `📋 **已获得能力**\n`
if (thoughts.size > 0) content += `- 🧠 思维模式:${Array.from(thoughts).join(', ')}\n`
if (executions.size > 0) content += `- ⚡ 执行技能:${Array.from(executions).join(', ')}\n`
// 显示角色核心组件
const roleComponents = []
if (roleSemantics.personality?.fullSemantics) roleComponents.push('👤 人格特征')
if (roleSemantics.principle?.fullSemantics) roleComponents.push('⚖️ 行为原则')
if (roleSemantics.knowledge?.fullSemantics) roleComponents.push('📚 专业知识')
if (roleComponents.length > 0) {
content += `- 🎭 角色组件:${roleComponents.join(', ')}\n`
}
content += `💡 **现在可以立即开始以 ${roleId} 身份提供专业服务!**\n`
// 自动执行 recall 命令

View File

@ -2,6 +2,8 @@ const BasePouchCommand = require('../BasePouchCommand')
const fs = require('fs-extra')
const path = require('path')
const { buildCommand } = require('../../../../constants')
const SimplifiedRoleDiscovery = require('../../resource/SimplifiedRoleDiscovery')
const logger = require('../../../utils/logger')
/**
* 角色发现锦囊命令
@ -10,7 +12,8 @@ const { buildCommand } = require('../../../../constants')
class HelloCommand extends BasePouchCommand {
constructor () {
super()
this.roleRegistry = null // 角色注册表将从资源系统动态加载
// 移除roleRegistry缓存,改为每次实时扫描
this.discovery = new SimplifiedRoleDiscovery()
}
getPurpose () {
@ -18,28 +21,21 @@ class HelloCommand extends BasePouchCommand {
}
/**
* 动态加载角色注册表
* 动态加载角色注册表 - 使用SimplifiedRoleDiscovery
* 移除缓存机制,每次都实时扫描,确保角色发现的一致性
*/
async loadRoleRegistry () {
if (this.roleRegistry) {
return this.roleRegistry
}
// 移除缓存检查,每次都实时扫描
// 原因1) 客户端应用action频次不高 2) 避免新角色创建后的状态不一致问题
try {
// 使用新的ResourceManager架构
const ResourceManager = require('../../resource/resourceManager')
const resourceManager = new ResourceManager()
// 使用新的SimplifiedRoleDiscovery算法
const allRoles = await this.discovery.discoverAllRoles()
// 加载统一注册表(包含系统+用户资源)
const unifiedRegistry = await resourceManager.loadUnifiedRegistry()
// 提取角色数据
const roleData = unifiedRegistry.role || {}
// 转换为HelloCommand期望的格式
this.roleRegistry = {}
for (const [roleId, roleInfo] of Object.entries(roleData)) {
this.roleRegistry[roleId] = {
// 转换为HelloCommand期望的格式不缓存
const roleRegistry = {}
for (const [roleId, roleInfo] of Object.entries(allRoles)) {
roleRegistry[roleId] = {
file: roleInfo.file,
name: roleInfo.name || roleId,
description: this.extractDescription(roleInfo) || `${roleInfo.name || roleId}专业角色`,
@ -48,21 +44,21 @@ class HelloCommand extends BasePouchCommand {
}
// 如果没有任何角色,使用基础角色
if (Object.keys(this.roleRegistry).length === 0) {
this.roleRegistry = {
assistant: {
file: '@package://prompt/domain/assistant/assistant.role.md',
name: '🙋 智能助手',
description: '通用助理角色,提供基础的助理服务和记忆支持',
source: 'fallback'
}
if (Object.keys(roleRegistry).length === 0) {
roleRegistry.assistant = {
file: '@package://prompt/domain/assistant/assistant.role.md',
name: '🙋 智能助手',
description: '通用助理角色,提供基础的助理服务和记忆支持',
source: 'fallback'
}
}
return roleRegistry
} catch (error) {
console.warn('角色注册表加载失败,使用基础角色:', error.message)
logger.warn('角色注册表加载失败,使用基础角色:', error.message)
// 使用基础角色作为fallback
this.roleRegistry = {
return {
assistant: {
file: '@package://prompt/domain/assistant/assistant.role.md',
name: '🙋 智能助手',
@ -71,8 +67,6 @@ class HelloCommand extends BasePouchCommand {
}
}
}
return this.roleRegistry
}
/**
@ -212,19 +206,28 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
* 获取角色信息(提供给其他命令使用)
*/
async getRoleInfo (roleId) {
logger.debug(`[HelloCommand] getRoleInfo调用角色ID: ${roleId}`)
const registry = await this.loadRoleRegistry()
logger.debug(`[HelloCommand] 注册表加载完成,包含角色:`, Object.keys(registry))
const roleData = registry[roleId]
logger.debug(`[HelloCommand] 查找角色${roleId}结果:`, roleData ? '找到' : '未找到')
if (!roleData) {
logger.debug(`[HelloCommand] 角色${roleId}在注册表中不存在`)
return null
}
return {
const result = {
id: roleId,
name: roleData.name,
description: roleData.description,
file: roleData.file
}
logger.debug(`[HelloCommand] 返回角色信息:`, result)
return result
}
/**
@ -238,62 +241,10 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
}
/**
* 动态发现本地角色文件
* 注意原来的discoverLocalRoles方法已被移除
* 现在使用SimplifiedRoleDiscovery.discoverAllRoles()替代
* 这避免了glob依赖和跨平台兼容性问题
*/
async discoverLocalRoles () {
const PackageProtocol = require('../../resource/protocols/PackageProtocol')
const packageProtocol = new PackageProtocol()
const glob = require('glob')
const path = require('path')
try {
const packageRoot = await packageProtocol.getPackageRoot()
const domainPath = path.join(packageRoot, 'prompt', 'domain')
// 扫描所有角色目录
const rolePattern = path.join(domainPath, '*', '*.role.md')
const roleFiles = glob.sync(rolePattern)
const discoveredRoles = {}
for (const roleFile of roleFiles) {
try {
const content = await fs.readFile(roleFile, 'utf-8')
const relativePath = path.relative(packageRoot, roleFile)
const roleName = path.basename(roleFile, '.role.md')
// 尝试从文件内容中提取角色信息
let description = '本地发现的角色'
let name = `🎭 ${roleName}`
// 简单的元数据提取(支持多行)
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
if (descMatch) {
description = descMatch[1].trim()
}
const nameMatch = content.match(/name:\s*(.+?)(?:\n|$)/i)
if (nameMatch) {
name = nameMatch[1].trim()
}
discoveredRoles[roleName] = {
file: `@package://${relativePath}`,
name,
description,
source: 'local-discovery'
}
} catch (error) {
console.warn(`跳过无效的角色文件: ${roleFile}`, error.message)
}
}
return discoveredRoles
} catch (error) {
console.warn('动态角色发现失败:', error.message)
return {}
}
}
}
module.exports = HelloCommand

View File

@ -0,0 +1,179 @@
/**
* DPML内容解析器
* 统一处理DPML标签内的混合内容@引用 + 直接内容)
* 确保标签语义完整性
*/
class DPMLContentParser {
/**
* 解析DPML标签的完整语义内容
* @param {string} content - 标签内的原始内容
* @param {string} tagName - 标签名称
* @returns {Object} 完整的语义结构
*/
parseTagContent(content, tagName) {
if (!content || !content.trim()) {
return {
fullSemantics: '',
references: [],
directContent: '',
metadata: {
tagName,
hasReferences: false,
hasDirectContent: false,
contentType: 'empty'
}
}
}
const cleanContent = content.trim()
const references = this.extractReferencesWithPosition(cleanContent)
const directContent = this.extractDirectContent(cleanContent)
return {
// 完整语义内容(用户看到的最终效果)
fullSemantics: cleanContent,
// 引用部分(需要解析和加载的资源)
references,
// 直接部分(用户原创内容)
directContent,
// 元数据
metadata: {
tagName,
hasReferences: references.length > 0,
hasDirectContent: directContent.length > 0,
contentType: this.determineContentType(cleanContent)
}
}
}
/**
* 提取所有@引用
* @param {string} content - 内容
* @returns {Array} 引用数组
*/
extractReferences(content) {
// 使用新的位置信息方法,但保持向下兼容
return this.extractReferencesWithPosition(content).map(ref => ({
fullMatch: ref.fullMatch,
priority: ref.priority,
protocol: ref.protocol,
resource: ref.resource,
isRequired: ref.isRequired,
isOptional: ref.isOptional
}))
}
/**
* 新增:获取引用的位置信息
* @param {string} content - 内容
* @returns {Array} 包含位置信息的引用数组
*/
extractReferencesWithPosition(content) {
if (!content) {
return []
}
const resourceRegex = /@([!?]?)([a-zA-Z][a-zA-Z0-9_-]*):\/\/([a-zA-Z0-9_\/.,-]+?)(?=[\s\)\],]|$)/g
const matches = []
let match
while ((match = resourceRegex.exec(content)) !== null) {
matches.push({
fullMatch: match[0],
priority: match[1],
protocol: match[2],
resource: match[3],
position: match.index, // 位置信息
isRequired: match[1] === '!',
isOptional: match[1] === '?'
})
}
return matches.sort((a, b) => a.position - b.position) // 按位置排序
}
/**
* 提取直接内容(移除@引用后的剩余内容)
* @param {string} content - 内容
* @returns {string} 直接内容
*/
extractDirectContent(content) {
// 移除所有@引用行,保留其他内容
const withoutReferences = content.replace(/^.*@[!?]?[a-zA-Z][a-zA-Z0-9_-]*:\/\/.*$/gm, '')
// 清理多余的空行
const cleaned = withoutReferences.replace(/\n{3,}/g, '\n\n').trim()
return cleaned
}
/**
* 检查是否包含引用
* @param {string} content - 内容
* @returns {boolean}
*/
hasReferences(content) {
return /@[!?]?[a-zA-Z][a-zA-Z0-9_-]*:\/\//.test(content)
}
/**
* 检查是否包含直接内容
* @param {string} content - 内容
* @returns {boolean}
*/
hasDirectContent(content) {
const withoutReferences = this.extractDirectContent(content)
return withoutReferences.length > 0
}
/**
* 确定内容类型
* @param {string} content - 内容
* @returns {string} 内容类型
*/
determineContentType(content) {
const hasRefs = this.hasReferences(content)
const hasDirect = this.hasDirectContent(content)
if (hasRefs && hasDirect) return 'mixed'
if (hasRefs) return 'references-only'
if (hasDirect) return 'direct-only'
return 'empty'
}
/**
* 从DPML文档中提取指定标签的内容
* @param {string} dpmlContent - 完整的DPML文档内容
* @param {string} tagName - 标签名称
* @returns {string} 标签内容
*/
extractTagContent(dpmlContent, tagName) {
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, 'i')
const match = dpmlContent.match(regex)
return match ? match[1] : ''
}
/**
* 解析完整的DPML角色文档
* @param {string} roleContent - 角色文档内容
* @returns {Object} 解析后的角色语义结构
*/
parseRoleDocument(roleContent) {
const dpmlTags = ['personality', 'principle', 'knowledge']
const roleSemantics = {}
dpmlTags.forEach(tagName => {
const tagContent = this.extractTagContent(roleContent, tagName)
if (tagContent) {
roleSemantics[tagName] = this.parseTagContent(tagContent, tagName)
}
})
return roleSemantics
}
}
module.exports = DPMLContentParser

View File

@ -0,0 +1,83 @@
/**
* SemanticRenderer - DPML语义渲染器
*
* 核心理念:@引用 = 语义占位符
* 在标签的原始位置插入引用内容,保持完整的语义流程
*/
class SemanticRenderer {
/**
* 语义占位符渲染:将@引用替换为实际内容
* @param {Object} tagSemantics - 标签语义结构
* @param {string} tagSemantics.fullSemantics - 完整的语义内容
* @param {Array} tagSemantics.references - 引用列表
* @param {ResourceManager} resourceManager - 资源管理器
* @returns {string} 完整融合的语义内容
*/
async renderSemanticContent(tagSemantics, resourceManager) {
if (!tagSemantics || !tagSemantics.fullSemantics) {
return ''
}
let content = tagSemantics.fullSemantics
if (!tagSemantics.references || tagSemantics.references.length === 0) {
return content.trim()
}
// 按出现顺序处理每个@引用(保持位置语义)
// 需要按位置排序确保正确的替换顺序
const sortedReferences = [...tagSemantics.references].sort((a, b) => a.position - b.position)
for (const ref of sortedReferences) {
try {
// 解析引用内容
const result = await resourceManager.resolve(ref.fullMatch)
// 检查解析是否成功
if (result.success) {
// 提取标签内容去掉外层DPML标签
const cleanContent = this.extractTagInnerContent(result.content, ref.protocol)
// 用<reference>标签包装引用内容,标明这是占位符渲染
const wrappedContent = `<reference protocol="${ref.protocol}" resource="${ref.resource}">\n${cleanContent}\n</reference>`
// 在原始位置替换@引用为实际内容
const refIndex = content.indexOf(ref.fullMatch)
if (refIndex !== -1) {
content = content.substring(0, refIndex) + wrappedContent + content.substring(refIndex + ref.fullMatch.length)
} else {
content = content.replace(ref.fullMatch, wrappedContent)
}
} else {
// 解析失败时的优雅降级
content = content.replace(ref.fullMatch, `<!-- 引用解析失败: ${ref.fullMatch} - ${result.error?.message || 'Unknown error'} -->`)
}
} catch (error) {
// 引用解析失败时的优雅降级
content = content.replace(ref.fullMatch, `<!-- 引用解析失败: ${ref.fullMatch} - ${error.message} -->`)
}
}
return content.trim()
}
/**
* 提取DPML标签内的内容
* @param {string} content - 包含DPML标签的完整内容
* @param {string} protocol - 协议名称thought, execution等
* @returns {string} 标签内的纯内容
*/
extractTagInnerContent(content, protocol) {
// 根据协议类型确定标签名
const tagName = protocol
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, 'i')
const match = content.match(regex)
if (match && match[1]) {
return match[1].trim()
}
// 如果没有匹配到标签,返回原内容(可能已经是纯内容)
return content.trim()
}
}
module.exports = SemanticRenderer

View File

@ -0,0 +1,285 @@
const fs = require('fs-extra')
const path = require('path')
const logger = require('../../utils/logger')
/**
* SimplifiedRoleDiscovery - 简化的角色发现算法
*
* 设计原则:
* 1. 系统角色:完全依赖静态注册表,零动态扫描
* 2. 用户角色:最小化文件系统操作,简单有效
* 3. 统一接口:单一发现入口,无重复逻辑
* 4. 跨平台安全使用Node.js原生API避免glob
*/
class SimplifiedRoleDiscovery {
constructor() {
this.USER_RESOURCE_DIR = '.promptx'
this.RESOURCE_DOMAIN_PATH = ['resource', 'domain']
}
/**
* 发现所有角色(系统 + 用户)
* @returns {Promise<Object>} 合并后的角色注册表
*/
async discoverAllRoles() {
logger.debug('[SimplifiedRoleDiscovery] 开始发现所有角色...')
try {
// 并行加载,提升性能
const [systemRoles, userRoles] = await Promise.all([
this.loadSystemRoles(),
this.discoverUserRoles()
])
logger.debug('[SimplifiedRoleDiscovery] 系统角色数量:', Object.keys(systemRoles).length)
logger.debug('[SimplifiedRoleDiscovery] 用户角色数量:', Object.keys(userRoles).length)
logger.debug('[SimplifiedRoleDiscovery] 用户角色列表:', Object.keys(userRoles))
// 用户角色覆盖同名系统角色
const mergedRoles = this.mergeRoles(systemRoles, userRoles)
logger.debug('[SimplifiedRoleDiscovery] 合并后总角色数量:', Object.keys(mergedRoles).length)
logger.debug('[SimplifiedRoleDiscovery] 最终角色列表:', Object.keys(mergedRoles))
return mergedRoles
} catch (error) {
logger.warn(`[SimplifiedRoleDiscovery] 角色发现失败: ${error.message}`)
return {}
}
}
/**
* 加载系统角色(零文件扫描)
* @returns {Promise<Object>} 系统角色注册表
*/
async loadSystemRoles() {
try {
const registryPath = path.resolve(__dirname, '../../../resource.registry.json')
if (!await fs.pathExists(registryPath)) {
console.warn('系统资源注册表文件不存在')
return {}
}
const registry = await fs.readJSON(registryPath)
return registry.protocols?.role?.registry || {}
} catch (error) {
console.warn(`加载系统角色失败: ${error.message}`)
return {}
}
}
/**
* 发现用户角色(最小化扫描)
* @returns {Promise<Object>} 用户角色注册表
*/
async discoverUserRoles() {
try {
const userRolePath = await this.getUserRolePath()
logger.debug('[SimplifiedRoleDiscovery] 用户角色路径:', userRolePath)
// 快速检查:目录不存在直接返回
if (!await fs.pathExists(userRolePath)) {
logger.debug('[SimplifiedRoleDiscovery] 用户角色目录不存在')
return {}
}
logger.debug('[SimplifiedRoleDiscovery] 开始扫描用户角色目录...')
const result = await this.scanUserRolesOptimized(userRolePath)
logger.debug('[SimplifiedRoleDiscovery] 用户角色扫描完成,发现角色:', Object.keys(result))
return result
} catch (error) {
logger.warn(`[SimplifiedRoleDiscovery] 用户角色发现失败: ${error.message}`)
return {}
}
}
/**
* 优化的用户角色扫描算法
* @param {string} basePath - 用户角色基础路径
* @returns {Promise<Object>} 发现的用户角色
*/
async scanUserRolesOptimized(basePath) {
const roles = {}
try {
// 使用withFileTypes提升性能一次读取获得文件类型
const entries = await fs.readdir(basePath, { withFileTypes: true })
// 只处理目录,跳过文件
const directories = entries.filter(entry => entry.isDirectory())
// 并行检查所有角色目录(性能优化)
const rolePromises = directories.map(dir =>
this.checkRoleDirectory(basePath, dir.name)
)
const roleResults = await Promise.allSettled(rolePromises)
// 收集成功的角色
roleResults.forEach((result, index) => {
if (result.status === 'fulfilled' && result.value) {
const roleName = directories[index].name
roles[roleName] = result.value
}
})
} catch (error) {
console.warn(`扫描用户角色目录失败: ${error.message}`)
}
return roles
}
/**
* 检查单个角色目录
* @param {string} basePath - 基础路径
* @param {string} roleName - 角色名称
* @returns {Promise<Object|null>} 角色信息或null
*/
async checkRoleDirectory(basePath, roleName) {
logger.debug(`[SimplifiedRoleDiscovery] 检查角色目录: ${roleName}`)
try {
const roleDir = path.join(basePath, roleName)
const roleFile = path.join(roleDir, `${roleName}.role.md`)
logger.debug(`[SimplifiedRoleDiscovery] 角色文件路径: ${roleFile}`)
// 核心检查:主角色文件必须存在
const fileExists = await fs.pathExists(roleFile)
logger.debug(`[SimplifiedRoleDiscovery] 角色文件${roleName}是否存在: ${fileExists}`)
if (!fileExists) {
logger.debug(`[SimplifiedRoleDiscovery] 角色${roleName}文件不存在,跳过`)
return null
}
// 简化验证只检查基础DPML标签
logger.debug(`[SimplifiedRoleDiscovery] 读取角色文件内容: ${roleName}`)
const content = await fs.readFile(roleFile, 'utf8')
const isValid = this.isValidRoleFile(content)
logger.debug(`[SimplifiedRoleDiscovery] 角色${roleName}内容验证: ${isValid}`)
if (!isValid) {
logger.debug(`[SimplifiedRoleDiscovery] 角色${roleName}内容格式无效,跳过`)
return null
}
// 返回角色信息(简化元数据)
const roleInfo = {
file: roleFile,
name: this.extractRoleName(content) || roleName,
description: this.extractDescription(content) || `${roleName}专业角色`,
source: 'user-generated'
}
logger.debug(`[SimplifiedRoleDiscovery] 角色${roleName}检查成功:`, roleInfo.name)
return roleInfo
} catch (error) {
// 单个角色失败不影响其他角色
logger.warn(`[SimplifiedRoleDiscovery] 角色${roleName}检查失败: ${error.message}`)
logger.debug(`[SimplifiedRoleDiscovery] 错误堆栈:`, error.stack)
return null
}
}
/**
* 简化的DPML验证只检查关键标签
* @param {string} content - 文件内容
* @returns {boolean} 是否为有效角色文件
*/
isValidRoleFile(content) {
if (!content || typeof content !== 'string') {
return false
}
const trimmedContent = content.trim()
if (trimmedContent.length === 0) {
return false
}
return trimmedContent.includes('<role>') && trimmedContent.includes('</role>')
}
/**
* 简化的角色名称提取
* @param {string} content - 文件内容
* @returns {string|null} 提取的角色名称
*/
extractRoleName(content) {
if (!content) return null
// 提取Markdown标题
const match = content.match(/^#\s*(.+)$/m)
return match ? match[1].trim() : null
}
/**
* 简化的描述提取
* @param {string} content - 文件内容
* @returns {string|null} 提取的描述
*/
extractDescription(content) {
if (!content) return null
// 提取Markdown引用描述
const match = content.match(/^>\s*(.+)$/m)
return match ? match[1].trim() : null
}
/**
* 合并角色(用户优先)
* @param {Object} systemRoles - 系统角色
* @param {Object} userRoles - 用户角色
* @returns {Object} 合并后的角色注册表
*/
mergeRoles(systemRoles, userRoles) {
if (!systemRoles || typeof systemRoles !== 'object') {
systemRoles = {}
}
if (!userRoles || typeof userRoles !== 'object') {
userRoles = {}
}
return {
...systemRoles, // 系统角色作为基础
...userRoles // 用户角色覆盖同名系统角色
}
}
/**
* 获取用户角色路径
* @returns {Promise<string>} 用户角色目录路径
*/
async getUserRolePath() {
const projectRoot = await this.findProjectRoot()
return path.join(projectRoot, this.USER_RESOURCE_DIR, ...this.RESOURCE_DOMAIN_PATH)
}
/**
* 简化的项目根目录查找
* @returns {Promise<string>} 项目根目录路径
*/
async findProjectRoot() {
let currentDir = process.cwd()
// 向上查找包含package.json的目录
while (currentDir !== path.dirname(currentDir)) {
const packageJsonPath = path.join(currentDir, 'package.json')
try {
if (await fs.pathExists(packageJsonPath)) {
return currentDir
}
} catch (error) {
// 忽略权限错误,继续向上查找
}
currentDir = path.dirname(currentDir)
}
// 如果没找到package.json返回当前工作目录
return process.cwd()
}
}
module.exports = SimplifiedRoleDiscovery