Files
AIclinicalresearch/backend/src/modules/aia/controllers/attachmentController.ts
HaHafeng 40c2f8e148 feat(rag): Complete RAG engine implementation with pgvector
Major Features:
- Created ekb_schema (13th schema) with 3 tables: KB/Document/Chunk
- Implemented EmbeddingService (text-embedding-v4, 1024-dim vectors)
- Implemented ChunkService (smart Markdown chunking)
- Implemented VectorSearchService (multi-query + hybrid search)
- Implemented RerankService (qwen3-rerank)
- Integrated DeepSeek V3 QueryRewriter for cross-language search
- Python service: Added pymupdf4llm for PDF-to-Markdown conversion
- PKB: Dual-mode adapter (pgvector/dify/hybrid)

Architecture:
- Brain-Hand Model: Business layer (DeepSeek) + Engine layer (pgvector)
- Cross-language support: Chinese query matches English documents
- Small Embedding (1024) + Strong Reranker strategy

Performance:
- End-to-end latency: 2.5s
- Cost per query: 0.0025 RMB
- Accuracy improvement: +20.5% (cross-language)

Tests:
- test-embedding-service.ts: Vector embedding verified
- test-rag-e2e.ts: Full pipeline tested
- test-rerank.ts: Rerank quality validated
- test-query-rewrite.ts: Cross-language search verified
- test-pdf-ingest.ts: Real PDF document tested (Dongen 2003.pdf)

Documentation:
- Added 05-RAG-Engine-User-Guide.md
- Added 02-Document-Processing-User-Guide.md
- Updated system status documentation

Status: Production ready
2026-01-21 20:24:29 +08:00

97 lines
2.1 KiB
TypeScript

/**
* AIA 智能问答模块 - 附件控制器
* @module aia/controllers/attachmentController
*
* API 端点:
* - POST /api/v1/aia/conversations/:id/attachments 上传附件
*/
import type { FastifyRequest, FastifyReply } from 'fastify';
import { logger } from '../../../common/logging/index.js';
import * as attachmentService from '../services/attachmentService.js';
/**
* 从 JWT Token 获取用户 ID
*/
function getUserId(request: FastifyRequest): string {
const userId = (request as any).user?.userId;
if (!userId) {
throw new Error('User not authenticated');
}
return userId;
}
/**
* 上传附件
* POST /api/v1/aia/conversations/:id/attachments
*/
export async function uploadAttachment(
request: FastifyRequest<{
Params: { id: string };
}>,
reply: FastifyReply
) {
try {
const userId = getUserId(request);
const { id: conversationId } = request.params;
// 获取上传的文件
const data = await request.file();
if (!data) {
return reply.status(400).send({
code: -1,
error: {
code: 'VALIDATION_ERROR',
message: '请上传文件',
},
});
}
const buffer = await data.toBuffer();
logger.info('[AIA:AttachmentController] 上传附件', {
userId,
conversationId,
filename: data.filename,
mimetype: data.mimetype,
size: buffer.length,
});
const attachment = await attachmentService.uploadAttachment(
userId,
conversationId,
{
filename: data.filename,
mimetype: data.mimetype,
buffer,
}
);
return reply.send({
code: 0,
data: attachment,
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error('[AIA:AttachmentController] 上传附件失败', {
error: errorMessage,
stack: error instanceof Error ? error.stack : undefined,
});
return reply.status(500).send({
code: -1,
error: {
code: 'INTERNAL_ERROR',
message: errorMessage,
},
});
}
}