Skip to content

第8章 会规划才值钱

前几章我们给Agent装了脑子、手脚、记性。现在Agent会思考、会干活、会查资料——但如果一个任务有10个步骤,它该先做哪个?做错了能不能自己发现并改正?这一章,我们讲Agent的"规划力"——它值钱的地方就在这。


8.1 让Agent学会"想一步、做一步"

8.1.1 光想不做和光做不想都不行

你可以让LLM直接回答:"帮我规划一个三天两夜的北京旅行"。LLM会给你一份看起来很合理的行程。

但你也可以让LLM真的去查天气、比价格、看评价——做一步,看一眼结果,再决定下一步

第二种方式叫 ReAct(Reasoning + Acting,推理+行动)

举个例子:用户问"北京和上海今天哪个更热?"

纯LLM回答(只有推理,没有行动):

LLM: 北京和上海的气温我没法实时获取,建议你自己查一下天气App。

ReAct Agent回答(推理+行动循环):

思考:我需要分别查北京和上海的天气。
行动:调用 get_weather("北京")
观察:北京今天28度。
思考:还需要上海的天气。
行动:调用 get_weather("上海")  
观察:上海今天32度。
思考:上海比北京热4度。
回答:上海今天更热,32度,比北京高4度。

8.1.2 ReAct的代码逻辑

python
# ReAct Agent 的简化逻辑
def react_agent(task, tools, llm):
    context = f"任务:{task}\n"
    max_steps = 10
    
    for step in range(max_steps):
        # 让LLM决定下一步
        response = llm.invoke(f"""{context}
        你可以使用以下工具:{tools}
        请决定:是继续调用工具,还是直接给出最终答案?
        如果是调用工具,格式:ACTION: tool_name(params)
        如果是最终答案,格式:ANSWER: 你的答案
        """)
        
        if "ANSWER:" in response:
            return response.split("ANSWER:")[1].strip()
        
        if "ACTION:" in response:
            # 执行工具
            action = response.split("ACTION:")[1].strip()
            result = execute_tool(action)
            context += f"\n执行了:{action}\n结果:{result}\n"
    
    return "任务超时,未能完成"

ReAct的核心:不是一次性把所有步骤想好再执行,而是做一步、看结果、再想下一步。就像你做饭——你不会把所有步骤都写在纸上才开火,你是边做边调整。


8.2 当"一条路走到黑"不够用的时候

8.2.1 ReAct的局限性

ReAct很好用,但它有一个问题:一旦走错方向,就很难回头

比如Agent帮你想了一个旅行方案,执行到一半发现"第一天去的博物馆周一闭馆"。ReAct只能回头看一步——它可能改成去别的地方,但它不会想"也许我该从第一天开始重新规划"。

8.2.2 思维树:同时想多条路

思维树(Tree of Thoughts,ToT) 是一种更高级的推理方式——它不只走一条路,而是同时想好几个方案,然后挑最好的

和ReAct的区别

ReAct思维树
思考方式一条路走到底同时想多条路
适合任务步骤明确的(查天气→比价→下单)需要创意的(写作、规划、策略)
计算成本高(需要多次调用LLM)
纠错能力看到上一步的结果调整能从根上换一条思路

实际使用建议:大部分Agent用ReAct就够了。只有当任务本身"没有标准答案"时(比如写文章、做方案),才考虑思维树。


8.3 Agent是怎么"化整为零"的?

8.3.1 大任务拆小任务

你说"帮我安排下个月的出差",这句话背后其实有十几件事:

  1. 确认出差日期和目的地
  2. 查航班
  3. 订机票
  4. 订酒店
  5. 预约接送机
  6. 准备会议材料
  7. ……

Agent需要把这些"化整为零"——拆成一个个小任务,逐个完成。

任务拆解本质上就是 Prompt 工程:让LLM先列出所有子任务,再逐个执行。

python
plan_prompt = """
你是一个任务规划助手。请把以下任务拆解成3-7个可执行的子任务。
每个子任务应该具体、可独立完成。

任务:{user_task}

请用列表形式返回。
"""

