PROJECT 07

从可视化到代码
LangChain 实战进阶

用代码实现你曾在 FastGPT、Coze、Dify 中搭建的一切。12 课覆盖 RAG 全链路、API 服务化、Web 界面、多步 Agent。
12
LESSONS
3
PHASES
6
FRAMEWORKS
1
PROJECT
TEST DATA

知识库测试数据

配套课程使用的结构化测试数据,涵盖备案流程、材料清单、常见问题等字段,方便你直接导入知识库进行 RAG 实验。

📄 飞书文档 ↗

点击在新标签页打开飞书知识库

OVERVIEW

学习路径总览

三个阶段,从基础调用到工程化部署,每课对标已学平台操作。

P0 基础(6课)
→
P1 工程化(3课)
→
P2 进阶(3课)
阶段课程对标已学核心能力
P001-06FastGPT / Coze / DifyRAG 全链路 + Agent 工具调用
P107-09FastGPT API / Coze 工作流API 服务化 + Web 界面 + 多步 Agent
P210-12框架对比 + 文档处理LlamaIndex + Unstructured + Flask
核心心法

LangChain 的本质就是管道语法:prompt | llm | parser —— 前一个的输出自动流入后一个的输入,就像 Unix 管道。

P0 · FOUNDATION

P0 基础课程(01-06)

从调用 LLM 到完整 RAG + Agent,6 课打通用代码实现 AI 应用的全部基础。

01
调用大模型ChatOllama · invoke · stream

学习目标

  • 用 ChatOllama 连接本地 Ollama 模型
  • 掌握 invoke(一次性返回)和 stream(流式输出)两种调用方式
  • 理解 SystemMessage / HumanMessage / AIMessage 消息角色

核心代码

lesson01_call_llm.py
from langchain_ollama import ChatOllama

# 连接本地模型
llm = ChatOllama(model="qwen2.5:7b", base_url="http://localhost:11434")

# 方式1:一次性返回
response = llm.invoke("什么是网站备案?")
print(response.content)

# 方式2:流式输出(逐字返回)
for chunk in llm.stream("网站备案需要什么材料?"):
    print(chunk.content, end="", flush=True)

对标已学

FastGPT 操作LangChain 代码
配置模型渠道(选 qwen2.5:7b)ChatOllama(model=...)
配置 Ollama 地址base_url="http://localhost:11434"
温度参数temperature=0.1
运行结果

三个测试全部通过:invoke 返回一句话解释、stream 逐字输出材料清单、多轮对话正确记住上文。

02
Prompt 模板ChatPromptTemplate · Few-shot · LCEL

学习目标

  • 用 ChatPromptTemplate 创建带变量的提示词模板
  • 用代码实现 CREATE 框架(Context/Role/Example/ACTION/Tone/Extra)
  • 掌握 Few-shot 给 AI 看示例学习格式
  • 理解 LCEL 链式语法 prompt | llm | parser

核心代码

lesson02_prompt_template.py
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

# CREATE 框架代码版
prompt = ChatPromptTemplate.from_messages([
    ("system", """你是{role}。
领域:{domain}
职责:{responsibility}
语气:{tone}
最多{max_words}字"""),
    ("human", "{question}"),
])

# LCEL 链式语法
chain = prompt | llm | StrOutputParser()
result = chain.invoke({
    "role": "网站备案专家",
    "domain": "工信部网站备案",
    "question": "备案被退回怎么办?",
    ...
})

LCEL 分步调试

管道每一步的中间结果
step1 = prompt.invoke({...})     # → ChatPromptValue
step2 = llm.invoke(step1)        # → AIMessage
step3 = parser.invoke(step2)     # → 纯文本字符串

# 等价于一步到位:
chain = prompt | llm | parser
result = chain.invoke({...})
对标已学

你在 FastGPT 中配置的"开场白"和"知识库回答提示词",在代码里就是 ChatPromptTemplate。CREATE 框架从文档概念变成了可运行的代码。

03
文档加载与切分PyPDFLoader · RecursiveCharacterTextSplitter
知识库测试文件

📄 下载测试 PDF

本课程使用此 PDF 作为知识库测试文档,请下载后配合 Lesson 03 代码运行。

学习目标

  • 用 PyPDFLoader 加载 PDF 文档
  • 用 RecursiveCharacterTextSplitter 切分文档为知识块
  • 理解 chunk_size 和 chunk_overlap 参数的作用
  • 管理元数据(来源、页码等)

核心代码

lesson03_document_split.py
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 加载 PDF
pages = PyPDFLoader("网站备案办事指南.pdf").load()

