DSPy项目教程:使用SIMBA优化器构建高级工具调用智能体
概述
在自然语言处理领域,构建能够有效使用各种工具的智能体是一个具有挑战性的任务。本文将介绍如何使用DSPy框架构建和优化一个能够处理复杂工具调用任务的智能体系统。我们将重点介绍DSPy中的SIMBA优化器,它能够显著提升智能体的性能表现。
环境准备
首先需要安装必要的Python包:
pip install -U dspy func_timeout
推荐设置MLflow跟踪系统来监控和可视化DSPy的行为,这有助于理解智能体的决策过程。
数据准备
我们使用ToolHop数据集,这是一个专门设计用于测试智能体工具调用能力的基准数据集。每个问题都配有一组独特的工具函数,智能体需要学会如何有效地使用这些工具来解决问题。
from dspy.utils import download
import ujson
import random
# 下载并加载数据
download("ToolHop.json")
data = ujson.load(open("ToolHop.json"))
random.Random(0).shuffle(data)
数据处理
我们需要对原始数据进行预处理,提取可用的工具函数并构建训练集、开发集和测试集:
import re
import inspect
examples = []
fns2code = {}
def finish(answer: str):
"""结束轨迹并返回最终答案"""
return answer
# 处理每个数据点
for datapoint in data:
func_dict = {}
for func_code in datapoint["functions"]:
# 清理代码并提取函数名
cleaned_code = func_code.rsplit("\n\n# Example usage", 1)[0]
fn_name = re.search(r"^\s*def\s+([a-zA-Z0-9_]+)\s*\(", cleaned_code)
fn_name = fn_name.group(1) if fn_name else None
if fn_name:
# 动态执行代码获取函数对象
local_vars = {}
exec(cleaned_code, {}, local_vars)
fn_obj = local_vars.get(fn_name)
if callable(fn_obj):
func_dict[fn_name] = fn_obj
fns2code[fn_obj] = (fn_name, cleaned_code)
func_dict["finish"] = finish
example = dspy.Example(question=datapoint["question"],
answer=datapoint["answer"],
functions=func_dict)
examples.append(example.with_inputs("question", "functions"))
# 划分数据集
trainset, devset, testset = examples[:100], examples[100:400], examples[400:]
评估指标
我们定义了严格的评估标准,要求预测结果必须与标准答案完全匹配(经过规范化处理后):
from func_timeout import func_set_timeout
def metric(example, pred, trace=None):
gold = str(example.answer).rstrip(".0").replace(",", "").lower()
pred = str(pred.answer).rstrip(".0").replace(",", "").lower()
return pred == gold # 比原论文更严格的评估标准
evaluate = dspy.Evaluate(devset=devset, metric=metric, num_threads=24,
display_progress=True, display_table=0, max_errors=999)
智能体设计
我们基于ReAct范式设计智能体,它会在每一步观察当前轨迹和可用工具,然后决定调用哪个工具:
class Agent(dspy.Module):
def __init__(self, max_steps=5):
self.max_steps = max_steps
instructions = "对于最终答案,生成简短(非完整句子)的答案,日期格式为YYYY-MM-DD,姓名为Firstname Lastname,数字不带前导0。"
signature = dspy.Signature('question, trajectory, functions -> next_selected_fn, args: dict[str, Any]', instructions)
self.react = dspy.ChainOfThought(signature)
def forward(self, question, functions):
tools = {fn_name: fn_metadata(fn) for fn_name, fn in functions.items()}
trajectory = []
for _ in range(self.max_steps):
pred = self.react(question=question, trajectory=trajectory, functions=tools)
selected_fn = pred.next_selected_fn.strip('"').strip("'")
fn_output = wrap_function_with_timeout(functions[selected_fn])(**pred.args)
trajectory.append(dict(reasoning=pred.reasoning, selected_fn=selected_fn,
args=pred.args, **fn_output))
if selected_fn == "finish":
break
return dspy.Prediction(answer=fn_output.get("return_value", ''), trajectory=trajectory)
初始评估
使用GPT-4o作为基础模型,初始智能体在开发集上的准确率为35%:
agent = Agent()
evaluate(agent) # 输出: 35.0
使用SIMBA优化
SIMBA(Stochastic Introspective Mini-Batch Ascent)是DSPy中的一种提示优化器,它通过一系列小批量处理来逐步改进提示指令或少量示例:
simba = dspy.SIMBA(metric=metric, max_steps=12, max_demos=10)
optimized_agent = simba.compile(agent, trainset=trainset, seed=6793115)
优化后评估
经过SIMBA优化后,智能体的性能显著提升,准确率从35%提升至60.7%,相对提升超过70%:
evaluate(optimized_agent) # 输出: 60.67
总结
本教程展示了如何使用DSPy框架构建和优化工具调用智能体。通过SIMBA优化器,我们显著提升了智能体的性能。这种方法不仅适用于ToolHop任务,也可以推广到其他需要复杂工具调用的场景中。DSPy提供了一套系统化的方法来构建和优化基于语言模型的智能体,大大简化了开发流程。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考