knowledge-base:基于 FastAPI 与 DeepSeek 的 RAG 个人知识库项目

一个有关个人知识库的项目。

分支2Tags0

VON AI_Agent

VON AI_Agent 是一个面向大学生和计算机相关人员的可对话式个人知识库。系统将公开的 Markdown 文章读取、切分并转换为向量;访客提问时,先检索相关片段,再由 OpenAI 兼容模型根据资料生成回答,并返回可点击的原文来源。

1. 当前进度

  • 已导入 7 篇 CSDN 公开文章和 1 篇脱敏后的 AI 基础指南;本地 TXT/PDF 默认按私有资料导入,个人资料不纳入版本库。
  • 已完成 Markdown 元数据校验、按标题切分和纯图片片段过滤。
  • 公开样例当前生成 139 个可检索片段和对应的 512 维 Embedding;个人资料另生成本地私有索引,不写入公开构建产物。
  • 已支持 CSDN 文章下载、正文清理、重复检查和索引登记。
  • 已支持 TXT、PDF 文件导入,并将本地文件登记到统一索引。
  • 资料 CRUD、TXT/PDF/CSDN 导入和 CSDN 同步已切换到 SQLite,旧 CSV 作为迁移来源保留。
  • 已支持检查已登记 CSDN 文章的内容变化并同步本地文件。
  • 已支持增量生成 Embedding,只为新增或修改的片段计算向量。
  • 已完成本地相似度检索和 OpenAI 兼容模型 RAG 问答。
  • 已提供 FastAPI GET /health 和 POST /ask 接口。
  • 已取消访客问答次数限制,网页端固定显示“不限次数”。
  • 访客可以从网页下拉框选择当前配置的模型;模型列表由 LLM_MODELS 或内置常用列表提供,未指定时使用 .env 中的 LLM_MODEL。
  • 网页模型菜单按品牌展示 Logo 和模型能力;图片、音频、视频模型已内置并标注能力,但当前知识库问答只支持文本 Chat Completions 模型。
  • 已提供网页问答界面,可分区展示模型提示词与回答、Token 用量和可点击的知识来源,并可分别复制问题、提示词和回答。
  • 资料不足时返回“资料中没有足够依据”,不输出来源。
  • 隐私保护开启时不加载私有索引;个人资料问题不进入检索和模型调用,也不输出来源。

2. 目录说明

knowledge-base/
├── app/
│   ├── main.py                  # 创建应用、挂载静态资源、注册路由
│   ├── api/                     # HTTP 路由层
│   │   ├── chat.py              # /ask、/ask/stream
│   │   ├── materials.py         # 资料列表、上传、修改、删除和重建索引
│   │   ├── documents.py         # 本地 Markdown 阅读
│   │   └── system.py            # /health、/models
│   ├── schemas/                 # API 请求和响应模型
│   │   ├── chat.py              # 问答、历史、来源和 Token 结构
│   │   └── materials.py         # 资料摘要结构
│   ├── services/                # 应用流程编排
│   │   ├── chat_service.py      # 单次及流式问答流程
│   │   ├── material_service.py  # 本地资料导入边界
│   │   └── indexing_service.py  # 切片、向量化与重建边界
│   ├── domain/                  # 不依赖 HTTP 的业务规则
│   │   ├── retrieval.py         # 上下文、来源与本地链接
│   │   ├── privacy.py           # 隐私问题识别
│   │   ├── history.py           # 历史清理和追问补全
│   │   └── prompts.py           # 模型提示词
│   ├── infrastructure/          # 外部模型、文件和向量设施
│   │   ├── llm_client.py        # OpenAI 兼容模型调用
│   │   ├── material_database.py # 资料 SQLite 仓储与 CSV 迁移
│   │   ├── vector_store.py      # 向量加载与相似度检索
│   │   └── repositories.py      # Markdown 文件读取
│   ├── quota.py                 # 旧版 SQLite 额度模块(当前接口不启用)
│   └── static/                  # 网页、样式、脚本和图片
├── data/
│   ├── documents/               # 清理后的 Markdown 知识源
│   ├── raw/csdn/                # CSDN 原始 HTML,仅用于追溯
│   ├── chunks/                  # 公开/私有切分结果和 Embedding
│   ├── knowledge_base.db        # 本机资料元数据库
│   └── index.csv                # 旧文章登记表,保留作迁移源
├── scripts/
│   ├── read_documents.py        # Markdown 仓储兼容命令入口
│   ├── chunk_documents.py       # 按标题切分文章
│   ├── embed_chunks.py          # 生成向量
│   ├── search_chunks.py         # 向量检索兼容命令入口
│   ├── ask_knowledge.py         # 问答服务兼容命令入口
│   ├── import_url.py            # 导入 CSDN 文章
│   ├── import_file.py           # 导入本地 TXT 文件
│   ├── import_pdf.py            # 提取并导入本地 PDF 文件
│   ├── sync_sources.py          # 检查并同步 CSDN 文章更新
│   ├── rebuild_knowledge.py     # 一键切片并增量更新向量
│   ├── migrate_materials_to_sqlite.py # 将旧资料索引导入 SQLite
│   └── test_deepseek.py         # OpenAI 兼容模型连通性测试(保留旧文件名)
├── docs/
│   └── API.md                   # 已实现与规划中的接口说明
├── .env.example                 # 环境变量模板
└── requirements.txt             # Python 依赖

