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
97 lines
2.1 KiB
TypeScript
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,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|