06_文档向量化 API 实现指南

第一阶段:环境与配置准备

1.1 安装必要依赖

apps/api 目录下,我们需要安装处理文件上传和对接 AI 模型的工具:

powershell 复制代码
uv add python-multipart langchain-openai

注意python-multipart 是 FastAPI 处理文件上传的必需件。

1.2 更新 .env

.env 文件中加入你的 AI 服务密钥(以 OpenAI 为例,你也可以更换为本地模型):

env 复制代码
OPENAI_API_KEY=sk-xxxx...
# 2026 年推荐使用更快速的 Embedding 模型
EMBEDDING_MODEL=text-embedding-3-small

第二阶段:编写向量生成服务

apps/api 下新建 services/ 文件夹,并创建 embedding.py

python 复制代码
from langchain_openai import OpenAIEmbeddings
import os
from dotenv import load_dotenv

load_dotenv()

# 初始化 Embedding 客户端
# 2026 年的 LangChain 已原生适配 Python 3.14 的并发特性
embeddings_model = OpenAIEmbeddings(
    model=os.getenv("EMBEDDING_MODEL", "text-embedding-3-small")
)

async def generate_vector(text: str):
    """将文本转化为 1536 维的向量"""
    # 这里会自动调用远程或本地模型进行计算
    vector = await embeddings_model.aembed_query(text)
    return vector

第三阶段:更新主应用文件

现在我们把所有零件组装在 apps/api/main.py 中。更新后的完整代码如下:

python 复制代码
from fastapi import FastAPI, UploadFile, File, Depends, HTTPException
from contextlib import asynccontextmanager
from sqlmodel import Session, text
from typing import List
import os

# 数据库和模型导入
from database import init_db, engine, get_session
from models import User, Document
from services.embedding import generate_vector

