第 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 包装?
动手实践
- 为每条证据增加唯一 ID,答案必须引用对应 ID。
- 替换为实际检索器,但保留未知问题拒答的验收测试。
- 为输出增加人工审批节点,批准后才导出本地报告。
官方参考
核验日期:2026-09-17。