周五晚上八点,老李一个人坐在工位上,盯着屏幕上那份“市场分析报告”,脸都快绿了。Agent 交上来的活儿,两页纸,一段行业百科拼凑的概述,加几句“市场前景广阔”的废话。这是他花了半小时给 Agent 下的指令——“帮我做一份云原生安全市场的竞品分析报告”。
“就这?”老李把保温杯往桌上重重一放,回头对着还在调试代码的小王喊,“你不是说 Agent 能干活吗?我看它能干个锤子!”
小王走过来看了两眼报告,眉头一皱,然后笑了:“老李,你这指令,就像你跟一个新来的实习生说‘去把 Q3 的业绩搞上去’。实习生没当场辞职,已经算客气了。”
“那我应该怎么说?”
“你得先帮他把大象从冰箱里拿出来。”
把大象放进冰箱:不是靠力气大,是靠步骤对
老李一愣:“什么大象?”
“你小时候没听过那个脑筋急转弯吗?把大象放进冰箱需要几步?三步——打开冰箱门,把大象放进去,关上冰箱门。”小王走到白板前,画了一个冰箱和大象,“你刚才下指令的方式,就是站在实习生面前说‘搞定那头大象’。他连冰箱在哪都不知道。”
“复杂任务之所以复杂,不是因为每一步都难,而是因为整体被看作一团混沌。人脑会自动拆解——你给下属布置任务,哪怕你没说出来,你心里已经默默分成了‘确定范围→收集资料→分析对比→输出结论’。但 Agent 不会。你让它‘做分析’,它只会从自己有限的上下文里提取最像‘分析报告’的模板,然后填进去。”
老李若有所思:“所以核心是任务分解?”
“对。而且不是靠你每次都手工列步骤,是让 Agent 自己学会拆。这就涉及到两个关键能力:Plan-then-Execute,和 Tree of Thoughts。”
图:Plan-then-Execute 把模糊任务变成可执行序列
Plan-then-Execute:先想好,再动手
“简单说,就是让 Agent 在动手之前,先把‘作战计划’列出来。这跟 ReAct 不一样——ReAct 是边走边看,Plan-then-Execute 是先画地图再出发。”
小王打开笔记本,写了一段示意代码:
def plan_then_execute(user_goal: str) -> str:
# 第一步:生成计划
plan_prompt = f"""
你是一个任务规划助手。用户的目标是:{user_goal}
请将这个目标分解为3到7个具体的子任务,每个子任务应单一、可执行、可验证。
输出格式:每行一个子任务,以“- ”开头。
"""
plan = llm.generate(plan_prompt)
steps = [line.strip("- ") for line in plan.split("\n") if line.startswith("- ")]
# 第二步:顺序执行每个子任务,收集中间结果
intermediate_results = []
for step in steps:
result = execute_step(step, context=intermediate_results)
intermediate_results.append(result)
# 验证步骤:检查输出是否满足预期(可加入人工确认点)
# 第三步:汇总所有中间结果,生成最终输出
final_output = synthesize_results(user_goal, intermediate_results)
return final_output“你看,同样是‘做竞品分析’,它会先拆成——确定分析范围(哪几家公司、什么时间)、搜索各家产品信息、对比功能与定价、分析优劣势、总结趋势、输出报告。每一步都有明确的输入和输出,每个中间结果都可以验证。”
老李盯着那个计划生成的 Prompt:“那万一它拆错了呢?比如第一步就漏了关键竞品?”
“这就是 Plan-then-Execute 的局限——计划是静态的,错了很难中途调整。所以我们需要升级版:Tree of Thoughts。”
Tree of Thoughts:多想几条路
“Tree of Thoughts,简称 ToT,是一种让 Agent 在规划阶段并行探索多个思路的策略。”小王在白板上画了一棵树。
“比如刚才的竞品分析,Agent 可能同时考虑三种分解方式——A 按公司维度、B 按功能模块维度、C 按时间阶段维度。它每个思路都往下想一两步,评估哪个思路产出的中间结果最有价值,然后选择最好的那条路继续走下去。”
“这就像你让三个实习生同时做方案草案,你选出最好的那份,再让所有人沿着那个方向深入。这样能避免‘一条路走到黑’。”
def tree_of_thoughts_plan(user_goal, num_branches=3, depth=2):
# 第一层:生成多个候选计划
branches = []
for i in range(num_branches):
plan_prompt = f"用不同的角度拆解任务:{user_goal}。请给出一种拆解方案。"
branches.append(llm.generate(plan_prompt))
# 评估每个分支的优劣(可以人工介入,或用LLM打分)
best_branch = evaluate_and_select(branches)
# 沿着最佳分支深入细化
refined_plan = refine_plan(best_branch, user_goal, depth)
return refined_plan老李看着 Tree of Thoughts 的示意图,手指在桌上轻轻敲着:“这是不是要烧很多 Token?”
“是。ToT 的 Token 消耗是线性规划的几倍。所以实际项目里,不是所有任务都值得用 ToT——只有那些高价值、高不确定性的复杂任务才用,比如策略分析、架构设计、重要决策。日常客服问答,Plan-then-Execute 甚至 ReAct 就够了。”
子任务间的依赖管理:有些事必须按顺序来
老李突然想到一个问题:“那如果子任务之间有依赖呢?比如‘分析市场份额’必须等‘收集完各家营收数据’之后。Agent 怎么知道哪个先哪个后?”
小王点点头:“这就需要在计划生成时,明确标注依赖关系。你可以让 LLM 输出的计划用 JSON 格式,包含 step_id、description、depends_on 字段。然后由执行引擎根据依赖关系拓扑排序,决定执行顺序。”
# 带依赖的子任务定义
plan_with_deps = [
{"id": 1, "task": "确定竞品范围", "depends": []},
{"id": 2, "task": "搜索各家产品功能", "depends": [1]},
{"id": 3, "task": "收集各家营收数据", "depends": [1]},
{"id": 4, "task": "对比功能与定价", "depends": [2, 3]},
{"id": 5, "task": "撰写分析报告", "depends": [4]},
]
# 执行引擎按拓扑顺序执行,可并行的(如2和3)并发执行“而且如果执行到第 3 步发现某家公司的营收数据根本查不到,”小王补充道,“执行引擎可以触发动态重规划——返回给 Agent 说‘这条路走不通,请重新规划第 3 步或者替换数据源’。Agent 在原计划基础上调整,而不是从头再来。”
实战:让 Agent 自动规划并执行一份报告
“光说不练假把式,”老李敲敲桌子,“你让 Agent 现场跑一遍,我看看它拆成啥样。”
小王打开终端,挂上了搜索和文档撰写两个工具,输入指令。几分钟后,控制台打印出了一份完整的执行日志:
[Plan] 任务: 云原生安全市场竞品分析
步骤1: 确定竞品范围 (Palo Alto Prisma, Wiz, Aqua, Sysdig)
步骤2: 搜索 Prisma Cloud 产品功能与定价
步骤3: 搜索 Wiz 产品功能与定价
...(步骤2-5并行执行)
步骤6: 对比各产品功能矩阵
步骤7: 分析优劣势与市场定位
步骤8: 生成最终报告(含对比表和总结)
[Execute] 步骤1完成... 步骤2完成... ...
[Final Output] 一份15页的报告,包含功能对比表、SWOT分析、市场趋势老李翻看着这份报告,沉默了好一会儿。然后他拧开保温杯,枸杞的香气弥漫开来。
“这要是实习生干的,我得给他转正。”
小王笑了:“老李,这就是 Plan-then-Execute 加 Tree of Thoughts 的威力。你给 Agent 的不是一条指令,而是一套思考的结构。”
老李站起来,指着屏幕上那份报告:“这个结构,能复用吗?下次做‘数据库选型分析’,是不是换个目标就行?”
“对。计划框架是可复用的模板。你甚至可以建一个‘任务模板库’——‘竞品分析模板’、‘技术选型模板’、‘事故复盘模板’。Agent 接到类似任务,直接套用模板微调,效率更高。”
老李把保温杯重重地放在桌上,声音却轻了很多:“下不为例啊。以后别让我再教 Agent 怎么干活——让它自己先列计划给我审批,通过了再动手。”
他走到门口,停住脚步:“那什么,你刚说的动态重规划,如果中间步骤失败,Agent 会自动调整。那它调整之后的新计划,怎么保证不会偏离原始目标?你那个 evaluate 函数是怎么给分支打分的?给我看看。”
小王比了个 OK,拉开椅子。老李端着保温杯又坐了回去,屏幕上那些被拆解得整整齐齐的任务步骤,像一台精密机器的齿轮,终于咬合在了一起。

