使用 AI 代理自动化机器学习
Source: Dev.to
概览
在 Kaggle 竞赛中快速会注意到一种模式:
- 基线 – 上传数据,运行 CatBoost 或 LightGBM,获取基线指标(≈ ½ 小时)。
- 顶级方案 – 需要数十种预处理选项、数百种特征组合以及数千个超参数集合。
现有的 AutoML 系统帮助不大:
| 系统 | 工作原理 | 局限性 |
|---|---|---|
| AutoGluon | 训练多个模型并构建多层集成。 | 每次运行都从头开始。 |
| TPOT | 通过遗传算法生成流水线。 | 未从先前运行中学习。 |
| Typical AutoML | 尝试固定的一组算法,根据指标挑选最佳。 | 没有推理、没有适应、没有经验积累。 |
主要问题是 缺乏推理。这些系统不会分析某种方法为何成功或失败,也不会针对新任务的具体情况进行适应。每个新数据集都被视作第一次处理。
人类的工作方式不同。数据科学家一看到类别不平衡,就会立刻考虑分层抽样和阈值选择;如果之前处理过类似问题,他们会复用有效的方案。首次尝试失败时,他们会分析原因并尝试不同的方法。
类人 AutoML 架构
随着大语言模型(LLMs)的出现,构建更像人类推理的系统成为可能。LLM 可以:
- 分析数据。
- 推理方法选择。
- 从示例中学习。
然而,仅靠单一模型仍可能遗漏明显错误或陷入错误的思路。因此我们需要一种 能够让系统自我检查并积累经验的架构。
Actor‑Critic 启发
在强化学习中,Actor‑Critic 方法使用两个代理:
- Actor – 执行动作。
- Critic – 评估这些动作。
将此思路应用于 AutoML:
| 角色 | 职责 |
|---|---|
| Actor | 接收数据和一套专用服务工具箱(MCP 服务器)。探索数据集,决定需要哪些步骤,并生成解决方案(报告 + 工件)。 |
| Critic | 仅接收 Actor 的报告(不使用工具)。检查是否全部正确完成。如发现问题,返回反馈以便 Actor 迭代。 |
| Memory | 每次迭代后,将经验(报告、反馈、结果)存储起来,随后在类似任务中检索。 |
循环为:Actor → Critic → 反馈 → Actor(重复)。
Source: …
工具:MCP(模型上下文协议)
LLM 可以进行推理,但它们需要 工具 来操作数据。我把工具分为四类:
- 数据预览 – 快速检查原始文件。
- 统计分析 – 描述性统计、缺失值诊断等。
- 处理 – 编码、插补、缩放等。
- 模型训练 – 拟合模型、生成预测、集成。
示例:数据预览工具输出
{
"shape": [150, 5],
"columns": ["sepal_length", "sepal_width", "petal_length", "petal_width", "species"],
"dtypes": {
"sepal_length": "Float64",
"species": "String"
},
"sample": [
{"sepal_length": 5.1, "species": "setosa"},
...
]
}
Actor 可以看到维度、列类型以及几行样本——足以决定下一步操作。
一致的预处理
一个关键要求是 对训练集和测试集进行相同的转换。例如,如果在训练集里把分类特征编码为 {"red": 0, "blue": 1},则必须在测试集上使用相同的映射。映射会保存为 JSON 文件:
mapping_path = Path(output_dir) / f"{column}_mapping.json"
with open(mapping_path, "w") as f:
json.dump(mapping, f)
这在分类任务中尤为重要:模型输出的是数值代码,必须转换回原始的类别标签。
训练工具合约
每个训练工具返回 三个项目:
- 已保存模型的路径。
- 预测文件的路径。
- 在训练数据上计算的指标。
路径会使用时间戳和 UUID 生成,使 Actor 能够并行运行多个算法而不会产生命名冲突。
扩展工具箱
当工具数量超过十个时,管理、支持和扩展它们会变得繁琐。FastMCP 框架(Model Context Protocol 的实现)通过以下方式解决此问题:
- 将每个工具打包为独立的服务器。
- 暴露一个简单的 RPC 风格 API,供 Actor 按需调用。
我创建了五个 MCP 服务器:
| Server | Purpose |
|---|---|
file_operations | 文件 I/O 实用工具。 |
data_preview | 快速 CSV 预览。 |
data_analysis | 统计摘要。 |
data_processing | 转换(编码、填补、缩放)。 |
machine_learning | 模型训练、预测、集成。 |
结构化报告与多评审批评者
Actor 生成一份包含四个章节的结构化报告:
- 数据分析
- 预处理
- 模型训练
- 结果
Critic 不使用任何工具,只阅读报告。与单一的整体评审不同,我采用 四位专门的 LLM 评审,每位聚焦于一个章节。
judges = [
LLMJudge(rubric="Evaluate data_analysis: Is exploration thorough?"),
LLMJudge(rubric="Evaluate preprocessing: Are steps appropriate?"),
LLMJudge(rubric="Evaluate model_training: Is selection justified?"),
LLMJudge(rubric="Evaluate results: Are metrics calculated correctly?"),
]
每位评审返回:
- 一个 0 到 1 之间的分数。
- 一个 解释说明,阐述评分理由。
整体 Critic 分数 为四位评审分数的平均值。如果平均分低于预设阈值,Critic 会向 Actor 发送详细反馈,Actor 随后修正其方案并进入迭代。
端到端流程(伪代码)
memory = ExperienceMemory()
def auto_ml_pipeline(data_path):
# 1. Load prior experience (if any)
context = memory.retrieve_similar(data_path)
# 2. Actor generates an initial solution
report, artifacts = Actor.run(data_path, context)
while True:
# 3. Critic evaluates the report
scores, feedback = Critic.evaluate(report)
# 4. If the average score is high enough → finish
if sum(scores) / len(scores) >= 0.85:
break
# 5. Otherwise, give feedback to Actor and iterate
report, artifacts = Actor.improve(report, feedback)
# 6. Store the successful experience for future tasks
memory.store(data_path, report, artifacts, scores)
return artifacts["predictions_path"]
要点
- 推理 + 工具 → 单独的 LLM 无法操作数据;工具箱(MCP 服务器)弥补了这一缺口。
- Actor‑Critic 循环 能及早捕捉错误并推动迭代改进。
- 专用评审 提供聚焦且细粒度的反馈,而不是单一的整体评估。
- 经验记忆 使系统能够在多个任务中积累知识,使 AutoML 更接近人类水平的专长。
迭代决策与 Actor 与 Critic
Actor 的方案会与接受阈值(通常为 0.75)进行比较。
- 如果分数更高,则接受该决定。
- 否则,Critic 会收集所有评审的评论反馈,并将其传回给 Actor 进行下一轮迭代。
这种多评审者的方法比依赖单一评审更稳定。
单个 LLM 可能过于严格或遗漏明显错误,而四位专门的评审可以平滑主观性。
文件系统隔离
当代理处理文件时,不得拥有对整个文件系统的访问权限。通过为每个会话创建专用目录来实现隔离:
~/.scald/actor/
│
├─ data/ # copies of the source data
├─ output/ # intermediate files
└─ workspace/ # models and predictions
- 源 CSV 文件会被复制到
data/。 - 所有工具只能在这些目录内运行,防止意外覆盖重要文件或读取无关数据。
运行结束后,所有产出会被复制到带时间戳的 会话目录,工作区会被清空。你可以稍后检查该目录,以准确了解代理的操作情况:
- 哪些模型已被训练(从
.pkl加载) - 获得了哪些指标
- 执行了哪些步骤
经验存储与检索
在每次迭代后,系统会保存经验:
self.mm.save(
actor_solution=actor_solution,
critic_evaluation=critic_evaluation,
task_type=task_type,
iteration=iteration,
)
搜索相似的过去解决方案
# Retrieve the most relevant memories
actor_memory, critic_memory = self.mm.retrieve(
actor_report=actor_solution.report,
task_type=task_type,
top_k=5,
)
- Actor 报告 和 Critic 评估 被存储在 ChromaDB 向量数据库中。
- 当有新任务到来时,系统会使用 Jina 嵌入模型进行语义搜索,以找到相似的过去解决方案。
- 这些解决方案会作为上下文提供给代理(agent)。
即使是不成功的尝试也很有价值。
如果 Critic 曾说过“你忘记处理间隙”,这条反馈可以指导后续任务。语义搜索同样会把此类案例检索出来。
Source: …
完整迭代循环
当所有组件准备就绪时,循环将持续运行,直至:
- 达到最大迭代次数,或
- 评论者(Critic)作出最终决定。
在每一次迭代中:
- 执行者(Actor) 解决问题,并结合任何反馈。
- 评论者(Critic) 评估该解决方案。
- 将经验存入记忆。
- 为下一次尝试提取相关上下文。
观察执行者的学习
- 第 1 次迭代:简单的预处理 + 单一模型。
- 评论者反馈:“你没有检查类别平衡”,“缺少特征工程”。
- 第 2 次迭代:补充缺失的步骤,尝试多个模型,构建集成模型。
一个具体的失败案例:执行者对目标列进行了编码,训练了模型,但 忘记对预测结果进行解码。输出的是数值 ID 而不是类别标签。解决办法是,在系统提示中加入明确指令:
如果你对目标列进行编码,必须在返回之前对预测结果进行解码。
使用decode_categorical_label,并使用编码步骤中生成的映射路径。
当执行者尝试多个模型时,文件可能会相互覆盖。让大型语言模型生成唯一文件名的提示并不可靠。更稳妥的做法是在工具层面处理命名,在每个文件名后追加 时间戳 和 UUID。
实验结果
系统在多个 OpenML 数据集上进行了评估。
| 数据集 | 指标 (F1) | 基线 (RF) | 基线 (AutoGluon) | 基线 (FLAML) |
|---|---|---|---|---|
| christine | 0.743 | 0.713 (‑4 %) | – | – |
| cnae‑9 | 0.980 | – | – | 0.945 (‑3.5 %) |
| Australian | 0.836 | – | 0.860 | – |
| blood‑transfusion | 0.756 | 0.712 | 0.734 | 0.767 (‑1.5 %) |
- 每次运行的成本 在 $0.14 到 $3.43 之间,取决于任务复杂度和迭代次数。
- 运行时间 从 1 分钟 到 30 分钟 不等。
系统的价值不仅在于原始指标本身,更在于它所实现的 智能自动化。
通过对工作流进行模块化(MCP),我们可以为任何任务插入专门的代理,保持单一的迭代改进循环,并随时间累积经验。
局限性
- 最适用于使用 梯度提升 算法的 表格数据。
- 对 深度学习 或 时间序列 任务并非开箱即用(需要额外工具)。
- 整体质量在很大程度上取决于底层大语言模型的规模和能力。
入门
安装
pip install scald
使用 CLI
scald --train data/train.csv \
--test data/test.csv \
--target price \
--task-type regression
使用 Python API
from scald import Scald
scald = Scald(max_iterations=5)
predictions = await scald.run(
train="data/train.csv", # .csv or pandas DataFrame
test="data/test.csv",
target="target_column",
task_type="classification",
)
注意: 您需要从兼容 OpenAI 的提供商(例如 OpenRouter)获取 API 密钥。
附加信息
- Need a key from Jina 用于记忆系统中的嵌入(该服务在注册后提供大量免费令牌)。
- All code is packaged in a library 并已在 GitHub 上提供。