3. 环境准备

项目建议使用 Python 3.12 或更高版本,并在项目根目录执行命令。

python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

复制环境变量模板并填写自己的模型服务密钥。阿里云 Token Plan Lite 可以直接使用模板中的地址:

cp .env.example .env

.env 已加入 .gitignore,不要把真实 API Key 提交到仓库。

默认配置使用阿里云 Token Plan 的 OpenAI 兼容接口:

LLM_API_KEY=你的阿里云 Token Plan API Key
LLM_BASE_URL=https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
LLM_MODEL=qwen3.8-max

LLM_MODELS 是可选项,用逗号分隔模型 ID。未设置时,代码内置 Token Plan Lite 控制台列出的全部模型; 模型 ID 以 Token Plan 控制台显示的名称为准。 如果仍使用旧的 DeepSeek 配置,程序会兼容读取 DEEPSEEK_API_KEY、DEEPSEEK_BASE_URL 和 DEEPSEEK_MODEL。

4. 导入公开 CSDN 文章

导入一篇新的 CSDN 文章:

python scripts/import_url.py \
  "https://blog.csdn.net/用户名/article/details/文章ID" \
  --category "项目经历"

导入脚本会自动完成:

  1. 校验文章 URL,只允许标准的 CSDN HTTPS 地址;
  2. 下载原始 HTML 到 data/raw/csdn/;
  3. 提取标题和正文,并转换为 Markdown;
  4. 保存 Markdown 到 data/documents/;
  5. 将文章元数据登记到 SQLite。

相同 URL 不会重复下载或重复登记。导入时增加 --rebuild,可以在登记完成后自动重建知识库:

python scripts/import_url.py \
  "https://blog.csdn.net/用户名/article/details/文章ID" \
  --category "项目经历" \
  --rebuild

5. 导入本地 TXT/PDF 文件

导入 TXT 文件:

python scripts/import_file.py \
  "/path/to/note.txt" \
  --category "学习资料"

导入 PDF 文件:

python scripts/import_pdf.py \
  "/path/to/document.pdf" \
  --category "个人资料"

两个脚本都会把内容转换为带 YAML 元数据的 Markdown,保存到 data/documents/,并将资料元数据登记到 SQLite。本地文件默认标记为 private,不会进入 公开检索构建产物;本地服务会将它们写入独立的私有索引,仅在本机问答时按需读取。确认内容已脱敏后,才手动将文档可见性改为 public。标题默认使用原文件名,也可以通过 --title 指定。PDF 会在正文中保留 ## 第 N 页 标记;如果提取文字少于 100 个字符, 脚本会提示可能是扫描版或图片型 PDF。

6. 同步 CSDN 文章

先用试运行检查哪些文章发生了变化:

python scripts/sync_sources.py --dry-run

确认结果后执行正式同步:

python scripts/sync_sources.py

同步脚本会从 SQLite 读取已登记的 CSDN 文章,本地 TXT/PDF 不参与同步,个人资料也不提交到版本库。 单篇网络请求失败不会覆盖原文件,脚本会继续检查其他文章并在最后报告失败数量。

7. 重新构建知识库

当 data/documents/ 中的 Markdown 有新增或修改时,按顺序执行:

python scripts/read_documents.py
python scripts/chunk_documents.py
python scripts/embed_chunks.py

也可以使用统一入口:

python scripts/rebuild_knowledge.py

chunk_documents.py 会按 visibility 将文档分别写入 chunks.jsonl 和 private_chunks.jsonl。embed_chunks.py 对两套切片分别生成向量,输出到 embeddings.jsonl 和 private_embeddings.jsonl;缓存键相同的片段会直接复用旧向量,只有新增或修改的片段才会加载模型并重新计算。

private_*.jsonl 已加入 .gitignore,只保留在本机。公开检索命令默认不读取私有索引;网页问答只有在个人知识库开启且隐私保护关闭时才追加读取私有索引。此时涉及个人资料的问题会优先保留私有片段,避免被公开文章挤出检索结果。

8. 命令行问答

python scripts/ask_knowledge.py "我为什么还要学习编程基础?"

