第1讲:项目概述与环境搭建¶
🎬 推荐:在学习本讲之前或之后,观看 RAG Pipeline 执行流程动画 建立对系统整体执行流程的直观认识。
本讲目标¶
- 理解 RAG 系统的基本概念和应用场景
- 了解本项目的整体架构和技术栈
- 完成开发环境的搭建和验证
本讲地图¶
本图把第 1 讲的环境准备和项目入口串起来,后续章节都会沿着这套运行环境继续增量开发。
图 1:第 01 讲功能闭环地图¶
flowchart TD
C01_ENV["运行配置<br/>Settings / get_settings()"]
C01_DOCKER["基础服务<br/>milvus / mysql / minio / etcd / api"]
C01_APP["应用入口<br/>create_app()"]
C01_STATIC["讲义站点<br/>mkdocs build"]
C01_VERIFY{{"环境验证<br/>run_preflight()"}}
C01_ENV --> C01_DOCKER
C01_DOCKER --> C01_APP
C01_APP --> C01_STATIC
C01_STATIC --> C01_VERIFY
style C01_ENV fill:#F8FAFC,stroke:#64748B,stroke-width:2px
style C01_DOCKER fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style C01_APP fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
style C01_STATIC fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style C01_VERIFY fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
节点与代码对齐¶
| 节点 | 对齐文件 | 函数/对象 | 本章职责 |
|---|---|---|---|
| 运行配置 | .env / .env.compose |
Settings / get_settings() |
准备本机运行和 Docker Compose 运行所需配置。 |
| 基础服务 | docker-compose.yml |
milvus / mysql / minio / etcd / api |
启动知识库、历史、对象存储和 API 运行依赖。 |
| 应用入口 | app.py |
create_app() |
创建 FastAPI 应用并注册页面、API 和治理路由。 |
| 讲义站点 | mkdocs.yml + docs/ |
mkdocs build |
生成课程讲义站点并作为后续学习入口。 |
| 环境验证 | qa_core/config/preflight.py |
run_preflight() |
检查关键配置、模型路径和场景配置是否可用。 |
第一部分:前置知识¶
1.1 什么是 RAG(检索增强生成)¶
RAG = Retrieval-Augmented Generation,即"检索增强生成"。
在没有 RAG 之前,大语言模型(LLM)存在几个核心问题:
- 知识截止日期:模型训练完成后,无法获取训练数据之后的新信息。例如 GPT-4 的知识截止到 2023 年某月,之后发生的事情它不知道。
- 幻觉问题:当模型不确定某个答案时,它可能会"编造"一个看起来很合理但实际上是错误的内容。这在企业场景中是不可接受的。
- 私有知识无法覆盖:企业内部的制度、流程、业务文档是私有数据,从未进入过公开训练集,模型自然无法回答。
RAG 的解决思路非常简单:
可以把 RAG 理解成"开卷考试":LLM 不再只靠记忆回答,而是可以查阅我们提供的资料后再作答。
RAG 的核心价值: - 答案是可溯源的(每个回答都能追溯到具体的文档片段) - 知识可以实时更新(更新知识库不需要重新训练模型) - 幻觉大幅减少(模型被约束在提供的文档范围内回答)
1.2 RAG 系统的基本组成¶
一个完整的 RAG 系统包含两条核心链路:
flowchart LR
subgraph Offline["离线链路(入库)"]
A1["📄 文档加载<br/>PDF/MD/Word/Excel"] --> A2["✂️ 文档切分<br/>父子块策略"]
A2 --> A3["🧮 向量化<br/>BGE-M3 Embedding"]
A3 --> A4["💾 向量存储<br/>Milvus Collection"]
end
subgraph Online["在线链路(问答)"]
B1["❓ 用户提问"] --> B2["🎯 意图识别"]
B2 --> B3["🔍 语义检索<br/>Dense + Sparse Hybrid"]
B3 --> B4["📊 重排序<br/>BGE Reranker"]
B4 --> B5["📝 上下文构建"]
B5 --> B6["🤖 LLM 生成<br/>DashScope 流式输出"]
end
A4 -.->|"检索"| B3
B6 --> C["✅ 可溯源答案"]
style Offline fill:#EFF6FF,stroke:#3B82F6,stroke-width:2px
style Online fill:#ECFDF5,stroke:#059669,stroke-width:2px
对比:传统 LLM vs RAG
flowchart TD
subgraph Traditional["传统 LLM(仅靠记忆)"]
T1["❓ 用户提问"] --> T2["🧠 LLM 回忆训练数据"]
T2 --> T3["⚠️ 可能幻觉<br/>知识有截止日期<br/>无法回答私有知识"]
end
subgraph RAG["RAG(开卷考试)"]
R1["❓ 用户提问"] --> R2["🔍 检索知识库"]
R2 --> R3["📋 获取相关文档片段"]
R3 --> R4["🧠 LLM 基于资料回答"]
R4 --> R5["✅ 有据可查的答案"]
end
style Traditional fill:#FEF2F2,stroke:#DC2626,stroke-width:2px
style RAG fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
离线链路(入库): 1. 文档加载:读取 PDF、Markdown、Word 等各种格式的文档 2. 文档切分:把长文档按语义边界切成小块(chunk),每块通常是几百到一千字 3. 向量化:用 Embedding 模型把每个 chunk 转成一串数字(向量),表达其语义 4. 存储:把向量和原文一起存入向量数据库
在线链路(问答): 1. 意图识别:判断用户问的是什么类型的问题 2. 查询向量化:把用户问题转成向量 3. 语义检索:在向量数据库中找最相似的文档片段 4. 上下文构建:把检索到的片段整理成 LLM 的参考资料 5. 答案生成:LLM 基于资料生成回答
1.3 向量和向量检索是什么¶
这是理解 RAG 最关键的概念。我们通过一个类比来理解:
类比:图书馆找书
传统关键词搜索(比如 MySQL LIKE 查询)就像你告诉图书管理员"我要找书名里有'Python'的书"。问题在于: - 一本叫《Python 机器学习实战》的书会被找到 - 但一本叫《用编程语言做数据分析》的书不会被找到,虽然它也讲 Python
向量检索就像你告诉图书管理员"我要找一本关于'用代码分析数据'的书"。图书管理员理解了这个概念的"语义",然后去书架上找到《Python 机器学习实战》、《R 语言统计分析》、《数据科学入门》等语义相近的书。
技术层面: - Embedding 模型把一段文本(比如一句话、一个段落)转换成一个固定长度的浮点数数组,例如 1024 维的向量 - 语义相近的文本,它们的向量在数学空间中的"距离"也近 - 向量数据库就是专门存储和检索这些向量的系统
第二部分:项目架构¶
2.1 一句话描述¶
这是一个基于 LangChain + Milvus 2.5 Hybrid Search 的多场景 RAG 平台。不是简单的 Demo,而是补齐了企业级 RAG 完整工程闭环的项目。
2.2 全景架构图¶
flowchart TD
User["🖥️ 浏览器用户<br/>static/index.html"] --> Page["GET /api/scenarios"]
User --> Sources["GET /api/sources"]
User --> KbList["GET /api/kb_versions"]
User --> CreateSession["POST /api/create_session"]
User --> Stream["WebSocket /api/stream"]
User --> HistoryApi["GET /api/history/{id}"]
User --> ClearHistoryApi["DELETE /api/history/{id}"]
User --> FeedbackApi["POST /api/feedback"]
AdminPage["🖥️ 状态页<br/>static/admin.html"] --> AdminApi["GET /api/admin/*"]
subgraph FastAPI["FastAPI (app.py)"]
Router["路由层 qa_core/api"]
Pages["pages.py<br/>页面/健康检查"]
Chat["chat.py<br/>问答/历史/反馈"]
Admin["admin.py<br/>管理接口"]
KbVersions["kb_versions.py<br/>版本管理"]
end
Stream --> Chat
AdminApi --> Admin
subgraph Core["qa_core 核心引擎"]
QAService["QAService<br/>服务编排层"]
Intent["intent<br/>意图识别"]
Retrieval["retrieval<br/>Milvus Hybrid Search"]
Pipeline["pipeline<br/>RAG 主流程"]
Prompts["prompts<br/>Prompt Profile"]
Memory["memory<br/>聊天历史"]
Governance["governance<br/>版本/隔离"]
Indexing["indexing<br/>文档入库"]
end
Chat --> QAService
QAService --> Intent
QAService --> Retrieval
QAService --> Pipeline
QAService --> Prompts
QAService --> Memory
QAService --> Governance
Retrieval --> Milvus[("Milvus<br/>Dense + Sparse<br/>Hybrid Search")]
Memory --> MySQL[("MySQL<br/>Chat History<br/>Feedback")]
Indexing --> Milvus
Prompts --> LLM["🤖 DashScope LLM<br/>OpenAI 兼容接口"]
Pipeline --> LLM
style FastAPI fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style Core fill:#F8FAFC,stroke:#64748B,stroke-width:2px
style Milvus fill:#ECFDF5,stroke:#059669,stroke-width:2px
style MySQL fill:#FFFBEB,stroke:#D97706,stroke-width:2px
style LLM fill:#FEF2F2,stroke:#DC2626,stroke-width:2px
2.2.1 架构分层解读¶
全景架构图从上到下可以划分为 五个层次,每一层各司其职:
| 层次 | 名称 | 包含组件 | 一句话职责 |
|---|---|---|---|
| 第一层 | 用户入口层 | 浏览器用户(问答页)、状态页(admin 页) | 用户看得见、点得到的地方 |
| 第二层 | 路由层 | FastAPI qa_core/api(pages / chat / admin / kb_versions) |
把 HTTP/WebSocket 请求分发给正确的后端模块 |
| 第三层 | 核心引擎层 | qa_core 八大模块(QAService、Intent、Retrieval 等) |
所有 RAG 业务逻辑的真正执行者 |
| 第四层 | 外部依赖层 | Milvus、MySQL、DashScope LLM | 提供向量检索、会话存储、文本生成能力 |
| 第五层 | 入库链路(离线) | Indexing → Milvus | 文档和 FAQ 怎么进知识库 |
为什么要分层? 每一层只跟相邻层打交道:浏览器不直接调 Milvus,API 路由不直接拼 Prompt,核心引擎不直接写 HTTP 响应。这保证了任何一层被替换时,其他层不受影响。
2.2.2 第一层:用户入口 — 问答页和状态页各做什么¶
全景架构图的顶部有两个入口角色:
问答页(static/index.html) 是面向普通用户的交互界面,它会发出以下常用请求:
| 端点 | 方法 | 触发时机 | 作用 |
|---|---|---|---|
GET /api/scenarios |
HTTP | 页面加载时 | 拉取所有可用业务场景列表(下拉框的数据来源) |
GET /api/sources |
HTTP | 切换业务场景时 | 拉取当前场景可选的 source 过滤项 |
GET /api/kb_versions |
HTTP | 切换业务场景时 | 查看当前场景的知识库版本状态 |
POST /api/create_session |
HTTP | 用户选择场景后 | 创建新会话,返回 session_id(后续所有问答都绑定这个 ID) |
WebSocket /api/stream |
WebSocket | 用户每次发送问题 | 走完整 RAG 流式问答链路,逐 token 推送答案 |
GET /api/history/{id} |
HTTP | 用户刷新页面或切换会话 | 恢复之前的聊天记录 |
DELETE /api/history/{id} |
HTTP | 用户清空会话时 | 删除该会话历史和摘要 |
POST /api/feedback |
HTTP | 用户点赞/点踩 | 记录用户对某个回答的满意度 |
关键理解:这些端点里,只有
/api/stream是 WebSocket 长连接——它承载了最重的 RAG 逻辑。其他端点都是轻量 HTTP 请求,负责页面初始化、历史、反馈、版本或过滤项查询。
状态页(static/admin.html) 是面向开发者/管理员的诊断界面,它通过 GET /api/admin/* 访问 LangSmith 状态、active 版本、入库质量报告和回归报告入口。状态页不参与在线问答,只做"事后排查"。
2.2.3 第二层:FastAPI 路由层 — 请求如何分发¶
app.py 只做四件事:创建 FastAPI 应用、配置 CORS、启动时执行环境校验、注册路由。业务逻辑全在路由模块里:
| 路由模块 | 文件 | 承接的请求 |
|---|---|---|
pages.py |
qa_core/api/pages.py |
页面渲染(GET /、GET /admin)、健康检查(GET /health)、创建会话 |
chat.py |
qa_core/api/chat.py |
主链路:WebSocket 流式问答、历史查询、反馈、检索诊断 |
admin.py |
qa_core/api/admin.py |
管理接口:LangSmith 状态、入库报告、回归报告入口、回归状态 |
kb_versions.py |
qa_core/api/kb_versions.py |
知识库版本查看、激活、归档 |
关键设计决策:在线问答统一走 WebSocket(/api/stream),不再提供额外的 HTTP 问答入口。如果 HTTP 和 WebSocket 两套问答入口并存,容易出现"HTTP 返回的结果和 WebSocket 不一样"的不一致问题。问候、越界、人工客服短句这些无需检索的问题,也在 WebSocket 主链路的意图识别阶段直接返回。
2.2.4 第三层:qa_core 核心引擎 — 八大模块如何协作¶
这是整个项目的大脑。图表中的八个模块各自承担独立的职责,通过 QAService(服务编排层)统一调度:
① QAService — 服务编排层(总指挥)
QAService 是唯一对外的业务入口。路由层的 chat.py 不直接调 Intent、不直接拼 Prompt,一切通过 QAService 调度。它提供两个问答相关核心方法:
QAService 是进程级单例(应用启动时创建一次,全局复用),但不保存任何请求级状态——所有变化的数据都在方法局部变量中,多用户并发不会互相覆盖。
② Intent — 意图识别(检索准备中的业务判断)
在线主链路的第一个决策点是 decide_route():先处理问候、转人工、越界、场景边界和 FAQ 精确命中。只有 route=retrieval 时,才进入 classify_intent() 做检索类意图识别。Intent 模块回答三个问题:
- 这是什么类型的问题?(问候 / 标准问答 / 知识咨询 / 追问 / 人工客服 / 越界)
- 能不能直接回答?(问候直接回"你好",越界直接拒答)
- 如果不能直接回答,后续该怎么处理?(改写成独立问题?FAQ 优先还是文档优先?)
检索类意图识别的核心策略是 规则优先 + 默认知识查询:
这一步的决策结果会影响后面的所有步骤:检索计划、查询改写、Prompt 模板选择、FAQ 直出阈值。
③ Retrieval — 检索系统(查什么、怎么查)
Retrieval 模块负责和 Milvus 打交道,它封装了:
- MilvusHybridStore:连接管理、Collection 操作、add_documents / search
- 检索计划(RetrievalPlan):把意图转成具体参数——FAQ 查几条、Doc 查几条、阈值多高、是否 rerank
- 过滤表达式:把
scenario_id、kb_version、tenant_id、dataset_id、visibility、source拼成 Milvus expr,保证不会跨场景、跨版本混查 - 重排序(Reranker):用 BGE Reranker CrossEncoder 对召回结果精排
- 去重:FAQ 和 Doc 各自的去重逻辑
检索不是"一把梭",而是分层策略:先查 FAQ(高频确定答案),FAQ 不命中再查文档(需要整合资料),两者分开决策、分开返回。
④ Pipeline — RAG 主流程(流水线)
Pipeline 是问答的"流水线车间",包含五个关键环节:
| 环节 | 触发条件 | 做了什么 |
|---|---|---|
| 查询改写(rewrite) | 意图为 FOLLOW_UP 时 | "那审批呢" → "入职流程中的审批步骤是什么",把省略的主语和背景补全 |
| 查询变体(query_variants) | 所有 RAG 问题 | 原问题 + 等价问法(规则命中本地生成,否则 LLM 生成),提高召回覆盖 |
| 上下文构建(context) | Doc RAG 时有多个 chunk | FAQ 前 2 条 + Doc 得分达标片段,按 [1] 来源 + 内容 格式拼接 |
| 事件生成(events) | 流式问答全程 | 产出 start → status → token... → end 事件序列,前端按 type 渲染 |
| 引用标注(citations) | Doc RAG 时 | 为每个来源片段标注文件名和 chunk 位置 |
⑤ Prompts — 提示词工程(给 LLM 的指令)
不同的意图和问题类别需要不同的 System Prompt。例如:
faq_answer:严格用标准答案回答,不要自行发挥knowledge_answer:基于提供的文档资料整合回答follow_up:结合对话历史理解用户追问的上下文default:通用问答模板
Prompt 选择由 build_answer_prompt_profile 根据意图和问题类别自动决定,而不是 hardcode 一个通用模板。
⑥ Memory — 聊天记忆(上下文连续性)
Memory 模块管理两件事:
- 聊天历史(HistoryStore):基于 LangChain 的
SQLChatMessageHistory,每次问答后写入 MySQL 的chat_messages表。下次提问时读取最近 N 条消息 + 历史摘要,让模型知道"刚才在聊什么" - 反馈(FeedbackStore):用户点赞/点踩后写入 MySQL,用于后续 bad case 分析和评测集补充
⑦ Governance — 知识库治理(版本与隔离)
Governance 模块保证不同场景、不同版本、不同权限的数据不会"串门":
- kb_version:每个 FAQ 和 chunk 都标记了入库版本号。在线检索时自动拼入
kb_version == "xxx"过滤条件,确保只查 active 版本 - data_scope:每条数据还有
tenant_id、dataset_id、visibility、allowed_roles字段。检索时拼成 Milvus 表达式,实现租户级数据隔离
⑧ Indexing — 文档入库(离线链路)
Indexing 模块不在在线问答链路中执行(太慢),而是通过离线脚本触发:
入库时还会做增量判断:通过文件 fingerprint(哈希指纹)检查内容是否变化,没变化的跳过,有变化的先删旧 chunk 再写新的。
2.2.5 第四层:外部依赖 — 三个独立服务各管什么¶
全景架构图底部有三个圆柱形节点([(...)]),代表独立部署的外部服务:
Milvus — 向量数据库
每次入库时,BGE-M3 为每个 chunk 同时生成两种向量——Dense(语义向量,1024 维浮点数)和 Sparse(关键词权重,由 Milvus 内置 BM25 函数生成)。检索时两种向量一起查,兼顾语义相近和关键词命中。
MySQL — 关系型数据库
MySQL 不承担知识检索。FAQ 和文档的检索全在 Milvus 中进行。MySQL 的角色非常单一——"聊天记录存储"。这个边界很重要,避免了"用 MySQL LIKE 模糊查询冒充语义检索"的情况。
DashScope LLM — 大语言模型
项目中有两处调用 LLM:意图识别不确定时的结构化输出(with_structured_output),以及最终答案的流式生成(llm.stream)。两处共用同一个 get_chat_model() 工厂方法,只是 streaming 参数不同。
2.2.6 完整问答链路走读:一次用户提问经历了什么¶
把全景架构图的箭头串起来,就是一次完整问答请求的真实轨迹。以下用"入职流程有哪些步骤"这个提问来走一遍:
sequenceDiagram
actor User as 用户 / Browser
participant WS as WebSocket<br/>(/api/stream)
participant Chat as chat.py
participant QS as QAService
participant RT as Route<br/>decide_route()
participant IC as Intent<br/>Classifier
participant ML as Milvus<br/>(检索/召回)
participant PL as Pipeline<br/>(rag.py)
participant LLM as LLM<br/>(DashScope)
participant Mem as History<br/>/ Memory
User->>WS: 输入"入职流程有哪些步骤"并点击发送
WS->>Chat: {"type":"query","content":"入职流程有哪些步骤",<br/>"session_id":"xxx","scenario_id":"enterprise_knowledge"}
Chat->>QS: stream_query(query, session_id, scenario_id)
Note over QS: ① resolve_scenario() → 加载场景 TOML 配置
Note over QS: ② resolve_active_kb_version() → 查当前 active 版本
QS-->>WS: ③ {"type":"start","session_id":"xxx"}
WS-->>User: 问答已开始
QS-->>WS: ④ {"type":"status","message":"正在进行查询路由"}
WS-->>User: 进度:正在进行查询路由
QS->>RT: ⑤ decide_route(query)
RT-->>QS: route=retrieval
QS-->>WS: ⑥ {"type":"status","message":"正在识别问题意图"}
WS-->>User: 进度:正在识别问题意图
QS->>Mem: ⑦ 读取聊天历史(MySQL chat_messages)
Mem-->>QS: 历史消息列表
QS->>IC: ⑧ classify_intent(query, history, scenario)
IC-->>QS: Intent=KNOWLEDGE_QUERY, source=hr_process
QS->>PL: ⑨ build_retrieval_plan(intent)
PL-->>QS: FAQ top_k=3, Doc top_k=8, rerank=True
QS-->>WS: ⑩ {"type":"status","message":"正在检索业务 FAQ 知识库"}
WS-->>User: 进度:正在检索
QS->>ML: ⑪ FAQ Hybrid Search(Dense + Sparse)
ML-->>QS: 0 条命中(FAQ 未命中)
Note over QS: ⑫ FAQ 未命中 → 进入 Doc RAG
QS-->>WS: ⑬ {"type":"status","message":"正在匹配相关业务资料"}
QS->>ML: ⑭ Doc Hybrid Search(Dense + Sparse)
ML-->>QS: 召回 8 条相关 chunk
QS->>ML: ⑮ BGE Reranker CrossEncoder 精排
ML-->>QS: 保留得分最高的 5 条
QS->>PL: ⑯ prepare_answer() / build_context()
PL-->>QS: 按 [文件名] + 内容 拼接
QS->>PL: ⑰ build_answer_prompt_profile(intent)
PL-->>QS: 选择 knowledge_answer 模板
QS-->>WS: ⑱ {"type":"status","message":"正在生成回答"}
WS-->>User: 进度:正在生成回答
QS->>LLM: ⑲ llm.stream(SystemMessage(模板), HumanMessage(ctx))
LLM-->>QS: 逐 token 返回
QS-->>WS: 每 token → {"type":"token","content":"..."}
WS-->>User: 逐字打印答案
QS->>PL: ⑳ enforce_answer_citations(answer, context_docs)
QS->>Mem: ㉑ add_turn(question, full_answer) → 写入 MySQL
Note over QS: ㉒ finish_success() → Trace
QS-->>WS: ㉓ {"type":"end","sources":[...], ...}
WS-->>User: 回答结束,展示来源引用
耗时分布(典型值):意图识别 ~50ms | FAQ 检索 ~80ms | Doc 检索 ~120ms | Rerank ~200ms | LLM 生成首 token ~2500ms | 总计 ~3000-4000ms。最慢的环节永远是 LLM 生成——因为需要等待云端模型推理,这是所有 RAG 系统的共性。
两条特殊路径(图中箭头覆盖但上面没走到的):
-
问候/越界快速通道:
User → /api/stream → chat.py → QAService.stream_query → decide_route()在主链路内直接产出答案事件,后面的检索准备、检索和 LLM 全部跳过。耗时取决于运行环境和限流/数据库状态,通常远低于完整 RAG。 -
FAQ 直出通道:分两种情况:Stage 1 的
route=faq_exact只允许标准问题精确匹配;Stage 3 的 FAQ 标准直出则发生在检索准备之后,允许精确匹配或达到动态阈值。两者都会返回metadata.answer,不进入 Doc 检索和 LLM 生成。 -
追问改写通道:用户说"那审批呢"→ 意图识别为 FOLLOW_UP → Pipeline 读取历史,把"入职流程中的审批步骤"补全 → 后续流程和普通 RAG 一样。
2.3 部署架构图¶
flowchart LR
subgraph Host["宿主机"]
API["🐍 FastAPI App<br/>127.0.0.1:8000"]
end
subgraph Docker["Docker Compose"]
MilvusSvc["🔍 Milvus Standalone<br/>127.0.0.1:19530"]
Etcd["⚙️ etcd<br/>元数据存储"]
MinIO["📦 MinIO<br/>索引/日志存储"]
MySQLSvc["🗄️ MySQL<br/>127.0.0.1:3306"]
end
subgraph Local["本地模型"]
BGE["🧮 BGE-M3<br/>Embedding"]
Reranker["📊 BGE Reranker<br/>CrossEncoder"]
end
subgraph Cloud["云服务"]
DashScope["☁️ DashScope<br/>LLM API"]
end
API --> MilvusSvc
API --> MySQLSvc
API --> BGE
API --> Reranker
API --> DashScope
MilvusSvc --> Etcd
MilvusSvc --> MinIO
style Docker fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style Local fill:#ECFDF5,stroke:#059669,stroke-width:2px
style Cloud fill:#FFFBEB,stroke:#D97706,stroke-width:2px
2.3.1 部署拓扑解读¶
部署架构图将整个系统划分为四个物理区域,每个区域的部署方式和选型理由各不相同:
| 区域 | 位置 | 包含组件 | 部署方式 | 为什么放这里 |
|---|---|---|---|---|
| 宿主机 | 本地 | FastAPI App (127.0.0.1:8000) | Python 进程直接运行 | 应用代码需要频繁修改调试,不适合容器化 |
| Docker | 本地容器 | Milvus + etcd + MinIO + MySQL | Docker Compose 编排 | 这些是"基础设施",不需要修改代码,容器化能一键启动、统一管理 |
| 本地模型 | 本地磁盘 | BGE-M3 + BGE Reranker | Python 进程加载本地文件 | Embedding 和 Rerank 延迟敏感,不能走网络;且涉及私有数据不适合上传 |
| 云服务 | 阿里云 | DashScope LLM API | HTTPS 远程调用 | LLM 推理需要 GPU 集群,本地跑不动;API 调用按量付费,成本可控 |
2.3.2 六条连接线逐一解读¶
图中的每一条箭头都是一条运行时依赖,按调用频率和延迟敏感度排列:
① FastAPI → Milvus(高频 · 延迟敏感)
这是调用最频繁的外部依赖。每次用户提问,FastAPI 都要把问题向量化后发给 Milvus 做语义检索。部署在本地 Docker 是因为延迟必须可控——如果 Milvus 在云端,每次检索多 50-100ms 网络延迟,用户体验会明显变差。
② FastAPI → MySQL(中频 · 延迟不敏感)
MySQL 只存聊天记录和反馈。部署在本地 Docker 同样是为了避免网络延迟累积——虽然单次 MySQL 查询很快(<5ms),但每次问答要读写多次,走公网会显著拖慢。
③ FastAPI → BGE-M3(高频 · 极度延迟敏感)
BGE-M3 是 CPU/GPU 本地推理,不走网络。为什么必须本地?因为 Embedding 调用极其高频——入库时每个 chunk 都要向量化,在线问答时每个 query variant 都要向量化。如果走 API,不仅延迟不可控,API 调用费用也会非常高。而且,私有文档内容发送给第三方 Embedding 服务本身就有数据安全风险。
④ FastAPI → BGE Reranker(中频 · 延迟敏感)
Reranker 用 CrossEncoder 架构对 Milvus 召回的候选文档逐对打分。这个环节对精度影响最大——同样的召回结果,Rerank 前后 MRR 可能差 10-15 个百分点。本地部署保证了延迟和精度都可控。
⑤ FastAPI → DashScope LLM(低频 · 延迟最高)
LLM 是整个链路中唯一部署在云端的组件,也是最慢的环节(首 token 延迟 ~2500ms)。为什么 LLM 可以走云端而 Embedding 不行?
- LLM 推理需要 GPU 显存(至少 16GB+),本地开发机通常跑不动
- LLM 调用频率相对低(每次问答 1-2 次),不像 Embedding 那样一次入库就调用上百次
- API 调用按 token 计费,成本可控
- LLM 是文本进文本出,不涉及向量数据,传输量小
⑥⑦ Milvus → etcd / MinIO(内部依赖 · 用户无感)
- etcd:存 Milvus 的元数据(Collection Schema、索引配置、段信息)。类比 MySQL 的 information_schema。
- MinIO:存 Milvus 的索引文件、日志和 binlog。类比 MySQL 的 ibdata 文件。
2.3.3 端口与网络边界¶
flowchart TB
subgraph Host["宿主机 Host Machine"]
direction TB
API["FastAPI 应用<br/>127.0.0.1:8000"]
LocalModels["BGE-M3 Embedding<br/>BGE Reranker<br/>(本地文件加载)"]
subgraph Bridge["Docker 桥接网络"]
direction TB
Milvus["Milvus Standalone<br/>127.0.0.1:19530"]
Etcd["etcd<br/>元数据存储"]
MinIO["MinIO<br/>索引/日志"]
MySQL["MySQL<br/>127.0.0.1:3306"]
end
API -->|gRPC| Milvus
API -->|TCP| MySQL
Milvus --> Etcd
Milvus --> MinIO
API -->|进程内调用| LocalModels
end
Host -->|"HTTPS 出站"| DashScope["☁️ DashScope 云端 LLM"]
style Bridge fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style Host fill:#F9FAFB,stroke:#6B7280,stroke-width:2px
style DashScope fill:#FFFBEB,stroke:#D97706,stroke-width:2px
所有本地组件都绑定在 127.0.0.1,不暴露到公网,安全性由操作系统网络栈保证。
2.4 技术栈详解¶
| 层级 | 技术 | 为什么选它 |
|---|---|---|
| API 框架 | FastAPI | 原生支持异步、WebSocket、自动生成 OpenAPI 文档 |
| RAG 编排 | LangChain | 开源生态成熟,封装了 ChatModel、VectorStore、MessageHistory 等 |
| 向量数据库 | Milvus 2.5.x | 支持 Dense + Sparse 混合检索,一次入库同时生成两种向量 |
| Embedding | BGE-M3 | 中文语义理解能力强,支持本地部署,生成 1024 维 Dense 向量 |
| Sparse 向量 | Milvus BM25BuiltInFunction | 服务端内置函数,不需要额外部署分词器 |
| Reranker | BGE Reranker Large | CrossEncoder 架构,对召回结果做精细排序 |
| LLM | DashScope (OpenAI 兼容) | 通过 LangChain ChatOpenAI 统一调用 |
| 会话存储 | MySQL | LangChain SQLChatMessageHistory 自动管理表结构 |
| 配置 | .env.compose / .env + scenario.toml |
运行时环境变量 + 场景级 TOML 配置 |
2.4.1 LlamaIndex 在本项目中的定位¶
本项目主链路不引入 LlamaIndex。这里不是否定 LlamaIndex,而是为了让课程主线更清晰:本课程要讲的是多场景企业级 RAG 如何落地,而不是把多个 RAG 框架同时混在一条链路里。
| 位置 | 当前项目选择 | 为什么这样做 |
|---|---|---|
| 在线问答编排 | QAService + RAG Pipeline + RetrievalPlan |
显式展示查询路由、检索计划、FAQ/Doc 分层检索、Prompt Profile 和引用返回 |
| 向量检索 | LangChain Milvus VectorStore + Milvus Hybrid Search |
dense、sparse、rerank、source、kb_version、DataScope 都在同一条检索链路里可见 |
| 文档入库 | 项目内置 loader 注册表 + LangChain loader + 自定义表格 loader | 文件类型、metadata、source、kb_version、DataScope、质量报告完全对齐企业治理逻辑 |
| 多版本治理 | KnowledgeBaseVersionStore + MySQL active 指针 |
版本发布、回滚、质量门禁是本项目的企业级核心能力 |
| 可选扩展 | LlamaIndex SimpleDirectoryReader / IngestionPipeline |
可作为文档加载、转换、缓存的替代方案理解,但不放进一期主代码 |
LlamaIndex 官方提供了 SimpleDirectoryReader、IngestionPipeline、transformations 和 cache 等能力,适合快速搭建数据接入与入库流水线。但如果把它直接放到本项目主链路里,学习者需要同时理解 LangChain 的 Document/VectorStore 和 LlamaIndex 的 Document/Node/Index/QueryEngine,概念会重叠。当前课程选择保留 LangChain + Milvus + 项目显式 Pipeline,便于把企业 RAG 的业务边界讲清楚。
一句话记住:LlamaIndex 可以作为入库基础设施的可选替代方案,不作为本项目一期在线问答主链路框架。
2.4.2 课程友好型重构边界¶
这个项目不是为了追求最少代码,而是为了把企业级 RAG 的关键边界讲清楚。重构时遵循一个原则:基础设施能用成熟组件就收敛,企业业务闭环必须显式保留。
| 类型 | 当前处理 | 原因 |
|---|---|---|
| Redis FAQ 缓存 | 不引入 | FAQ 和文档召回统一放在 Milvus Hybrid Search,避免 MySQL/Redis/Python BM25 三套状态不一致 |
| Python 本地 BM25 | 不引入 rank_bm25 到主链路 |
Sparse 检索由 Milvus BM25BuiltInFunction 承担,版本过滤和 DataScope 过滤在同一表达式中完成 |
| LlamaIndex | 不放入一期主链路 | 避免 LangChain 与 LlamaIndex 两套 RAG 抽象同时出现,降低首轮学习复杂度 |
| RAGAS | 作为补充评测 | 用来观察语义质量,但主门禁仍验证 Recall@K、MRR、source、Prompt Profile、DataScope、active 版本 |
| 多版本治理 / DataScope / Prompt Profile | 保留项目显式实现 | 这些是企业级 RAG 的业务边界,不能被通用框架隐藏 |
因此,本项目的减复杂度不是删掉企业能力,而是避免引入无关组件和重复框架。首轮学习只需要沿着一条主线理解:FastAPI -> QAService -> Pipeline -> Milvus Hybrid Search -> Prompt -> LLM -> 引用返回。
2.5 八大业务场景¶
项目内置 8 个行业场景,共享同一套核心引擎:
| 场景 ID | 行业 | 典型问题 |
|---|---|---|
enterprise_knowledge |
企业内部知识 | "入职流程有哪些步骤" |
saas_support |
SaaS 客服 | "API 限流导致接口失败怎么排查" |
equipment_ops |
设备运维 | "日检异常怎么升级" |
compliance_qa |
合规风控 | "供应商尽调需要哪些材料" |
cross_border_risk |
跨境贸易 | "HS 归类争议怎么处理" |
tender_contract_risk |
招投标合同 | "合同变更流程是什么" |
insurance_claims |
保险理赔 | "收款账户不一致可以打款吗" |
engineering_project_qa |
工程项目 | "施工图纸和强制性规范冲突怎么办" |
2.6 核心模块一览¶
第三部分:环境搭建与部署¶
3.1 前置依赖¶
| 组件 | 用途 | 必需? |
|---|---|---|
| Python 3.11+ | 应用运行环境 | 是 |
| Docker + Docker Compose | 运行 Milvus、MySQL | 是 |
| 约 4GB 磁盘空间 | 存放本地模型 (BGE-M3 + BGE Reranker) | 是 |
| DashScope API Key | LLM 调用 | 是 |
项目只保留两个环境模板,先按运行模式选一个,不要混用:
| 文件 | 是否提交 | 用途 |
|---|---|---|
.env.compose.example |
是 | Docker Compose 模板,地址使用 mysql、milvus、/app/models/... |
.env.compose |
否 | Docker Compose 实际运行配置,由 .env.compose.example 复制后填写 |
.env.local.example |
是 | 本机 API 调试模板,地址使用 localhost 和 models/... |
.env |
否 | 本机 API 实际运行配置,由 .env.local.example 复制后填写 |
仓库不再保留通用 .env.example,因为这个名字无法表达“容器内视角”还是“宿主机视角”,容易把 Milvus/MySQL 地址和模型路径写错。
3.2 环境类型总览¶
本课程统一把运行环境分成两类:开发环境和 Docker 环境。Docker 环境再按部署机器分成 Windows 本机和 Linux 服务器。
| 环境 | 运行位置 | 适用场景 | 配置文件 | 知识库初始化口径 |
|---|---|---|---|---|
| 开发环境 | MySQL、Milvus 在 Docker,FastAPI 在宿主机 Python | 改代码、断点调试、跑单测 | .env |
默认按当前 active 场景调试;需要时手动初始化 8 大业务场景 |
| Docker Windows 本机 | MySQL、Milvus、FastAPI 全部在 Docker Desktop | 本机运行、交付验收 | .env.compose |
默认初始化并激活 8 大业务场景 |
| Docker Linux 服务器 | MySQL、Milvus、FastAPI 全部在 192.168.88.100 的 Docker |
局域网访问、多人访问、准生产验收 | .env.compose |
默认初始化并激活 8 大业务场景 |
不要把 .env 和 .env.compose 混用。两者最大的区别不是变量名,而是“API 进程在哪里运行”:
| 配置项 | 开发环境:宿主机 API | Docker 环境:容器内 API |
|---|---|---|
MYSQL_HOST |
localhost |
mysql |
MILVUS_URI |
http://localhost:19530 |
http://milvus:19530 |
EMBEDDING_MODEL_PATH |
models/bge-m3 |
/app/models/bge-m3 |
RERANKER_MODEL_PATH |
models/bge-reranker-large |
/app/models/bge-reranker-large |
ENV_FILE |
.env |
.env.compose |
原因很简单:API 如果在宿主机上运行,访问的是宿主机暴露端口;API 如果在 Compose 网络里运行,访问的是容器服务名和容器内挂载路径。
3.2.1 Docker 部署拓扑¶
Windows 本机和 Linux 服务器的 Docker 部署拓扑一致,只是浏览器访问地址不同:Windows 本机访问 127.0.0.1,Linux 服务器从局域网访问 192.168.88.100。
flowchart LR
Browser["浏览器<br/>Windows: 127.0.0.1:8000<br/>Linux: 192.168.88.100:8000"] --> API["api 容器<br/>FastAPI :8000"]
subgraph Compose["Docker Compose"]
API --> MySQL["mysql 容器<br/>mysql:3306"]
API --> Milvus["milvus 容器<br/>milvus:19530"]
Milvus --> Etcd["etcd 容器"]
Milvus --> MinIO["minio 容器"]
API --> Models["宿主机 ./models<br/>挂载到 /app/models"]
API --> Scenarios["宿主机 ./scenarios<br/>挂载到 /app/scenarios"]
end
style Compose fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style API fill:#ECFDF5,stroke:#059669,stroke-width:2px
3.3 开发环境:本机 API 调试¶
开发环境适合改 Python 代码时使用:MySQL、Milvus、MinIO、etcd 仍由 Docker 管理,FastAPI 在宿主机上用 Python 环境启动。首轮验收不建议从这条路径开始,先跑通 Docker 环境更稳。
3.3.1 准备本机配置¶
至少填写:
确认本机开发模式使用宿主机地址和宿主机模型路径:
3.3.2 启动基础服务¶
开发环境只让 Docker 启动依赖服务,不启动 API 容器:
如果本机还没有 .env.compose,先从模板生成;这里的 .env.compose 只用于启动 Docker 依赖服务,不代表 API 也要在容器里运行:
确认模型目录存在:
3.3.3 初始化知识库并启动 API¶
开发环境默认可以只初始化当前 active 场景,方便快速调试:
如果开发机也要一次性准备全部 8 大业务场景,使用批量初始化脚本:
访问地址仍然是宿主机端口:
- 问答页:http://127.0.0.1:8000/
- 状态页:http://127.0.0.1:8000/admin
- 文档页:http://127.0.0.1:8000/docs
- 健康检查:http://127.0.0.1:8000/health
3.4 Docker 环境:Windows 本机部署¶
Windows 本机 Docker 部署用于本机运行和验收。这个模式下 MySQL、Milvus、FastAPI 都运行在 Docker Desktop 中,默认初始化并激活 8 大业务场景。
3.4.1 准备 Compose 配置¶
至少确认这些项已经改成真实值:
Compose 模式地址必须保持为容器服务名,不要改成 localhost:
确认模型目录存在:
3.4.2 一键部署:默认激活 8 大业务场景¶
推荐直接使用项目提供的部署脚本:
脚本会按顺序完成:启动 MySQL/Milvus/MinIO/etcd -> 构建 API 镜像 -> 重建并激活 8 大业务场景 -> 启动 API。
如果你没有提前创建 .env.compose,脚本会先从 .env.compose.example 生成文件并退出,提示你填写 DASHSCOPE_API_KEY 和 ADMIN_API_TOKEN。填好后再运行一次同一条命令即可。
如果只是临时调试当前 active 场景,才使用单场景模式:
-AllScenarios 参数保留为兼容旧命令;现在不传参数也会初始化全部 8 大业务场景。
3.4.3 手动等价命令¶
如果不用部署脚本,Windows 本机 Docker 环境按下面顺序执行。新环境首次部署默认初始化全部 8 大业务场景:
如果只重建一个已有场景,例如企业知识场景:
只有在新环境全量初始化、旧 collection schema 不兼容,或明确要清空重建单场景 collection 时,才追加 --reset-collections:
注意:--force 只表示忽略文件指纹、强制重新写入数据;它不会修改已经存在的 Milvus Collection Schema。只有 --reset-collections 才会删除旧 collection,让入库链路按当前代码重新创建 text + dense + sparse + BM25 Function 的 Hybrid Search 结构。
3.4.4 访问和验收¶
查看容器状态和 API 日志:
看到 Runtime preflight passed 后,说明 API 已经通过启动前置校验。
浏览器访问:
- 问答页:http://127.0.0.1:8000/
- 状态页:http://127.0.0.1:8000/admin
- 文档页:http://127.0.0.1:8000/docs
- 健康检查:http://127.0.0.1:8000/health
执行接口冒烟测试:
讲义站点由宿主机 ./site 挂载到 API 容器的 /app/site。修改 docs/ 或 docs/animation/ 后,在宿主机执行下面命令即可刷新 /docs,不需要为了讲义内容重建 API 镜像:
3.5 Docker 环境:Linux 服务器部署(192.168.88.100)¶
Linux 服务器部署适合局域网访问或多人使用。这里默认服务器地址为 192.168.88.100,服务仍然由 Docker Compose 管理,并且默认初始化并激活 8 大业务场景。
3.5.1 登录服务器并准备配置¶
至少填写:
Linux Docker 环境的核心配置保持容器内视角:
确认模型目录和运行目录存在:
如果模型目录不存在,先把 models/bge-m3 和 models/bge-reranker-large 同步到服务器项目目录。Docker 容器里读取的是挂载后的 /app/models/...,不是服务器上的绝对路径。
3.5.2 部署并初始化 8 大业务场景¶
Linux 服务器上使用手动等价命令更清晰:
如果服务器已安装 PowerShell Core,也可以使用同一个部署脚本;脚本默认同样会初始化全部 8 大业务场景:
3.5.3 访问和网络检查¶
服务器本机先检查健康状态:
局域网浏览器访问:
- 问答页:http://192.168.88.100:8000/
- 状态页:http://192.168.88.100:8000/admin
- 文档页:http://192.168.88.100:8000/docs
- 健康检查:http://192.168.88.100:8000/health
如果局域网无法访问,但服务器本机 curl 正常,优先检查服务器防火墙。CentOS/Rocky Linux 常用命令:
Ubuntu 常用命令:
3.6 配置变更后如何重启¶
修改 Docker 环境配置后,必须让 API 容器重新读取 .env.compose。只改 .env.compose 但不重建或重启容器,运行中的 API 不会感知新配置。
Windows 本机编辑配置:
Linux 服务器编辑配置:
常见变更对应的处理方式:
| 变更内容 | 是否需要重建镜像 | 是否需要重建知识库 | 推荐命令 |
|---|---|---|---|
修改 DASHSCOPE_API_KEY、ADMIN_API_TOKEN、CORS_ALLOW_ORIGINS |
否 | 否 | docker compose --env-file .env.compose up -d --force-recreate api |
| 修改 Python 代码或依赖 | 是 | 通常否 | docker compose --env-file .env.compose build api,再 docker compose --env-file .env.compose up -d api |
| 修改业务资料或 FAQ | 否 | 是 | docker compose --env-file .env.compose run --rm api python scripts/rebuild_scenarios.py |
| 首次部署或需要清空重建全部场景 | 否 | 是 | docker compose --env-file .env.compose run --rm api python scripts/rebuild_scenarios.py --reset-collections |
| 修改 Milvus/MySQL 地址 | 否 | 视情况 | 重启依赖服务,再重启 API |
最常用的 Docker 重启命令:
Linux 服务器也使用同一组 docker compose 命令,只是浏览器访问地址从 127.0.0.1 换成 192.168.88.100。
如果使用开发环境,修改 .env 后直接停止并重新启动 uvicorn 进程;如果修改了 Python 依赖,再执行一次 pip install -r requirements.txt。
3.7 启动前置校验的工作机制¶
Milvus、MySQL、本地模型、LLM Key、场景配置和 active 知识库版本都是启动必需条件,任一缺失直接启动失败。
flowchart TD
Start["🚀 服务启动"] --> C1{"1️⃣ LLM API Key<br/>是否配置?"}
C1 -- "❌ 占位符/空" --> Fail1["❌ 启动失败<br/>DASHSCOPE_API_KEY 未配置"]
C1 -- "✅" --> C2{"2️⃣ Admin Token<br/>是否配置?"}
C2 -- "❌" --> Fail2["❌ 启动失败<br/>ADMIN_API_TOKEN 未配置"]
C2 -- "✅" --> C3{"3️⃣ 模型目录<br/>是否存在?"}
C3 -- "❌" --> Fail3["❌ 启动失败<br/>models/bge-m3 不存在"]
C3 -- "✅" --> C4{"4️⃣ Milvus TCP<br/>是否可达?"}
C4 -- "❌" --> Fail4["❌ 启动失败<br/>Milvus 不可连接"]
C4 -- "✅" --> C5{"5️⃣ MySQL TCP<br/>是否可达?"}
C5 -- "❌" --> Fail5["❌ 启动失败<br/>MySQL 不可连接"]
C5 -- "✅" --> C6{"6️⃣ LLM 连通性<br/>是否正常?"}
C6 -- "❌" --> Fail6["❌ 启动失败<br/>LLM 服务不可用"]
C6 -- "✅" --> C7{"7️⃣ Active KB<br/>版本是否存在?"}
C7 -- "❌" --> Fail7["❌ 启动失败<br/>请先执行入库脚本"]
C7 -- "✅" --> C8{"8️⃣ 场景配置<br/>是否完整?"}
C8 -- "❌" --> Fail8["❌ 启动失败<br/>场景配置缺失"]
C8 -- "✅" --> Warmup["🔥 检索栈预热<br/>加载 8 个场景的<br/>FAQ/Doc Collection"]
Warmup --> Ready["✅ 服务就绪<br/>开始接受请求"]
style Fail1 fill:#FEF2F2,stroke:#DC2626
style Fail2 fill:#FEF2F2,stroke:#DC2626
style Fail3 fill:#FEF2F2,stroke:#DC2626
style Fail4 fill:#FEF2F2,stroke:#DC2626
style Fail5 fill:#FEF2F2,stroke:#DC2626
style Fail6 fill:#FEF2F2,stroke:#DC2626
style Fail7 fill:#FEF2F2,stroke:#DC2626
style Fail8 fill:#FEF2F2,stroke:#DC2626
style Ready fill:#ECFDF5,stroke:#059669,stroke-width:2px
style Warmup fill:#EFF6FF,stroke:#2563EB
设计意图:如果允许部分组件缺失时继续启动,就会出现"页面能打开但提问时才报错"的情况。这会让排查路径变得很绕:看起来像前端或网络问题,最后才发现是模型文件没下载。
本讲实践闭环¶
| 项目 | 内容 |
|---|---|
| 本讲类型 | 系统集成 |
| 实践产物 | Docker Compose、.env.compose、基础页面和 API 启动流程 |
| 是否进入最终项目 | 是,作为后续所有模块运行底座 |
| 验收方式 | docker compose --env-file .env.compose ps、访问页面、执行 API 冒烟测试 |
| 后续落点 | 第 12/13/19 讲继续完善 Web 服务、启动校验和生产部署 |
通过标准:服务容器处于 running/healthy,页面可访问,API 冒烟请求能返回正常响应。
本讲从 0 到 1 实现闭环¶
本讲不展开业务算法,重点是把项目运行骨架搭起来。这里先完成“能启动、能访问、能看日志”,不要在第 1 讲就实现完整 RAG。
- 先准备
docker-compose.yml,定义 api、mysql、milvus、etcd、minio。 - 再准备
.env.compose,配置 LLM、Milvus、MySQL、active 场景。 - 然后写最小
app.py,提供/health或首页访问。 - 最后挂载静态页面,确认前端能请求 API。
实现完成后,相关运行底座结构应该是下面这张图:
flowchart LR
Compose["docker-compose.yml<br/>api / mysql / milvus / etcd / minio"] --> API["app.py<br/>FastAPI 应用入口"]
Env[".env.compose<br/>LLM / Milvus / MySQL / active 场景"] --> API
API --> Static["static/<br/>前端页面"]
API --> Health["/health<br/>健康检查"]
API --> Routers["qa_core/api/*<br/>后续章节逐步补齐路由"]
Compose --> MySQL["MySQL<br/>会话/版本/反馈"]
Compose --> Milvus["Milvus<br/>向量检索"]
来源:真实配置文件,见 docker-compose.yml。
来源:真实配置文件,见 .env.compose.example 和本地 .env.compose。
来源:真实代码调用点,见 app.py。
来源:命令行验收,对应 Docker Compose 启动流程。
闭环验证重点:
| 验证项 | 验证方式 | 期望结果 |
|---|---|---|
| 容器启动 | docker compose --env-file .env.compose ps |
api、mysql、milvus 等服务处于 running/healthy |
| 配置读取 | 查看 API 日志 | .env.compose 中的 active 场景、Milvus、MySQL 配置被加载 |
| 健康检查 | Invoke-RestMethod http://127.0.0.1:8000/health |
返回正常状态 |
| 页面访问 | 浏览器打开站点 | 前端页面可加载 |
| 日志定位 | 故意缺少关键配置 | 启动日志能提示问题方向 |
建议先只做“能启动、能访问、能看日志”,不要在第 1 讲就实现完整 RAG。
重点掌握¶
| 优先级 | 内容 | 原因 |
|---|---|---|
| ★★★ 必会 | RAG 核心概念(检索+生成、开卷考试类比、解决幻觉/私有知识问题) | 本项目的根本出发点,所有后续内容建立在此之上 |
| ★★★ 必会 | 离线链路(文档加载→切分→向量化→存储)和在线链路(意图识别→检索→重排→生成)的职责划分 | 理解两条链路的边界是理解整个项目架构的钥匙 |
| ★★★ 必会 | 全景架构图的五层分层(用户入口→路由→核心引擎→外部依赖→入库) | 每一层各司其职,是理解系统边界的基础 |
| ★★★ 必会 | 部署架构:宿主机(FastAPI)、Docker(Milvus/MySQL)、本地模型(BGE-M3/Reranker)、云服务(DashScope)分别部署的原因 | 理解每类组件的部署选型依据 |
| ★★ 理解 | 向量检索的基本概念(语义相似度 vs 关键词匹配) | 为第 2 讲深入做铺垫 |
| ★★ 理解 | 20 步问答链路走读(从用户输入到 end 事件的完整路径) | 串联全部模块的整体认知 |
| ★★ 理解 | 三条特殊路径(问候/越界快速通道、FAQ 直出通道、追问改写通道) | 理解 RAG 的分支逻辑 |
| ★ 了解 | 项目启动的 7 个步骤和前置校验 | 实操需要,但非概念重点 |
| ★ 了解 | 8 大业务场景和核心模块一览 | 了解项目覆盖范围即可 |
本讲小结¶
- RAG = 检索 + 生成,让 LLM 能够基于外部知识库回答,解决幻觉和私有知识问题
- 向量检索通过语义相似度(而非关键词匹配)找到相关文档
- 本项目采用 FastAPI + LangChain + Milvus + BGE-M3 + DashScope 的技术栈
- 启动前需要完整的环境配置,项目不走简化旁路
下一讲:RAG 核心概念深入 — 向量检索原理、Embedding 模型、混合检索策略