声明:本篇博客是以吴恩达的【Agent智能体】教程为基础,并对其中的内容做了笔记整理以及个人收获的总结。
结合代码执行的规划,指的是不再让LLM直接输出计划,比如说,不是让它以JSON格式输出,逐行执行每一步。
可以直接让LLM编写代码,这样代码可以涵盖计划中的多个步骤,比如调用这个函数,再调用那个函数等等。
通过LLM生成的代码,实际上可以执行相当复杂的计划,下面我们看看,什么情况下可能会用到这种技术!
使用工具进行规划的挑战
假设你想基于一份包含数据的电子表格,构建一个能回答咖啡机销售问题的系统。
用户提问:“哪个月的热巧克力销量最高?“你可以使用这些工具来回答这个问题,如图右下角。但是我们不难发现,过程是相当复杂的!
- 步骤 1 & 2:先用
filter_rows筛出 1 月份的热巧克力,再用get_column_mean算平均销售额。 - 步骤 3 & 4:重复上述动作,筛出 2 月份,算平均值。
- 步骤 5:LLM 被迫输出(或系统被迫执行)针对 3 月到 12 月的无聊重复。如果展开,这至少需要 24 步单独的工具调用。
- 步骤 6:最后再把这 12 个结果拿出来进行比对。
更复杂的提问如下:
- 如果有人问上周有多少笔独特的交易这些工具根本无法给出答案,这样你可能需要重新构建一个工具。
- 或者有人问:”最近几笔交易的金额是多少?“这样你又得创建一个新的工具。
- 我们不难发现,这样每出现一个需求就创建新工具的方式是复杂且低效的
更好的解决方案
这个图呈现了如何通过系统提示词(System Prompt)引导大语言模型(LLM)直接输出可执行的代码,从而一次性解决用户的复杂问题
系统提示词
预设的指令:
“Write code to solve the user’s query. Return your answer as python code delimited with
<execute_python>and</execute_python>tags.”(编写代码来解决用户的查询。将你的答案作为 Python 代码返回,并用<execute_python>标签包裹。)这个提示词做到了两件事:
- 改变输出范式:告诉 LLM 不要输出自然的对话文本,也不要输出零碎的 JSON 工具调用计划,而是直接写代码。
- 方便系统拦截:通过特定的 XML 标签
<execute_python>,外部的 Agent 框架系统可以精准地使用正则表达式提取出这段代码,并直接丢进沙盒环境运行。
实际运作流程
- 用户输入(红色框):“最后 5 笔交易的金额是多少?”(What were the amounts of the last 5 transactions?)
- 大模型生成(灰色框):LLM 接收到问题后,立刻调动了其内部的编程知识,输出了一套完整的、逻辑严密的解决方案。
代码逻辑拆解
右侧灰色框生成的 Python 代码:
- 加载数据:
pd.read_csv一句话搞定数据读取。 - 数据清洗:
pd.to_datetime确保日期格式正确。 - 排序逻辑:
sort_values(ascending=False)直接按时间倒序排列,把最新的交易顶到前面。 - 截取目标:
.head(5)干净利落地切出前 5 行。 - 输出结果:提取
price列并打印。
- 加载数据:
传统工具调用为了找“最后5笔”,可能要调用“获取列表”->“按日期排序工具”->“切片工具”。而这里,LLM一次思考,一次输出,整个规划都在这段脚本里完成了。
依靠 Python 和 Pandas 生态,过滤、聚合、时间序列处理等复杂逻辑都可以轻松实现,不需要开发者去手写几十个琐碎的专属工具。
这些函数 LLM见过很多调用场景,所以让你的LLM来写代码时,它能从这些数百上千个相关函数中选择,因为它已经见过很多调用数据
所以它能把这个大型库里的不同函数串联起来,来制定一个应对复杂查询的方案。
再看一个问题(有人问上周有多少笔独特的交易)
跟上个图除了系统提示词以外其他都一样,看一下**代码逻辑拆解**
步骤一:预处理时间数据
df = pd.read_csv(..., parse_dates=["date"])LLM 非常聪明地在读取 CSV 时,顺手就把文本格式的日期转换成了标准的时间格式,为后面的计算铺路。步骤二:动态计算时间窗口
today=pd.Timestamp.today()week_ago=today-pd.Timedelta(days=7)自己写出了时间戳的减法逻辑,定义了“上周”的时间边界。
步骤三:数据过滤
last_week = df[df["date"].between(week_ago, today)]一句代码,把目标时间段的数据切片拿了出来。步骤四:去重并计数
print(last_week.drop_duplicates().shape[0])直接调用内置的drop_duplicates()删掉重复项,然后用.shape[0]获取最终的行数(也就是笔数)并打印。
对于可以通过编写代码完成的任务,让LLM用软件代码表达方案 并直接执行,是一种让它制定丰富计划的强大方式。
当然在工具使用模块提到的注意事项也适用:比如你需要考虑寻找安全的执行环境 如沙箱。虽然这可能不是最佳实践,但也有很多开发者并没有用沙箱,事实证明用代码规划效果很好,看下文。
使用代码进行规划可以提高性能
- 三种 Action Mode
- Text as Action(纯文本 - 紫色条):模型直接输出自然语言来描述它想做什么。这是最原始、最不精确的方式,系统很难解析并自动化执行。
- JSON as Action(JSON格式 - 粉色条):这就是第一张图里那种传统的工具调用(Tool Calling)模式。模型输出结构化的 JSON 数据来调用预设的 API 或函数。
- Code as Action(代码执行 - 绿色条):这对应第二张图的模式,模型直接编写完整的 Python 等可执行代码来解决问题。
观察图表中的柱状图长度和虚线的趋势,我们可以得出极其明确的结论:“Code as Action”(绿色条)在所有测试的模型中都取得了最高的任务成功率(Successful task completion rate)。
- 对于顶级模型(如 GPT-4 家族):提升最为显著。以最上面的
gpt-4-1106-preview为例,使用传统的 JSON 工具调用时,成功率在 50% 左右;但切换到代码执行后,成功率飙升到了 70% 以上,实现了质的飞跃。 - 跨模型的普适性:无论是强如 Claude-2、GPT-4,还是相对较小的开源模型(如最下方的 Llama-2-70b),绿色的柱子永远是最长的。这说明“用代码规划”不是某个特定模型的专利,而是一种降维打击般的范式优势。
总结
事实证明 如果你让这些高级智能编程辅助工具为你编写复杂软件,它可能会先制定详细计划 先构建软件的某个组件,再构建第二个,甚至可能在过程中规划测试这些组件,接着形成一个清单,按步骤逐一执行。
这种方式在构建越来越复杂的软件时效果非常好,对于其他应用场景,规划的使用还在不断增长和发展,规划的一个缺点是:开发者没有明确告诉系统具体要做什么 因此控制起来会稍微困难一些,而且你无法提前预知运行时会发生什么 ,但放弃部分控制权,确实能显著扩大模型可能尝试的方案范围,所以这项重要技术算是前沿领域,且有很大的上升空间!
如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!