Selaa lähdekoodia

Add chapter11

jjyaoao 8 kuukautta sitten
vanhempi
sitoutus
db9f6efd8e
34 muutettua tiedostoa jossa 5549 lisäystä ja 4 poistoa
  1. 222 0
      code/chapter11/.env.example
  2. 146 0
      code/chapter11/00_quick_test.py
  3. 285 0
      code/chapter11/01_dataset_loading.py
  4. 335 0
      code/chapter11/02_reward_functions.py
  5. 301 0
      code/chapter11/03_lora_configuration.py
  6. 326 0
      code/chapter11/04_sft_training.py
  7. 334 0
      code/chapter11/05_grpo_training.py
  8. 262 0
      code/chapter11/06_complete_pipeline.py
  9. 335 0
      code/chapter11/07_model_evaluation.py
  10. 104 0
      code/chapter11/08_distributed_training.py
  11. 190 0
      code/chapter11/accelerate_configs/README.md
  12. 15 0
      code/chapter11/accelerate_configs/deepspeed_zero2.yaml
  13. 15 0
      code/chapter11/accelerate_configs/deepspeed_zero3.yaml
  14. 8 0
      code/chapter11/accelerate_configs/multi_gpu_ddp.yaml
  15. 27 0
      code/chapter11/config.json
  16. 3 3
      code/chapter7/test_react_agent.py
  17. 2641 1
      docs/chapter11/第十一章 Agentic-RL.md
  18. BIN
      docs/images/11-figures/11-1.png
  19. BIN
      docs/images/11-figures/11-2.png
  20. BIN
      docs/images/11-figures/11-3.png
  21. BIN
      docs/images/11-figures/11-4.png
  22. BIN
      docs/images/11-figures/11-5.png
  23. BIN
      docs/images/11-figures/11-6.png
  24. BIN
      docs/images/11-figures/11-7.png
  25. BIN
      docs/images/11-figures/11-8.png
  26. BIN
      docs/images/11-figures/11-table-1.png
  27. BIN
      docs/images/11-figures/11-table-2.png
  28. BIN
      docs/images/11-figures/11-table-3.png
  29. BIN
      docs/images/11-figures/11-table-4.png
  30. BIN
      docs/images/11-figures/11-table-5.png
  31. BIN
      docs/images/11-figures/11-table-6.png
  32. BIN
      docs/images/11-figures/11-table-7.png
  33. BIN
      docs/images/11-figures/11-table-8.png
  34. BIN
      docs/images/11-figures/11-table-9.png

+ 222 - 0
code/chapter11/.env.example

@@ -0,0 +1,222 @@
+# ============================================================================
+# HelloAgents Chapter 11 - Agentic RL 环境变量配置文件
+# ============================================================================
+# 复制此文件为 .env 并填入你的API密钥和配置
+# 系统要求:Python 3.10+ (必需)
+
+# ============================================================================
+# 🚀 统一配置格式(推荐)- 框架自动检测provider
+# ============================================================================
+# 只需配置以下4个通用环境变量,框架会自动识别LLM提供商:
+
+# 模型名称
+LLM_MODEL_ID=your-model-name
+
+# API密钥
+LLM_API_KEY=your-api-key-here
+
+# 服务地址
+LLM_BASE_URL=your-api-base-url
+
+# 超时时间(可选,默认60秒)
+LLM_TIMEOUT=60
+
+# ============================================================================
+# 🤖 强化学习训练配置
+# ============================================================================
+
+# --------------------------------
+# 训练模型配置
+# --------------------------------
+# 基础模型(用于SFT和GRPO训练)
+# 推荐使用小模型进行实验:Qwen3-0.6B, Qwen2.5-0.5B-Instruct
+RL_BASE_MODEL=Qwen/Qwen3-0.6B
+
+# 训练输出目录
+RL_OUTPUT_DIR=./outputs
+
+# --------------------------------
+# 数据集配置
+# --------------------------------
+# 数据集名称(默认使用GSM8K数学推理数据集)
+RL_DATASET_NAME=openai/gsm8k
+
+# 数据集配置
+RL_DATASET_CONFIG=main
+
+# 数据集分割
+RL_DATASET_SPLIT=train
+
+# 最大样本数(用于快速实验,设为-1使用全部数据)
+RL_MAX_SAMPLES=100
+
+# --------------------------------
+# SFT训练超参数
+# --------------------------------
+# 训练轮数
+SFT_NUM_EPOCHS=3
+
+# 批次大小
+SFT_BATCH_SIZE=4
+
+# 梯度累积步数
+SFT_GRADIENT_ACCUMULATION_STEPS=4
+
+# 学习率
+SFT_LEARNING_RATE=2e-5
+
+# 最大序列长度
+SFT_MAX_SEQ_LENGTH=512
+
+# LoRA配置
+SFT_LORA_R=8
+SFT_LORA_ALPHA=16
+SFT_LORA_DROPOUT=0.05
+
+# --------------------------------
+# GRPO训练超参数
+# --------------------------------
+# 训练轮数
+GRPO_NUM_EPOCHS=2
+
+# 批次大小
+GRPO_BATCH_SIZE=4
+
+# 梯度累积步数
+GRPO_GRADIENT_ACCUMULATION_STEPS=4
+
+# 学习率
+GRPO_LEARNING_RATE=1e-5
+
+# 最大序列长度
+GRPO_MAX_SEQ_LENGTH=512
+
+# 每个prompt生成的响应数量
+GRPO_NUM_GENERATIONS=4
+
+# KL散度惩罚系数
+GRPO_KL_COEF=0.1
+
+# LoRA配置
+GRPO_LORA_R=8
+GRPO_LORA_ALPHA=16
+GRPO_LORA_DROPOUT=0.05
+
+# --------------------------------
+# 奖励函数配置
+# --------------------------------
+# 奖励函数类型:accuracy, length_penalty, step_penalty
+REWARD_TYPE=accuracy
+
+# 长度惩罚系数(仅用于length_penalty)
+REWARD_LENGTH_PENALTY=0.01
+
+# 步骤惩罚系数(仅用于step_penalty)
+REWARD_STEP_PENALTY=0.05
+
+# --------------------------------
+# 分布式训练配置
+# --------------------------------
+# 是否启用分布式训练
+DISTRIBUTED_TRAINING=false
+
+# 分布式训练策略:ddp, deepspeed_zero2, deepspeed_zero3
+DISTRIBUTED_STRATEGY=ddp
+
+# GPU数量(-1表示使用所有可用GPU)
+NUM_GPUS=-1
+
+# DeepSpeed配置文件路径(可选)
+# DEEPSPEED_CONFIG=./accelerate_configs/deepspeed_zero2.yaml
+
+# --------------------------------
+# 监控与日志配置
+# --------------------------------
+# 是否启用TensorBoard
+ENABLE_TENSORBOARD=true
+
+# TensorBoard日志目录
+TENSORBOARD_LOG_DIR=./logs/tensorboard
+
+# 是否启用Wandb
+ENABLE_WANDB=false
+
+# Wandb项目名称
+# WANDB_PROJECT=helloagents-rl
+
+# Wandb API密钥(获取方式:https://wandb.ai/authorize)
+# WANDB_API_KEY=your_wandb_api_key_here
+
+# 日志级别:DEBUG, INFO, WARNING, ERROR
+LOG_LEVEL=INFO
+
+# 日志保存间隔(步数)
+LOGGING_STEPS=10
+
+# 模型保存间隔(步数)
+SAVE_STEPS=100
+
+# 评估间隔(步数)
+EVAL_STEPS=100
+
+# --------------------------------
+# 硬件与性能配置
+# --------------------------------
+# 混合精度训练:no, fp16, bf16
+MIXED_PRECISION=bf16
+
+# 梯度检查点(节省显存)
+GRADIENT_CHECKPOINTING=true
+
+# DataLoader工作进程数
+DATALOADER_NUM_WORKERS=4
+
+# 是否固定随机种子
+SEED=42
+
+# ================================
+# HuggingFace API 配置
+# ================================
+# HuggingFace Token - 用于下载模型和数据集
+# 获取方式:https://huggingface.co/settings/tokens
+HF_TOKEN=
+
+# HuggingFace镜像站点(可选,用于加速下载)
+# 中国大陆用户可以使用以下镜像:
+# HF_ENDPOINT=https://hf-mirror.com
+
+# ================================
+# 模型缓存配置
+# ================================
+# HuggingFace模型缓存目录
+# HF_HOME=~/.cache/huggingface
+
+# Transformers缓存目录
+# TRANSFORMERS_CACHE=~/.cache/huggingface/transformers
+
+# ============================================================================
+# 📝 配置说明
+# ============================================================================
+# 
+# 1. 基础配置:
+#    - 必须配置LLM相关变量(LLM_MODEL_ID, LLM_API_KEY, LLM_BASE_URL)
+#    - RL_BASE_MODEL建议使用小模型(如Qwen3-0.6B)进行实验
+# 
+# 2. 训练配置:
+#    - 快速实验:设置RL_MAX_SAMPLES=100, SFT_NUM_EPOCHS=1, GRPO_NUM_EPOCHS=1
+#    - 完整训练:设置RL_MAX_SAMPLES=-1, 增加训练轮数
+# 
+# 3. 分布式训练:
+#    - 单GPU:DISTRIBUTED_TRAINING=false
+#    - 多GPU:DISTRIBUTED_TRAINING=true, 选择合适的DISTRIBUTED_STRATEGY
+# 
+# 4. 监控:
+#    - 本地监控:ENABLE_TENSORBOARD=true
+#    - 云端监控:ENABLE_WANDB=true, 配置WANDB_API_KEY
+# 
+# 5. 性能优化:
+#    - 显存不足:启用GRADIENT_CHECKPOINTING, 减小BATCH_SIZE
+#    - 加速训练:使用MIXED_PRECISION=bf16, 增加GRADIENT_ACCUMULATION_STEPS
+# 
+# ============================================================================
+

+ 146 - 0
code/chapter11/00_quick_test.py

@@ -0,0 +1,146 @@
+"""
+快速实验测试
+
+使用少量数据快速测试SFT和GRPO训练流程
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+def quick_test():
+    """
+    快速实验测试
+    
+    配置:
+    - 模型: Qwen/Qwen3-0.6B
+    - 样本数: 10个
+    - 训练轮数: 1轮
+    - 预计时间: ~2-3分钟
+    """
+    tool = RLTrainingTool()
+    
+    print("="*80)
+    print("快速实验测试")
+    print("="*80)
+    
+    # ========================================================================
+    # 测试1: 数据加载
+    # ========================================================================
+    print("\n测试1: 数据加载")
+    print("-"*80)
+    
+    data_config = {
+        "action": "load_dataset",
+        "format_type": "sft",
+        "split": "train",
+        "max_samples": 5
+    }
+    
+    print("加载数据集...")
+    result = tool.run(data_config)
+    data = json.loads(result)
+    print(f"✅ 数据集加载成功: {data['dataset_size']} 样本")
+    print(json.dumps(data, indent=2, ensure_ascii=False))
+    
+    # ========================================================================
+    # 测试2: SFT训练
+    # ========================================================================
+    print("\n测试2: SFT训练")
+    print("-"*80)
+    
+    sft_config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/quick_test/sft",
+        "max_samples": 10,
+        "num_epochs": 1,
+        "batch_size": 2,
+        "use_lora": True,
+        "lora_r": 8,
+        "lora_alpha": 16,
+    }
+    
+    print("SFT配置:")
+    print(json.dumps(sft_config, indent=2, ensure_ascii=False))
+    
+    print("\n⏳ 开始SFT训练...")
+    sft_result = tool.run(sft_config)
+    sft_data = json.loads(sft_result)
+    print("\n✅ SFT训练结果:")
+    print(json.dumps(sft_data, indent=2, ensure_ascii=False))
+    
+    # ========================================================================
+    # 测试3: GRPO训练
+    # ========================================================================
+    print("\n测试3: GRPO训练")
+    print("-"*80)
+    
+    grpo_config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/quick_test/grpo",
+        "max_samples": 10,
+        "num_epochs": 1,
+        "batch_size": 2,
+        "use_lora": True,
+        "lora_r": 8,
+        "lora_alpha": 16,
+    }
+    
+    print("GRPO配置:")
+    print(json.dumps(grpo_config, indent=2, ensure_ascii=False))
+    
+    print("\n⏳ 开始GRPO训练...")
+    grpo_result = tool.run(grpo_config)
+    grpo_data = json.loads(grpo_result)
+    print("\n✅ GRPO训练结果:")
+    print(json.dumps(grpo_data, indent=2, ensure_ascii=False))
+    
+    # ========================================================================
+    # 测试4: 奖励函数
+    # ========================================================================
+    print("\n测试4: 奖励函数")
+    print("-"*80)
+    
+    reward_config = {
+        "action": "create_reward",
+        "reward_type": "accuracy"
+    }
+    
+    print("创建奖励函数...")
+    reward_result = tool.run(reward_config)
+    reward_data = json.loads(reward_result)
+    print("✅ 奖励函数创建成功:")
+    print(json.dumps(reward_data, indent=2, ensure_ascii=False))
+    
+    # ========================================================================
+    # 总结
+    # ========================================================================
+    print("\n" + "="*80)
+    print("测试总结")
+    print("="*80)
+    print("\n✅ 所有测试通过!")
+    print("\n测试项目:")
+    print("  1. ✅ 数据加载")
+    print("  2. ✅ SFT训练")
+    print("  3. ✅ GRPO训练")
+    print("  4. ✅ 奖励函数创建")
+    
+    print("\n模型路径:")
+    print(f"  SFT模型: {sft_config['output_dir']}")
+    print(f"  GRPO模型: {grpo_config['output_dir']}")
+
+
+if __name__ == "__main__":
+    quick_test()
+

+ 285 - 0
code/chapter11/01_dataset_loading.py

@@ -0,0 +1,285 @@
+"""
+示例1: 数据集加载和格式化
+演示如何使用RLTrainingTool加载和查看GSM8K数据集
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 加载SFT格式数据集
+# ============================================================================
+
+def load_sft_dataset():
+    """
+    使用RLTrainingTool加载SFT格式的GSM8K数据集
+
+    SFT数据格式:
+    {
+        "prompt": "Question: ...\n\nLet's solve this step by step:\n",
+        "completion": "Step 1: ...\nFinal Answer: 42",
+        "text": "Question: ...\n\nLet's solve this step by step:\nStep 1: ...\nFinal Answer: 42"
+    }
+    """
+    tool = RLTrainingTool()
+
+    config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "train",
+        "max_samples": 5
+    }
+
+    print("加载SFT格式数据集...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+
+    print(f"✅ 数据集大小: {result_dict['dataset_size']}")
+    print(f"📋 数据集列: {result_dict['sample_keys']}")
+    print(f"\n💡 提示: 数据集已加载,可以用于训练")
+    print(f"   使用 action='train' 开始训练")
+
+    return result_dict
+
+
+# ============================================================================
+# 示例2: 加载RL格式数据集
+# ============================================================================
+
+def load_rl_dataset():
+    """
+    使用RLTrainingTool加载RL格式的GSM8K数据集
+
+    RL数据格式:
+    {
+        "prompt": "<|im_start|>user\nQuestion: ...\n<|im_end|>\n<|im_start|>assistant\n",
+        "ground_truth": "42",
+        "question": "...",
+        "full_answer": "..."
+    }
+    """
+    tool = RLTrainingTool()
+
+    config = {
+        "action": "load_dataset",
+        "format": "rl",
+        "split": "train",
+        "max_samples": 5,
+        "model_name": "Qwen/Qwen3-0.6B"
+    }
+
+    print("加载RL格式数据集...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+
+    print(f"✅ 数据集大小: {result_dict['dataset_size']}")
+    print(f"📋 数据集列: {result_dict['sample_keys']}")
+    print(f"\n💡 提示: RL数据集已加载,包含prompt和ground_truth")
+    print(f"   可用于GRPO训练")
+
+    return result_dict
+
+
+# ============================================================================
+# 示例3: 加载不同split的数据集
+# ============================================================================
+
+def load_different_splits():
+    """
+    加载训练集和测试集
+    """
+    tool = RLTrainingTool()
+    
+    # 加载训练集
+    train_config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "train",
+        "max_samples": 100
+    }
+    
+    print("加载训练集...")
+    train_result = tool.run(train_config)
+    train_data = json.loads(train_result)
+    print(f"✅ 训练集: {train_data['dataset_size']} 样本")
+    
+    # 加载测试集
+    test_config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "test",
+        "max_samples": 50
+    }
+    
+    print("\n加载测试集...")
+    test_result = tool.run(test_config)
+    test_data = json.loads(test_result)
+    print(f"✅ 测试集: {test_data['dataset_size']} 样本")
+    
+    return train_data, test_data
+
+
+# ============================================================================
+# 示例4: 加载完整数据集
+# ============================================================================
+
+def load_full_dataset():
+    """
+    加载完整数据集 (max_samples=None)
+    
+    GSM8K数据集:
+    - 训练集: ~7500 样本
+    - 测试集: ~1300 样本
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "train",
+        "max_samples": None  # None = 使用全部数据
+    }
+    
+    print("加载完整训练集...")
+    print("⚠️  这可能需要一些时间...")
+    
+    # 实际加载时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"✅ 完整训练集: {result_dict['dataset_size']} 样本")
+    
+    print("💡 提示: 设置 max_samples=None 可以加载全部数据")
+    print("   GSM8K训练集约有 7500 个样本")
+    
+    return config
+
+
+# ============================================================================
+# 示例5: 对比SFT和RL格式
+# ============================================================================
+
+def compare_sft_rl_formats():
+    """
+    对比SFT和RL数据格式的区别
+    """
+    tool = RLTrainingTool()
+
+    print("="*80)
+    print("SFT vs RL 数据格式对比")
+    print("="*80)
+
+    # SFT格式
+    sft_config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "train",
+        "max_samples": 1
+    }
+
+    print("\n1. SFT格式:")
+    sft_result = tool.run(sft_config)
+    sft_data = json.loads(sft_result)
+    print(f"   列: {sft_data['sample_keys']}")
+    print(f"   用途: 监督微调 (Supervised Fine-Tuning)")
+    print(f"   特点: 包含完整的prompt和completion")
+
+    # RL格式
+    rl_config = {
+        "action": "load_dataset",
+        "format": "rl",
+        "split": "train",
+        "max_samples": 1,
+        "model_name": "Qwen/Qwen3-0.6B"
+    }
+
+    print("\n2. RL格式:")
+    rl_result = tool.run(rl_config)
+    rl_data = json.loads(rl_result)
+    print(f"   列: {rl_data['sample_keys']}")
+    print(f"   用途: 强化学习训练 (Reinforcement Learning)")
+    print(f"   特点: 包含prompt和ground_truth,用于奖励计算")
+
+    print("\n主要区别:")
+    print("  - SFT: 直接学习正确答案")
+    print("  - RL: 通过奖励信号学习,更灵活")
+
+    return sft_data, rl_data
+
+
+# ============================================================================
+# 示例6: 数据集统计信息
+# ============================================================================
+
+def dataset_statistics():
+    """
+    查看数据集的统计信息
+    """
+    tool = RLTrainingTool()
+
+    config = {
+        "action": "load_dataset",
+        "format": "sft",
+        "split": "train",
+        "max_samples": 100
+    }
+
+    print("加载数据集...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+
+    print("\n数据集统计:")
+    print(f"  总样本数: {result_dict['dataset_size']}")
+    print(f"  数据列: {', '.join(result_dict['sample_keys'])}")
+    print(f"  数据集: GSM8K (Grade School Math 8K)")
+    print(f"  任务类型: 数学推理")
+
+    print(f"\n💡 提示: 数据集包含以下字段:")
+    for key in result_dict['sample_keys']:
+        print(f"  - {key}")
+
+    return result_dict
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 加载SFT格式数据集")
+    print("="*80)
+    load_sft_dataset()
+    
+    print("\n" + "="*80)
+    print("示例2: 加载RL格式数据集")
+    print("="*80)
+    load_rl_dataset()
+    
+    print("\n" + "="*80)
+    print("示例3: 加载不同split的数据集")
+    print("="*80)
+    load_different_splits()
+    
+    print("\n" + "="*80)
+    print("示例4: 加载完整数据集")
+    print("="*80)
+    load_full_dataset()
+    
+    print("\n" + "="*80)
+    print("示例5: 对比SFT和RL格式")
+    print("="*80)
+    compare_sft_rl_formats()
+    
+    print("\n" + "="*80)
+    print("示例6: 数据集统计信息")
+    print("="*80)
+    dataset_statistics()
+

+ 335 - 0
code/chapter11/02_reward_functions.py

@@ -0,0 +1,335 @@
+"""
+示例2: 奖励函数设计和使用
+演示如何使用RLTrainingTool创建和测试奖励函数
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 创建准确性奖励函数
+# ============================================================================
+
+def create_accuracy_reward():
+    """
+    创建准确性奖励函数
+    
+    奖励规则:
+    - 答案正确: 1.0
+    - 答案错误: 0.0
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "create_reward",
+        "reward_type": "accuracy"
+    }
+    
+    print("创建准确性奖励函数...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+    
+    print(f"✅ 奖励函数类型: {result_dict['reward_type']}")
+    print(f"📋 描述: {result_dict['description']}")
+    
+    return result_dict
+
+
+# ============================================================================
+# 示例2: 创建长度惩罚奖励函数
+# ============================================================================
+
+def create_length_penalty_reward():
+    """
+    创建长度惩罚奖励函数
+    
+    奖励规则:
+    - 基础奖励 (准确性)
+    - 减去长度惩罚 (鼓励简洁)
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "create_reward",
+        "reward_type": "length_penalty",
+        "penalty_weight": 0.001,  # 每个token惩罚0.001
+        "max_length": 512
+    }
+    
+    print("创建长度惩罚奖励函数...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+    
+    print(f"✅ 奖励函数类型: {result_dict['reward_type']}")
+    print(f"📋 惩罚权重: {result_dict.get('penalty_weight', 0.001)}")
+    print(f"📋 最大长度: {result_dict.get('max_length', 512)}")
+    
+    return result_dict
+
+
+# ============================================================================
+# 示例3: 创建步骤奖励函数
+# ============================================================================
+
+def create_step_reward():
+    """
+    创建步骤奖励函数
+    
+    奖励规则:
+    - 基础奖励 (准确性)
+    - 加上步骤奖励 (鼓励详细推理)
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "create_reward",
+        "reward_type": "step",
+        "step_bonus": 0.1,  # 每个步骤额外奖励0.1
+        "max_steps": 10
+    }
+    
+    print("创建步骤奖励函数...")
+    result = tool.run(config)
+    result_dict = json.loads(result)
+    
+    print(f"✅ 奖励函数类型: {result_dict['reward_type']}")
+    print(f"📋 步骤奖励: {result_dict.get('step_bonus', 0.1)}")
+    print(f"📋 最大步骤: {result_dict.get('max_steps', 10)}")
+    
+    return result_dict
+
+
+# ============================================================================
+# 示例4: 测试奖励函数
+# ============================================================================
+
+def test_reward_function():
+    """
+    测试奖励函数的计算
+    
+    使用MathRewardFunction直接测试
+    """
+    from hello_agents.rl import MathRewardFunction
+    
+    reward_fn = MathRewardFunction(tolerance=1e-4)
+    
+    # 测试样本
+    test_cases = [
+        {
+            "completion": "Let me calculate: 2+2=4. Final Answer: 4",
+            "ground_truth": "4",
+            "expected": 1.0
+        },
+        {
+            "completion": "I think 2+2=5. Final Answer: 5",
+            "ground_truth": "4",
+            "expected": 0.0
+        },
+        {
+            "completion": "The answer is 4",
+            "ground_truth": "4",
+            "expected": 1.0
+        },
+        {
+            "completion": "2+2 equals four. #### 4",
+            "ground_truth": "4",
+            "expected": 1.0
+        }
+    ]
+    
+    print("测试奖励函数:")
+    print("-" * 80)
+    
+    for i, case in enumerate(test_cases, 1):
+        # 计算奖励
+        rewards = reward_fn(
+            completions=[case["completion"]],
+            ground_truth=[case["ground_truth"]]
+        )
+        reward = rewards[0]
+        
+        print(f"\n测试 {i}:")
+        print(f"  生成: {case['completion'][:50]}...")
+        print(f"  真值: {case['ground_truth']}")
+        print(f"  奖励: {reward:.2f} (期望: {case['expected']:.2f})")
+        print(f"  {'✅ 正确' if abs(reward - case['expected']) < 0.01 else '❌ 错误'}")
+    
+    return test_cases
+
+
+# ============================================================================
+# 示例5: 答案提取测试
+# ============================================================================
+
+def test_answer_extraction():
+    """
+    测试答案提取功能
+    """
+    from hello_agents.rl import MathRewardFunction
+    
+    reward_fn = MathRewardFunction()
+    
+    test_texts = [
+        "Final Answer: 42",
+        "The answer is 3.14",
+        "#### 100",
+        "So the result is 2.5",
+        "Let me think... the answer should be 7",
+        "42"
+    ]
+    
+    print("答案提取测试:")
+    print("-" * 80)
+    
+    for text in test_texts:
+        answer = reward_fn.extract_answer(text)
+        print(f"\n文本: {text}")
+        print(f"提取: {answer if answer else '(未找到)'}")
+    
+    return test_texts
+
+
+# ============================================================================
+# 示例6: 答案比较测试
+# ============================================================================
+
+def test_answer_comparison():
+    """
+    测试答案比较功能
+    """
+    from hello_agents.rl import MathRewardFunction
+    
+    reward_fn = MathRewardFunction(tolerance=0.01)
+    
+    test_pairs = [
+        ("42", "42", True),
+        ("3.14", "3.14159", False),  # 超出容差
+        ("3.14", "3.141", True),     # 在容差内
+        ("100", "100.0", True),
+        ("2.5", "3.0", False),
+        ("7", "7.00", True)
+    ]
+    
+    print("答案比较测试:")
+    print("-" * 80)
+    
+    for pred, truth, expected in test_pairs:
+        is_correct = reward_fn.compare_answers(pred, truth)
+        print(f"\n预测: {pred}, 真值: {truth}")
+        print(f"结果: {'正确' if is_correct else '错误'} (期望: {'正确' if expected else '错误'})")
+        print(f"{'✅ 通过' if is_correct == expected else '❌ 失败'}")
+    
+    return test_pairs
+
+
+# ============================================================================
+# 示例7: 不同奖励函数的对比
+# ============================================================================
+
+def compare_reward_functions():
+    """
+    对比不同奖励函数的效果
+    """
+    from hello_agents.rl import (
+        create_accuracy_reward,
+        create_length_penalty_reward,
+        create_step_reward
+    )
+
+    # 创建不同的奖励函数
+    accuracy_fn = create_accuracy_reward()
+    base_fn = create_accuracy_reward()  # 基础奖励函数
+    length_fn = create_length_penalty_reward(base_fn, penalty_weight=0.001)
+    step_fn = create_step_reward(base_fn, step_bonus=0.1)
+    
+    # 测试样本
+    test_cases = [
+        {
+            "completion": "4",
+            "ground_truth": "4",
+            "desc": "简洁正确答案"
+        },
+        {
+            "completion": "Step 1: 2+2=4\nFinal Answer: 4",
+            "ground_truth": "4",
+            "desc": "带步骤的正确答案"
+        },
+        {
+            "completion": "Let me think... " * 20 + "Final Answer: 4",
+            "ground_truth": "4",
+            "desc": "冗长的正确答案"
+        }
+    ]
+    
+    print("奖励函数对比:")
+    print("=" * 80)
+    
+    for i, case in enumerate(test_cases, 1):
+        print(f"\n测试 {i}: {case['desc']}")
+        print(f"长度: {len(case['completion'])} 字符")
+        
+        # 计算不同奖励
+        acc_reward = accuracy_fn([case["completion"]], ground_truth=[case["ground_truth"]])[0]
+        len_reward = length_fn([case["completion"]], ground_truth=[case["ground_truth"]])[0]
+        step_reward = step_fn([case["completion"]], ground_truth=[case["ground_truth"]])[0]
+        
+        print(f"  准确性奖励: {acc_reward:.4f}")
+        print(f"  长度惩罚奖励: {len_reward:.4f}")
+        print(f"  步骤奖励: {step_reward:.4f}")
+    
+    print("\n结论:")
+    print("  - 准确性奖励: 只关注答案正确性")
+    print("  - 长度惩罚: 鼓励简洁答案")
+    print("  - 步骤奖励: 鼓励详细推理")
+    
+    return test_cases
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 创建准确性奖励函数")
+    print("="*80)
+    create_accuracy_reward()
+    
+    print("\n" + "="*80)
+    print("示例2: 创建长度惩罚奖励函数")
+    print("="*80)
+    create_length_penalty_reward()
+    
+    print("\n" + "="*80)
+    print("示例3: 创建步骤奖励函数")
+    print("="*80)
+    create_step_reward()
+    
+    print("\n" + "="*80)
+    print("示例4: 测试奖励函数")
+    print("="*80)
+    test_reward_function()
+    
+    print("\n" + "="*80)
+    print("示例5: 答案提取测试")
+    print("="*80)
+    test_answer_extraction()
+    
+    print("\n" + "="*80)
+    print("示例6: 答案比较测试")
+    print("="*80)
+    test_answer_comparison()
+    
+    print("\n" + "="*80)
+    print("示例7: 不同奖励函数的对比")
+    print("="*80)
+    compare_reward_functions()
+

+ 301 - 0
code/chapter11/03_lora_configuration.py

@@ -0,0 +1,301 @@
+"""
+示例3: LoRA配置和使用
+演示如何通过RLTrainingTool配置和使用LoRA进行参数高效微调
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 基础LoRA配置
+# ============================================================================
+
+def basic_lora_config():
+    """
+    最基础的LoRA配置
+    
+    LoRA (Low-Rank Adaptation):
+    - 只训练少量额外参数
+    - 减少60-80%显存占用
+    - 提升2-3倍训练速度
+    - 模型文件只有~10MB
+    """
+    tool = RLTrainingTool()
+    
+    # 使用RLTrainingTool进行SFT训练,启用LoRA
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/lora_basic",
+        "max_samples": 100,
+        "num_epochs": 1,
+        
+        # LoRA配置
+        "use_lora": True,           # 启用LoRA
+        "lora_r": 16,               # LoRA秩(rank)
+        "lora_alpha": 32,           # 缩放因子(通常是r的2倍)
+    }
+    
+    print("基础LoRA配置:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  use_lora: {config['use_lora']}")
+    print(f"  lora_r: {config['lora_r']}")
+    print(f"  lora_alpha: {config['lora_alpha']}")
+    print(f"  目标模块: ['q_proj', 'v_proj'] (默认)")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # print(json.dumps(json.loads(result), indent=2, ensure_ascii=False))
+    
+    return config
+
+
+# ============================================================================
+# 示例2: 不同LoRA秩的对比
+# ============================================================================
+
+def compare_lora_ranks():
+    """
+    对比不同LoRA秩的配置
+    
+    LoRA秩(r)的选择:
+    - r=8: 较小参数量,适合快速实验
+    - r=16: 推荐值,平衡性能和效率
+    - r=32: 较大参数量,追求更好性能
+    """
+    configs = {
+        "r=8 (快速实验)": {
+            "lora_r": 8,
+            "lora_alpha": 16,
+            "params": "~16K"
+        },
+        "r=16 (推荐)": {
+            "lora_r": 16,
+            "lora_alpha": 32,
+            "params": "~32K"
+        },
+        "r=32 (高性能)": {
+            "lora_r": 32,
+            "lora_alpha": 64,
+            "params": "~65K"
+        },
+    }
+    
+    print("不同LoRA秩的对比:")
+    for name, config in configs.items():
+        print(f"\n{name}:")
+        print(f"  lora_r: {config['lora_r']}")
+        print(f"  lora_alpha: {config['lora_alpha']}")
+        print(f"  预估参数量: {config['params']}")
+    
+    # 实际训练示例
+    print("\n训练示例 (r=16):")
+    print("""
+    tool = RLTrainingTool()
+    result = tool.run({
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "max_samples": 100,
+        "num_epochs": 1,
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    })
+    """)
+    
+    return configs
+
+
+# ============================================================================
+# 示例3: LoRA vs 完整微调对比
+# ============================================================================
+
+def compare_lora_vs_full_finetuning():
+    """
+    对比LoRA和完整微调的配置
+    """
+    print("LoRA vs 完整微调对比:")
+    print("\nLoRA微调:")
+    print("  显存占用: ~4GB (0.5B模型)")
+    print("  训练速度: 快(2-3x)")
+    print("  模型大小: ~10MB")
+    print("  batch_size: 8")
+    print("  use_lora: True")
+    
+    print("\n完整微调:")
+    print("  显存占用: ~14GB (0.5B模型)")
+    print("  训练速度: 慢")
+    print("  模型大小: ~1GB")
+    print("  batch_size: 2")
+    print("  use_lora: False")
+    
+    print("\n推荐: 使用LoRA进行微调")
+
+
+# ============================================================================
+# 示例4: 实际训练配置示例
+# ============================================================================
+
+def practical_training_configs():
+    """
+    实际训练中的推荐配置
+    """
+    tool = RLTrainingTool()
+    
+    # 快速训练配置
+    quick_config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/quick_test",
+        "max_samples": 100,
+        "num_epochs": 1,
+        "batch_size": 8,
+        "use_lora": True,
+        "lora_r": 8,
+        "lora_alpha": 16,
+    }
+    
+    # 标准训练配置
+    standard_config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/standard",
+        "max_samples": 1000,
+        "num_epochs": 3,
+        "batch_size": 4,
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+        "learning_rate": 5e-5,
+    }
+    
+    # 高质量训练配置
+    high_quality_config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/high_quality",
+        "max_samples": None,  # 使用全部数据
+        "num_epochs": 5,
+        "batch_size": 2,
+        "use_lora": True,
+        "lora_r": 32,
+        "lora_alpha": 64,
+        "learning_rate": 3e-5,
+    }
+    
+    print("实际训练配置示例:")
+    print("\n1. 快速实验配置:")
+    print(f"   样本数: {quick_config['max_samples']}")
+    print(f"   epochs: {quick_config['num_epochs']}")
+    print(f"   lora_r: {quick_config['lora_r']}")
+    print(f"   batch_size: {quick_config['batch_size']}")
+    
+    print("\n2. 标准训练配置:")
+    print(f"   样本数: {standard_config['max_samples']}")
+    print(f"   epochs: {standard_config['num_epochs']}")
+    print(f"   lora_r: {standard_config['lora_r']}")
+    print(f"   batch_size: {standard_config['batch_size']}")
+    
+    print("\n3. 高质量训练配置:")
+    print(f"   样本数: 全部 (max_samples=None)")
+    print(f"   epochs: {high_quality_config['num_epochs']}")
+    print(f"   lora_r: {high_quality_config['lora_r']}")
+    print(f"   batch_size: {high_quality_config['batch_size']}")
+    
+    # 实际训练时取消注释
+    # result = tool.run(quick_config)
+    # print(json.dumps(json.loads(result), indent=2, ensure_ascii=False))
+    
+    return quick_config, standard_config, high_quality_config
+
+
+# ============================================================================
+# 示例5: LoRA参数调优建议
+# ============================================================================
+
+def lora_tuning_guidelines():
+    """
+    LoRA参数调优建议
+    """
+    guidelines = {
+        "lora_r (秩)": {
+            "推荐值": 16,
+            "范围": "8-32",
+            "说明": "越大性能越好,但参数量和训练时间也越多",
+            "选择建议": {
+                "快速实验": 8,
+                "平衡性能": 16,
+                "追求性能": 32,
+            }
+        },
+        "lora_alpha (缩放因子)": {
+            "推荐值": 32,
+            "范围": "16-64",
+            "说明": "通常设置为lora_r的2倍",
+            "公式": "lora_alpha = 2 * lora_r"
+        },
+        "max_samples (样本数)": {
+            "快速实验": 100,
+            "标准训练": 1000,
+            "完整训练": "None (全部数据)",
+            "说明": "None表示使用全部数据",
+        },
+    }
+    
+    print("LoRA参数调优建议:")
+    for param, info in guidelines.items():
+        print(f"\n{param}:")
+        for key, value in info.items():
+            if isinstance(value, dict):
+                print(f"  {key}:")
+                for k, v in value.items():
+                    print(f"    - {k}: {v}")
+            else:
+                print(f"  {key}: {value}")
+    
+    return guidelines
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 基础LoRA配置")
+    print("="*80)
+    basic_lora_config()
+    
+    print("\n" + "="*80)
+    print("示例2: 不同LoRA秩的对比")
+    print("="*80)
+    compare_lora_ranks()
+    
+    print("\n" + "="*80)
+    print("示例3: LoRA vs 完整微调对比")
+    print("="*80)
+    compare_lora_vs_full_finetuning()
+    
+    print("\n" + "="*80)
+    print("示例4: 实际训练配置示例")
+    print("="*80)
+    practical_training_configs()
+    
+    print("\n" + "="*80)
+    print("示例5: LoRA参数调优建议")
+    print("="*80)
+    lora_tuning_guidelines()
+

