07 LangGraph 集成 RAG

LangGraph 集成 RAG

一、RAG 是什么?

RAG = Retrieval-Augmented Generation(检索增强生成)
#mermaid-svg-jucQxT5Ypp5NnVsd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jucQxT5Ypp5NnVsd .error-icon{fill:#552222;}#mermaid-svg-jucQxT5Ypp5NnVsd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jucQxT5Ypp5NnVsd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jucQxT5Ypp5NnVsd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jucQxT5Ypp5NnVsd .marker.cross{stroke:#333333;}#mermaid-svg-jucQxT5Ypp5NnVsd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jucQxT5Ypp5NnVsd p{margin:0;}#mermaid-svg-jucQxT5Ypp5NnVsd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster-label text{fill:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster-label span{color:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster-label span p{background-color:transparent;}#mermaid-svg-jucQxT5Ypp5NnVsd .label text,#mermaid-svg-jucQxT5Ypp5NnVsd span{fill:#333;color:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd .node rect,#mermaid-svg-jucQxT5Ypp5NnVsd .node circle,#mermaid-svg-jucQxT5Ypp5NnVsd .node ellipse,#mermaid-svg-jucQxT5Ypp5NnVsd .node polygon,#mermaid-svg-jucQxT5Ypp5NnVsd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jucQxT5Ypp5NnVsd .rough-node .label text,#mermaid-svg-jucQxT5Ypp5NnVsd .node .label text,#mermaid-svg-jucQxT5Ypp5NnVsd .image-shape .label,#mermaid-svg-jucQxT5Ypp5NnVsd .icon-shape .label{text-anchor:middle;}#mermaid-svg-jucQxT5Ypp5NnVsd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jucQxT5Ypp5NnVsd .rough-node .label,#mermaid-svg-jucQxT5Ypp5NnVsd .node .label,#mermaid-svg-jucQxT5Ypp5NnVsd .image-shape .label,#mermaid-svg-jucQxT5Ypp5NnVsd .icon-shape .label{text-align:center;}#mermaid-svg-jucQxT5Ypp5NnVsd .node.clickable{cursor:pointer;}#mermaid-svg-jucQxT5Ypp5NnVsd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jucQxT5Ypp5NnVsd .arrowheadPath{fill:#333333;}#mermaid-svg-jucQxT5Ypp5NnVsd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jucQxT5Ypp5NnVsd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jucQxT5Ypp5NnVsd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jucQxT5Ypp5NnVsd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jucQxT5Ypp5NnVsd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jucQxT5Ypp5NnVsd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster text{fill:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd .cluster span{color:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jucQxT5Ypp5NnVsd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jucQxT5Ypp5NnVsd rect.text{fill:none;stroke-width:0;}#mermaid-svg-jucQxT5Ypp5NnVsd .icon-shape,#mermaid-svg-jucQxT5Ypp5NnVsd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jucQxT5Ypp5NnVsd .icon-shape p,#mermaid-svg-jucQxT5Ypp5NnVsd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jucQxT5Ypp5NnVsd .icon-shape .label rect,#mermaid-svg-jucQxT5Ypp5NnVsd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jucQxT5Ypp5NnVsd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jucQxT5Ypp5NnVsd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jucQxT5Ypp5NnVsd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户提问
问题向量化
向量库检索 Top-K 片段
拼进 Prompt
LLM 生成回答
你的文档 txt/pdf
切分 Chunk
Embedding 向量化
向量库

为什么需要 RAG?

方式 能回答什么 局限
纯 LLM 通用知识 不知道你的私有文档、公司内部资料
RAG 基于 你的文档 回答 需要先入库;检索质量影响答案

RAG :文档切成很多块 → 每块变成向量 → 用户提问时找最相关的几块 → 交给 LLM 作答。

二、安装依赖

复制代码
cd D:\yunyanshijie\skill_test\python_study
.venv\Scripts\activate
pip install langchain-text-splitters

若需要解析PDF

复制代码
from langchain_community.document_loaders import PyPDFLoader

三、RAG 四步流水线

加载文档 → 切分 Chunk → 向量化入库 → 检索 + 生成

第 1 步:加载文档(Load)

把文件读成文本。本课主练 txt;PDF 作为加分项(后面说)。

py 复制代码
from pathlib import Path

text = Path("data/knowledge.txt").read_text(encoding="utf-8")

LangChain 里一条记录叫 Document,通常包含 page_content(正文)和 metadata(来源等):

py 复制代码
from langchain_core.documents import Document

doc = Document(page_content=text, metadata={"source": "knowledge.txt"})

第 2 步:切分(Split)

整篇文档往往超过模型上下文,也不能整篇 embedding,所以要切成 Chunk(小块)。

