Agent Function-Calling + DPO RL 强化学习微调实战

为什么要提升Agent Function-Calling 能力?

Agent 在调用工具(Function-Calling)时,可能会遇到以下问题:
❌ 选择错误的工具
❌ 参数拼接错误
❌ 多轮对话中遗漏关键信息
❌ 明明需要工具却回答“我不知道”
❌ 工具调用格式不符合 API Schema

本文教你,使用 llama factory 通过 强化学习(RL)中的 DPO算法 进行微调,提升 Agent 工具调用(Function-Calling)能力,不再遇到上述问题。

微调步骤概览

想要进行 DPO 微调,需要先进行以下步骤:

  1. 记录 Agent 调用过程:使用 DeepSeek Agent 调用项目,记录 Agent 的调用过程。
  2. 通过 AutoToolDPO 生成 DPO 微调数据集:将记录的调用过程自动转换为 DPO 微调数据集。
  3. 使用 LLaMA Factory 微调模型:使用生成的数据集进行微调。

所有项目代码可加 小助理 免费领取

一、记录 Agent 调用过程

通过轻量级的 DeepSeek Agent 调用项目,完整记录 Agent 的调用过程,为后续数据集构建提供原始数据。

项目结构

├── config.py           # 配置文件 - API 配置和系统参数
├── tools.py            # 工具函数库 - Agent 可调用的工具集
├── agent_caller.py     # 主程序 - Agent 调用逻辑和日志记录
├── requirements.txt    # Python 依赖包清单
├── .env.example        # 环境变量模板
├── .env                # 环境变量配置(需自行创建)
├── README.md           # 项目文档
└── logs/               # 日志目录(自动生成)
    └── agent_call_*.json

运行:

python agent_caller.py

获取agent调用过程

二、通过 AutoToolDPO 生成 DPO 微调数据集

将 Agent 调用过程的 JSON 文件通过自研的 AutoToolDPO 工具进行处理,自动生成可用于 DPO 微调的 JSONL 格式数据集。

项目概述

项目定位

AutoToolDPO 是一个企业级 Agent 工具调用 DPO 数据集自动构建系统,旨在解决高质量训练数据的获取难题。

核心问题

训练一个能够正确调用工具的 AI Agent,需要大量高质量的训练数据,这些数据应包括:

  • 用户查询:自然语言表述的问题
  • 正确响应(Chosen):AI 应该调用的正确工具及参数
  • 错误响应(Rejected):AI 不应该调用的错误工具或错误参数

传统的人工标注成本极高,本项目通过 LLM 自动生成的方式高效解决这一问题。

项目价值

工作流程

输入:工具定义(JSON)+ 配置参数
处理:自动生成正负样本对比数据
输出:符合 DPO 格式的 JSONL 数据集

价值:将数周的人工标注工作缩短至数小时

核心概念解释

DPO(Direct Preference Optimization)

DPO 是一种强化学习方法,通过对比学习让模型区分"优质响应"与"劣质响应",从而实现模型对齐。

标准数据格式

{
  "system": "系统提示词",
  "tools": "[工具定义JSON]",
  "messages": [
    {"role": "user", "content": "北京天气怎么样?"}
  ],
  "chosen": "<function_call>\n{\"name\": \"get_weather@v1\", \"arguments\": {\"city\": \"北京\"}}\n</function_call>",
  "rejected": "我不知道,你可以查天气预报"
}

核心思想

  • chosen:正确的回答(正样本)- 模型应该学习的目标响应
  • rejected:错误的回答(负样本)- 模型应该避免的响应
  • 学习目标:提高 chosen 的生成概率,降低 rejected 的生成概率
Agent 工具调用机制

Agent 根据用户的自然语言问题,从可用工具库中选择合适的工具并正确调用。

工具定义示例

{
  "name": "get_weather@v1",
  "description": "查询指定城市的天气信息",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {"type": "string", "description": "城市名称"}
    },
    "required": ["city"]
  }
}
多轮对话处理

Agent 可能需要通过多轮交互收集必要信息,才能完成最终的工具调用。

多轮对话示例

User: 我想订机票
Agent: 好的,请问您要从哪里出发?
User: 北京
Agent: 目的地是哪里?
User: 上海
Agent: <function_call> book_flight@v1 </function_call>

在 DPO 格式中的存储方式

  • messages:存储完整的对话历史(除最后一条 assistant 回复)
  • chosen:最后一条正确的 assistant 回复
  • rejected:最后一条错误的 assistant 回复