# 递归切分
splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,      # 每块约200字
    chunk_overlap=30,    # 相邻块重叠30字(防截断)
    separators=["\n\n", "\n", "。", ";", ",", " ", ""],
)
splits = splitter.split_documents(pages)
# 结果:1页PDF → 5个知识块

overlap 对比实验

运行结果

overlap=0:4 个知识块,在句子中间截断
overlap=50:5 个知识块,相邻块有重叠内容,语义连续

对标已学

等价于你在 FastGPT 知识库中上传文档并配置"分块长度 512、重叠 0"的操作。区别是现在能看到每个知识块的具体内容。

04
向量嵌入与检索OllamaEmbeddings · FAISS · MMR

学习目标

  • 用 OllamaEmbeddings 把文本转成 1024 维向量
  • 用 FAISS 存储向量并做相似度检索
  • 理解相似度分数(越低越相关)
  • 掌握 MMR 多样性检索(避免重复结果)

核心代码

lesson04_vector_retrieval.py
from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import FAISS

# 嵌入模型
embeddings = OllamaEmbeddings(model="bge-m3")

# 存入 FAISS 向量数据库
vectorstore = FAISS.from_documents(splits, embeddings)

# 相似度检索
results = vectorstore.similarity_search("备案需要什么材料?", k=3)

# 带分数检索(越低越相关)
results = vectorstore.similarity_search_with_score("备案被退回", k=3)
# 结果:score 0.76 → 0.80 → 0.85

# MMR 多样性检索
results = vectorstore.max_marginal_relevance_search("备案流程", k=3)
关键收获

带分数检索是 FastGPT 界面里看不到的——你能看到每个结果的相似度分数,精确控制检索质量。

05
完整 RAG 流程retriever | format_docs → prompt | llm | parser

学习目标

  • 组装完整 RAG 链:检索 → 格式化 → 提示词 → LLM → 输出
  • 实现防幻觉提示词:"只用检索内容回答,不编造"
  • 流式 RAG 输出
  • 接入重排模型(硅基流动 API)

核心代码

lesson05_full_rag.py
# RAG 提示词(防幻觉核心)
rag_prompt = ChatPromptTemplate.from_template("""你是网站备案专家。根据以下资料回答问题。

【资料】{context}
【规范】只根据资料回答,不编造。无相关信息时说明"根据现有资料无法回答"。
【问题】{question}""")

# 组装 RAG 链
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | rag_prompt | llm | StrOutputParser()
)

三个问题测试结果

运行结果

Q: 网站备案需要什么材料?
A: 列出6项材料(负责人证件、域名证书等)

Q: 备案被退回原因?
A: "根据现有资料无法回答"——防幻觉生效

Q: 备案需要多长时间?
A: "根据现有资料无法回答"——诚实拒绝

核心收获

对比问题 1(有答案→精准回答)和问题 2/3(无答案→拒绝编造),这就是 RAG 的核心价值。你用代码实现了和 FastGPT 完全等价的系统能力。

06
Agent 与工具调用@tool · bind_tools · ReAct 循环

学习目标

  • 用 @tool 装饰器定义工具函数
  • 用 bind_tools 把工具绑定到 LLM
  • 实现 ReAct 循环:思考 → 调用工具 → 观察 → 生成回答
  • LLM 自主判断何时调用工具、何时不调用

核心代码

lesson06_agent_tools.py
from langchain_core.tools import tool

# 定义工具
@tool
def search_filing_info(question: str) -> str:
    """搜索网站备案相关信息"""
    return retriever.invoke(question)

# 绑定工具到 LLM
llm_with_tools = llm.bind_tools([search_filing_info, calculate_days])

# Agent 执行循环
def run_agent(question):
    messages = [HumanMessage(content=question)]
    while True:
        response = llm_with_tools.invoke(messages)
        if response.tool_calls:
            # LLM 要调工具 → 执行工具 → 结果加入对话
            ...
        else:
            return response.content  # 不需要工具 → 直接回答

三个测试结果

运行结果

Q: 备案需要什么材料? → 调用 search_filing_info → 生成回答

Q: 广东备案需要多久? → 调用 calculate_days → 生成回答

Q: 你好,你是做什么的? → 不调用任何工具 → 直接回答

对标已学

等价于你在 Coze 中创建 Agent 并添加插件,或在 Dify 中配置 Agent 节点。第 3 个测试最关键——LLM 自己判断了"这个问题不需要工具"。

P1 · ENGINEERING

P1 工程化实战(07-09)