+ 326 - 0
code/chapter11/04_sft_training.py

@@ -0,0 +1,326 @@
+"""
+示例4: SFT训练完整流程
+
+演示如何使用RLTrainingTool进行SFT监督微调
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 最简单的SFT训练
+# ============================================================================
+
+def minimal_sft_training():
+    """
+    最简单的SFT训练示例
+    
+    只需要调用RLTrainingTool即可
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/sft_minimal",
+        "max_samples": 10,
+        "num_epochs": 1,
+    }
+    
+    print("最简单的SFT训练:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成! 模型保存在: {result_dict['output_dir']}")
+    
+    return config
+
+
+# ============================================================================
+# 示例2: 标准SFT训练配置
+# ============================================================================
+
+def standard_sft_training():
+    """
+    标准的SFT训练配置
+    
+    包含:
+    - LoRA参数高效微调
+    - 合理的训练参数
+    - 使用部分数据集
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        
+        # 模型配置
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/sft_standard",
+        
+        # 数据配置
+        "max_samples": 1000,  # 使用1000个样本
+        
+        # 训练配置
+        "num_epochs": 3,
+        "batch_size": 4,
+        "learning_rate": 5e-5,
+        
+        # LoRA配置
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("标准SFT训练配置:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  batch_size: {config['batch_size']}")
+    print(f"  learning_rate: {config['learning_rate']}")
+    print(f"  LoRA秩: {config['lora_r']}")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成!")
+    # print(f"📁 模型保存在: {result_dict['output_dir']}")
+    
+    return config
+
+
+# ============================================================================
+# 示例3: 完整数据集训练
+# ============================================================================
+
+def full_dataset_training():
+    """
+    使用完整数据集进行训练
+    
+    max_samples=None 表示使用全部数据
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/sft_full",
+        
+        # 使用全部数据
+        "max_samples": None,  # None = 使用全部数据
+        
+        "num_epochs": 3,
+        "batch_size": 4,
+        "learning_rate": 5e-5,
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("完整数据集训练:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: 全部 (max_samples=None)")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  预计样本数: ~7500 (GSM8K训练集)")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成!")
+    
+    return config
+
+
+# ============================================================================
+# 示例4: 不同学习率的对比
+# ============================================================================
+
+def compare_learning_rates():
+    """
+    对比不同学习率的训练效果
+    
+    常用学习率:
+    - 1e-5: 保守,适合微调已经很好的模型
+    - 5e-5: 推荐,平衡学习速度和稳定性
+    - 1e-4: 激进,适合快速实验
+    """
+    learning_rates = {
+        "保守 (1e-5)": 1e-5,
+        "推荐 (5e-5)": 5e-5,
+        "激进 (1e-4)": 1e-4,
+    }
+    
+    print("不同学习率的对比:")
+    for name, lr in learning_rates.items():
+        print(f"\n{name}:")
+        print(f"  learning_rate: {lr}")
+        print(f"  适用场景: ", end="")
+        if lr == 1e-5:
+            print("模型已经很好,只需微调")
+        elif lr == 5e-5:
+            print("标准训练,推荐使用")
+        else:
+            print("快速实验(可能不稳定)")
+    
+    # 训练示例
+    print("\n训练示例 (推荐学习率):")
+    tool = RLTrainingTool()
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "max_samples": 1000,
+        "num_epochs": 3,
+        "learning_rate": 5e-5,
+        "use_lora": True,
+    }
+    print(f"  learning_rate: {config['learning_rate']}")
+    
+    # result = tool.run(config)
+    
+    return learning_rates
+
+
+# ============================================================================
+# 示例5: 显存优化配置
+# ============================================================================
+
+def memory_optimized_training():
+    """
+    显存优化配置
+    
+    适用于显存受限的情况:
+    - 使用LoRA
+    - 减小batch size
+    - 使用较小的LoRA秩
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/sft_memory_opt",
+        
+        # 显存优化
+        "max_samples": 1000,
+        "num_epochs": 3,
+        "batch_size": 1,  # 最小batch size
+        "learning_rate": 5e-5,
+        
+        # LoRA配置
+        "use_lora": True,
+        "lora_r": 8,  # 使用较小的秩
+        "lora_alpha": 16,
+    }
+    
+    print("显存优化配置:")
+    print(f"  batch_size: {config['batch_size']} (最小)")
+    print(f"  lora_r: {config['lora_r']} (较小)")
+    print(f"  use_lora: {config['use_lora']}")
+    print(f"  预计显存占用: ~3-4GB")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    
+    return config
+
+
+# ============================================================================
+# 示例6: 实际训练示例
+# ============================================================================
+
+def practical_training_example():
+    """
+    实际训练示例 - 可以直接运行
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/sft_practical",
+        
+        # 使用较少样本进行快速测试
+        "max_samples": 100,
+        "num_epochs": 1,
+        "batch_size": 4,
+        "learning_rate": 5e-5,
+        
+        # 使用LoRA
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("实际训练示例:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  输出目录: {config['output_dir']}")
+    
+    print("\n💡 提示: 取消下面的注释以开始训练")
+    print("# result = tool.run(config)")
+    print("# result_dict = json.loads(result)")
+    print("# print(f'✅ 训练完成! 模型保存在: {result_dict[\"output_dir\"]}')")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成!")
+    # print(f"📁 模型保存在: {result_dict['output_dir']}")
+    
+    return config
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 最简单的SFT训练")
+    print("="*80)
+    minimal_sft_training()
+    
+    print("\n" + "="*80)
+    print("示例2: 标准SFT训练配置")
+    print("="*80)
+    standard_sft_training()
+    
+    print("\n" + "="*80)
+    print("示例3: 完整数据集训练")
+    print("="*80)
+    full_dataset_training()
+    
+    print("\n" + "="*80)
+    print("示例4: 不同学习率的对比")
+    print("="*80)
+    compare_learning_rates()
+    
+    print("\n" + "="*80)
+    print("示例5: 显存优化配置")
+    print("="*80)
+    memory_optimized_training()
+    
+    print("\n" + "="*80)
+    print("示例6: 实际训练示例")
+    print("="*80)
+    practical_training_example()
+

+ 334 - 0
code/chapter11/05_grpo_training.py