单个样本生成流程

完整的样本生成流程包括六个关键步骤:

# 步骤 1:任务生成阶段
task = TaskGenerator.generate_single_task(tools)
# → 返回 Task 对象,包含:user_query(用户查询)、tools(工具定义)、system_prompt(系统提示词)

# 步骤 2:生成 Chosen 响应(正样本)
chosen = await LLMClient.generate_chosen_response(
    user_query=task.user_query,
    tools_json=task.tools_json
)
# → LLM 返回正确的 function_call 调用

# 步骤 3:生成 Rejected 响应(负样本)- 使用智能策略
rejected = await LLMClient.generate_rejected_response(
    user_query=task.user_query,
    tools_json=task.tools_json,
    chosen_response=chosen  # 参考正确答案,生成对比性错误
)
# → LLM 返回错误的回答(工具选择错误、参数错误或未调用工具)

# 步骤 4:LLM 自评估(可选)
validation = await LLMClient.validate_and_correct(sample)
# → 返回质量评分:{quality_score: 8.5, similarity_score: 25.0}

# 步骤 5:格式验证
is_valid, errors = Validator.validate_sample(sample)
# → 验证数据格式是否符合 DPO 要求

# 步骤 6:数据导出
Exporter.export_to_jsonl(valid_samples)
# → 导出为 JSONL 格式文件

快速开始指南

项目代码可加 小助理 免费领取

步骤 1:环境准备
# 创建并激活 Python 虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
# 或
venv\Scripts\activate  # Windows

# 安装 Python 依赖
pip install -r requirements.txt

# 安装 Node.js 依赖(前端)
cd frontend
npm install
步骤 2:配置 API Key
# 方式 1:编辑配置文件
vim backend/configs/tools_registry.json

# 方式 2:在前端界面填写
# - API URL: https://api.deepseek.com/v1
# - API Key: sk-your-api-key
# - Model: deepseek-chat
步骤 3:启动服务
# 终端 1:启动后端服务
./scripts/start_backend.sh

# 终端 2:启动前端服务
cd frontend && npm run dev
步骤 4:生成数据
  1. 打开浏览器:访问 http://localhost:3000
  2. 配置参数
    • 样本数量:100
    • 并发度:3
    • 多轮对话比例:0.3
  3. 开始生成:点击"开始生成"按钮
  4. 下载数据集:等待生成完成后点击"下载数据集"

三、LLaMA-Factory 微调

数据准备的核心要点

要点 1:文件格式必须是 JSONL

LLaMA-Factory 的所有数据加载(SFT、DPO、RM)底层都使用 datasets.load_dataset("json") 逐行解析。

严格要求

  • ✅ 每行是一个合法的 JSON 对象
  • ✅ 每行之间无逗号分隔
  • ❌ 不能有数组包裹符号 [ ]
  • ❌ 文件末尾不能有空行
要点 2:每条数据结构必须一致

所有 JSON 对象必须具有相同的键集合,无论字段是否为空。

示例

{"system": "...", "tools": "...", "messages": [...], "chosen": "...", "rejected": "..."}

注意:即使某条数据的 tools 为空,也要保留该字段并写为 "tools": "",不能省略。

要点 3:避免使用转义换行符 \n

LLaMA-Factory UI 在前端直接展示字符串内容。如果使用 \n,会按字面意义显示而非换行。

最佳实践

  • ✅ 在生成数据时写入真实换行符(实际回车)
  • ❌ 避免使用转义序列 \n
要点 4:避免非法 JSON 字符

常见错误

  • 结尾多余逗号
  • 非 UTF-8 编码
  • 字符串中未转义的双引号 "
  • 中文全角符号(如 ,:;)混入英文环境导致转义失败

数据集注册的关键点

要点 1:dataset_info.json 必须是标准 JSON

错误示例(缺少外层大括号):

"my_dataset": {...}
"another": {...}

正确示例

{
  "my_dataset": {...},
  "another": {...}
}
要点 2:字段映射 columns 必须对齐实际数据

dataset_info.json 中的 columns 字段必须与实际数据的键名完全一致,否则会导致加载错误或预览为空。

示例
如果数据包含 toolschosenrejected 字段,则需配置:

"columns": {
  "system": "system",
  "tools": "tools",
  "messages": "messages",
  "chosen": "chosen",
  "rejected": "rejected"
}
要点 3:DPO 数据集必须添加 "ranking": true

