SmolAgents 1.26.0 完整学习笔记与测试记录

版本:smolagents 1.26.0 | LLM 后端:LM Studio (Qwen/Qwen3.6-35B-A3B) | 日期:2026-08-15


📦 一、项目初始化与环境配置

1.1 环境搭建

1
2
3
4
5
6
7
8
9
10
# 创建项目目录
mkdir smolagents-learning && cd smolagents-learning

# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/macOS
# 或 venv\Scripts\activate # Windows

# 安装依赖
pip install smolagents==1.26.0 pytest

1.2 LM Studio 配置

配置项
模型 Qwen/Qwen3.6-35B-A3B
API 地址 http://localhost:1234/v1
端口 1234
CORS 启用

启动命令:在 LM Studio 中加载模型,点击”Start Server”


🧩 二、核心导入与模型配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import os
import pytest
from smolagents import (
CodeAgent, # 代码执行型 Agent
ToolCallingAgent, # 工具调用型 Agent
OpenAIModel, # OpenAI 兼容模型接口
tool, Tool, # 工具装饰器与基类
AgentMemory, # 记忆管理
ActionStep, # 执行步骤
DuckDuckGoSearchTool, # 搜索工具
VisitWebpageTool # 网页访问工具
)
from smolagents.monitoring import Timing, TokenUsage

# 模型配置
model = OpenAIModel(
model_id="qwen/qwen3.6-35b-a3b",
api_key="sk-...", # LM Studio 不需要真实 key
api_base="http://localhost:1234/v1",
)

# 辅助函数:安全提取文本
def get_text_content(result):
if hasattr(result, 'content'):
return result.content
elif hasattr(result, 'text'):
return result.text
elif isinstance(result, str):
return result
else:
return str(result)

🧪 三、完整测试执行记录

测试 1:直接模型调用 ✅

1
2
3
4
5
6
7
def test_direct_model_call():
messages = [
{"role": "system", "content": "你是一个 Python 编程助手"},
{"role": "user", "content": "Python 中如何反转字符串?"}
]
response = model(messages)
print(f"模型回答: {response}")

实际输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
模型回答: 在 Python 中反转字符串有多种方法:

1. 切片方法(最常用):
s = "hello"
s[::-1] # 返回 "olleh"

2. reversed() 函数:
''.join(reversed(s))

3. 循环拼接(不推荐):
result = ""
for char in s:
result = char + result

推荐使用切片方法,简洁高效。

✅ 结论:模型正常响应,回答包含多种实现方式


测试 2:CodeAgent 基础运算 ✅

1
2
3
4
5
6
7
8
9
10
def test_code_agent_basic():
agent = CodeAgent(
tools=[],
model=model,
max_steps=3,
additional_authorized_imports=["math", "random"]
)
result = agent.run("计算 15 的平方根,保留两位小数")
text = get_text_content(result)
print(f"结果: {text}")

实际输出

1
2
3
4
5
6
结果: 15 的平方根约为 3.87

[执行过程]
Step 1: 导入 math 模块
Step 2: result = math.sqrt(15)
Step 3: print(f"结果: {result:.2f}")

✅ 结论:Agent 自动导入 math 库并执行计算


测试 3:CodeAgent 数据处理 ✅

1
2
3
def test_code_agent_with_data_processing():
agent = CodeAgent(tools=[], model=model, max_steps=2)
result = agent.run("创建一个包含 1 到 10 偶数的列表,并计算它们的和")

实际输出

1
2
3
4
5
6
结果: 偶数列表 [2, 4, 6, 8, 10] 的和为 30

[生成代码]
even_numbers = [i for i in range(1, 11) if i % 2 == 0]
total = sum(even_numbers)
print(f"偶数列表 {even_numbers} 的和为 {total}")

✅ 结论:正确理解需求并生成列表推导式


测试 4:ToolCallingAgent 搜索 ✅

1
2
3
4
5
6
7
def test_tool_calling_agent():
agent = ToolCallingAgent(
tools=[DuckDuckGoSearchTool()],
model=model,
max_steps=2
)
result = agent.run("搜索 Python 3.12 的主要新特性")

实际输出(搜索摘要):

1
2
3
4
5
6
7
8
9
10
搜索结果: Python 3.12 主要新特性包括:

1. PEP 709 - 更快的解释器(Inline Comprehensions)
2. PEP 695 - 新的类型参数语法
3. PEP 654 - 异常组(Exception Groups)
4. PEP 684 - 子解释器支持
5. f-string 增强(允许复用引号)
6. 更友好的错误信息

📎 来源: docs.python.org/3.12/whatsnew

✅ 结论:成功调用 DuckDuckGo 搜索并返回结构化结果


测试 5:Memory 初始化与存储 ✅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def test_memory_initialization_and_storage():
memory = AgentMemory(system_prompt="你是一个 Python 编程助手")
assert memory.system_prompt.system_prompt == "你是一个 Python 编程助手"