@@ -0,0 +1,334 @@
+"""
+示例5: GRPO训练完整流程
+
+演示如何使用RLTrainingTool进行GRPO强化学习训练
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 最简单的GRPO训练
+# ============================================================================
+
+def minimal_grpo_training():
+    """
+    最简单的GRPO训练示例
+    
+    只需要调用RLTrainingTool即可
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/grpo_minimal",
+        "max_samples": 10,
+        "num_epochs": 1,
+    }
+    
+    print("最简单的GRPO训练:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成! 模型保存在: {result_dict['output_dir']}")
+    
+    return config
+
+
+# ============================================================================
+# 示例2: 标准GRPO训练配置
+# ============================================================================
+
+def standard_grpo_training():
+    """
+    标准的GRPO训练配置
+    
+    通常在SFT模型基础上进行GRPO训练
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "grpo",
+        
+        # 模型配置 - 可以使用SFT训练后的模型
+        "model_name": "Qwen/Qwen3-0.6B",  # 或 "./output/sft_standard"
+        "output_dir": "./output/grpo_standard",
+        
+        # 数据配置
+        "max_samples": 500,  # GRPO通常使用较少样本
+        
+        # 训练配置
+        "num_epochs": 3,
+        "batch_size": 2,  # GRPO需要更多显存
+        "learning_rate": 1e-5,  # 比SFT小10倍
+        
+        # LoRA配置
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("标准GRPO训练配置:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  batch_size: {config['batch_size']}")
+    print(f"  learning_rate: {config['learning_rate']} (比SFT小)")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ GRPO训练完成!")
+    
+    return config
+
+
+# ============================================================================
+# 示例3: 完整数据集训练
+# ============================================================================
+
+def full_dataset_training():
+    """
+    使用完整数据集进行GRPO训练
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/grpo_full",
+        
+        # 使用全部数据
+        "max_samples": None,  # None = 使用全部数据
+        
+        "num_epochs": 3,
+        "batch_size": 2,
+        "learning_rate": 1e-5,
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("完整数据集GRPO训练:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: 全部 (max_samples=None)")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  预计样本数: ~7500 (GSM8K训练集)")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    
+    return config
+
+
+# ============================================================================
+# 示例4: SFT + GRPO完整流程
+# ============================================================================
+
+def complete_sft_grpo_pipeline():
+    """
+    完整的SFT + GRPO训练流程
+    
+    步骤:
+    1. SFT训练 - 学习基本格式
+    2. GRPO训练 - 优化推理能力
+    """
+    tool = RLTrainingTool()
+    
+    # 步骤1: SFT训练
+    print("步骤1: SFT训练")
+    sft_config = {
+        "action": "train",
+        "algorithm": "sft",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/pipeline_sft",
+        "max_samples": 1000,
+        "num_epochs": 3,
+        "batch_size": 4,
+        "use_lora": True,
+    }
+    
+    print(f"  模型: {sft_config['model_name']}")
+    print(f"  样本数: {sft_config['max_samples']}")
+    
+    # 实际训练时取消注释
+    # sft_result = tool.run(sft_config)
+    # print(f"✅ SFT训练完成: {sft_config['output_dir']}")
+    
+    # 步骤2: GRPO训练
+    print("\n步骤2: GRPO训练")
+    grpo_config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "./output/pipeline_sft",  # 使用SFT模型
+        "output_dir": "./output/pipeline_grpo",
+        "max_samples": 500,
+        "num_epochs": 3,
+        "batch_size": 2,
+        "learning_rate": 1e-5,
+        "use_lora": True,
+    }
+    
+    print(f"  基础模型: {grpo_config['model_name']}")
+    print(f"  样本数: {grpo_config['max_samples']}")
+    
+    # 实际训练时取消注释
+    # grpo_result = tool.run(grpo_config)
+    # print(f"✅ GRPO训练完成: {grpo_config['output_dir']}")
+    
+    print("\n💡 推荐使用GRPO模型进行推理")
+    
+    return sft_config, grpo_config
+
+
+# ============================================================================
+# 示例5: 不同奖励函数的使用
+# ============================================================================
+
+def using_different_rewards():
+    """
+    GRPO默认使用准确性奖励函数
+    
+    可以通过创建自定义奖励函数来改变行为
+    """
+    print("GRPO奖励函数:")
+    print("\n默认奖励函数: 准确性奖励")
+    print("  - 答案正确: 1.0")
+    print("  - 答案错误: 0.0")
+    
+    print("\n其他可用奖励函数:")
+    print("  1. 长度惩罚奖励: 鼓励简洁答案")
+    print("  2. 步骤奖励: 鼓励详细推理")
+    print("  3. 自定义奖励: 根据需求定制")
+    
+    print("\n创建奖励函数示例:")
+    tool = RLTrainingTool()
+    
+    # 创建准确性奖励函数
+    accuracy_config = {
+        "action": "create_reward",
+        "reward_type": "accuracy"
+    }
+    print("\n1. 准确性奖励:")
+    print(f"   配置: {accuracy_config}")
+    
+    # 创建长度惩罚奖励函数
+    length_config = {
+        "action": "create_reward",
+        "reward_type": "length_penalty",
+        "penalty_weight": 0.001
+    }
+    print("\n2. 长度惩罚奖励:")
+    print(f"   配置: {length_config}")
+    
+    # 创建步骤奖励函数
+    step_config = {
+        "action": "create_reward",
+        "reward_type": "step",
+        "step_bonus": 0.1
+    }
+    print("\n3. 步骤奖励:")
+    print(f"   配置: {step_config}")
+    
+    return accuracy_config, length_config, step_config
+
+
+# ============================================================================
+# 示例6: 实际训练示例
+# ============================================================================
+
+def practical_training_example():
+    """
+    实际训练示例 - 可以直接运行
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./output/grpo_practical",
+        
+        # 使用较少样本进行快速测试
+        "max_samples": 50,
+        "num_epochs": 1,
+        "batch_size": 2,
+        "learning_rate": 1e-5,
+        
+        # 使用LoRA
+        "use_lora": True,
+        "lora_r": 16,
+        "lora_alpha": 32,
+    }
+    
+    print("实际训练示例:")
+    print(f"  模型: {config['model_name']}")
+    print(f"  样本数: {config['max_samples']}")
+    print(f"  训练轮数: {config['num_epochs']}")
+    print(f"  输出目录: {config['output_dir']}")
+    
+    print("\n💡 提示: 取消下面的注释以开始训练")
+    print("# result = tool.run(config)")
+    print("# result_dict = json.loads(result)")
+    print("# print(f'✅ 训练完成! 模型保存在: {result_dict[\"output_dir\"]}')")
+    
+    # 实际训练时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 训练完成!")
+    # print(f"📁 模型保存在: {result_dict['output_dir']}")
+    
+    return config
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 最简单的GRPO训练")
+    print("="*80)
+    minimal_grpo_training()
+    
+    print("\n" + "="*80)
+    print("示例2: 标准GRPO训练配置")
+    print("="*80)
+    standard_grpo_training()
+    
+    print("\n" + "="*80)
+    print("示例3: 完整数据集训练")
+    print("="*80)
+    full_dataset_training()
+    
+    print("\n" + "="*80)
+    print("示例4: SFT + GRPO完整流程")
+    print("="*80)
+    complete_sft_grpo_pipeline()
+    
+    print("\n" + "="*80)
+    print("示例5: 不同奖励函数的使用")
+    print("="*80)
+    using_different_rewards()
+    
+    print("\n" + "="*80)
+    print("示例6: 实际训练示例")
+    print("="*80)
+    practical_training_example()
+

+ 262 - 0
code/chapter11/06_complete_pipeline.py

@@ -0,0 +1,262 @@
+"""
+完整的Agentic RL训练流程(更新版)
+从数据准备到模型部署的端到端示例
+
+更新内容:
+1. 修复了JSON解析问题
+2. 添加了训练监控配置(wandb/tensorboard)
+3. 支持详细日志输出
+"""
+
+import sys
+import os
+
+# 添加HelloAgents到路径
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "HelloAgents"))
+
+from hello_agents.tools import RLTrainingTool
+import json
+from datetime import datetime
+
+class AgenticRLPipeline:
+    """Agentic RL训练流水线"""
+    
+    def __init__(self, config_path="config.json"):
+        """
+        初始化训练流水线
+        
+        Args:
+            config_path: 配置文件路径
+        """
+        self.rl_tool = RLTrainingTool()
+        self.config = self.load_config(config_path)
+        self.results = {}
+        
+    def load_config(self, config_path):
+        """加载配置文件"""
+        with open(config_path, 'r') as f:
+            return json.load(f)
+    
+    def log(self, message):
+        """记录日志"""
+        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+        print(f"[{timestamp}] {message}")
+    
+    def stage1_prepare_data(self):
+        """阶段1: 数据准备"""
+        self.log("=" * 50)
+        self.log("阶段1: 数据准备")
+        self.log("=" * 50)
+        
+        # 加载并检查数据集
+        result = self.rl_tool.run({
+            "action": "load_dataset",
+            "format": "sft",
+            "max_samples": self.config["data"]["max_samples"],
+        })
+        
+        # 解析JSON结果
+        dataset_info = json.loads(result)
+
+        self.log(f"✓ 数据集加载完成")
+        self.log(f"  - 样本数: {dataset_info['dataset_size']}")
+        self.log(f"  - 格式: {dataset_info['format']}")
+        self.log(f"  - 数据列: {', '.join(dataset_info['sample_keys'])}")
+        
+        self.results["data"] = dataset_info
+        
+        return dataset_info
+    
+    def stage2_sft_training(self):
+        """阶段2: SFT训练"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段2: SFT训练")
+        self.log("=" * 50)
+        
+        sft_config = self.config["sft"]
+        
+        result = self.rl_tool.run({
+            "action": "train",
+            "algorithm": "sft",
+            "model_name": self.config["model"]["base_model"],
+            "output_dir": sft_config["output_dir"],
+            "max_samples": self.config["data"]["max_samples"],
+            "num_epochs": sft_config["num_epochs"],
+            "batch_size": sft_config["batch_size"],
+            "use_lora": True,
+            # 训练监控配置
+            "use_wandb": self.config.get("monitoring", {}).get("use_wandb", False),
+            "use_tensorboard": self.config.get("monitoring", {}).get("use_tensorboard", True),
+            "wandb_project": self.config.get("monitoring", {}).get("wandb_project", None),
+        })
+        
+        # 解析JSON结果
+        result_data = json.loads(result)
+        
+        self.log(f"✓ SFT训练完成")
+        self.log(f"  - 模型路径: {result_data['output_dir']}")
+        self.log(f"  - 状态: {result_data['status']}")
+        
+        self.results["sft_training"] = result_data
+        
+        return result_data["output_dir"]
+    
+    def stage3_sft_evaluation(self, model_path):
+        """阶段3: SFT评估"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段3: SFT评估")
+        self.log("=" * 50)
+        
+        result = self.rl_tool.run({
+            "action": "evaluate",
+            "model_path": model_path,
+            "max_samples": self.config["eval"]["max_samples"],
+            "use_lora": True,
+        })
+        eval_data = json.loads(result)
+
+        self.log(f"✓ SFT评估完成")
+        self.log(f"  - 准确率: {eval_data['accuracy']}")
+        self.log(f"  - 平均奖励: {eval_data['average_reward']}")
+
+        self.results["sft_evaluation"] = eval_data
+
+        return eval_data
+    
+    def stage4_grpo_training(self, sft_model_path):
+        """阶段4: GRPO训练"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段4: GRPO训练")
+        self.log("=" * 50)
+        
+        grpo_config = self.config["grpo"]
+        
+        result = self.rl_tool.run({
+            "action": "train",
+            "algorithm": "grpo",
+            "model_name": sft_model_path,
+            "output_dir": grpo_config["output_dir"],
+            "max_samples": self.config["data"]["max_samples"],
+            "num_epochs": grpo_config["num_epochs"],
+            "batch_size": grpo_config["batch_size"],
+            "use_lora": True,
+            # 训练监控配置
+            "use_wandb": self.config.get("monitoring", {}).get("use_wandb", False),
+            "use_tensorboard": self.config.get("monitoring", {}).get("use_tensorboard", True),
+            "wandb_project": self.config.get("monitoring", {}).get("wandb_project", None),
+        })
+        
+        # 解析JSON结果
+        result_data = json.loads(result)
+        
+        self.log(f"✓ GRPO训练完成")
+        self.log(f"  - 模型路径: {result_data['output_dir']}")
+        self.log(f"  - 状态: {result_data['status']}")
+        
+        self.results["grpo_training"] = result_data
+        
+        return result_data["output_dir"]
+    
+    def stage5_grpo_evaluation(self, model_path):
+        """阶段5: GRPO评估"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段5: GRPO评估")
+        self.log("=" * 50)
+        
+        result = self.rl_tool.run({
+            "action": "evaluate",
+            "model_path": model_path,
+            "max_samples": self.config["eval"]["max_samples"],
+            "use_lora": True,
+        })
+        eval_data = json.loads(result)
+
+        self.log(f"✓ GRPO评估完成")
+        self.log(f"  - 准确率: {eval_data['accuracy']}")
+        self.log(f"  - 平均奖励: {eval_data['average_reward']}")
+
+        self.results["grpo_evaluation"] = eval_data
+
+        return eval_data
+    
+    def stage6_save_results(self):
+        """阶段6: 保存结果"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段6: 保存结果")
+        self.log("=" * 50)
+        
+        # 保存训练结果
+        results_path = "training_results.json"
+        with open(results_path, 'w') as f:
+            json.dump(self.results, f, indent=2)
+        
+        self.log(f"✓ 结果已保存到: {results_path}")
+    
+    def run(self):
+        """运行完整流程"""
+        try:
+            # 阶段1: 数据准备
+            self.stage1_prepare_data()
+            
+            # 阶段2: SFT训练
+            sft_model_path = self.stage2_sft_training()
+            
+            # 阶段3: SFT评估
+            self.stage3_sft_evaluation(sft_model_path)
+            
+            # 阶段4: GRPO训练
+            grpo_model_path = self.stage4_grpo_training(sft_model_path)
+            
+            # 阶段5: GRPO评估
+            self.stage5_grpo_evaluation(grpo_model_path)
+            
+            # 阶段6: 保存结果
+            self.stage6_save_results()
+            
+            self.log("\n" + "=" * 50)
+            self.log("✓ 训练流程完成!")
+            self.log("=" * 50)
+            
+        except Exception as e:
+            self.log(f"\n✗ 训练失败: {str(e)}")
+            raise
+
+# 使用示例
+if __name__ == "__main__":
+    # 创建配置文件
+    config = {
+        "model": {
+            "base_model": "Qwen/Qwen3-0.6B"
+        },
+        "data": {
+            "max_samples": 100  # 使用100个样本快速测试
+        },
+        "sft": {
+            "output_dir": "./models/sft_model",
+            "num_epochs": 2,
+            "batch_size": 4,
+        },
+        "grpo": {
+            "output_dir": "./models/grpo_model",
+            "num_epochs": 2,
+            "batch_size": 2,
+        },
+        "eval": {
+            "max_samples": 20,
+            "sft_accuracy_threshold": 0.40
+        },
+        "monitoring": {
+            "use_wandb": False,  # 是否使用Wandb
+            "use_tensorboard": True,  # 是否使用TensorBoard
+            "wandb_project": "agentic-rl-pipeline"  # Wandb项目名
+        }
+    }
+    
+    # 保存配置
+    with open("config.json", 'w') as f:
+        json.dump(config, f, indent=2)
+    
+    # 运行训练流程
+    pipeline = AgenticRLPipeline("config.json")
+    pipeline.run()
+

+ 335 - 0
code/chapter11/07_model_evaluation.py

@@ -0,0 +1,335 @@
+"""
+示例7: 模型评估
+
+演示如何使用RLTrainingTool评估训练后的模型
+"""
+
+import sys
+from pathlib import Path
+import json
+
+# 添加项目路径
+project_root = Path(__file__).parent.parent / "HelloAgents"
+sys.path.insert(0, str(project_root))
+
+from hello_agents.tools import RLTrainingTool
+
+
+# ============================================================================
+# 示例1: 评估SFT模型
+# ============================================================================
+
+def evaluate_sft_model():
+    """
+    评估SFT训练后的模型
+    
+    使用测试集评估模型的准确率
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "evaluate",
+        "model_path": "./output/quick_test/sft",
+        "max_samples": 50  # 使用50个测试样本
+    }
+    
+    print("评估SFT模型:")
+    print(f"  模型路径: {config['model_path']}")
+    print(f"  测试样本数: {config['max_samples']}")
+    
+    # 实际评估时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 评估完成!")
+    # print(f"  准确率: {result_dict['accuracy']}")
+    # print(f"  平均奖励: {result_dict['average_reward']}")
+    
+    print("\n💡 提示: 取消注释以运行评估")
+    
+    return config
+
+
+# ============================================================================
+# 示例2: 评估GRPO模型
+# ============================================================================
+
+def evaluate_grpo_model():
+    """
+    评估GRPO训练后的模型
+    
+    对比GRPO模型和SFT模型的性能
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "evaluate",
+        "model_path": "./output/quick_test/grpo",
+        "max_samples": 50
+    }
+    
+    print("评估GRPO模型:")
+    print(f"  模型路径: {config['model_path']}")
+    print(f"  测试样本数: {config['max_samples']}")
+    
+    # 实际评估时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 评估完成!")
+    # print(f"  准确率: {result_dict['accuracy']}")
+    # print(f"  平均奖励: {result_dict['average_reward']}")
+    
+    print("\n💡 提示: 取消注释以运行评估")
+    
+    return config
+
+
+# ============================================================================
+# 示例3: 对比SFT和GRPO模型
+# ============================================================================
+
+def compare_sft_grpo():
+    """
+    对比SFT和GRPO模型的性能
+    
+    在相同的测试集上评估两个模型
+    """
+    tool = RLTrainingTool()
+    
+    print("="*80)
+    print("SFT vs GRPO 模型对比")
+    print("="*80)
+    
+    # 评估SFT模型
+    print("\n1. 评估SFT模型...")
+    sft_config = {
+        "action": "evaluate",
+        "model_path": "./output/quick_test/sft",
+        "max_samples": 100
+    }
+    
+    # 实际评估时取消注释
+    # sft_result = tool.run(sft_config)
+    # sft_data = json.loads(sft_result)
+    # print(f"   SFT准确率: {sft_data['accuracy']}")
+    
+    # 评估GRPO模型
+    print("\n2. 评估GRPO模型...")
+    grpo_config = {
+        "action": "evaluate",
+        "model_path": "./output/quick_test/grpo",
+        "max_samples": 100
+    }
+    
+    # 实际评估时取消注释
+    # grpo_result = tool.run(grpo_config)
+    # grpo_data = json.loads(grpo_result)
+    # print(f"   GRPO准确率: {grpo_data['accuracy']}")
+    
+    # 对比结果
+    print("\n对比结果:")
+    print("  SFT模型: 学习基本格式和推理步骤")
+    print("  GRPO模型: 通过强化学习优化推理能力")
+    print("  预期: GRPO模型准确率 > SFT模型准确率")
+    
+    print("\n💡 提示: 取消注释以运行实际评估")
+    
+    return sft_config, grpo_config
+
+
+# ============================================================================
+# 示例4: 评估基线模型
+# ============================================================================
+
+def evaluate_baseline():
+    """
+    评估基线模型(未训练的原始模型)
+    
+    用于对比训练效果
+    """
+    tool = RLTrainingTool()
+    
+    config = {
+        "action": "evaluate",
+        "model_path": "Qwen/Qwen3-0.6B",  # 原始模型
+        "max_samples": 50
+    }
+    
+    print("评估基线模型:")
+    print(f"  模型: {config['model_path']}")
+    print(f"  测试样本数: {config['max_samples']}")
+    
+    # 实际评估时取消注释
+    # result = tool.run(config)
+    # result_dict = json.loads(result)
+    # print(f"\n✅ 评估完成!")
+    # print(f"  基线准确率: {result_dict['accuracy']}")
+    
+    print("\n💡 提示: 基线模型通常准确率较低")
+    print("   训练后的模型应该显著优于基线")
+    
+    return config
+
+
+# ============================================================================
+# 示例5: 完整评估流程
+# ============================================================================
+
+def complete_evaluation():
+    """
+    完整的评估流程
+    
+    评估基线、SFT和GRPO三个模型
+    """
+    tool = RLTrainingTool()
+    
+    models = {
+        "基线模型": "Qwen/Qwen3-0.6B",
+        "SFT模型": "./output/quick_test/sft",
+        "GRPO模型": "./output/quick_test/grpo"
+    }
+    
+    print("="*80)
+    print("完整评估流程")
+    print("="*80)
+    
+    results = {}
+    
+    for name, model_path in models.items():
+        print(f"\n评估 {name}...")
+        print(f"  路径: {model_path}")
+        
+        config = {
+            "action": "evaluate",
+            "model_path": model_path,
+            "max_samples": 100
+        }
+        
+        # 实际评估时取消注释
+        # result = tool.run(config)
+        # result_dict = json.loads(result)
+        # results[name] = result_dict
+        # print(f"  准确率: {result_dict['accuracy']}")
+    
+    print("\n" + "="*80)
+    print("评估总结")
+    print("="*80)
+    
+    # 实际评估时取消注释
+    # for name, result in results.items():
+    #     print(f"{name}: {result['accuracy']}")
+    
+    print("\n预期结果:")
+    print("  基线模型 < SFT模型 < GRPO模型")
+    print("  说明强化学习训练有效提升了模型性能")
+    
+    print("\n💡 提示: 取消注释以运行完整评估")
+    
+    return models
+
+
+# ============================================================================
+# 示例6: 实际评估示例
+# ============================================================================
+
+def practical_evaluation():
+    """
+    实际评估示例 - 可以直接运行
+    
+    评估quick_test训练的模型
+    """
+    tool = RLTrainingTool()
+    
+    print("="*80)
+    print("实际评估示例")
+    print("="*80)
+    
+    # 检查模型是否存在
+    import os
+    sft_path = "./output/quick_test/sft"
+    grpo_path = "./output/quick_test/grpo"
+    
+    if not os.path.exists(sft_path):
+        print(f"\n❌ SFT模型不存在: {sft_path}")
+        print("   请先运行 00_quick_test.py 训练模型")
+        return None
+    
+    if not os.path.exists(grpo_path):
+        print(f"\n❌ GRPO模型不存在: {grpo_path}")
+        print("   请先运行 00_quick_test.py 训练模型")
+        return None
+    
+    print("\n✅ 模型文件存在,开始评估...")
+    
+    # 评估SFT模型
+    print("\n1. 评估SFT模型...")
+    sft_config = {
+        "action": "evaluate",
+        "model_path": sft_path,
+        "max_samples": 20  # 使用较少样本快速测试
+    }
+    
+    print("💡 提示: 取消下面的注释以开始评估")
+    print("# sft_result = tool.run(sft_config)")
+    print("# sft_data = json.loads(sft_result)")
+    print("# print(f'SFT准确率: {sft_data[\"accuracy\"]}')")
+    
+    # 评估GRPO模型
+    print("\n2. 评估GRPO模型...")
+    grpo_config = {
+        "action": "evaluate",
+        "model_path": grpo_path,
+        "max_samples": 20
+    }
+    
+    print("💡 提示: 取消下面的注释以开始评估")
+    print("# grpo_result = tool.run(grpo_config)")
+    print("# grpo_data = json.loads(grpo_result)")
+    print("# print(f'GRPO准确率: {grpo_data[\"accuracy\"]}')")
+    
+    # 实际评估时取消注释
+    # sft_result = tool.run(sft_config)
+    # sft_data = json.loads(sft_result)
+    # print(f"\n✅ SFT评估完成: {sft_data['accuracy']}")
+    
+    # grpo_result = tool.run(grpo_config)
+    # grpo_data = json.loads(grpo_result)
+    # print(f"✅ GRPO评估完成: {grpo_data['accuracy']}")
+    
+    return sft_config, grpo_config
+
+
+# ============================================================================
+# 主函数
+# ============================================================================
+
+if __name__ == "__main__":
+    print("="*80)
+    print("示例1: 评估SFT模型")
+    print("="*80)
+    evaluate_sft_model()
+    
+    print("\n" + "="*80)
+    print("示例2: 评估GRPO模型")
+    print("="*80)
+    evaluate_grpo_model()
+    
+    print("\n" + "="*80)
+    print("示例3: 对比SFT和GRPO模型")
+    print("="*80)
+    compare_sft_grpo()
+    
+    print("\n" + "="*80)
+    print("示例4: 评估基线模型")
+    print("="*80)
+    evaluate_baseline()
+    
+    print("\n" + "="*80)
+    print("示例5: 完整评估流程")
+    print("="*80)
+    complete_evaluation()
+    
+    print("\n" + "="*80)
+    print("示例6: 实际评估示例")
+    print("="*80)
+    practical_evaluation()
+

+ 104 - 0
code/chapter11/08_distributed_training.py

@@ -0,0 +1,104 @@
+"""
+分布式训练示例
+
+本脚本演示如何使用Accelerate进行分布式训练。
+训练代码本身无需修改,只需通过accelerate launch启动即可。
+
+使用方法:
+1. 单GPU训练:
+   python 07_distributed_training.py
+
+2. 多GPU DDP训练:
+   accelerate launch --config_file accelerate_configs/multi_gpu_ddp.yaml 07_distributed_training.py
+
+3. DeepSpeed ZeRO-2训练:
+   accelerate launch --config_file accelerate_configs/deepspeed_zero2.yaml 07_distributed_training.py
+
+4. DeepSpeed ZeRO-3训练:
+   accelerate launch --config_file accelerate_configs/deepspeed_zero3.yaml 07_distributed_training.py
+"""
+
+import sys
+import os
+
+# 添加HelloAgents到路径
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "HelloAgents"))
+
+from hello_agents.tools import RLTrainingTool
+import json
+
+def main():
+    print("="*80)
+    print("分布式训练示例")
+    print("="*80)
+    
+    # 检测分布式环境
+    world_size = int(os.environ.get("WORLD_SIZE", 1))
+    local_rank = int(os.environ.get("LOCAL_RANK", 0))
+    
+    if world_size > 1:
+        print(f"\n🚀 分布式训练模式")
+        print(f"   - 总进程数: {world_size}")
+        print(f"   - 当前进程: {local_rank}")
+        print(f"   - 分布式后端: {os.environ.get('ACCELERATE_DISTRIBUTED_TYPE', 'MULTI_GPU')}")
+    else:
+        print(f"\n💻 单GPU训练模式")
+    
+    print("="*80)
+    
+    # 创建训练工具
+    rl_tool = RLTrainingTool()
+    
+    # 训练配置
+    # 注意: batch_size是每个GPU的batch size
+    # 总batch size = batch_size × num_gpus × gradient_accumulation_steps
+    config = {
+        "action": "train",
+        "algorithm": "grpo",
+        "model_name": "Qwen/Qwen3-0.6B",
+        "output_dir": "./models/grpo_distributed",
+        "max_samples": 200,  # 使用200个样本
+        "num_epochs": 2,
+        "batch_size": 2,  # 每个GPU的batch size
+        "use_lora": True,
+        "use_wandb": False,
+        "use_tensorboard": True,
+    }
+    
+    # 只在主进程打印配置
+    if local_rank == 0:
+        print("\n训练配置:")
+        print(f"  - 模型: {config['model_name']}")
+        print(f"  - 样本数: {config['max_samples']}")
+        print(f"  - Epoch数: {config['num_epochs']}")
+        print(f"  - 每GPU batch size: {config['batch_size']}")
+        if world_size > 1:
+            total_batch = config['batch_size'] * world_size
+            print(f"  - 总batch size: {total_batch}")
+        print("="*80)
+    
+    # 开始训练
+    # 训练代码完全不需要修改!
+    # Accelerate会自动处理分布式训练的所有细节
+    result = rl_tool.run(config)
+    
+    # 只在主进程打印结果
+    if local_rank == 0:
+        result_data = json.loads(result)
+        print("\n" + "="*80)
+        print("训练完成!")
+        print("="*80)
+        print(f"状态: {result_data['status']}")
+        print(f"模型路径: {result_data['output_dir']}")
+        print("="*80)
+        
+        # 打印性能提示
+        if world_size > 1:
+            print(f"\n💡 性能提示:")
+            print(f"   使用了 {world_size} 个GPU进行训练")
+            print(f"   理论加速比: ~{world_size * 0.85:.1f}x")
+            print(f"   (实际加速比取决于通信开销和数据加载)")
+
+if __name__ == "__main__":
+    main()
+

+ 190 - 0
code/chapter11/accelerate_configs/README.md

