优化角色注册,发现,nuwa 角色的提示词等
This commit is contained in:
495
docs/role-discovery-optimization.md
Normal file
495
docs/role-discovery-optimization.md
Normal file
@ -0,0 +1,495 @@
|
||||
# 角色发现机制优化设计
|
||||
|
||||
## 📋 概述
|
||||
|
||||
当前PromptX的角色发现机制存在过度复杂的扫描逻辑,导致跨平台兼容性问题和性能瓶颈。本文档分析现状问题,并提出系统性的优化方案。
|
||||
|
||||
## 🚨 当前问题分析
|
||||
|
||||
### 问题1: 双重角色发现机制
|
||||
**现状**:
|
||||
- `ResourceManager.loadUnifiedRegistry()` - 统一资源管理
|
||||
- `HelloCommand.discoverLocalRoles()` - 独立的本地角色发现
|
||||
|
||||
**问题**:
|
||||
- 逻辑重复,维护成本高
|
||||
- 数据格式转换复杂
|
||||
- 容易产生不一致的结果
|
||||
|
||||
### 问题2: glob库跨平台兼容性风险
|
||||
**现状代码**:
|
||||
```javascript
|
||||
// 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验证过于简化
|
||||
**当前验证**:
|
||||
```javascript
|
||||
validateDPMLFormat(content, type) {
|
||||
const tags = DPML_TAGS[type]
|
||||
return content.includes(tags.start) && content.includes(tags.end)
|
||||
}
|
||||
```
|
||||
|
||||
**局限性**:
|
||||
- 只检查标签存在,不验证格式正确性
|
||||
- 无结构验证和嵌套检查
|
||||
- 验证失败时无详细错误信息
|
||||
- 无法处理标签损坏的情况
|
||||
|
||||
### 问题5: PackageProtocol检测过度复杂
|
||||
**现状**:
|
||||
```javascript
|
||||
_performInstallModeDetection() {
|
||||
// 5种检测模式,每次都执行
|
||||
_isNpxExecution()
|
||||
_isGlobalInstall()
|
||||
_isDevelopmentMode()
|
||||
_isMonorepoWorkspace()
|
||||
_isNpmLink()
|
||||
}
|
||||
```
|
||||
|
||||
**开销问题**:
|
||||
- 每次调用都重新检测环境
|
||||
- 文件系统操作频繁
|
||||
- 逻辑分支复杂,维护困难
|
||||
|
||||
## 🎯 优化方案设计
|
||||
|
||||
### 方案1: 统一角色发现架构(推荐)
|
||||
|
||||
#### 1.1 移除双重机制
|
||||
```javascript
|
||||
// 移除HelloCommand.discoverLocalRoles()
|
||||
// 完全依赖ResourceManager统一管理
|
||||
|
||||
class HelloCommand {
|
||||
async loadRoleRegistry() {
|
||||
// 仅调用ResourceManager,无独立扫描逻辑
|
||||
const resourceManager = new ResourceManager()
|
||||
const unifiedRegistry = await resourceManager.loadUnifiedRegistry()
|
||||
return unifiedRegistry.role || {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 简化ResourceManager
|
||||
```javascript
|
||||
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
|
||||
```javascript
|
||||
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 跨平台路径处理最佳实践
|
||||
```javascript
|
||||
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 结构化验证
|
||||
```javascript
|
||||
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 文件扫描缓存
|
||||
```javascript
|
||||
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 智能缓存失效
|
||||
```javascript
|
||||
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 基础环境检测
|
||||
```javascript
|
||||
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依赖(立即实施)
|
||||
**优先级**: 🔥 紧急
|
||||
**影响**: 解决跨平台兼容性问题
|
||||
|
||||
**具体步骤**:
|
||||
1. ✅ 替换`HelloCommand.discoverLocalRoles()`中的glob调用
|
||||
2. ✅ 使用`fs.readdir()`和`path.join()`替代
|
||||
3. ✅ 添加跨平台路径处理
|
||||
|
||||
### Phase 2: 统一角色发现架构(本周)
|
||||
**优先级**: 🔥 高
|
||||
**影响**: 简化维护,提升性能
|
||||
|
||||
**具体步骤**:
|
||||
1. ✅ 移除`HelloCommand.discoverLocalRoles()`方法
|
||||
2. ✅ 简化`ResourceManager.scanResourceDirectory()`逻辑
|
||||
3. ✅ 统一错误处理机制
|
||||
|
||||
### Phase 3: 增强验证和缓存(下周)
|
||||
**优先级**: 🔧 中
|
||||
**影响**: 提升可靠性和性能
|
||||
|
||||
**具体步骤**:
|
||||
1. ✅ 实现`DPMLValidator`结构化验证
|
||||
2. ✅ 添加`RoleDiscoveryCache`缓存机制
|
||||
3. ✅ 优化PackageProtocol检测逻辑
|
||||
|
||||
### Phase 4: 性能监控和测试(持续)
|
||||
**优先级**: 📊 中
|
||||
**影响**: 确保优化效果
|
||||
|
||||
**具体步骤**:
|
||||
1. ✅ 添加角色发现性能指标
|
||||
2. ✅ 完善跨平台测试用例
|
||||
3. ✅ 建立性能回归测试
|
||||
|
||||
## 📊 预期收益
|
||||
|
||||
### 性能提升
|
||||
- **文件扫描速度**: 提升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
|
||||
```
|
||||
|
||||
**系统资源注册**(保持不变):
|
||||
```json
|
||||
// resource.registry.json
|
||||
{
|
||||
"role": {
|
||||
"assistant": {
|
||||
"file": "@package://prompt/domain/assistant/assistant.role.md",
|
||||
"name": "🙋 智能助手"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 开发者API保持兼容
|
||||
```javascript
|
||||
// 现有API保持不变
|
||||
const helloCommand = new HelloCommand()
|
||||
const roles = await helloCommand.getAllRoles()
|
||||
const roleInfo = await helloCommand.getRoleInfo('assistant')
|
||||
```
|
||||
|
||||
## 🧪 测试策略
|
||||
|
||||
### 跨平台兼容性测试
|
||||
```javascript
|
||||
// 新增测试用例
|
||||
describe('角色发现跨平台兼容性', () => {
|
||||
test('Windows路径处理', () => {
|
||||
// 测试Windows特殊字符处理
|
||||
})
|
||||
|
||||
test('Unix权限处理', () => {
|
||||
// 测试Unix文件权限
|
||||
})
|
||||
|
||||
test('符号链接处理', () => {
|
||||
// 测试符号链接角色文件
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 性能基准测试
|
||||
```javascript
|
||||
describe('角色发现性能', () => {
|
||||
test('大量角色扫描性能', async () => {
|
||||
// 创建100个测试角色
|
||||
// 测试扫描时间<100ms
|
||||
})
|
||||
|
||||
test('缓存命中率', async () => {
|
||||
// 测试缓存有效性
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [用户角色创建系统](./user-role-creation-system.md)
|
||||
- [DPML协议规范](../prompt/protocol/dpml.protocol.md)
|
||||
- [ResourceManager架构](../src/lib/core/resource/)
|
||||
- [跨平台测试指南](../src/tests/commands/CrossPlatformDiscovery.unit.test.js)
|
||||
|
||||
---
|
||||
|
||||
**总结**: 通过系统性的优化,PromptX的角色发现机制将更加简洁、高效、可靠,为用户提供更好的跨平台体验。
|
||||
Reference in New Issue
Block a user