从零开始做一个高校课程资料 AI Agent 问答系统
本系列博客将带你从零开始,使用 Python + FastAPI + RAG + AI Agent 搭建一个面向 Java Web 课程资料问答与智能学习辅助 的后端系统,涵盖 课程资料上传、文档解析、文本切块、本地检索、问答生成、Agent 工具调用、执行链路追踪、运行记录回放 等实战场景。无论你是 AI 应用初学者,还是希望掌握 RAG 与 Agent 工程化落地的开发者,都能通过本教程理解一个教学场景 AI Agent 系统的规划、开发、测试与升级过程。
内容主要包括:
✅ 基础架构搭建:FastAPI 后端项目初始化、接口路由设计、Pydantic Schema、SQLAlchemy 数据模型、SQLite 本地数据库配置。
✅ 课程资料知识库构建:支持 Markdown、TXT、代码文件、PDF、Word、PPTX 等资料上传,完成文档解析、文本切块、来源元数据保存与资料入库。
✅ RAG 问答流程实现:基于课程资料 chunk 进行本地检索,构造 grounded prompt,生成带引用来源的回答,并在资料不足时安全拒答,降低大模型幻觉。
✅ LLM 接入与兜底机制:支持 OpenAI-compatible 接口,可对接 OpenAI、Ollama、本地大模型等,同时保留 stub fallback,保证系统在无模型环境下也能稳定测试。
✅ AI Agent 能力升级:从简单 Agent Harness 逐步升级为具备 Planner、Executor、Tool Registry、Memory、Verifier 的课程学习 Agent,实现任务规划、工具调用、短期记忆、结果校验和执行追踪。
✅ Agent 执行记录与回放:新增 AgentRun、AgentStep、AgentToolCall 数据模型,保存每次 Agent 的执行计划、工具调用、引用来源、校验结果,并提供运行历史查询接口。
✅ 接口调试与测试验证:通过 Swagger UI 手动测试文档上传、问答、Agent 运行、工具列表和运行记录接口,并使用 pytest 编写自动化测试,覆盖 RAG、Agent、工具、记忆和校验流程。
✅ 系统演进与工程实践:从 MVP 到 v1.2.0 Agent 升级,逐步讲解如何控制范围、拆分模块、保持接口兼容、设计可测试代码,并为后续前端 Trace 面板、向量检索、LangGraph 工作流和 SaaS 化扩展打基础。
SQLite
SQLite 和 MySQL 本质上都是关系型数据库,都用 SQL,都有表、字段、行、索引、事务这些概念;SQLite 更轻量,但它和 MySQL 的定位差别很大。
核心区别在这里:
| 对比项 | SQLite | MySQL |
|---|---|---|
| 部署方式 | 一个本地 .db 文件 |
一个独立数据库服务 |
| 是否需要启动服务 | 不需要 | 需要 MySQL Server |
| 连接方式 | 程序直接读写文件 | 程序通过网络/本地端口连接数据库服务 |
| 适合场景 | 小型项目、本地开发、桌面应用、测试、原型 | Web 后端生产环境、多用户、高并发、复杂权限 |
| 并发能力 | 读多写少可以,写并发较弱 | 支持更强并发 |
| 运维成本 | 很低,复制文件就能备份 | 需要用户、权限、备份、服务管理 |
| 权限系统 | 基本没有独立用户权限 | 有完整用户和权限管理 |
| 数据文件 | 单个 .db 文件 |
多个数据文件,由服务管理 |
你可以把它们理解成:
text
SQLite = 数据库功能嵌进应用里,数据存在一个本地文件
MySQL = 单独跑一个数据库服务器,应用通过连接访问它
在你当前这个项目里:
python
database_url: str = "sqlite:///./rag_assistant.db"
意思是用 SQLite 文件:
text
rag_assistant.db
这很适合当前阶段:
- 本地开发方便
- 不用安装 MySQL
- 不用建库建用户
- 测试简单
- 上传资料、chunks、聊天记录都能存
但如果以后变成正式系统,比如多人同时使用、部署到服务器、数据量变大,就更适合换成 MySQL/PostgreSQL。
SQLAlchemy 的好处是:项目代码大部分不用大改,只要把配置从:
env
DATABASE_URL=sqlite:///./rag_assistant.db
换成类似:
env
DATABASE_URL=mysql+pymysql://user:password@localhost:3306/rag_assistant
当然还需要安装 MySQL 驱动、建库、处理迁移等。
一句话总结:
text
SQLite 和 MySQL 都是关系型数据库;SQLite 更像"本地文件数据库",MySQL 更像"数据库服务器"。
查看SQLite 数据库
默认配置在:
E:\CodeX\agent\backend\app\core\config.py
python
database_url: str = "sqlite:///./rag_assistant.db"
所以如果你是在 E:\CodeX\agent\backend 目录启动后端,实际数据库文件就是:
text
E:\CodeX\agent\backend\rag_assistant.db
方式 1:用 PyCharm 查看
PyCharm Professional 支持数据库工具窗口;Community 版通常不完整支持。
如果你是 Professional:
- 打开右侧或左侧的
Database工具窗口 - 点击
+ - 选择:
text
Data Source -> SQLite
- 选择数据库文件:
text
E:\CodeX\agent\backend\rag_assistant.db
- 连接后可以看到表:
text
documents
chunks
users
chat_messages