通过该标志告知 LLaMA-Factory 这是偏好学习格式,包含 chosenrejected 字段。

配置示例

"RL_data_dpo": {
  "file_name": "RL_data_dpo.jsonl",
  "ranking": true,
  "formatting": "sharegpt",
  "columns": {...}
}
要点 4:注册目录与启动命令保持一致

启动 WebUI 或 CLI 时,--dataset_dir 必须指向包含 dataset_info.json 的目录。

启动命令示例

llamafactory-cli webui --dataset_dir ./datasets/RL_data_dpo

推荐目录结构

datasets/
└── RL_data_dpo/
    ├── dataset_info.json
    └── RL_data_dpo.jsonl

数据二次转换

为确保数据格式符合 LLaMA-Factory 格式要求,使用以下转换脚本 convert_to_jsonl.py

执行命令

python convert_to_jsonl.py --input RL_data_dpo.jsonl --output RL_data_dpo_fixed.jsonl

对应的 dataset_info.json 配置

{
  "RL_data_dpo": {
    "file_name": "RL_data_dpo_fixed.jsonl",
    "ranking": true,
    "formatting": "sharegpt",
    "columns": {
      "system": "system",
      "tools": "tools",
      "messages": "messages",
      "chosen": "chosen",
      "rejected": "rejected"
    }
  }
}

注意:需要在 LLaMA-Factory 微调界面验证数据集是否能正常加载和预览。


微调配置与使用

模型下载

使用 ModelScope 下载基座模型:

modelscope download --model Qwen/Qwen3-4B-Instruct-2507 --local_dir /home/ubuntu/Qwen3-4B
启动 DPO 微调

完整的训练命令如下:

llamafactory-cli train \
    --stage dpo \
    --do_train True \
    --model_name_or_path /home/ubuntu/Qwen3-4B \
    --preprocessing_num_workers 16 \
    --finetuning_type lora \
    --template qwen3_nothink \
    --flash_attn auto \
    --dataset_dir data \
    --dataset RL_data_dpo \
    --cutoff_len 4096 \
    --learning_rate 1e-05 \
    --num_train_epochs 2.0 \
    --max_samples 100000 \
    --per_device_train_batch_size 4 \
    --gradient_accumulation_steps 8 \
    --lr_scheduler_type cosine \
    --max_grad_norm 1.0 \
    --logging_steps 5 \
    --save_steps 100 \
    --warmup_steps 0 \
    --packing False \
    --enable_thinking True \
    --report_to none \
    --output_dir saves/Qwen3-4B-Instruct-2507/lora/train_2025-11-12-20-09-23 \
    --bf16 True \
    --plot_loss True \
    --trust_remote_code True \
    --ddp_timeout 180000000 \
    --include_num_input_tokens_seen True \
    --optim adamw_torch \
    --lora_rank 8 \
    --lora_alpha 16 \
    --lora_dropout 0 \
    --lora_target all \
    --pref_beta 0.1 \
    --pref_ftx 0 \
    --pref_loss sigmoid 
参数说明

总体目标

使用 LoRA 低秩微调方式,基于 DPO 偏好数据集对 Qwen3-4B 进行偏好优化
(Direct Preference Optimization,强化学习阶段的无奖励模型替代方案)

核心参数详解

