Skip to content

第 12 章 · 综合实战:有证据的本地问答图 ​

把检索、缺失证据路由和回答组合成完整流程。数据来自固定内存资料库,回答用确定性模板,便于离线验证;之后可以单独替换检索器或回答节点。

工作机制与设计选择 ​

流程为 retrieve → route → answer/refuse。State 显式保留 evidence 与 answer,不让模型自行声称“已经检索”。若证据为空就拒答;真实 RAG 还需来源 ID、访问控制、召回评估与引用核对。示例按完整课程键查找,并非语义搜索;不要把它宣称为完整向量 RAG。

示例 ​

python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END

CORPUS = {"langgraph": "LangGraph orchestrates stateful workflows.",
          "deepagents": "Deep Agents adds an agent harness on LangGraph."}

class State(TypedDict):
    question: str
    evidence: str
    answer: str

def retrieve(state: State):
    return {"evidence": CORPUS.get(state["question"].strip().lower(), "")}

def route(state: State):
    return "answer" if state["evidence"] else "refuse"

def answer(state: State):
    return {"answer": state["evidence"] + " [source: local-course-corpus]"}

def refuse(state: State):
    return {"answer": "No evidence found. Please choose a known course."}

builder = StateGraph(State)
for name, function in [("retrieve", retrieve), ("answer", answer), ("refuse", refuse)]:
    builder.add_node(name, function)
builder.add_edge(START, "retrieve")
builder.add_conditional_edges("retrieve", route, {"answer": "answer", "refuse": "refuse"})
builder.add_edge("answer", END)
builder.add_edge("refuse", END)
graph = builder.compile()
for question in ["langgraph", "unknown"]:
    result = graph.invoke({"question": question, "evidence": "", "answer": ""})
    assert bool(result["evidence"]) == (question == "langgraph")
    print(result["answer"])

运行与验收 ​

运行已知和未知查询,分别得到带来源的回答与拒答。验收同时检查 evidence 与 answer,不能只看界面是否有文本。下一步部署学习链接见 Agent 工程实战,本章不执行云部署。

容易踩的坑 ​

生产化不是把 invoke 包进 HTTP 就结束。需要输入校验、租户隔离、超时、取消、日志脱敏、持久化与回归集;更换检索器后应重跑空结果和权限测试。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 没有证据时怎么办?

2. 本例是语义搜索吗?

3. 换检索器后需要重测吗?

4. 上线是否只需 HTTP 包装?

动手实践 ​

  1. 为每条证据增加唯一 ID,答案必须引用对应 ID。
  2. 替换为实际检索器,但保留未知问题拒答的验收测试。
  3. 为输出增加人工审批节点,批准后才导出本地报告。

官方参考 ​

核验日期:2026-09-17。