feat(admin): Implement Prompt knowledge base integration

Features:

- PromptService enhancement: enhanceWithKnowledge(), loadFullKnowledge(), ragSearch()

- FULL mode: Load entire knowledge base content

- RAG mode: Vector search based on user query

- Knowledge config API: PUT /:code/knowledge-config

- Test render with knowledge injection support

- Frontend: Knowledge config UI in Prompt editor

Bug fixes:

- Fix knowledge config not returned in getPromptDetail

- Fix publish button 400 error (empty request body)

- Fix cache not invalidated after publish

- Add detailed logging for debugging

Documentation:

- Add development record 2026-01-28

- Update ADMIN module status to Phase 4.6

- Update system status document to v4.5

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-01 20:26:20 +08:00
parent 0d9e6b9922
commit aaa29ea9d3
15 changed files with 1459 additions and 26 deletions

View File

@@ -0,0 +1,164 @@
/**
* 端到端测试 PromptService.get() 知识库注入
*
* 使用方法:
* cd backend
* node src/common/prompt/__tests__/test-prompt-get.cjs
*/
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// 模拟 PromptService.get() 的逻辑
async function testPromptServiceGet(code, variables = {}) {
console.log(`\n🎯 测试 promptService.get("${code}", ${JSON.stringify(variables)})\n`);
console.log('='.repeat(60));
// 1. 获取模板
const template = await prisma.prompt_templates.findUnique({
where: { code },
});
if (!template) {
console.log(`❌ Prompt 模板 ${code} 不存在!`);
return;
}
console.log(`\n📝 Prompt 模板: ${template.name}`);
console.log(` 知识库配置: ${JSON.stringify(template.knowledge_config, null, 2)}`);
// 2. 获取版本
const version = await prisma.prompt_versions.findFirst({
where: { template_id: template.id, status: 'ACTIVE' },
orderBy: { version: 'desc' },
});
if (!version) {
console.log(`❌ 没有 ACTIVE 版本!`);
return;
}
console.log(`\n📄 Prompt 内容 (v${version.version}):`);
console.log(' ' + version.content.substring(0, 200).replace(/\n/g, '\n ') + '...');
// 3. 知识库增强
const config = template.knowledge_config;
if (!config || !config.enabled) {
console.log('\n⚠ 知识库增强未启用');
return;
}
console.log('\n📚 知识库增强配置:');
console.log(` 启用: ${config.enabled}`);
console.log(` 知识库: ${config.kb_codes?.join(', ')}`);
console.log(` 模式: ${config.injection_mode}`);
console.log(` 目标变量: ${config.target_variable || 'context'}`);
// 4. 加载知识库内容 (FULL 模式)
if (config.injection_mode === 'FULL') {
console.log('\n🔄 FULL 模式: 加载知识库全文...');
// 获取 system_knowledge_bases ID
const systemKbs = await prisma.system_knowledge_bases.findMany({
where: { code: { in: config.kb_codes || [] }, status: 'active' },
select: { id: true, code: true, name: true },
});
console.log(` 找到 ${systemKbs.length} 个系统知识库:`, systemKbs.map(kb => kb.code));
const kbIds = systemKbs.map(kb => kb.id);
console.log(` 系统知识库 IDs: ${kbIds.join(', ')}`);
// 检查 EKB 知识库
const ekbKbs = await prisma.ekbKnowledgeBase.findMany({
where: { id: { in: kbIds } },
select: { id: true, name: true },
});
console.log(` EKB 知识库: ${ekbKbs.length}`, ekbKbs.map(kb => kb.id));
// 查询分块
const chunks = await prisma.ekbChunk.findMany({
where: {
document: {
kbId: { in: kbIds },
},
},
select: { content: true },
take: 5,
});
console.log(` 找到 ${chunks.length} 个分块`);
if (chunks.length === 0) {
console.log('\n❌ 问题定位: ekbChunk 表中没有找到匹配的分块!');
console.log(' 这可能是因为 ekbChunk.document.kbId 与 system_knowledge_bases.id 不匹配');
// 检查 ekbDocument 的 kbId
console.log('\n 📋 检查 ekbDocument 表...');
const docs = await prisma.ekbDocument.findMany({
take: 5,
select: { id: true, kbId: true, fileName: true },
});
console.log(` ekbDocument 样本:`, docs.map(d => ({ id: d.id.substring(0, 8), kbId: d.kbId.substring(0, 8), name: d.fileName })));
console.log('\n 📋 检查 ekbKnowledgeBase 表...');
const allEkb = await prisma.ekbKnowledgeBase.findMany({
take: 5,
select: { id: true, name: true },
});
console.log(` ekbKnowledgeBase 样本:`, allEkb.map(kb => ({ id: kb.id.substring(0, 8), name: kb.name })));
console.log('\n 📋 检查 system_knowledge_bases 表...');
const allSystemKbs = await prisma.system_knowledge_bases.findMany({
take: 5,
select: { id: true, code: true, name: true },
});
console.log(` system_knowledge_bases 样本:`, allSystemKbs.map(kb => ({ id: kb.id.substring(0, 8), code: kb.code, name: kb.name })));
return;
}
// 合并内容
const knowledgeContent = chunks.map(c => c.content).join('\n\n');
console.log(`\n✅ 知识库内容加载成功,长度: ${knowledgeContent.length} 字符`);
console.log(` 预览: ${knowledgeContent.substring(0, 200).replace(/\n/g, ' ')}...`);
// 5. 注入到变量
const targetVariable = config.target_variable || 'context';
const enhancedVariables = { ...variables };
enhancedVariables[targetVariable] = knowledgeContent;
console.log(`\n📊 增强后的变量: { ${Object.keys(enhancedVariables).join(', ')} }`);
console.log(` ${targetVariable} 长度: ${enhancedVariables[targetVariable]?.length || 0} 字符`);
// 6. 渲染模板
const rendered = version.content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return enhancedVariables[key] || '';
});
console.log('\n📄 渲染后的 Prompt:');
console.log(' ' + rendered.substring(0, 500).replace(/\n/g, '\n ') + '...');
// 检查 {{context}} 是否被替换
if (rendered.includes('{{context}}')) {
console.log('\n❌ 问题: {{context}} 未被替换!');
} else {
console.log('\n✅ 成功: {{context}} 已被知识库内容替换!');
}
}
}
async function main() {
console.log('🧪 PromptService.get() 端到端测试\n');
// 测试已配置知识库的 Prompt
await testPromptServiceGet('AIA_SCIENTIFIC_QUESTION', {});
console.log('\n' + '='.repeat(60));
console.log('测试完成');
}
main()
.catch(console.error)
.finally(() => prisma.$disconnect());