把 RAG 封装成 API 服务、加上 Web 界面、构建多步复杂 Agent 工作流。

07
FastAPI 服务化POST /ask · SSE 流式 · Swagger 文档

学习目标

  • 把 Lesson 05 的 RAG 链封装成 HTTP API
  • POST /ask 接口:接收问题 JSON,返回回答
  • POST /ask/stream:SSE 流式返回(打字效果)
  • FastAPI 自动生成 API 文档(Swagger UI)

核心代码

lesson07_fastapi.py
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel

app = FastAPI(title="网站备案 RAG API", version="1.0.0")

class AskRequest(BaseModel):
    question: str

@app.post("/ask")
async def ask(request: AskRequest):
    answer = rag_chain.invoke(request.question)
    return {"answer": answer, "question": request.question}

@app.post("/ask/stream")
async def ask_stream(request: AskRequest):
    def generate():
        for chunk in rag_chain.stream(request.question):
            yield f"data: {chunk}\n\n"
    return StreamingResponse(generate(), media_type="text/event-stream")
从使用者变成提供者

你之前调用 FastGPT 的 POST https://cloud.fastgpt.cn/api(带 Bearer 令牌)。现在你是 API 的提供者——别人调用你的 http://localhost:8000/ask。

测试结果

POST /ask {"question":"网站备案需要什么材料?"}
→ 返回6项材料清单

GET /docs → 浏览器打开自动生成的 Swagger API 文档

08
Streamlit 界面聊天界面 · 文件上传 · 流式输出

学习目标

  • 用 Streamlit 搭建聊天界面(不到 200 行代码)
  • 侧边栏上传 PDF/TXT 动态构建知识库
  • 多轮对话 + 流式输出
  • 和 FastGPT/Coze/Dify 前端界面的对比

核心代码

lesson08_streamlit.py
import streamlit as st

# 侧边栏:上传文件
uploaded_files = st.sidebar.file_uploader("上传PDF/TXT", type=["pdf", "txt"])

# 聊天界面
if prompt := st.chat_input("输入你的问题..."):
    with st.chat_message("user"):
        st.write(prompt)
    with st.chat_message("assistant"):
        # 流式输出
        for chunk in rag_chain.stream(prompt):
            st.write(chunk, end="")
一键启动

python -m streamlit run lesson08_streamlit.py → 浏览器自动打开 http://localhost:8501 → 和 ChatGPT 一样的聊天界面。

09
LangGraph 多步 AgentStateGraph · 条件路由 · 多步推理

学习目标

  • LangGraph 核心概念:图(Graph)、节点(Node)、边(Edge)、状态(State)
  • 条件路由:根据检索结果决定走"生成"还是"搜索"路径
  • 多步 Agent:检索 → 判断相关性 → 生成/联网搜索
  • 和 Coze/Dify 工作流编排的对比

Agent 工作流程图

用户提问
→
检索知识库
→
判断相关性
有相关文档 → 生成回答
|
无相关文档 → 搜索工具 → 整合回答

核心代码

lesson09_langgraph.py
from langgraph.graph import StateGraph, END

# 定义状态
class AgentState(TypedDict):
    question: str
    documents: list
    answer: str

# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("retrieve", retrieve_node)
workflow.add_node("grade", grade_documents_node)
workflow.add_node("generate", generate_node)
workflow.add_node("search", search_tool_node)

workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade")
workflow.add_conditional_edges("grade", route_after_grading, {
    "generate": "generate",  # 有文档 → 生成
    "search": "search",      # 无文档 → 搜索
})
workflow.add_edge("generate", END)
workflow.add_edge("search", END)

app_graph = workflow.compile()
对比 Coze/Dify 工作流

Coze 拖拽连线 = LangGraph 的 add_edge;Coze 的 if-else 节点 = LangGraph 的 conditional_edges。代码版更灵活,支持循环、并行、人工介入。

P2 · ADVANCED

P2 框架进阶(10-12)

学习 LlamaIndex 框架、专业文档解析工具 Unstructured、经典 Web 框架 Flask。

10
LlamaIndex 基础VectorStoreIndex · QueryEngine · ChatEngine

学习目标

  • LlamaIndex 核心概念:Document → Node → Index → QueryEngine
  • 3 行代码搭建 RAG(对比 LangChain 的 3 步组装)
  • ChatEngine 多轮对话
  • 两个框架的选型建议

框架对比

概念LangChainLlamaIndex
知识块split_documentsNode
存储FAISS / ChromaVectorStoreIndex
检索retriever.invokeindex.as_retriever
问答RAG ChainQueryEngine
链式语法LCEL (a | b | c)Workflow
核心优势通用、灵活数据处理、索引管理

