self-evolve-framework 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/rules/ponytail.mdc +98 -23
- package/template/skills/skillopt-sleep/SKILL.md +42 -0
- package/template/skills/skillopt-sleep/configs/_base_/default.yaml +103 -0
- package/template/skills/skillopt-sleep/configs/alfworld/default.yaml +29 -0
- package/template/skills/skillopt-sleep/configs/docvqa/default.yaml +28 -0
- package/template/skills/skillopt-sleep/configs/features/soft_gate.yaml +47 -0
- package/template/skills/skillopt-sleep/configs/livemathematicianbench/default.yaml +22 -0
- package/template/skills/skillopt-sleep/configs/officeqa/default.yaml +34 -0
- package/template/skills/skillopt-sleep/configs/searchqa/default.yaml +32 -0
- package/template/skills/skillopt-sleep/configs/spreadsheetbench/default.yaml +34 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/__init__.py +28 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/config.py +282 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/__init__.py +7 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/datasets/base.py +512 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/__init__.py +9 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/engine/trainer.py +2379 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/__init__.py +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/README.md +43 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/config_template.yaml +55 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/env_template.py +151 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/_template/loader_template.py +87 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/__init__.py +5 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/adapter.py +428 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/dataloader.py +123 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_error.md +55 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/analyst_success.md +33 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_no_history.md +8 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_history.md +9 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/prompts/rollout_with_memory.md +16 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/reflect.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/rollout.py +366 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/skills/initial.md +45 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/__init__.py +9 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_envs.py +221 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_projection.py +60 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/alfworld_prompts.py +8 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/config_tw.yaml +145 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_base.py +84 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/env_manager.py +139 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/alfworld/vendor/memory.py +87 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/base.py +329 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/__init__.py +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/adapter.py +90 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/dataloader.py +61 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/evaluator.py +113 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_error.md +35 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/analyst_success.md +24 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/prompts/rollout_system.md +12 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/rollout.py +391 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/docvqa/skills/initial.md +11 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/__init__.py +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/adapter.py +129 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/dataloader.py +308 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/evaluator.py +62 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_error.md +37 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/analyst_success.md +25 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/prompts/rollout_system.md +12 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/reflect.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/rollout.py +434 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/livemathematicianbench/skills/initial.md +16 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/__init__.py +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/adapter.py +112 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/dataloader.py +71 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/evaluator.py +46 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_error.md +37 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/analyst_success.md +25 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/prompts/rollout_system.md +15 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/rollout.py +799 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/skills/initial.md +15 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/officeqa/tool_runtime.py +552 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/__init__.py +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/adapter.py +96 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/dataloader.py +42 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/evaluator.py +100 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_error.md +46 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/analyst_success.md +32 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/prompts/rollout_system.md +13 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/reflect.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/rollout.py +494 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/searchqa/skills/initial.md +3 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/__init__.py +5 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/adapter.py +159 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/codegen_agent.py +731 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/dataloader.py +37 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/evaluator.py +158 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/executor.py +67 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_error.md +46 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/analyst_success.md +32 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/codegen_system.md +1 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/critical_rules.md +9 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/prompts/react_system.md +21 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/react_agent.py +395 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/reflect.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/rollout.py +979 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/envs/spreadsheetbench/skills/initial.md +56 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/__init__.py +13 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/evaluation/gate.py +148 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/__init__.py +15 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/aggregate.py +253 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/gradient/reflect.py +635 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/__init__.py +514 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/azure_openai.py +915 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/backend_config.py +185 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/claude_backend.py +371 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_backend.py +666 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/codex_harness.py +1057 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/common.py +229 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/minimax_backend.py +277 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/qwen_backend.py +456 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/model/router.py +236 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/__init__.py +15 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/appendix.py +156 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/clip.py +109 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/lr_autonomous.py +108 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/meta_skill.py +79 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/rewrite.py +59 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/scheduler.py +127 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/select.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill.py +201 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/skill_aware.py +206 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/slow_update.py +396 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/optimizer/update_modes.py +135 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/__init__.py +63 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error.md +41 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_full_rewrite.md +32 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_error_rewrite.md +44 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success.md +36 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_full_rewrite.md +30 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/analyst_success_rewrite.md +33 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/lr_autonomous.md +20 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure.md +30 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_full_rewrite.md +28 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_failure_rewrite.md +26 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final.md +33 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_full_rewrite.md +28 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_final_rewrite.md +25 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success.md +28 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_full_rewrite.md +28 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/merge_success_rewrite.md +25 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/meta_skill.md +40 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking.md +20 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/ranking_rewrite.md +15 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/rewrite_skill.md +25 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/prompts/slow_update.md +60 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/scheduler/__init__.py +8 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/types.py +306 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/__init__.py +4 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/json_utils.py +172 -0
- package/template/skills/skillopt-sleep/scripts/framework/skillopt/utils/scoring.py +28 -0
- package/template/skills/ponytail/SKILL.md +0 -133
- package/template/skills/ponytail/scripts/hooks/claude-codex-hooks.json +0 -44
- package/template/skills/ponytail/scripts/hooks/copilot-hooks.json +0 -21
- package/template/skills/ponytail/scripts/hooks/ponytail-activate.js +0 -91
- package/template/skills/ponytail/scripts/hooks/ponytail-config.js +0 -122
- package/template/skills/ponytail/scripts/hooks/ponytail-instructions.js +0 -94
- package/template/skills/ponytail/scripts/hooks/ponytail-mode-tracker.js +0 -55
- package/template/skills/ponytail/scripts/hooks/ponytail-runtime.js +0 -68
- package/template/skills/ponytail/scripts/hooks/ponytail-statusline.ps1 +0 -21
- package/template/skills/ponytail/scripts/hooks/ponytail-statusline.sh +0 -12
- package/template/skills/ponytail/scripts/hooks/ponytail-subagent.js +0 -22
- package/template/skills/ponytail/scripts/mcp/README.md +0 -46
- package/template/skills/ponytail/scripts/mcp/index.js +0 -48
- package/template/skills/ponytail/scripts/mcp/instructions.js +0 -26
- package/template/skills/ponytail/scripts/mcp/package.json +0 -13
- package/template/skills/ponytail/scripts/mcp/test/instructions.test.js +0 -22
- package/template/skills/ponytail-audit/SKILL.md +0 -41
- package/template/skills/ponytail-debt/SKILL.md +0 -44
- package/template/skills/ponytail-gain/SKILL.md +0 -50
- package/template/skills/ponytail-help/SKILL.md +0 -69
- package/template/skills/ponytail-review/SKILL.md +0 -57
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""ReflACT Slow Update — epoch-level longitudinal skill refinement.
|
|
2
|
+
|
|
3
|
+
At the end of each epoch, the slow update compares rollout performance of the
|
|
4
|
+
same sample set under the previous epoch's skill vs. the current epoch's skill
|
|
5
|
+
(Markov: only adjacent epochs). A optimizer analyzes regressions, improvements,
|
|
6
|
+
and persistent failures, then writes a free-form guidance block into a
|
|
7
|
+
**protected** section of the skill document. This section cannot be modified by
|
|
8
|
+
step-level analyst edits — only the slow update process overwrites it.
|
|
9
|
+
|
|
10
|
+
Public API
|
|
11
|
+
----------
|
|
12
|
+
- :func:`inject_empty_slow_update_field` — add empty placeholder (epoch 1)
|
|
13
|
+
- :func:`extract_slow_update_field` — read current content
|
|
14
|
+
- :func:`replace_slow_update_field` — overwrite content
|
|
15
|
+
- :func:`has_slow_update_field` — check if markers are present
|
|
16
|
+
- :func:`build_comparison_text` — format side-by-side rollout results
|
|
17
|
+
- :func:`run_slow_update` — optimizer call to produce guidance
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import traceback
|
|
24
|
+
|
|
25
|
+
from skillopt.model import chat_optimizer
|
|
26
|
+
from skillopt.prompts import load_prompt
|
|
27
|
+
from skillopt.utils import extract_json
|
|
28
|
+
|
|
29
|
+
# ── Protected field markers ─────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
SLOW_UPDATE_START = "<!-- SLOW_UPDATE_START -->"
|
|
32
|
+
SLOW_UPDATE_END = "<!-- SLOW_UPDATE_END -->"
|
|
33
|
+
|
|
34
|
+
# ── Field manipulation helpers ──────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def has_slow_update_field(skill: str) -> bool:
|
|
38
|
+
return SLOW_UPDATE_START in skill and SLOW_UPDATE_END in skill
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def inject_empty_slow_update_field(skill: str) -> str:
|
|
42
|
+
if has_slow_update_field(skill):
|
|
43
|
+
return skill
|
|
44
|
+
block = (
|
|
45
|
+
f"\n\n{SLOW_UPDATE_START}\n"
|
|
46
|
+
f"{SLOW_UPDATE_END}\n"
|
|
47
|
+
)
|
|
48
|
+
return skill.rstrip() + block
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def extract_slow_update_field(skill: str) -> str:
|
|
52
|
+
start = skill.find(SLOW_UPDATE_START)
|
|
53
|
+
end = skill.find(SLOW_UPDATE_END)
|
|
54
|
+
if start == -1 or end == -1:
|
|
55
|
+
return ""
|
|
56
|
+
inner_start = start + len(SLOW_UPDATE_START)
|
|
57
|
+
return skill[inner_start:end].strip()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _strip_all_slow_update_fields(skill: str) -> str:
|
|
61
|
+
"""Remove every SLOW_UPDATE_START/END pair (and content between) from *skill*."""
|
|
62
|
+
while True:
|
|
63
|
+
start = skill.find(SLOW_UPDATE_START)
|
|
64
|
+
if start == -1:
|
|
65
|
+
break
|
|
66
|
+
end = skill.find(SLOW_UPDATE_END, start)
|
|
67
|
+
if end == -1:
|
|
68
|
+
# Orphan start marker — remove it
|
|
69
|
+
skill = skill[:start] + skill[start + len(SLOW_UPDATE_START):]
|
|
70
|
+
break
|
|
71
|
+
skill = skill[:start] + skill[end + len(SLOW_UPDATE_END):]
|
|
72
|
+
# Clean up stray end markers
|
|
73
|
+
skill = skill.replace(SLOW_UPDATE_END, "")
|
|
74
|
+
# Collapse excess blank lines left behind
|
|
75
|
+
while "\n\n\n" in skill:
|
|
76
|
+
skill = skill.replace("\n\n\n", "\n\n")
|
|
77
|
+
return skill.rstrip()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def replace_slow_update_field(skill: str, new_content: str) -> str:
|
|
81
|
+
# Remove all existing slow update regions first to guarantee exactly one.
|
|
82
|
+
skill = _strip_all_slow_update_fields(skill)
|
|
83
|
+
block = (
|
|
84
|
+
f"\n\n{SLOW_UPDATE_START}\n"
|
|
85
|
+
f"{new_content.strip()}\n"
|
|
86
|
+
f"{SLOW_UPDATE_END}\n"
|
|
87
|
+
)
|
|
88
|
+
return skill + block
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ── Comparison text builder ─────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# NOTE: Character-length limits on the comparison samples fed to the slow-update /
|
|
95
|
+
# meta-skill optimizer have been REMOVED. Previously a whole-trajectory cap plus
|
|
96
|
+
# per-field caps (cmd/obs/reasoning/etc.) and comparison-metadata caps
|
|
97
|
+
# (task/answer/fail_reason) trimmed this context to save optimizer tokens and
|
|
98
|
+
# speed up the call. They never affected what gets written into the skill — only
|
|
99
|
+
# how much longitudinal context the optimizer sees. We now pass everything through
|
|
100
|
+
# at full length: the comparison input is as long as the source data is.
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _clip_text(value, limit: int | None = None) -> str:
|
|
104
|
+
# Truncation disabled: return the full text. The `limit` argument is kept only
|
|
105
|
+
# for call-site compatibility and is intentionally ignored (see NOTE above).
|
|
106
|
+
if value is None:
|
|
107
|
+
return ""
|
|
108
|
+
return str(value)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _read_trajectory(rollout_dir: str, task_id: str) -> str:
|
|
112
|
+
"""Read and format a single trajectory from a rollout directory."""
|
|
113
|
+
conv_path = os.path.join(rollout_dir, "predictions", task_id, "conversation.json")
|
|
114
|
+
if not os.path.exists(conv_path):
|
|
115
|
+
return "(trajectory not available)"
|
|
116
|
+
try:
|
|
117
|
+
with open(conv_path) as f:
|
|
118
|
+
conversation = json.load(f)
|
|
119
|
+
except Exception:
|
|
120
|
+
return "(trajectory read error)"
|
|
121
|
+
if not conversation:
|
|
122
|
+
return "(empty trajectory)"
|
|
123
|
+
|
|
124
|
+
lines: list[str] = []
|
|
125
|
+
for entry in conversation:
|
|
126
|
+
if not isinstance(entry, dict):
|
|
127
|
+
continue
|
|
128
|
+
# Per-field truncation removed: feed each step's full cmd/obs/reasoning/
|
|
129
|
+
# action/feedback/content (see NOTE above).
|
|
130
|
+
if entry.get("type") == "tool_call":
|
|
131
|
+
cmd = _clip_text(entry.get("cmd"))
|
|
132
|
+
obs = _clip_text(entry.get("obs"))
|
|
133
|
+
lines.append(f"[action] {cmd}")
|
|
134
|
+
lines.append(f"[obs] {obs}")
|
|
135
|
+
elif "action" in entry and "env_feedback" in entry:
|
|
136
|
+
step = entry.get("step", "?")
|
|
137
|
+
reasoning = _clip_text(entry.get("reasoning"))
|
|
138
|
+
action = _clip_text(entry.get("action"))
|
|
139
|
+
feedback = _clip_text(entry.get("env_feedback"))
|
|
140
|
+
if reasoning:
|
|
141
|
+
lines.append(f"[step {step} think] {reasoning}")
|
|
142
|
+
lines.append(f"[step {step} action] {action}")
|
|
143
|
+
lines.append(f"[step {step} obs] {feedback}")
|
|
144
|
+
elif entry.get("role") == "system":
|
|
145
|
+
msg = _clip_text(entry.get("content"))
|
|
146
|
+
lines.append(f"[verification] {msg}")
|
|
147
|
+
else:
|
|
148
|
+
msg = _clip_text(entry.get("content"))
|
|
149
|
+
role = entry.get("role", "agent")
|
|
150
|
+
lines.append(f"[{role}] {msg}")
|
|
151
|
+
|
|
152
|
+
# Whole-trajectory truncation removed: return the full formatted trajectory.
|
|
153
|
+
return "\n".join(lines)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ── Structured comparison pairs ─────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def build_comparison_pairs(
|
|
160
|
+
results_prev: list[dict],
|
|
161
|
+
results_curr: list[dict],
|
|
162
|
+
items: list[dict],
|
|
163
|
+
prev_rollout_dir: str = "",
|
|
164
|
+
curr_rollout_dir: str = "",
|
|
165
|
+
) -> list[dict]:
|
|
166
|
+
"""Build a structured list of per-sample comparison entries.
|
|
167
|
+
|
|
168
|
+
Each entry bundles the original item, both rollout results, the change
|
|
169
|
+
category, and both trajectories into one dict — the single source of
|
|
170
|
+
truth for this sample's longitudinal comparison.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
list[dict]
|
|
175
|
+
One dict per sample with keys:
|
|
176
|
+
``id, task, category, prev, curr, prev_trajectory, curr_trajectory``
|
|
177
|
+
"""
|
|
178
|
+
prev_by_id = {str(r["id"]): r for r in results_prev}
|
|
179
|
+
curr_by_id = {str(r["id"]): r for r in results_curr}
|
|
180
|
+
|
|
181
|
+
pairs: list[dict] = []
|
|
182
|
+
for item in items:
|
|
183
|
+
tid = str(item.get("id", ""))
|
|
184
|
+
prev = prev_by_id.get(tid, {})
|
|
185
|
+
curr = curr_by_id.get(tid, {})
|
|
186
|
+
prev_ok = bool(prev.get("hard", 0))
|
|
187
|
+
curr_ok = bool(curr.get("hard", 0))
|
|
188
|
+
|
|
189
|
+
if not prev_ok and curr_ok:
|
|
190
|
+
category = "improved"
|
|
191
|
+
elif prev_ok and not curr_ok:
|
|
192
|
+
category = "regressed"
|
|
193
|
+
elif not prev_ok and not curr_ok:
|
|
194
|
+
category = "persistent_fail"
|
|
195
|
+
else:
|
|
196
|
+
category = "stable_success"
|
|
197
|
+
|
|
198
|
+
pairs.append({
|
|
199
|
+
"id": tid,
|
|
200
|
+
"task": item.get("question", item.get("task_description", item.get("instruction", tid))),
|
|
201
|
+
"category": category,
|
|
202
|
+
"prev": {
|
|
203
|
+
"hard": int(prev_ok),
|
|
204
|
+
"soft": float(prev.get("soft", 0.0)),
|
|
205
|
+
"predicted_answer": prev.get("predicted_answer", prev.get("answer", "N/A")),
|
|
206
|
+
"fail_reason": prev.get("fail_reason", ""),
|
|
207
|
+
},
|
|
208
|
+
"curr": {
|
|
209
|
+
"hard": int(curr_ok),
|
|
210
|
+
"soft": float(curr.get("soft", 0.0)),
|
|
211
|
+
"predicted_answer": curr.get("predicted_answer", curr.get("answer", "N/A")),
|
|
212
|
+
"fail_reason": curr.get("fail_reason", ""),
|
|
213
|
+
},
|
|
214
|
+
"prev_trajectory": (
|
|
215
|
+
_read_trajectory(prev_rollout_dir, tid) if prev_rollout_dir else ""
|
|
216
|
+
),
|
|
217
|
+
"curr_trajectory": (
|
|
218
|
+
_read_trajectory(curr_rollout_dir, tid) if curr_rollout_dir else ""
|
|
219
|
+
),
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
return pairs
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def save_comparison_pairs(pairs: list[dict], out_path: str) -> None:
|
|
226
|
+
"""Persist comparison pairs to JSON (without trajectory text to save space)."""
|
|
227
|
+
slim = []
|
|
228
|
+
for p in pairs:
|
|
229
|
+
slim.append({
|
|
230
|
+
"id": p["id"],
|
|
231
|
+
"task": p["task"],
|
|
232
|
+
"category": p["category"],
|
|
233
|
+
"prev": p["prev"],
|
|
234
|
+
"curr": p["curr"],
|
|
235
|
+
})
|
|
236
|
+
with open(out_path, "w") as f:
|
|
237
|
+
json.dump(slim, f, ensure_ascii=False, indent=2)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def format_comparison_text(pairs: list[dict]) -> str:
|
|
241
|
+
"""Format structured comparison pairs into optimizer-readable text."""
|
|
242
|
+
by_cat: dict[str, list[dict]] = {
|
|
243
|
+
"regressed": [],
|
|
244
|
+
"persistent_fail": [],
|
|
245
|
+
"improved": [],
|
|
246
|
+
"stable_success": [],
|
|
247
|
+
}
|
|
248
|
+
for p in pairs:
|
|
249
|
+
by_cat.setdefault(p["category"], []).append(p)
|
|
250
|
+
|
|
251
|
+
total = len(pairs)
|
|
252
|
+
parts = [
|
|
253
|
+
f"## Longitudinal Comparison Summary\n"
|
|
254
|
+
f"Total samples: {total}\n"
|
|
255
|
+
f"- Improved (wrong→right): {len(by_cat['improved'])}\n"
|
|
256
|
+
f"- Regressed (right→wrong): {len(by_cat['regressed'])}\n"
|
|
257
|
+
f"- Persistent failures (wrong→wrong): {len(by_cat['persistent_fail'])}\n"
|
|
258
|
+
f"- Stable successes (right→right): {len(by_cat['stable_success'])}\n"
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
categories = [
|
|
262
|
+
("regressed", "Regressions (right→wrong) — HIGHEST PRIORITY", True),
|
|
263
|
+
("persistent_fail", "Persistent Failures (wrong→wrong)", True),
|
|
264
|
+
("improved", "Improvements (wrong→right)", True),
|
|
265
|
+
("stable_success", "Stable Successes (right→right)", False),
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
for cat_key, label, show_traj in categories:
|
|
269
|
+
entries = by_cat[cat_key]
|
|
270
|
+
if not entries:
|
|
271
|
+
parts.append(f"### {label}\n(none)\n")
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
lines = [f"### {label}"]
|
|
275
|
+
for e in entries:
|
|
276
|
+
prev = e["prev"]
|
|
277
|
+
curr = e["curr"]
|
|
278
|
+
lines.append(
|
|
279
|
+
f"\n#### Task {e['id']}: {e['task']}\n"
|
|
280
|
+
f"- Prev epoch: {'PASS' if prev['hard'] else 'FAIL'} "
|
|
281
|
+
f"(soft={prev['soft']:.2f}) — answer: {str(prev['predicted_answer'])}\n"
|
|
282
|
+
f"- Curr epoch: {'PASS' if curr['hard'] else 'FAIL'} "
|
|
283
|
+
f"(soft={curr['soft']:.2f}) — answer: {str(curr['predicted_answer'])}"
|
|
284
|
+
)
|
|
285
|
+
if curr.get("fail_reason"):
|
|
286
|
+
lines.append(f"- Curr fail reason: {curr['fail_reason']}")
|
|
287
|
+
if prev.get("fail_reason") and not prev["hard"]:
|
|
288
|
+
lines.append(f"- Prev fail reason: {prev['fail_reason']}")
|
|
289
|
+
|
|
290
|
+
if show_traj:
|
|
291
|
+
if e.get("prev_trajectory"):
|
|
292
|
+
lines.append(
|
|
293
|
+
f"\n**Previous epoch trajectory:**\n```\n{e['prev_trajectory']}\n```"
|
|
294
|
+
)
|
|
295
|
+
if e.get("curr_trajectory"):
|
|
296
|
+
lines.append(
|
|
297
|
+
f"\n**Current epoch trajectory:**\n```\n{e['curr_trajectory']}\n```"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
parts.append("\n".join(lines))
|
|
301
|
+
|
|
302
|
+
return "\n\n".join(parts)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ── Optimizer call ────────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def run_slow_update(
|
|
310
|
+
skill_content: str,
|
|
311
|
+
results_prev: list[dict],
|
|
312
|
+
results_curr: list[dict],
|
|
313
|
+
items: list[dict],
|
|
314
|
+
*,
|
|
315
|
+
prev_skill: str = "",
|
|
316
|
+
prev_slow_update_content: str = "",
|
|
317
|
+
prev_rollout_dir: str = "",
|
|
318
|
+
curr_rollout_dir: str = "",
|
|
319
|
+
comparison_pairs: list[dict] | None = None,
|
|
320
|
+
system_prompt: str | None = None,
|
|
321
|
+
) -> dict | None:
|
|
322
|
+
"""Run the slow update optimizer call for one epoch boundary.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
skill_content : str
|
|
327
|
+
Current epoch's skill (after fast updates).
|
|
328
|
+
results_prev : list[dict]
|
|
329
|
+
Rollout results of the 20 samples under previous epoch's skill.
|
|
330
|
+
results_curr : list[dict]
|
|
331
|
+
Rollout results of the 20 samples under current epoch's skill.
|
|
332
|
+
items : list[dict]
|
|
333
|
+
The 20 sample items used for comparison.
|
|
334
|
+
prev_skill : str
|
|
335
|
+
Previous epoch's skill content.
|
|
336
|
+
prev_slow_update_content : str
|
|
337
|
+
The slow update guidance from the previous epoch (to reflect on).
|
|
338
|
+
prev_rollout_dir : str
|
|
339
|
+
Path to previous epoch rollout output (contains predictions/).
|
|
340
|
+
curr_rollout_dir : str
|
|
341
|
+
Path to current epoch rollout output (contains predictions/).
|
|
342
|
+
system_prompt : str | None
|
|
343
|
+
Custom system prompt override.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
dict | None
|
|
348
|
+
Conforms to :class:`~skillopt.types.SlowUpdateResult`:
|
|
349
|
+
``{"reasoning": str, "slow_update_content": str}`` or ``None``.
|
|
350
|
+
"""
|
|
351
|
+
actual_system = system_prompt if system_prompt is not None else load_prompt("slow_update")
|
|
352
|
+
|
|
353
|
+
pairs = comparison_pairs
|
|
354
|
+
if pairs is None:
|
|
355
|
+
pairs = build_comparison_pairs(
|
|
356
|
+
results_prev, results_curr, items,
|
|
357
|
+
prev_rollout_dir=prev_rollout_dir,
|
|
358
|
+
curr_rollout_dir=curr_rollout_dir,
|
|
359
|
+
)
|
|
360
|
+
comparison_text = format_comparison_text(pairs)
|
|
361
|
+
|
|
362
|
+
prev_guidance_section = (
|
|
363
|
+
prev_slow_update_content.strip()
|
|
364
|
+
if prev_slow_update_content and prev_slow_update_content.strip()
|
|
365
|
+
else "(No previous guidance — this is the first slow update.)"
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
user = (
|
|
369
|
+
f"## Previous Epoch's Skill\n{prev_skill}\n\n"
|
|
370
|
+
f"## Current Epoch's Skill\n{skill_content}\n\n"
|
|
371
|
+
f"## Previous Slow Update Guidance\n"
|
|
372
|
+
f"The following guidance was active during the current epoch. "
|
|
373
|
+
f"Reflect on its effectiveness before writing the new version.\n\n"
|
|
374
|
+
f"{prev_guidance_section}\n\n"
|
|
375
|
+
f"## Longitudinal Comparison (same 20 tasks, two skill versions)\n"
|
|
376
|
+
f"{comparison_text}"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
response, _ = chat_optimizer(
|
|
381
|
+
system=actual_system,
|
|
382
|
+
user=user,
|
|
383
|
+
max_completion_tokens=16384,
|
|
384
|
+
retries=3,
|
|
385
|
+
stage="slow_update",
|
|
386
|
+
)
|
|
387
|
+
result = extract_json(response)
|
|
388
|
+
if result and result.get("slow_update_content"):
|
|
389
|
+
return {
|
|
390
|
+
"reasoning": str(result.get("reasoning", "")).strip(),
|
|
391
|
+
"slow_update_content": str(result["slow_update_content"]).strip(),
|
|
392
|
+
}
|
|
393
|
+
except Exception: # noqa: BLE001
|
|
394
|
+
traceback.print_exc()
|
|
395
|
+
|
|
396
|
+
return None
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Helpers for switching between patch edits and rewrite-from-suggestions."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
PATCH_MODE = "patch"
|
|
7
|
+
REWRITE_MODE = "rewrite_from_suggestions"
|
|
8
|
+
FULL_REWRITE_MINIBATCH_MODE = "full_rewrite_minibatch"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def normalize_update_mode(mode: str | None) -> str:
|
|
12
|
+
raw = str(mode or PATCH_MODE).strip().lower()
|
|
13
|
+
aliases = {
|
|
14
|
+
"patch": PATCH_MODE,
|
|
15
|
+
"edits": PATCH_MODE,
|
|
16
|
+
"rewrite": REWRITE_MODE,
|
|
17
|
+
"rewrite_from_suggestions": REWRITE_MODE,
|
|
18
|
+
"suggestions": REWRITE_MODE,
|
|
19
|
+
"rewrite_suggestions": REWRITE_MODE,
|
|
20
|
+
"full_rewrite": FULL_REWRITE_MINIBATCH_MODE,
|
|
21
|
+
"full_rewrite_minibatch": FULL_REWRITE_MINIBATCH_MODE,
|
|
22
|
+
"minibatch_full_rewrite": FULL_REWRITE_MINIBATCH_MODE,
|
|
23
|
+
"skill_rewrite_minibatch": FULL_REWRITE_MINIBATCH_MODE,
|
|
24
|
+
}
|
|
25
|
+
return aliases.get(raw, PATCH_MODE)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_rewrite_mode(mode: str | None) -> bool:
|
|
29
|
+
return normalize_update_mode(mode) == REWRITE_MODE
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_full_rewrite_minibatch_mode(mode: str | None) -> bool:
|
|
33
|
+
return normalize_update_mode(mode) == FULL_REWRITE_MINIBATCH_MODE
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def payload_key(mode: str | None) -> str:
|
|
37
|
+
if is_full_rewrite_minibatch_mode(mode):
|
|
38
|
+
return "skill_candidates"
|
|
39
|
+
return "revise_suggestions" if is_rewrite_mode(mode) else "edits"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def payload_label(mode: str | None, *, singular: bool = False, title: bool = False) -> str:
|
|
43
|
+
if is_full_rewrite_minibatch_mode(mode):
|
|
44
|
+
word = "skill candidate" if singular else "skill candidates"
|
|
45
|
+
elif is_rewrite_mode(mode):
|
|
46
|
+
word = "suggestion" if singular else "suggestions"
|
|
47
|
+
else:
|
|
48
|
+
word = "edit" if singular else "edits"
|
|
49
|
+
return word.title() if title else word
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_payload_items(container: dict | None, mode: str | None) -> list[dict]:
|
|
53
|
+
if not isinstance(container, dict):
|
|
54
|
+
return []
|
|
55
|
+
items = container.get(payload_key(mode), [])
|
|
56
|
+
return items if isinstance(items, list) else []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def set_payload_items(container: dict, items: list[dict], mode: str | None) -> dict:
|
|
60
|
+
container[payload_key(mode)] = items
|
|
61
|
+
return container
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def truncate_payload(container: dict, max_items: int, mode: str | None) -> dict:
|
|
65
|
+
if max_items < 0:
|
|
66
|
+
return container
|
|
67
|
+
items = get_payload_items(container, mode)
|
|
68
|
+
if len(items) > max_items:
|
|
69
|
+
set_payload_items(container, items[:max_items], mode)
|
|
70
|
+
return container
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def describe_item(item: dict, mode: str | None, *, max_chars: int | None = None) -> str:
|
|
74
|
+
if not isinstance(item, dict):
|
|
75
|
+
return ""
|
|
76
|
+
if is_full_rewrite_minibatch_mode(mode):
|
|
77
|
+
parts = [
|
|
78
|
+
f"title={item.get('title', '')!r}",
|
|
79
|
+
f"change_summary={item.get('change_summary', [])!r}",
|
|
80
|
+
]
|
|
81
|
+
if item.get("source_type"):
|
|
82
|
+
parts.append(f"source={item.get('source_type')}")
|
|
83
|
+
if item.get("support_count") is not None:
|
|
84
|
+
parts.append(f"support={item.get('support_count')}")
|
|
85
|
+
new_skill = str(item.get("new_skill", "")).strip()
|
|
86
|
+
if new_skill:
|
|
87
|
+
parts.append(f"new_skill_preview={new_skill!r}")
|
|
88
|
+
text = " ".join(parts)
|
|
89
|
+
elif is_rewrite_mode(mode):
|
|
90
|
+
parts = [
|
|
91
|
+
f"type={item.get('type', '?')}",
|
|
92
|
+
f"title={item.get('title', '')!r}",
|
|
93
|
+
f"instruction={item.get('instruction', '')!r}",
|
|
94
|
+
]
|
|
95
|
+
if item.get("priority_hint"):
|
|
96
|
+
parts.append(f"priority={item.get('priority_hint')}")
|
|
97
|
+
if item.get("support_count") is not None:
|
|
98
|
+
parts.append(f"support={item.get('support_count')}")
|
|
99
|
+
text = " ".join(parts)
|
|
100
|
+
else:
|
|
101
|
+
op = item.get("op", "?")
|
|
102
|
+
target = item.get("target", "")
|
|
103
|
+
content = item.get("content", "")
|
|
104
|
+
parts = [f"op={op}"]
|
|
105
|
+
if target:
|
|
106
|
+
parts.append(f"target={target!r}")
|
|
107
|
+
if content:
|
|
108
|
+
parts.append(f"content={content!r}")
|
|
109
|
+
if item.get("support_count") is not None:
|
|
110
|
+
parts.append(f"support={item.get('support_count')}")
|
|
111
|
+
text = " ".join(parts)
|
|
112
|
+
# Truncation disabled: the optimizer is given the full item description.
|
|
113
|
+
return text
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def short_item_summary(item: dict, mode: str | None, *, max_chars: int | None = None) -> dict[str, Any]:
|
|
117
|
+
if is_full_rewrite_minibatch_mode(mode):
|
|
118
|
+
return {
|
|
119
|
+
"title": str(item.get("title", "")),
|
|
120
|
+
"change_summary": [
|
|
121
|
+
str(x) for x in item.get("change_summary", [])
|
|
122
|
+
] if isinstance(item.get("change_summary"), list) else [],
|
|
123
|
+
"source_type": item.get("source_type", ""),
|
|
124
|
+
}
|
|
125
|
+
if is_rewrite_mode(mode):
|
|
126
|
+
return {
|
|
127
|
+
"type": item.get("type", "?"),
|
|
128
|
+
"title": str(item.get("title", "")),
|
|
129
|
+
"instruction": str(item.get("instruction", "")),
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
"op": item.get("op", "?"),
|
|
133
|
+
"content": str(item.get("content", "")),
|
|
134
|
+
"target": item.get("target", ""),
|
|
135
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Prompt loading utilities for ReflACT.
|
|
2
|
+
|
|
3
|
+
Prompts are stored as ``.md`` files and loaded at runtime:
|
|
4
|
+
|
|
5
|
+
- **Generic** prompts live in ``skillopt/prompts/*.md``
|
|
6
|
+
- **Env-specific** prompts live in ``skillopt/envs/<env>/prompts/*.md``
|
|
7
|
+
|
|
8
|
+
``load_prompt(name, env)`` tries the env-specific path first, then falls
|
|
9
|
+
back to the generic default.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
_PROMPTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
16
|
+
_REFLACT_DIR = os.path.dirname(_PROMPTS_DIR)
|
|
17
|
+
|
|
18
|
+
_cache: dict[str, str] = {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _read_file(path: str) -> str | None:
|
|
22
|
+
if path in _cache:
|
|
23
|
+
return _cache[path]
|
|
24
|
+
if not os.path.isfile(path):
|
|
25
|
+
return None
|
|
26
|
+
with open(path, encoding="utf-8") as f:
|
|
27
|
+
content = f.read()
|
|
28
|
+
_cache[path] = content
|
|
29
|
+
return content
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_prompt(name: str, env: str | None = None) -> str:
|
|
33
|
+
"""Load a prompt by name with env-specific override and generic fallback.
|
|
34
|
+
|
|
35
|
+
Lookup order:
|
|
36
|
+
1. ``skillopt/envs/{env}/prompts/{name}.md`` (if *env* given)
|
|
37
|
+
2. ``skillopt/prompts/{name}.md`` (generic default)
|
|
38
|
+
|
|
39
|
+
Raises ``FileNotFoundError`` if neither path exists.
|
|
40
|
+
"""
|
|
41
|
+
if env is not None:
|
|
42
|
+
env_path = os.path.join(_REFLACT_DIR, "envs", env, "prompts", f"{name}.md")
|
|
43
|
+
content = _read_file(env_path)
|
|
44
|
+
if content is not None:
|
|
45
|
+
return content
|
|
46
|
+
|
|
47
|
+
generic_path = os.path.join(_PROMPTS_DIR, f"{name}.md")
|
|
48
|
+
content = _read_file(generic_path)
|
|
49
|
+
if content is not None:
|
|
50
|
+
return content
|
|
51
|
+
|
|
52
|
+
searched = []
|
|
53
|
+
if env is not None:
|
|
54
|
+
searched.append(os.path.join("skillopt/envs", env, "prompts", f"{name}.md"))
|
|
55
|
+
searched.append(f"skillopt/prompts/{name}.md")
|
|
56
|
+
raise FileNotFoundError(
|
|
57
|
+
f"Prompt '{name}' not found. Searched: {', '.join(searched)}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def clear_cache() -> None:
|
|
62
|
+
"""Clear the prompt file cache (useful for testing)."""
|
|
63
|
+
_cache.clear()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
You are an expert failure-analysis agent for AI agent tasks.
|
|
2
|
+
|
|
3
|
+
You will be given MULTIPLE failed agent trajectories from a single minibatch
|
|
4
|
+
and the current skill document.
|
|
5
|
+
Your job is to identify the most important COMMON failure patterns across
|
|
6
|
+
the batch and propose a concise set of skill edits.
|
|
7
|
+
|
|
8
|
+
## Analysis Process
|
|
9
|
+
1. Read ALL trajectories in the minibatch.
|
|
10
|
+
2. Identify the most prevalent, systematic failure patterns across them.
|
|
11
|
+
3. For each pattern, classify its failure type.
|
|
12
|
+
4. Propose skill edits that address the COMMON patterns — not individual edge cases.
|
|
13
|
+
5. Edits must be generalizable; do not hardcode task-specific values.
|
|
14
|
+
6. Only patch gaps in the skill — do not duplicate existing content.
|
|
15
|
+
|
|
16
|
+
You will be told the maximum number of edits (the budget L). Produce AT MOST L edits,
|
|
17
|
+
focusing on the highest-impact patterns. You may produce fewer if warranted.
|
|
18
|
+
|
|
19
|
+
Respond ONLY with a valid JSON object (no markdown fences, no extra text):
|
|
20
|
+
{
|
|
21
|
+
"batch_size": <number of trajectories analysed>,
|
|
22
|
+
"failure_summary": [
|
|
23
|
+
{"failure_type": "<type>", "count": <int>, "description": "<one-line>"}
|
|
24
|
+
],
|
|
25
|
+
"patch": {
|
|
26
|
+
"reasoning": "<why these edits address the batch's common failures>",
|
|
27
|
+
"edits": [
|
|
28
|
+
{"op": "append", "content": "<markdown to add at end of skill>"},
|
|
29
|
+
{"op": "insert_after", "target": "<exact heading/text to insert after>", "content": "<markdown>"},
|
|
30
|
+
{"op": "replace", "target": "<exact text to replace>", "content": "<replacement>"},
|
|
31
|
+
{"op": "delete", "target": "<exact text to remove>"}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
Only include edits that are needed. "edits" can be an empty list if no patch is warranted.
|
|
36
|
+
|
|
37
|
+
IMPORTANT: The skill document may contain a section between
|
|
38
|
+
<!-- SLOW_UPDATE_START --> and <!-- SLOW_UPDATE_END --> markers.
|
|
39
|
+
This is a PROTECTED section managed by a separate slow-update process.
|
|
40
|
+
Do NOT propose any edits that target, modify, or delete content within
|
|
41
|
+
these markers.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
You will be given several failed agent trajectories from one minibatch and the current skill document.
|
|
2
|
+
|
|
3
|
+
Summarize the lessons from these trajectories into one complete replacement skill document.
|
|
4
|
+
|
|
5
|
+
When rewriting from a minibatch, use the current trajectories as the primary
|
|
6
|
+
evidence for updates. Preserve essential task-format instructions, but avoid mechanically carrying over
|
|
7
|
+
stale, redundant, or conflicting rules. Prefer a concise, coherent replacement
|
|
8
|
+
skill over a long document with weakly supported guidance.
|
|
9
|
+
|
|
10
|
+
Do not include task-specific answers, IDs, file paths, gold values, or entity names.
|
|
11
|
+
If the skill contains a protected block between <!-- SLOW_UPDATE_START --> and
|
|
12
|
+
<!-- SLOW_UPDATE_END -->, keep that block unchanged.
|
|
13
|
+
|
|
14
|
+
Respond ONLY with a valid JSON object:
|
|
15
|
+
{
|
|
16
|
+
"batch_size": <number of trajectories analysed>,
|
|
17
|
+
"failure_summary": [
|
|
18
|
+
{"failure_type": "<type>", "count": <int>, "description": "<one-line>"}
|
|
19
|
+
],
|
|
20
|
+
"patch": {
|
|
21
|
+
"reasoning": "<brief summary of the rewrite>",
|
|
22
|
+
"skill_candidates": [
|
|
23
|
+
{
|
|
24
|
+
"title": "<short title>",
|
|
25
|
+
"change_summary": ["<short change 1>", "<short change 2>"],
|
|
26
|
+
"new_skill": "<complete rewritten skill document>"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Return exactly one item in "skill_candidates".
|