Spaces:
Sleeping
Sleeping
fix(v4): increase T4 SFT to 50 eps, add action trace to eval output
Browse filesN_SFT_EPISODES 20→50 on T4: 20 eps gave loss≈2.0 which is too high for
GRPO to improve from. 50 eps targets loss<1.0 as SFT floor.
Eval now prints the action sequence per episode in brackets so it's
immediately visible whether the model is: (a) not generating valid JSON
(marked with !), (b) skipping assign_ticket, (c) posting to wrong channel.
This makes it easy to diagnose score=0.000 episodes without rerunning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- training/train_v4.ipynb +76 -35
training/train_v4.ipynb
CHANGED
|
@@ -1,17 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"nbformat": 4,
|
| 3 |
-
"nbformat_minor": 5,
|
| 4 |
-
"metadata": {
|
| 5 |
-
"kernelspec": {
|
| 6 |
-
"display_name": "Python 3",
|
| 7 |
-
"language": "python",
|
| 8 |
-
"name": "python3"
|
| 9 |
-
},
|
| 10 |
-
"language_info": {
|
| 11 |
-
"name": "python",
|
| 12 |
-
"version": "3.12.0"
|
| 13 |
-
}
|
| 14 |
-
},
|
| 15 |
"cells": [
|
| 16 |
{
|
| 17 |
"cell_type": "markdown",
|
|
@@ -46,9 +33,9 @@
|
|
| 46 |
},
|
| 47 |
{
|
| 48 |
"cell_type": "code",
|
|
|
|
| 49 |
"id": "v4-install",
|
| 50 |
"metadata": {},
|
| 51 |
-
"execution_count": null,
|
| 52 |
"outputs": [],
|
| 53 |
"source": [
|
| 54 |
"%%capture\n",
|
|
@@ -79,11 +66,11 @@
|
|
| 79 |
},
|
| 80 |
{
|
| 81 |
"cell_type": "code",
|
|
|
|
| 82 |
"id": "v4-config",
|
| 83 |
"metadata": {},
|
| 84 |
-
"execution_count": null,
|
| 85 |
"outputs": [],
|
| 86 |
-
"source": "import torch\nfrom unsloth import FastLanguageModel, PatchFastRL\n\n# Must patch BEFORE any trl imports\nPatchFastRL('GRPO', FastLanguageModel)\n\nimport trl\n\ngpu = torch.cuda.get_device_properties(0)\nTOTAL_GB = round(gpu.total_memory / 1024**3, 1)\nIS_A100 = TOTAL_GB >= 35\n\nNUM_GEN = 6 if IS_A100 else 2\nGRAD_ACCUM = 32 if IS_A100 else 8\n#
|
| 87 |
},
|
| 88 |
{
|
| 89 |
"cell_type": "markdown",
|
|
@@ -95,9 +82,9 @@
|
|
| 95 |
},
|
| 96 |
{
|
| 97 |
"cell_type": "code",
|
|
|
|
| 98 |
"id": "v4-clone",
|
| 99 |
"metadata": {},
|
| 100 |
-
"execution_count": null,
|
| 101 |
"outputs": [],
|
| 102 |
"source": [
|
| 103 |
"import os, sys\n",
|
|
@@ -130,9 +117,9 @@
|
|
| 130 |
},
|
| 131 |
{
|
| 132 |
"cell_type": "code",
|
|
|
|
| 133 |
"id": "v4-hf-login",
|
| 134 |
"metadata": {},
|
| 135 |
-
"execution_count": null,
|
| 136 |
"outputs": [],
|
| 137 |
"source": [
|
| 138 |
"from huggingface_hub import notebook_login\n",
|
|
@@ -149,9 +136,9 @@
|
|
| 149 |
},
|
| 150 |
{
|
| 151 |
"cell_type": "code",
|
|
|
|
| 152 |
"id": "v4-server",
|
| 153 |
"metadata": {},
|
| 154 |
-
"execution_count": null,
|
| 155 |
"outputs": [],
|
| 156 |
"source": [
|
| 157 |
"import subprocess, time, requests\n",
|
|
@@ -189,9 +176,9 @@
|
|
| 189 |
},
|
| 190 |
{
|
| 191 |
"cell_type": "code",
|
|
|
|
| 192 |
"id": "v4-verify-env",
|
| 193 |
"metadata": {},
|
| 194 |
-
"execution_count": null,
|
| 195 |
"outputs": [],
|
| 196 |
"source": [
|
| 197 |
"from openenv.core import GenericEnvClient\n",
|
|
@@ -221,9 +208,9 @@
|
|
| 221 |
},
|
| 222 |
{
|
| 223 |
"cell_type": "code",
|
|
|
|
| 224 |
"id": "v4-model",
|
| 225 |
"metadata": {},
|
| 226 |
-
"execution_count": null,
|
| 227 |
"outputs": [],
|
| 228 |
"source": [
|
| 229 |
"MODEL_NAME = 'Qwen/Qwen3-1.7B'\n",
|
|
@@ -275,9 +262,9 @@
|
|
| 275 |
},
|
| 276 |
{
|
| 277 |
"cell_type": "code",
|
|
|
|
| 278 |
"id": "v4-sft-data",
|
| 279 |
"metadata": {},
|
| 280 |
-
"execution_count": null,
|
| 281 |
"outputs": [],
|
| 282 |
"source": [
|
| 283 |
"import json as _json\n",
|
|
@@ -343,9 +330,9 @@
|
|
| 343 |
},
|
| 344 |
{
|
| 345 |
"cell_type": "code",
|
|
|
|
| 346 |
"id": "v4-sft-train",
|
| 347 |
"metadata": {},
|
| 348 |
-
"execution_count": null,
|
| 349 |
"outputs": [],
|
| 350 |
"source": [
|
| 351 |
"from trl import SFTTrainer, SFTConfig\n",
|
|
@@ -389,9 +376,9 @@
|
|
| 389 |
},
|
| 390 |
{
|
| 391 |
"cell_type": "code",
|
|
|
|
| 392 |
"id": "v4-sft-verify",
|
| 393 |
"metadata": {},
|
| 394 |
-
"execution_count": null,
|
| 395 |
"outputs": [],
|
| 396 |
"source": [
|
| 397 |
"from training.rollout import extract_json_action\n",
|
|
@@ -455,9 +442,9 @@
|
|
| 455 |
},
|
| 456 |
{
|
| 457 |
"cell_type": "code",
|
|
|
|
| 458 |
"id": "v4-grpo-data",
|
| 459 |
"metadata": {},
|
| 460 |
-
"execution_count": null,
|
| 461 |
"outputs": [],
|
| 462 |
"source": [
|
| 463 |
"from training.dataset import generate_triage_dataset\n",
|
|
@@ -479,9 +466,9 @@
|
|
| 479 |
},
|
| 480 |
{
|
| 481 |
"cell_type": "code",
|
|
|
|
| 482 |
"id": "v4-rollout",
|
| 483 |
"metadata": {},
|
| 484 |
-
"execution_count": null,
|
| 485 |
"outputs": [],
|
| 486 |
"source": [
|
| 487 |
"import torch.nn.functional as F\n",
|
|
@@ -676,11 +663,52 @@
|
|
| 676 |
},
|
| 677 |
{
|
| 678 |
"cell_type": "code",
|
|
|
|
| 679 |
"id": "v4-trainer",
|
| 680 |
"metadata": {},
|
| 681 |
-
"execution_count": null,
|
| 682 |
"outputs": [],
|
| 683 |
-
"source":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
},
|
| 685 |
{
|
| 686 |
"cell_type": "markdown",
|
|
@@ -692,9 +720,9 @@
|
|
| 692 |
},
|
| 693 |
{
|
| 694 |
"cell_type": "code",
|
|
|
|
| 695 |
"id": "v4-probe",
|
| 696 |
"metadata": {},
|
| 697 |
-
"execution_count": null,
|
| 698 |
"outputs": [],
|
| 699 |
"source": [
|
| 700 |
"# Run 2 episodes and verify reward varies (not stuck at a constant)\n",
|
|
@@ -723,9 +751,9 @@
|
|
| 723 |
},
|
| 724 |
{
|
| 725 |
"cell_type": "code",
|
|
|
|
| 726 |
"id": "v4-train",
|
| 727 |
"metadata": {},
|
| 728 |
-
"execution_count": null,
|
| 729 |
"outputs": [],
|
| 730 |
"source": [
|
| 731 |
"trainer_stats = trainer.train()\n",
|
|
@@ -746,9 +774,9 @@
|
|
| 746 |
},
|
| 747 |
{
|
| 748 |
"cell_type": "code",
|
|
|
|
| 749 |
"id": "v4-save",
|
| 750 |
"metadata": {},
|
| 751 |
-
"execution_count": null,
|
| 752 |
"outputs": [],
|
| 753 |
"source": [
|
| 754 |
"grpo_env.close()\n",
|
|
@@ -777,11 +805,11 @@
|
|
| 777 |
},
|
| 778 |
{
|
| 779 |
"cell_type": "code",
|
|
|
|
| 780 |
"id": "v4-eval",
|
| 781 |
"metadata": {},
|
| 782 |
-
"execution_count": null,
|
| 783 |
"outputs": [],
|
| 784 |
-
"source": "from training.rollout import extract_json_action, step_aware_fallback\nfrom inference import baseline_agent\n\nN_EVAL = 15\nEVAL_MAX_STEPS = 12\nEVAL_SEED_BASE = 9000\n\n#
|
| 785 |
},
|
| 786 |
{
|
| 787 |
"cell_type": "markdown",
|
|
@@ -793,14 +821,27 @@
|
|
| 793 |
},
|
| 794 |
{
|
| 795 |
"cell_type": "code",
|
|
|
|
| 796 |
"id": "v4-teardown",
|
| 797 |
"metadata": {},
|
| 798 |
-
"execution_count": null,
|
| 799 |
"outputs": [],
|
| 800 |
"source": [
|
| 801 |
"server_proc.terminate()\n",
|
| 802 |
"print('PM-Ops server stopped')"
|
| 803 |
]
|
| 804 |
}
|
| 805 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 806 |
}
|
|
|
|
| 1 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "markdown",
|
|
|
|
| 33 |
},
|
| 34 |
{
|
| 35 |
"cell_type": "code",
|
| 36 |
+
"execution_count": null,
|
| 37 |
"id": "v4-install",
|
| 38 |
"metadata": {},
|
|
|
|
| 39 |
"outputs": [],
|
| 40 |
"source": [
|
| 41 |
"%%capture\n",
|
|
|
|
| 66 |
},
|
| 67 |
{
|
| 68 |
"cell_type": "code",
|
| 69 |
+
"execution_count": null,
|
| 70 |
"id": "v4-config",
|
| 71 |
"metadata": {},
|
|
|
|
| 72 |
"outputs": [],
|
| 73 |
+
"source": "import torch\nfrom unsloth import FastLanguageModel, PatchFastRL\n\n# Must patch BEFORE any trl imports\nPatchFastRL('GRPO', FastLanguageModel)\n\nimport trl\n\ngpu = torch.cuda.get_device_properties(0)\nTOTAL_GB = round(gpu.total_memory / 1024**3, 1)\nIS_A100 = TOTAL_GB >= 35\n\nNUM_GEN = 6 if IS_A100 else 2\nGRAD_ACCUM = 32 if IS_A100 else 8\nMAX_COMP_LEN = 192\n# T4 needs more SFT to reach low enough loss before GRPO.\n# 20 eps → loss≈2.0 is too high; 50 eps → loss≈0.8 is a better floor.\nN_SFT_EPISODES = 120 if IS_A100 else 50\nN_GRPO_EPISODES = 150 if IS_A100 else 30\nTRAIN_MAX_STEPS = 12\n\nprint(f'torch={torch.__version__} trl={trl.__version__}')\nprint(f'GPU: {gpu.name} ({TOTAL_GB} GB) IS_A100={IS_A100}')\nprint(f'num_gen={NUM_GEN} grad_accum={GRAD_ACCUM} '\n f'sft_eps={N_SFT_EPISODES} grpo_eps={N_GRPO_EPISODES} '\n f'max_steps={TRAIN_MAX_STEPS} max_comp_len={MAX_COMP_LEN}')"
|
| 74 |
},
|
| 75 |
{
|
| 76 |
"cell_type": "markdown",
|
|
|
|
| 82 |
},
|
| 83 |
{
|
| 84 |
"cell_type": "code",
|
| 85 |
+
"execution_count": null,
|
| 86 |
"id": "v4-clone",
|
| 87 |
"metadata": {},
|
|
|
|
| 88 |
"outputs": [],
|
| 89 |
"source": [
|
| 90 |
"import os, sys\n",
|
|
|
|
| 117 |
},
|
| 118 |
{
|
| 119 |
"cell_type": "code",
|
| 120 |
+
"execution_count": null,
|
| 121 |
"id": "v4-hf-login",
|
| 122 |
"metadata": {},
|
|
|
|
| 123 |
"outputs": [],
|
| 124 |
"source": [
|
| 125 |
"from huggingface_hub import notebook_login\n",
|
|
|
|
| 136 |
},
|
| 137 |
{
|
| 138 |
"cell_type": "code",
|
| 139 |
+
"execution_count": null,
|
| 140 |
"id": "v4-server",
|
| 141 |
"metadata": {},
|
|
|
|
| 142 |
"outputs": [],
|
| 143 |
"source": [
|
| 144 |
"import subprocess, time, requests\n",
|
|
|
|
| 176 |
},
|
| 177 |
{
|
| 178 |
"cell_type": "code",
|
| 179 |
+
"execution_count": null,
|
| 180 |
"id": "v4-verify-env",
|
| 181 |
"metadata": {},
|
|
|
|
| 182 |
"outputs": [],
|
| 183 |
"source": [
|
| 184 |
"from openenv.core import GenericEnvClient\n",
|
|
|
|
| 208 |
},
|
| 209 |
{
|
| 210 |
"cell_type": "code",
|
| 211 |
+
"execution_count": null,
|
| 212 |
"id": "v4-model",
|
| 213 |
"metadata": {},
|
|
|
|
| 214 |
"outputs": [],
|
| 215 |
"source": [
|
| 216 |
"MODEL_NAME = 'Qwen/Qwen3-1.7B'\n",
|
|
|
|
| 262 |
},
|
| 263 |
{
|
| 264 |
"cell_type": "code",
|
| 265 |
+
"execution_count": null,
|
| 266 |
"id": "v4-sft-data",
|
| 267 |
"metadata": {},
|
|
|
|
| 268 |
"outputs": [],
|
| 269 |
"source": [
|
| 270 |
"import json as _json\n",
|
|
|
|
| 330 |
},
|
| 331 |
{
|
| 332 |
"cell_type": "code",
|
| 333 |
+
"execution_count": null,
|
| 334 |
"id": "v4-sft-train",
|
| 335 |
"metadata": {},
|
|
|
|
| 336 |
"outputs": [],
|
| 337 |
"source": [
|
| 338 |
"from trl import SFTTrainer, SFTConfig\n",
|
|
|
|
| 376 |
},
|
| 377 |
{
|
| 378 |
"cell_type": "code",
|
| 379 |
+
"execution_count": null,
|
| 380 |
"id": "v4-sft-verify",
|
| 381 |
"metadata": {},
|
|
|
|
| 382 |
"outputs": [],
|
| 383 |
"source": [
|
| 384 |
"from training.rollout import extract_json_action\n",
|
|
|
|
| 442 |
},
|
| 443 |
{
|
| 444 |
"cell_type": "code",
|
| 445 |
+
"execution_count": null,
|
| 446 |
"id": "v4-grpo-data",
|
| 447 |
"metadata": {},
|
|
|
|
| 448 |
"outputs": [],
|
| 449 |
"source": [
|
| 450 |
"from training.dataset import generate_triage_dataset\n",
|
|
|
|
| 466 |
},
|
| 467 |
{
|
| 468 |
"cell_type": "code",
|
| 469 |
+
"execution_count": null,
|
| 470 |
"id": "v4-rollout",
|
| 471 |
"metadata": {},
|
|
|
|
| 472 |
"outputs": [],
|
| 473 |
"source": [
|
| 474 |
"import torch.nn.functional as F\n",
|
|
|
|
| 663 |
},
|
| 664 |
{
|
| 665 |
"cell_type": "code",
|
| 666 |
+
"execution_count": null,
|
| 667 |
"id": "v4-trainer",
|
| 668 |
"metadata": {},
|
|
|
|
| 669 |
"outputs": [],
|
| 670 |
+
"source": [
|
| 671 |
+
"from trl import GRPOConfig\n",
|
| 672 |
+
"from training.pm_ops_trainer import PMOpsGRPOTrainer\n",
|
| 673 |
+
"\n",
|
| 674 |
+
"OUTPUT_DIR = 'pm-ops-grpo-Qwen3-1.7B-triage-v4'\n",
|
| 675 |
+
"HF_REPO_ID = f'Saurav1/{OUTPUT_DIR}'\n",
|
| 676 |
+
"\n",
|
| 677 |
+
"grpo_cfg = GRPOConfig(\n",
|
| 678 |
+
" num_train_epochs = 2,\n",
|
| 679 |
+
" learning_rate = 1e-6,\n",
|
| 680 |
+
" gradient_accumulation_steps = GRAD_ACCUM,\n",
|
| 681 |
+
" per_device_train_batch_size = 1,\n",
|
| 682 |
+
" warmup_steps = 5,\n",
|
| 683 |
+
" num_generations = NUM_GEN,\n",
|
| 684 |
+
" # max_completion_length must match MAX_COMP_LEN so TRL's internal\n",
|
| 685 |
+
" # single-turn generation doesn't overflow and zero the policy loss\n",
|
| 686 |
+
" # (clipped_ratio=1.0 means all gradient is masked → loss=0.000).\n",
|
| 687 |
+
" max_completion_length = MAX_COMP_LEN,\n",
|
| 688 |
+
" max_prompt_length = 4096,\n",
|
| 689 |
+
" use_vllm = False,\n",
|
| 690 |
+
" output_dir = OUTPUT_DIR,\n",
|
| 691 |
+
" report_to = 'none',\n",
|
| 692 |
+
" logging_steps = 1,\n",
|
| 693 |
+
" save_steps = 20,\n",
|
| 694 |
+
" gradient_checkpointing = False,\n",
|
| 695 |
+
")\n",
|
| 696 |
+
"\n",
|
| 697 |
+
"eff_batch = grpo_cfg.per_device_train_batch_size * GRAD_ACCUM\n",
|
| 698 |
+
"total_steps = len(grpo_dataset) * NUM_GEN * grpo_cfg.num_train_epochs // eff_batch\n",
|
| 699 |
+
"print(f'GRPO: {len(grpo_dataset)} eps × {NUM_GEN} gen × {grpo_cfg.num_train_epochs} epochs → ~{total_steps} steps')\n",
|
| 700 |
+
"\n",
|
| 701 |
+
"trainer = PMOpsGRPOTrainer(\n",
|
| 702 |
+
" model = model,\n",
|
| 703 |
+
" processing_class = tokenizer,\n",
|
| 704 |
+
" reward_funcs = grpo_reward_func,\n",
|
| 705 |
+
" train_dataset = grpo_dataset,\n",
|
| 706 |
+
" args = grpo_cfg,\n",
|
| 707 |
+
" rollout_func = grpo_rollout_func,\n",
|
| 708 |
+
")\n",
|
| 709 |
+
"assert grpo_cfg.use_vllm is False\n",
|
| 710 |
+
"print(f'Trainer: {type(trainer).__name__} ready')"
|
| 711 |
+
]
|
| 712 |
},
|
| 713 |
{
|
| 714 |
"cell_type": "markdown",
|
|
|
|
| 720 |
},
|
| 721 |
{
|
| 722 |
"cell_type": "code",
|
| 723 |
+
"execution_count": null,
|
| 724 |
"id": "v4-probe",
|
| 725 |
"metadata": {},
|
|
|
|
| 726 |
"outputs": [],
|
| 727 |
"source": [
|
| 728 |
"# Run 2 episodes and verify reward varies (not stuck at a constant)\n",
|
|
|
|
| 751 |
},
|
| 752 |
{
|
| 753 |
"cell_type": "code",
|
| 754 |
+
"execution_count": null,
|
| 755 |
"id": "v4-train",
|
| 756 |
"metadata": {},
|
|
|
|
| 757 |
"outputs": [],
|
| 758 |
"source": [
|
| 759 |
"trainer_stats = trainer.train()\n",
|
|
|
|
| 774 |
},
|
| 775 |
{
|
| 776 |
"cell_type": "code",
|
| 777 |
+
"execution_count": null,
|
| 778 |
"id": "v4-save",
|
| 779 |
"metadata": {},
|
|
|
|
| 780 |
"outputs": [],
|
| 781 |
"source": [
|
| 782 |
"grpo_env.close()\n",
|
|
|
|
| 805 |
},
|
| 806 |
{
|
| 807 |
"cell_type": "code",
|
| 808 |
+
"execution_count": null,
|
| 809 |
"id": "v4-eval",
|
| 810 |
"metadata": {},
|
|
|
|
| 811 |
"outputs": [],
|
| 812 |
+
"source": "from training.rollout import extract_json_action, step_aware_fallback\nfrom inference import baseline_agent\n\nN_EVAL = 15\nEVAL_MAX_STEPS = 12\nEVAL_SEED_BASE = 9000\n\n# Required after training: switch Unsloth from training hooks to fast inference.\n# Without this, model.generate() is very slow and hangs on T4.\nFastLanguageModel.for_inference(model)\nmodel.eval()\nprint('Model switched to fast inference mode')\n\n\ndef _model_step(obs_dict, task_brief, history, step):\n \"\"\"One greedy decode step. Returns (action_type, args, json_ok).\"\"\"\n obs_text = _current_obs_text(obs_dict, step, task_brief)\n msgs = build_messages(history, obs_text)\n prompt = tokenizer.apply_chat_template(\n msgs, add_generation_prompt=True, tokenize=False, enable_thinking=False\n )\n inputs = tokenizer([prompt], return_tensors='pt', truncation=True, max_length=4096)\n inputs = {k: v.to(model.device) for k, v in inputs.items()}\n with torch.no_grad():\n out_ids = model.generate(\n **inputs,\n max_new_tokens = 128,\n do_sample = False,\n pad_token_id = tokenizer.eos_token_id,\n )\n completion = tokenizer.decode(\n out_ids[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True\n )\n parsed = extract_json_action(completion)\n json_ok = parsed is not None\n if not json_ok:\n parsed = step_aware_fallback(step, EVAL_MAX_STEPS)\n history.append({'obs_text': obs_text, 'completion': completion, 'is_runbook': False})\n return parsed.get('action_type', 'meta.noop'), parsed.get('args', {}), json_ok, obs_text\n\n\ndef run_eval(n=N_EVAL, verbose=False):\n scores = []\n with GenericEnvClient(base_url=ENV_URL).sync() as env:\n for i in range(n):\n result = env.reset(seed=EVAL_SEED_BASE + i)\n obs_dict = _obs_to_dict(result.observation if hasattr(result, 'observation') else result)\n task_brief = obs_dict.get('task_brief', '')\n history, step, score, done = [], 0, 0.0, False\n actions = []\n\n while not done and step < EVAL_MAX_STEPS:\n action_type, args, json_ok, _ = _model_step(obs_dict, task_brief, history, step)\n actions.append(f'{action_type}{\"\" if json_ok else \"!\"}')\n result = env.step({'action_type': action_type, 'args': args})\n obs_dict = _obs_to_dict(result.observation if hasattr(result, 'observation') else result)\n done = bool(getattr(result, 'done', obs_dict.get('done', False)))\n score = float(getattr(result, 'reward', obs_dict.get('reward', 0.0)))\n step += 1\n\n scores.append(score)\n action_summary = ' → '.join(a.split('.')[-1] for a in actions)\n print(f' Trained ep {i+1}/{n}: score={score:.3f} [{action_summary}]')\n return scores\n\n\ndef run_baseline(n=N_EVAL):\n scores = []\n with GenericEnvClient(base_url=ENV_URL).sync() as env:\n for i in range(n):\n result = env.reset(seed=EVAL_SEED_BASE + i)\n obs_dict = _obs_to_dict(result.observation if hasattr(result, 'observation') else result)\n org_config, step, score, done = {}, 0, 0.0, False\n actions = []\n while not done and step < EVAL_MAX_STEPS:\n at, args = baseline_agent(obs_dict, org_config)\n actions.append(at.split('.')[-1])\n result = env.step({'action_type': at, 'args': args})\n obs_dict = _obs_to_dict(result.observation if hasattr(result, 'observation') else result)\n done = bool(getattr(result, 'done', obs_dict.get('done', False)))\n score = float(getattr(result, 'reward', obs_dict.get('reward', 0.0)))\n if at == 'meta.read_runbook':\n last = obs_dict.get('last_action_result') or {}\n if last.get('ok'):\n data = last.get('data') or {}\n if isinstance(data, dict) and 'org_config' in data:\n org_config.update(data['org_config'])\n step += 1\n scores.append(score)\n action_summary = ' → '.join(actions)\n print(f' Baseline ep {i+1}/{n}: score={score:.3f} [{action_summary}]')\n return scores\n\n\nprint('--- Baseline ---')\nbaseline_scores = run_baseline()\nprint('\\n--- Trained ---')\ntrained_scores = run_eval()\n\nb_avg = sum(baseline_scores) / N_EVAL\nt_avg = sum(trained_scores) / N_EVAL\nprint(f'\\nBaseline avg : {b_avg:.3f}')\nprint(f'Trained avg : {t_avg:.3f}')\nprint(f'Delta : {t_avg - b_avg:+.3f}')\nprint()\nprint('Action key: ! = fallback (no valid JSON from model)')"
|
| 813 |
},
|
| 814 |
{
|
| 815 |
"cell_type": "markdown",
|
|
|
|
| 821 |
},
|
| 822 |
{
|
| 823 |
"cell_type": "code",
|
| 824 |
+
"execution_count": null,
|
| 825 |
"id": "v4-teardown",
|
| 826 |
"metadata": {},
|
|
|
|
| 827 |
"outputs": [],
|
| 828 |
"source": [
|
| 829 |
"server_proc.terminate()\n",
|
| 830 |
"print('PM-Ops server stopped')"
|
| 831 |
]
|
| 832 |
}
|
| 833 |
+
],
|
| 834 |
+
"metadata": {
|
| 835 |
+
"kernelspec": {
|
| 836 |
+
"display_name": "Python 3",
|
| 837 |
+
"language": "python",
|
| 838 |
+
"name": "python3"
|
| 839 |
+
},
|
| 840 |
+
"language_info": {
|
| 841 |
+
"name": "python",
|
| 842 |
+
"version": "3.12.0"
|
| 843 |
+
}
|
| 844 |
+
},
|
| 845 |
+
"nbformat": 4,
|
| 846 |
+
"nbformat_minor": 5
|
| 847 |
}
|