Files
AIclinicalresearch/frontend/src/pages/rvw/components/ReportDetail.tsx
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

129 lines
4.2 KiB
TypeScript

/**
* 报告详情页组件
*/
import { useState } from 'react';
import { ArrowLeft, FileCheck, Tag } from 'lucide-react';
import type { ReviewReport } from '../types';
import EditorialReport from './EditorialReport';
import MethodologyReport from './MethodologyReport';
interface ReportDetailProps {
report: ReviewReport;
onBack: () => void;
}
export default function ReportDetail({ report, onBack }: ReportDetailProps) {
const [activeTab, setActiveTab] = useState<'editorial' | 'methodology'>('editorial');
const hasEditorial = !!report.editorialReview;
const hasMethodology = !!report.methodologyReview;
// 如果只有方法学,默认显示方法学
const effectiveTab = activeTab === 'editorial' && !hasEditorial && hasMethodology ? 'methodology' : activeTab;
return (
<div className="flex-1 flex flex-col h-full bg-slate-50 relative fade-in">
{/* 顶部导航栏 */}
<header className="h-16 bg-white border-b border-gray-200 px-6 flex items-center justify-between sticky top-0 z-20 shadow-sm">
<div className="flex items-center gap-4">
<button
onClick={onBack}
className="flex items-center gap-2 text-slate-500 hover:text-slate-800 transition-colors px-2 py-1 rounded hover:bg-slate-100"
>
<ArrowLeft className="w-5 h-5" />
<span className="text-sm font-medium"></span>
</button>
<div className="h-6 w-px bg-slate-200" />
<div>
<h1 className="text-base font-bold text-slate-800 flex items-center gap-2">
{report.fileName}
{report.overallScore && (
<span className={`tag ${
report.overallScore >= 80 ? 'tag-green' :
report.overallScore >= 60 ? 'tag-amber' : 'tag-red'
}`}>
{report.overallScore}
</span>
)}
</h1>
</div>
</div>
<div className="flex items-center gap-3">
<button className="px-3 py-1.5 bg-indigo-600 text-white rounded text-sm font-medium hover:bg-indigo-700 transition shadow-sm flex items-center gap-2">
<FileCheck className="w-4 h-4" />
</button>
</div>
</header>
{/* 内容区域 */}
<div className="flex-1 overflow-auto p-8 max-w-5xl mx-auto w-full">
{/* Tab切换 */}
{(hasEditorial || hasMethodology) && (
<div className="flex gap-1 bg-slate-200/50 p-1 rounded-lg mb-8 w-fit mx-auto">
{hasEditorial && (
<button
onClick={() => setActiveTab('editorial')}
className={`px-6 py-2 rounded-md text-sm transition-all ${
effectiveTab === 'editorial'
? 'font-bold bg-white text-indigo-600 shadow-sm'
: 'font-medium text-slate-500 hover:text-slate-700'
}`}
>
稿 ({report.editorialReview?.overall_score})
</button>
)}
{hasMethodology && (
<button
onClick={() => setActiveTab('methodology')}
className={`px-6 py-2 rounded-md text-sm transition-all ${
effectiveTab === 'methodology'
? 'font-bold bg-white text-indigo-600 shadow-sm'
: 'font-medium text-slate-500 hover:text-slate-700'
}`}
>
({report.methodologyReview?.overall_score})
</button>
)}
</div>
)}
{/* 报告内容 */}
{effectiveTab === 'editorial' && report.editorialReview && (
<EditorialReport data={report.editorialReview} />
)}
{effectiveTab === 'methodology' && report.methodologyReview && (
<MethodologyReport data={report.methodologyReview} />
)}
{/* 无数据状态 */}
{!hasEditorial && !hasMethodology && (
<div className="text-center py-12 text-slate-500">
<Tag className="w-12 h-12 mx-auto mb-4 text-slate-300" />
<p></p>
</div>
)}
</div>
</div>
);
}