user_task = "帮我安排下个月去上海的出差"
subtasks = llm.invoke(plan_prompt.format(user_task=user_task))
# 1. 确认出差日期
# 2. 查询北京到上海的航班
# 3. 筛选合适时间和价格的航班
# 4. 查询上海公司附近的酒店
# 5. 预订确认

8.3.2 依赖关系:有些任务有先后顺序

拆出来的任务不是随便做的。有些有依赖关系——"订酒店"必须在"确认日期"之后。

Agent需要理解这种依赖——不然它会同时订机票和酒店,结果日期还没确认,全订错了。

处理依赖的技巧:让LLM在拆解任务时,同时标注每个子任务的"前置条件"。

子任务 - 前置条件
1. 确认日期 - 无
2. 查航班 - 子任务1
3. 订机票 - 子任务2
4. 订酒店 - 子任务1

8.4 Agent的"反思"能力

8.4.1 做错了要能改

Agent不是神,它也会犯错。调了错误的工具、理解错了用户意思、生成了不准确的数据——都很常见。

好的Agent不是不犯错,而是犯了错能发现并改正。

这个过程叫 Self-Reflection(自我反思)

8.4.2 三个常见的反思场景

场景一:工具返回异常。

Agent调了 get_weather("北京"),返回 {"temp": 999}
反思模块:温度不可能999度,API可能出错了。
Agent:用备用工具 retry,或者告诉用户"暂时查不到"。

场景二:LLM回答自相矛盾。

LLM回答:"北京今天晴天,适合户外运动。不过出门记得带伞。"
反思模块:晴天为什么带伞?回答有矛盾。
Agent:重新生成,或者标注"可能有误"。

场景三:用户反馈。

用户:"你推荐的餐厅已经关门了。"
反思模块:用户明确指出了错误。
Agent:记录这家餐厅已关闭,下次不会再推。同时道歉并重新推荐。

8.4.3 反思的实现方式

最简单的方式:多一层LLM调用,专门做"审查官"。

python
def reflect_on_result(task, result):
    """反思模块:检查Agent的输出是否合理"""
    check_prompt = f"""
    任务:{task}
    Agent的输出:{result}
    
    请检查以下问题:
    1. 输出是否完成了任务?
    2. 数据是否合理(没有明显错误)?
    3. 回答是否自相矛盾?
    4. 有没有更好的方式?
    
    如果发现问题,请指出并给出修正建议。
    如果没有问题,回复"通过"。
    """
    return llm.invoke(check_prompt)

8.5 本章小结

这一章我们讲了Agent的"规划力"——它和普通聊天机器人拉开差距的地方:

  1. ReAct:想一步、做一步、看结果、再调整。不把所有步骤想好再动手,而是边干边想。
  2. 思维树:当"一条路走到黑"不行时,同时想好几个方案再挑。创意型任务的神器。
  3. 任务拆解:大任务拆小任务,标注依赖关系再执行。有先有后,不瞎做。
  4. 反思机制:让Agent审查自己的答案。犯错不可怕,错了不能改才可怕。

✅ 知识点检查

学完这一章,试试回答这几个问题:

  • [ ] ReAct 和纯LLM推理的核心区别是什么?ReAct多了一个什么循环?
  • [ ] 思维树和ReAct各适合什么场景?什么时候该用ToT?
  • [ ] 任务拆解时,为什么需要标注"依赖关系"?
  • [ ] Agent的反思模块主要检查哪三个方面?

📚 延伸阅读

  • 《ReAct: Synergizing Reasoning and Acting in Language Models》
  • 《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》
  • LangGraph 官方教程中的 Agent 模式:https://langchain-ai.github.io/langgraph/
  • 本书配套源码:关注公众号「图解AI系列」免费领取

🎯 下一章预告

第9章,一个Agent搞不定的时候——

"一个人不行?那就组个团。多Agent协作,各司其职。"