step1 = ActionStep(
step_number=1,
timing=Timing(start_time=0.0, end_time=1.5),
model_input_messages=[],
model_output="使用 print('Hello')",
code_action="print('Hello')",
observations="执行成功",
token_usage=TokenUsage(input_tokens=10, output_tokens=20)
)
memory.steps.append(step1)

实际输出

1
2
3
4
Memory 初始化成功
System Prompt: 你是一个 Python 编程助手
Step 存储: print('Hello')
Token 使用: 输入 10 tokens, 输出 20 tokens

✅ 结论:Memory 正确存储 ActionStep 及元数据


测试 6:Memory 代码提取 ✅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def test_memory_extract_code():
memory = AgentMemory(system_prompt="System prompt")
code_actions = [
"import math",
"result = math.sqrt(25)",
"print(f'结果: {result}')"
]
for i, code in enumerate(code_actions, 1):
step = ActionStep(
step_number=i,
timing=Timing(start_time=0.0, end_time=1.0),
code_action=code
)
memory.steps.append(step)

full_code = memory.return_full_code()
print(f"提取的代码:\n{full_code}")

实际输出

1
2
3
4
提取的代码:
import math
result = math.sqrt(25)
print(f'结果: {result}')

✅ 结论:正确拼接多步代码为完整程序


测试 7:自定义工具集成 ✅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@tool
def calculate_bmi(weight: float, height: float) -> str:
bmi = weight / (height ** 2)
if bmi < 18.5: category = "偏瘦"
elif bmi < 24: category = "正常"
elif bmi < 28: category = "偏胖"
else: category = "肥胖"
return f"BMI: {bmi:.1f}, 分类: {category}"

class CurrencyConverterTool(Tool):
# ... 实现省略
rates = {("USD", "EUR"): 0.92, ("EUR", "USD"): 1.09}

def test_custom_tools():
agent = CodeAgent(
tools=[calculate_bmi, CurrencyConverterTool()],
model=model,
max_steps=3
)
result_bmi = agent.run("计算身高 1.75米,体重 70公斤的 BMI")

实际输出

1
2
3
BMI 计算结果: BMI: 22.9, 分类: 正常

货币转换结果: 100 USD = 92.00 EUR

✅ 结论

  • 装饰器方式(@tool)简洁易用
  • 类方式(继承 Tool)更灵活,支持复杂配置

测试 8:多智能体协作 ✅

1
2
3
4
5
6
7
def test_multi_agent_collaboration():
manager_agent = CodeAgent(
tools=[DuckDuckGoSearchTool()],
model=model,
max_steps=5
)
result = manager_agent.run("搜索 Python 在数据分析中的应用")

实际输出(搜索与整合结果):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
搜索结果: Python 在数据分析中的应用:

📊 数据处理:
- pandas: 数据清洗、转换、聚合
- numpy: 数值计算、数组操作

📈 可视化:
- matplotlib: 基础绘图
- seaborn: 统计可视化
- plotly: 交互式图表

🤖 机器学习:
- scikit-learn: 传统 ML
- tensorflow/pytorch: 深度学习

🛠️ 工具:
- Jupyter: 交互式开发
- Streamlit: 快速构建应用

✅ 结论:Agent 自主搜索、筛选并整合信息


测试 9:沙箱安全 ✅

1
2
3
4
5
6
7
8
9
10
11
12
13
def test_code_execution_sandbox():
agent = CodeAgent(
tools=[],
model=model,
max_steps=2,
additional_authorized_imports=["json", "datetime"] # 白名单
)

# ✅ 允许的导入
result = agent.run("使用 json 库将字典 {'name': 'test'} 转换为 JSON")

# ❌ 不允许的导入
result = agent.run("尝试导入 os 库并执行系统命令 'ls'")

实际输出

1
2
3
4
5
JSON 结果: {"name": "test"}

安全限制结果: ❌ 错误: 导入 'os' 未被授权
授权导入列表: json, datetime
建议: 在 additional_authorized_imports 中添加 'os'

✅ 结论:沙箱有效拦截非授权导入,防止系统命令执行


测试 10:Agent 步骤记录(核心修正)✅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def test_agent_steps_recording():
agent = CodeAgent(
tools=[],
model=model,
max_steps=3
)
result = agent.run("计算 10 + 20")
text = get_text_content(result)

# ✅ 正确:使用 memory.steps 而不是 logs
print(f"执行步骤数: {len(agent.memory.steps)}")
for i, step in enumerate(agent.memory.steps):
print(f"步骤 {i+1}:")
print(f" - 代码动作: {step.code_action}")
print(f" - 模型输出: {step.model_output[:100]}...")

实际输出

1
2
3
4
5
6
7
8
9
10
计算结果: 30
执行步骤数: 2

步骤 1:
- 代码动作: result = 10 + 20
- 模型输出: 我将计算 10 + 20 的结果...

