学习顺序说明:本文是AI学习路线的第12篇,也是最终篇,建议按顺序学习:
- … → 11 AI Agent → 12 项目实战(本文)
本文将综合运用前面学习的知识,从零构建一个完整的AI应用项目。
项目概述
项目目标
构建一个智能文档助手系统,具备以下功能:
- 文档上传和解析
- 基于RAG的智能问答
- 多轮对话记忆
- Web界面和API接口
技术架构
┌─────────────────────────────────────────────────────────────┐
│ 前端 (Vue/React) │
├─────────────────────────────────────────────────────────────┤
│ API层 (FastAPI) │
├─────────────────────────────────────────────────────────────┤
│ 文档处理服务 │ RAG检索服务 │ 对话服务 │ 向量数据库 │
├─────────────────────────────────────────────────────────────┤
│ 大语言模型 │
└─────────────────────────────────────────────────────────────┘
第一部分:项目结构
1.1 目录结构
ai-doc-assistant/
├── backend/
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py # FastAPI入口
│ │ ├── config.py # 配置
│ │ ├── routers/ # API路由
│ │ │ ├── chat.py
│ │ │ └── documents.py
│ │ ├── services/ # 业务逻辑
│ │ │ ├── document_processor.py
│ │ │ ├── rag_service.py
│ │ │ └── llm_service.py
│ │ └── models/ # 数据模型
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── src/
│ └── package.json
├── docker-compose.yml
└── README.md
1.2 依赖配置
# requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
python-multipart==0.0.6
langchain==0.1.0
langchain-openai==0.0.2
langchain-community==0.0.12
chromadb==0.4.22
pypdf==3.17.4
python-docx==1.1.0
openai==1.6.1
pydantic==2.5.2
pydantic-settings==2.1.0
第二部分:后端实现
2.1 配置管理
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
# API配置
API_TITLE: str = "AI文档助手"
API_VERSION: str = "1.0.0"
# 模型配置
OPENAI_API_KEY: str
LLM_MODEL: str = "gpt-4"
EMBEDDING_MODEL: str = "text-embedding-ada-002"
# 向量数据库
CHROMA_PERSIST_DIR: str = "./chroma_db"
# 文档配置
UPLOAD_DIR: str = "./uploads"
CHUNK_SIZE: int = 500
CHUNK_OVERLAP: int = 50
class Config:
env_file = ".env"
settings = Settings()
2.2 文档处理服务
# services/document_processor.py
import os
from typing import List
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader
from langchain.schema import Document
class DocumentProcessor:
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
)
def load_document(self, file_path: str) -> List[Document]:
"""加载文档"""
ext = os.path.splitext(file_path)[1].lower()
if ext == '.pdf':
loader = PyPDFLoader(file_path)
elif ext in ['.docx', '.doc']:
loader = Docx2txtLoader(file_path)
else:
# 默认作为文本处理
with open(file_path, 'r', encoding='utf-8') as f:
text = f.read()
return [Document(page_content=text, metadata={"source": file_path})]
return loader.load()
def split_documents(self, documents: List[Document]) -> List[Document]:
"""切分文档"""
return self.text_splitter.split_documents(documents)
def process_file(self, file_path: str) -> List[Document]:
"""处理单个文件"""
documents = self.load_document(file_path)
return self.split_documents(documents)
2.3 RAG服务
# services/rag_service.py
from typing import List, Optional
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
class RAGService:
def __init__(self, persist_directory: str, embedding_model: str, llm_model: str):
self.embeddings = OpenAIEmbeddings(model=embedding_model)
self.llm = ChatOpenAI(model=llm_model, temperature=0.7)
self.vectorstore = Chroma(
persist_directory=persist_directory,
embedding_function=self.embeddings
)
self.memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
self.prompt_template = """
你是一个专业的文档助手。请根据以下上下文回答问题。
如果上下文中没有相关信息,请明确说明,不要编造答案。
上下文:
{context}
问题:{question}
回答:
"""
def add_documents(self, documents: List) -> None:
"""添加文档到向量库"""
self.vectorstore.add_documents(documents)
def search(self, query: str, k: int = 4) -> List:
"""检索相关文档"""
return self.vectorstore.similarity_search(query, k=k)
def query(self, question: str) -> dict:
"""问答"""
# 检索相关文档
docs = self.search(question)
context = "\n\n".join([doc.page_content for doc in docs])
# 构建prompt
prompt = PromptTemplate.from_template(self.prompt_template)
# 生成回答
chain = prompt | self.llm
response = chain.invoke({"context": context, "question": question})
return {
"answer": response.content,
"sources": [{"content": doc.page_content[:200],
"source": doc.metadata.get("source", "unknown")}
for doc in docs]
}
def chat(self, question: str) -> dict:
"""带记忆的对话"""
# 获取对话历史
chat_history = self.memory.load_memory_variables({}).get("chat_history", [])
# 检索
docs = self.search(question)
context = "\n\n".join([doc.page_content for doc in docs])
# 生成回答
prompt = PromptTemplate.from_template(self.prompt_template)
chain = prompt | self.llm
response = chain.invoke({"context": context, "question": question})
# 保存到记忆
self.memory.save_context({"input": question}, {"output": response.content})
return {
"answer": response.content,
"sources": [{"content": doc.page_content[:200]} for doc in docs]
}
2.4 API路由
# routers/documents.py
from fastapi import APIRouter, UploadFile, File, HTTPException
from typing import List
import os
import uuid
router = APIRouter(prefix="/documents", tags=["documents"])
@router.post("/upload")
async def upload_document(file: UploadFile = File(...)):
"""上传文档"""
# 保存文件
file_id = str(uuid.uuid4())
file_path = f"./uploads/{file_id}_{file.filename}"
os.makedirs("./uploads", exist_ok=True)
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
# 处理文档
from ..services.document_processor import DocumentProcessor
from ..services.rag_service import RAGService
from ..config import settings
processor = DocumentProcessor(
chunk_size=settings.CHUNK_SIZE,
chunk_overlap=settings.CHUNK_OVERLAP
)
documents = processor.process_file(file_path)
# 添加到向量库
rag = RAGService(
persist_directory=settings.CHROMA_PERSIST_DIR,
embedding_model=settings.EMBEDDING_MODEL,
llm_model=settings.LLM_MODEL
)
rag.add_documents(documents)
return {
"file_id": file_id,
"filename": file.filename,
"chunks": len(documents)
}
# routers/chat.py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter(prefix="/chat", tags=["chat"])
class ChatRequest(BaseModel):
question: str
use_memory: bool = True
class ChatResponse(BaseModel):
answer: str
sources: list
@router.post("/query", response_model=ChatResponse)
async def query(request: ChatRequest):
"""问答接口"""
from ..services.rag_service import RAGService
from ..config import settings
rag = RAGService(
persist_directory=settings.CHROMA_PERSIST_DIR,
embedding_model=settings.EMBEDDING_MODEL,
llm_model=settings.LLM_MODEL
)
if request.use_memory:
result = rag.chat(request.question)
else:
result = rag.query(request.question)
return ChatResponse(**result)
2.5 主应用
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .config import settings
from .routers import documents, chat
app = FastAPI(
title=settings.API_TITLE,
version=settings.API_VERSION
)
# CORS配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(documents.router)
app.include_router(chat.router)
@app.get("/")
async def root():
return {"message": "AI文档助手API", "version": settings.API_VERSION}
@app.get("/health")
async def health():
return {"status": "healthy"}
第三部分:前端实现
3.1 Vue组件示例
<!-- ChatView.vue -->
<template>
<div class="chat-container">
<div class="messages">
<div v-for="msg in messages" :key="msg.id"
:class="['message', msg.role]">
<div class="content"></div>
<div v-if="msg.sources" class="sources">
<div v-for="source in msg.sources" :key="source">
</div>
</div>
</div>
</div>
<div class="input-area">
<el-input
v-model="question"
placeholder="输入您的问题..."
@keyup.enter="sendMessage"
/>
<el-button type="primary" @click="sendMessage">发送</el-button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const messages = ref([])
const question = ref('')
const sendMessage = async () => {
if (!question.value.trim()) return
// 添加用户消息
messages.value.push({
id: Date.now(),
role: 'user',
content: question.value
})
const userQuestion = question.value
question.value = ''
try {
const response = await axios.post('/api/chat/query', {
question: userQuestion,
use_memory: true
})
messages.value.push({
id: Date.now(),
role: 'assistant',
content: response.data.answer,
sources: response.data.sources
})
} catch (error) {
console.error('Error:', error)
}
}
</script>
第四部分:部署
4.1 Docker配置
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
4.2 Docker Compose
# docker-compose.yml
version: '3.8'
services:
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./uploads:/app/uploads
- ./chroma_db:/app/chroma_db
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
4.3 启动服务
# 开发环境
uvicorn app.main:app --reload
# Docker部署
docker-compose up -d
项目扩展方向
功能增强
- 支持更多文档格式(Excel、PPT等)
- 多用户和权限管理
- 文档版本管理
- 流式输出支持
性能优化
- 文档处理异步化
- 向量检索优化
- 缓存机制
- 负载均衡
高级功能
- 多模态支持(图片理解)
- Agent自动问答
- 知识图谱集成
学习总结
恭喜你完成了AI学习路线的全部内容!
学习路线回顾
01 入门基础 → 02 机器学习 → 03 深度学习 → 04 NLP基础
→ 05 Transformer进阶 → 06 大模型应用 → 07 RAG系统
→ 08 AI工具链 → 09 计算机视觉 → 10 多模态大模型
→ 11 AI Agent → 12 项目实战
后续发展建议
- 深入专精:选择一个方向深入研究
- 开源贡献:参与开源项目
- 持续学习:关注最新论文和技术
- 实践项目:持续构建个人项目作品集
上一篇:11 AI Agent智能体 - 自主决策与工具调用
最后更新: 2026年4月10日
本文综合运用了前面所有文档的知识,祝你在AI领域取得成功!