参数 含义 建议值
chunk_size 每块最大字符数 300~500
chunk_overlap 相邻块重叠字符数 50~100

overlap 的作用:避免一句话被拦腰切断,导致检索时语义不完整。

py 复制代码
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=80,
)
chunks = splitter.split_documents([doc])
print(f"切成 {len(chunks)} 块")

第 3 步:向量化入库(Embed + Store)

学习阶段用用内置的 内存向量库(进程结束即清空):

py 复制代码
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(
    model=embedding_model,
    api_key=embedding_api_key,
    base_url=embedding_base_url,
    tiktoken_enabled=False,   # 非 OpenAI 官方 Embedding 模型时必加
)
vectorstore = InMemoryVectorStore.from_documents(chunks, embeddings)

同时需要安装 transformers(LangChain 会用它做分词):

py 复制代码
pip install transformers

第 4 步:检索 + 生成(Retrieve + Generate)

py 复制代码
# 检索最相关的 3 块
results = vectorstore.similarity_search("公司的年假有多少天?", k=3)
context = "\n\n".join(doc.page_content for doc in results)

# 拼进 Prompt,再调 LLM
prompt = f"""根据以下资料回答问题。如果资料中没有相关信息,请说「资料中未找到」。

【资料】
{context}

【问题】
{question}
"""

核心:先检索,再生成------LLM 看到的是「问题 + 相关片段」,而不是整本文档。

四、LangGraph 集成 RAG 示例

定义状态时候新增 context

py 复制代码
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    context: str

定义检索节点

py 复制代码
def make_retrieve_node(vectorstore: InMemoryVectorStore):
    def retrieve_node(state: AgentState):
        query = ""
        for msg in reversed(state["messages"]):
            if isinstance(msg, HumanMessage):
                query = msg.content
                break

        results = vectorstore.similarity_search(query, k=3)
        context = "\n\n".join(doc.page_content for doc in results)
        return {"context": context}

    return retrieve_node

agent 节点动态修改系统提示词

py 复制代码
def make_agent_node(llm: ChatOpenAI):
    llm_with_tools = llm.bind_tools(tools)

    def agent_node(state: AgentState):
        context = state["context"]
        system = SystemMessage(
            content=f"""根据以下资料回答问题。如果资料中没有相关信息,请说「资料中未找到」
            【参考资料】
            {context}
             """
        )
        response = llm_with_tools.invoke([system, *state["messages"]])
        return {"messages": [response]}

    return agent_node

构建图

py 复制代码
def build_graph(llm: ChatOpenAI, vectorstore: InMemoryVectorStore):
    builder = StateGraph(AgentState)
    builder.add_node("retrieve", make_retrieve_node(vectorstore))
    builder.add_node("agent", make_agent_node(llm))
    builder.add_node("tools", tool_node)
    builder.add_edge(START, "retrieve")
    builder.add_edge("retrieve", "agent")
    builder.add_conditional_edges(
        "agent", tools_condition, {"tools": "tools", "__end__": END}
    )
    builder.add_edge("tools", "agent")
    checkPointer = MemorySaver()
    graph = builder.compile(checkpointer=checkPointer)
    return graph

rag问答

py 复制代码
def rag_answer(vectorstore: InMemoryVectorStore, graph: StateGraph):
    thread_id = "demo_thread_001"
    config = {"configurable": {"thread_id": thread_id}}
    while True:
        user_input = input("你:").strip()
        if user_input.lower() in ["q", "quit", "exit"]:
            break
        if not user_input:
            continue
        messages = [HumanMessage(content=user_input)]

        results = graph.invoke({"messages": messages, "context": ""}, config=config)
        print(results["messages"][-1].content)
相关推荐
VipSoft1 小时前
LangChain — RAG 知识库(实操)
langchain·rag
维度攻城狮1 小时前
PyCharm 使用 DevContainer 开发:打造一致、隔离、高效的开发环境
ide·python·pycharm·devcontainer
七夜zippoe1 小时前
OpenClaw 测试策略实战:AI Agent 自动化测试体系搭建与落地
人工智能·ai·自动化·agent·测试体系·openclaw
ctlover1 小时前
Python模块与包
开发语言·python
月光船幽幽2 小时前
锁死后干预有效性的关键突破
人工智能·python·算法
阳光开朗男孩2 小时前
Pytorch的安装与配置
人工智能·pytorch·python
等一朵映山红2 小时前
动态图 vs 静态图:PyTorch 与 TensorFlow 架构底层对比
人工智能·pytorch·python
过期的秋刀鱼!2 小时前
决策树-测量纯度
人工智能·python·深度学习·算法·决策树·机器学习·数据挖掘
文艺理科生Owen2 小时前
3 年,8 种方案,1 次重构:LangChain 记忆方案如何从混乱走向清晰
重构·langchain