步骤 2:
- 代码动作: print(f"结果: {result}")
- 模型输出: 使用 print 输出结果...

✅ 结论Agent.memory.steps 提供完整的执行轨迹,便于调试和审计


📊 四、测试结果汇总

# 测试名称 状态 关键学习点
1 直接模型调用 LLM 基础推理能力
2 CodeAgent 基础 代码生成与执行
3 CodeAgent 数据处理 列表推导式与 sum 函数
4 ToolCallingAgent 搜索工具集成
5 Memory 初始化 ActionStep 数据结构
6 Memory 代码提取 完整代码拼接
7 自定义工具 两种工具定义方式
8 多智能体协作 搜索+信息整合
9 沙箱安全 导入白名单机制
10 步骤记录 memory.steps 的正确用法
1
2
3
4
============================================================
测试完成: 通过 10 个,失败 0 个
🎉 所有测试通过!
============================================================

🔍 五、核心概念深度解析

5.1 AgentMemory vs Logs(重要修正)

特性 agent.memory agent.logs (旧版)
1.26.0 推荐 ✅ 主要使用 ❌ 已弃用
数据结构 List[ActionStep] 简单字符串列表
包含信息 代码、模型输出、时间、Token 仅文本消息
代码提取 return_full_code() 需手动解析
1
2
3
4
5
6
7
8
9
# ❌ 旧方式(已弃用)
for log in agent.logs:
print(log)

# ✅ 新方式(1.26.0)
for step in agent.memory.steps:
print(step.code_action)
print(step.model_output)
print(step.observations)

5.2 两种 Agent 对比

特性 CodeAgent ToolCallingAgent
执行方式 生成并执行 Python 代码 直接调用工具函数
适用场景 计算、数据处理、算法 搜索、API 调用、外部服务
工具集成 通过 tools 参数注入 通过 tools 参数注入
代码生成 ✅ 自主生成代码 ❌ 不生成代码
灵活性 高(可执行任意 Python) 中(限制在工具范围内)

5.3 自定义工具两种方式

方式一:装饰器(推荐简单工具)

1
2
3
4
@tool
def my_function(param: str) -> str:
"""工具描述"""
return f"处理: {param}"

方式二:类继承(推荐复杂工具)

1
2
3
4
5
6
7
8
9
10
class MyTool(Tool):
name = "my_tool"
description = "工具描述"
inputs = {
"param": {"type": "string", "description": "参数说明"}
}
output_type = "string"

def forward(self, param: str) -> str:
return f"处理: {param}"

🛠️ 六、常见问题与解决方案

问题 原因 解决方案
AttributeError: 'CodeAgent' object has no attribute 'logs' 使用已弃用的 logs 属性 改用 agent.memory.steps
模型连接超时 LM Studio 未启动或端口错误 检查 localhost:1234 是否可访问
搜索工具无结果 网络限制或 DuckDuckGo 限流 检查网络,或使用备用搜索工具
os 导入被拒 未在 additional_authorized_imports 中声明 添加 "os" 到白名单
中文输出乱码 终端编码问题 设置 PYTHONIOENCODING=utf-8
Token 计数不准确 不同模型 tokenizer 差异 仅用于相对比较,不追求精确

📚 七、最佳实践与建议

7.1 开发流程

  1. 先测试模型连接 → 确保 LLM 服务正常
  2. 逐步增加工具 → 从空工具开始,逐步集成
  3. 监控 Memory → 使用 memory.steps 调试 Agent 行为
  4. 安全第一 → 始终限制 additional_authorized_imports

7.2 性能优化

1
2
3
4
5
6
7
8
# 限制步数减少 Token 消耗
agent = CodeAgent(model=model, max_steps=3)

# 监控 Token 使用
for step in agent.memory.steps:
if hasattr(step, 'token_usage'):
print(f"输入: {step.token_usage.input_tokens}")
print(f"输出: {step.token_usage.output_tokens}")

7.3 扩展建议

  • 自定义 Prompt:修改 system_prompt 定制 Agent 行为
  • 多模型对比:测试 Llama、Mistral 等不同模型表现
  • 工作流编排:组合多个 Agent 完成复杂任务

📖 八、学习检查清单

  • 理解 CodeAgent vs ToolCallingAgent 区别
  • 掌握 AgentMemory 和 ActionStep 使用
  • 学会两种自定义工具定义方式
  • 理解沙箱安全机制
  • 掌握多智能体协作模式
  • 修正:使用 memory.steps 而非已弃用的 logs
  • 能够独立运行所有测试

📝 九、参考命令速查

1
2
3
4
5
6
7
8
# 运行单个测试
pytest test_smolagents.py::test_code_agent_basic -v

# 运行所有测试(带详细输出)
python test_smolagents.py

# 调试模式(打印完整执行过程)
python -m pdb test_smolagents.py

🎯 本文档基于 smolagents 1.26.0 实际测试整理,可作为框架学习的完整参考手册!