Qwen-Ranker Pro全栈开发:从后端API到前端展示的完整实现
Qwen-Ranker Pro全栈开发:从后端API到前端展示的完整实现
1. 引言
在当今信息爆炸的时代,如何从海量数据中快速准确地找到最相关的内容,成为了许多应用面临的核心挑战。语义搜索技术通过理解查询的深层含义,而不仅仅是关键词匹配,为用户提供了更精准的搜索结果。Qwen-Ranker Pro作为先进的语义重排序模型,能够对初步检索结果进行智能精排,显著提升搜索质量。
本文将带你完整实现一个基于Qwen-Ranker Pro的全栈应用,从前端界面到后端API,从数据库集成到模型部署,手把手教你构建一个功能完备的语义搜索系统。无论你是前端开发者想了解后端集成,还是后端工程师希望扩展全栈技能,这篇文章都能为你提供实用的指导和代码示例。
2. 技术架构设计
2.1 整体架构概述
我们的全栈应用采用典型的三层架构:前端展示层、后端服务层和数据存储层。前端使用Vue.js构建响应式用户界面,后端采用FastAPI提供RESTful API,数据库使用PostgreSQL存储文档和向量数据,Qwen-Ranker Pro模型负责语义重排序。
这种架构的优势在于各层职责清晰,便于维护和扩展。前端专注于用户体验,后端处理业务逻辑和模型推理,数据库负责数据持久化,模型提供AI能力。
2.2 核心技术选型
前端技术栈:Vue 3 + TypeScript + Element Plus + Axios。Vue 3提供了优秀的响应式系统和组合式API,TypeScript增强了代码类型安全,Element Plus提供了丰富的UI组件,Axios处理HTTP请求。
后端技术栈:FastAPI + Uvicorn + SQLAlchemy + Psycopg2。FastAPI以其高性能和自动API文档生成而著称,Uvicorn作为ASGI服务器,SQLAlchemy提供ORM支持,Psycopg2是PostgreSQL数据库驱动。
数据库:PostgreSQL + pgvector扩展。PostgreSQL是功能强大的关系型数据库,pgvector扩展支持向量相似度搜索,非常适合语义搜索场景。
AI模型:Qwen-Ranker Pro。该模型专门用于语义重排序,能够理解查询和文档之间的语义相关性,提供精准的排序分数。
3. 后端API开发
3.1 环境配置与依赖安装
首先创建项目目录并初始化Python虚拟环境:
mkdir qwen-ranker-pro-app
cd qwen-ranker-pro-app
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
pip install fastapi uvicorn sqlalchemy psycopg2-binary python-multipart
pip install transformers torch
创建主要的依赖文件requirements.txt:
fastapi==0.104.1
uvicorn==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9
python-multipart==0.0.6
transformers==4.35.2
torch==2.1.0
python-dotenv==1.0.0
3.2 数据库模型设计
创建models.py文件定义数据模型:
from sqlalchemy import Column, Integer, String, Text, Float, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os
from dotenv import load_dotenv
load_dotenv()
Base = declarative_base()
class Document(Base):
__tablename__ = "documents"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(500), nullable=False)
content = Column(Text, nullable=False)
embedding = Column(Text) # 存储向量化后的文本表示
metadata = Column(Text) # 存储额外元信息
# 数据库连接配置
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost/qwen_ranker_db")
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建表
def create_tables():
Base.metadata.create_all(bind=engine)
3.3 FastAPI应用搭建
创建main.py文件设置FastAPI应用:
from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
from typing import List
import models
import schemas
import services
app = FastAPI(
title="Qwen-Ranker Pro API",
description="语义搜索重排序API服务",
version="1.0.0"
)
# 配置CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 数据库依赖
def get_db():
db = models.SessionLocal()
try:
yield db
finally:
db.close()
@app.on_event("startup")
async def startup_event():
models.create_tables()
@app.get("/")
async def root():
return {"message": "Qwen-Ranker Pro API服务运行中"}
@app.post("/documents/", response_model=schemas.DocumentResponse)
async def create_document(document: schemas.DocumentCreate, db: Session = Depends(get_db)):
return services.create_document(db=db, document=document)
@app.get("/documents/", response_model=List[schemas.DocumentResponse])
async def get_documents(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
return services.get_documents(db=db, skip=skip, limit=limit)
@app.post("/search/", response_model=List[schemas.SearchResult])
async def semantic_search(query: schemas.SearchQuery, db: Session = Depends(get_db)):
return services.semantic_search(db=db, query=query.query)
3.4 Qwen-Ranker Pro集成
创建services.py实现核心业务逻辑:
from sqlalchemy.orm import Session
import models
import schemas
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import numpy as np
from typing import List
# 加载Qwen-Ranker Pro模型
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-Ranker-Pro")
model = AutoModelForSequenceClassification.from_pretrained("Qwen/Qwen-Ranker-Pro")
def create_document(db: Session, document: schemas.DocumentCreate):
db_document = models.Document(
title=document.title,
content=document.content,
metadata=document.metadata
)
db.add(db_document)
db.commit()
db.refresh(db_document)
return db_document
def get_documents(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Document).offset(skip).limit(limit).all()
def semantic_search(db: Session, query: str):
# 首先进行初步检索(这里简化处理,实际应使用向量数据库)
documents = db.query(models.Document).limit(50).all()
# 使用Qwen-Ranker Pro进行重排序
ranked_results = rerank_documents(query, documents)
return ranked_results
def rerank_documents(query: str, documents: List[models.Document]):
# 准备模型输入
pairs = [[query, doc.content] for doc in documents]
# 批量处理
inputs = tokenizer(
pairs,
padding=True,
truncation=True,
max_length=512,
return_tensors="pt"
)
# 模型推理
with torch.no_grad():
scores = model(**inputs).logits
# 处理结果
results = []
for i, doc in enumerate(documents):
results.append({
"id": doc.id,
"title": doc.title,
"content": doc.content[:200] + "..." if len(doc.content) > 200 else doc.content,
"score": float(scores[i]),
"metadata": doc.metadata
})
# 按分数降序排序
results.sort(key=lambda x: x["score"], reverse=True)
return results[:10] # 返回前10个结果
3.5 数据序列化模型
创建schemas.py定义Pydantic模型:
from pydantic import BaseModel
from typing import Optional
class DocumentBase(BaseModel):
title: str
content: str
metadata: Optional[str] = None
class DocumentCreate(DocumentBase):
pass
class DocumentResponse(DocumentBase):
id: int
class Config:
from_attributes = True
class SearchQuery(BaseModel):
query: str
class SearchResult(BaseModel):
id: int
title: str
content: str
score: float
metadata: Optional[str] = None
4. 前端界面开发
4.1 Vue项目初始化
使用Vite创建Vue项目:
npm create vite@latest frontend -- --template vue-ts
cd frontend
npm install
npm install element-plus axios
配置main.ts:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')
4.2 搜索界面组件
创建SearchComponent.vue:
<template>
<div class="search-container">
<el-input
v-model="searchQuery"
placeholder="请输入搜索内容..."
size="large"
@keyup.enter="handleSearch"
>
<template #append>
<el-button :icon="Search" @click="handleSearch">搜索</el-button>
</template>
</el-input>
<div v-if="loading" class="loading">
<el-icon class="is-loading"><Loading /></el-icon>
搜索中...
</div>
<div v-if="results.length > 0" class="results-container">
<div class="results-count">找到 {{ results.length }} 条结果</div>
<el-card
v-for="(result, index) in results"
:key="result.id"
class="result-card"
:class="{ 'first-result': index === 0 }"
>
<template #header>
<div class="card-header">
<span class="result-title">{{ result.title }}</span>
<el-tag
type="success"
size="small"
class="score-tag"
>
相关度: {{ result.score.toFixed(4) }}
</el-tag>
</div>
</template>
<div class="result-content">
{{ result.content }}
</div>
<div v-if="result.metadata" class="result-metadata">
<el-text type="info" size="small">
{{ result.metadata }}
</el-text>
</div>
</el-card>
</div>
<div v-if="searched && results.length === 0" class="no-results">
<el-empty description="没有找到相关结果" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Search, Loading } from '@element-plus/icons-vue'
import axios from 'axios'
interface SearchResult {
id: number
title: string
content: string
score: number
metadata?: string
}
const searchQuery = ref('')
const results = ref<SearchResult[]>([])
const loading = ref(false)
const searched = ref(false)
const API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8000'
const handleSearch = async () => {
if (!searchQuery.value.trim()) return
loading.value = true
searched.value = true
try {
const response = await axios.post(`${API_BASE}/search/`, {
query: searchQuery.value
})
results.value = response.data
} catch (error) {
console.error('搜索失败:', error)
ElMessage.error('搜索失败,请稍后重试')
} finally {
loading.value = false
}
}
</script>
<style scoped>
.search-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.results-count {
margin: 20px 0;
font-size: 14px;
color: #666;
}
.result-card {
margin-bottom: 16px;
transition: all 0.3s ease;
}
.result-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.first-result {
border: 2px solid #409eff;
}
.card-header {
display: flex;
justify-content: between;
align-items: center;
}
.result-title {
font-weight: bold;
font-size: 16px;
}
.score-tag {
margin-left: auto;
}
.result-content {
line-height: 1.6;
color: #333;
}
.result-metadata {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #eee;
}
.no-results {
text-align: center;
padding: 40px 0;
}
</style>
4.3 主应用组件
更新App.vue:
<template>
<div id="app">
<el-container>
<el-header class="app-header">
<h1>Qwen-Ranker Pro 语义搜索系统</h1>
<el-text type="info">基于先进AI模型的智能语义重排序</el-text>
</el-header>
<el-main>
<router-view />
</el-main>
<el-footer class="app-footer">
<el-text type="info" size="small">
© 2024 语义搜索系统 | 基于 Qwen-Ranker Pro 构建
</el-text>
</el-footer>
</el-container>
</div>
</template>
<script setup lang="ts">
// 主应用组件逻辑
</script>
<style>
#app {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.app-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
padding: 20px;
}
.app-header h1 {
margin: 0 0 8px 0;
font-size: 2em;
}
.app-footer {
text-align: center;
padding: 20px;
background-color: #f5f7fa;
}
</style>
5. 系统部署与优化
5.1 环境配置与部署
创建Dockerfile用于容器化部署:
# 后端Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
创建docker-compose.yml编排服务:
version: '3.8'
services:
backend:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/qwen_ranker_db
depends_on:
- db
volumes:
- .:/app
frontend:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- backend
db:
image: postgres:13
environment:
- POSTGRES_DB=qwen_ranker_db
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
5.2 性能优化建议
数据库优化:
# 添加索引优化查询性能
class Document(Base):
# ... 其他字段
__table_args__ = (
Index('ix_document_content', 'content', postgresql_using='gin'),
Index('ix_document_title', 'title'),
)
模型推理优化:
# 使用模型缓存和批量处理
model = AutoModelForSequenceClassification.from_pretrained(
"Qwen/Qwen-Ranker-Pro",
torchscript=True # 启用TorchScript优化
)
# 启用模型eval模式和多线程
model.eval()
torch.set_num_threads(4)
API响应优化:
# 使用异步数据库操作
@app.post("/search/")
async def semantic_search(query: schemas.SearchQuery, db: Session = Depends(get_db)):
# 使用异步方式执行耗时操作
results = await run_in_threadpool(services.semantic_search, db, query.query)
return results
6. 总结
通过本文的完整实现,我们构建了一个基于Qwen-Ranker Pro的全栈语义搜索应用。从后端的FastAPI服务到前端的Vue界面,从数据库设计到模型集成,每个环节都提供了详细的代码示例和实践建议。
这个项目展示了如何将先进的AI模型与传统的Web开发技术相结合,创造出智能化的应用体验。Qwen-Ranker Pro的语义重排序能力显著提升了搜索结果的相关性,而全栈架构确保了系统的可扩展性和维护性。
在实际部署时,建议进一步考虑缓存机制、负载均衡和监控系统,以确保生产环境的稳定性和性能。此外,可以根据具体业务需求调整模型参数和搜索策略,获得更好的效果。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐



所有评论(0)