AI学习:第3天

太棒了!API 调用成功,你已经拥有了一个本地的、可编程的 LLM 服务。接下来,我们进入第三天:实现 RAG(检索增强生成)

🎯 第三天目标:做一个能基于你自己的文档回答问题的系统

比如,你可以上传一段关于你后端项目的描述,然后问 "我的项目用了哪些技术栈?",模型会根据你提供的文档来回答,而不是凭空想象。

你需要做什么(总耗时约 50 分钟)

1️⃣ 安装必要的库(5 分钟)

ai_env 环境中执行:

bash 复制代码
pip install sentence-transformers chromadb
  • sentence-transformers:提供嵌入模型(将文本转成向量)
  • chromadb:轻量级向量数据库(用于存储和检索文档片段)
2️⃣ 准备一个示例文档(5 分钟)

创建一个 my_knowledge.txt 文件,写入一些你自己的项目介绍。例如:

复制代码
我的后端项目使用了 Spring Boot 3.0,数据库是 MySQL 8.0,缓存用 Redis,消息队列用 RabbitMQ。项目部署在阿里云 Kubernetes 集群上。
3️⃣ 编写 RAG API 代码(30 分钟)

app.py 的基础上,增加一个 /rag 端点。你可以复制以下完整代码到 app_rag.py

python 复制代码
# app_rag.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from modelscope import AutoModelForCausalLM, AutoTokenizer
from sentence_transformers import SentenceTransformer
import chromadb
from chromadb.utils import embedding_functions
import torch
import time
import os

# ---------- 1. 加载大模型 ----------
print("Loading LLM...")
model_id = 'qwen/Qwen2-0.5B-Instruct'
llm = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
print("LLM loaded.")

# ---------- 2. 加载嵌入模型 ----------
print("Loading embedding model...")
embedder = SentenceTransformer('BAAI/bge-small-zh')  # 中文嵌入模型,约 33MB
print("Embedding model loaded.")

# ---------- 3. 初始化向量数据库 ----------
chroma_client = chromadb.Client()
# 使用嵌入函数
embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(model_name='BAAI/bge-small-zh')
collection = chroma_client.create_collection(name="docs", embedding_function=embedding_fn)

