跳到主要内容

模型接口与记忆

两个支撑性子系统:Model 让 agent 不关心背后是哪家 LLM;Memory 让 agent 的每一步既能存下来(回放/序列化),又能变回对话喂给模型。它们是循环(01)两端的适配层。

1. Model:把各家 LLM 抹平成一个接口

1.1 要解决的小问题

OpenAI、Anthropic、HF Inference、本地 Transformers……每家 API 的入参名、消息格式、工具调用返回都不一样。agent 循环不该被这些差异污染。Model 基类(models.py:452)定义一个统一契约:

:一列 ChatMessage +(可选)停止序列、response_format、可调工具。:一个 ChatMessage

核心方法就一个 generate(models.py:553),各家子类实现自己那版。

1.2 统一契约

子类背后文件位置
InferenceClientModelHF Inference Providersmodels.py:1456
LiteLLMModelLiteLLM(100+ 家)models.py:1205
OpenAIModel / AzureOpenAIModelOpenAI 兼容端点models.py:1646 / 1799
AmazonBedrockModelAWS Bedrockmodels.py:1859
TransformersModel / VLLMModel / MLXModel本地推理models.py:860 / 633 / 751

1.3 两处抹平差异的关键点

入参装配 _prepare_completion_kwargs(models.py:502):把内部的 ChatMessage 列表清洗成各家要的 dict(get_clean_message_list,:332,处理角色映射、图片转 URL、内容扁平化),再按明确的优先级合并 stoptoolsresponse_format 和用户 kwargs。工具在这里被转成 JSON schema 塞进 tools 字段(:539-542)。

出参兜底 parse_tool_calls(models.py:583):有些模型不返回结构化 tool_calls,而是把工具调用写在文本里。这个方法从文本里把工具名和参数解析出来,补成标准 tool_calls——让 ToolCallingAgent 即便对着「不太会 function calling」的模型也能工作(agents.py:1327-1331 会调它)。

还有 supports_stop_parameter(models.py:418)这类小适配:某些模型不接受 stop 参数,就自动不传。这些琐碎补丁正是「统一接口」的成本所在。

2. Memory:存储与上下文两用

2.1 要解决的小问题

agent 每一步产生一堆东西:模型说了什么、调了什么工具、观测是什么、有没有报错。这些既要存起来(回放、调试、序列化到 Hub),又要在下一步变回对话喂给模型。两个用途、一份数据。

2.2 每步是一个结构化对象

AgentMemory(memory.py:214)持有一个 steps 列表,元素是几种 MemoryStep:

步类型存什么符号
SystemPromptStep系统提示memory.py:199
TaskStep用户任务(可带图)memory.py:186
PlanningStep一次规划的产出memory.py:153
ActionStep一个行动步的全部:模型输出、工具调用、代码、观测、错误、token 用量memory.py:51
FinalAnswerStep最终答案memory.py:209

2.3 关键机制:同一步,两种投影

每个步类型都实现 to_messages(),把自己变回模型能读的 ChatMessage。循环开头的 write_memory_to_messages(agents.py:758)就是把系统提示 + 每一步的 to_messages() 顺次拼成完整对话历史。

ActionStep.to_messages(memory.py:92)为例,它把一步拆成多条消息:

ActionStep
├─ model_output → assistant 消息(模型那段思考+代码)
├─ tool_calls → "Calling tools: [...]"
├─ observations → "Observation:\n..."(工具/执行结果)
└─ error → "Error:\n... Now let's retry: 别重复之前的错"

错误也被翻译成上下文(memory.py:138-148):上一步的报错会变成一条带「别再犯同样错误」提示的消息——这正是 01 讲的「错误即上下文、自我纠错」在数据层的落点。

2.4 关键机制:summary_mode(给规划步用的精简历史)

to_messages(summary_mode=True) 会丢掉一些内容:PlanningStep 在 summary 模式返回空(memory.py:174-176),ActionStep 略过冗长的 model_output。规划步(01 §6)用它拿一份「不被旧计划过度影响」的精简历史。

2.5 序列化与回调

  • 存/取:dict() 把每步转成可 JSON 化的结构(处理嵌套 dataclass、图片转 bytes,memory.py:66-90),支撑 agent 存到 Hub、replay() 回放(memory.py:248)。
  • 回调:CallbackRegistry(memory.py:280)让你按步类型注册回调,每步结束时触发(agents.py:620-623_finalize_step)——用于自定义日志、遥测、可视化。

3. 边界与坑

  • 上下文只增不减:每步都往历史里加,长任务会逼近上下文上限。观测被 truncate_content 截断缓解,但没有自动摘要/淘汰旧步的机制(除了规划步的 summary_mode)。
  • 模型接口是「最小公倍数」:统一契约意味着某些模型的独有能力要靠 **kwargs 透传,不是一等公民。
  • token 统计依赖各家如实上报:某步缺 token_usage 时,RunResult 的总量会标为不可用(agents.py:512-521)。

4. 代码地图

主题文件符号
模型统一基类src/smolagents/models.pyModel
生成(子类实现)src/smolagents/models.pyModel.generate
入参装配src/smolagents/models.pyModel._prepare_completion_kwargs
从文本兜底解析工具调用src/smolagents/models.pyModel.parse_tool_calls
消息清洗src/smolagents/models.pyget_clean_message_list
记忆容器src/smolagents/memory.pyAgentMemory
行动步 + 变回消息src/smolagents/memory.pyActionStep / ActionStep.to_messages
记忆回灌src/smolagents/agents.pyMultiStepAgent.write_memory_to_messages
步回调src/smolagents/memory.pyCallbackRegistry