- 重命名核心目录: /prompt/ → /resource/ - 更新PackageDiscovery中所有硬编码路径引用 - 重新生成package.registry.json,61个资源全部更新为@package://resource/路径 - 批量更新文档中的路径引用,保持一致性 - 目录结构保持不变:domain/, core/, protocol/, tool/子目录结构完全一致 重构原因: 随着tool协议的加入,prompt目录名称不再准确描述系统本质 重构价值: 为未来资源生态扩展奠定清晰的命名基础 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
12 KiB
角色发现机制优化设计
📋 概述
当前PromptX的角色发现机制存在过度复杂的扫描逻辑,导致跨平台兼容性问题和性能瓶颈。本文档分析现状问题,并提出系统性的优化方案。
🚨 当前问题分析
问题1: 双重角色发现机制
现状:
ResourceManager.loadUnifiedRegistry()- 统一资源管理HelloCommand.discoverLocalRoles()- 独立的本地角色发现
问题:
- 逻辑重复,维护成本高
- 数据格式转换复杂
- 容易产生不一致的结果
问题2: glob库跨平台兼容性风险
现状代码:
// HelloCommand.js:254
const rolePattern = path.join(domainPath, '*', '*.role.md')
const roleFiles = glob.sync(rolePattern)
风险点:
- Windows路径分隔符处理不一致
- glob模式匹配在不同平台行为差异
- 同步操作阻塞主线程
- 外部依赖增加包大小和安全风险
问题3: 过度复杂的文件系统扫描
扫描流程:
ResourceManager.discoverUserResources()
↓
scanResourceDirectory() - 扫描基础目录
↓
scanRoleResources() - 扫描角色文件
↓
scanOtherResources() - 扫描thought/execution
↓
validateDPMLFormat() - DPML格式验证
↓
extractRoleName() - 元数据提取
复杂性问题:
- 4层嵌套的异步操作
- 每个目录多次
fs.stat()和fs.pathExists()调用 - 错误处理不一致(有些抛异常,有些仅警告)
- 无缓存机制,重复I/O操作
问题4: DPML验证过于简化
当前验证:
validateDPMLFormat(content, type) {
const tags = DPML_TAGS[type]
return content.includes(tags.start) && content.includes(tags.end)
}
局限性:
- 只检查标签存在,不验证格式正确性
- 无结构验证和嵌套检查
- 验证失败时无详细错误信息
- 无法处理标签损坏的情况
问题5: PackageProtocol检测过度复杂
现状:
_performInstallModeDetection() {
// 5种检测模式,每次都执行
_isNpxExecution()
_isGlobalInstall()
_isDevelopmentMode()
_isMonorepoWorkspace()
_isNpmLink()
}
开销问题:
- 每次调用都重新检测环境
- 文件系统操作频繁
- 逻辑分支复杂,维护困难
🎯 优化方案设计
方案1: 统一角色发现架构(推荐)
1.1 移除双重机制
// 移除HelloCommand.discoverLocalRoles()
// 完全依赖ResourceManager统一管理
class HelloCommand {
async loadRoleRegistry() {
// 仅调用ResourceManager,无独立扫描逻辑
const resourceManager = new ResourceManager()
const unifiedRegistry = await resourceManager.loadUnifiedRegistry()
return unifiedRegistry.role || {}
}
}
1.2 简化ResourceManager
class ResourceManager {
async loadUnifiedRegistry() {
// 并行加载,提升性能
const [systemRegistry, userRoles] = await Promise.all([
this.loadSystemRegistry(),
this.discoverUserRolesSimple()
])
return this.mergeRegistries(systemRegistry, userRoles)
}
async discoverUserRolesSimple() {
// 最小化用户资源发现逻辑
const userPath = path.join(await this.getPackageRoot(), USER_RESOURCE_DIR, ...RESOURCE_DOMAIN_PATH)
if (!await fs.pathExists(userPath)) {
return { role: {} }
}
return await this.scanUserRolesOptimized(userPath)
}
}
方案2: 原生API替代glob
2.1 使用Node.js原生fs API
async function discoverRolesNative(domainPath) {
const roles = {}
try {
// 使用withFileTypes提升性能
const entries = await fs.readdir(domainPath, { withFileTypes: true })
for (const entry of entries) {
if (entry.isDirectory()) {
const roleFile = path.join(domainPath, entry.name, `${entry.name}.role.md`)
// 单次检查文件存在性
if (await fs.pathExists(roleFile)) {
roles[entry.name] = {
file: roleFile,
name: entry.name,
source: 'user-generated'
}
}
}
}
} catch (error) {
// 统一错误处理
logger.warn(`角色发现失败 ${domainPath}: ${error.message}`)
return {}
}
return roles
}
2.2 跨平台路径处理最佳实践
class PathUtils {
static normalizeRolePath(roleName) {
// 确保跨平台路径兼容性
return path.join('.promptx', 'resource', 'domain', roleName, `${roleName}.role.md`)
}
static async safeReadDir(dirPath) {
try {
return await fs.readdir(dirPath, { withFileTypes: true })
} catch (error) {
// 处理权限问题
if (error.code === 'EACCES' || error.code === 'EPERM') {
logger.warn(`权限不足,跳过目录: ${dirPath}`)
return []
}
throw error
}
}
}
方案3: 增强DPML验证器
3.1 结构化验证
class DPMLValidator {
static validate(content, type) {
const result = {
isValid: false,
errors: [],
metadata: {},
structure: null
}
try {
// 1. 基础标签检查
if (!this.hasValidTags(content, type)) {
result.errors.push(`缺少${type}标签`)
return result
}
// 2. 结构验证
const structure = this.parseStructure(content, type)
if (!structure) {
result.errors.push('标签结构无效')
return result
}
// 3. 内容验证
const metadata = this.extractMetadata(content, type)
result.isValid = true
result.metadata = metadata
result.structure = structure
} catch (error) {
result.errors.push(`验证失败: ${error.message}`)
}
return result
}
static parseStructure(content, type) {
// 解析XML结构,验证嵌套正确性
const regex = new RegExp(`<${type}>(.*?)</${type}>`, 's')
const match = content.match(regex)
return match ? match[1].trim() : null
}
static extractMetadata(content, type) {
// 提取角色元数据
const metadata = {}
// 提取标题
const titleMatch = content.match(/^#\s+(.+)$/m)
if (titleMatch) {
metadata.title = titleMatch[1].trim()
}
// 提取描述
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
if (descMatch) {
metadata.description = descMatch[1].trim()
}
return metadata
}
}
方案4: 缓存机制
4.1 文件扫描缓存
class RoleDiscoveryCache {
constructor() {
this.cache = new Map()
this.timestamps = new Map()
this.ttl = 5 * 60 * 1000 // 5分钟缓存
}
async getOrScan(key, scanFn) {
const now = Date.now()
// 检查缓存是否有效
if (this.cache.has(key)) {
const timestamp = this.timestamps.get(key)
if (now - timestamp < this.ttl) {
return this.cache.get(key)
}
}
// 执行扫描并缓存结果
const result = await scanFn()
this.cache.set(key, result)
this.timestamps.set(key, now)
return result
}
invalidate(key) {
this.cache.delete(key)
this.timestamps.delete(key)
}
}
4.2 智能缓存失效
class SmartCache extends RoleDiscoveryCache {
async watchDirectory(dirPath) {
// 监听目录变化,智能失效缓存
const watcher = fs.watch(dirPath, (eventType, filename) => {
if (filename && filename.endsWith('.role.md')) {
this.invalidate(dirPath)
logger.debug(`角色文件变化,失效缓存: ${filename}`)
}
})
return watcher
}
}
方案5: 简化PackageProtocol
5.1 基础环境检测
class SimplePackageProtocol {
constructor() {
this.mode = this.detectMode()
this.packageRoot = null
}
detectMode() {
// 简化为3种基本模式
if (process.env.PROMPTX_ENV === 'development') {
return 'development'
}
if (process.argv[1]?.includes('npx')) {
return 'npx'
}
return 'installed'
}
async getPackageRoot() {
if (this.packageRoot) {
return this.packageRoot
}
switch (this.mode) {
case 'development':
this.packageRoot = process.cwd()
break
case 'npx':
this.packageRoot = await this.findNpxRoot()
break
default:
this.packageRoot = await this.findInstalledRoot()
}
return this.packageRoot
}
}
🚀 实施计划
Phase 1: 移除glob依赖(立即实施)
优先级: 🔥 紧急 影响: 解决跨平台兼容性问题
具体步骤:
- ✅ 替换
HelloCommand.discoverLocalRoles()中的glob调用 - ✅ 使用
fs.readdir()和path.join()替代 - ✅ 添加跨平台路径处理
Phase 2: 统一角色发现架构(本周)
优先级: 🔥 高 影响: 简化维护,提升性能
具体步骤:
- ✅ 移除
HelloCommand.discoverLocalRoles()方法 - ✅ 简化
ResourceManager.scanResourceDirectory()逻辑 - ✅ 统一错误处理机制
Phase 3: 增强验证和缓存(下周)
优先级: 🔧 中 影响: 提升可靠性和性能
具体步骤:
- ✅ 实现
DPMLValidator结构化验证 - ✅ 添加
RoleDiscoveryCache缓存机制 - ✅ 优化PackageProtocol检测逻辑
Phase 4: 性能监控和测试(持续)
优先级: 📊 中 影响: 确保优化效果
具体步骤:
- ✅ 添加角色发现性能指标
- ✅ 完善跨平台测试用例
- ✅ 建立性能回归测试
📊 预期收益
性能提升
- 文件扫描速度: 提升60%(移除glob,减少I/O)
- 初始化时间: 减少40%(缓存机制)
- 内存使用: 降低30%(移除重复数据结构)
兼容性改善
- Windows兼容性: 100%(原生API)
- 权限处理: 增强错误恢复
- 路径处理: 统一跨平台标准
维护性提升
- 代码复杂度: 降低50%(移除双重机制)
- 测试覆盖: 提升到95%
- Bug减少: 预计减少70%的跨平台问题
🔧 配置迁移指南
用户无感知迁移
优化后的角色发现机制对用户完全透明,无需修改现有配置:
现有用户资源结构(保持不变):
.promptx/
resource/
domain/
my-role/
my-role.role.md
thought/
my-role.thought.md
execution/
my-role.execution.md
系统资源注册(保持不变):
// resource.registry.json
{
"role": {
"assistant": {
"file": "@package://resource/domain/assistant/assistant.role.md",
"name": "🙋 智能助手"
}
}
}
开发者API保持兼容
// 现有API保持不变
const helloCommand = new HelloCommand()
const roles = await helloCommand.getAllRoles()
const roleInfo = await helloCommand.getRoleInfo('assistant')
🧪 测试策略
跨平台兼容性测试
// 新增测试用例
describe('角色发现跨平台兼容性', () => {
test('Windows路径处理', () => {
// 测试Windows特殊字符处理
})
test('Unix权限处理', () => {
// 测试Unix文件权限
})
test('符号链接处理', () => {
// 测试符号链接角色文件
})
})
性能基准测试
describe('角色发现性能', () => {
test('大量角色扫描性能', async () => {
// 创建100个测试角色
// 测试扫描时间<100ms
})
test('缓存命中率', async () => {
// 测试缓存有效性
})
})
📚 相关文档
总结: 通过系统性的优化,PromptX的角色发现机制将更加简洁、高效、可靠,为用户提供更好的跨平台体验。