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 系统,提升问答准确性"。
相关推荐
汤姆小白1 小时前
01-环境搭建与项目导览
人工智能·python·机器学习·numpy
喜欢就别3 小时前
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (2)--- On-Policy Distillation
人工智能·笔记
糯米导航5 小时前
AI 视觉回归实战:截图对比不是“找不同”,如何让智能差异分析真正服务 UI 质量
人工智能·ui·回归
科技圈观察6 小时前
2026年好伴AI医疗专用大模型应用梳理与梯队参考
人工智能
jkyy20146 小时前
深耕AI健康医疗数据智库,赋能企业构建主动健康管理新生态
大数据·人工智能·健康医疗
cd_949217216 小时前
3D角色自动绑骨怎么做?用V2Fun完成建模、绑定、动作和导出
人工智能·3d
瑞禧生物tech7 小时前
SH-PEG-Biotin巯基-聚乙二醇-生物素 HS-PEG-Bio 深度解析
人工智能
QYR-分析7 小时前
机器人安全控制器行业高速扩容 本土替代迎来全新发展窗口期
人工智能·安全·机器人
xian_wwq7 小时前
【学习笔记】模型权重管理:Safetensors与私有化Hub(23/35)
笔记·学习
冬奇Lab7 小时前
MCP 系列(06):MCP vs Function Calling——用数据说话的选型指南
人工智能