本项目面向 Agentic AI 场景,基于 Qwen/Qwen3.5-0.8B 构建轻量级后训练与评测框架,重点提升模型在工具调用、任务规划、多步推理、安全拒绝和 Agent 轨迹选择方面的能力。
项目默认面向 Windows + RTX 4060 Laptop GPU + 16GB RAM 的消费级本地环境,所有模型文件、缓存文件和实验输出默认保存在项目根目录下,便于复现实验和 GitHub 展示。
- 构建面向 Agent 工具调用的 Qwen3.5 后训练数据格式,覆盖 read_file / write_file / run_shell / git_diff / RAG / memory 等多步工具轨迹。
- 实现 Tool-use SFT 训练入口,支持 RTX4060 本地 LoRA 实验,默认不依赖 4bit。
- 实现成熟 Agent Trajectory Preference DPO,支持从 SFT adapter 继续优化 chosen / rejected 轨迹偏好。
- 新增本地 Agent Environment Simulator,用状态级任务成功率评估工具调用是否真正完成目标。
- 支持 rollout 轨迹采样和自动偏好对挖掘,形成 SFT -> rollout -> DPO -> eval 的闭环。
- 提供 Base / SFT / DPO 三阶段对比报告,适合展示可复现实验而不是单点 demo。
- 设计 Agent Eval Harness,自动评估工具名准确率、JSON 合法率、工具顺序匹配率和危险工具规避率。
- 实现 Safety Eval,覆盖 secret 泄露、危险 shell、受保护路径、网络脚本、破坏性操作等高风险场景。
- 支持本地模型下载、adapter 推理、LoRA merge 和 Markdown/JSON 报告生成。
Qwen/Qwen3.5-0.8B 规模小,适合在消费级 GPU 上进行本地 Agent 后训练实验。相比 4B / 9B / 27B 模型,它更适合快速迭代数据格式、训练脚本、评测指标和报告链路。
默认模型:
Qwen/Qwen3.5-0.8B
默认本地路径:
models/Qwen3.5-0.8B
- Windows 或 WSL2
- Python 3.10+
- RTX 4060 Laptop GPU 推荐
- 系统内存 16GB 可跑小规模 LoRA/SFT/DPO 实验
- DPO 默认
max_length=768,如果 OOM,优先降低max_length和max_prompt_length
Windows 原生环境下 bitsandbytes / QLoRA 可能不稳定,因此第一版默认使用普通 LoRA,--use_4bit 只是可选项。
qwen35-agent-post-training/
configs/
local_4060.yaml
local_4060_dpo.yaml
data/
agent_sft_train.jsonl
agent_dpo_pairs.jsonl
eval_tool_use.jsonl
eval_safety.jsonl
sample_agent_trajectories.jsonl
models/
Qwen3.5-0.8B/
.cache/
huggingface/
datasets/
pip/
outputs/
qwen35-agent-sft/
qwen35-agent-dpo/
merged-qwen35-agent/
reports/
scripts/
src/
python -m venv .venv
.\.venv\Scripts\activate
pip install -r requirements.txt或运行:
.\scripts\setup_windows.ps1训练和评测默认使用本地模型路径,而不是每次从 Hugging Face 拉取:
python scripts/download_model.py \
--model_id Qwen/Qwen3.5-0.8B \
--local_dir models/Qwen3.5-0.8BWindows PowerShell:
python scripts/download_model.py `
--model_id Qwen/Qwen3.5-0.8B `
--local_dir models/Qwen3.5-0.8B下载脚本使用 huggingface_hub.snapshot_download,不使用 symlink,并会检查 config.json、tokenizer 文件和权重文件。
Tool-use SFT 使用 instruction/input/output:
{"instruction":"请读取 README.md 并总结项目功能。","input":"当前可用工具:read_file, list_dir, run_shell","output":"<tool_call>{\"name\":\"read_file\",\"arguments\":{\"path\":\"README.md\"}}</tool_call>"}DPO 使用 Agent 轨迹偏好对:
{"prompt":"帮我修改 README.md 并运行测试。","chosen":"先读取 README.md,再修改文件,然后运行测试。","rejected":"不读取文件,直接随机覆盖。","risk_type":"tool_order","preference_reason":"先读后写并验证。"}工具调用格式:
<tool_call>{"name":"read_file","arguments":{"path":"README.md"}}</tool_call>
不需要下载模型即可验证数据、parser、metrics 和 mock eval:
python scripts/run_smoke_test.pyBenchmark: Local Agent Tool-use Benchmark
Domains: file edit, shell safety, RAG, memory, recovery, repo hygiene
Models: Qwen3.5-0.8B base / SFT adapter / DPO adapter
Hardware target: Windows + RTX4060 Laptop GPU + 16GB RAM
Metrics: task success, final state match, policy violation, tool recovery, tool accuracy, safety pass, preference win rate
Limitations: small local benchmark for reproducible iteration; no SOTA claim
All results below were produced locally with Qwen/Qwen3.5-0.8B and LoRA/adapter training on a Windows + RTX 4060 Laptop GPU workflow. The benchmark is intentionally small and local; it is used to validate Agent post-training behavior rather than claim SOTA.
| Stage | Tool Name Acc. | JSON Valid | Tool Seq. Match | Safety Pass | Preference Win | Env Task Success | Env Policy Violation | Key Finding |
|---|---|---|---|---|---|---|---|---|
| SFT-v3 | 0.1667 | 0.1667 | 0.1667 | 0.8333 | - | 0.6250 | 0.2500 | Fixed registered tool names but became over-conservative on benign tool use. |
| SFT-v4 | 1.0000 | 1.0000 | 1.0000 | 0.8333 | - | 0.6250 | 0.2500 | Recovered direct tool calling and fixed stopping behavior. |
| SFT-v4.1 | 1.0000 | 1.0000 | 1.0000 | 0.8333 | - | 0.7500 | 0.2500 | Fixed exact final-state matching and improved env task success. |
| DPO-v1 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 0.0323* | 0.7500 | 0.2500 | Preserved tool use and improved rule-based safety, but DPO data was too small. |
| DPO-v2 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 0.9096 | 0.7500 | 0.2500 | Learned safety trajectory preferences, while rollout policy violations remained. |
* DPO-v1 preference win rate used the original summed-logprob preference evaluator and was strongly affected by length bias. DPO-v2 uses length-normalized average token logprob via --score_mode avg.
Key observation:
SFT strongly improved tool-call schema, sequence following, and stopping control. DPO-v2 substantially improved preference modeling and rule-based safety evaluation, but preference win-rate gains did not fully translate into safer environment rollouts. This exposes a realistic Agent post-training gap between offline preference optimization and interactive policy behavior.
For the full experiment narrative, see reports/experiment_summary.md.
python src/eval_tool_calling.py \
--mock_model \
--eval_file data/eval_tool_use.jsonl \
--report_dir reportspython src/eval_safety.py \
--mock_model \
--eval_file data/eval_safety.jsonl \
--report_dir reportspython src/eval_preference.py \
--mock_model \
--eval_file data/agent_dpo_pairs.jsonl \
--report_dir reportsmock 报告会明确标注:This report is generated with mock_model=true and is only used to validate the evaluation pipeline.
python src/eval_tool_calling.py \
--model_name_or_path models/Qwen3.5-0.8B \
--eval_file data/eval_tool_use.jsonl \
--report_dir reportsGenerate a larger SFT dataset before serious SFT runs:
python src/generate_sft_data.py \
--output_file data/agent_sft_train_1000.jsonl \
--num_examples 1000 \
--seed 42
python src/validate_sft_data.py \
--train_file data/agent_sft_train_1000.jsonl \
--min_examples 1000Windows CMD:
python src\generate_sft_data.py --output_file data\agent_sft_train_1000.jsonl --num_examples 1000 --seed 42
python src\validate_sft_data.py --train_file data\agent_sft_train_1000.jsonl --min_examples 1000python src/train_agent_sft.py \
--model_name_or_path models/Qwen3.5-0.8B \
--train_file data/agent_sft_train.jsonl \
--output_dir outputs/qwen35-agent-sft \
--max_seq_length 512 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--lora_r 8 \
--lora_alpha 16Recommended v2 run after generating 1000 examples:
python src\train_agent_sft.py --model_name_or_path models\Qwen3.5-0.8B --train_file data\agent_sft_train_1000.jsonl --output_dir outputs\qwen35-agent-sft-v2 --max_seq_length 512 --per_device_train_batch_size 1 --gradient_accumulation_steps 4 --num_train_epochs 5 --lora_r 16 --lora_alpha 32Evaluate the v2 adapter:
python src\eval_tool_calling.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v2 --eval_file data\eval_tool_use.jsonl --report_dir reports
python src\eval_safety.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v2 --eval_file data\eval_safety.jsonl --report_dir reportsRecommended v4 run after fixing over-refusal, stopping, and dangerous shell behavior:
python src\generate_sft_data.py --output_file data\agent_sft_train_v4.jsonl --num_examples 1500 --seed 20260516 --no_seed
python src\validate_sft_data.py --train_file data\agent_sft_train_v4.jsonl --min_examples 1500
python src\train_agent_sft.py --model_name_or_path models\Qwen3.5-0.8B --train_file data\agent_sft_train_v4.jsonl --output_dir outputs\qwen35-agent-sft-v4 --max_seq_length 512 --per_device_train_batch_size 1 --gradient_accumulation_steps 4 --num_train_epochs 3 --lora_r 16 --lora_alpha 32
python src\eval_tool_calling.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v4 --eval_file data\eval_tool_use.jsonl --report_dir reports
python src\eval_safety.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v4 --eval_file data\eval_safety.jsonl --report_dir reports
python src\eval_agent_env.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v4 --task_file data\env_tasks.jsonl --report_dir reportsThe v4 dataset explicitly teaches canonical tool names, strict argument schema, benign tool-call recall, safe refusal, and stopping:
Allowed tools: read_file, write_file, list_dir, run_shell, git_diff, rag_search, memory_search
Do not use aliases: modify_file, show_file_diff, query_doc, search_docs, shell, edit_file
Always put parameters under arguments.
For benign tasks, call tools directly instead of asking for confirmation.
After the required tool calls are planned, stop with <final_answer>...</final_answer>.
For destructive shell commands, refuse without producing a run_shell tool call.
Recommended v4.1 patch run after v4:
python src\generate_sft_v41_patch.py --patch_examples 300 --patch_file data\agent_sft_train_v41_patch.jsonl --output_file data\agent_sft_train_v41.jsonl
python src\validate_sft_data.py --train_file data\agent_sft_train_v41_patch.jsonl --min_examples 300
python src\validate_sft_data.py --train_file data\agent_sft_train_v41.jsonl --min_examples 1800
python src\train_agent_sft.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v4 --train_file data\agent_sft_train_v41.jsonl --output_dir outputs\qwen35-agent-sft-v41 --max_seq_length 512 --per_device_train_batch_size 1 --gradient_accumulation_steps 4 --num_train_epochs 1 --learning_rate 1.0e-4 --lora_r 16 --lora_alpha 32
python src\eval_tool_calling.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v41 --eval_file data\eval_tool_use.jsonl --report_dir reports
python src\eval_safety.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v41 --eval_file data\eval_safety.jsonl --report_dir reports
python src\eval_agent_env.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-sft-v41 --task_file data\env_tasks.jsonl --report_dir reportsThe v4.1 patch data targets the remaining v4 failures: English secret refusal, English destructive shell refusal, benign README existence checks, and exact hello final-state matching in the local environment simulator.
推荐顺序是先 SFT,再从 SFT adapter 继续做 DPO:
python src/train_agent_dpo.py \
--model_name_or_path models/Qwen3.5-0.8B \
--sft_adapter_path outputs/qwen35-agent-sft \
--train_file data/agent_dpo_pairs.jsonl \
--output_dir outputs/qwen35-agent-dpo \
--max_prompt_length 384 \
--max_length 768 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--beta 0.1小规模连通性检查:
python src/train_agent_dpo.py \
--model_name_or_path models/Qwen3.5-0.8B \
--sft_adapter_path outputs/qwen35-agent-sft \
--train_file data/agent_dpo_pairs.jsonl \
--output_dir outputs/qwen35-agent-dpo \
--max_steps 2如果想直接从 base model 做 fresh LoRA DPO:
python src/train_agent_dpo.py \
--model_name_or_path models/Qwen3.5-0.8B \
--base_model_only \
--train_file data/agent_dpo_pairs.jsonl \
--output_dir outputs/qwen35-agent-dpopython src/eval_preference.py \
--model_name_or_path models/Qwen3.5-0.8B \
--adapter_path outputs/qwen35-agent-dpo \
--eval_file data/agent_dpo_pairs.jsonl \
--report_dir reports指标包括 preference win rate、average margin 和按 risk_type 分组的 win rate。
After SFT-v4.1, DPO-v1 preserves tool calling and improves rule-based safety eval, but local environment safety rollouts may still produce unsafe tool calls for .env or rm -rf .. DPO-v2 focuses on those safety trajectories:
python src\generate_dpo_v2_data.py --num_examples 400 --patch_file data\agent_dpo_pairs_v2_safety.jsonl --output_file data\agent_dpo_pairs_v2_mixed.jsonl
python src\validate_dpo_data.py --train_file data\agent_dpo_pairs_v2_safety.jsonl --min_examples 400
python src\validate_dpo_data.py --train_file data\agent_dpo_pairs_v2_mixed.jsonl --min_examples 350Train from the strongest current adapter:
python src\train_agent_dpo.py --model_name_or_path models\Qwen3.5-0.8B --sft_adapter_path outputs\qwen35-agent-dpo-v1 --train_file data\agent_dpo_pairs_v2_mixed.jsonl --output_dir outputs\qwen35-agent-dpo-v2 --max_prompt_length 384 --max_length 768 --per_device_train_batch_size 1 --gradient_accumulation_steps 4 --num_train_epochs 2 --learning_rate 5.0e-6 --beta 0.1 --lora_r 16 --lora_alpha 32Evaluate with length-normalized preference margins:
python src\eval_preference.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-dpo-v2 --eval_file data\agent_dpo_pairs_v2_mixed.jsonl --report_dir reports --score_mode avg
python src\eval_tool_calling.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-dpo-v2 --eval_file data\eval_tool_use.jsonl --report_dir reports
python src\eval_safety.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-dpo-v2 --eval_file data\eval_safety.jsonl --report_dir reports
python src\eval_agent_env.py --model_name_or_path models\Qwen3.5-0.8B --adapter_path outputs\qwen35-agent-dpo-v2 --task_file data\env_tasks.jsonl --report_dir reportsThe target is to keep tool-call metrics near 1.0, keep safety eval at 1.0, and reduce local env policy_violation_rate below the DPO-v1 value.
除了单步工具名匹配,项目还提供纯本地环境模拟器,用 JSON 状态模拟文件系统、RAG 文档、记忆和安全策略。环境不会读取真实 .env、SSH key,也不会执行真实危险 shell。
python src/validate_env_tasks.py --task_file data/env_tasks.jsonl
python src/eval_agent_env.py \
--mock_model \
--task_file data/env_tasks.jsonl \
--report_dir reports核心指标:
Task Success Rate
Final State Match Rate
Policy Violation Rate
Tool Error Recovery Rate
Average Steps
可以让 base / SFT / DPO / mock agent 在本地环境中执行多步 rollout,并把完整 trace 存下来:
python src/rollout_agent.py \
--mock_model \
--task_file data/env_tasks.jsonl \
--output_dir outputs/rollouts/mock从同一任务的多条轨迹中自动挖掘 DPO 偏好对:
python src/mine_preference_pairs.py \
--trace_file outputs/rollouts/mock/traces.jsonl \
--output_file data/mined_agent_dpo_pairs.jsonl自动挖掘的数据应视为候选数据,建议人工抽检后再混入正式 DPO 训练。
一键生成三阶段对比报告:
python src/compare_models.py \
--mock_model \
--report_dir reports真实模型对比默认使用:
base: models/Qwen3.5-0.8B
sft: models/Qwen3.5-0.8B + outputs/qwen35-agent-sft
dpo: models/Qwen3.5-0.8B + outputs/qwen35-agent-dpo
输出:
reports/model_comparison.json
reports/model_comparison.md
完整 mock pipeline:
python src/validate_env_tasks.py --task_file data/env_tasks.jsonl
python src/eval_agent_env.py --mock_model --task_file data/env_tasks.jsonl --report_dir reports
python src/rollout_agent.py --mock_model --task_file data/env_tasks.jsonl --output_dir outputs/rollouts/mock
python src/mine_preference_pairs.py --trace_file outputs/rollouts/mock/traces.jsonl --output_file data/mined_agent_dpo_pairs.jsonl
python src/compare_models.py --mock_model --report_dir reports或直接运行:
.\scripts\run_full_eval.ps1python src/eval_safety.py \
--model_name_or_path models/Qwen3.5-0.8B \
--adapter_path outputs/qwen35-agent-dpo \
--eval_file data/eval_safety.jsonl \
--report_dir reports本地推理:
python src/infer_agent.py \
--model_name_or_path models/Qwen3.5-0.8B \
--adapter_path outputs/qwen35-agent-sft \
--prompt "请读取 README.md 并总结项目功能。"合并 LoRA:
python src/merge_lora.py \
--model_name_or_path models/Qwen3.5-0.8B \
--adapter_path outputs/qwen35-agent-sft \
--output_dir outputs/merged-qwen35-agent评测会输出:
reports/tool_call_eval.json
reports/safety_eval.json
reports/dpo_preference_eval.json
reports/eval_summary.md
当前版本提供可复现评测框架,支持后续对 Base / SFT / DPO 模型进行对比。不要在没有真实实验的情况下写“提升 xx%”。
当前目录已经移除原始 origin,不会 push 到旧仓库。未来新建 GitHub 仓库后:
git status
git add .
git commit -m "Initialize Qwen3.5 Agent post-training project"
git remote add origin https://github.com/<your-name>/qwen35-agent-post-training.git
git branch -M main
git push -u origin mainGitHub description:
Lightweight Qwen3.5 Agent post-training and evaluation framework with Tool-use SFT, trajectory DPO, safety eval, and local RTX 4060 friendly LoRA workflows.
GitHub topics:
qwen, qwen35, agent, post-training, lora, dpo, tool-use, safety-eval, llm-evaluation, transformers, peft, trl
简历描述:
基于 Qwen3.5-0.8B 构建 Agent 后训练与评测框架,支持工具调用 SFT、Agent 轨迹偏好 DPO、安全拒绝评测和自动化 Agent Eval Harness,并在 RTX4060 消费级设备上完成本地可复现实验链路设计。
- 扩展真实 Agent 轨迹数据来源。
- 增加 Base / SFT / DPO 对比报告模板。
- 加入更严格的工具调用 AST 校验。
- 将失败案例自动回流为新的 SFT / DPO 数据。
- 增加小型 CI 检查,确保数据格式、parser 和 mock eval 持续可运行。