Files
AIclinicalresearch/backend/src/modules/pkb/controllers/knowledgeBaseController.ts
HaHafeng 483c62fb6f feat(pkb): Replace Dify with self-developed pgvector RAG engine
Major milestone: Successfully replaced Dify external service with PostgreSQL + pgvector RAG engine

Backend changes:
- Refactor ragService.ts: Remove dual-track mode, keep only pgvector
- Refactor knowledgeBaseService.ts: Remove Dify creation logic
- Refactor documentService.ts: Remove Dify upload/polling logic
- DifyClient.ts: Convert to deprecated stub file (for legacy compatibility)
- common/rag/index.ts: Update exports
- common/rag/types.ts: Remove Dify types, keep generic RAG types
- config/env.ts: Remove Dify configuration

Frontend changes:
- DashboardPage.tsx: Add delete knowledge base dropdown menu
- KnowledgeBaseList.tsx: Enhance quota warning display
- CreateKBDialog.tsx: Add quota exceeded modal with guidance
- knowledgeBaseApi.ts: Add auth interceptor

Documentation:
- Update PKB module status guide (v2.3)
- Update system status guide (v4.0)

Performance metrics:
- Single query latency: 2.5s
- Single query cost: 0.0025 CNY
- Cross-language accuracy improvement: +20.5%

Remaining tasks:
- OSS storage integration
- pg_bigm extension installation

Tested: End-to-end test passed (create KB -> upload doc -> vector search)
2026-01-21 22:35:50 +08:00

368 lines
7.7 KiB
TypeScript
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.
import type { FastifyRequest, FastifyReply } from 'fastify';
import * as knowledgeBaseService from '../services/knowledgeBaseService.js';
/**
* 获取用户ID从JWT Token中获取
*/
function getUserId(request: FastifyRequest): string {
const userId = (request as any).user?.userId;
if (!userId) {
throw new Error('User not authenticated');
}
return userId;
}
/**
* 创建知识库
*/
export async function createKnowledgeBase(
request: FastifyRequest<{
Body: {
name: string;
description?: string;
};
}>,
reply: FastifyReply
) {
try {
const { name, description } = request.body;
if (!name || name.trim().length === 0) {
return reply.status(400).send({
success: false,
message: 'Knowledge base name is required',
});
}
const userId = getUserId(request);
const knowledgeBase = await knowledgeBaseService.createKnowledgeBase(
userId,
name,
description
);
return reply.status(201).send({
success: true,
data: knowledgeBase,
});
} catch (error: any) {
console.error('Failed to create knowledge base:', error);
// 处理配额超限错误
if (error.code === 'QUOTA_EXCEEDED') {
return reply.status(400).send({
success: false,
code: 'QUOTA_EXCEEDED',
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || '创建知识库失败,请稍后重试',
});
}
}
/**
* 获取知识库列表
*/
export async function getKnowledgeBases(
request: FastifyRequest,
reply: FastifyReply
) {
try {
const userId = getUserId(request);
const knowledgeBases = await knowledgeBaseService.getKnowledgeBases(
userId
);
return reply.send({
success: true,
data: knowledgeBases,
});
} catch (error: any) {
console.error('Failed to get knowledge bases:', error);
return reply.status(500).send({
success: false,
message: error.message || 'Failed to get knowledge bases',
});
}
}
/**
* 获取知识库详情
*/
export async function getKnowledgeBaseById(
request: FastifyRequest<{
Params: {
id: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const userId = getUserId(request);
const knowledgeBase = await knowledgeBaseService.getKnowledgeBaseById(
userId,
id
);
return reply.send({
success: true,
data: knowledgeBase,
});
} catch (error: any) {
console.error('Failed to get knowledge base:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to get knowledge base',
});
}
}
/**
* 更新知识库
*/
export async function updateKnowledgeBase(
request: FastifyRequest<{
Params: {
id: string;
};
Body: {
name?: string;
description?: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const updateData = request.body;
const userId = getUserId(request);
const knowledgeBase = await knowledgeBaseService.updateKnowledgeBase(
userId,
id,
updateData
);
return reply.send({
success: true,
data: knowledgeBase,
});
} catch (error: any) {
console.error('Failed to update knowledge base:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to update knowledge base',
});
}
}
/**
* 删除知识库
*/
export async function deleteKnowledgeBase(
request: FastifyRequest<{
Params: {
id: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const userId = getUserId(request);
await knowledgeBaseService.deleteKnowledgeBase(userId, id);
return reply.send({
success: true,
message: 'Knowledge base deleted successfully',
});
} catch (error: any) {
console.error('Failed to delete knowledge base:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to delete knowledge base',
});
}
}
/**
* 检索知识库
*/
export async function searchKnowledgeBase(
request: FastifyRequest<{
Params: {
id: string;
};
Querystring: {
query: string;
top_k?: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const { query, top_k } = request.query;
if (!query || query.trim().length === 0) {
return reply.status(400).send({
success: false,
message: 'Query parameter is required',
});
}
const topK = top_k ? parseInt(top_k, 10) : 15; // Phase 1优化默认从3增加到15
const userId = getUserId(request);
const results = await knowledgeBaseService.searchKnowledgeBase(
userId,
id,
query,
topK
);
return reply.send({
success: true,
data: results,
});
} catch (error: any) {
console.error('Failed to search knowledge base:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to search knowledge base',
});
}
}
/**
* 获取知识库统计信息
*/
export async function getKnowledgeBaseStats(
request: FastifyRequest<{
Params: {
id: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const userId = getUserId(request);
const stats = await knowledgeBaseService.getKnowledgeBaseStats(
userId,
id
);
return reply.send({
success: true,
data: stats,
});
} catch (error: any) {
console.error('Failed to get knowledge base stats:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to get knowledge base stats',
});
}
}
/**
* 获取知识库文档选择Phase 2: 全文阅读模式)
*/
export async function getDocumentSelection(
request: FastifyRequest<{
Params: {
id: string;
};
Querystring: {
max_files?: string;
max_tokens?: string;
};
}>,
reply: FastifyReply
) {
try {
const { id } = request.params;
const { max_files, max_tokens } = request.query;
const maxFiles = max_files ? parseInt(max_files, 10) : undefined;
const maxTokens = max_tokens ? parseInt(max_tokens, 10) : undefined;
const userId = getUserId(request);
const selection = await knowledgeBaseService.getDocumentSelection(
userId,
id,
maxFiles,
maxTokens
);
return reply.send({
success: true,
data: selection,
});
} catch (error: any) {
console.error('Failed to get document selection:', error);
if (error.message.includes('not found')) {
return reply.status(404).send({
success: false,
message: error.message,
});
}
return reply.status(500).send({
success: false,
message: error.message || 'Failed to get document selection',
});
}
}