从零开始做一个高校课程资料 AI Agent 问答系统(四)SQLite 数据库

从零开始做一个高校课程资料 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:

  1. 打开右侧或左侧的 Database 工具窗口
  2. 点击 +
  3. 选择:
text 复制代码
Data Source -> SQLite
  1. 选择数据库文件:
text 复制代码
E:\CodeX\agent\backend\rag_assistant.db
  1. 连接后可以看到表:
text 复制代码
documents
chunks
users
chat_messages

方式 2:用 DB Browser for SQLite

这是最直观的方式,适合新手看数据。

下载:

https://sqlitebrowser.org/

打开后选择:

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 的一部分能力