guanghulab/persona-studio/backend/brain/knowledge-extractor.js
2026-05-10 13:12:44 +08:00

226 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* persona-studio · 知识提取引擎
*
* 触发:代码生成完成时 / 用户确认方案时 / 解决技术问题时
* 功能:从对话中提取有价值的知识条目存入 knowledge-base.json
*/
const fs = require('fs');
const path = require('path');
const BRAIN_DIR = path.join(__dirname, '..', '..', 'brain');
const KB_PATH = path.join(BRAIN_DIR, 'knowledge-base.json');
/**
* 加载知识库
*/
function loadKnowledgeBase() {
try {
return JSON.parse(fs.readFileSync(KB_PATH, 'utf-8'));
} catch {
return {
schema_version: '1.0',
description: '系统知识库 · 自动积累',
last_updated: null,
total_entries: 0,
entries: []
};
}
}
/**
* 保存知识库
*/
function saveKnowledgeBase(kb) {
kb.last_updated = new Date().toISOString();
kb.total_entries = kb.entries.length;
fs.writeFileSync(KB_PATH, JSON.stringify(kb, null, 2), 'utf-8');
}
/**
* 从对话中提取技术方案
*/
function extractSolutions(conversation, sourceExp, sourceProject) {
const entries = [];
const messages = conversation || [];
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
if (msg.role !== 'assistant') continue;
const content = msg.content || '';
// 检测方案类关键词
if (/技术方案|解决方案|实现方式|推荐.*方案|建议使用/i.test(content)) {
entries.push({
id: 'KB-' + Date.now() + '-' + i,
type: 'solution',
category: detectCategory(content),
tags: extractTags(content),
title: extractTitle(content),
content: content.substring(0, 1000),
source_exp: sourceExp,
source_project: sourceProject || null,
usage_count: 0,
created_at: new Date().toISOString()
});
}
// 检测最佳实践
if (/最佳实践|best practice|推荐做法|正确.*方式/i.test(content)) {
entries.push({
id: 'KB-' + Date.now() + '-bp-' + i,
type: 'best_practice',
category: detectCategory(content),
tags: extractTags(content),
title: extractTitle(content),
content: content.substring(0, 1000),
source_exp: sourceExp,
source_project: sourceProject || null,
usage_count: 0,
created_at: new Date().toISOString()
});
}
// 检测常见陷阱
if (/注意|陷阱|坑|避免|不要.*这样|常见错误|pitfall/i.test(content)) {
entries.push({
id: 'KB-' + Date.now() + '-pit-' + i,
type: 'pitfall',
category: detectCategory(content),
tags: extractTags(content),
title: extractTitle(content),
content: content.substring(0, 1000),
source_exp: sourceExp,
source_project: sourceProject || null,
usage_count: 0,
created_at: new Date().toISOString()
});
}
}
return entries;
}
/**
* 检测技术类别
*/
function detectCategory(content) {
const lower = content.toLowerCase();
if (/react|vue|angular|前端|frontend|html|css|ui/i.test(lower)) return 'frontend';
if (/node|express|api|后端|backend|server|数据库|sql/i.test(lower)) return 'backend';
if (/docker|部署|deploy|ci|cd|运维|devops|nginx/i.test(lower)) return 'devops';
if (/设计|design|ux|ui|交互|布局|layout/i.test(lower)) return 'design';
if (/安全|security|auth|鉴权|加密|token/i.test(lower)) return 'security';
if (/性能|performance|优化|缓存|cache/i.test(lower)) return 'performance';
return 'general';
}
/**
* 提取标签
*/
function extractTags(content) {
const tags = [];
const techKeywords = [
'javascript', 'typescript', 'python', 'react', 'vue', 'node',
'express', 'html', 'css', 'sql', 'mongodb', 'redis', 'docker',
'nginx', 'git', 'api', 'rest', 'graphql', 'websocket'
];
const lower = content.toLowerCase();
techKeywords.forEach(function (kw) {
if (lower.includes(kw)) tags.push(kw);
});
return tags.slice(0, 10);
}
/**
* 提取标题取第一行非空文本的前50字符
*/
function extractTitle(content) {
const lines = content.split('\n').filter(function (l) { return l.trim().length > 0; });
if (lines.length === 0) return '未命名知识条目';
const first = lines[0].replace(/^[#*\-\s]+/, '').trim();
return first.length > 50 ? first.substring(0, 50) + '…' : first;
}
/**
* 将提取的知识添加到知识库(自动去重)
*/
function addToKnowledgeBase(entries) {
if (!entries || entries.length === 0) return;
const kb = loadKnowledgeBase();
entries.forEach(function (entry) {
// 简单去重:同标题+同类型 = 已存在
const exists = kb.entries.some(function (existing) {
return existing.title === entry.title && existing.type === entry.type;
});
if (!exists) {
kb.entries.push(entry);
}
});
// 保留最近 500 条
if (kb.entries.length > 500) {
kb.entries = kb.entries.slice(-500);
}
saveKnowledgeBase(kb);
}
/**
* 查询知识库(供 persona-engine 调用)
* @param {string} query - 搜索关键词
* @param {number} limit - 最大返回数量
* @returns {Array} 匹配的知识条目
*/
function queryKnowledge(query, limit) {
const kb = loadKnowledgeBase();
if (!query || kb.entries.length === 0) return [];
const lowerQuery = query.toLowerCase();
const keywords = lowerQuery.split(/[\s,,。.!?]+/).filter(Boolean);
const scored = kb.entries.map(function (entry) {
let score = 0;
const entryText = ((entry.title || '') + ' ' + (entry.content || '') + ' ' + (entry.tags || []).join(' ')).toLowerCase();
keywords.forEach(function (kw) {
if (entryText.includes(kw)) score += 1;
});
// 使用次数加权
score += (entry.usage_count || 0) * 0.1;
return { entry: entry, score: score };
});
return scored
.filter(function (s) { return s.score > 0; })
.sort(function (a, b) { return b.score - a.score; })
.slice(0, limit || 5)
.map(function (s) {
// 增加使用计数
s.entry.usage_count = (s.entry.usage_count || 0) + 1;
return s.entry;
});
}
/**
* 核心方法:自动提取知识
*/
function autoExtractKnowledge(devId, conversation, projectName) {
const entries = extractSolutions(conversation, devId, projectName);
if (entries.length > 0) {
addToKnowledgeBase(entries);
}
return entries.length;
}
module.exports = {
autoExtractKnowledge,
queryKnowledge,
loadKnowledgeBase,
addToKnowledgeBase,
extractSolutions
};