欢迎来到《Python + AI Agent 实战开发完全指南》专栏!

在上一讲的 RAG 实战中,我们成功让大模型拥有了查阅企业内部知识库的能力。但这仅仅是一个开始。如果你仔细观察目前的系统,会发现它依然像是一个被设定好程序的齿轮——只要用户提问,它就会机械地去检索并生成答案。面对需要多步推导的复杂问题,它往往显得力不从心。

在这一讲中,我们将引入 AI Agent 领域最经典的范式之一:ReAct(Reasoning + Acting)推理模式。我们将打破传统的“一问一答”,赋予大模型“边思考、边行动、边观察”的自主决策能力,让它真正进化为一个能够解决复杂任务的智能体!


一、 架构升级:什么是 ReAct 推理模式?

ReAct 是 Reasoning(推理)和 Acting(行动)的合成词。它的核心思想极其朴素却无比强大:让大语言模型交替进行“内部思考”与“外部行动”

在传统的纯推理模式(如思维链 CoT)中,AI 只是在脑海中演算,无法获取外界新信息;而在纯行动模式中,AI 盲目调用工具,缺乏全局规划。ReAct 完美融合了两者,形成了一个动态闭环:

  1. Thought(思考):分析当前状态,决定下一步该做什么。
  2. Action(行动):调用具体的工具(如搜索、计算)去执行操作。
  3. Observation(观察):获取工具返回的结果,评估是否还需要继续收集信息。
  4. 循环迭代:基于观察结果再次进入 Thought,直到信息充足,输出 Final Answer。

这种机制不仅大幅提升了复杂任务的成功率,还让 AI 的决策过程变得透明可追溯,有效缓解了“幻觉”问题。


二、 核心实战:手写你的第一个 ReAct Agent

为了让概念落地,我们不依赖高度封装的黑盒组件,而是利用 LangGraph 手动搭建一个标准的 ReAct 工作流。首先,初始化我们的基础 LLM 与向量库:

import os
os.environ["PYTHONUTF8"] = "1"

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.vectorstores import InMemoryVectorStore
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage

# --- 1. 初始化 LLM 与向量库 (复用前文配置) ---
API_KEY = "sk-XXXXXXXXXXXXXXXXXXXXXXXXXX"
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
MODEL = "qwen-turbo"

llm = ChatOpenAI(api_key=API_KEY, base_url=BASE_URL, model=MODEL)
embedding = OpenAIEmbeddings(api_key=API_KEY, base_url=BASE_URL, model="text-embedding-v4", check_embedding_ctx_length=False)

docs = [
    Document(page_content="【员工手册】入职满一年可享受5天带薪年假。"),
    Document(page_content="【报销制度】出差住宿标准为每天不超过500元,需凭发票报销。"),
]
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
splits = splitter.split_documents(docs)
vector_store = InMemoryVectorStore.from_documents(splits, embedding)
retriever = vector_store.as_retriever()

接下来,定义供 Agent 调用的工具函数:

def knowledge_retrieval(query: str) -> str:
    """从公司内部知识库中检索相关信息。"""
    results = retriever.invoke(query)
    if not results:
        return "未在知识库中找到相关信息。"
    return "\n".join([doc.page_content for doc in results])

tools = [knowledge_retrieval]
tool_map = {func.__name__: func for func in tools}

最后,注入 ReAct 的灵魂——结构化提示词,并构建状态机图:

# 【核心】定义 ReAct 专属的系统提示词
REACT_PROMPT = """你是一个具备深度推理能力的专家助手。请严格遵循以下 ReAct 工作流:
1. Thought: 分析用户的问题,判断当前掌握的信息是否足以回答。
2. Action: 如果信息不足,必须调用 knowledge_retrieval 工具获取资料。
3. Observation: 评估工具返回的结果。如果仍然不够,可以再次发起思考和调用。
4. Final Answer: 只有当你确信收集到了所有必要信息,才能给出最终的详细解答。
"""

llm_with_tools = llm.bind_tools(tools)

def agent_node(state: MessagesState):
    system_msg = SystemMessage(content=REACT_PROMPT)
    response = llm_with_tools.invoke([system_msg] + state["messages"])
    return {"messages": response}

def tool_executor_node(state: MessagesState):
    last_message = state["messages"][-1]
    results = []
    for tool_call in last_message.tool_calls:
        func = tool_map[tool_call["name"]]
        result = func(**tool_call["args"])
        results.append(ToolMessage(content=str(result), tool_call_id=tool_call["id"]))
    return {"messages": results}

def should_continue(state: MessagesState):
    last_message = state["messages"][-1]
    if hasattr(last_message, "tool_calls") and last_message.tool_calls:
        return "tools"
    return END

builder = StateGraph(MessagesState)
builder.add_node("agent", agent_node)
builder.add_node("tools", tool_executor_node)

builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
builder.add_edge("tools", "agent") # 形成闭环:工具执行完后,强制回到 Agent 节点继续思考

checkpointer = InMemorySaver()
app = builder.compile(checkpointer=checkpointer)

三、 见证奇迹:跨文档综合推理测试

现在,让我们用一个需要跨越两份文档才能回答的复合问题来检验 ReAct 的威力:

if __name__ == "__main__":
    config = {"configurable": {"thread_id": "session_react_001"}}
    
    print("--- 测试:ReAct 跨文档综合推理 ---")
    user_question = "帮我总结一下公司的出差住宿标准,以及工作满一年的年假天数。"
    res = app.invoke({"messages": [HumanMessage(content=user_question)]}, config=config)
    print(f"\nAssistant: {res['messages'][-1].content}")

预期效果:
你会观察到,Agent 并没有急于给出答案,而是先进入了 Thought 阶段,意识到需要查询两个维度的信息。接着它触发 Action 调用 knowledge_retrieval。拿到结果后,它在 Observation 阶段确认信息完整,最终才输出一段逻辑严密、涵盖两项政策的总结回复。整个过程展现了极强的自主规划能力。


四、 本节小结与进阶展望

恭喜你!至此,你已经掌握了现代 AI Agent 最核心的推理引擎。

  1. 认知升级:深刻理解了 ReAct 模式如何通过“思考-行动-观察”的交替循环,打破了传统大模型的静态知识边界。
  2. 工程落地:通过自定义 REACT_PROMPT 结合 LangGraph 的条件边,成功将抽象的推理范式转化为了可执行的代码状态机。
  3. 动态修正:Agent 不再是死板地执行预设流程,而是学会了根据中间反馈动态调整策略,这是迈向高级智能体的关键一步。

课后思考:
目前的 ReAct 循环虽然强大,但如果遇到极端情况(例如用户问了一个完全无解的问题),Agent 可能会陷入无限次的“思考-检索-失败-再思考”的死循环中。在实际的生产环境中,你会如何改造这个状态机,为 Agent 增加错误重试上限兜底退出机制

在下一讲(第9讲)中,我们将深入探讨状态机与工作流的高级设计,为你的 Agent 补齐异常处理与容错控制的最后一块拼图!

更多推荐