226 lines
6.4 KiB
JavaScript
226 lines
6.4 KiB
JavaScript
|
|
/**
|
|||
|
|
* 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
|
|||
|
|
};
|