175 lines
4.9 KiB
Python
Executable File
175 lines
4.9 KiB
Python
Executable File
"""
|
|
RAG v10专用路由 - 使用苦荞SPL附表增强版索引
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import pickle
|
|
import numpy as np
|
|
from pathlib import Path
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import List, Optional
|
|
import requests
|
|
from src.rag.project_config import PROJECT_ROOT
|
|
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
router = APIRouter(prefix="/v10")
|
|
|
|
# ========== 配置 ==========
|
|
OLLAMA_URL = "http://localhost:11434"
|
|
EMBEDDING_MODEL = "bge-m3:567m"
|
|
LLM_MODEL = "qwen3:0.6b"
|
|
|
|
# v10索引路径
|
|
V10_INDEX_PATH = PROJECT_ROOT / "paper" / "v10" / "rag_v10_faiss_final.index"
|
|
V10_CHUNKS_PATH = PROJECT_ROOT / "paper" / "v10" / "rag_v10_chunks_with_summary.json"
|
|
V10_ENHANCED_PATH = PROJECT_ROOT / "paper" / "v10" / "rag_v10_chunks_enhanced.json"
|
|
|
|
# 全局变量
|
|
_v10_index = None
|
|
_v10_chunks = None
|
|
|
|
def get_embedding(text):
|
|
"""获取文本向量"""
|
|
response = requests.post(
|
|
f"{OLLAMA_URL}/api/embeddings",
|
|
json={"model": EMBEDDING_MODEL, "prompt": text}
|
|
)
|
|
vec = np.array(response.json()['embedding'], dtype=np.float32)
|
|
return vec / np.linalg.norm(vec)
|
|
|
|
def load_v10_index():
|
|
"""加载v10索引"""
|
|
global _v10_index, _v10_chunks
|
|
|
|
if _v10_index is not None:
|
|
return _v10_index, _v10_chunks
|
|
|
|
print(f"[V10 RAG] 加载v10索引...")
|
|
|
|
# 加载FAISS索引
|
|
import faiss
|
|
_v10_index = faiss.read_index(str(V10_INDEX_PATH))
|
|
print(f"[V10 RAG] 索引加载完成: {_v10_index.ntotal} 个向量")
|
|
|
|
# 加载文本块
|
|
with open(V10_CHUNKS_PATH, 'r') as f:
|
|
chunks1 = json.load(f)
|
|
with open(V10_ENHANCED_PATH, 'r') as f:
|
|
chunks2 = json.load(f)
|
|
|
|
_v10_chunks = chunks1 + chunks2
|
|
print(f"[V10 RAG] 文本块加载完成: {len(_v10_chunks)} 个")
|
|
|
|
return _v10_index, _v10_chunks
|
|
|
|
# ========== 请求/响应模型 ==========
|
|
class V10RAGQueryRequest(BaseModel):
|
|
query: str
|
|
top_k: int = 3
|
|
use_llm: bool = True
|
|
|
|
class V10RAGSource(BaseModel):
|
|
chunk_id: str
|
|
chunk_type: str
|
|
content: str
|
|
score: float
|
|
|
|
class V10RAGResponse(BaseModel):
|
|
query: str
|
|
answer: str
|
|
sources: List[V10RAGSource]
|
|
total_chunks: int
|
|
|
|
# ========== API端点 ==========
|
|
@router.post("/chat", response_model=V10RAGResponse)
|
|
async def v10_rag_chat(request: V10RAGQueryRequest):
|
|
"""V10专用RAG查询"""
|
|
try:
|
|
# 加载索引
|
|
index, chunks = load_v10_index()
|
|
|
|
# 1. 查询向量化
|
|
query_vec = get_embedding(request.query).reshape(1, -1)
|
|
|
|
# 2. FAISS检索
|
|
distances, indices = index.search(query_vec, request.top_k)
|
|
|
|
# 3. 构建sources
|
|
sources = []
|
|
context_parts = []
|
|
for i, (dist, idx) in enumerate(zip(distances[0], indices[0])):
|
|
chunk = chunks[idx]
|
|
sources.append(V10RAGSource(
|
|
chunk_id=chunk['id'],
|
|
chunk_type=chunk['metadata']['type'],
|
|
content=chunk['text'],
|
|
score=float(dist)
|
|
))
|
|
context_parts.append(f"[{i+1}] {chunk['text']}")
|
|
|
|
# 4. LLM生成(可选)
|
|
if request.use_llm:
|
|
context = "\n\n".join(context_parts)
|
|
prompt = f"""你是基因家族分析助手,基于以下资料回答问题。
|
|
请准确回答,并标注引用来源(如[1]、[2]等)。
|
|
|
|
参考资料:
|
|
{context}
|
|
|
|
用户问题:{request.query}
|
|
|
|
请基于以上资料回答:"""
|
|
|
|
response = requests.post(
|
|
f"{OLLAMA_URL}/api/generate",
|
|
json={
|
|
"model": LLM_MODEL,
|
|
"prompt": prompt,
|
|
"stream": False,
|
|
"options": {"temperature": 0.3}
|
|
}
|
|
)
|
|
answer = response.json()['response']
|
|
else:
|
|
# 直接返回检索结果
|
|
answer = "\n\n".join([f"[{i+1}] {s.content}" for i, s in enumerate(sources)])
|
|
|
|
return V10RAGResponse(
|
|
query=request.query,
|
|
answer=answer,
|
|
sources=sources,
|
|
total_chunks=len(chunks)
|
|
)
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"V10 RAG查询失败: {str(e)}")
|
|
|
|
@router.get("/info")
|
|
async def v10_info():
|
|
"""获取v10索引信息"""
|
|
try:
|
|
index, chunks = load_v10_index()
|
|
|
|
# 统计
|
|
type_counts = {}
|
|
for chunk in chunks:
|
|
t = chunk['metadata']['type']
|
|
type_counts[t] = type_counts.get(t, 0) + 1
|
|
|
|
return {
|
|
"index_type": "FAISS v10专用",
|
|
"total_chunks": len(chunks),
|
|
"embedding_model": EMBEDDING_MODEL,
|
|
"llm_model": LLM_MODEL,
|
|
"chunk_types": type_counts,
|
|
"data_source": "苦荞SPL附表增强版"
|
|
}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
print("[V10 RAG] 路由加载完成")
|