# ---------- 4. 加载并切分你的文档 ----------
def load_and_chunk(file_path, chunk_size=200):
    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()
    # 简单按字符数切分,也可以按句号分割
    chunks = [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
    return chunks

# 请将你的文档放在同目录下的 my_knowledge.txt
if os.path.exists("my_knowledge.txt"):
    chunks = load_and_chunk("my_knowledge.txt", chunk_size=200)
    # 存入 ChromaDB
    for i, chunk in enumerate(chunks):
        collection.add(
            documents=[chunk],
            ids=[f"doc_{i}"]
        )
    print(f"Loaded {len(chunks)} document chunks.")
else:
    print("Warning: my_knowledge.txt not found. RAG will work but no context.")

app = FastAPI()

class RAGRequest(BaseModel):
    question: str
    top_k: int = 3
    max_tokens: int = 200
    temperature: float = 0.7

@app.post("/rag")
async def rag_endpoint(request: RAGRequest):
    # 1. 检索相关文档片段
    results = collection.query(query_texts=[request.question], n_results=request.top_k)
    retrieved_docs = results['documents'][0] if results['documents'] else []
    
    # 2. 构造增强的 prompt
    context = "\n".join(retrieved_docs) if retrieved_docs else "无相关文档。"
    prompt = f"""基于以下已知信息,简洁地回答用户的问题。如果无法从已知信息中找到答案,请说"根据已有信息无法回答"。
已知信息:
{context}
问题:{request.question}
回答:"""
    
    # 3. 调用大模型生成
    messages = [{"role": "user", "content": prompt}]
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    inputs = tokenizer(text, return_tensors="pt")
    with torch.no_grad():
        outputs = llm.generate(
            inputs.input_ids,
            max_new_tokens=request.max_tokens,
            temperature=request.temperature,
            do_sample=True
        )
    full_response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    assistant_response = full_response.split("assistant")[-1].strip()
    
    return {
        "answer": assistant_response,
        "retrieved_docs": retrieved_docs,
        "inference_time": time.time()
    }

@app.get("/health")
def health():
    return {"status": "ok"}

由于我的本地下载不下来这个模型,所以更换了其他模型

python 复制代码
# app_rag.py
from fastapi import FastAPI
from pydantic import BaseModel
from modelscope import AutoModelForCausalLM, AutoTokenizer, snapshot_download
from sentence_transformers import SentenceTransformer
import chromadb
from chromadb.utils import embedding_functions
import torch
import time
import os

# ---------- 1. 加载LLM ----------
print("Loading LLM...")
model_id = 'qwen/Qwen2-0.5B-Instruct'
llm = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
print("LLM loaded.")

# ---------- 2. 加载Embedding模型 ----------
print("Loading embedding model from ModelScope...")
# 确保这里使用的model_id是正确的
embedding_model_id = 'BAAI/bge-small-zh-v1.5'
# 下载模型到本地,如果已经下载过,snapshot_download会直接使用缓存
local_model_dir = snapshot_download(embedding_model_id)
# 使用 SentenceTransformer 加载下载好的本地模型
embedder = SentenceTransformer(local_model_dir)
print("Embedding model loaded.")

# ---------- 3. 初始化向量数据库 ----------
# 注意:为了演示,这里使用了In-Memory的Client,服务重启后数据会丢失。
# 如需持久化,请使用 chromadb.PersistentClient(path="./chroma_db")
chroma_client = chromadb.Client()
collection = chroma_client.create_collection(name="docs")

# ---------- 4. 加载并切分你的文档 ----------
def load_and_chunk(file_path, chunk_size=200):
    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()
    chunks = [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
    return chunks

# 请确保同目录下有一个 my_knowledge.txt 文件
if os.path.exists("my_knowledge.txt"):
    chunks = load_and_chunk("my_knowledge.txt", chunk_size=200)
    for i, chunk in enumerate(chunks):
        # 手动生成向量
        embedding = embedder.encode(chunk).tolist()
        collection.add(
            embeddings=[embedding],
            documents=[chunk],
            ids=[f"doc_{i}"]
        )
    print(f"Loaded {len(chunks)} document chunks into ChromaDB.")
else:
    print("Warning: my_knowledge.txt not found. RAG will work but no context.")

app = FastAPI()

class RAGRequest(BaseModel):
    question: str
    top_k: int = 3
    max_tokens: int = 200
    temperature: float = 0.7

@app.post("/rag")
async def rag_endpoint(request: RAGRequest):
    # 1. 检索:将问题转为向量,在ChromaDB中搜索
    question_embedding = embedder.encode(request.question).tolist()
    results = collection.query(query_embeddings=[question_embedding], n_results=request.top_k)
    retrieved_docs = results['documents'][0] if results['documents'] else []

    # 2. 构造增强的 prompt
    context = "\n".join(retrieved_docs) if retrieved_docs else "无相关文档。"
    prompt = f"""基于以下已知信息,简洁地回答用户的问题。如果无法从已知信息中找到答案,请说"根据已有信息无法回答"。
已知信息:
{context}
问题:{request.question}
回答:"""

    # 3. 调用大模型生成
    messages = [{"role": "user", "content": prompt}]
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    inputs = tokenizer(text, return_tensors="pt")
    with torch.no_grad():
        outputs = llm.generate(
            inputs.input_ids,
            max_new_tokens=request.max_tokens,
            temperature=request.temperature,
            do_sample=True
        )
    full_response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    assistant_response = full_response.split("assistant")[-1].strip()

    return {
        "answer": assistant_response,
        "retrieved_docs": retrieved_docs,
        "inference_time": time.time()
    }

@app.get("/health")
def health():
    return {"status": "ok"}
4️⃣ 启动新服务并测试(10 分钟)
bash 复制代码
uvicorn app_rag:app --reload --port 8000

然后另开终端测试:

python 复制代码
import requests
resp = requests.post("http://127.0.0.1:8000/rag", json={"question": "我的项目用了什么数据库?"})
print(resp.json()["answer"])

你会看到模型基于 my_knowledge.txt 中的内容回答 "MySQL 8.0"。


🧭 后续计划(第4~7天)

  • 第四天:让 RAG 支持多文档(上传 PDF、Word)和更好的文本切分(按段落/语义)。
  • 第五天:增加对话记忆(多轮对话)。
  • 第六天:优化推理速度(使用 vLLM 或量化模型)。
  • 第七天:写一个简单的 Gradio 前端界面,展示你的完整作品。

你也可以根据自己的节奏调整。目前你已完成了 环境+API+RAG基础,这在简历上已经是一个很完整的 AI 工程化项目了。


💎 今天的核心产出

  • 一个能基于私有文档问答的 RAG API。
  • 理解了"检索"+"生成"的基本流程。
  • 为简历增加了一条亮点:"基于 Qwen 和 ChromaDB 实现 RAG 系统,提升问答准确性"。
相关推荐
诗词在线3 分钟前
江上渔者|原文+注释+译文+赏析+考点
学习
宸津-代码粉碎机5 分钟前
Spring AI 高危CVE漏洞深度复盘|生产禁跑版本汇总+临时防御+修复方案
java·大数据·人工智能·python·spring
不要生病了13 分钟前
MinD-Vis:用稀疏掩码预训练和双条件扩散从 fMRI 重建视觉图像
人工智能·计算机视觉·脑机接口
天远API15 分钟前
零信任架构实战:基于天远全能消金报告构建自动化消费分期网关
运维·人工智能·架构·自动化
weifont17 分钟前
已为你安排相关自动化工具,请查收
人工智能
luckystar513~18 分钟前
每日AI资讯(2026-09-13)
人工智能·ai·ai资讯·每日资讯
syounger22 分钟前
SAP认证走过30年:考试开始允许使用AI,证书价值正在被重新定义
人工智能
逆境不可逃31 分钟前
Pi Agent 学习笔记:不同模型如何接入同一套工具循环
笔记·学习
YOLO数据集集合36 分钟前
洪水场景建筑与植被检测数据集 | 洪水检测 建筑识别 植被监测 灾情评估 目标检测9073期
人工智能·目标检测·机器学习·计算机视觉·目标跟踪·无人机视角·灾害救援
人工智能AI技术40 分钟前
AI Agent 接入MCP:不用重复造轮子,外部工具一键接入
人工智能