核心代码

lesson10_llamaindex.py
# LlamaIndex:1步搞定索引(自动切分+向量化)
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=3)
response = query_engine.query("网站备案需要什么材料?")

# 对比 LangChain:需要3步
# splits = splitter.split_documents(pages)
# vectorstore = FAISS.from_documents(splits, embeddings)
# retriever = vectorstore.as_retriever()
选型建议

LangChain = 万能瑞士军刀(Agent、多步推理、高度自定义)
LlamaIndex = 文档处理专家(多种索引、LlamaHub 数据源、增量更新)
实际项目中两个可以组合使用。

11
Unstructured 文档解析版面分析 · 表格提取 · OCR 选型

学习目标

  • 理解 PyPDFLoader 的局限:表格乱码、图片丢失、版面混乱
  • Unstructured 的版面分析:标题/正文/表格/列表分类
  • 元素级智能分块(对比字符级分块)
  • OCR 方案选型:Tesseract / PaddleOCR / 多模态 LLM

版面分析示意图

hi_res 模式自动识别版面元素
┌──────────────────────────┐
│  [Title] 网站备案办事指南  │ ← 识别为标题
├──────────────────────────┤
│  [NarrativeText]          │ ← 识别为正文
│  第一步:备案单位...       │
├──────────────────────────┤
│  [Table]                  │ ← 识别为表格(不切断)
│  材料名称 | 材料形式      │
├──────────────────────────┤
│  [ListItem]               │ ← 识别为列表项
│  1. 登录系统              │
└──────────────────────────┘

选型决策树

文档解析选型

纯文本 PDF + 简单版面 → PyPDFLoader(够用)
纯文本 PDF + 复杂版面/表格 → Unstructured hi_res
扫描件 PDF + 中文 → PaddleOCR
扫描件 PDF + 高精度 → 多模态 LLM (Qwen-VL)

12
Flask API 基础路由 · 请求 · 响应 · 认证

学习目标

  • Flask 基础:路由 @app.route、请求 request.get_json()、响应 jsonify()
  • 用 Flask 封装 RAG API(和 FastAPI 实现同样的功能)
  • 实现 Bearer Token 认证(和你调用 FastGPT API 时一样)
  • Flask vs FastAPI 选型

Flask vs FastAPI 对比

特性FlaskFastAPI
路由定义@app.route@app.get/post
请求体验证手动 get_json()Pydantic 自动验证
异步支持需要扩展原生 async/await
API 文档需要扩展自动生成 Swagger
性能中高(ASGI)
生态成熟度非常成熟快速成长中
选型建议

快速原型/学习 → 都可以,Flask 更简单;新项目/高性能 → FastAPI(异步+自动文档);岗位要求 Flask → 必须会,但也了解 FastAPI。

COVERAGE

岗位要求覆盖对照

基于 AI 工程师岗位要求的技能覆盖情况。

✅
LangChain
P0-P1 已掌握
✅
LlamaIndex
P2 已学习
✅
Flask / FastAPI
P1-P2 已掌握
✅
Streamlit
P1 已掌握
✅
LangGraph
P1 已掌握
✅
RAG 全链路
P0 已掌握
✅
Agent 工具调用
P0-P1 已掌握
✅
模型微调
之前已学
✅
评测体系
之前已学
✅
Docker 部署
之前已学
📋
vLLM 部署
P3 待学习
📋
CV / 传统 ML
按需补充
覆盖率

LLM/Agent 方向岗位要求覆盖率约 80%。主要差距在 vLLM 部署和 CV/传统 ML,可按需补充。

SUMMARY

学习路径总结

从"点页面"到"写代码"的完整进阶路径。

已完成的学习路径

RAG 搭建 (FastGPT)
→
模型微调 (百炼+LLaMA-Factory)
→
评测体系 (Ragas)
→
Agent 应用 (Coze/Dify)
→
Prompt 工程
→
Docker 部署
→
LangChain P0 (01-06)
→
LangChain P1 (07-09)
→
LangChain P2 (10-12)

下一步

→
→
→
核心收获

你用代码实现了之前在 FastGPT、Coze、Dify 三个平台上做的全部事情。区别是:以前是"点页面",现在是"写代码"——你能控制每一个细节,看到每一步的中间结果。这就是从"AI 应用使用者"到"AI 工程师"的转变。

所有课程文件

12 课代码文件位于 langchain-learning/lessons/ 目录,每课都有详细中文注释,可随时回看复习。