回答只使用知识库资料。正常回答会显示文章标题、章节和原文链接;资料不足或隐私保护拦截时,来源列表为空。网页端关闭个人知识库后,模型直接使用通用知识回答;隐私保护开启时不加载私有索引,关闭后才允许基于本地私有资料回答。

9. 启动 FastAPI

服务启动时会自动创建 data/knowledge_base.db。首次启动时,如果存在旧的 data/index.csv,会自动迁移其中的资料,并补录尚未登记的私有 Markdown。

uvicorn app.main:app --reload

服务地址:

调用 POST /ask:

curl -X POST "http://127.0.0.1:8000/ask" \
  -H "Content-Type: application/json" \
  -H "X-Visitor-ID: visitor-demo-001" \
  -d '{"question":"我在项目质量测试方面有哪些实践?","model":"qwen3-coder-plus"}'

返回结构:

remaining_uses 是为兼容旧客户端保留的字段,当前固定为 null;token_usage 是本次模型调用的实际用量。prompt 返回实际发送给模型的系统提示词和用户提示词;隐私拦截或未检索到资料而没有调用模型时,该字段为 null。

{
  "prompt": {
    "system": "系统提示词",
    "user": "包含对话历史、用户问题和检索资料的用户提示词"
  },
  "answer": "根据参考资料生成的回答",
  "sources": [
    {
      "title": "文章标题",
      "section": "文章章节",
      "url": "https://blog.csdn.net/...",
      "similarity": 0.8732
    }
  ],
  "remaining_uses": null,
  "model": "qwen3-coder-plus",
  "token_usage": {
    "prompt_tokens": 1200,
    "completion_tokens": 180,
    "total_tokens": 1380
  }
}

10. 设计边界

  • 当前主要回答学习过程、项目经验和工程实践。
  • 默认不加载个人介绍、简历等私有索引,也不回答手机号、邮箱、住址、身份证等个人资料;网页端关闭隐私保护后,才允许基于本地私有资料回答。
  • 不允许模型自行编造来源链接,来源由检索片段元数据提供。
  • 当前公开文章导入支持 CSDN,其他网站需要单独适配正文结构。
  • 当前不会自动定时抓取文章,只有手动执行导入命令时才会下载。
  • 当前历史对话只保存在网页内存中,最多将最近 3 轮完整问答作为下一次请求的上下文;刷新页面或新建对话后会清空,左侧“最近对话”仍是占位界面。
  • 网页端的 localStorage 当前只保存随机访客 ID,不保存问题、回答、提示词或私有资料。
  • 关闭隐私保护后,展示和复制的用户提示词可能包含检索到的私有资料原文,当前界面定位为本地个人工具。

11. 第五至八阶段总结

  • 第五阶段:质量验收与项目收尾。不增加新的用户功能,集中完成后端、前端、API、检索质量和运行稳定性测试;校正模型异常、错误恢复、历史上下文和资料不足等边界行为;补充中文 README、测试说明和运行检查,确保已有功能可以稳定交付。
  • 第六阶段:个人知识库与隐私控制。将个人介绍和个人简历切片、向量分开保存到本机私有索引,避免敏感内容进入公开 JSONL;网页端提供个人知识库和隐私过滤两个独立开关;个人问题优先检索私有资料;本地来源可以打开渲染后的 Markdown 阅读页。
  • 第七阶段:项目分层与模块拆分。不增加或改变现有功能,将单体代码拆分为 API 路由、数据模型、应用服务、领域规则和基础设施五层;精简 FastAPI 应用入口,并保留原命令行脚本作为兼容入口,确保现有 API、网页和命令行行为保持不变。
  • 第八阶段:资料管理与数据迁移(已完成)。补齐资料的增删改查,将 data/index.csv 中的资料元数据迁移到 SQLite,并将资料查询、导入、删除、修改和 CSDN 同步切换到数据库;服务启动时会自动初始化和执行首次迁移,Markdown 继续作为正文源文件。

12. 后续计划

  1. 将会话和消息分表存入 SQLite,实现最近对话加载、恢复、重命名和删除,并为提示词与来源保留当时快照。
  2. 在独立实验入口中评估 LangChain,与现有轻量 RAG 流程对比。
  3. 增加可选的定时同步任务,并补充网络失败重试和运行日志。

13. 运行自动化测试

安装开发测试依赖:

python -m pip install -r requirements-dev.txt

运行测试:

python -m pytest -q

当前测试覆盖文档导入、PDF 页码处理、CSDN 同步、SQLite 迁移与资料 CRUD、检索质量、问答 API、提示词响应和 Token 用量统计。 测试不会调用真实模型服务 API,接口测试会使用模拟问答结果。

项目介绍

一个有关个人知识库的项目。

定制我的领域