参数 说明
--stage dpo 指定训练阶段为 DPO (Direct Preference Optimization),一种无需奖励模型的强化学习对齐方法,通过偏好对比直接优化模型。
--do_train True 启动训练模式(而非推理或验证)。
--model_name_or_path /home/ubuntu/Qwen3-4B 预训练基座模型的本地路径。
--preprocessing_num_workers 16 数据预处理的并行线程数,加速数据加载。
--finetuning_type lora 采用 LoRA(低秩适配) 微调,仅训练部分参数,大幅降低显存消耗。
--template qwen3_nothink 使用 Qwen3 系列的无思维链模板格式。
--flash_attn auto 自动启用 FlashAttention,节省显存并提升速度。
--dataset_dir data 数据集所在目录。
--dataset RL_data_dpo 指定使用的数据集名称,包含 chosen(正样本)和 rejected(负样本)。
--cutoff_len 4096 最大序列长度,超出部分截断。
--learning_rate 1e-05 学习率(DPO 阶段通常较低,防止过拟合)。
--num_train_epochs 2.0 训练轮数(完整遍历数据集 2 次)。
--max_samples 100000 最大样本数量限制。
--per_device_train_batch_size 4 每张 GPU 的 batch size。
--gradient_accumulation_steps 8 梯度累积步数,实际 batch size = 4×8 = 32。
--lr_scheduler_type cosine 学习率调度策略(余弦衰减)。
--max_grad_norm 1.0 梯度裁剪上限,防止梯度爆炸。
--logging_steps 5 每 5 步打印一次训练日志。
--save_steps 100 每 100 步保存一次模型检查点。
--warmup_steps 0 学习率预热步数(0 表示无预热)。
--packing False 禁用样本打包(每条样本独立处理)。
--enable_thinking True 启用思维过程记录功能(兼容 Qwen3 思维链模式)。
--report_to none 不上报日志到 wandb / tensorboard。
--output_dir saves/... 模型输出目录。
--bf16 True 使用 bfloat16 混合精度训练。
--plot_loss True 训练过程中绘制损失曲线。
--trust_remote_code True 允许加载模型自定义代码。
--ddp_timeout 180000000 分布式训练超时设置。
--include_num_input_tokens_seen True 在日志中统计累计输入 token 数。
--optim adamw_torch 优化器(AdamW PyTorch 实现)。
--lora_rank 8 LoRA 秩(低秩矩阵维度)。
--lora_alpha 16 LoRA 缩放因子,影响权重更新幅度。
--lora_dropout 0 LoRA dropout(0 表示不使用)。
--lora_target all 对所有可训练层应用 LoRA。

DPO 特有参数详解

参数 说明
--pref_beta 0.1 β 是 DPO 损失函数中的温度/强度系数,控制"奖励差距"对梯度的影响。β 越小,优化越温和。常见范围:0.1~0.5。
--pref_ftx 0 是否混合监督损失(FTX)。0 表示纯 DPO 损失;1 表示在 DPO 中加入部分 SFT loss(混合训练)。
--pref_loss sigmoid DPO 损失函数形式。sigmoid 使用 logistic sigmoid 计算偏好概率(原论文形式);也可设为 hinge

训练结果分析

损失曲线解读

坐标轴说明

  • 横轴(X):训练步数(step)
  • 纵轴(Y):损失值(loss)

曲线说明

  • 浅蓝线(original):原始训练 loss(每步)
  • 深蓝线(smoothed):平滑后的 loss 曲线(移动平均)

曲线特征

  • 初始 loss ≈ 0.68(接近 log(2),DPO 随机输出的典型水平)
  • 前 40 步迅速下降至 0.1 以下
  • 80 步后趋于平稳
  • 100 步后基本收敛在 0.05 左右波动
训练质量评估

1. 收敛性优秀

Loss 从 ~0.7 平稳降至接近 0,无震荡或爆炸,说明:

  • ✅ 学习率设置合理(1e-5 适中稳定)
  • ✅ 数据质量良好
  • ✅ 梯度累积/batch 配置平衡
  • ✅ 无梯度爆炸或欠拟合问题

结论:模型成功学习了偏好方向,DPO 损失优化顺利。

2. 收敛速度适中

前 40 步下降最快,属于正常现象:

  • DPO 损失在早期快速下降,表明模型快速区分 preferred 与 rejected
  • 后期趋稳表明已掌握偏好趋势,继续训练收益递减

结论:模型进入稳定对齐阶段。

3. 后期低波动,训练稳定

曲线后半段震荡极小(0.05 左右浮动),表示:

  • ✅ 训练进入收敛区
  • ✅ 无过拟合迹象(过拟合时 loss 会反复上升或剧烈波动)

结论:稳定良性收敛。

DPO 训练阶段 Loss 参考值
训练阶段 典型 Loss 区间 含义
初始阶段 0.65~0.7 模型随机偏好(未区分)
中期(20–50 步) 0.2~0.1 模型逐渐学会区分 preferred / rejected
收敛期(100 步后) 0.05~0.1 以下 模型稳定对齐,可停止训练
震荡或上升 >0.2 回弹 学习率过高或过拟合问题

加入 赋范空间 免费领取 Agent 开发、强化学习教程 ,还有更多 Agent 工程化落地实战、 RL 优化实践、Agent 训练技巧等你来拿。

参数配置合理性分析

结合本次训练配置:

  • learning_rate=1e-5 → 温和,不易震荡
  • pref_beta=0.1 → β 较小,梯度平滑,损失曲线下降平稳
  • pref_loss=sigmoid → 损失函数平滑,数值变化连续
  • num_train_epochs=2 → 对偏好任务通常足够

结论:从配置到结果,整体设计科学合理。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