小白龙 Bailongma - 初始提交
自主操作员与思考搭档系统。 包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
This commit is contained in:
178
orchestrator-v2/role-templates.js
Normal file
178
orchestrator-v2/role-templates.js
Normal file
@@ -0,0 +1,178 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const AGENCY_DIR = path.resolve('D:/q/Bailongma/agency-agents-zh');
|
||||
|
||||
class RoleTemplates {
|
||||
constructor() {
|
||||
this.roles = new Map(); // roleId -> role definition
|
||||
this.byCategory = new Map(); // category -> [roleId, ...]
|
||||
this.byKeyword = new Map(); // keyword -> [roleId, ...]
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (this.initialized) return;
|
||||
this._scanDirectory(AGENCY_DIR);
|
||||
this.initialized = true;
|
||||
console.log(`[RoleTemplates] Loaded ${this.roles.size} roles in ${this.byCategory.size} categories`);
|
||||
}
|
||||
|
||||
_scanDirectory(dir, category = null) {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
const subCategory = category ? `${category}/${entry.name}` : entry.name;
|
||||
if (!entry.name.startsWith('.')) {
|
||||
this._scanDirectory(fullPath, subCategory);
|
||||
}
|
||||
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'README.md') {
|
||||
// Skip non-role files
|
||||
const skipFiles = ['CATALOG.md', 'UPSTREAM.md', 'CONTRIBUTING.md', 'LICENSE',
|
||||
'AGENT-LIST.md', 'EXECUTIVE-BRIEF.md', 'QUICKSTART.md', 'nexus-strategy.md'];
|
||||
if (skipFiles.includes(entry.name)) continue;
|
||||
if (entry.name.startsWith('bug') || entry.name.startsWith('feature') || entry.name.startsWith('new_agent') || entry.name.startsWith('PULL_REQUEST')) continue;
|
||||
|
||||
this._parseRoleFile(fullPath, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parseRoleFile(filePath, category) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const roleId = path.basename(filePath, '.md');
|
||||
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
||||
|
||||
let name = roleId;
|
||||
let description = '';
|
||||
let emoji = '🤖';
|
||||
let color = 'gray';
|
||||
let body = content;
|
||||
|
||||
if (match) {
|
||||
const frontMatter = match[1];
|
||||
body = match[2].trim();
|
||||
const nameMatch = frontMatter.match(/^name:\s*(.+)$/m);
|
||||
if (nameMatch) name = nameMatch[1].trim();
|
||||
const descMatch = frontMatter.match(/^description:\s*(.+)$/m);
|
||||
if (descMatch) description = descMatch[1].trim();
|
||||
const emojiMatch = frontMatter.match(/^emoji:\s*(.+)$/m);
|
||||
if (emojiMatch) emoji = emojiMatch[1].trim();
|
||||
const colorMatch = frontMatter.match(/^color:\s*(.+)$/m);
|
||||
if (colorMatch) color = colorMatch[1].trim();
|
||||
}
|
||||
|
||||
const role = {
|
||||
id: roleId,
|
||||
name,
|
||||
description,
|
||||
emoji,
|
||||
color,
|
||||
category: category || 'uncategorized',
|
||||
body,
|
||||
filePath,
|
||||
fullPrompt: body // The full markdown body serves as the system prompt template
|
||||
};
|
||||
|
||||
this.roles.set(roleId, role);
|
||||
|
||||
// Index by category
|
||||
const cat = category || 'uncategorized';
|
||||
if (!this.byCategory.has(cat)) this.byCategory.set(cat, []);
|
||||
this.byCategory.get(cat).push(roleId);
|
||||
|
||||
// Index keywords from name and description
|
||||
const keywords = [...new Set(
|
||||
(name + ' ' + description + ' ' + roleId)
|
||||
.toLowerCase()
|
||||
.split(/[\s,,、()()\/\\\-_]+/)
|
||||
.filter(k => k.length > 1)
|
||||
)];
|
||||
for (const kw of keywords) {
|
||||
if (!this.byKeyword.has(kw)) this.byKeyword.set(kw, new Set());
|
||||
this.byKeyword.get(kw).add(roleId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[RoleTemplates] Error parsing ${filePath}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
getTemplate(roleId) {
|
||||
const role = this.roles.get(roleId);
|
||||
if (!role) return null;
|
||||
return {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
emoji: role.emoji,
|
||||
color: role.color,
|
||||
category: role.category,
|
||||
prompt: role.fullPrompt
|
||||
};
|
||||
}
|
||||
|
||||
searchRoles(query) {
|
||||
const q = query.toLowerCase();
|
||||
const results = [];
|
||||
for (const role of this.roles.values()) {
|
||||
if (role.name.toLowerCase().includes(q) ||
|
||||
role.description.toLowerCase().includes(q) ||
|
||||
role.id.toLowerCase().includes(q) ||
|
||||
role.category.toLowerCase().includes(q)) {
|
||||
results.push({
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
emoji: role.emoji,
|
||||
category: role.category,
|
||||
description: role.description
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
searchByKeywords(keywords) {
|
||||
const matched = new Set();
|
||||
for (const kw of keywords.map(k => k.toLowerCase()).filter(k => k.length > 1)) {
|
||||
const ids = this.byKeyword.get(kw);
|
||||
if (ids) ids.forEach(id => matched.add(id));
|
||||
}
|
||||
return [...matched].map(id => ({
|
||||
id,
|
||||
name: this.roles.get(id).name,
|
||||
emoji: this.roles.get(id).emoji,
|
||||
category: this.roles.get(id).category,
|
||||
description: this.roles.get(id).description
|
||||
}));
|
||||
}
|
||||
|
||||
getCategories() {
|
||||
const result = [];
|
||||
for (const [cat, roleIds] of this.byCategory) {
|
||||
result.push({
|
||||
category: cat,
|
||||
count: roleIds.length,
|
||||
roles: roleIds.map(id => ({
|
||||
id,
|
||||
name: this.roles.get(id).name,
|
||||
emoji: this.roles.get(id).emoji
|
||||
}))
|
||||
});
|
||||
}
|
||||
return result.sort((a, b) => b.count - a.count);
|
||||
}
|
||||
|
||||
getCategoryRoles(category) {
|
||||
const ids = this.byCategory.get(category);
|
||||
if (!ids) return [];
|
||||
return ids.map(id => this.getTemplate(id));
|
||||
}
|
||||
|
||||
getRoleCount() {
|
||||
return this.roles.size;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RoleTemplates;
|
||||
Reference in New Issue
Block a user