方式 2:用 DB Browser for SQLite
这是最直观的方式,适合新手看数据。
下载:
打开后选择:
text
Open Database
然后打开:
text
E:\CodeX\agent\backend\rag_assistant.db
你可以直接看表数据,比如:
documents:上传的资料记录chunks:资料解析后的文本切片chat_messages:问答记录users:用户表,目前可能没怎么用
方式 3:命令行查看
如果你安装了 sqlite3 命令,可以在 PowerShell 里执行:
powershell
cd E:\CodeX\agent\backend
sqlite3 rag_assistant.db
进入 SQLite 后:
sql
.tables
查看所有表。
sql
.schema documents
查看 documents 表结构。
sql
select * from documents;
查看上传资料记录。
sql
select id, document_id, chunk_type, source_title, source_page, substr(content, 1, 100) from chunks;
查看资料切片,内容只显示前 100 个字符。
退出:
sql
.quit
方式 4:用 Python 临时查看
在 backend 目录执行:
powershell
python -c "import sqlite3; conn=sqlite3.connect('rag_assistant.db'); print(conn.execute('select name from sqlite_master where type=\"table\"').fetchall())"
这会列出当前数据库里的表。
init_db() 具体做了什么
在:
E:\CodeX\agent\backend\app\core\database.py
核心代码是:
python
Base.metadata.create_all(bind=engine)
意思是:
根据 SQLAlchemy 里定义的模型类创建数据库表。
它会导入这些模型:
python
import app.models.chat
import app.models.chunk
import app.models.document
import app.models.user
然后创建对应表。
注意:create_all() 只会创建不存在的表,不会自动帮你做复杂表结构迁移。比如你以后修改了字段,通常要用 Alembic 这类迁移工具,而不是只靠 init_db()。
SQLAlchemy
SQLAlchemy 是 Python 里很常用的数据库工具库。它的作用是:让 Python 程序更方便地操作数据库。
你当前项目里用它来操作 SQLite。
可以把 SQLAlchemy 理解成两层东西:
1. SQL 工具层
它可以帮你生成和执行 SQL。
比如原始 SQL 可能是:
sql
select * from documents where id = 1;
用 SQLAlchemy 可以写成类似:
python
session.get(Document, 1)
或者:
python
select(Document).where(Document.id == 1)
SQLAlchemy 会帮你转换成对应数据库能执行的 SQL。
2. ORM 层
ORM 是 Object Relational Mapping,中文一般叫"对象关系映射"。
简单说就是:
text
Python 类 <-> 数据库表
Python 对象 <-> 数据库行
类属性 <-> 表字段
项目里的这个类:
python
class Document(Base):
__tablename__ = "documents"
id = mapped_column(primary_key=True)
filename = mapped_column(String(255))
file_type = mapped_column(String(50))
storage_path = mapped_column(Text)
status = mapped_column(String(30))
对应数据库里的表:
text
documents
类里的字段对应表字段:
text
Document.id -> documents.id
Document.filename -> documents.filename
Document.file_type -> documents.file_type
Document.storage_path -> documents.storage_path
Document.status -> documents.status
所以你可以不用手写:
sql
insert into documents ...
而是写:
python
document = Document(
filename="login-lab.md",
file_type="md",
storage_path="uploads/xxx-login-lab.md",
status="uploaded",
)
session.add(document)
session.commit()
SQLAlchemy 会帮你生成并执行 INSERT SQL。
在当前项目里,SQLAlchemy 主要负责:
text
1. 连接 SQLite 数据库
2. 根据模型类创建表
3. 保存上传资料记录
4. 保存资料 chunks
5. 查询资料列表
6. 删除资料和对应 chunks
7. 保存/查询聊天记录
例如:
python
engine = create_engine(settings.database_url)
表示创建数据库连接引擎。
python
SessionLocal = sessionmaker(bind=engine)
表示创建数据库会话工厂。
python
Base.metadata.create_all(bind=engine)
表示根据模型类创建数据库表。
一句话总结:
text
SQLAlchemy 是 Python 操作数据库的工具;它让你用 Python 类和对象来管理数据库表和记录,而不是到处手写 SQL。
SQLAlchemy ≈ JDBC + MyBatis / JPA 的一部分能力