@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理"""
    # 启动时执行
    print("🚀 启动 KnoSphere API...")
    with engine.connect() as conn:
        # 激活向量扩展,这是 2026 年 RAG 系统的核心
        conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector;"))
        conn.commit()
    init_db()  # 这会创建所有表,包括 User 和 Document
    print("✅ 数据库初始化完成")
    yield
    # 关闭时执行(如果需要清理资源)
    print("👋 关闭 KnoSphere API...")

app = FastAPI(
    title="KnoSphere API",
    description="2026 企业级智能知识库系统",
    version="1.0.0",
    lifespan=lifespan
)

@app.get("/")
async def root():
    return {"message": "欢迎使用 KnoSphere API - 2026 企业级智能知识库系统"}

@app.get("/health")
async def health():
    return {"status": "healthy", "service": "KnoSphere API"}

@app.get("/documents")
async def list_documents(
    db: Session = Depends(get_session),
    limit: int = 10,
    offset: int = 0
):
    """获取文档列表"""
    documents = db.query(Document).offset(offset).limit(limit).all()
    return {
        "total": db.query(Document).count(),
        "documents": [
            {
                "id": doc.id,
                "title": doc.title,
                "created_at": doc.created_at,
                "content_preview": doc.content[:100] + "..." if len(doc.content) > 100 else doc.content
            }
            for doc in documents
        ]
    }

@app.post("/upload")
async def upload_document(
    file: UploadFile = File(...), 
    db: Session = Depends(get_session)
):
    """
    上传文档并生成向量
    支持格式:.txt, .md, .pdf, .docx
    """
    # 1. 验证文件类型
    allowed_extensions = {'.txt', '.md', '.pdf', '.docx'}
    file_ext = os.path.splitext(file.filename)[1].lower()
    
    if file_ext not in allowed_extensions:
        raise HTTPException(
            status_code=400, 
            detail=f"不支持的文件格式。支持格式: {', '.join(allowed_extensions)}"
        )
    
    # 2. 读取文件内容
    try:
        content = await file.read()
        
        # 处理不同文件类型
        if file_ext == '.pdf':
            # PDF 处理 - 需要额外的依赖
            try:
                import PyPDF2
                pdf_reader = PyPDF2.PdfReader(io.BytesIO(content))
                text_content = ""
                for page in pdf_reader.pages:
                    text_content += page.extract_text()
            except ImportError:
                # 如果未安装 PyPDF2,提示安装
                raise HTTPException(
                    status_code=400,
                    detail="PDF 处理需要安装 PyPDF2。请运行: uv add PyPDF2"
                )
        elif file_ext == '.docx':
            # DOCX 处理
            try:
                import docx
                doc = docx.Document(io.BytesIO(content))
                text_content = "\n".join([paragraph.text for paragraph in doc.paragraphs])
            except ImportError:
                raise HTTPException(
                    status_code=400,
                    detail="DOCX 处理需要安装 python-docx。请运行: uv add python-docx"
                )
        else:
            # 文本文件处理
            text_content = content.decode("utf-8")
            
    except UnicodeDecodeError:
        raise HTTPException(status_code=400, detail="文件编码错误,请使用 UTF-8 编码")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"文件读取失败: {str(e)}")

    # 3. 检查文本长度
    if len(text_content.strip()) == 0:
        raise HTTPException(status_code=400, detail="文件内容为空")
    
    # 4. 生成向量 (AI 核心步骤)
    try:
        vector = await generate_vector(text_content)
    except Exception as e:
        raise HTTPException(
            status_code=500, 
            detail=f"向量生成失败: {str(e)}。请检查 OPENAI_API_KEY 环境变量"
        )

    # 5. 存储到 PostgreSQL 17
    try:
        new_doc = Document(
            title=file.filename,
            content=text_content,
            embedding=vector  # 存入我们之前定义的 Vector 字段
        )
        
        db.add(new_doc)
        db.commit()
        db.refresh(new_doc)
        
        return {
            "message": "上传成功", 
            "document_id": new_doc.id,
            "title": new_doc.title,
            "vector_dimensions": len(vector) if vector else 0,
            "content_length": len(text_content)
        }
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"数据库存储失败: {str(e)}")

# 添加缺失的导入
import io

# 如果需要添加更多文件格式处理,可以取消下面的注释并安装相应依赖
# @app.on_event("startup")
# async def check_dependencies():
#     """检查可选依赖"""
#     try:
#         import PyPDF2
#         print("✅ PyPDF2 已安装,支持 PDF 处理")
#     except ImportError:
#         print("⚠️  PyPDF2 未安装,PDF 文件处理将不可用")
#     
#     try:
#         import docx
#         print("✅ python-docx 已安装,支持 DOCX 处理")
#     except ImportError:
#         print("⚠️  python-docx 未安装,DOCX 文件处理将不可用")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        app, 
        host="0.0.0.0", 
        port=8000,
        reload=True  # 开发模式下自动重载
    )

2026 年的核心技术细节解析

  1. 异步处理 (Async) :在 2026 年,FastAPI 的异步性能已经非常成熟。通过 await,系统在等待 AI 生成向量时不会阻塞,可以同时处理成百上千个用户的上传请求。
  2. 存算分离趋势 :虽然我们目前存入 PostgreSQL,但如果你的知识库未来达到亿级规模,只需将 Document 的存储逻辑切向 Milvus 3.x 即可,架构上是完全兼容的。
  3. 向量压缩text-embedding-3-small 支持缩减维度而保持精度,这能显著降低 PostgreSQL 的索引压力。
  4. 生命周期管理 :使用 @asynccontextmanager 替代旧的 @app.on_event("startup")@app.on_event("shutdown"),这是 FastAPI 2.0+ 的最佳实践。

第四阶段:可选依赖安装

如果你想支持更多文件格式,可以安装额外的依赖:

powershell 复制代码
# 安装 PDF 处理支持
uv add PyPDF2

# 安装 DOCX 处理支持
uv add python-docx

第五阶段:测试 API

现在你可以启动 API 服务器进行测试:

powershell 复制代码
# 在 apps/api 目录下运行
uv run python main.py

或者直接使用 uvicorn:

powershell 复制代码
uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000

测试上传接口:

bash 复制代码
# 使用 curl 测试
curl -X POST "http://localhost:8000/upload" \
  -H "accept: application/json" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@example.txt"

检查点:你的 AI 链路已跑通

  • API 端点 :可以通过 POST /upload 接收文件。
  • AI 转化:文本已成功转化为高维数学向量。
  • 持久化:原始文本和向量数据已安全存入 PostgreSQL。

!!!问题: 但目前只支持openai的,不支持大多数模型

  • 兼容大多数模型
相关推荐
renhongxia12 天前
如何基于知识图谱进行故障原因、事故原因推理,需要用到哪些算法
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·知识图谱
玄同7652 天前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
zhengfei6113 天前
【AI平台】- 基于大模型的知识库与知识图谱智能体开发平台
vue.js·语言模型·langchain·知识图谱·多分类
玄同7653 天前
LangChain 1.0 模型接口:多厂商集成与统一调用
开发语言·人工智能·python·langchain·知识图谱·rag·智能体
L_Jason先生3 天前
高效利用 Coding Agent 进行规约驱动开发 (SDD):原理、实践与未来洞察
人工智能·驱动开发·知识图谱
Allen_LVyingbo3 天前
医疗AI新范式:当数理模型开始“计算”生命,传统大模型面临重构(中)
开发语言·人工智能·python·自然语言处理·重构·知识图谱
千桐科技4 天前
qKnow 知识平台核心能力解析|第 03 期:结构化抽取能力全流程介绍
大模型·llm·知识图谱·知识库·rag·qknow·知识平台