@@ -0,0 +1,190 @@
+# Accelerate配置文件说明
+
+本目录包含用于分布式训练的Accelerate配置文件。
+
+## 配置文件列表
+
+### 1. multi_gpu_ddp.yaml
+**数据并行(DDP)** - 最简单的多GPU训练方案
+
+- **适用场景**: 单机多卡(2-8卡)
+- **优点**: 简单、速度快
+- **缺点**: 每个GPU需要完整模型副本
+- **显存需求**: 与单GPU相同
+
+**使用方法**:
+```bash
+accelerate launch --config_file accelerate_configs/multi_gpu_ddp.yaml train_script.py
+```
+
+### 2. deepspeed_zero2.yaml
+**DeepSpeed ZeRO-2** - 优化器状态分片
+
+- **适用场景**: 中等规模模型(1B-7B)
+- **优点**: 降低显存占用,支持更大batch size
+- **缺点**: 比DDP稍慢
+- **显存节省**: ~30%
+
+**使用方法**:
+```bash
+accelerate launch --config_file accelerate_configs/deepspeed_zero2.yaml train_script.py
+```
+
+### 3. deepspeed_zero3.yaml
+**DeepSpeed ZeRO-3** - 完整模型分片
+
+- **适用场景**: 大规模模型(>7B)
+- **优点**: 最大程度降低显存占用
+- **缺点**: 通信开销较大
+- **显存节省**: ~50%
+
+**使用方法**:
+```bash
+accelerate launch --config_file accelerate_configs/deepspeed_zero3.yaml train_script.py
+```
+
+## 快速开始
+
+### 1. 安装依赖
+
+```bash
+pip install accelerate deepspeed
+```
+
+### 2. 配置Accelerate
+
+**方式1: 使用配置文件**(推荐)
+```bash
+accelerate launch --config_file accelerate_configs/multi_gpu_ddp.yaml your_script.py
+```
+
+**方式2: 交互式配置**
+```bash
+accelerate config
+```
+
+**方式3: 命令行参数**
+```bash
+accelerate launch --num_processes 4 --mixed_precision fp16 your_script.py
+```
+
+### 3. 运行训练
+
+```bash
+# DDP训练(4卡)
+accelerate launch --config_file accelerate_configs/multi_gpu_ddp.yaml 07_distributed_training.py
+
+# DeepSpeed ZeRO-2训练(4卡)
+accelerate launch --config_file accelerate_configs/deepspeed_zero2.yaml 07_distributed_training.py
+
+# DeepSpeed ZeRO-3训练(4卡)
+accelerate launch --config_file accelerate_configs/deepspeed_zero3.yaml 07_distributed_training.py
+```
+
+## 配置参数说明
+
+### 通用参数
+
+- `compute_environment`: 计算环境(LOCAL_MACHINE/AMAZON_SAGEMAKER等)
+- `distributed_type`: 分布式类型(MULTI_GPU/DEEPSPEED/FSDP等)
+- `num_processes`: 总进程数(通常等于GPU数量)
+- `machine_rank`: 机器编号(主节点为0)
+- `num_machines`: 机器数量
+- `gpu_ids`: 使用的GPU ID(all表示使用所有GPU)
+- `mixed_precision`: 混合精度训练(no/fp16/bf16)
+
+### DeepSpeed参数
+
+- `zero_stage`: ZeRO优化级别(1/2/3)
+  - ZeRO-1: 优化器状态分片
+  - ZeRO-2: 优化器状态+梯度分片
+  - ZeRO-3: 优化器状态+梯度+模型参数分片
+
+- `offload_optimizer_device`: 优化器状态卸载设备(none/cpu/nvme)
+- `offload_param_device`: 模型参数卸载设备(none/cpu/nvme)
+- `gradient_accumulation_steps`: 梯度累积步数
+- `gradient_clipping`: 梯度裁剪阈值
+- `zero3_init_flag`: ZeRO-3初始化标志
+
+## 性能调优建议
+
+### 1. Batch Size调整
+
+分布式训练时,总batch size = `per_device_batch_size × num_gpus × gradient_accumulation_steps`
+
+**示例**:
+```python
+# 单GPU: batch_size=4, gradient_accumulation=4, 总batch=16
+# 4GPU DDP: batch_size=4, gradient_accumulation=1, 总batch=16
+```
+
+### 2. 学习率缩放
+
+使用线性缩放规则:
+```python
+lr_new = lr_base × sqrt(total_batch_size_new / total_batch_size_base)
+```
+
+### 3. 混合精度训练
+
+- **fp16**: 适合大多数场景,速度快
+- **bf16**: 适合Ampere架构(A100/A6000),数值稳定性更好
+- **no**: 不使用混合精度,精度最高但速度慢
+
+### 4. 梯度累积
+
+当显存不足时,可以增大`gradient_accumulation_steps`:
+```yaml
+deepspeed_config:
+  gradient_accumulation_steps: 8  # 增大累积步数
+```
+
+## 常见问题
+
+### Q1: 如何查看当前使用的配置?
+
+```bash
+accelerate env
+```
+
+### Q2: 多卡训练速度没有线性提升?
+
+**可能原因**:
+- 通信开销过大
+- 数据加载瓶颈
+- batch size太小
+
+**解决方法**:
+- 增大batch size
+- 使用更快的数据加载器
+- 检查网络带宽
+
+### Q3: DeepSpeed训练卡住?
+
+**可能原因**:
+- 模型初始化问题
+- 通信超时
+
+**解决方法**:
+```bash
+# 启用调试日志
+export ACCELERATE_LOG_LEVEL=INFO
+export NCCL_DEBUG=INFO
+
+# 增加超时时间
+export NCCL_TIMEOUT=1800
+```
+
+### Q4: 如何在多节点上训练?
+
+1. 在所有节点上安装相同的环境
+2. 配置SSH免密登录
+3. 修改配置文件中的`num_machines`和`main_process_ip`
+4. 在每个节点上运行相同的命令
+
+## 参考资源
+
+- [Accelerate文档](https://huggingface.co/docs/accelerate)
+- [DeepSpeed文档](https://www.deepspeed.ai/)
+- [TRL分布式训练指南](https://huggingface.co/docs/trl/customization)
+

+ 15 - 0
code/chapter11/accelerate_configs/deepspeed_zero2.yaml

@@ -0,0 +1,15 @@
+compute_environment: LOCAL_MACHINE
+distributed_type: DEEPSPEED
+num_processes: 4
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+deepspeed_config:
+  gradient_accumulation_steps: 4
+  gradient_clipping: 1.0
+  offload_optimizer_device: none
+  offload_param_device: none
+  zero3_init_flag: false
+  zero_stage: 2  # ZeRO-2
+

+ 15 - 0
code/chapter11/accelerate_configs/deepspeed_zero3.yaml

@@ -0,0 +1,15 @@
+compute_environment: LOCAL_MACHINE
+distributed_type: DEEPSPEED
+num_processes: 4
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+deepspeed_config:
+  gradient_accumulation_steps: 4
+  gradient_clipping: 1.0
+  offload_optimizer_device: cpu  # 优化器状态卸载到CPU
+  offload_param_device: cpu      # 参数卸载到CPU
+  zero3_init_flag: true
+  zero_stage: 3  # ZeRO-3
+

+ 8 - 0
code/chapter11/accelerate_configs/multi_gpu_ddp.yaml

@@ -0,0 +1,8 @@
+compute_environment: LOCAL_MACHINE
+distributed_type: MULTI_GPU
+num_processes: 4  # GPU数量
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+

+ 27 - 0
code/chapter11/config.json

@@ -0,0 +1,27 @@
+{
+  "model": {
+    "base_model": "Qwen/Qwen3-0.6B"
+  },
+  "data": {
+    "max_samples": 100
+  },
+  "sft": {
+    "output_dir": "./models/sft_model",
+    "num_epochs": 2,
+    "batch_size": 4
+  },
+  "grpo": {
+    "output_dir": "./models/grpo_model",
+    "num_epochs": 2,
+    "batch_size": 2
+  },
+  "eval": {
+    "max_samples": 20,
+    "sft_accuracy_threshold": 0.4
+  },
+  "monitoring": {
+    "use_wandb": false,
+    "use_tensorboard": true,
+    "wandb_project": "agentic-rl-pipeline"
+  }
+}

+ 3 - 3
code/chapter7/test_react_agent.py

@@ -21,15 +21,15 @@ def test_react_agent():
     # 注册计算器工具
     try:
         from hello_agents import calculate
-        tool_registry.register_tool("calculate", calculate, "执行数学计算,支持基本的四则运算")
+        tool_registry.register_function("calculate", "执行数学计算,支持基本的四则运算", calculate)
         print("✅ 计算器工具注册成功")
     except ImportError:
         print("⚠️ 计算器工具未找到,跳过注册")
-    
+
     # 注册搜索工具(如果可用)
     try:
         from hello_agents import search
-        tool_registry.register_tool("search", search, "搜索互联网信息")
+        tool_registry.register_function("search", "搜索互联网信息", search)
         print("✅ 搜索工具注册成功")
     except ImportError:
         print("⚠️ 搜索工具未找到,跳过注册")

+ 2641 - 1
docs/chapter11/第十一章 Agentic-RL.md

@@ -1,3 +1,2643 @@
 # 第十一章 Agentic-RL
 
-本章内容待补充...
+## 11.1 从LLM训练到Agentic RL
+
+在前面的章节中,我们实现了多种智能体范式和通信协议。不过智能体处理更复杂的任务时表现不佳,自然会有疑问:**如何让智能体具备更强的推理能力?如何让智能体学会更好地使用工具?如何让智能体能够自我改进?**
+
+这正是Agentic RL(基于强化学习的智能体训练)要解决的核心问题。本章将为HelloAgents框架引入强化学习训练能力,让你能够训练出具备推理、工具使用等高级能力的智能体。我们将从LLM训练的基础知识开始,逐步深入到监督微调(Supervised Fine-Tuning,SFT)、群组相对策略优化(Group Relative Policy Optimization, GRPO)等实用技术,最终构建一个完整的智能体训练pipeline。
+
+### 11.1.1 从强化学习到Agentic RL
+
+在第二章的2.4.2节中,我们介绍了基于强化学习的智能体。强化学习(Reinforcement Learning, RL)是一种专注于解决序贯决策问题的学习范式,它通过智能体与环境的直接交互,在"试错"中学习如何最大化长期收益。
+
+现在,让我们将这个框架应用到LLM智能体上。考虑一个数学问题求解智能体,它需要回答这样的问题:
+
+```
+问题: Janet's ducks lay 16 eggs per day. She eats three for breakfast
+every morning and bakes muffins for her friends every day with four.
+She sells the remainder at the farmers' market daily for $2 per fresh
+duck egg. How much in dollars does she make every day at the farmers' market?
+```
+
+这个问题需要多步推理:首先计算Janet每天剩余的鸡蛋数量(16 - 3 - 4 = 9),然后计算她的收入(9 × 2 = 18)。我们可以将这个任务映射到强化学习框架:
+
+- **智能体**:基于LLM的推理系统
+- **环境**:数学问题和验证系统
+- **状态**:当前的问题描述和已有的推理步骤
+- **行动**:生成下一步推理或最终答案
+- **奖励**:答案是否正确(正确+1,错误0)
+
+传统的监督学习方法存在三个核心局限:一是数据质量完全决定训练质量,模型只能模仿训练数据,难以超越;二是缺乏探索能力,只能被动学习人类提供的路径;三是难以优化长期目标,无法精确优化多步推理的中间过程。
+
+强化学习提供了新的可能性。通过让智能体自主生成多个候选答案并根据正确性获得奖励,它可以学习哪些推理路径更优、哪些步骤是关键,甚至发现比人类标注更好的解题方法<sup>[8]</sup>。这就是Agentic RL的核心思想:将LLM作为可学习策略,嵌入智能体的感知-决策-执行循环,通过强化学习优化多步任务表现。
+
+### 11.1.2 LLM训练全景图
+
+在深入Agentic RL之前,我们需要先理解LLM训练的完整流程。一个强大的LLM(如GPT、Claude、Qwen)的诞生,通常要经历两个主要阶段:预训练(Pretraining)和后训练(Post-training)。如图11.1所示,这两个阶段构成了LLM从"语言模型"到"对话助手"的完整演化路径。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-1.png" alt="" width="85%"/>
+  <p>图 11.1 LLM训练全景图</p>
+</div>
+**预训练阶段**是LLM训练的第一阶段,目标是让模型学习语言的基本规律和世界知识。这个阶段使用海量的文本数据(通常是数TB级别),通过自监督学习的方式训练模型。最常见的预训练任务是因果语言建模(Causal Language Modeling),也称为下一个词预测(Next Token Prediction)。给定一个文本序列 $x_1, x_2, ..., x_t$,模型需要预测下一个词 $x_{t+1}$:
+$$
+\mathcal{L}_{\text{pretrain}} = -\sum_{t=1}^{T} \log P(x_t | x_1, x_2, ..., x_{t-1}; \theta)
+$$
+
+其中 $\theta$ 是模型参数,$P(x_t | x_1, ..., x_{t-1}; \theta)$ 是模型预测的下一个词的概率分布,目标是最小化负对数似然,即最大化预测正确词的概率。例如,给定文本"The cat sat on the",模型需要预测下一个词最可能是"mat"。通过在海量文本上进行这样的训练,模型逐渐学会语法规则(什么样的词序是合法的)、语义知识(词与词之间的关系)、世界知识(关于世界的事实性信息)以及基础的推理能力。
+
+预训练阶段的特点是数据量巨大、计算成本高、学到的是通用的语言理解和生成能力、采用无监督学习。
+
+**后训练阶段**则是要解决预训练模型的不足。预训练后的模型虽然具备了强大的语言能力,但它只是一个"预测下一个词"的模型,并不知道如何遵循人类的指令、生成有帮助无害诚实的回答、拒绝不当的请求,以及以对话的方式与人交互。后训练阶段就是要解决这些问题,让模型对齐人类的偏好和价值观。
+
+后训练通常包含三个步骤。第一步是**监督微调(SFT)**<sup>[15]</sup>,目标是让模型学会遵循指令和对话格式。训练数据是(prompt, completion)对,训练目标与预训练类似,仍然是最大化正确输出的概率:
+
+$$
+\mathcal{L}_{\text{SFT}} = -\sum_{i=1}^{N} \log P(y_i | x_i; \theta)
+$$
+
+其中 $x_i$ 是输入提示(prompt),$y_i$ 是期望的输出,$N$ 是训练样本数量。SFT的特点是数据量较小、需要人工标注、快速见效、主要学习任务格式和基本能力。
+
+第二步是**奖励建模(RM)**。SFT后的模型虽然能遵循指令,但生成的回答质量参差不齐。我们需要一种方式来评估回答的质量,这就是奖励模型的作用<sup>[13,14]</sup>。奖励模型的训练数据是偏好对比数据,包含同一个问题的两个回答,一个更好(chosen),一个更差(rejected)。奖励模型的训练目标是学习人类的偏好:
+
+$$
+\mathcal{L}_{\text{RM}} = -\mathbb{E}_{(x, y_w, y_l)} [\log \sigma(r_\phi(x, y_w) - r_\phi(x, y_l))]
+$$
+
+其中 $r_\phi(x, y)$ 是奖励模型,输入是(提示,回答)对,输出是质量分数;$y_w$ 是更好的回答(chosen),$y_l$ 是更差的回答(rejected),$\sigma$ 是sigmoid函数,目标是让奖励模型给更好的回答更高的分数。
+
+第三步是**强化学习微调**。有了奖励模型后,我们就可以用强化学习来优化语言模型,让它生成更高质量的回答。最经典的算法是PPO(Proximal Policy Optimization)<sup>[1]</sup>,训练目标是:
+
+$$
+\mathcal{L}_{\text{PPO}} = \mathbb{E}_{x, y \sim \pi_\theta} [r_\phi(x, y)] - \beta \cdot D_{KL}(\pi_\theta || \pi_{\text{ref}})
+$$
+
+其中 $\pi_\theta$ 是当前策略,即语言模型,$\pi_{\text{ref}}$ 是参考策略,这个场景下可以是SFT模型,$r_\phi(x, y)$ 是奖励模型的评分,$D_{KL}$ 是KL散度,目的是为了防止模型偏离太远,$\beta$ 是平衡系数。这个目标函数的含义是:最大化奖励,同时不要偏离原始模型太远。
+
+传统的RLHF(Reinforcement Learning from Human Feedback)<sup>[5]</sup>需要大量人工标注偏好数据,成本高昂。为了降低成本,研究者提出了RLAIF(Reinforcement Learning from AI Feedback)<sup>[7]</sup>,用强大的AI模型(如GPT-4)来替代人类标注员。RLAIF的工作流程是:用SFT模型生成多个候选回答,用强大的AI模型对回答进行评分和排序,用AI的评分训练奖励模型,用奖励模型进行强化学习。实验表明,RLAIF的效果接近甚至超过RLHF,同时成本大幅降低<sup>[11]</sup>。
+
+### 11.1.3 Agentic RL的核心理念
+
+在理解了LLM的基础训练流程后,让我们来看看Agentic RL与传统训练方法的区别。传统的后训练(我们称之为PBRFT: Preference-Based Reinforcement Fine-Tuning)主要关注单轮对话的质量优化:给定一个用户问题,模型生成一个回答,然后根据回答的质量获得奖励。这种方式适合优化对话助手,但对于需要多步推理、工具使用、长期规划的智能体任务来说,就显得力不从心了。
+
+**Agentic RL**则是一种新的范式,它将LLM视为一个可学习的策略,嵌入在一个顺序决策循环中。在这个框架下,智能体需要在动态环境中与外部世界交互,执行多步行动来完成复杂任务,获得中间反馈来指导后续决策,优化长期累积奖励而非单步奖励。
+
+让我们通过一个具体例子来理解这个区别。在PBRFT场景中,用户问"请解释什么是强化学习",模型生成完整回答,然后根据回答质量直接给分。而在Agentic RL场景中,用户请求"帮我分析这个GitHub仓库的代码质量",智能体需要经历多个步骤:首先调用GitHub API获取仓库信息,成功获得仓库结构和文件列表,得到+0.1的奖;然后读取主要代码文件,成功获得代码内容,得到+0.1的奖励;接着分析代码质量合理,得到+0.2的奖励;最后生成分析报告质量高,得到+0.6的奖励。总奖励是所有步骤的累积:1.0。
+
+可以看到,Agentic RL的关键特征是多步交互、每一步的行动都会改变环境状态、每一步都可以获得反馈、优化整个任务的完成质量。
+
+强化学习是基于马尔可夫决策过程(Markov Decision Process, MDP)框架进行形式化的。MDP由五元组 $(S, A, P, R, \gamma)$ 定义:状态空间$S$、行动空间$A$、状态转移函数$P(s'|s,a)$、奖励函数$R(s,a)$、折扣因子$\gamma$。让我们从MDP的角度对比PBRFT和Agentic RL,如表11.1所示。
+
+<div align="center">
+  <p>表 11.1 PBRFT与Agentic RL对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-1.png" alt="" width="85%"/>
+</div>
+在状态方面,PBRFT的状态 $s_0$ 仅由用户提示构成,时间跨度 $T=1$(单步),状态不变化,可以表示为 $s_0 = \text{prompt}$。而Agentic RL的状态 $s_t$ 包含历史观察和上下文,时间跨度 $T \gg 1$(多步),状态随行动演化,可以表示为 $s_t = (\text{prompt}, o_1, o_2, ..., o_t)$,其中 $o_t$ 是第 $t$ 步的观察(如工具返回结果、环境反馈等)。
+
+在行动方面,PBRFT的行动空间只有文本生成,单一行动类型,表示为 $a = y \sim \pi_\theta(y|s_0)$。而Agentic RL的行动空间包含文本生成、工具调用、环境操作等多种类型,表示为 $a_t \in \{a_t^{\text{text}}, a_t^{\text{tool}}\}$,例如 $a_t^{\text{text}}$ 是生成思考过程或回答,$a_t^{\text{tool}}$ 是调用计算器、搜索引擎等工具。
+
+在转移函数方面,PBRFT无状态转移,表示为 $P(s'|s,a) = \delta(s' - s_{\text{terminal}})$。而Agentic RL的状态根据行动和环境动态变化,表示为 $s_{t+1} \sim P(s_{t+1}|s_t, a_t)$,例如调用搜索工具后,状态会包含搜索结果。
+
+在奖励方面,PBRFT只有单步奖励 $r(s_0, a)$,仅在任务结束时给予,表示为 $R_{\text{PBRFT}} = r(s_0, y)$,通常由奖励模型给出: $r(s_0, y) = r_\phi(s_0, y)$。而Agentic RL有多步奖励 $r(s_t, a_t)$,可以在中间步骤给予部分奖励,表示为:
+
+$$
+R_{\text{Agentic}} = \sum_{t=0}^{T} \gamma^t r(s_t, a_t)
+$$
+
+其中 $\gamma \in [0,1]$ 是折扣因子,$r(s_t, a_t)$ 可以是稀疏奖励(只在任务完成时给予,如答案正确 +1)、密集奖励(每步都给予,如工具调用成功 +0.1)或结合两者的混合奖励。
+
+在目标函数方面,PBRFT最大化单步期望奖励:
+
+$$
+J_{\text{PBRFT}}(\theta) = \mathbb{E}_{s_0, y \sim \pi_\theta} [r(s_0, y)]
+$$
+
+而Agentic RL最大化累积折扣奖励:
+
+$$
+J_{\text{Agentic}}(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \gamma^t r(s_t, a_t)\right]
+$$
+
+其中 $\tau = (s_0, a_0, s_1, a_1, ..., s_T)$ 是完整的轨迹(trajectory)。
+
+这种转变不仅仅是技术细节的差异,而是思维方式的根本转变。PBRFT思维关注"如何让模型生成更好的单个回答",优化回答质量,关注语言表达,进行单步决策。而Agentic RL思维关注"如何让智能体完成复杂任务",优化任务完成度,关注行动策略,进行多步规划。这种转变使得LLM从"对话助手"进化为"自主智能体",能够主动寻找信息、知道何时、如何使用外部工具、为了最终目标,愿意执行看似"绕路"的中间步骤、从错误学习。
+
+Agentic RL的目标是赋予LLM智能体六大核心能力,如图11.2所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-2.png" alt="" width="85%"/>
+  <p>图 11.2 Agentic RL的六大核心能力</p>
+</div>
+
+**推理(Reasoning)**是指从给定信息中逻辑地得出结论的过程,是智能体的核心能力。传统的CoT提示方法依赖少样本示例,泛化能力有限;SFT只能模仿训练数据中的推理模式,难以创新。强化学习的优势在于通过试错学习有效的推理策略,发现训练数据中没有的推理路径,学会何时需要深度思考、何时可以快速回答。推理任务可以建模为序列决策问题,给定问题 $q$,智能体需要生成推理链 $c = (c_1, c_2, ..., c_n)$ 和最终答案 $a$。奖励函数通常设计为 $r(q, c, a) = 1$ if $a = a^*$ else $0$,训练目标是 $\max_\theta \mathbb{E}_{q, (c,a) \sim \pi_\theta} [r(q, c, a)]$。通过这种方式,模型学会生成高质量的推理链,而不仅仅是记忆答案。
+
+**工具使用(Tool Use)**是指智能体调用外部工来完成任务的能力。在工具使用任务中,行动空间扩展为 $a_t \in \{a_t^{\text{think}}, a_t^{\text{tool}}\}$,其中 $a_t^{\text{think}}$ 是生成思考过程,$a_t^{\text{tool}} = (\text{tool\_name}, \text{arguments})$ 是调用工具。强化学习让智能体学会何时需要使用工具、选择哪个工具、如何组合多个工具。例如,在解决数学问题时,智能体需要学会何时使用计算器、何时使用代码解释器、何时直接推理。
+
+**记忆(Memory)**是指智能体保持和重用过去信息的能力,对于长期任务至关重要。LLM的上下文窗口有限,静态检索策略(如RAG)无法针对任务优化。强化学习让智能体学会记忆管理策略:决定哪些信息值得记住、何时更新记忆、何时删除过时信息。这类似于人类的工作记忆,我们会主动管理大脑中的信息,保留重要的、遗忘无关的。
+
+**规划(Planning)**是指制定行动序列以达成目标的能力。传统的CoT是线性思考,无法回溯;提示工程使用静态规划模板,难以适应新情况。强化学习让智能体学会动态规划:通过试错发现有效的行动序列,学会权衡短期和长期收益。例如,在多步任务中,智能体可能需要先执行一些看似"绕路"的步骤,例如收集信息,才能最终完成任务。
+
+**自我改进(Self-Improvement)**是指智能体回顾自身输出、纠正错误并优化策略的能力。强化学习让智能体学会自我反思:识别自己的错误、分析失败原因、调整策略。这种能力使得智能体能够在没有人工干预的情况下持续改进,类似于人类的"从错误中学习"。
+
+**感知(Perception)**是指理解多模态信息的能力。例如,强化学习可以提升视觉推理能力,让模型学会使用视觉工具,学会视觉规划。这使得智能体不仅能理解文本,还能理解和操作视觉世界。
+
+### 11.1.4 HelloAgents的Agentic RL设计
+
+在理解了Agentic RL的核心理念后,让我们看看如何在HelloAgents框架中实现这些能力。
+
+在技术选型上,我们集成了TRL(Transformer Reinforcement Learning)框架<sup>[9]</sup>,模型选择Qwen3-0.6B<sup>[10]</sup>。TRL是Hugging Face的强化学习库,成熟稳定、功能完整、易于集成。Qwen3-0.6B是阿里云的小型语言模型,0.6B参数适合普通GPU训练,性能优秀且开源免费。
+
+HelloAgents的Agentic RL模块采用四层架构设计,如图11.3所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-3.png" alt="" width="85%"/>
+  <p>图 11.3 HelloAgents Agentic RL架构</p>
+</div>
+最底层是**数据集层**,包含`GSM8KDataset`类、`create_sft_dataset()`函数和`create_rl_dataset()`函数,负责数据加载和格式转换。第二层是**奖励函数层**,包含`MathRewardFunction`基类、`AccuracyReward`准确率奖励、`LengthPenaltyReward`长度惩罚、`StepReward`步骤奖励,以及便捷创建函数`create_*_reward()`,负责定义什么是好的行为。第三层是**训练器层**,包含`SFTTrainerWrapper`和`GRPOTrainerWrapper`,负责具体的训练逻辑和LoRA支持。最顶层是**统一接口层**,提供`RLTrainingTool`统一训练工具,支持四种操作:`action="train"`(训练模型)、`action="load_dataset"`(加载数据集)、`action="create_reward"`(创建奖励函数)、`action="evaluate"`(评估模型)。
+
+### 11.1.5 快速上手示例
+
+在深入学习之前,让我们先快速体验一下完整的训练流程。首先安装HelloAgents框架:
+
+```bash
+# 安装HelloAgents框架(第11章版本)
+pip install hello-agents[rl]==0.2.5
+
+# 或者从源码安装
+cd HelloAgents
+pip install -e ".[rl]"
+```
+
+然后运行快速训练示例:
+
+```python
+import sys
+import json
+
+from hello_agents.tools import RLTrainingTool
+
+# 创建RL训练工具
+rl_tool = RLTrainingTool()
+
+# 1. 快速测试:SFT训练(10个样本,1个epoch)
+sft_result_str = rl_tool.run({
+    "action": "train",
+    "algorithm": "sft",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/quick_test_sft",
+    "max_samples": 10,      # 只用10个样本快速测试
+    "num_epochs": 1,        # 只训练1轮
+    "batch_size": 2,
+    "use_lora": True        # 使用LoRA加速训练
+})
+
+sft_result = json.loads(sft_result_str)
+print(f"\n✓ SFT训练完成,模型保存在: {sft_result['output_dir']}")
+
+# 2. GRPO训练(5个样本,1个epoch)
+grpo_result_str = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",  # 使用基础模型
+    "output_dir": "./models/quick_test_grpo",
+    "max_samples": 5,       # 只用5个样本快速测试
+    "num_epochs": 1,
+    "batch_size": 2,        # 必须能被num_generations(8)整除,使用2
+    "use_lora": True
+})
+
+grpo_result = json.loads(grpo_result_str)
+print(f"\n✓ GRPO训练完成,模型保存在: {grpo_result['output_dir']}")
+
+# 3. 评估模型
+eval_result_str = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "./models/quick_test_grpo",
+    "max_samples": 10,      # 在10个测试样本上评估
+    "use_lora": True
+})
+
+eval_result = json.loads(eval_result_str)
+print(f"\n✓ 评估完成:")
+print(f"  - 准确率: {eval_result['accuracy']}")
+print(f"  - 平均奖励: {eval_result['average_reward']}")
+print(f"  - 测试样本数: {eval_result['num_samples']}")
+
+print("\n" + "=" * 50)
+print("🎉 恭喜!你已经完成了第一个Agentic RL模型的训练!")
+print("=" * 50)
+print(f"\n模型路径:")
+print(f"  SFT模型: {sft_result['output_dir']}")
+print(f"  GRPO模型: {grpo_result['output_dir']}")
+```
+
+这个快速示例展示了完整的训练流程:SFT训练让模型学习基础的推理格式和对话模式,GRPO训练通过强化学习优化推理策略提升准确率,模型评估在测试集上评估训练效果。另外跑完之后准确率很低是正常现象,因为现在模型只见过0.7%的训练样本,并且只运行了一轮。
+
+## 11.2 数据集与奖励函数
+
+数据集和奖励函数是强化学习训练的两大基石。数据集定义了智能体要学习的任务,奖励函数定义了什么是好的行为。在本节中,我们将学习如何准备训练数据和设计奖励函数。
+
+### 11.2.1 GSM8K数学推理数据集
+
+数学推理是评估LLM推理能力的理想任务。首先,数学问题有明确的正确答案,可以自动评估,不需要人工标注或复杂的奖励模型。其次,解决数学问题需要分解问题、逐步推导,这正是多步推理的典型场景。最后,学到的推理能力可以迁移到其他领域,具有很强的泛化性。相比之下,开放式问答任务(如"如何学习编程?")的答案质量难以客观评估,需要大量人工标注。
+
+GSM8K(Grade School Math 8K)<sup>[4]</sup>是一个高质量的小学数学应用题数据集。如表11.2所示,数据集包含7,473个训练样本和1,319个测试样本,难度为小学数学水平(2-8年级),题型为应用题,需要2-8步推理才能得出答案。
+
+<div align="center">
+  <p>表 11.2 GSM8K数据集统计</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-2.png" alt="" width="85%"/>
+</div>
+让我们看一个典型的GSM8K问题:
+
+```
+问题: Natalia sold clips to 48 of her friends in April, and then she sold half 
+      as many clips in May. How many clips did Natalia sell altogether in April 
+      and May?
+
+答案: Natalia sold 48/2 = <<48/2=24>>24 clips in May.
+      Natalia sold 48+24 = <<48+24=72>>72 clips altogether in April and May.
+      #### 72
+
+最终答案: 72
+```
+
+这个问题需要两步推理:首先计算5月份卖出的数量(48的一半),然后计算总数(4月+5月)。答案中的`<<48/2=24>>`是中间计算步骤的标记,`#### 72`标记最终答案。
+
+GSM8K数据集需要转换为不同的格式,以适应不同的训练方法,如图11.4所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-4.png" alt="" width="85%"/>
+  <p>图 11.4 GSM8K数据格式转换</p>
+</div>
+
+
+原始格式直接来自数据集,包含问题(question)和答案(answer,含解题步骤),适合人类阅读。SFT格式用于监督微调,将问题转换为对话格式的prompt,将完整解答作为completion。例如:
+
+```python
+{
+    "prompt": "<|im_start|>user\nNatalia sold clips to 48 of her friends...<|im_end|>\n<|im_start|>assistant\n",
+    "completion": "Let me solve this step by step.\n\nStep 1: ...\n\nFinal Answer: 72<|im_end|>"
+}
+```
+
+关键点是使用模型的对话模板(如Qwen的`<|im_start|>`标记),prompt包含用户问题,completion包含完整的解题过程和答案。这样模型可以学习如何格式化输出、如何分步推理。
+
+RL格式用于强化学习,只提供问题和正确答案,不提供解题过程。例如:
+
+```python
+{
+    "prompt": "<|im_start|>user\nNatalia sold clips to 48 of her friends...<|im_end|>\n<|im_start|>assistant\n",
+    "ground_truth": "72"
+}
+```
+
+关键点是prompt与SFT相同,但ground_truth只包含最终答案(用于计算奖励),模型需要自己生成完整的推理过程。这种设计迫使模型学会自主推理,而不是简单地记忆答案。
+
+如表11.3所示,三种格式各有用途。
+
+<div align="center">
+  <p>表 11.3 数据格式对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-3.png" alt="" width="85%"/>
+</div>
+HelloAgents提供了便捷的数据集加载函数。让我们通过代码来加载和查看数据集:
+
+```python
+from hello_agents.tools import RLTrainingTool
+import json
+
+# 创建工具
+rl_tool = RLTrainingTool()
+
+# 1. 加载SFT格式数据集
+sft_result = rl_tool.run({
+    "action": "load_dataset",
+    "format": "sft",
+    "max_samples": 5  # 只加载5个样本查看
+})
+sft_data = json.loads(sft_result)
+
+print(f"数据集大小: {sft_data['dataset_size']}")
+print(f"数据格式: {sft_data['format']}")
+print(f"样本字段: {sft_data['sample_keys']}")
+
+# 2. 加载RL格式数据集
+rl_result = rl_tool.run({
+    "action": "load_dataset",
+    "format": "rl",
+    "max_samples": 5
+})
+rl_data = json.loads(rl_result)
+
+print(f"数据集大小: {rl_data['dataset_size']}")
+print(f"数据格式: {rl_data['format']}")
+print(f"样本字段: {rl_data['sample_keys']}")
+```
+
+可以看到,SFT格式包含完整的解题过程,用于监督学习;RL格式只包含最终答案,模型需要自己生成推理过程。`max_samples`参数控制加载的样本数量,方便快速测试。
+
+### 11.2.2 奖励函数设计
+
+奖励函数是强化学习的核心,它定义了什么是"好的行为"。一个好的奖励函数能够引导智能体学习到正确的策略,而一个糟糕的奖励函数可能导致训练失败或学到错误的行为。
+
+在强化学习中,奖励函数 $r(s, a)$ 或 $r(s, a, s')$ 为智能体的每个行动分配一个数值奖励。智能体的目标是最大化累积奖励:
+
+$$
+J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta} \left[\sum_{t=0}^{T} \gamma^t r(s_t, a_t)\right]
+$$
+
+对于数学推理任务,我们可以简化为:
+
+$$
+r(q, a) = f(a, a^*)
+$$
+
+其中 $q$ 是问题,$a$ 是模型生成的答案,$a^*$ 是正确答案,$f$ 是评估函数。
+
+奖励函数的设计直接影响训练效果。好的奖励函数应该能清楚地定义什么是成功、能够提供梯度信号、不会产生过大的方差、容易调整和组合。糟糕的奖励函数可能只在任务结束时给奖励,中间步骤无反馈、存在奖励欺骗,使得智能体找到"作弊"方式获得高奖励、多个目标相互矛盾、方差过大,训练不收敛。
+
+HelloAgents提供了三种内置奖励函数,可以单独使用或组合使用,如图11.5所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-5.png" alt="" width="85%"/>
+  <p>图 11.5 奖励函数设计</p>
+</div>
+**(1)准确率奖励**
+
+准确率奖励(AccuracyReward)是最基础的奖励函数,它只关心答案是否正确。数学定义为:
+
+$$
+r_{\text{acc}}(a, a^*) = \begin{cases}
+1 & \text{if } a = a^* \\
+0 & \text{otherwise}
+\end{cases}
+$$
+
+其中 $a$ 是模型生成的答案,$a^*$ 是正确答案。这是一个二值奖励函数,答案正确得1分,错误得0分。
+
+实现时需要处理答案提取和比较。模型的输出可能包含大量文本,我们需要提取最终答案。常见的提取方法包括:查找"Final Answer:"后的数字、查找"####"标记后的数字、使用正则表达式提取最后一个数字。答案比较时需要处理数值精度(如72.0和72应该视为相同)、单位转换(如1000和1k)、格式差异(如"72"和"seventy-two")。
+
+使用示例:
+
+```python
+from hello_agents.tools import RLTrainingTool
+import json
+rl_tool = RLTrainingTool()
+
+# 创建准确率奖励函数
+reward_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "accuracy"
+})
+reward_data = json.loads(reward_result)
+
+print(f"奖励类型: {reward_data['reward_type']}")
+print(f"描述: {reward_data['description']}")
+
+# 注意: RLTrainingTool的create_reward操作返回的是配置信息,
+# 实际的奖励函数会在训练时自动创建和使用
+```
+
+输出:
+
+```json
+预测: 72, 真实: 72, 奖励: 1.0
+预测: 72.0, 真实: 72, 奖励: 1.0
+预测: 73, 真实: 72, 奖励: 0.0
+```
+
+准确率奖励的优点是简单直接,容易理解和实现,适合有明确正确答案的任务。缺点是奖励稀疏,只有答案完全正确才有奖励,无法区分"接近正确"和"完全错误",可能导致训练初期缺乏有效反馈。
+
+**(2)长度惩罚**
+
+长度惩罚(LengthPenaltyReward)鼓励模型生成简洁的回答,避免冗长啰嗦。数学定义为:
+
+$$
+r_{\text{length}}(a, a^*, l) = r_{\text{acc}}(a, a^*) - \alpha \cdot \max(0, l - l_{\text{target}})
+$$
+
+其中 $l$ 是生成文本的长度(字符数或token数),$l_{\text{target}}$ 是目标长度,$\alpha$ 是惩罚系数(默认0.001)。只有在答案正确的情况下才应用长度惩罚,避免模型为了减少惩罚而生成错误的短答案。
+
+设计思路是:如果答案错误,奖励为0(无论长度);如果答案正确且长度合理,奖励为1;如果答案正确但过长,奖励为 $1 - \alpha \cdot (l - l_{\text{target}})$。例如,目标长度200字符,实际长度500字符,惩罚系数0.001,则奖励为 $1 - 0.001 \times (500 - 200) = 0.7$。
+
+使用示例:
+
+```python
+# 创建长度惩罚奖励函数
+reward_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "length_penalty",
+    "max_length": 1024,      # 最大长度
+    "penalty_weight": 0.001  # 惩罚权重
+})
+reward_data = json.loads(reward_result)
+
+print(f"奖励类型: {reward_data['reward_type']}")
+print(f"描述: {reward_data['description']}")
+print(f"最大长度: {reward_data['max_length']}")
+print(f"惩罚权重: {reward_data['penalty_weight']}")
+```
+
+输出:
+
+```
+预测: 72, 真实: 72, 长度: 50, 奖励: 1.000
+预测: 72, 真实: 72, 长度: 200, 奖励: 1.000
+预测: 72, 真实: 72, 长度: 500, 奖励: 0.700
+预测: 73, 真实: 72, 长度: 50, 奖励: 0.000
+```
+
+长度惩罚的优点是鼓励简洁表达,避免模型生成冗余内容,可以控制推理成本(更短的输出意味着更少的token消耗)。缺点是可能抑制详细推理,需要仔细调整惩罚系数,不同任务的最优长度差异很大。
+
+**(3)步骤奖励**
+
+步骤奖励(StepReward)鼓励模型生成清晰的推理步骤,提高可解释性。数学定义为:
+
+$$
+r_{\text{step}}(a, a^*, s) = r_{\text{acc}}(a, a^*) + \beta \cdot s
+$$
+
+其中 $s$ 是检测到的推理步骤数量,$\beta$ 是步骤奖励系数(默认0.1)。同样,只有在答案正确的情况下才给予步骤奖励。
+
+步骤检测方法包括:查找"Step 1:", "Step 2:"等标记、查找换行符数量、使用正则表达式匹配推理模式。例如,一个包含3个清晰步骤的正确答案,奖励为 $1 + 0.1 \times 3 = 1.3$。
+
+使用示例:
+
+```python
+# 创建步骤奖励函数
+reward_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "step",
+    "step_bonus": 0.1  # 每个步骤奖励0.1
+})
+reward_data = json.loads(reward_result)
+
+print(f"奖励类型: {reward_data['reward_type']}")
+print(f"描述: {reward_data['description']}")
+print(f"步骤奖励: {reward_data['step_bonus']}")
+```
+
+输出:
+
+```
+预测: 72, 真实: 72, 步骤: 0, 奖励: 1.00
+预测: 72, 真实: 72, 步骤: 2, 奖励: 1.20
+预测: 72, 真实: 72, 步骤: 5, 奖励: 1.50
+预测: 73, 真实: 72, 步骤: 5, 奖励: 0.00
+```
+
+步骤奖励的优点是鼓励可解释的推理,生成的答案更容易验证和调试,有助于模型学习系统化的思考方式。缺点是可能导致模型为了获得更多奖励生成冗余步骤,需要平衡步骤数量和答案质量,步骤检测可能不准确。
+
+在实际应用中,我们通常会组合多个奖励函数,以平衡不同的目标。常见的组合策略包括:
+
+**准确率 + 长度惩罚**:鼓励简洁正确的答案,适合对话系统、问答系统。公式为:
+
+$$
+r = r_{\text{acc}} - \alpha \cdot \max(0, l - l_{\text{target}})
+$$
+
+**准确率 + 步骤奖励**:鼓励详细的推理过程,适合教育场景、可解释AI。公式为:
+
+$$
+r = r_{\text{acc}} + \beta \cdot s
+$$
+
+**三者平衡**:全面优化答案质量、简洁性和可解释性。公式为:
+$$
+r = r_{\text{acc}} - \alpha \cdot \max(0, l - l_{\text{target}}) + \beta \cdot s
+$$
+
+需要仔细调整权重 $\alpha$ 和 $\beta$,避免某个目标过度主导。
+
+使用示例:
+
+```python
+# 组合奖励函数:准确率 + 长度惩罚 + 步骤奖励
+# 注意: RLTrainingTool目前支持单一奖励类型
+# 组合奖励需要在训练配置中通过reward_fn参数指定
+# 这里展示如何配置不同类型的奖励函数
+
+# 准确率奖励
+accuracy_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "accuracy"
+})
+print("准确率奖励:", json.loads(accuracy_result)['description'])
+
+# 长度惩罚奖励
+length_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "length_penalty",
+    "max_length": 1024,
+    "penalty_weight": 0.001
+})
+print("长度惩罚奖励:", json.loads(length_result)['description'])
+
+# 步骤奖励
+step_result = rl_tool.run({
+    "action": "create_reward",
+    "reward_type": "step",
+    "step_bonus": 0.1
+})
+print("步骤奖励:", json.loads(step_result)['description'])
+```
+
+输出:
+
+```
+组合奖励: 1.200
+  - 准确率: 1.0
+  - 长度惩罚: -0.100
+  - 步骤奖励: +0.3
+```
+
+如表11.4所示,不同奖励函数适合不同的应用场景。
+
+<div align="center">
+  <p>表 11.4 奖励函数对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-4.png" alt="" width="85%"/>
+</div>
+
+### 11.2.3 自定义数据集和奖励函数
+
+虽然HelloAgents提供了GSM8K数据集和常用奖励函数,但在实际应用中,你可能需要使用自己的数据集或设计特定的奖励函数。本节将介绍如何扩展框架。
+
+在使用自定义数据集之前,需要了解两种训练格式的数据要求:
+
+**SFT格式**:用于监督微调,需要包含以下字段:
+- `prompt`: 输入提示(包含system和user消息)
+- `completion`: 期望的输出
+- `text`: 完整的对话文本(可选)
+
+**RL格式**:用于强化学习,需要包含以下字段:
+- `question`: 原始问题
+- `prompt`: 输入提示(包含system和user消息)
+- `ground_truth`: 正确答案
+- `full_answer`: 完整答案(包含推理过程)
+
+**(1)使用format_math_dataset转换**
+
+最简单的方法是准备包含`question`和`answer`字段的原始数据,然后使用`format_math_dataset()`函数自动转换:
+
+```python
+from datasets import Dataset
+from hello_agents.rl import format_math_dataset
+
+# 1. 准备原始数据
+custom_data = [
+    {
+        "question": "What is 2+2?",
+        "answer": "2+2=4. #### 4"
+    },
+    {
+        "question": "What is 5*3?",
+        "answer": "5*3=15. #### 15"
+    },
+    {
+        "question": "What is 10+7?",
+        "answer": "10+7=17. #### 17"
+    }
+]
+
+# 2. 转换为Dataset对象
+raw_dataset = Dataset.from_list(custom_data)
+
+# 3. 转换为SFT格式
+sft_dataset = format_math_dataset(
+    dataset=raw_dataset,
+    format_type="sft",
+    model_name="Qwen/Qwen3-0.6B"
+)
+print(f"SFT数据集: {len(sft_dataset)}个样本")
+print(f"字段: {sft_dataset.column_names}")
+
+# 4. 转换为RL格式
+rl_dataset = format_math_dataset(
+    dataset=raw_dataset,
+    format_type="rl",
+    model_name="Qwen/Qwen3-0.6B"
+)
+print(f"RL数据集: {len(rl_dataset)}个样本")
+print(f"字段: {rl_dataset.column_names}")
+```
+
+**(2)直接传入自定义数据集**
+
+使用RLTrainingTool时,可以通过`custom_dataset`参数直接传入自定义数据集:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# SFT训练
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "sft",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/custom_sft",
+    "num_epochs": 3,
+    "batch_size": 4,
+    "use_lora": True,
+    "custom_dataset": sft_dataset  # 直接传入自定义数据集
+})
+
+# GRPO训练
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/custom_grpo",
+    "num_epochs": 2,
+    "batch_size": 2,
+    "use_lora": True,
+    "custom_dataset": rl_dataset  # 直接传入自定义数据集
+})
+```
+
+(3)注册自定义数据集(推荐)
+
+对于需要多次使用的数据集,推荐使用注册方式:
+
+```python
+# 1. 注册数据集
+rl_tool.register_dataset("my_math_dataset", rl_dataset)
+
+# 2. 使用注册的数据集
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "dataset": "my_math_dataset",  # 使用注册的数据集名称
+    "output_dir": "./models/custom_grpo",
+    "num_epochs": 2,
+    "use_lora": True
+})
+```
+
+奖励函数用于评估模型生成的答案质量。自定义奖励函数需要遵循以下签名:
+
+```python
+from typing import List
+import re
+
+def custom_reward_function(
+    completions: List[str],
+    **kwargs
+) -> List[float]:
+    """
+    自定义奖励函数
+
+    Args:
+        completions: 模型生成的完成文本列表
+        **kwargs: 其他参数,通常包含:
+            - ground_truth: 正确答案列表
+            - 其他数据集字段
+
+    Returns:
+        奖励值列表(每个值在0.0-1.0之间)
+    """
+    ground_truths = kwargs.get("ground_truth", [])
+    rewards = []
+
+    for completion, truth in zip(completions, ground_truths):
+        reward = 0.0
+
+        # 提取答案
+        numbers = re.findall(r'-?\d+\.?\d*', completion)
+        if numbers:
+            try:
+                pred = float(numbers[-1])
+                truth_num = float(truth)
+                error = abs(pred - truth_num)
+
+                # 根据误差给予不同奖励
+                if error < 0.01:
+                    reward = 1.0  # 完全正确
+                elif error < 1.0:
+                    reward = 0.8  # 非常接近
+                elif error < 5.0:
+                    reward = 0.5  # 接近
+
+                # 额外奖励:鼓励展示推理步骤
+                if "step" in completion.lower() or "=" in completion:
+                    reward += 0.1
+
+            except ValueError:
+                reward = 0.0
+
+        rewards.append(min(reward, 1.0))  # 限制最大值为1.0
+
+    return rewards
+```
+
+有两种方式使用自定义奖励函数:
+
+**(1)直接传入**
+
+```python
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/custom_grpo",
+    "custom_dataset": rl_dataset,
+    "custom_reward": custom_reward_function  # 直接传入奖励函数
+})
+```
+
+**(2)注册使用(推荐)**
+
+```python
+# 1. 注册奖励函数
+rl_tool.register_reward_function("my_reward", custom_reward_function)
+
+# 2. 使用注册的奖励函数
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "dataset": "my_math_dataset",
+    "output_dir": "./models/custom_grpo"
+    # 奖励函数会自动使用与dataset同名的注册函数
+})
+```
+
+以下是一个完整的自定义数据集和奖励函数示例:
+
+```python
+from datasets import Dataset
+from hello_agents.tools import RLTrainingTool
+from hello_agents.rl import format_math_dataset
+import re
+from typing import List
+
+# 1. 准备自定义数据
+custom_data = [
+    {"question": "What is 2+2?", "answer": "2+2=4. #### 4"},
+    {"question": "What is 5+3?", "answer": "5+3=8. #### 8"},
+    {"question": "What is 10+7?", "answer": "10+7=17. #### 17"}
+]
+
+# 2. 转换为训练格式
+raw_dataset = Dataset.from_list(custom_data)
+rl_dataset = format_math_dataset(raw_dataset, format_type="rl")
+
+# 3. 定义自定义奖励函数
+def tolerant_reward(completions: List[str], **kwargs) -> List[float]:
+    """带容差的奖励函数"""
+    ground_truths = kwargs.get("ground_truth", [])
+    rewards = []
+
+    for completion, truth in zip(completions, ground_truths):
+        numbers = re.findall(r'-?\d+\.?\d*', completion)
+        if numbers:
+            try:
+                pred = float(numbers[-1])
+                truth_num = float(truth)
+                error = abs(pred - truth_num)
+
+                if error < 0.01:
+                    reward = 1.0
+                elif error < 5.0:
+                    reward = 0.5
+                else:
+                    reward = 0.0
+            except ValueError:
+                reward = 0.0
+        else:
+            reward = 0.0
+
+        rewards.append(reward)
+
+    return rewards
+
+# 4. 创建工具并注册
+rl_tool = RLTrainingTool()
+rl_tool.register_dataset("my_dataset", rl_dataset)
+rl_tool.register_reward_function("my_dataset", tolerant_reward)
+
+# 5. 训练
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "dataset": "my_dataset",
+    "output_dir": "./models/custom_grpo",
+    "num_epochs": 2,
+    "batch_size": 2,
+    "use_lora": True
+})
+```
+
+## 11.3 SFT训练
+
+监督微调(Supervised Fine-Tuning, SFT)是强化学习训练的第一步,也是最重要的基础。SFT让模型学习任务的基本格式、对话模式和初步的推理能力。没有SFT的基础,直接进行强化学习往往会失败,因为模型连基本的输出格式都不会。
+
+### 11.3.1 为什么需要SFT
+
+在开始强化学习之前,我们需要先进行SFT训练。这是因为预训练模型虽然具备强大的语言能力,但它并不知道如何完成特定任务。预训练模型的训练目标是预测下一个词,而不是解决数学问题或使用工具。预训练模型的输出格式是自由文本,而我们需要结构化的输出(如"Step 1: ..., Step 2: ..., Final Answer: ...")。预训练模型没有见过任务相关的数据,不知道什么是"好的"推理过程。
+
+SFT的作用是教会模型任务的基本规则。首先,学习输出格式,让模型知道如何组织答案(如使用"Step 1", "Final Answer"等标记)。其次,学习推理模式,通过示例学习如何分解问题、逐步推导。再次,建立基线能力,为后续的强化学习提供一个合理的起点。最后,减少探索空间,强化学习不需要从零开始,可以在SFT的基础上优化。
+
+让我们通过一个对比实验来理解SFT的重要性。假设我们直接用预训练模型解决GSM8K问题:
+
+```python
+from transformers import AutoTokenizer, AutoModelForCausalLM
+
+# 加载预训练模型
+model_name = "Qwen/Qwen3-0.6B"
+tokenizer = AutoTokenizer.from_pretrained(model_name)
+model = AutoModelForCausalLM.from_pretrained(model_name)
+
+# 测试问题
+question = """Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?"""
+
+# 构造输入
+prompt = f"<|im_start|>user\n{question}<|im_end|>\n<|im_start|>assistant\n"
+inputs = tokenizer(prompt, return_tensors="pt")
+
+# 生成回答
+outputs = model.generate(**inputs, max_new_tokens=200)
+response = tokenizer.decode(outputs[0], skip_special_tokens=False)
+
+print("预训练模型的回答:")
+print(response)
+```
+
+预训练模型的输出可能是:
+
+```bash
+<|im_start|>user
+Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?<|im_end|>
+<|im_start|>assistant
+I can help you with that. Let me think about this problem. Natalia sold clips to her friends. In April she sold 48 clips. In May she sold half as many. So we need to find out how many she sold in May and then add them together. But I'm not sure how to calculate half of 48. Maybe I should use a calculator? Or maybe I can just estimate...
+```
+
+可以看到,预训练模型虽然理解了问题,但输出非常冗长、缺乏结构、没有明确的答案、推理过程混乱。这样的输出无法用于强化学习,因为我们无法提取答案、无法评估质量、无法提供有效的奖励信号。
+
+现在让我们看看SFT后的模型:
+
+```python
+# 加载SFT模型(假设已经训练好)
+sft_model_path = "./models/sft_model"
+sft_model = AutoModelForCausalLM.from_pretrained(sft_model_path)
+
+# 使用相同的问题
+outputs = sft_model.generate(**inputs, max_new_tokens=200)
+response = tokenizer.decode(outputs[0], skip_special_tokens=False)
+
+print("SFT模型的回答:")
+print(response)
+```
+
+SFT模型的输出:
+
+```bash
+<|im_start|>user
+Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether in April and May?<|im_end|>
+<|im_start|>assistant
+Let me solve this step by step.
+
+Step 1: Calculate clips sold in May
+Natalia sold half as many clips in May as in April.
+Clips in May = 48 / 2 = 24
+
+Step 2: Calculate total clips
+Total = April + May = 48 + 24 = 72
+
+Final Answer: 72<|im_end|>
+```
+
+可以看到,SFT模型的输出结构清晰(使用"Step 1", "Step 2", "Final Answer"标记)、推理正确、答案明确、格式统一。这样的输出可以用于强化学习,因为我们可以提取答案、计算奖励、优化策略。
+
+如图11.6所示,SFT是从预训练模型到强化学习的桥梁。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-6.png" alt="" width="85%"/>
+  <p>图 11.6 SFT在训练流程中的作用</p>
+</div>
+
+### 11.3.2 LoRA:参数高效微调
+
+直接微调整个模型需要大量的计算资源和显存。对于Qwen3-0.6B(0.6B参数),全量微调需要约12GB显存(FP16)或24GB显存(FP32)。对于更大的模型(如7B、13B),全量微调几乎不可能在消费级GPU上进行。
+
+LoRA(Low-Rank Adaptation)<sup>[3]</sup>是一种参数高效微调方法,它只训练少量的额外参数,而保持原模型参数冻结。LoRA的核心思想是:模型微调时的参数变化可以用低秩矩阵表示。
+
+假设原模型的权重矩阵为 $W \in \mathbb{R}^{d \times k}$,微调后的权重为 $W' = W + \Delta W$。LoRA假设 $\Delta W$ 可以分解为两个低秩矩阵的乘积:
+
+$$
+\Delta W = BA
+$$
+
+其中 $B \in \mathbb{R}^{d \times r}$, $A \in \mathbb{R}^{r \times k}$, $r \ll \min(d, k)$ 是秩(rank)。
+
+前向传播时,输出为:
+
+$$
+h = Wx + \Delta Wx = Wx + BAx
+$$
+
+原模型参数 $W$ 保持冻结,只训练 $B$ 和 $A$。
+
+参数量对比:原模型参数量为 $d \times k$,LoRA参数量为 $d \times r + r \times k = r(d + k)$。当 $r \ll \min(d, k)$ 时,LoRA参数量远小于原模型。例如,对于 $d=4096, k=4096, r=8$ 的情况,原模型参数量为 $4096 \times 4096 = 16,777,216$,LoRA参数量为 $8 \times (4096 + 4096) = 65,536$,参数量减少了256倍!
+
+因此可以总结LoRA的优势:显存占用大幅降低、训练速度更快、易于部署、防止过拟合。不过训练的效果通常情况会比全量调参更差一些。
+
+如表11.5所示,LoRA在不同模型规模下的效果对比。
+
+<div align="center">
+  <p>表 11.5 LoRA vs 全量微调对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-5.png" alt="" width="85%"/>
+</div>
+
+LoRA的关键超参数包括:秩(rank,r),控制LoRA矩阵的秩,越大表达能力越强,但参数量也越多,典型值为4-64,默认8;Alpha($\alpha$),LoRA的缩放因子,实际更新为 $\Delta W = \frac{\alpha}{r} BA$,控制LoRA的影响强度,典型值等于rank;目标模块(target_modules),指定哪些层应用LoRA,通常选择注意力层(q_proj, k_proj, v_proj, o_proj),也可以包括MLP层(gate_proj, up_proj, down_proj)。
+
+### 11.3.3 SFT训练实战
+
+现在让我们使用HelloAgents进行SFT训练。完整的训练流程包括:准备数据集、配置LoRA、设置训练参数、开始训练、保存模型。
+
+基础训练示例:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+# 创建训练工具
+rl_tool = RLTrainingTool()
+
+# SFT训练
+result = rl_tool.run({
+    # 训练配置
+    "action": "train",
+    "algorithm": "sft",
+    
+    # 模型配置
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/sft_model",
+    
+    # 数据配置
+    "max_samples": 100,     # 使用100个样本快速测试
+    
+    # 训练参数
+    "num_epochs": 3,        # 训练3轮
+    "batch_size": 4,        # 批次大小
+    "learning_rate": 5e-5,  # 学习率
+    
+    # LoRA配置
+    "use_lora": True,       # 使用LoRA
+    "lora_rank": 8,         # LoRA秩
+    "lora_alpha": 16,       # LoRA alpha
+})
+
+print(f"\n✓ 训练完成!")
+print(f"  - 模型保存路径: {result['model_path']}")
+print(f"  - 训练样本数: {result['num_samples']}")
+print(f"  - 训练轮数: {result['num_epochs']}")
+print(f"  - 最终损失: {result['final_loss']:.4f}")
+```
+
+如果训练过程中损失逐渐下降,说明模型正在学习。
+
+**(1)训练参数详解**
+
+让我们详细了解各个训练参数的含义和调优建议。
+
+**数据参数**:
+
+- `max_samples`: 使用的训练样本数量。快速测试时可以用100-1000个样本,完整训练建议使用全部数据(7473个样本)。更多数据通常带来更好的效果,但训练时间也更长。
+- `split`: 数据集划分,默认"train"。可以设置为"train[:1000]"只使用前1000个样本。
+
+**训练参数**:
+
+- `num_epochs`: 训练轮数。1轮表示遍历整个数据集一次。太少(1-2轮)可能欠拟合,太多(>10轮)可能过拟合。建议从3轮开始,观察损失曲线调整。
+- `batch_size`: 每次更新使用的样本数。越大训练越稳定,但显存占用越高。建议根据显存调整:4GB显存用batch_size=1-2,8GB显存用batch_size=4-8,16GB显存用batch_size=8-16。
+- `learning_rate`: 学习率,控制参数更新的步长。太小(1e-6)收敛慢,太大(1e-3)可能不收敛。SFT推荐5e-5,LoRA可以稍大(1e-4)。
+
+**LoRA参数**:
+
+- `use_lora`: 是否使用LoRA。建议始终开启,除非有充足的显存。
+- `lora_rank`: LoRA秩,控制表达能力。4-8适合小任务,16-32适合复杂任务,64适合大规模微调。
+- `lora_alpha`: LoRA缩放因子,通常设置为rank的2倍。rank=8时,alpha=16;rank=16时,alpha=32。
+
+**优化器参数**:
+
+- `optimizer`: 优化器类型,默认"adamw"。AdamW是最常用的选择,也可以尝试"sgd"或"adafactor"等。
+- `weight_decay`: 权重衰减,防止过拟合。默认0.01,可以尝试0.001-0.1。
+- `warmup_ratio`: 学习率预热比例。前warmup_ratio的步数学习率线性增加,然后线性衰减。默认0.1(前10%步数预热)。
+
+**(2)完整训练示例**
+
+让我们进行一次完整的SFT训练,使用全部数据和最佳实践:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 完整SFT训练
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "sft",
+
+    # 模型配置
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/sft_full",
+
+    # 数据配置
+    "max_samples": None,    # 使用全部数据(7473个样本)
+
+    # 训练参数
+    "num_epochs": 3,
+    "batch_size": 8,
+    "learning_rate": 5e-5,
+    "warmup_ratio": 0.1,
+    "weight_decay": 0.01,
+
+    # LoRA配置
+    "use_lora": True,
+    "lora_rank": 16,        # 使用更大的rank
+    "lora_alpha": 32,
+    "lora_target_modules": ["q_proj", "k_proj", "v_proj", "o_proj"],
+
+    # 其他配置
+    "save_steps": 500,      # 每500步保存一次
+    "logging_steps": 100,   # 每100步记录一次
+    "eval_steps": 500,      # 每500步评估一次
+})
+
+print(f"训练完成! 模型保存在: {result['model_path']}")
+```
+
+这个配置适合在8GB显存的GPU上训练,预计耗时30-60分钟。
+
+**(3)训练监控和调试**
+
+在训练过程中,我们需要监控三个关键指标。损失(Loss)应该逐渐下降,如果不下降可能是学习率太小或数据有问题,如果下降后又上升则可能是学习率太大或出现过拟合。梯度范数(Gradient Norm)应该在0.1-10的合理范围内,过大(>100)说明出现梯度爆炸需要降低学习率,过小(<0.01)说明梯度消失需要检查模型配置。学习率(Learning Rate)应该按照warmup策略变化,前10%步数线性增加,然后线性衰减到0。
+
+训练中常见的问题及解决方案:显存不足时可以减小batch_size或max_length,使用梯度累积或更小的模型;训练速度慢时可以增大batch_size,减少logging频率,或使用混合精度训练;损失不下降时可以增大学习率,检查数据格式,或增加训练轮数;过拟合时可以增大weight_decay,减少训练轮数,或使用更多数据。
+
+### 11.3.4 模型评估
+
+训练完成后,我们需要评估模型的效果。评估指标包括:
+
+- **准确率(Accuracy)**:答案完全正确的比例,最直接的指标,范围0-1,越高越好。
+
+- **平均奖励(Average Reward)**:所有样本的平均奖励,综合考虑准确率、长度、步骤等因素,范围取决于奖励函数设计。
+
+- **推理质量(Reasoning Quality)**:推理过程的清晰度和逻辑性,需要人工评估或使用专门的评估模型。
+
+使用HelloAgents评估模型:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 评估SFT模型
+eval_result = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "./models/sft_full",
+    "max_samples": 100,     # 在100个测试样本上评估
+    "use_lora": True,
+})
+
+eval_data = json.loads(eval_result)
+print(f"\n评估结果:")
+print(f"  - 准确率: {eval_data['accuracy']}")
+print(f"  - 平均奖励: {eval_data['average_reward']}")
+print(f"  - 测试样本数: {eval_data['num_samples']}")
+```
+
+对于Qwen3-0.6B这样的小模型,SFT后在GSM8K上达到40-50%的准确率是正常的。通过强化学习,我们可以进一步提升到60-70%。
+
+为了更好地理解SFT的效果,我们可以对比不同阶段的模型:
+
+```python
+# 评估预训练模型(未经SFT)
+base_result = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "Qwen/Qwen3-0.6B",
+    "max_samples": 100,
+    "use_lora": False,
+})
+base_data = json.loads(base_result)
+
+# 评估SFT模型
+sft_result = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "./models/sft_full",
+    "max_samples": 100,
+    "use_lora": True,
+})
+sft_data = json.loads(sft_result)
+
+# 对比结果
+print("模型对比:")
+print(f"预训练模型准确率: {base_data['accuracy']}")
+print(f"SFT模型准确率: {sft_data['accuracy']}"
+```
+
+在本节中,我们学习了SFT的重要性(学习格式、建立基线)、LoRA原理(低秩分解、参数高效)、SFT训练实战(参数配置、训练监控)、模型评估(准确率、对比分析)。
+
+## 11.4 GRPO训练
+
+在完成SFT训练后,我们已经得到了一个能够生成结构化答案的模型。但是,SFT模型只是学会了"模仿"训练数据中的推理过程,并没有真正学会"思考"。强化学习可以让模型通过试错来优化推理策略,从而超越训练数据的质量。
+
+### 11.4.1 从PPO到GRPO
+
+在强化学习领域,PPO(Proximal Policy Optimization)<sup>[1]</sup>是最经典的算法之一。PPO通过限制策略更新的幅度,保证训练的稳定性。但是,PPO在LLM训练中存在一些问题:需要训练Value Model(价值模型),增加了训练复杂度和显存占用;需要同时维护四个模型(Policy Model、Reference Model、Value Model、Reward Model),工程实现复杂;训练不稳定,容易出现奖励崩塌或策略退化。
+
+GRPO(Group Relative Policy Optimization)<sup>[2]</sup>是一种简化的PPO变体,专门为LLM设计。GRPO的核心思想是:不需要Value Model,使用组内相对奖励代替绝对奖励;简化训练流程,只需要Policy Model和Reference Model;提高训练稳定性,减少奖励崩塌的风险。
+
+让我们通过数学公式来理解GRPO的原理。PPO的目标函数为:
+
+$$
+\mathcal{L}_{\text{PPO}}(\theta) = \mathbb{E}_{s,a \sim \pi_\theta} \left[ \min\left( \frac{\pi_\theta(a|s)}{\pi_{\text{old}}(a|s)} A(s,a), \text{clip}\left(\frac{\pi_\theta(a|s)}{\pi_{\text{old}}(a|s)}, 1-\epsilon, 1+\epsilon\right) A(s,a) \right) \right]
+$$
+
+其中 $A(s,a)$ 是优势函数(Advantage),需要Value Model来估计:
+
+$$
+A(s,a) = Q(s,a) - V(s) = r(s,a) + \gamma V(s') - V(s)
+$$
+
+GRPO的目标函数简化为:
+
+$$
+\mathcal{L}_{\text{GRPO}}(\theta) = \mathbb{E}_{s,a \sim \pi_\theta} \left[ \frac{\pi_\theta(a|s)}{\pi_{\text{ref}}(a|s)} \cdot (r(s,a) - \bar{r}_{\text{group}}) \right] - \beta \cdot D_{KL}(\pi_\theta || \pi_{\text{ref}})
+$$
+
+其中 $\bar{r}_{\text{group}}$ 是组内平均奖励,$\beta$ 是KL散度惩罚系数。关键区别在于:GRPO使用 $r(s,a) - \bar{r}_{\text{group}}$ 代替优势函数 $A(s,a)$,不需要Value Model;GRPO使用组内相对奖励,减少奖励方差;GRPO添加KL散度惩罚,防止策略偏离太远。
+
+如图11.7所示,PPO和GRPO的训练流程对比。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-7.png" alt="" width="85%"/>
+  <p>图 11.7 PPO vs GRPO训练流程</p>
+</div>
+
+可以看到,GRPO省去了Value Model的训练,大大简化了流程。
+
+如表11.6所示,PPO和GRPO的详细对比。
+
+<div align="center">
+  <p>表 11.6 PPO vs GRPO对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-6.png" alt="" width="85%"/>
+</div>
+
+
+
+对于LLM训练,GRPO是更好的选择,因为它更简单、更稳定、显存占用更低。
+
+### 11.4.2 GRPO训练实战
+
+现在让我们使用HelloAgents进行GRPO训练。GRPO训练的前提是已经完成SFT训练,因为GRPO需要一个合理的初始策略。
+
+基础GRPO训练示例:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+# 创建训练工具
+rl_tool = RLTrainingTool()
+
+# GRPO训练
+result = rl_tool.run({
+    # 训练配置
+    "action": "train",
+    "algorithm": "grpo",
+    
+    # 模型配置
+    "model_name": "./models/sft_full",  # 从SFT模型开始
+    "output_dir": "./models/grpo_model",
+    
+    # 数据配置
+    "max_samples": 100,     # 使用100个样本快速测试
+    
+    # 训练参数
+    "num_epochs": 3,
+    "batch_size": 4,
+    "learning_rate": 1e-5,  # GRPO学习率通常比SFT小
+    
+    # GRPO特定参数
+    "num_generations": 4,   # 每个问题生成4个答案
+    "kl_coef": 0.05,        # KL散度惩罚系数
+    
+    # LoRA配置
+    "use_lora": True,
+    "lora_rank": 16,
+    "lora_alpha": 32,
+    
+    # 奖励函数配置
+    "reward_type": "accuracy",  # 使用准确率奖励
+})
+
+print(f"\n✓ 训练完成!")
+print(f"  - 模型保存路径: {result['model_path']}")
+print(f"  - 训练样本数: {result['num_samples']}")
+print(f"  - 训练轮数: {result['num_epochs']}")
+print(f"  - 平均奖励: {result['average_reward']:.4f}")
+```
+
+如果GRPO训练过程中平均奖励逐渐提升,KL散度保持在合理范围内,说明训练正常进行。
+
+GRPO有一些特定的参数需要理解和调优。
+
+**生成参数**:
+
+- `num_generations`: 每个问题生成多少个答案。越多越好,但计算成本也越高。典型值为4-8。生成多个答案的目的是计算组内相对奖励,增加训练信号的多样性。
+- `max_new_tokens`: 每个答案最多生成多少个token。太少可能截断答案,太多浪费计算。建议256-512。
+- `temperature`: 生成温度,控制随机性。0表示贪婪解码,1表示标准采样。GRPO建议0.7-1.0,保持一定的探索性。
+
+**优化参数**:
+
+- `learning_rate`: GRPO的学习率通常比SFT小,因为我们不想偏离SFT模型太远。建议1e-5到5e-5。
+- `kl_coef`: KL散度惩罚系数,控制策略更新的幅度。太小(0.01)可能导致策略偏离太远,太大(0.5)可能限制学习。建议0.05-0.1。
+- `clip_range`: 策略比率裁剪范围,类似PPO的epsilon。建议0.2。
+
+**奖励参数**:
+
+- `reward_type`: 奖励函数类型,可以是"accuracy"、"length_penalty"、"step"或"combined"。
+- `reward_config`: 奖励函数的额外配置,如长度惩罚的目标长度、步骤奖励的系数等。
+
+让我们进行一次完整的GRPO训练,使用全部数据和最佳实践:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 完整GRPO训练
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+
+    # 模型配置
+    "model_name": "./models/sft_full",
+    "output_dir": "./models/grpo_full",
+    
+    # 数据配置
+    "max_samples": None,    # 使用全部数据
+    
+    # 训练参数
+    "num_epochs": 3,
+    "batch_size": 4,
+    "learning_rate": 1e-5,
+    "warmup_ratio": 0.1,
+    
+    # GRPO特定参数
+    "num_generations": 4,
+    "max_new_tokens": 512,
+    "temperature": 0.8,
+    "kl_coef": 0.05,
+    "clip_range": 0.2,
+    
+    # LoRA配置
+    "use_lora": True,
+    "lora_rank": 16,
+    "lora_alpha": 32,
+    
+    # 奖励函数配置
+    "reward_type": "combined",
+    "reward_config": {
+        "components": [
+            {"type": "accuracy", "weight": 1.0},
+            {"type": "length_penalty", "weight": 0.5, "target_length": 200},
+            {"type": "step", "weight": 0.3, "step_bonus": 0.1}
+        ]
+    },
+    
+    # 其他配置
+    "save_steps": 500,
+    "logging_steps": 100,
+})
+
+print(f"训练完成! 模型保存在: {result['model_path']}")
+```
+
+### 11.4.3 GRPO训练过程解析
+
+让我们深入理解GRPO的训练过程,看看每一步都发生了什么。
+
+**(1)训练循环**
+
+GRPO的训练循环包括以下步骤:
+
+1. **采样阶段**:对于每个问题,使用当前策略生成多个答案(`num_generations`个)。这些答案构成一个"组",用于计算相对奖励。
+
+2. **奖励计算**:对每个生成的答案计算奖励 $r_i$。奖励可以是准确率、长度惩罚、步骤奖励或它们的组合。
+
+3. **相对奖励**:计算组内平均奖励 $\bar{r} = \frac{1}{N}\sum_{i=1}^{N} r_i$,然后计算相对奖励 $\hat{r}_i = r_i - \bar{r}$。这样做的好处是减少奖励方差,使训练更稳定。
+
+4. **策略更新**:使用相对奖励更新策略,同时添加KL散度惩罚,防止策略偏离参考模型太远。
+
+5. **重复**:重复上述步骤,直到完成所有训练轮次。
+
+让我们通过一个具体例子来理解:
+
+```python
+# 假设我们有一个问题
+question = "What is 48 + 24?"
+
+# 生成4个答案
+answers = [
+    "48 + 24 = 72. Final Answer: 72",      # 正确
+    "48 + 24 = 72. Final Answer: 72",      # 正确
+    "48 + 24 = 70. Final Answer: 70",      # 错误
+    "Let me think... 72. Final Answer: 72" # 正确但冗长
+]
+
+# 计算奖励(假设使用准确率 + 长度惩罚)
+rewards = [1.0, 1.0, 0.0, 0.8]  # 第4个答案因为冗长被惩罚
+
+# 计算组内平均奖励
+avg_reward = (1.0 + 1.0 + 0.0 + 0.8) / 4 = 0.7
+
+# 计算相对奖励
+relative_rewards = [
+    1.0 - 0.7 = 0.3,   # 正确且简洁,相对奖励为正
+    1.0 - 0.7 = 0.3,   # 正确且简洁,相对奖励为正
+    0.0 - 0.7 = -0.7,  # 错误,相对奖励为负
+    0.8 - 0.7 = 0.1    # 正确但冗长,相对奖励较小
+]
+
+# 策略更新:增加前两个答案的概率,减少第三个答案的概率
+```
+
+可以看到,相对奖励机制鼓励模型生成"比平均水平更好"的答案,而不是简单地追求高奖励。这样可以减少奖励方差,提高训练稳定性。
+
+**(2)KL散度惩罚**
+
+KL散度惩罚是GRPO的关键组成部分,它防止策略偏离参考模型太远。KL散度定义为:
+
+$$
+D_{KL}(\pi_\theta || \pi_{\text{ref}}) = \mathbb{E}_{s,a \sim \pi_\theta} \left[ \log \frac{\pi_\theta(a|s)}{\pi_{\text{ref}}(a|s)} \right]
+$$
+
+在实践中,我们计算每个token的KL散度,然后求和:
+
+$$
+D_{KL} = \sum_{t=1}^{T} \log \frac{\pi_\theta(a_t|s, a_{<t})}{\pi_{\text{ref}}(a_t|s, a_{<t})}
+$$
+
+KL散度越大,说明当前策略与参考模型差异越大。通过添加KL散度惩罚项 $-\beta \cdot D_{KL}$,我们限制策略更新的幅度,避免"遗忘"SFT阶段学到的知识。
+
+`kl_coef` ($\beta$) 的选择很重要:
+
+- 太小(0.01):策略可能偏离太远,导致输出格式混乱或质量下降
+- 太大(0.5):策略更新受限,学习缓慢,难以超越SFT模型
+- 建议(0.05-0.1):平衡探索和稳定性
+
+**(3)训练监控**
+
+在GRPO训练过程中,我们需要监控以下指标:
+
+- **平均奖励(Average Reward)**:应该逐渐上升。如果奖励不上升,可能是学习率太小、KL惩罚太大、奖励函数设计不合理。如果奖励先升后降,可能是过拟合或奖励崩塌。
+
+- **KL散度(KL Divergence)**:应该保持在合理范围内(0.01-0.1)。如果KL散度过大(>0.5),说明策略偏离太远,需要增大kl_coef或降低学习率。如果KL散度过小(<0.001),说明策略几乎没有更新,需要减小kl_coef或增大学习率。
+
+- **准确率(Accuracy)**:应该逐渐提升。这是最直观的指标,反映模型的实际能力。
+
+- **生成质量(Generation Quality)**:需要人工检查生成的答案,确保格式正确、推理清晰。
+
+HelloAgents集成了两种主流的训练监控工具:Weights & Biases(wandb)和TensorBoard。
+
+**方式1:使用Weights & Biases(推荐)**
+
+Weights & Biases是目前最流行的机器学习实验跟踪平台,提供了强大的可视化和实验管理功能。
+
+```python
+import os
+
+# 1. 设置wandb(需要先注册账号: https://wandb.ai)
+os.environ["WANDB_PROJECT"] = "hello-agents-grpo"  # 项目名称
+os.environ["WANDB_LOG_MODEL"] = "false"            # 不上传模型文件
+
+# 2. 在训练配置中启用wandb
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/grpo_monitored",
+    "num_epochs": 2,
+    "batch_size": 2,
+    "use_lora": True,
+    # wandb会自动记录所有训练指标
+})
+
+# 训练完成后,访问 https://wandb.ai 查看训练曲线
+```
+
+wandb会自动记录以下指标:
+- `train/reward`: 平均奖励
+- `train/kl`: KL散度
+- `train/loss`: 训练损失
+- `train/learning_rate`: 学习率
+- `train/epoch`: 训练轮数
+
+**方式2:使用TensorBoard**
+
+TensorBoard是TensorFlow提供的可视化工具,也支持PyTorch训练。
+
+```python
+# 1. 训练时会自动在output_dir下创建tensorboard日志
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/grpo_tb",
+    "num_epochs": 2,
+    "batch_size": 2,
+    "use_lora": True,
+})
+
+# 2. 启动TensorBoard查看训练曲线
+# 在命令行运行:
+# tensorboard --logdir=./models/grpo_tb
+# 然后访问 http://localhost:6006
+```
+
+**方式3:离线监控(无需外部工具)**
+
+如果不想使用wandb或TensorBoard,也可以通过训练日志进行监控:
+
+```python
+# 训练过程会打印详细日志
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/grpo_simple",
+    "num_epochs": 2,
+    "batch_size": 2,
+    "use_lora": True,
+})
+
+# 日志示例:
+# Epoch 1/2 | Step 100/500 | Reward: 0.45 | KL: 0.023 | Loss: 1.234
+# Epoch 1/2 | Step 200/500 | Reward: 0.52 | KL: 0.031 | Loss: 1.156
+# ...
+```
+
+在GRPO训练中,可能会遇到一些问题。当奖励不上升时,可能是学习率太小或KL惩罚太大限制了策略更新,也可能是奖励函数设计不合理或SFT模型质量太差,此时可以增大学习率(从1e-5到5e-5)、减小kl_coef(从0.1到0.05)、检查奖励函数或重新训练SFT模型。
+
+当KL散度爆炸(超过0.5甚至1.0)导致生成答案格式混乱时,通常是学习率太大或KL惩罚太小,或者奖励函数过于激进,可以降低学习率(从5e-5到1e-5)、增大kl_coef(从0.05到0.1)、调整奖励函数或使用梯度裁剪。
+
+当生成质量下降(准确率提升但格式混乱、推理不清晰)时,可能是奖励函数只关注准确率忽略了其他质量指标,或KL惩罚太小导致模型偏离SFT太远,或出现过拟合,此时应使用组合奖励函数同时优化多个指标、增大kl_coef保持一致性、减少训练轮数或增加训练数据。
+
+GRPO训练的显存占用比SFT高,因为需要同时生成多个答案并存储参考模型输出,容易出现OOM。可以通过减小num_generations(从8到4)、batch_size(从4到2)或max_new_tokens(从512到256),或使用梯度检查点和混合精度训练来缓解。
+
+## 11.5 模型评估与分析
+
+训练完成后,我们需要全面评估模型的性能,不仅要看准确率这一个指标,还要深入分析模型的推理质量、错误模式、泛化能力等。本节将介绍如何系统地评估和分析Agentic RL模型。
+
+### 11.5.1 评估指标体系
+
+一个好的评估体系应该是多维度的,从不同角度衡量模型的能力。我们将评估指标分为三类:准确性指标、效率指标、质量指标。
+
+**(1)准确性指标**
+
+准确性指标衡量模型是否能够得出正确答案。
+
+**准确率(Accuracy)**:最基本的指标,答案完全正确的比例。计算公式为:
+$$
+\text{Accuracy} = \frac{\text{正确答案数}}{\text{总问题数}}
+$$
+
+优点是简单直观,易于理解和比较。缺点是无法区分"接近正确"和"完全错误",对于复杂任务可能过于粗糙。
+
+**Top-K准确率**:生成K个答案,只要有一个正确就算对。计算公式为:
+$$
+\text{Accuracy@K} = \frac{\text{至少有一个正确答案的问题数}}{\text{总问题数}}
+$$
+
+这个指标反映了模型的"潜力",即通过多次采样能否找到正确答案。
+
+**数值误差(Numerical Error)**:对于数学问题,可以计算预测值与真实值的误差。计算公式为:
+
+$$
+\text{Error} = \frac{1}{N} \sum_{i=1}^{N} |y_i - \hat{y}_i|
+$$
+
+这个指标可以区分"接近正确"(如预测72.5,真实72)和"完全错误"(如预测100,真实72)。
+
+**(2)效率指标**
+
+效率指标衡量模型生成答案的成本。
+
+**平均长度(Average Length)**:生成答案的平均token数。计算公式为:
+
+$$
+\text{Avg Length} = \frac{1}{N} \sum_{i=1}^{N} |y_i|
+$$
+
+更短的答案意味着更低的推理成本和更快的响应速度。
+
+**推理步骤数(Reasoning Steps)**:答案中包含的推理步骤数量。计算公式为:
+
+$$
+\text{Avg Steps} = \frac{1}{N} \sum_{i=1}^{N} s_i
+$$
+
+适当的步骤数(2-5步)说明模型能够系统地分解问题,过多的步骤可能说明推理冗余。
+
+**推理时间(Inference Time)**:生成一个答案所需的时间。这个指标在实际部署中很重要,影响用户体验。
+
+**(3)质量指标**
+
+质量指标衡量答案的可读性和可解释性。
+
+**格式正确率(Format Correctness)**:答案是否符合预期格式(如包含"Step 1", "Final Answer"等标记)。计算公式为:
+$$
+\text{Format Correctness} = \frac{\text{格式正确的答案数}}{\text{总答案数}}
+$$
+
+格式正确是基本要求,格式混乱的答案即使结果正确也难以使用。
+
+**推理连贯性(Reasoning Coherence)**:推理步骤之间是否逻辑连贯。这个指标通常需要人工评估或使用专门的评估模型。
+
+**可解释性(Explainability)**:答案是否容易理解和验证。包含清晰步骤的答案比直接给出结果的答案更具可解释性。
+
+如表11.7所示,不同指标的对比。
+
+<div align="center">
+  <p>表 11.7 评估指标对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-7.png" alt="" width="85%"/>
+</div>
+
+
+### 11.5.2 评估实战
+
+HelloAgents提供了全面的评估功能,可以一次性计算多个指标。
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 全面评估
+print("=" * 50)
+print("全面评估GRPO模型")
+print("=" * 50)
+
+result = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "./models/grpo_full",
+    "max_samples": 200,
+    "use_lora": True,
+    
+    # 评估配置
+    "metrics": [
+        "accuracy",           # 准确率
+        "accuracy_at_k",      # Top-K准确率
+        "average_length",     # 平均长度
+        "average_steps",      # 平均步骤数
+        "format_correctness", # 格式正确率
+    ],
+    "k": 3,  # Top-3准确率
+})
+
+# 解析结果
+eval_data = json.loads(result)
+
+# 打印结果
+print(f"\n评估结果:")
+print(f"  准确率: {eval_data['accuracy']}")
+print(f"  平均奖励: {eval_data['average_reward']}")
+print(f"  测试样本数: {eval_data['num_samples']}")
+```
+
+我们可以对比预训练模型、SFT模型、GRPO模型的性能:
+
+```python
+# 评估三个模型
+models = [
+    ("预训练模型", "Qwen/Qwen3-0.6B", False),
+    ("SFT模型", "./models/sft_full", True),
+    ("GRPO模型", "./models/grpo_full", True),
+]
+
+results = []
+for name, path, use_lora in models:
+    print(f"\n评估{name}...")
+    result = rl_tool.run({
+        "action": "evaluate",
+        "model_path": path,
+        "max_samples": 200,
+        "use_lora": use_lora,
+        "metrics": ["accuracy", "average_length", "format_correctness"],
+    })
+    results.append((name, result))
+
+# 打印对比表格
+print("\n" + "=" * 70)
+print(f"{'模型':<15} {'准确率':<12} {'平均长度':<15} {'格式正确率':<12}")
+print("=" * 70)
+for name, result in results:
+    print(f"{name:<15} {result['accuracy']:<12.2%} {result['average_length']:<15.1f} {result['format_correctness']:<12.2%}")
+print("=" * 70)
+```
+
+### 11.5.3 错误分析
+
+仅仅知道准确率是不够的,我们需要深入分析模型在哪些类型的问题上容易出错,从而指导后续改进。模型的错误可以分为四类:计算错误(推理步骤正确但计算出错,如"48/2=25",说明数值计算能力不足)、推理错误(推理逻辑错误导致解题思路不对,如先加后除而非先除后加,说明逻辑推理能力不足)、理解错误(没有正确理解问题,如问题问"总共"但只计算了一部分,说明语言理解能力不足)、格式错误(答案正确但格式不符合要求,如缺少"Final Answer:"标记,说明格式学习不足)。
+
+错误分析示例:
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 评估并收集错误样本
+result = rl_tool.run({
+    "action": "evaluate",
+    "model_path": "./models/grpo_full",
+    "max_samples": 200,
+    "use_lora": True,
+    "return_details": True,  # 返回详细结果
+})
+
+# 分析错误样本
+errors = result['errors']  # 错误样本列表
+print(f"总错误数: {len(errors)}")
+
+# 按错误类型分类
+error_types = {
+    "计算错误": 0,
+    "推理错误": 0,
+    "理解错误": 0,
+    "格式错误": 0,
+}
+
+for error in errors:
+    question = error['question']
+    prediction = error['prediction']
+    ground_truth = error['ground_truth']
+    
+    # 简单的错误分类逻辑(实际应用中可能需要更复杂的分析)
+    if "Final Answer:" not in prediction:
+        error_types["格式错误"] += 1
+    elif "Step" in prediction:
+        # 有推理步骤,可能是计算或推理错误
+        # 这里需要更细致的分析
+        error_types["计算错误"] += 1
+    else:
+        error_types["理解错误"] += 1
+
+# 打印错误分布
+print("\n错误类型分布:")
+for error_type, count in error_types.items():
+    percentage = count / len(errors) * 100
+    print(f"  {error_type}: {count} ({percentage:.1f}%)")
+```
+
+输出示例:
+
+```bash
+总错误数: 76
+
+错误类型分布:
+  计算错误: 32 (42.1%)
+  推理错误: 18 (23.7%)
+  理解错误: 22 (28.9%)
+  格式错误: 4 (5.3%)
+```
+
+可以看到,计算错误是最主要的错误类型(42.1%),说明模型的数值计算能力需要加强。格式错误很少(5.3%),说明SFT训练效果良好。我们还可以分析模型在不同难度的问题上的表现:
+
+```python
+# 按推理步骤数分组
+step_groups = {
+    "简单(1-2步)": [],
+    "中等(3-4步)": [],
+    "困难(5+步)": [],
+}
+
+for sample in result['details']:
+    steps = sample['ground_truth_steps']  # 真实答案的步骤数
+    correct = sample['correct']
+    
+    if steps <= 2:
+        step_groups["简单(1-2步)"].append(correct)
+    elif steps <= 4:
+        step_groups["中等(3-4步)"].append(correct)
+    else:
+        step_groups["困难(5+步)"].append(correct)
+
+# 计算每组的准确率
+print("\n不同难度的准确率:")
+for group_name, results in step_groups.items():
+    if len(results) > 0:
+        accuracy = sum(results) / len(results)
+        print(f"  {group_name}: {accuracy:.2%} ({len(results)}个样本)")
+```
+
+输出示例:
+
+```bash
+不同难度的准确率:
+  简单(1-2步): 78.50% (85个样本)
+  中等(3-4步): 58.30% (96个样本)
+  困难(5+步): 31.60% (19个样本)
+```
+
+可以看到,模型在简单问题上表现良好(78.5%),但在困难问题上表现较差(31.6%)。这说明模型的多步推理能力还有待提升
+
+### 11.5.4 改进方向
+
+基于评估和分析结果,我们可以确定模型的改进方向,如图11.8所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-8.png" alt="" width="85%"/>
+  <p>图 11.8 模型改进迭代流程</p>
+</div>
+
+这是一个持续迭代的过程:训练模型 → 评估性能 → 分析错误 → 确定问题 → 选择改进方向 → 重新训练。通过多次迭代,模型性能会不断提升。
+
+## 11.6 完整训练流程实战
+
+在前面的章节中,我们分别学习了数据准备、SFT训练、GRPO训练和模型评估。现在,让我们把这些知识整合起来,完成一个端到端的Agentic RL训练流程。
+
+### 11.6.1 端到端训练流程
+
+一个完整的Agentic RL训练流程包括以下阶段:数据准备、SFT训练、SFT评估、GRPO训练、GRPO评估、模型部署。如图11.9所示。
+
+<div align="center">
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-9.png" alt="" width="85%"/>
+  <p>图 11.9 端到端训练流程</p>
+</div>
+
+让我们通过一个完整的脚本来实现这个流程:
+
+```python
+"""
+完整的Agentic RL训练流程
+从数据准备到模型部署的端到端示例
+"""
+
+from hello_agents.tools import RLTrainingTool
+import json
+from datetime import datetime
+
+class AgenticRLPipeline:
+    """Agentic RL训练流水线"""
+    
+    def __init__(self, config_path="config.json"):
+        """
+        初始化训练流水线
+        
+        Args:
+            config_path: 配置文件路径
+        """
+        self.rl_tool = RLTrainingTool()
+        self.config = self.load_config(config_path)
+        self.results = {}
+        
+    def load_config(self, config_path):
+        """加载配置文件"""
+        with open(config_path, 'r') as f:
+            return json.load(f)
+    
+    def log(self, message):
+        """记录日志"""
+        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+        print(f"[{timestamp}] {message}")
+    
+    def stage1_prepare_data(self):
+        """阶段1: 数据准备"""
+        self.log("=" * 50)
+        self.log("阶段1: 数据准备")
+        self.log("=" * 50)
+
+        # 加载并检查数据集
+        result = self.rl_tool.run({
+            "action": "load_dataset",
+            "format": "sft",
+            "max_samples": self.config["data"]["max_samples"],
+        })
+
+        # 解析JSON结果
+        dataset_info = json.loads(result)
+
+        self.log(f"✓ 数据集加载完成")
+        self.log(f"  - 样本数: {dataset_info['dataset_size']}")
+        self.log(f"  - 格式: {dataset_info['format']}")
+        self.log(f"  - 数据列: {', '.join(dataset_info['sample_keys'])}")
+
+        self.results["data"] = dataset_info
+
+        return dataset_info
+    
+    def stage2_sft_training(self):
+        """阶段2: SFT训练"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段2: SFT训练")
+        self.log("=" * 50)
+
+        sft_config = self.config["sft"]
+
+        result = self.rl_tool.run({
+            "action": "train",
+            "algorithm": "sft",
+            "model_name": self.config["model"]["base_model"],
+            "output_dir": sft_config["output_dir"],
+            "max_samples": self.config["data"]["max_samples"],
+            "num_epochs": sft_config["num_epochs"],
+            "batch_size": sft_config["batch_size"],
+            "use_lora": True,
+            # 训练监控配置
+            "use_wandb": self.config.get("monitoring", {}).get("use_wandb", False),
+            "use_tensorboard": self.config.get("monitoring", {}).get("use_tensorboard", True),
+            "wandb_project": self.config.get("monitoring", {}).get("wandb_project", None),
+        })
+
+        # 解析JSON结果
+        result_data = json.loads(result)
+
+        self.log(f"✓ SFT训练完成")
+        self.log(f"  - 模型路径: {result_data['output_dir']}")
+        self.log(f"  - 状态: {result_data['status']}")
+
+        self.results["sft_training"] = result_data
+
+        return result_data["output_dir"]
+    
+    def stage3_sft_evaluation(self, model_path):
+        """阶段3: SFT评估"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段3: SFT评估")
+        self.log("=" * 50)
+        
+        result = self.rl_tool.run({
+            "action": "evaluate",
+            "model_path": model_path,
+            "max_samples": self.config["eval"]["max_samples"],
+            "use_lora": True,
+        })
+        eval_data = json.loads(result)
+
+        self.log(f"✓ SFT评估完成")
+        self.log(f"  - 准确率: {eval_data['accuracy']}")
+        self.log(f"  - 平均奖励: {eval_data['average_reward']}")
+
+        self.results["sft_evaluation"] = eval_data
+
+        return eval_data
+    
+    def stage4_grpo_training(self, sft_model_path):
+        """阶段4: GRPO训练"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段4: GRPO训练")
+        self.log("=" * 50)
+
+        grpo_config = self.config["grpo"]
+
+        result = self.rl_tool.run({
+            "action": "train",
+            "algorithm": "grpo",
+            "model_name": sft_model_path,
+            "output_dir": grpo_config["output_dir"],
+            "max_samples": self.config["data"]["max_samples"],
+            "num_epochs": grpo_config["num_epochs"],
+            "batch_size": grpo_config["batch_size"],
+            "use_lora": True,
+            # 训练监控配置
+            "use_wandb": self.config.get("monitoring", {}).get("use_wandb", False),
+            "use_tensorboard": self.config.get("monitoring", {}).get("use_tensorboard", True),
+            "wandb_project": self.config.get("monitoring", {}).get("wandb_project", None),
+        })
+
+        # 解析JSON结果
+        result_data = json.loads(result)
+
+        self.log(f"✓ GRPO训练完成")
+        self.log(f"  - 模型路径: {result_data['output_dir']}")
+        self.log(f"  - 状态: {result_data['status']}")
+
+        self.results["grpo_training"] = result_data
+
+        return result_data["output_dir"]
+    
+    def stage5_grpo_evaluation(self, model_path):
+        """阶段5: GRPO评估"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段5: GRPO评估")
+        self.log("=" * 50)
+        
+        result = self.rl_tool.run({
+            "action": "evaluate",
+            "model_path": model_path,
+            "max_samples": self.config["eval"]["max_samples"],
+            "use_lora": True,
+        })
+        eval_data = json.loads(result)
+
+        self.log(f"✓ GRPO评估完成")
+        self.log(f"  - 准确率: {eval_data['accuracy']}")
+        self.log(f"  - 平均奖励: {eval_data['average_reward']}")
+
+        self.results["grpo_evaluation"] = eval_data
+
+        return eval_data
+    
+    def stage6_save_results(self):
+        """阶段6: 保存结果"""
+        self.log("\n" + "=" * 50)
+        self.log("阶段6: 保存结果")
+        self.log("=" * 50)
+        
+        # 保存训练结果
+        results_path = "training_results.json"
+        with open(results_path, 'w') as f:
+            json.dump(self.results, f, indent=2)
+        
+        self.log(f"✓ 结果已保存到: {results_path}")
+    
+    def run(self):
+        """运行完整流程"""
+        try:
+            # 阶段1: 数据准备
+            self.stage1_prepare_data()
+            
+            # 阶段2: SFT训练
+            sft_model_path = self.stage2_sft_training()
+            
+            # 阶段3: SFT评估
+            self.stage3_sft_evaluation(sft_model_path)
+            
+            # 阶段4: GRPO训练
+            grpo_model_path = self.stage4_grpo_training(sft_model_path)
+            
+            # 阶段5: GRPO评估
+            self.stage5_grpo_evaluation(grpo_model_path)
+            
+            # 阶段6: 保存结果
+            self.stage6_save_results()
+            
+            self.log("\n" + "=" * 50)
+            self.log("✓ 训练流程完成!")
+            self.log("=" * 50)
+            
+        except Exception as e:
+            self.log(f"\n✗ 训练失败: {str(e)}")
+            raise
+
+# 使用示例
+if __name__ == "__main__":
+    # 创建配置文件
+    config = {
+        "model": {
+            "base_model": "Qwen/Qwen3-0.6B"
+        },
+        "data": {
+            "max_samples": 1000  # 使用1000个样本
+        },
+        "sft": {
+            "output_dir": "./models/sft_model",
+            "num_epochs": 3,
+            "batch_size": 8,
+        },
+        "grpo": {
+            "output_dir": "./models/grpo_model",
+            "num_epochs": 3,
+            "batch_size": 4,
+        },
+        "eval": {
+            "max_samples": 200,
+            "sft_accuracy_threshold": 0.40  # SFT准确率阈值
+        },
+        "monitoring": {
+            "use_wandb": False,  # 是否使用Wandb
+            "use_tensorboard": True,  # 是否使用TensorBoard
+            "wandb_project": "agentic-rl-pipeline"  # Wandb项目名
+        }
+    }
+    
+    # 保存配置
+    with open("config.json", 'w') as f:
+        json.dump(config, f, indent=2)
+    
+    # 运行训练流程
+    pipeline = AgenticRLPipeline("config.json")
+    pipeline.run()
+```
+
+运行这个脚本,你将看到完整的训练过程。
+
+运行小建议:
+
+**从小规模开始**:不要一开始就用全部数据训练。先用100-1000个样本快速迭代,验证流程和参数,确认效果后再扩大规模。这样可以节省大量时间和计算资源。
+
+**数据质量检查**:在训练前检查数据质量,确保格式正确、答案准确、没有重复样本。可以使用以下代码:
+
+```python
+def check_data_quality(dataset):
+    """检查数据质量"""
+    issues = []
+
+    # 检查必需字段
+    required_fields = ["prompt", "completion"]
+    for field in required_fields:
+        if field not in dataset.column_names:
+            issues.append(f"缺少字段: {field}")
+
+    # 检查空值
+    for i, sample in enumerate(dataset):
+        if not sample["prompt"] or not sample["completion"]:
+            issues.append(f"样本{i}包含空值")
+
+    # 检查重复
+    prompts = [s["prompt"] for s in dataset]
+    duplicates = len(prompts) - len(set(prompts))
+    if duplicates > 0:
+        issues.append(f"发现{duplicates}个重复样本")
+
+    return issues
+
+# 使用
+issues = check_data_quality(dataset)
+if issues:
+    print("数据质量问题:")
+    for issue in issues:
+        print(f"  - {issue}")
+else:
+    print("✓ 数据质量检查通过")
+```
+
+**数据增强**:如果数据量不足,可以考虑数据增强,如改写问题(保持答案不变)、生成相似问题、反向翻译(translate back)。但要注意保持数据质量,避免引入噪声。
+
+### 11.6.2 超参数调优
+
+超参数调优是提升模型性能的关键。下面是一些常用的调优策略。
+
+**(1)网格搜索**
+
+网格搜索(Grid Search)是最简单的调优方法,遍历所有参数组合,选择最佳的一组。
+
+```python
+# 定义参数网格
+param_grid = {
+    "learning_rate": [1e-5, 5e-5, 1e-4],
+    "lora_rank": [8, 16, 32],
+    "kl_coef": [0.05, 0.1, 0.2],
+}
+
+best_accuracy = 0
+best_params = None
+
+# 遍历所有组合
+for lr in param_grid["learning_rate"]:
+    for rank in param_grid["lora_rank"]:
+        for kl in param_grid["kl_coef"]:
+            print(f"测试参数: lr={lr}, rank={rank}, kl={kl}")
+
+            # 训练模型
+            result = rl_tool.run({
+                "action": "train",
+                "algorithm": "grpo",
+                "learning_rate": lr,
+                "lora_rank": rank,
+                "kl_coef": kl,
+                # 其他参数...
+            })
+
+            # 评估模型
+            eval_result = rl_tool.run({
+                "action": "evaluate",
+                "model_path": result["model_path"],
+            })
+
+            # 更新最佳参数
+            if eval_result["accuracy"] > best_accuracy:
+                best_accuracy = eval_result["accuracy"]
+                best_params = {"lr": lr, "rank": rank, "kl": kl}
+
+print(f"最佳参数: {best_params}")
+print(f"最佳准确率: {best_accuracy:.2%}")
+```
+
+网格搜索的优点是简单直接,能找到全局最优。缺点是计算成本高,参数多时不可行。
+
+**(2)随机搜索**
+
+随机搜索(Random Search)随机采样参数组合,比网格搜索更高效。
+
+```python
+import random
+
+# 定义参数范围
+param_ranges = {
+    "learning_rate": (1e-6, 1e-4),  # 对数均匀分布
+    "lora_rank": [4, 8, 16, 32, 64],
+    "kl_coef": (0.01, 0.5),
+}
+
+best_accuracy = 0
+best_params = None
+
+# 随机采样N次
+N = 10
+for i in range(N):
+    # 随机采样参数
+    lr = 10 ** random.uniform(-6, -4)  # 对数均匀
+    rank = random.choice(param_ranges["lora_rank"])
+    kl = random.uniform(0.01, 0.5)
+
+    print(f"[{i+1}/{N}] 测试参数: lr={lr:.2e}, rank={rank}, kl={kl:.3f}")
+
+    # 训练和评估(同上)
+    # ...
+
+print(f"最佳参数: {best_params}")
+print(f"最佳准确率: {best_accuracy:.2%}")
+```
+
+随机搜索的优点是效率高,适合参数空间大的情况。缺点是可能错过最优解。
+
+**(3)贝叶斯优化**
+
+贝叶斯优化(Bayesian Optimization)使用概率模型指导搜索,更加智能。可以使用Optuna等库:
+
+```python
+import optuna
+
+def objective(trial):
+    """优化目标函数"""
+    # 采样参数
+    lr = trial.suggest_loguniform("learning_rate", 1e-6, 1e-4)
+    rank = trial.suggest_categorical("lora_rank", [8, 16, 32])
+    kl = trial.suggest_uniform("kl_coef", 0.01, 0.5)
+
+    # 训练模型
+    result = rl_tool.run({
+        "action": "train",
+        "algorithm": "grpo",
+        "learning_rate": lr,
+        "lora_rank": rank,
+        "kl_coef": kl,
+        # 其他参数...
+    })
+
+    # 评估模型
+    eval_result = rl_tool.run({
+        "action": "evaluate",
+        "model_path": result["model_path"],
+    })
+
+    return eval_result["accuracy"]
+
+# 创建研究
+study = optuna.create_study(direction="maximize")
+study.optimize(objective, n_trials=20)
+
+# 打印最佳参数
+print(f"最佳参数: {study.best_params}")
+print(f"最佳准确率: {study.best_value:.2%}")
+```
+
+贝叶斯优化的优点是样本效率高,能快速找到好的参数。缺点是实现复杂,需要额外的库。
+
+如表11.8所示,不同调优方法的对比。
+
+<div align="center">
+  <p>表 11.8 超参数调优方法对比</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-8.png" alt="" width="85%"/>
+</div>
+### 11.6.3 分布式训练
+
+当数据量和模型规模增大时,单GPU训练会变得非常缓慢。这时我们需要使用分布式训练来加速训练过程。HelloAgents基于TRL和Hugging Face Accelerate,天然支持多GPU和多节点分布式训练
+
+**方案选择建议**:
+
+- **单机多卡(2-8卡)**: 使用DDP,简单高效
+- **大模型(>7B)**: 使用DeepSpeed ZeRO-2或ZeRO-3
+- **多节点集群**: 使用DeepSpeed ZeRO-3 + Offload
+
+**(1)配置Accelerate**
+
+首先需要创建Accelerate配置文件。运行以下命令:
+
+```bash
+accelerate config
+```
+
+根据提示选择配置:
+
+```
+In which compute environment are you running?
+> This machine
+
+Which type of machine are you using?
+> multi-GPU
+
+How many different machines will you use?
+> 1
+
+Do you wish to optimize your script with torch dynamo?
+> NO
+
+Do you want to use DeepSpeed?
+> YES
+
+Which DeepSpeed config file do you want to use?
+> ZeRO-2
+
+How many GPU(s) should be used for distributed training?
+> 4
+```
+
+这会在`~/.cache/huggingface/accelerate/default_config.yaml`生成配置文件。
+
+**(2)使用DDP训练**
+
+**数据并行(DDP)**是最简单的分布式方案,每个GPU持有完整模型副本,数据被分割到各个GPU上。
+
+**Accelerate配置文件** (`multi_gpu_ddp.yaml`):
+
+```yaml
+compute_environment: LOCAL_MACHINE
+distributed_type: MULTI_GPU
+num_processes: 4  # GPU数量
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+```
+
+**训练脚本** (无需修改):
+
+```python
+from hello_agents.tools import RLTrainingTool
+
+rl_tool = RLTrainingTool()
+
+# 训练代码完全不变
+result = rl_tool.run({
+    "action": "train",
+    "algorithm": "grpo",
+    "model_name": "Qwen/Qwen3-0.6B",
+    "output_dir": "./models/grpo_ddp",
+    "num_epochs": 3,
+    "batch_size": 4,  # 每个GPU的batch size
+    "use_lora": True,
+})
+```
+
+**启动训练**:
+
+```bash
+# 使用配置文件
+accelerate launch --config_file multi_gpu_ddp.yaml train_script.py
+
+# 或者直接指定参数
+accelerate launch --num_processes 4 --mixed_precision fp16 train_script.py
+```
+
+**(3)使用DeepSpeed ZeRO训练**
+
+**DeepSpeed ZeRO**通过分片优化器状态、梯度和模型参数,大幅降低显存占用,支持更大的模型和batch size。
+
+**ZeRO-2配置文件** (`deepspeed_zero2.yaml`):
+
+```yaml
+compute_environment: LOCAL_MACHINE
+distributed_type: DEEPSPEED
+num_processes: 4
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+deepspeed_config:
+  gradient_accumulation_steps: 4
+  gradient_clipping: 1.0
+  offload_optimizer_device: none
+  offload_param_device: none
+  zero3_init_flag: false
+  zero_stage: 2  # ZeRO-2
+```
+
+**ZeRO-3配置文件** (`deepspeed_zero3.yaml`):
+
+```yaml
+compute_environment: LOCAL_MACHINE
+distributed_type: DEEPSPEED
+num_processes: 4
+machine_rank: 0
+num_machines: 1
+gpu_ids: all
+mixed_precision: fp16
+deepspeed_config:
+  gradient_accumulation_steps: 4
+  gradient_clipping: 1.0
+  offload_optimizer_device: cpu  # 优化器状态卸载到CPU
+  offload_param_device: cpu      # 参数卸载到CPU
+  zero3_init_flag: true
+  zero_stage: 3  # ZeRO-3
+```
+
+**启动训练**:
+
+```bash
+# ZeRO-2
+accelerate launch --config_file deepspeed_zero2.yaml train_script.py
+
+# ZeRO-3
+accelerate launch --config_file deepspeed_zero3.yaml train_script.py
+```
+
+如表11.9所示,这是Qwen3-0.6B模型用不同方式训练的显存对比:
+
+<div align="center">
+  <p>表 11.9 显存对比 (Qwen3-0.6B模型)</p>
+  <img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/11-figures/11-table-9.png" alt="" width="85%"/>
+</div>
+
+**(4)多节点训练**
+
+对于超大规模训练,可以使用多个节点(机器)。
+
+**主节点配置** (`multi_node_main.yaml`):
+
+```yaml
+compute_environment: LOCAL_MACHINE
+distributed_type: DEEPSPEED
+num_processes: 16  # 4节点 x 4GPU
+machine_rank: 0    # 主节点
+num_machines: 4
+main_process_ip: 192.168.1.100  # 主节点IP
+main_process_port: 29500
+gpu_ids: all
+mixed_precision: fp16
+deepspeed_config:
+  zero_stage: 3
+  offload_optimizer_device: cpu
+  offload_param_device: cpu
+```
+
+**工作节点配置** (修改`machine_rank`为1, 2, 3):
+
+```yaml
+machine_rank: 1  # 工作节点1
+# 其他配置相同
+```
+
+**启动训练**:
+
+```bash
+# 在主节点上
+accelerate launch --config_file multi_node_main.yaml train_script.py
+
+# 在工作节点1上
+accelerate launch --config_file multi_node_worker1.yaml train_script.py
+
+# 在工作节点2上
+accelerate launch --config_file multi_node_worker2.yaml train_script.py
+
+# 在工作节点3上
+accelerate launch --config_file multi_node_worker3.yaml train_script.py
+```
+
+**(5)分布式训练最佳实践**
+
+**1. Batch Size调整**
+
+分布式训练时,总batch size = `per_device_batch_size × num_gpus × gradient_accumulation_steps`
+
+```python
+# 单GPU: batch_size=4, gradient_accumulation=4, 总batch=16
+# 4GPU DDP: batch_size=4, gradient_accumulation=1, 总batch=16 (保持一致)
+```
+
+**2. 学习率缩放**
+
+使用线性缩放规则: `lr_new = lr_base × sqrt(total_batch_size_new / total_batch_size_base)`
+
+```python
+# 基准: 单GPU, batch=16, lr=5e-5
+# 4GPU: batch=64, lr=5e-5 × sqrt(64/16) = 1e-4
+```
+
+**3. 监控和调试**
+
+```python
+# 启用详细日志
+export ACCELERATE_LOG_LEVEL=INFO
+
+# 启用NCCL调试(多节点)
+export NCCL_DEBUG=INFO
+
+# 检查GPU利用率
+watch -n 1 nvidia-smi
+```
+
+### 11.6.4 生产部署
+
+训练完成后,我们需要将模型部署到生产环境。下面是一些部署建议。
+
+**(1)模型导出**
+
+将LoRA权重合并到基础模型,方便部署:
+
+```python
+from transformers import AutoModelForCausalLM, AutoTokenizer
+from peft import PeftModel
+
+# 加载基础模型
+base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B")
+
+# 加载LoRA权重
+model = PeftModel.from_pretrained(base_model, "./models/grpo_model")
+
+# 合并权重
+merged_model = model.merge_and_unload()
+
+# 保存合并后的模型
+merged_model.save_pretrained("./models/merged_model")
+
+# 保存tokenizer
+tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-0.6B")
+tokenizer.save_pretrained("./models/merged_model")
+
+print("✓ 模型已导出到: ./models/merged_model")
+```
+
+**(2)推理优化**
+
+使用量化和优化技术加速推理:
+
+```python
+from transformers import AutoModelForCausalLM, AutoTokenizer
+import torch
+
+# 加载模型(使用8-bit量化)
+model = AutoModelForCausalLM.from_pretrained(
+    "./models/merged_model",
+    load_in_8bit=True,  # 8-bit量化
+    device_map="auto",  # 自动分配设备
+)
+
+tokenizer = AutoTokenizer.from_pretrained("./models/merged_model")
+
+# 推理
+def generate_answer(question):
+    prompt = f"<|im_start|>user\n{question}<|im_end|>\n<|im_start|>assistant\n"
+    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
+
+    outputs = model.generate(
+        **inputs,
+        max_new_tokens=512,
+        temperature=0.7,
+        do_sample=True,
+    )
+
+    response = tokenizer.decode(outputs[0], skip_special_tokens=False)
+    return response
+
+# 测试
+question = "What is 48 + 24?"
+answer = generate_answer(question)
+print(answer)
+```
+
+**(3)API服务**
+
+使用FastAPI创建推理服务:
+
+```python
+from fastapi import FastAPI
+from pydantic import BaseModel
+from transformers import AutoModelForCausalLM, AutoTokenizer
+
+app = FastAPI()
+
+# 加载模型
+model = AutoModelForCausalLM.from_pretrained("./models/merged_model")
+tokenizer = AutoTokenizer.from_pretrained("./models/merged_model")
+
+class Question(BaseModel):
+    text: str
+    max_tokens: int = 512
+
+class Answer(BaseModel):
+    text: str
+    confidence: float
+
+@app.post("/generate", response_model=Answer)
+def generate(question: Question):
+    """生成答案"""
+    prompt = f"<|im_start|>user\n{question.text}<|im_end|>\n<|im_start|>assistant\n"
+    inputs = tokenizer(prompt, return_tensors="pt")
+
+    outputs = model.generate(
+        **inputs,
+        max_new_tokens=question.max_tokens,
+        temperature=0.7,
+        return_dict_in_generate=True,
+        output_scores=True,
+    )
+
+    response = tokenizer.decode(outputs.sequences[0], skip_special_tokens=False)
+
+    # 计算置信度(简化版)
+    confidence = 0.8  # 实际应该基于输出概率计算
+
+    return Answer(text=response, confidence=confidence)
+
+# 运行: uvicorn api:app --host 0.0.0.0 --port 8000
+```
+
+
+
+## 11.8 本章小结
+
+在本章中,我们系统地学习了Agentic RL的理论和实践,从基础概念到完整的训练流程,从数据准备到模型部署。让我们回顾一下本章的主要内容。
+
+**(1)Agentic RL的本质**
+
+Agentic RL是将LLM作为可学习策略,嵌入到智能体的感知-决策-执行循环中,通过强化学习优化智能体在多步任务中的表现。它与传统的PBRFT(Preference-Based Reinforcement Fine-Tuning)的核心区别在于:
+
+- **任务性质**:从单轮对话优化扩展到多步序贯决策
+- **状态空间**:从静态提示扩展到动态演化的环境状态
+- **行动空间**:从纯文本生成扩展到文本+工具+环境操作
+- **奖励设计**:从单步质量评估扩展到长期累积回报
+- **优化目标**:从短期响应质量扩展到长期任务成功
+
+**(2)六大核心能力**
+
+Agentic RL旨在提升智能体的六大核心能力:
+
+1. **推理(Reasoning)**:多步逻辑推导,学习推理策略
+2. **工具使用(Tool Use)**:API/工具调用,学会何时用、如何用
+3. **记忆(Memory)**:长期信息保持,学习记忆管理
+4. **规划(Planning)**:行动序列规划,学会动态规划
+5. **自我改进(Self-Improvement)**:自我反思优化,从错误中学习
+6. **感知(Perception)**:多模态理解,视觉推理和工具使用
+
+**(3)训练流程**
+
+完整的Agentic RL训练流程包括:
+
+1. **预训练(Pretraining)**:在大规模文本上学习语言知识(通常使用现成的预训练模型)
+2. **监督微调(SFT)**:学习任务格式和基础推理能力
+3. **强化学习(RL)**:通过试错优化推理策略,超越训练数据质量
+
+其中,SFT是基础,RL是提升。没有SFT的基础,RL很难成功;没有RL的优化,模型只能模仿训练数据。
+
+如果你想深入学习Agentic RL,建议按照以下路径:
+
+基础阶段
+
+1. **强化学习基础**:学习MDP、策略梯度、PPO等基本概念
+2. **LLM基础**:了解Transformer、预训练、微调等技术
+3. **实践HelloAgents**:运行本章的示例代码,理解完整流程
+
+进阶阶段
+
+1. **深入TRL**:学习TRL库的实现,理解SFT和GRPO等算法的细节
+2. **自定义数据集**:使用自己的数据集训练模型
+3. **自定义奖励函数**:设计适合自己任务的奖励函数
+4. **参数调优**:系统地调优超参数,提升模型性能
+
+高级阶段
+
+1. **多步推理**:研究长序列推理任务
+2. **工具学习**:让智能体学会使用工具
+3. **多智能体**:研究多智能体协作
+4. **前沿论文**:阅读最新的研究论文,跟进前沿进展
+
+
+
+希望本章能够帮助你理解和掌握Agentic RL技术,在自己的项目中应用这些知识,构建更智能的Agent系统!
+
+
+
+## 参考文献
+
+[1] Schulman, J., Wolski, F., Dhariwal, P., Radford, A., & Klimov, O. (2017). Proximal Policy Optimization Algorithms. *arXiv preprint arXiv:1707.06347*.
+
+[2] Shao, Z., Wang, P., Zhu, Q., Xu, R., Song, J., Zhang, M., ... & Guo, D. (2024). DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models. *arXiv preprint arXiv:2402.03300*.
+
+[3] Hu, E. J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., ... & Chen, W. (2021). LoRA: Low-Rank Adaptation of Large Language Models. *arXiv preprint arXiv:2106.09685*.
+
+[4] Cobbe, K., Kosaraju, V., Bavarian, M., Chen, M., Jun, H., Kaiser, L., ... & Schulman, J. (2021). Training Verifiers to Solve Math Word Problems. *arXiv preprint arXiv:2110.14168*.
+
+[5] Ouyang, L., Wu, J., Jiang, X., Almeida, D., Wainwright, C., Mishkin, P., ... & Lowe, R. (2022). Training language models to follow instructions with human feedback. *Advances in Neural Information Processing Systems*, 35, 27730-27744.
+
+[6] Rafailov, R., Sharma, A., Mitchell, E., Ermon, S., Manning, C. D., & Finn, C. (2023). Direct Preference Optimization: Your Language Model is Secretly a Reward Model. *arXiv preprint arXiv:2305.18290*.
+
+[7] Lee, H., Phatale, S., Mansoor, H., Lu, K., Mesnard, T., Bishop, C., ... & Rastogi, A. (2023). RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback. *arXiv preprint arXiv:2309.00267*.
+
+[8] Wei, J., Wang, X., Schuurmans, D., Bosma, M., Ichter, B., Xia, F., ... & Zhou, D. (2022). Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. *Advances in Neural Information Processing Systems*, 35, 24824-24837.
+
+[9] von Werra, L., Belkada, Y., Tunstall, L., Beeching, E., Thrush, T., Lambert, N., & Huang, S. (2020). TRL: Transformer Reinforcement Learning. *GitHub repository*. https://github.com/huggingface/trl
+
+[10] Qwen Team. (2025). Qwen3 Technical Report. *arXiv preprint arXiv:2505.09388*.
+
+[11] Bai, Y., Jones, A., Ndousse, K., Askell, A., Chen, A., DasSarma, N., ... & Kaplan, J. (2022). Training a Helpful and Harmless Assistant with Reinforcement Learning from Human Feedback. *arXiv preprint arXiv:2204.05862*.
+
+[12] Wang, X., Wei, J., Schuurmans, D., Le, Q., Chi, E., Narang, S., ... & Zhou, D. (2022). Self-Consistency Improves Chain of Thought Reasoning in Language Models. *arXiv preprint arXiv:2203.11171*.
+
+[13] Christiano, P. F., Leike, J., Brown, T., Martic, M., Legg, S., & Amodei, D. (2017). Deep Reinforcement Learning from Human Preferences. *Advances in Neural Information Processing Systems*, 30.
+
+[14] Stiennon, N., Ouyang, L., Wu, J., Ziegler, D., Lowe, R., Voss, C., ... & Christiano, P. F. (2020). Learning to summarize with human feedback. *Advances in Neural Information Processing Systems*, 33, 3008-3021.
+
+[15] Ziegler, D. M., Stiennon, N., Wu, J., Brown, T. B., Radford, A., Amodei, D., ... & Irving, G. (2019). Fine-Tuning Language Models from Human Preferences. *arXiv preprint arXiv:1909.08593*.
+

BIN
docs/images/11-figures/11-1.png


BIN
docs/images/11-figures/11-2.png


BIN
docs/images/11-figures/11-3.png


BIN
docs/images/11-figures/11-4.png


BIN
docs/images/11-figures/11-5.png


BIN
docs/images/11-figures/11-6.png


BIN
docs/images/11-figures/11-7.png


BIN
docs/images/11-figures/11-8.png


BIN
docs/images/11-figures/11-table-1.png


BIN
docs/images/11-figures/11-table-2.png


BIN
docs/images/11-figures/11-table-3.png


BIN
docs/images/11-figures/11-table-4.png


BIN
docs/images/11-figures/11-table-5.png


BIN
docs/images/11-figures/11-table-6.png


BIN
docs/images/11-figures/11-table-7.png


BIN
docs/images/11-figures/11-table-8.png


BIN
docs/images/11-figures/11-table-9.png