官网链接:https://huggingface.co/docs/smolagents/v1.9.2/en/tutorials/building_good_agents
在文档的最前方说到了这部分的核心准则 - 尽最大可能简化你的工作流程:
The best agentic systems are the simplest: simplify the workflow as much as you can
对于LLM而言,越复杂的的对话出错的概率越大,因此需要对工作流程进行合理拆分并尽可能简化,即 尽可能降低调用模型的次数,他们给了两个建议:
- 尽可能细化你的工具类,并将一个抽象功能拆分成多个具体功能然后调用;
- 尽可能为Agent提供具体的函数,而不是让Agent进行决策;
Improve the information flow to the LLM engine
你需要给LLM模型提供更好的信息流,这里的信息约接近自然语言越好。
官网提供了一个根据地区查询天气的示例,但很遗憾他们提供的示例跑不起来,因为对库 datetime
中的函数 strptime
使用错误,类似这种坑在文档中比较多,我会直接给修正后的示例:
from datetime import datetime
from smolagents import tool
def get_weather_report_at_coordinates(coordinates, date_time):
# Dummy function, returns a list of [temperature in °C, risk of rain on a scale 0-1, wave height in m]
return [28.0, 0.35, 0.85]
def convert_location_to_coordinates(location):
# Returns dummy coordinates
return [3.3, -42.0]
@tool
def get_weather_api(location: str, date_time: str) -> str:
"""
Returns the weather report.
Args:
location: the name of the place that you want the weather for.
date_time: the date and time for which you want the report.
"""
lon, lat = convert_location_to_coordinates(location)
date_time = datetime.strptime(date_time, "%Y-%m-%d %H:%M:%S")
return str(get_weather_report_at_coordinates((lon, lat), date_time))
print(get_weather_api("Beijing", "2025-01-21 12:00:01"))
上面的示例运行结果如下:
(LLM) ~/Desktop/LLM $ python smolagent.py
[28.0, 0.35, 0.85]
根据官网的说法这是一个反面例子,理由如下:
- datetime 数据格式不精确;
- 返回结果中没有关于位置的内容;
- 没有日志记录机制试图记录明确的故障情况,如位置格式不正确或日期时间格式不正确;
- 输出格式很难理解
如果一段话连人都不容易猜出来其含义,那么对于LLM而言就更加困难了,为此他们给了一个改进后的版本,但很遗憾仍然跑不起来,经过修正后如下:
from datetime import datetime
from smolagents import tool
def get_weather_report_at_coordinates(coordinates, date_time):
# Dummy function, returns a list of [temperature in °C, risk of rain on a scale 0-1, wave height in m]
return [28.0, 0.35, 0.85]
def convert_location_to_coordinates(location):
# Returns dummy coordinates
return [3.3, -42.0]
@tool
def get_weather_api(location: str, date_time: str) -> str:
"""
Returns the weather report.
Args:
location: the name of the place that you want the weather for. Should be a place name, followed by possibly a city name, then a country, like "Anchor Point, Taghazout, Morocco".
date_time: the date and time for which you want the report, formatted as '%m/%d/%y %H:%M:%S'.
"""
lon, lat = convert_location_to_coordinates(location)
try:
date_time = datetime.strptime(date_time, "%Y-%m-%d %H:%M:%S")
except Exception as e:
raise ValueError("Conversion of `date_time` to datetime format failed, make sure to provide a string in format '%m/%d/%y %H:%M:%S'. Full trace:" + str(e))
temperature_celsius, risk_of_rain, wave_height = get_weather_report_at_coordinates((lon, lat), date_time)
return f"Weather report for {location}, {date_time}: Temperature will be {temperature_celsius}°C, risk of rain is {risk_of_rain*100:.0f}%, wave height is {wave_height}m."
print(get_weather_api("Beijing", "2025-01-21 12:00:01"))
运行结果如下:
(LLM) ~/Desktop/LLM $ python smolagent.py
Weather report for Beijing, 2025-01-21 12:00:01: Temperature will be 28.0°C, risk of rain is 35%, wave height is 0.85m.
改进后的输出结果对于人而言就很容易理解了,能够从语句中快速提炼出有价值的信息如地区、时间、温度等,显然要比上面的示范更加友好。
根据上面的改进内容我们可以大致得出结论,一个对LLM友好的信息流应该有以下几个特征:
- 工具函数内部调用的函数应该尽可能准确;
- 在函数中恰当的位置使用try catch以便Agent快速定位异常并反馈给模型;
- 输出的最终结果应该是简单易读且容易理解的;
Give more arguments to the agent
为Agent提供更多的参数,可以使用Agent的additional_args
参数将额外内容通过Agent传递给LLM。
和之前的文章一样我们这里有两种方案来使用免费的在线LLM模型:
- 设置环境变量
$ export HF_TOKEN="你申请的token"
- 在代码中明确token
from smolagents import CodeAgent, HfApiModel
agent = CodeAgent(tools=[], model=HfApiModel(token="你申请的token"), add_base_tools=True)
agent.run(
"Why does Mike not know many people in New York?",
additional_args={"mp3_sound_file_url":'https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3'}
)
这里因为使用的是免费的 Qwen/Qwen2.5-Coder-32B-Instruct
,其并不能理解音频文件,因此运行结果如下,后期如果有自己合适的模型Token可以替换成自己的:
How to debug your agent
这里节介绍如何对Agent进行debug,大致可以总结出以下几点:
- Use a stronger LLM 使用更强大的模型,模型参数量越大越不容易犯错。官网的例子是让模型生成一张汽车的照片,模型返回了一个文件路径而不是保存到变量中,这类信息容易被Agent认为是异常从而让LLM重新生成,但如果使用的是更强大的模型则不容易出现这种情况;
- Provide more guidance / more information 提供更充分的信息。尽可能详细地对你的任务进行描述、更准确地描述tools的功能;
- Change the system prompt (generally not advised) 修改系统提示词(不推荐)。官网的解释是除非你非常熟悉模型与提示词工程,否则不要轻易变动系统提示词,系统提示词其实就是给模型的前情摘要,是一些非常格式化的规则。
- Extra planning 额外的规划,通过设置参数
planning_interval
让Agent 进行重新规划。这里重新规划并不是说完全从头开始,而是通过检查上一步输出结果来决定下一步应该做什么,有点类似于动态调整给LLM的命令;
使用下面的代码可以查看 CodeAgent
的系统提示词:
from smolagents import CodeAgent, HfApiModel
agent = CodeAgent(tools=[], model=HfApiModel(), add_base_tools=True)
print(agent.prompt_templates["system_prompt"])
输出结果如下,虽然内容很长但通常第一句都是 You are an expert assistant
这也算LLM的潜规则吧,我之前写自己的提示词时也会加上这句话:
You are an expert assistant who can solve any task using code blobs. You will be given a task to solve as best you can.
To do so, you have been given access to a list of tools: these tools are basically Python functions which you can call with code.
To solve the task, you must plan forward to proceed in a series of steps, in a cycle of 'Thought:', 'Code:', and 'Observation:' sequences.
At each step, in the 'Thought:' sequence, you should first explain your reasoning towards solving the task and the tools that you want to use.
Then in the 'Code:' sequence, you should write the code in simple Python. The code sequence must end with '<end_code>' sequence.
During each intermediate step, you can use 'print()' to save whatever important information you will then need.
These print outputs will then appear in the 'Observation:' field, which will be available as input for the next step.
In the end you have to return a final answer using the `final_answer` tool.
Here are a few examples using notional tools:
【内容太多,中间省略,感兴趣可以之际运行看看开源工具的提示词规范】
Here are the rules you should always follow to solve your task:
1. Always provide a 'Thought:' sequence, and a 'Code:\n```py' sequence ending with '```<end_code>' sequence, else you will fail.
2. Use only variables that you have defined!
3. Always use the right arguments for the tools. DO NOT pass the arguments as a dict as in 'answer = wiki({'query': "What is the place where James Bond lives?"})', but use the arguments directly as in 'answer = wiki(query="What is the place where James Bond lives?")'.
4. Take care to not chain too many sequential tool calls in the same code block, especially when the output format is unpredictable. For instance, a call to search has an unpredictable return format, so do not have another tool call that depends on its output in the same block: rather output results with print() to use them in the next block.
5. Call a tool only when needed, and never re-do a tool call that you previously did with the exact same parameters.
6. Don't name any new variable with the same name as a tool: for instance don't name a variable 'final_answer'.
7. Never create any notional variables in our code, as having these in your logs will derail you from the true variables.
8. You can use imports in your code, but only from the following list of modules: {{authorized_imports}}
9. The state persists between code executions: so if in one step you've created variables or imported modules, these will all persist.
10. Don't give up! You're in charge of solving the task, not providing directions to solve it.
Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000.
如果你真的需要修改提示词,官方建议也不要直接清空,而是在这个基础上追加内容:
【注意】:很遗憾官网文档中的代码仍然跑不起来,因为官方说要取缔 smolagents.prompts
模块,并在 Github 上的 Issus [BUG]ImportError: cannot import name ‘CODE_SYSTEM_PROMPT’ from ‘smolagents.prompts’ (unknown location) 做了回复,大家记得未来某个版本上可能会有这么一个功能就行
我这里提供一个方法可以修改/追加系统提示词,因为prompt_templates[“system_prompt”] 本质是个 成员变量 :
from smolagents import CodeAgent, HfApiModel
agent = CodeAgent(model=HfApiModel(), tools=[])
print(agent.prompt_templates["system_prompt"])
print('-' * 50)
agent.prompt_templates["system_prompt"] = "This is a test demo" # 修改系统提示词
print(agent.prompt_templates["system_prompt"])
print('-' * 50)
agent.prompt_templates["system_prompt"] += "I add one sentence." # 追加系统提示词
print(agent.prompt_templates["system_prompt"])
Extra planning
对于参数 planning_interval
而言:
- 设低值(1~3):适合探索性任务,代码需要频繁调整,比如解决开放式问题或优化 AI 生成的代码。
- 设高值(5~10 或更高):适合稳定任务,比如批量修改代码、补全已有代码时,减少不必要的反思,提高效率。
运行官网示例并使用免费的Qwen:
from smolagents import load_tool, CodeAgent, HfApiModel, DuckDuckGoSearchTool
from dotenv import load_dotenv
load_dotenv()
# Import tool from Hub
image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True)
search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(
tools=[search_tool, image_generation_tool],
model=HfApiModel(),
planning_interval=3 # This is where you activate planning!
)
# Run it!
result = agent.run(
"How long would a cheetah at full speed take to run the length of Pont Alexandre III?",
)
输出结果如下,可以看到在第三步之后多了一步 Update plan
,这里其实就是根据前面LLM模型输出的结果进行自动调整: