Agent-Lightning 实战全流程:从Agentic RL原理到 4×A800部署与SQL-Agent强化学习训练

环境:Ubuntu 22.04 · 4×NVIDIA A800 80GB · Python 3.12 · verl 0.5.0 + vllm 0.10.2 + agentlightning 0.3.0
项目:基于 LangGraph 的 SQL-Agent,使用 Agentic RL(GRPO)训练 Qwen3-1.7B 提升文本转 SQL 能力

1. 原理

2. 环境部署

3. 项目结构与数据准备

3.1 目录结构

/work/agent_lignting/
├── models/Qwen3-1.7B/          # 模型权重(modelscope 下载,13 个文件)
├── SQL-Agent-RL/
│   ├── data/                   # Spider 数据集
│   │   ├── train_spider.parquet    # 训练集 7000 样本
│   │   ├── test_dev_500.parquet    # 验证集 500 样本
│   │   ├── test_dev.parquet        # 快速验证 100 样本
│   │   └── database/               # 200+ 个 SQLite 数据库
│   ├── models -> ../models     # 软链接(让 models/Qwen3-1.7B)
│   ├── spider/
│   │   ├── sql_agent.py            # LangGraph SQL-Agent + LitSQLAgent
│   │   ├── train_sql_agent.py      # 训练脚本 + 配置
│   │   └── spider_eval/            # Spider 官方评估函数
│   ├── swanlog/                # swanlab 本地缓存
│   └── checkpoints/            # verl 断点(save_freq=10)

3.2 数据说明

Spider 数据集是大规模跨域文本到 SQL 数据集(耶鲁大学),包含约 200 个不同业务领域的 SQLite 数据库,每个 parquet 文件包含字段:

  • question:自然语言问题
  • db_id:数据库标识符
  • query:标准答案 SQL

跨域特性使训练出的模型有更好的泛化能力。数据库为 SQLite 文件,零配置,无需独立数据库服务器。

3.3 模型下载

pip install modelscope
mkdir -p ./models/Qwen3-1.7B
modelscope download --model Qwen/Qwen3-1.7B --local_dir ./models/Qwen3-1.7B

下载后需验证 safetensors 完整性:

python -c "
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
m = AutoModelForCausalLM.from_pretrained('models/Qwen3-1.7B', torch_dtype=torch.bfloat16).to('cuda')
print('模型加载成功', sum(p.numel() for p in m.parameters())/1e9, 'B')
"

⚠️ Qwen3 thinking 模式:Qwen3 默认开启思考模式,会输出 <think>...</think> 块。本项目已在 models/Qwen3-1.7B/tokenizer_config.json 中修补为默认关闭(备份 tokenizer_config.json.bak),否则 SQL-Agent 的 parse_query 会把 thinking 内容当 SQL 执行而报错。

{%- if add_generation_prompt %}
    {{- '<|im_start|>assistant\n' }}
    {%- if enable_thinking ... %}           <!-- 这里被修改 -->
        {{- ' thinking\n\n response\n\n' }}
    {%- endif %}
{%- endif %}

4. 训练配置详解

训练脚本 spider/train_sql_agent.py 的核心配置(已优化到 4 卡稳定运行):

RL_TRAINING_CONFIG = {
    "algorithm": {
        "adv_estimator": "grpo",        # GRPO 算法
        "use_kl_in_reward": False,
    },
    "data": {
        "train_files": "data/train_spider.parquet",   # 训练集 7000 样本
        "val_files": "data/test_dev_500.parquet",     # 验证集 500 样本
        "train_batch_size": 32,
        "max_prompt_length": 4096,
        "max_response_length": 2048,
        "truncation": "error",
    },
    "actor_rollout_ref": {
        "rollout": {
            "tensor_model_parallel_size": 1,          # 4 卡下 vLLM 按 DP=4 并行
            "n": 4,                                   # 每个样本生成 4 条轨迹
            "log_prob_micro_batch_size_per_gpu": 4,
            "multi_turn": {"format": "hermes"},
            "name": "vllm",                           # verl 自建 vLLM 引擎
            "gpu_memory_utilization": 0.6,            # ★ 防 OOM 关键值
            "engine_kwargs": {"vllm": {
                "enable_auto_tool_choice": True,
                "tool_call_parser": "hermes",
            }},
        },
        "actor": {
            "ppo_mini_batch_size": 32,
            "ppo_micro_batch_size_per_gpu": 4,
            "optim": {"lr": 1e-6},
            "use_kl_loss": False,
            "clip_ratio_low": 0.2, "clip_ratio_high": 0.3,
            "fsdp_config": {
                "param_offload": False,       # ★ 小模型不需要 CPU offload
                "optimizer_offload": False,   # ★ 去掉后参数更新快 2.9 倍
            },
        },
        "ref": {
            "log_prob_micro_batch_size_per_gpu": 8,
            "fsdp_config": {"param_offload": False},
        },
        "model": {
            "path": "models/Qwen3-1.7B",      # 本地模型
            "use_remove_padding": True,       # 依赖 flash-attn
            "enable_gradient_checkpointing": True,
        },
    },
    "trainer": {
        "n_gpus_per_node": 4,                 # ★ 4 卡并行
        "val_before_train": True,
        "logger": ["console", "swanlab"],     # ★ swanlab 日志
        "project_name": "Qwen3-1.7B-AgenticRL",
        "experiment_name": "spider",          # 固定名称保证 checkpoint 可续训
        "nnodes": 1,
        "test_freq": 32,                      # 每 32 步验证一次
        "total_epochs": 2,
        "save_freq": 10,                      # ★ 每 10 步保存 checkpoint
        "resume_mode": "auto",                # ★ 崩溃自动续训
    },
}

4.1 关键配置项解析

配置 说明
gpu_memory_utilization 0.6 防 OOM 的关键。0.8 会导致 KV cache 唤醒时显存不足死锁
param_offload/optimizer_offload False 1.7B 模型 4 卡完全装得下,offload 是负优化
n_runners(train() 函数内) 32 并行 SQL-Agent worker 数,提升 GPU 利用率
save_freq / resume_mode 10 / auto 每 10 步保存断点,崩溃自动续训
n_gpus_per_node 4 4 卡 FSDP + vLLM DP 并行

4.2 三种训练模式

参数 fast(冒烟) qwen(正式)
验证集 test_dev(100) test_dev_500(500)
epochs / 步数 1 步 2 epochs(~438 步)
gpu_memory_utilization 0.6 0.6
模型 models/Qwen3-1.7B models/Qwen3-1.7B
说明 CI 快速验证 正式实验

5. SwanLab 实验日志系统

SwanLab 是国产免费的实验跟踪平台,接口与 wandb 高度兼容,支持云端与本地两种模式,提供实时可视化、超参数记录、多实验对比、团队协作等完整功能,且国内访问快。

5.1 安装与登录

pip install swanlab

# 注册获取 API Key(https://swanlab.cn → 设置 → API Key)
swanlab login --api-key <你的APIKey>
# 示例:swanlab login --api-key xxx

5.2 快速上手

import swanlab
import random

# 初始化实验
swanlab.init(
    project="Qwen3-1.7B-AgenticRL",   # 项目名
    workspace="yuanyou",              # 工作区(账号)
    config={                          # 超参数记录
        "learning_rate": 1e-6,
        "model": "Qwen3-1.7B",
    },
)

# 训练循环中上报指标
for epoch in range(1, 11):
    acc = 1 - 2 ** -epoch - random.random() / epoch
    loss = 2 ** -epoch + random.random() / epoch
    swanlab.log({"acc": acc, "loss": loss})   # 实时上报

swanlab.finish()   # 结束实验(notebook 中必须调用)

5.3 verl 原生集成

  • verl 0.5.0 原生支持 swanlab 后端,无需修改任何代码。只需在训练配置的 trainer.logger 里启用:
"logger": ["console", "swanlab"],       # 启用 swanlab 日志
"project_name": "Qwen3-1.7B-AgenticRL",

verl 的 verl/utils/tracking.py 内置了 if "swanlab" in default_backend 分支,会自动:

  1. 读取环境变量 SWANLAB_API_KEY(可选,已登录可省略)
  2. 调用 swanlab.init(project=, experiment_name=, config=, logdir=, mode=)
  3. 每步结束后批量上报训练指标(loss、reward、timing 等 80+ 个指标)
  4. 验证集生成结果通过 swanlab.echarts.Table 记录

运行时的环境变量

export SWANLAB_API_KEY=xxx   # 设置后 verl 会自动 login
export SWANLAB_MODE=cloud                       # 云端(默认);本地实验可用 local/offline

6. 完整运行流程:从 fast 冒烟到正式训练

6.1 环境激活

source /opt/conda/etc/profile.d/conda.sh
conda activate agent_lighting
cd /mnt/nodestorlv/users/sales06/work/agent_lignting/SQL-Agent-RL

6.2 预检

# 关键包可导入
python -c "import torch,vllm,verl,flash_attn; import agentlightning as agl; print('stack OK', agl.__version__)"

# 配置可构造
python -c "
import sys; sys.path.insert(0, 'spider')
from train_sql_agent import config_train_qwen
import agentlightning as agl
alg = agl.VERL(config_train_qwen())
tr = agl.Trainer(n_runners=32, algorithm=alg)
print('4卡 qwen 配置 OK')"

# GPU 4-7 空闲确认
nvidia-smi --query-gpu=index,memory.used --format=csv,noheader | awk -F',' '$1>=4 && $2+0>1000 {print "GPU "$1" 被占用"}'

6.3 fast 冒烟实验(~15 分钟)

CUDA_VISIBLE_DEVICES=4,5,6,7 SWANLAB_API_KEY=4nPZGCu0kzMkkcGdObxmn \
  python spider/train_sql_agent.py fast

成功标志:日志出现 step:1val/reward 有数值;4 卡利用率 ~30%+。

6.4 正式训练(全参,约27-20小时)

nohup env CUDA_VISIBLE_DEVICES=4,5,6,7 SWANLAB_API_KEY=4nPZGCu0kzMkkcGdObxmn \
  python spider/train_sql_agent.py qwen > train.log 2>&1 &

nohup ... & 保证关掉终端训练不中断(nohup 忽略 SIGHUP,无控制终端)。
预期节奏

  • 初始化 ~3 分钟(ray 集群 + 4 卡加载模型 + verl 内部 vLLM 引擎)
  • 初始验证 500×4=2000 rollout(~25 分钟)
  • 每步 ~60s(生成 + GRPO 更新),共 ~438 步
  • 每 32 步跑一次验证
  • 每 10 步保存 checkpoint

6.5 监控

tail -f /tmp/qwen_train_full.log                    # 实时日志
watch -n 2 nvidia-smi                               # GPU 利用率
grep -c "Completed in" train.log                    # rollout 进度
grep -E "step:[0-9]+ - val/reward" train.log        # 验证集指标
# swanlab 云端看板:https://swanlab.cn/@yuanyou/Qwen3-1.7B-AgenticRL

6.6 断点续训

训练中断(崩溃/关机)后,直接重新运行同一条命令即可自动恢复:

# checkpoint 保存于 SQL-Agent-RL/checkpoints/Qwen3-1.7B-AgenticRL/spider/global_step_<N>/
# resume_mode: auto 会从最近断点继续,最多丢 10 步

7. 踩坑记录与性能优化

7.1 训练中途 OOM 死锁(必踩)

现象:训练正常跑了几十步后突然卡死,日志:

RuntimeError: CUDA Error: out of memory at /workspace/csrc/cumem_allocator.cpp:62

发生在 vLLM wake_up(tags=["kv_cache"])(训练步切回 rollout 阶段)。卡死后步数不再增长、GPU 0% 利用率但显存被占。

根因:verl 把 FSDP 训练和 vLLM 引擎共卡部署gpu_memory_utilization 设为 0.8 时,vLLM 预留 64GB/卡 做 KV cache,训练步结束后切回 rollout 时 KV cache 重新分配显存不足 → OOM → worker 崩 → 死锁。实测 0.8 在 step 55 挂,0.6 稳定无 OOM

修复gpu_memory_utilization 降到 0.6。1.7B 模型 KV cache 需求小,48GB 预留足够,代价(生成吞吐略降)可忽略。

7.2 LoRA 不可用(版本兼容性)

现象:启用 LoRA(lora_rank: 16)后:

  • reward 迅速崩塌(0.63 → 0.09 → 0.08)
  • update_actor 反而变慢(32-44s vs 全参 24s)
  • vLLM 报 Error in preprocessing prompt inputs

根因verl 0.5.0 + vllm 0.10.2 的 LoRA 集成不兼容。verl 0.5.0 官方锁 vllm<=0.8.5,本项目用 0.10.2。LoRA 适配器权重需要在 FSDP worker ↔ vLLM 引擎间同步,该同步在 0.10.2 上失效——rollout 用到错误权重,模型在收敛但生成越来越烂。

  • 结论本项目坚持全参微调,不要用 LoRA

7.3 性能优化记录(实测)

优化项 修改前 修改后 效果
CPU offload True False 参数更新 2.9× 加速
并行 runner 10 32 rollout 吞吐 +30%,GPU 利用率均衡
gpu_memory_utilization 0.8 0.6 消除 OOM 死锁
checkpoint save_freq=10 中断可恢复

单步耗时实测:1卡+offload 200s → 4卡+优化后 46-100s(稳态 ~60s)。训练步阶段 4 卡利用率 100%,rollout 阶段 ~35%。

7.4 为什么 GPU 利用率不是 100%

  • Agentic RL 的 rollout 是串行多轮 agent 执行:每个 rollout = SQL-Agent 的 write→执行SQL→check→rewrite 循环(平均 3.7 轮),LLM 调用之间夹着 SQLite 执行、评估、HTTP 往返等 CPU 工作。GPU 在 agent 的 CPU 工作期间空闲,这是该负载的固有特性。32 runner 已远超 GPU 需求,进一步加大收益递减。

8. 实验进展与结果

8.1 训练状态

当前正式实验运行中(4×A800 全参微调 Qwen3-1.7B,GRPO,2 epochs):

指标 数值
模型 Qwen3-1.7B(1.7B 参数,全参 FSDP,bf16)
训练数据 Spider 训练集 7000 样本
单步耗时 ~60s
checkpoint 每 10 步保存
日志 SwanLab 云端实时上报

8.2 验证集指标

验证点 val/reward
step 0(基线) 0.534
step 32 0.594
step 64 0.612
step 96 0.638
step 128 0.626
step 160 0.676
step 192 0.674
step 224 0.672
step 256 0.690
step 288 0.686
step 320 0.680
step 352 0.682
step 384 0.700
step 416 0.692
最终 0.712

验证集正确率单调上升(0.534 → 0.712,+33.3%),说明 GRPO 训练真实提升了模型的 SQL 生成能力,且具备泛化性(不是在训练集上过拟合)。

8.3 训练奖励:分段稳步提升

训练阶段 平均训练 reward
step 1-100 0.573
step 101-200 0.624
step 201-300 0.649
step 301-436 0.669
  • 训练奖励从 0.573 稳步提升到 0.669(+16.8%),四个阶段单调上升
  • 训练后期出现 0.78-0.79 的高位(step 432: 0.789, step 433: 0.781)
  • 训练奖励 < 验证集奖励(0.669 vs 0.712)——因为训练集是 7000 样本的更全集合,含更多难题

9. 正式效果评测(后训练评估)

9.1 评测设置

  • 模型:合并训练后的 Qwen3-1.7B(global_step_436 checkpoint,经 verl FSDP 分片合并重建)
  • 测试集test_dev_500.parquet(500 条,与训练验证集一致,便于对比)
  • 评测方式:加载训练后模型 → vLLM 服务 → 运行完整 SQL-Agent(write→execute→check→rewrite,max_turns=3)→ eval_exec_match 执行结果匹配
  • 推理配置:temperature=0(贪心解码)、4 卡并行、20 个评估 worker

9.2 评测结果

指标 数值
总样本 500
正确 309
准确率 61.8%
平均耗时 14.2s/条
400 错误 0

9.3 与训练验证的对比(关键差异)

维度 训练验证(val/reward) 正式评测
每问题生成数 n=4(4 条采样轨迹) 1 条(贪心解码)
准确率 71.2% 61.8%

差异解读

  • 训练验证的 71.2% 是"每个问题采样 4 次"的平均奖励——采样多样性给了更多命中正确 SQL 的机会
  • 正式评测的 61.8% 是单次贪心生成的冷准确率——更保守、更真实地反映模型单次能力
  • 两者差距(~9.4 个百分点)说明采样多样性(n=4)有实际收益,贪心解码会损失一部分正确率

9.4 评测结论

  1. 训练后模型的单次 SQL 生成准确率 61.8%(相对基线 53.4% 提升 8.4 个百分点),配合 n=4 采样可达 71.2%
  2. 4 卡并行评估方案验证有效:GPU 利用率 80%+,500 条评测 ~15 分钟完成(对比单卡串行 ~2 小时)
  3. 评测与训练验证口径不同(n=1 贪心 vs n=4 采样),单次准确率是更保守的评估

10. 总结与展望

10.1 核心收获

  1. Agentic RL 让"小模型低成本"成为可能:通过 GRPO 从执行反馈中学习,1.7B 模型在文本转 SQL 任务上能达到约 64% 正确率并持续提升,验证了"低成本 + 高能力"的技术路径。
  2. 工业级架构的价值:LangGraph(Agent 执行)+ verl(RL 训练)+ Agent-Lightning(封装桥接)的"运行与训练分离"架构,让 Agent 上线后还能持续迭代。
  3. 环境部署的经验:flash-attn 用官方预编译 wheel 零编译、三个版本坑(libstdc++/fastapi/transformers)、Qwen3 thinking 模式处理。
  4. 4 卡训练的经验:防 OOM(gpu_memory_utilization 0.6)、去 CPU offload、加大 runner 并行、checkpoint 断点续训。

10.2 展望

  • 模型升级:Qwen3-Coder 系列(4B/8B)在 SQL/代码任务上更强,80GB 卡可承载。
  • 算法探索:尝试不同 adv_estimator、reward shaping(SQL 执行时间、结果集相似度等)。
  • 任务迁移:同一套架构可迁移到数据分析、检索问答、多工具 Agent 等场景,只需替换 LangGraph 执行节点和奖励函数。
  • LoRA 前瞻:若升级到与 vllm 0.10.2 兼容的 verl 版本,可重新评估 LoRA 以降低训练成本。
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