优化角色注册,发现,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

@ -0,0 +1,329 @@
# DPML语义渲染升级方案
## 📋 文档信息
- **版本**: v1.0
- **创建时间**: 2025-06-11
- **作者**: Claude Code & Sean
- **优先级**: High
- **类型**: 架构升级
## 🎯 核心理念
**@引用 = 语义占位符**
DPML标签中的@引用不应该被视为"独立的资源",而应该被理解为"语义占位符",在渲染时在原始位置插入引用内容,保持标签的完整语义流程。
## 🔍 问题分析
### 当前实现的语义割裂
```xml
<personality>
@!thought://remember
# 网络杠精思维模式
## 核心思维特征
- 挑刺思维:看到任何观点都先找问题和漏洞
- 抬杠本能:天生反对派,习惯性质疑一切表述
@!thought://recall
## 认知偏好模式
- 逆向思考优先:遇到任何论点先想如何反驳
- 细节放大镜效应:善于将小问题放大成大问题
</personality>
```
**当前渲染结果(割裂的)**
```
## ✅ 🧠 思维模式remember
[remember的内容 - 100行]
---
## ✅ 🧠 思维模式recall
[recall的内容 - 80行]
---
## ✅ 🧠 思维模式internet-debater-personality
# 网络杠精思维模式
## 核心思维特征
- 挑刺思维:看到任何观点都先找问题和漏洞
- 抬杠本能:天生反对派,习惯性质疑一切表述
## 认知偏好模式
- 逆向思考优先:遇到任何论点先想如何反驳
- 细节放大镜效应:善于将小问题放大成大问题
---
```
**问题**
1. **语义割裂**完整的personality被分割成3个独立片段
2. **位置语义丢失**@引用的位置信息完全丢失
3. **阅读体验差**:用户无法获得连贯的角色理解
4. **违背用户意图**:用户精心设计的内容组织被破坏
## 💡 升级方案:语义占位符渲染
### 核心概念
**@引用 = 占位符**:在标签的原始位置插入引用内容,保持完整的语义流程。
### 理想渲染结果(完整的)
```
## ✅ 🧠 完整思维模式internet-debater
[remember的完整内容 - 100行记忆相关思维模式]
# 网络杠精思维模式
## 核心思维特征
- 挑刺思维:看到任何观点都先找问题和漏洞
- 抬杠本能:天生反对派,习惯性质疑一切表述
[recall的完整内容 - 80行回忆相关思维模式]
## 认知偏好模式
- 逆向思考优先:遇到任何论点先想如何反驳
- 细节放大镜效应:善于将小问题放大成大问题
---
```
**优势**
1. **语义完整性**用户看到完整连贯的personality
2. **位置语义保持**:内容按用户设计的顺序自然流淌
3. **阅读体验优化**:连贯的角色理解体验
4. **用户意图忠实**:完全按照用户的内容组织呈现
## 🔧 技术实现设计
### 1. 核心渲染算法
```javascript
class SemanticRenderer {
/**
* 语义占位符渲染:将@引用替换为实际内容
* @param {Object} tagSemantics - 标签语义结构
* @param {ResourceManager} resourceManager - 资源管理器
* @returns {string} 完整融合的语义内容
*/
async renderSemanticContent(tagSemantics, resourceManager) {
let content = tagSemantics.fullSemantics // 保持原始标签内容结构
// 按出现顺序处理每个@引用(保持位置语义)
for (const ref of tagSemantics.references) {
try {
// 解析引用内容
const refContent = await resourceManager.resolveReference(ref)
// 在原始位置替换@引用为实际内容
content = content.replace(ref.fullMatch, refContent)
} catch (error) {
// 引用解析失败时的优雅降级
content = content.replace(ref.fullMatch, `<!-- 引用解析失败: ${ref.fullMatch} -->`)
}
}
return content.trim()
}
}
```
### 2. DPMLContentParser扩展
```javascript
class DPMLContentParser {
// 现有方法保持不变...
/**
* 新增:获取引用的位置信息
*/
extractReferencesWithPosition(content) {
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) // 按位置排序
}
}
```
### 3. ActionCommand升级
```javascript
class ActionCommand {
/**
* 升级:生成语义完整的学习计划
*/
async generateSemanticLearningPlan(roleId, dependencies) {
const { roleSemantics } = dependencies
const renderer = new SemanticRenderer()
let content = `🎭 **角色激活完成:${roleId}** - 语义完整加载\n`
// 渲染完整的personality语义
if (roleSemantics.personality) {
content += `# 🧠 完整思维模式:${roleId}\n`
const personalityContent = await renderer.renderSemanticContent(
roleSemantics.personality,
this.resourceManager
)
content += `${personalityContent}\n---\n`
}
// 渲染完整的principle语义
if (roleSemantics.principle) {
content += `# ⚡ 完整执行模式:${roleId}\n`
const principleContent = await renderer.renderSemanticContent(
roleSemantics.principle,
this.resourceManager
)
content += `${principleContent}\n---\n`
}
// 渲染完整的knowledge语义
if (roleSemantics.knowledge) {
content += `# 📚 完整知识体系:${roleId}\n`
const knowledgeContent = await renderer.renderSemanticContent(
roleSemantics.knowledge,
this.resourceManager
)
content += `${knowledgeContent}\n---\n`
}
return content
}
}
```
## 📊 语义保障对比
### 升级前:语义割裂
```
独立片段1: remember内容 (100行)
独立片段2: recall内容 (80行)
独立片段3: 杠精思维 (50行)
```
**问题**: 用户无法理解这些片段的组织关系
### 升级后:语义完整
```
完整personality: remember内容 + 杠精思维 + recall内容 (230行)
```
**优势**: 用户获得完整连贯的角色理解
## 🎯 用户体验提升
### 1. **内容组织忠实性**
用户精心设计的内容组织得到完全保持:
- 引言 → @引用基础能力 → 核心特征 → @引用扩展能力 → 总结
### 2. **阅读连贯性**
从分离的技术片段转变为连贯的角色叙述:
- ❌ "这个角色有3个独立的思维片段"
- ✅ "这个角色具有完整连贯的思维体系"
### 3. **角色理解深度**
用户能够理解角色的完整图景,而不是零散的技能点。
## 🔧 实现阶段
### 阶段1SemanticRenderer实现
- 创建语义渲染器核心类
- 实现占位符替换算法
- 支持引用解析失败的优雅降级
### 阶段2DPMLContentParser扩展
- 添加位置信息提取
- 增强引用解析能力
- 保持向下兼容性
### 阶段3ActionCommand集成
- 更新学习计划生成逻辑
- 集成语义渲染器
- 全面测试各种角色类型
### 阶段4全系统推广
- 扩展到LearnCommand
- 更新所有Protocol类
- 建立统一的语义渲染标准
## 📝 测试策略
### 1. 语义完整性测试
```javascript
test('应该保持@引用的位置语义', async () => {
const content = `intro @!thought://A middle @!thought://B end`
const rendered = await renderer.renderSemanticContent(semantics, rm)
expect(rendered).toBe(`intro [A的内容] middle [B的内容] end`)
})
```
### 2. 复杂布局测试
```javascript
test('应该处理复杂的@引用布局', async () => {
const content = `
# 标题
@!thought://base
## 子标题
- 列表项1
@!execution://action
- 列表项2
`
// 验证内容在正确位置插入
})
```
### 3. 错误处理测试
```javascript
test('应该优雅处理引用解析失败', async () => {
// 验证失败时的降级策略
})
```
## 💡 长期价值
### 1. **DPML协议语义标准**
这个升级建立了DPML协议中@引用的语义标准
- @引用 = 占位符,不是独立资源
- 位置语义优于类型语义
- 用户意图优于技术实现
### 2. **可扩展的语义框架**
为未来的DPML扩展奠定基础
- 支持更复杂的引用类型
- 支持条件渲染
- 支持嵌套引用
### 3. **用户体验范式**
从"技术驱动"转向"用户意图驱动"的设计范式。
## 🔗 相关文档
- [DPML基础协议](./dpml.protocol.md)
- [角色内容解析问题](./issues/role-content-parsing-incomplete.md)
- [ActionCommand架构设计](./action-command-architecture.md)
## ⚠️ 注意事项
1. **性能考虑**: 语义渲染涉及异步资源解析,需要考虑缓存策略
2. **错误处理**: 引用解析失败时的用户体验设计
3. **兼容性**: 确保现有角色的正常工作
4. **文档更新**: 用户需要了解新的语义渲染效果
---
**这个升级将彻底解决DPML语义完整性问题为PromptX建立真正以用户意图为中心的语义框架。**

View File

@ -0,0 +1,235 @@
# 角色内容解析不完整问题
## 🔥 问题等级
**高优先级 (High Priority)**
## 📋 问题描述
当前PromptX的角色解析机制存在重大缺陷**只解析@引用完全忽略DPML标签内的直接内容**。这导致许多用户自定义角色的核心能力丢失。
## 🔍 问题详情
### 当前解析机制
ActionCommand在`analyzeRoleDependencies`方法中使用以下正则表达式:
```javascript
const resourceRegex = /@([!?]?)([a-zA-Z][a-zA-Z0-9_-]*):\/\/([a-zA-Z0-9_\/.,-]+?)(?=[\s\)\],]|$)/g
```
这个正则**只提取@引用**完全忽略DPML标签内直接编写的内容。
### 影响的角色类型
#### 1. 系统角色(使用@引用)
```xml
<!-- prompt/domain/assistant/assistant.role.md -->
<role>
<personality>
@!thought://remember
@!thought://recall
@!thought://assistant
</personality>
<principle>
@!execution://assistant
</principle>
</role>
```
**状态**:✅ 正常解析
#### 2. 用户角色(直接内容)
```xml
<!-- .promptx/resource/domain/internet-debater/internet-debater.role.md -->
<role>
<personality>
# 网络杠精思维模式
## 核心思维特征
- **挑刺思维**:看到任何观点都先找问题和漏洞
- **抬杠本能**:天生反对派,习惯性质疑一切表述
...
</personality>
<principle>
# 网络杠精行为原则
## 核心抬杠准则
- **逢言必反**:对任何观点都要找到反对的角度
...
</principle>
<knowledge>
# 网络杠精专业知识体系
## 逻辑谬误大全
- **稻草人谬误**:故意曲解对方观点
...
</knowledge>
</role>
```
**状态**:❌ **内容完全丢失**
## 🎯 具体影响
### internet-debater角色损失分析
- **丢失内容**3个完整的DPML节段约100行专业内容
- **丢失能力**
- 杠精思维模式和认知特征
- 抬杠行为原则和语言风格
- 逻辑谬误知识和争论技巧
- **用户体验**:激活角色后获得的是"空壳",无法发挥预期作用
### 潜在影响范围
- 所有直接编写内容的用户自定义角色
- 混合编写方式的角色(@引用 + 直接内容)
- 未来可能创建的角色
## 🔍 根因分析
### 1. 设计假设错误
系统设计时假设所有角色都使用@引用方式,但实际上:
- 用户更倾向于直接编写内容
- 文档没有强制要求使用@引用
- 角色创建工具支持直接编写
### 2. 验证与解析分离
```javascript
// resourceManager.js - 只验证格式,不解析内容
validateDPMLFormat(content, type) {
const tags = DPML_TAGS[type]
return content.includes(tags.start) && content.includes(tags.end)
}
```
### 3. 解析逻辑单一
ActionCommand只有一种解析模式正则匹配@引用,没有考虑直接内容。
## 💡 解决方案
### 方案1混合解析机制推荐
扩展ActionCommand解析逻辑同时支持
1. **@引用解析**:保持现有逻辑
2. **直接内容解析**提取DPML标签内的Markdown内容
3. **内容合并**@引用内容 + 直接内容 = 完整角色能力
### 方案2内容提取器
创建专门的DPML内容提取器
```javascript
class DPMLContentExtractor {
extractTagContent(content, tagName) {
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, 'g')
// 提取标签内容,同时处理@引用和直接内容
}
}
```
### 方案3解析策略统一
统一角色解析策略:
1. 优先解析@引用
2. 补充解析直接内容
3. 合并生成完整的角色Profile
## 🔧 技术实现要点
### 1. 扩展依赖分析
```javascript
// ActionCommand.js
async analyzeRoleDependencies(roleInfo) {
const roleContent = await fs.readFile(filePath, 'utf-8')
// 现有:提取@引用
const references = this.extractReferences(roleContent)
// 新增:提取直接内容
const directContent = this.extractDirectContent(roleContent)
// 合并依赖
return this.mergeDependencies(references, directContent)
}
```
### 2. 内容提取算法
```javascript
extractDirectContent(content) {
const sections = {}
const tags = ['personality', 'principle', 'knowledge']
tags.forEach(tag => {
const regex = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`, 'g')
const match = regex.exec(content)
if (match && match[1].trim()) {
// 过滤掉@引用,只保留直接内容
const directText = this.filterOutReferences(match[1])
if (directText.trim()) {
sections[tag] = directText
}
}
})
return sections
}
```
## 📊 影响评估
### 修复收益
- **功能完整性**:用户自定义角色能力完全恢复
- **用户体验**:角色激活后获得预期的专业能力
- **兼容性**:支持所有编写方式,向下兼容
### 修复成本
- **开发工作量**中等主要在ActionCommand和相关解析逻辑
- **测试工作量**:中等(需要测试各种角色格式)
- **风险评估**:低(主要是增强功能,不破坏现有逻辑)
## 🧪 测试用例
### 测试场景1纯@引用角色
```xml
<role>
<personality>@!thought://assistant</personality>
<principle>@!execution://assistant</principle>
</role>
```
**期望**:正常解析@引用
### 测试场景2纯直接内容角色
```xml
<role>
<personality># 直接编写的个性内容</personality>
<principle># 直接编写的原则内容</principle>
</role>
```
**期望**:正确提取直接内容
### 测试场景3混合方式角色
```xml
<role>
<personality>
@!thought://base-personality
# 补充的个性特征
- 额外特征1
- 额外特征2
</personality>
</role>
```
**期望**@引用 + 直接内容都被解析
## 📅 建议修复时间线
- **阶段1**:问题确认和方案设计(已完成)
- **阶段2**核心解析逻辑实现1-2天
- **阶段3**测试用例编写和验证1天
- **阶段4**兼容性测试和文档更新1天
## 🔗 相关文件
- `src/lib/core/pouch/commands/ActionCommand.js` - 主要修改文件
- `src/lib/core/resource/resourceManager.js` - 可能需要增强DPML处理
- `.promptx/resource/domain/internet-debater/internet-debater.role.md` - 受影响的测试案例
- `src/tests/commands/ActionCommand.*.test.js` - 需要补充的测试
## ⚠️ 注意事项
1. **保持向下兼容**:现有@引用方式不能受影响
2. **性能考虑**:内容解析不应显著影响角色激活速度
3. **内容去重**:避免@引用和直接内容的重复
4. **错误处理**DPML格式错误时的优雅降级
---
**报告人**Claude Code
**发现时间**2025-06-11
**优先级**High
**标签**bug, role-parsing, user-experience, content-loss

View File

@ -0,0 +1,203 @@
# 角色发现一致性问题
## 🔥 问题等级
**中等优先级 (Medium Priority)**
## 📋 问题描述
用户自定义角色在创建后出现间歇性的"角色不存在"错误表现为hello命令能发现角色但action命令无法激活同一角色。重启MCP Server后问题消失。
## 🔍 问题详情
### 问题表现
1. **创建新角色后** - `internet-empathy-master`角色文件已正确创建在`.promptx/resource/domain/`
2. **hello命令正常** - `promptx_hello`能正确发现并显示新角色
3. **action命令失败** - `promptx_action internet-empathy-master`提示"角色不存在"
4. **重启后恢复** - 重启MCP Server后action命令立即可用
### 错误信息
```
❌ 角色 "internet-empathy-master" 不存在!
🔍 请使用以下命令查看可用角色:
```bash
pnpm start hello
```
### 用户反馈
> "我刚刚配置了一下,然后重启了 mcp 就可以了"
## 🔍 技术分析
### 当前架构分析
**MCP Server启动流程**
1. MCP Server启动时**不执行角色发现**(延迟初始化设计)
2. 只有调用`promptx_hello`时才触发`SimplifiedRoleDiscovery.discoverAllRoles()`
3. HelloCommand使用实例级缓存`this.roleRegistry`避免重复扫描
4. ActionCommand通过懒加载HelloCommand实例复用缓存
**角色发现流程:**
```javascript
// HelloCommand.loadRoleRegistry()
if (this.roleRegistry) {
return this.roleRegistry // 实例级缓存
}
const allRoles = await this.discovery.discoverAllRoles()
this.roleRegistry = {} // 缓存结果
```
**ActionCommand角色查询**
```javascript
// ActionCommand.getRoleInfo()
if (!this.helloCommand) {
this.helloCommand = new HelloCommand() // 懒加载新实例
}
return await this.helloCommand.getRoleInfo(roleId)
```
### 问题假设分析
#### 假设1实例级缓存不一致 ❌
- **假设**不同HelloCommand实例缓存状态不同
- **反驳**ActionCommand懒加载HelloCommand应该使用相同的发现逻辑
#### 假设2SimplifiedRoleDiscovery不稳定 ❓
- **假设**`Promise.allSettled`并行文件操作存在竞态条件
- **可能性**文件系统I/O操作的时序不确定性
#### 假设3MCP Server状态管理问题 ❓
- **假设**MCP Server在处理多个请求时状态混乱
- **可能性**不同MCP工具调用之间存在状态污染
## 🧪 问题复现
### 复现步骤
1. 启动MCP Server
2. 创建新的用户角色文件(如`test-role.role.md`
3. 立即调用`promptx_hello` - 预期能看到新角色
4. 立即调用`promptx_action test-role` - 可能失败
5. 重启MCP Server
6. 再次调用`promptx_action test-role` - 预期成功
### 复现条件
- 角色文件在MCP Server运行期间创建
- 立即尝试激活新创建的角色
- 系统macOS (用户报告环境)
## 🔍 调试数据需求
### 需要收集的日志
使用`DEBUG=1`环境变量启用调试日志:
1. **HelloCommand调用日志**
```
[HelloCommand] getRoleInfo调用角色ID: internet-empathy-master
[HelloCommand] 注册表加载完成,包含角色: [...]
[HelloCommand] 查找角色internet-empathy-master结果: 找到/未找到
```
2. **SimplifiedRoleDiscovery日志**
```
[SimplifiedRoleDiscovery] 开始发现所有角色...
[SimplifiedRoleDiscovery] 用户角色扫描完成,发现角色: [...]
[SimplifiedRoleDiscovery] 检查角色目录: internet-empathy-master
[SimplifiedRoleDiscovery] 角色文件是否存在: true/false
```
3. **ActionCommand调用日志**
```
[ActionCommand] 开始激活角色: internet-empathy-master
[ActionCommand] 创建新的HelloCommand实例 / 复用现有HelloCommand实例
[ActionCommand] getRoleInfo结果: {...} / null
```
## 💡 可能解决方案
### 方案1添加缓存失效机制
```javascript
class HelloCommand {
invalidateCache() {
this.roleRegistry = null
}
}
```
### 方案2实时角色发现
移除HelloCommand的实例级缓存每次都重新扫描
```javascript
async getRoleInfo(roleId) {
// 移除缓存检查,直接执行发现
const registry = await this.loadRoleRegistry(true) // 强制重新加载
}
```
### 方案3文件系统监听
监听`.promptx/resource/domain`目录变化,自动刷新缓存:
```javascript
const chokidar = require('chokidar')
const watcher = chokidar.watch('.promptx/resource/domain')
watcher.on('add', () => this.invalidateCache())
```
### 方案4全局角色注册表
使用单例模式管理角色注册表,确保所有实例共享状态:
```javascript
class GlobalRoleRegistry {
static instance = null
static getInstance() {
if (!this.instance) {
this.instance = new GlobalRoleRegistry()
}
return this.instance
}
}
```
## 📊 影响评估
### 影响范围
- **用户体验**新角色创建后无法立即使用需要重启MCP Server
- **开发效率**:角色开发和测试流程被中断
- **系统可靠性**:间歇性错误难以预测和重现
### 影响程度
- **频率**新角色创建时100%触发
- **严重性**中等有解决方案重启MCP Server
- **用户反馈**:已有用户报告此问题
## 🔧 技术实现建议
### 推荐方案方案1 + 方案2组合
1. **短期**移除HelloCommand缓存改为每次实时发现方案2
2. **长期**实现智能缓存失效机制方案1
### 实现优先级
1. **高优先级**:添加详细调试日志,收集实际出错的调试数据
2. **中优先级**:实现缓存失效或实时发现机制
3. **低优先级**:文件系统监听(增加系统复杂性)
## 📅 建议时间线
- **阶段1**问题确认和调试数据收集1天
- **阶段2**实现临时解决方案移除缓存1天
- **阶段3**设计长期解决方案智能缓存2-3天
- **阶段4**测试和验证1天
## 🔗 相关文件
- `src/lib/core/pouch/commands/HelloCommand.js` - 角色注册表缓存逻辑
- `src/lib/core/pouch/commands/ActionCommand.js` - 角色信息获取逻辑
- `src/lib/core/resource/SimplifiedRoleDiscovery.js` - 角色发现算法
- `docs/issues/role-content-parsing-incomplete.md` - 相关的角色解析问题
## ⚠️ 注意事项
1. **性能平衡**:移除缓存可能影响性能,需要测试文件系统操作耗时
2. **并发安全**多个MCP请求并发时的角色发现一致性
3. **错误处理**:文件系统操作失败时的优雅降级
4. **跨平台兼容**:确保解决方案在不同操作系统上稳定工作
---
**报告人**Claude Code
**发现时间**2025-06-11
**优先级**Medium
**标签**role-discovery, mcp-server, caching, user-experience, file-system

View 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的角色发现机制将更加简洁、高效、可靠为用户提供更好的跨平台体验。