agent-apprenticeship 0.1.6 → 0.1.8
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/bin/agent-apprenticeship.js +85 -37
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/agent_apprenticeship_trace/__init__.py +1 -1
- package/src/agent_apprenticeship_trace/actual_outputs_normalizer.py +104 -28
- package/src/agent_apprenticeship_trace/apprentice_adapters.py +3 -1
- package/src/agent_apprenticeship_trace/artifact_resolver.py +6 -0
- package/src/agent_apprenticeship_trace/bundle_exporter.py +132 -2
- package/src/agent_apprenticeship_trace/cli.py +1171 -262
- package/src/agent_apprenticeship_trace/codex_runner.py +89 -6
- package/src/agent_apprenticeship_trace/config.py +251 -32
- package/src/agent_apprenticeship_trace/ecosystem_r2.py +515 -0
- package/src/agent_apprenticeship_trace/env.py +5 -5
- package/src/agent_apprenticeship_trace/evaluator.py +127 -11
- package/src/agent_apprenticeship_trace/grader.py +12 -11
- package/src/agent_apprenticeship_trace/learning.py +270 -1
- package/src/agent_apprenticeship_trace/llm_output_normalizer.py +8 -7
- package/src/agent_apprenticeship_trace/loop.py +103 -4
- package/src/agent_apprenticeship_trace/loop_review.py +686 -0
- package/src/agent_apprenticeship_trace/mentor_checkpoints.py +49 -21
- package/src/agent_apprenticeship_trace/openai_structured.py +84 -14
- package/src/agent_apprenticeship_trace/package_exporter.py +1 -1
- package/src/agent_apprenticeship_trace/progress.py +2 -1
- package/src/agent_apprenticeship_trace/public_run.py +369 -57
- package/src/agent_apprenticeship_trace/public_sanitizer.py +37 -1
- package/src/agent_apprenticeship_trace/rubric_generation.py +11 -10
- package/src/agent_apprenticeship_trace/schemas.py +378 -3
- package/src/agent_apprenticeship_trace/task_intake.py +43 -9
- package/src/agent_apprenticeship_trace/tdo.py +2304 -0
- package/src/agent_apprenticeship_trace/trace_normalizer.py +351 -17
- package/src/agent_apprenticeship_trace/trace_prompt.py +38 -11
- package/src/agent_apprenticeship_trace/training_signals.py +1 -1
- package/src/agent_apprenticeship_trace/verifier.py +12 -6
|
@@ -0,0 +1,2304 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import shutil
|
|
6
|
+
from collections import Counter, defaultdict
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from statistics import mean
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .config import Settings, apprentice_agent_display_name, configured_model_provider_ready
|
|
13
|
+
from .env import contains_secret, redact_secrets
|
|
14
|
+
from .io import append_jsonl, read_json, read_jsonl, write_json
|
|
15
|
+
from .progress import ProgressCallback, append_progress_event
|
|
16
|
+
from .public_sanitizer import sanitize_public_obj, sanitize_public_text
|
|
17
|
+
from .schemas import TDOJudgeResult, TDOReport
|
|
18
|
+
|
|
19
|
+
TDO_VERSION = "aa-tdo-v0.1"
|
|
20
|
+
TDO_JSONL_FILES = [
|
|
21
|
+
"tdo_trace.jsonl",
|
|
22
|
+
"tdo_rounds.jsonl",
|
|
23
|
+
"tdo_judge_results.jsonl",
|
|
24
|
+
"training_examples.jsonl",
|
|
25
|
+
"evaluation_examples.jsonl",
|
|
26
|
+
"verifier_examples.jsonl",
|
|
27
|
+
"reward_model_rows.jsonl",
|
|
28
|
+
"preference_pairs.jsonl",
|
|
29
|
+
"failure_cases.jsonl",
|
|
30
|
+
"task_variants.jsonl",
|
|
31
|
+
"environment_response_pairs.jsonl",
|
|
32
|
+
"multi_agent_handoffs.jsonl",
|
|
33
|
+
"multimodal_artifacts.jsonl",
|
|
34
|
+
"ui_interaction_events.jsonl",
|
|
35
|
+
"physical_environment_events.jsonl",
|
|
36
|
+
"api_interaction_events.jsonl",
|
|
37
|
+
"mcp_interaction_events.jsonl",
|
|
38
|
+
"database_events.jsonl",
|
|
39
|
+
"structured_table_events.jsonl",
|
|
40
|
+
"software_execution_events.jsonl",
|
|
41
|
+
"search_retrieval_events.jsonl",
|
|
42
|
+
"enterprise_app_events.jsonl",
|
|
43
|
+
"async_workflow_events.jsonl",
|
|
44
|
+
"collaboration_events.jsonl",
|
|
45
|
+
"document_events.jsonl",
|
|
46
|
+
"cross_system_events.jsonl",
|
|
47
|
+
"meta_optimization_records.jsonl",
|
|
48
|
+
"rejected_rows.jsonl",
|
|
49
|
+
]
|
|
50
|
+
TDO_JSON_FILES = [
|
|
51
|
+
"tdo_manifest.json",
|
|
52
|
+
"tdo_report.json",
|
|
53
|
+
"tdo_recipe.json",
|
|
54
|
+
"optimized_task_spec.json",
|
|
55
|
+
"optimized_rubric.json",
|
|
56
|
+
"domain_value_profile.json",
|
|
57
|
+
"environment_spec.json",
|
|
58
|
+
]
|
|
59
|
+
ROW_FILE_BY_TYPE = {
|
|
60
|
+
"training_example": "training_examples.jsonl",
|
|
61
|
+
"evaluation_example": "evaluation_examples.jsonl",
|
|
62
|
+
"verifier_example": "verifier_examples.jsonl",
|
|
63
|
+
"reward_model_row": "reward_model_rows.jsonl",
|
|
64
|
+
"preference_pair": "preference_pairs.jsonl",
|
|
65
|
+
"failure_case": "failure_cases.jsonl",
|
|
66
|
+
"task_variant": "task_variants.jsonl",
|
|
67
|
+
"environment_response_pair": "environment_response_pairs.jsonl",
|
|
68
|
+
"multi_agent_handoff": "multi_agent_handoffs.jsonl",
|
|
69
|
+
"multimodal_artifact": "multimodal_artifacts.jsonl",
|
|
70
|
+
"ui_interaction_event": "ui_interaction_events.jsonl",
|
|
71
|
+
"physical_environment_event": "physical_environment_events.jsonl",
|
|
72
|
+
"api_interaction_event": "api_interaction_events.jsonl",
|
|
73
|
+
"mcp_interaction_event": "mcp_interaction_events.jsonl",
|
|
74
|
+
"database_event": "database_events.jsonl",
|
|
75
|
+
"structured_table_event": "structured_table_events.jsonl",
|
|
76
|
+
"software_execution_event": "software_execution_events.jsonl",
|
|
77
|
+
"search_retrieval_event": "search_retrieval_events.jsonl",
|
|
78
|
+
"enterprise_app_event": "enterprise_app_events.jsonl",
|
|
79
|
+
"async_workflow_event": "async_workflow_events.jsonl",
|
|
80
|
+
"collaboration_event": "collaboration_events.jsonl",
|
|
81
|
+
"document_event": "document_events.jsonl",
|
|
82
|
+
"cross_system_event": "cross_system_events.jsonl",
|
|
83
|
+
}
|
|
84
|
+
ENVIRONMENT_DOMAINS = {
|
|
85
|
+
"terminal",
|
|
86
|
+
"browser",
|
|
87
|
+
"computer_use",
|
|
88
|
+
"web",
|
|
89
|
+
"search",
|
|
90
|
+
"codebase",
|
|
91
|
+
"software",
|
|
92
|
+
"os",
|
|
93
|
+
"android",
|
|
94
|
+
"mcp",
|
|
95
|
+
"api",
|
|
96
|
+
"database",
|
|
97
|
+
"spreadsheet",
|
|
98
|
+
"file",
|
|
99
|
+
"multimodal",
|
|
100
|
+
"device",
|
|
101
|
+
"robotics",
|
|
102
|
+
"lab",
|
|
103
|
+
"industrial",
|
|
104
|
+
"physical",
|
|
105
|
+
"mixed",
|
|
106
|
+
"unknown",
|
|
107
|
+
}
|
|
108
|
+
SURFACE_FILES = {
|
|
109
|
+
"multi_agent_handoff": "multi_agent_handoffs.jsonl",
|
|
110
|
+
"multimodal_artifact": "multimodal_artifacts.jsonl",
|
|
111
|
+
"ui_interaction_event": "ui_interaction_events.jsonl",
|
|
112
|
+
"physical_environment_event": "physical_environment_events.jsonl",
|
|
113
|
+
"api_interaction_event": "api_interaction_events.jsonl",
|
|
114
|
+
"mcp_interaction_event": "mcp_interaction_events.jsonl",
|
|
115
|
+
"database_event": "database_events.jsonl",
|
|
116
|
+
"structured_table_event": "structured_table_events.jsonl",
|
|
117
|
+
"software_execution_event": "software_execution_events.jsonl",
|
|
118
|
+
"search_retrieval_event": "search_retrieval_events.jsonl",
|
|
119
|
+
"enterprise_app_event": "enterprise_app_events.jsonl",
|
|
120
|
+
"async_workflow_event": "async_workflow_events.jsonl",
|
|
121
|
+
"collaboration_event": "collaboration_events.jsonl",
|
|
122
|
+
"document_event": "document_events.jsonl",
|
|
123
|
+
"cross_system_event": "cross_system_events.jsonl",
|
|
124
|
+
}
|
|
125
|
+
PRIVATE_RE = re.compile(r"(/Users/|/private/|/var/folders/|/tmp/|\.env(?:\.local)?)")
|
|
126
|
+
FORBIDDEN_KEYS = {
|
|
127
|
+
"source_ref",
|
|
128
|
+
"source_basis",
|
|
129
|
+
"source_url_or_ref",
|
|
130
|
+
"source_url",
|
|
131
|
+
"source_kind",
|
|
132
|
+
"source_license",
|
|
133
|
+
("b" + "uyer"),
|
|
134
|
+
("s" + "eller"),
|
|
135
|
+
("pr" + "ice"),
|
|
136
|
+
("list" + "ing"),
|
|
137
|
+
("market" + "place"),
|
|
138
|
+
"agent_self_eval",
|
|
139
|
+
"self_eval",
|
|
140
|
+
"trace_context",
|
|
141
|
+
"worker_agent",
|
|
142
|
+
"worker_attempt",
|
|
143
|
+
"evaluation_mode",
|
|
144
|
+
"data_sharing_level",
|
|
145
|
+
("feat" + "ured"),
|
|
146
|
+
("cur" + "ated"),
|
|
147
|
+
("limit" + "ations"),
|
|
148
|
+
("known_" + "limitations"),
|
|
149
|
+
("limitations" + "_count"),
|
|
150
|
+
("when_" + "not_to_use"),
|
|
151
|
+
("not_" + "useful_for"),
|
|
152
|
+
("non_" + "use_cases"),
|
|
153
|
+
("rubric_" + "limitations"),
|
|
154
|
+
}
|
|
155
|
+
PUBLIC_TEXT_REPLACEMENTS = {
|
|
156
|
+
"Gatekeeping": "Review Routing",
|
|
157
|
+
"gatekeeping": "review routing",
|
|
158
|
+
("Worker " + "Agent"): "Apprentice Agent",
|
|
159
|
+
("worker " + "agent"): "Apprentice Agent",
|
|
160
|
+
("Deep" + "Seek"): "model provider",
|
|
161
|
+
("files_created_" + "list" + "ing_inconsistency"): "files_created_index_inconsistency",
|
|
162
|
+
("Cur" + "ated"): "Selected",
|
|
163
|
+
("cur" + "ated"): "selected",
|
|
164
|
+
("Contribution " + "Bundle"): "Experience Compilation",
|
|
165
|
+
("contribution " + "bundle"): "Experience Compilation",
|
|
166
|
+
("Export Full " + "Training Package"): "Export Full Experience Compilation",
|
|
167
|
+
("Training Data " + "Optimization"): "Experience Compiler",
|
|
168
|
+
("T" + "DO"): "Experience Compiler",
|
|
169
|
+
("Honest " + "limitations"): "Generation notes",
|
|
170
|
+
("honest " + "limitations"): "generation notes",
|
|
171
|
+
"Limitations": "Generation Notes",
|
|
172
|
+
"limitations": "generation notes",
|
|
173
|
+
"limitation": "generation note",
|
|
174
|
+
("Applic" + "ability"): "Best Use",
|
|
175
|
+
("applic" + "ability"): "fit",
|
|
176
|
+
("Required " + "Conditions"): "Required Inputs",
|
|
177
|
+
("Evidence " + "Coverage"): "Source Evidence",
|
|
178
|
+
("Evidence " + "coverage"): "Source evidence",
|
|
179
|
+
("evidence " + "coverage"): "source evidence",
|
|
180
|
+
("Coverage " + "Notes"): "Generation Notes",
|
|
181
|
+
("Data Coverage " + "Notes"): "Generation Notes",
|
|
182
|
+
("Transfer " + "Boundaries"): "Transfer Notes",
|
|
183
|
+
("Missing Evidence " + "Notes"): "Generation Notes",
|
|
184
|
+
("Quality " + "Notes"): "Quality Signals",
|
|
185
|
+
("quality " + "notes"): "quality signals",
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def utc_now() -> str:
|
|
190
|
+
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _safe_id(value: str) -> str:
|
|
194
|
+
return re.sub(r"[^A-Za-z0-9_]+", "_", value).strip("_") or "package"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _truthy_env(name: str) -> bool:
|
|
198
|
+
return (os.getenv(name) or "").strip().lower() in {"1", "true", "yes", "y", "on"}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _int_env(name: str, default: int) -> int:
|
|
202
|
+
try:
|
|
203
|
+
return max(1, int(os.getenv(name) or default))
|
|
204
|
+
except ValueError:
|
|
205
|
+
return default
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _float_env(name: str, default: float) -> float:
|
|
209
|
+
try:
|
|
210
|
+
return max(0.0, min(1.0, float(os.getenv(name) or default)))
|
|
211
|
+
except ValueError:
|
|
212
|
+
return default
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _safe_read(path: Path) -> Any:
|
|
216
|
+
try:
|
|
217
|
+
return read_json(path)
|
|
218
|
+
except Exception:
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _safe_jsonl(path: Path) -> list[Any]:
|
|
223
|
+
try:
|
|
224
|
+
return read_jsonl(path)
|
|
225
|
+
except Exception:
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _write_jsonl(path: Path, rows: list[dict[str, Any]]) -> None:
|
|
230
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
path.write_text("")
|
|
232
|
+
for row in rows:
|
|
233
|
+
append_jsonl(path, sanitize_public_obj(row))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _clean_text(value: Any, limit: int = 1200) -> str:
|
|
237
|
+
if value is None:
|
|
238
|
+
return ""
|
|
239
|
+
if isinstance(value, (dict, list)):
|
|
240
|
+
text = str(sanitize_public_obj(value) if isinstance(value, dict) else value)
|
|
241
|
+
else:
|
|
242
|
+
text = str(value)
|
|
243
|
+
text = sanitize_public_text(redact_secrets(text)) or ""
|
|
244
|
+
for old, new in PUBLIC_TEXT_REPLACEMENTS.items():
|
|
245
|
+
text = text.replace(old, new)
|
|
246
|
+
return text[:limit]
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _normalize_ref(ref: Any) -> str | None:
|
|
250
|
+
if not ref:
|
|
251
|
+
return None
|
|
252
|
+
text = str(ref).replace("\\", "/").strip()
|
|
253
|
+
while text.startswith("./"):
|
|
254
|
+
text = text[2:]
|
|
255
|
+
if text.startswith("/") or ".." in Path(text).parts:
|
|
256
|
+
return None
|
|
257
|
+
return text or None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _refs(values: list[Any] | Any) -> list[str]:
|
|
261
|
+
if not values:
|
|
262
|
+
return []
|
|
263
|
+
raw = values if isinstance(values, list) else [values]
|
|
264
|
+
out: list[str] = []
|
|
265
|
+
for value in raw:
|
|
266
|
+
ref = _normalize_ref(value)
|
|
267
|
+
if ref and ref not in out:
|
|
268
|
+
out.append(ref)
|
|
269
|
+
return out
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _score(value: Any, default: float = 0.0) -> float:
|
|
273
|
+
try:
|
|
274
|
+
raw = float(value)
|
|
275
|
+
except (TypeError, ValueError):
|
|
276
|
+
return default
|
|
277
|
+
if raw > 1.0:
|
|
278
|
+
raw = raw / 100.0
|
|
279
|
+
return max(0.0, min(1.0, raw))
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _source_task_id(evidence: dict[str, Any]) -> str:
|
|
283
|
+
task = evidence.get("task") or {}
|
|
284
|
+
raw = evidence.get("raw_task") or {}
|
|
285
|
+
manifest = evidence.get("manifest") or {}
|
|
286
|
+
return str(
|
|
287
|
+
task.get("task_id")
|
|
288
|
+
or raw.get("task_id")
|
|
289
|
+
or raw.get("raw_task_id")
|
|
290
|
+
or manifest.get("task_id")
|
|
291
|
+
or evidence["package_id"]
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _load_evidence(package_root: Path, run_root: Path | None = None) -> dict[str, Any]:
|
|
296
|
+
attempts: dict[str, dict[str, Any]] = {}
|
|
297
|
+
for attempt_dir in sorted((package_root / "attempts").glob("*")):
|
|
298
|
+
if not attempt_dir.is_dir():
|
|
299
|
+
continue
|
|
300
|
+
if not (attempt_dir / "actual_outputs.json").exists() and not (attempt_dir / "agent_trace.json").exists():
|
|
301
|
+
continue
|
|
302
|
+
kind = attempt_dir.name
|
|
303
|
+
attempts[kind] = {
|
|
304
|
+
"actual_outputs": _safe_read(attempt_dir / "actual_outputs.json") or {},
|
|
305
|
+
"agent_trace": _safe_read(attempt_dir / "agent_trace.json") or {},
|
|
306
|
+
"attempt_manifest": _safe_read(attempt_dir / "attempt_manifest.json") or {},
|
|
307
|
+
}
|
|
308
|
+
loop_review_refs: list[str] = []
|
|
309
|
+
loop_review_packets: list[dict[str, Any]] = []
|
|
310
|
+
for packet_path in sorted((package_root / "loops").glob("iterations/*/review_packet.json")):
|
|
311
|
+
try:
|
|
312
|
+
packet = read_json(packet_path)
|
|
313
|
+
except Exception:
|
|
314
|
+
continue
|
|
315
|
+
rel = packet_path.relative_to(package_root).as_posix()
|
|
316
|
+
loop_review_refs.append(rel)
|
|
317
|
+
loop_review_packets.append({"ref": rel, "packet": packet})
|
|
318
|
+
for rel in [
|
|
319
|
+
"loops/loop_manifest.json",
|
|
320
|
+
*[p.relative_to(package_root).as_posix() for p in sorted((package_root / "loops").glob("iterations/*/reviewer_feedback.json"))],
|
|
321
|
+
*[p.relative_to(package_root).as_posix() for p in sorted((package_root / "loops").glob("iterations/*/revision_plan.json"))],
|
|
322
|
+
*[p.relative_to(package_root).as_posix() for p in sorted((package_root / "loops").glob("iterations/*/loop_decision.json"))],
|
|
323
|
+
*[p.relative_to(package_root).as_posix() for p in sorted((package_root / "loops").glob("iterations/*/diff_report.json"))],
|
|
324
|
+
]:
|
|
325
|
+
if (package_root / rel).exists() and rel not in loop_review_refs:
|
|
326
|
+
loop_review_refs.append(rel)
|
|
327
|
+
return {
|
|
328
|
+
"package_id": package_root.name,
|
|
329
|
+
"package_root": package_root,
|
|
330
|
+
"run_root": run_root,
|
|
331
|
+
"manifest": _safe_read(package_root / "package_manifest.json") or _safe_read(package_root / "manifest.json") or {},
|
|
332
|
+
"raw_task": _safe_read(package_root / "task" / "raw_task_record.json") or _safe_read(package_root / "task" / "task_packet.json") or {},
|
|
333
|
+
"task": _safe_read(package_root / "task" / "task_intake_spec.json") or _safe_read(package_root / "task" / "task_packet.json") or {},
|
|
334
|
+
"rubric": _safe_read(package_root / "rubric" / "rubric.json") or {},
|
|
335
|
+
"attempts": attempts,
|
|
336
|
+
"grader_results": {
|
|
337
|
+
path.stem.replace("_grader_result", ""): _safe_read(path) or {}
|
|
338
|
+
for path in sorted((package_root / "grading").glob("*_grader_result.json"))
|
|
339
|
+
},
|
|
340
|
+
"verifier_results": {
|
|
341
|
+
path.stem.replace("_verifier_result", ""): _safe_read(path) or {}
|
|
342
|
+
for path in sorted((package_root / "grading").glob("*_verifier_result.json"))
|
|
343
|
+
},
|
|
344
|
+
"evaluator_feedback": _safe_read(package_root / "feedback" / "baseline_evaluator_feedback.json") or {},
|
|
345
|
+
"revision_plan": _safe_read(package_root / "feedback" / "revision_plan.json") or {},
|
|
346
|
+
"hillclimb": _safe_read(package_root / "signals" / "hillclimb_result.json") or {},
|
|
347
|
+
"lessons": _safe_read(package_root / "signals" / "lesson_pack.json") or {},
|
|
348
|
+
"training_signals": _safe_jsonl(package_root / "signals" / "training_signals.jsonl"),
|
|
349
|
+
"process_supervision": _safe_jsonl(package_root / "signals" / "process_supervision.jsonl"),
|
|
350
|
+
"reward_modeling": _safe_jsonl(package_root / "signals" / "reward_modeling.jsonl"),
|
|
351
|
+
"revision_preference_pairs": _safe_jsonl(package_root / "signals" / "revision_preference_pairs.jsonl"),
|
|
352
|
+
"artifact_index": _safe_read(package_root / "artifacts_index.json") or [],
|
|
353
|
+
"session_events": _safe_jsonl(run_root / "session_events.jsonl") if run_root else [],
|
|
354
|
+
"loop_review_refs": loop_review_refs,
|
|
355
|
+
"loop_review_packets": loop_review_packets,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _attempt_ids(evidence: dict[str, Any]) -> list[str]:
|
|
360
|
+
ids: list[str] = []
|
|
361
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
362
|
+
actual = attempt.get("actual_outputs") or {}
|
|
363
|
+
attempt_id = actual.get("attempt_id")
|
|
364
|
+
if attempt_id and attempt_id not in ids:
|
|
365
|
+
ids.append(str(attempt_id))
|
|
366
|
+
return ids
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _source_artifact_refs(evidence: dict[str, Any]) -> list[str]:
|
|
370
|
+
refs: list[str] = []
|
|
371
|
+
package_root = evidence.get("package_root")
|
|
372
|
+
for attempt_kind, attempt in (evidence.get("attempts") or {}).items():
|
|
373
|
+
actual = attempt.get("actual_outputs") or {}
|
|
374
|
+
for key in ["deliverable_refs", "artifact_refs", "files_created", "input_artifact_refs"]:
|
|
375
|
+
refs.extend(_refs(actual.get(key)))
|
|
376
|
+
for local in [f"attempts/{attempt_kind}/actual_outputs.json", f"attempts/{attempt_kind}/agent_trace.json"]:
|
|
377
|
+
if not isinstance(package_root, Path) or (package_root / local).exists():
|
|
378
|
+
refs.append(local)
|
|
379
|
+
return list(dict.fromkeys(refs))
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _evidence_refs(evidence: dict[str, Any]) -> list[str]:
|
|
383
|
+
refs = ["task/task_intake_spec.json", "rubric/rubric.json"]
|
|
384
|
+
refs.extend(_source_artifact_refs(evidence))
|
|
385
|
+
for name in [
|
|
386
|
+
"grading/baseline_grader_result.json",
|
|
387
|
+
"grading/revised_grader_result.json",
|
|
388
|
+
"grading/baseline_verifier_result.json",
|
|
389
|
+
"grading/revised_verifier_result.json",
|
|
390
|
+
"feedback/baseline_evaluator_feedback.json",
|
|
391
|
+
"feedback/revision_plan.json",
|
|
392
|
+
"signals/hillclimb_result.json",
|
|
393
|
+
]:
|
|
394
|
+
if (evidence["package_root"] / name).exists():
|
|
395
|
+
refs.append(name)
|
|
396
|
+
refs.extend(evidence.get("loop_review_refs") or [])
|
|
397
|
+
return list(dict.fromkeys(refs))
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _infer_environment_domain(step: dict[str, Any] | None = None, tool: str | None = None, operation: str | None = None) -> str:
|
|
401
|
+
step = step or {}
|
|
402
|
+
raw = " ".join(str(v or "") for v in [tool or step.get("tool"), operation or step.get("operation"), step.get("action"), step.get("interaction_surface"), step.get("input"), step.get("output")]).lower()
|
|
403
|
+
if any(x in raw for x in ["industrial", "plc", "scada", "manufacturing", "telemetry"]):
|
|
404
|
+
return "industrial"
|
|
405
|
+
if any(x in raw for x in ["robot", "actuator", "sensor", "physical"]):
|
|
406
|
+
return "robotics" if "robot" in raw else "physical"
|
|
407
|
+
if any(x in raw for x in ["desktop", "computer_use", "accessibility", "ui automation"]):
|
|
408
|
+
return "computer_use"
|
|
409
|
+
if any(x in raw for x in ["bash", "shell", "terminal", "codex_cli", "command"]):
|
|
410
|
+
return "terminal"
|
|
411
|
+
if any(x in raw for x in ["browser", "playwright"]):
|
|
412
|
+
return "browser"
|
|
413
|
+
if any(x in raw for x in ["search", "web"]):
|
|
414
|
+
return "search"
|
|
415
|
+
if any(x in raw for x in ["file", "read", "write"]):
|
|
416
|
+
return "file"
|
|
417
|
+
if any(x in raw for x in ["code", "test", "build", "edit"]):
|
|
418
|
+
return "codebase"
|
|
419
|
+
if any(x in raw for x in ["sql", "database", "db"]):
|
|
420
|
+
return "database"
|
|
421
|
+
if any(x in raw for x in ["sheet", "spreadsheet", "xlsx", "csv"]):
|
|
422
|
+
return "spreadsheet"
|
|
423
|
+
if any(x in raw for x in ["document", "pdf", "ocr", "image", "audio", "video", "screenshot", "table"]):
|
|
424
|
+
return "multimodal"
|
|
425
|
+
if "mcp" in raw:
|
|
426
|
+
return "mcp"
|
|
427
|
+
if "api" in raw:
|
|
428
|
+
return "api"
|
|
429
|
+
return str(step.get("environment_domain") or "unknown") if step.get("environment_domain") in ENVIRONMENT_DOMAINS else "unknown"
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _weak_strong_proxy(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
433
|
+
hill = evidence.get("hillclimb") or {}
|
|
434
|
+
if hill.get("baseline_score") is not None and hill.get("revised_score") is not None:
|
|
435
|
+
weak = _score(hill.get("baseline_score"))
|
|
436
|
+
strong = _score(hill.get("revised_score"))
|
|
437
|
+
gap = strong - weak
|
|
438
|
+
return {
|
|
439
|
+
"available": True,
|
|
440
|
+
"weak_score_proxy": weak,
|
|
441
|
+
"strong_score_proxy": strong,
|
|
442
|
+
"solver_gap_proxy": round(gap, 4),
|
|
443
|
+
"weak_pattern": _clean_text("; ".join(hill.get("failed_criteria_before") or []) or "Baseline attempt before revision."),
|
|
444
|
+
"strong_pattern": _clean_text("; ".join(hill.get("criteria_improved") or []) or "Revised or selected attempt had stronger evidence."),
|
|
445
|
+
"gap_interpretation": "Revised evidence outperformed baseline." if gap > 0 else "No positive solver gap was observed.",
|
|
446
|
+
"grpo_suitability": "high" if gap >= 0.2 else "medium" if gap > 0.05 else "low",
|
|
447
|
+
"learning_signal_quality": "high" if gap >= 0.2 else "medium" if gap > 0 else "low",
|
|
448
|
+
}
|
|
449
|
+
graders = list((evidence.get("grader_results") or {}).values())
|
|
450
|
+
if len(graders) >= 2:
|
|
451
|
+
scores = [_score(g.get("final_score") if g.get("final_score") is not None else g.get("score")) for g in graders]
|
|
452
|
+
weak = min(scores)
|
|
453
|
+
strong = max(scores)
|
|
454
|
+
gap = strong - weak
|
|
455
|
+
return {
|
|
456
|
+
"available": True,
|
|
457
|
+
"weak_score_proxy": weak,
|
|
458
|
+
"strong_score_proxy": strong,
|
|
459
|
+
"solver_gap_proxy": round(gap, 4),
|
|
460
|
+
"weak_pattern": "Lower-scoring attempt.",
|
|
461
|
+
"strong_pattern": "Higher-scoring attempt.",
|
|
462
|
+
"gap_interpretation": "Score gap inferred from grader results.",
|
|
463
|
+
"grpo_suitability": "medium" if gap > 0.05 else "low",
|
|
464
|
+
"learning_signal_quality": "medium" if gap > 0 else "low",
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
"available": False,
|
|
468
|
+
"weak_score_proxy": None,
|
|
469
|
+
"strong_score_proxy": None,
|
|
470
|
+
"solver_gap_proxy": None,
|
|
471
|
+
"weak_pattern": None,
|
|
472
|
+
"strong_pattern": None,
|
|
473
|
+
"gap_interpretation": "Weak/strong proxy unavailable from this package.",
|
|
474
|
+
"grpo_suitability": "unknown",
|
|
475
|
+
"learning_signal_quality": "unknown",
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _source_audit_status(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
480
|
+
task_md = (evidence.get("task") or {}).get("metadata_json") or {}
|
|
481
|
+
rubric_md = (evidence.get("rubric") or {}).get("metadata_json") or {}
|
|
482
|
+
task_audited = bool(task_md.get("mentor_audited"))
|
|
483
|
+
rubric_audited = bool(rubric_md.get("mentor_audited"))
|
|
484
|
+
return {
|
|
485
|
+
"source_task_spec_mentor_audited": task_audited,
|
|
486
|
+
"source_task_spec_audit_source": task_md.get("mentor_audit_source"),
|
|
487
|
+
"source_task_spec_audit_status": task_md.get("audit_status") or ("mentor_audited" if task_audited else "unaudited"),
|
|
488
|
+
"source_rubric_mentor_audited": rubric_audited,
|
|
489
|
+
"source_rubric_audit_source": rubric_md.get("mentor_audit_source"),
|
|
490
|
+
"source_rubric_audit_status": rubric_md.get("audit_status") or ("mentor_audited" if rubric_audited else "unaudited"),
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _base_row(evidence: dict[str, Any], settings: Settings, round_idx: int, judge_source: str, mentor_audited: bool, proxy: dict[str, Any], quality: float) -> dict[str, Any]:
|
|
495
|
+
row = {
|
|
496
|
+
"source_package_id": evidence["package_id"],
|
|
497
|
+
"source_task_id": _source_task_id(evidence),
|
|
498
|
+
"source_trace_id": None,
|
|
499
|
+
"source_step_ids": [],
|
|
500
|
+
"source_attempt_ids": _attempt_ids(evidence),
|
|
501
|
+
"source_artifact_refs": _source_artifact_refs(evidence),
|
|
502
|
+
"source_evaluation_refs": [ref for ref in _evidence_refs(evidence) if ref.startswith(("grading/", "feedback/", "signals/"))],
|
|
503
|
+
"evidence_refs": _evidence_refs(evidence),
|
|
504
|
+
"provenance": "derived_from_observed_agent_apprenticeship_package",
|
|
505
|
+
"quality_score": quality,
|
|
506
|
+
"fidelity_score": min(1.0, quality + 0.04),
|
|
507
|
+
"difficulty_score": min(1.0, max(0.35, quality - 0.08)),
|
|
508
|
+
"learnability_score": min(1.0, quality + 0.02),
|
|
509
|
+
"reuse_value_score": min(1.0, quality + 0.03),
|
|
510
|
+
"weak_score_proxy": proxy.get("weak_score_proxy"),
|
|
511
|
+
"strong_score_proxy": proxy.get("strong_score_proxy"),
|
|
512
|
+
"solver_gap_proxy": proxy.get("solver_gap_proxy"),
|
|
513
|
+
"weak_pattern": proxy.get("weak_pattern"),
|
|
514
|
+
"strong_pattern": proxy.get("strong_pattern"),
|
|
515
|
+
"gap_interpretation": proxy.get("gap_interpretation"),
|
|
516
|
+
"grpo_suitability": proxy.get("grpo_suitability"),
|
|
517
|
+
"generated_by": "Experience Compiler",
|
|
518
|
+
"generator_mode": settings.worker_agent,
|
|
519
|
+
"tdo_judge_source": judge_source,
|
|
520
|
+
"mentor_audited": mentor_audited,
|
|
521
|
+
"tdo_round": round_idx,
|
|
522
|
+
"accepted": False,
|
|
523
|
+
"rejection_reason": None,
|
|
524
|
+
"created_at": utc_now(),
|
|
525
|
+
}
|
|
526
|
+
if judge_source == "apprentice_self_judge":
|
|
527
|
+
row["self_judge_generation_notes"] = [
|
|
528
|
+
"judge_source=apprentice_self_judge",
|
|
529
|
+
"recommended_future_mentor_audit",
|
|
530
|
+
]
|
|
531
|
+
row["recommended_future_mentor_audit"] = True
|
|
532
|
+
return row
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _has_concrete_source_evidence(row: dict[str, Any]) -> bool:
|
|
536
|
+
"""True when a row is grounded beyond task/rubric metadata alone."""
|
|
537
|
+
if row.get("source_trace_id"):
|
|
538
|
+
return True
|
|
539
|
+
for key in ("source_step_ids", "source_attempt_ids", "source_artifact_refs", "source_evaluation_refs"):
|
|
540
|
+
if _refs(row.get(key) or []):
|
|
541
|
+
return True
|
|
542
|
+
for ref in _refs(row.get("evidence_refs") or []):
|
|
543
|
+
if ref.startswith(("traces/", "attempts/", "artifacts/", "outputs/", "evaluation/", "grading/", "feedback/", "signals/", "loops/")):
|
|
544
|
+
return True
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _build_candidate_rows(evidence: dict[str, Any], settings: Settings, round_idx: int, judge_source: str, mentor_audited: bool, proxy: dict[str, Any]) -> list[dict[str, Any]]:
|
|
549
|
+
task = evidence.get("task") or {}
|
|
550
|
+
raw = evidence.get("raw_task") or {}
|
|
551
|
+
rubric = evidence.get("rubric") or {}
|
|
552
|
+
attempts = evidence.get("attempts") or {}
|
|
553
|
+
quality = min(0.92, 0.58 + 0.11 * round_idx)
|
|
554
|
+
common = _base_row(evidence, settings, round_idx, judge_source, mentor_audited, proxy, quality)
|
|
555
|
+
title = task.get("normalized_title") or raw.get("raw_title") or raw.get("title") or evidence["package_id"]
|
|
556
|
+
instruction = task.get("normalized_instruction") or raw.get("raw_description") or raw.get("instruction") or title
|
|
557
|
+
deliverables = task.get("output_requirements") or raw.get("output_requirements") or ["task artifact"]
|
|
558
|
+
rows: list[dict[str, Any]] = []
|
|
559
|
+
rows.append({
|
|
560
|
+
**common,
|
|
561
|
+
"data_type": "training_example",
|
|
562
|
+
"input": {"instruction": _clean_text(instruction), "expected_deliverables": deliverables},
|
|
563
|
+
"target": {"artifact_refs": _source_artifact_refs(evidence), "summary": _best_output_summary(evidence)},
|
|
564
|
+
"expected_output": _best_output_summary(evidence),
|
|
565
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
566
|
+
"scoring_hint": "Use actual_outputs.json, artifact refs, traces, and mentor/verifier/evaluator evidence.",
|
|
567
|
+
"training_use_case": "supervised_task_completion",
|
|
568
|
+
})
|
|
569
|
+
for item in rubric.get("rubric_items") or []:
|
|
570
|
+
rows.append({
|
|
571
|
+
**common,
|
|
572
|
+
"data_type": "evaluation_example",
|
|
573
|
+
"input": {"artifact_refs": _source_artifact_refs(evidence), "criterion": item.get("criterion_name")},
|
|
574
|
+
"target": {"criterion_description": item.get("criterion_description"), "pass_threshold": item.get("pass_threshold")},
|
|
575
|
+
"expected_output": "Score the attempt against the criterion using grounded evidence.",
|
|
576
|
+
"rubric": item,
|
|
577
|
+
"scoring_hint": "Do not reward easier or more permissive rubrics.",
|
|
578
|
+
})
|
|
579
|
+
for attempt_kind, verifier in (evidence.get("verifier_results") or {}).items():
|
|
580
|
+
rows.append({
|
|
581
|
+
**common,
|
|
582
|
+
"data_type": "verifier_example",
|
|
583
|
+
"input": {"attempt_kind": attempt_kind, "artifact_refs": _source_artifact_refs(evidence)},
|
|
584
|
+
"target": verifier,
|
|
585
|
+
"expected_output": "Verify grounding, artifact contract, score consistency, and leakage checks.",
|
|
586
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
587
|
+
"scoring_hint": "Verifier output is evidence; Apprentice Agent self-assessment is not outcome evidence.",
|
|
588
|
+
})
|
|
589
|
+
for attempt_kind, grader in (evidence.get("grader_results") or {}).items():
|
|
590
|
+
rows.append({
|
|
591
|
+
**common,
|
|
592
|
+
"data_type": "reward_model_row",
|
|
593
|
+
"input": {"attempt_kind": attempt_kind, "output_summary": _attempt_summary(attempts.get(attempt_kind) or {})},
|
|
594
|
+
"target": {"final_score": grader.get("final_score", grader.get("score")), "passed": grader.get("passed")},
|
|
595
|
+
"expected_output": "Prefer higher-quality grounded outputs with verifier-consistent evidence.",
|
|
596
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
597
|
+
"scoring_hint": "Use grader and verifier evidence as reward-model labels.",
|
|
598
|
+
})
|
|
599
|
+
if proxy.get("available"):
|
|
600
|
+
rows.append({
|
|
601
|
+
**common,
|
|
602
|
+
"data_type": "preference_pair",
|
|
603
|
+
"input": {"baseline_attempt_ref": "attempts/baseline", "revised_attempt_ref": "attempts/revised"},
|
|
604
|
+
"target": {
|
|
605
|
+
"chosen": "revised" if (proxy.get("strong_score_proxy") or 0) >= (proxy.get("weak_score_proxy") or 0) else "baseline",
|
|
606
|
+
"solver_gap_proxy": proxy.get("solver_gap_proxy"),
|
|
607
|
+
},
|
|
608
|
+
"expected_output": "Choose the attempt with stronger grounded evidence.",
|
|
609
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
610
|
+
"scoring_hint": "Preference is based on observed score/verifier gap, not a new model claim.",
|
|
611
|
+
})
|
|
612
|
+
for attempt_kind, grader in (evidence.get("grader_results") or {}).items():
|
|
613
|
+
for criterion in grader.get("failed_criteria") or []:
|
|
614
|
+
rows.append({
|
|
615
|
+
**common,
|
|
616
|
+
"data_type": "failure_case",
|
|
617
|
+
"input": {"attempt_kind": attempt_kind, "failed_criterion": criterion},
|
|
618
|
+
"target": {"failure_mode": criterion, "repair_hint": _clean_text((evidence.get("evaluator_feedback") or {}).get("feedback_summary"))},
|
|
619
|
+
"expected_output": "Identify the failure and propose grounded repair guidance.",
|
|
620
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
621
|
+
"scoring_hint": "Failure cases must point back to grader/verifier/evaluator evidence.",
|
|
622
|
+
})
|
|
623
|
+
rows.append({
|
|
624
|
+
**common,
|
|
625
|
+
"data_type": "task_variant",
|
|
626
|
+
"input": {"source_instruction": _clean_text(instruction)},
|
|
627
|
+
"target": {
|
|
628
|
+
"variant_instruction": _clean_text(f"{instruction}\n\nVariant constraint: preserve the same deliverables and make the result easier to verify."),
|
|
629
|
+
"expected_deliverables": deliverables,
|
|
630
|
+
},
|
|
631
|
+
"expected_output": "A grounded task variant with the same evidence-backed artifact contract.",
|
|
632
|
+
"rubric": rubric.get("rubric_items") or [],
|
|
633
|
+
"scoring_hint": "Variant must preserve artifact requirements and verifier checks.",
|
|
634
|
+
})
|
|
635
|
+
rows.extend(_environment_response_rows(evidence, common))
|
|
636
|
+
rows.extend(_surface_event_rows(evidence, common))
|
|
637
|
+
return [sanitize_public_obj(row) for row in rows]
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _best_output_summary(evidence: dict[str, Any]) -> str:
|
|
641
|
+
selected = (evidence.get("manifest") or {}).get("selected_attempt_id")
|
|
642
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
643
|
+
actual = attempt.get("actual_outputs") or {}
|
|
644
|
+
if selected and actual.get("attempt_id") == selected and actual.get("output_summary"):
|
|
645
|
+
return _clean_text(actual.get("output_summary"))
|
|
646
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
647
|
+
actual = attempt.get("actual_outputs") or {}
|
|
648
|
+
if actual.get("output_summary"):
|
|
649
|
+
return _clean_text(actual.get("output_summary"))
|
|
650
|
+
return "No output summary was available."
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _attempt_summary(attempt: dict[str, Any]) -> str:
|
|
654
|
+
return _clean_text((attempt.get("actual_outputs") or {}).get("output_summary") or "Attempt output summary unavailable.")
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _environment_response_rows(evidence: dict[str, Any], common: dict[str, Any]) -> list[dict[str, Any]]:
|
|
658
|
+
rows: list[dict[str, Any]] = []
|
|
659
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
660
|
+
trace = attempt.get("agent_trace") or {}
|
|
661
|
+
steps = trace.get("steps") or []
|
|
662
|
+
total = len(steps)
|
|
663
|
+
previous_state = ""
|
|
664
|
+
for idx, step in enumerate(steps, start=1):
|
|
665
|
+
if step.get("action") == "user_message":
|
|
666
|
+
previous_state = _clean_text(step.get("state_change") or previous_state)
|
|
667
|
+
continue
|
|
668
|
+
domain = step.get("environment_domain") if step.get("environment_domain") in ENVIRONMENT_DOMAINS else _infer_environment_domain(step)
|
|
669
|
+
score = 0.72
|
|
670
|
+
if step.get("success") is True:
|
|
671
|
+
score = 0.82
|
|
672
|
+
if step.get("step_outcome") in {"failed", "blocked"}:
|
|
673
|
+
score = 0.45
|
|
674
|
+
rows.append({
|
|
675
|
+
**common,
|
|
676
|
+
"data_type": "environment_response_pair",
|
|
677
|
+
"source_trace_id": trace.get("trace_id"),
|
|
678
|
+
"source_step_id": step.get("step") or idx,
|
|
679
|
+
"source_step_ids": [step.get("step") or idx],
|
|
680
|
+
"environment_domain": domain,
|
|
681
|
+
"interaction_surface": step.get("interaction_surface") or _surface_from_step(step),
|
|
682
|
+
"state_before": _clean_text(previous_state or step.get("observation")),
|
|
683
|
+
"agent_action": _clean_text(step.get("input") or step.get("action") or step.get("operation")),
|
|
684
|
+
"observed_environment_response": _clean_text(step.get("output") or step.get("observation")),
|
|
685
|
+
"state_after": _clean_text(step.get("state_change")),
|
|
686
|
+
"success_signal": _clean_text(step.get("step_outcome") or step.get("success")),
|
|
687
|
+
"failure_mode": _clean_text(step.get("error_type") or ""),
|
|
688
|
+
"verifier_feedback": _clean_text(_first_verifier_notes(evidence)),
|
|
689
|
+
"turn_idx": idx,
|
|
690
|
+
"total_turns": total,
|
|
691
|
+
"format_score": score,
|
|
692
|
+
"factuality_score": score,
|
|
693
|
+
"consistency_score": score,
|
|
694
|
+
"realism_score": score,
|
|
695
|
+
"training_quality_score": score,
|
|
696
|
+
"environment_response_quality_score": score,
|
|
697
|
+
"usable_for": ["environment_model_training", "agent_rl", "verifier_training"],
|
|
698
|
+
})
|
|
699
|
+
previous_state = _clean_text(step.get("state_change") or previous_state)
|
|
700
|
+
return rows
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _surface_from_step(step: dict[str, Any]) -> str:
|
|
704
|
+
surface = step.get("interaction_surface")
|
|
705
|
+
if surface:
|
|
706
|
+
return str(surface)
|
|
707
|
+
domain = step.get("environment_domain")
|
|
708
|
+
if domain in {"api", "database", "spreadsheet", "search", "browser", "computer_use", "file", "codebase"}:
|
|
709
|
+
return str(domain)
|
|
710
|
+
raw = " ".join(str(step.get(k) or "") for k in ["tool", "operation", "input", "output"]).lower()
|
|
711
|
+
if "mcp" in raw:
|
|
712
|
+
return "mcp_tool"
|
|
713
|
+
if any(x in raw for x in ["curl", "http", "graphql", "endpoint"]):
|
|
714
|
+
return "api"
|
|
715
|
+
if any(x in raw for x in ["sql", "table", "database"]):
|
|
716
|
+
return "database"
|
|
717
|
+
if any(x in raw for x in ["xlsx", "workbook", "spreadsheet", "csv"]):
|
|
718
|
+
return "spreadsheet"
|
|
719
|
+
if any(x in raw for x in ["bash", "shell", "command", "pytest", "git "]):
|
|
720
|
+
return "cli"
|
|
721
|
+
return "unknown"
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def _surface_common(common: dict[str, Any], trace: dict[str, Any], step: dict[str, Any], idx: int, data_type: str) -> dict[str, Any]:
|
|
725
|
+
step_id = step.get("step") or idx
|
|
726
|
+
return {
|
|
727
|
+
**common,
|
|
728
|
+
"data_type": data_type,
|
|
729
|
+
"source_trace_id": trace.get("trace_id"),
|
|
730
|
+
"source_step_id": step_id,
|
|
731
|
+
"source_step_ids": [step_id],
|
|
732
|
+
"environment_domain": step.get("environment_domain") or _infer_environment_domain(step),
|
|
733
|
+
"interaction_surface": _surface_from_step(step),
|
|
734
|
+
"state_change": _clean_text(step.get("state_change")),
|
|
735
|
+
"success_signal": _clean_text(step.get("step_outcome") or step.get("success")),
|
|
736
|
+
"failure_mode": _clean_text(step.get("error_type") or step.get("error_message") or ""),
|
|
737
|
+
"training_quality_score": step.get("environment_response_quality_score") or step.get("training_quality_score") or common.get("quality_score"),
|
|
738
|
+
"usable_for": ["trace_training", "environment_response_training", "verifier_training"],
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def _surface_event_rows(evidence: dict[str, Any], common: dict[str, Any]) -> list[dict[str, Any]]:
|
|
743
|
+
rows: list[dict[str, Any]] = []
|
|
744
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
745
|
+
trace = attempt.get("agent_trace") or {}
|
|
746
|
+
for idx, step in enumerate(trace.get("steps") or [], start=1):
|
|
747
|
+
if step.get("action") == "user_message":
|
|
748
|
+
continue
|
|
749
|
+
surface = _surface_from_step(step)
|
|
750
|
+
domain = step.get("environment_domain") or _infer_environment_domain(step)
|
|
751
|
+
if step.get("subagent_id") or step.get("delegation_id") or step.get("subagent_output_refs"):
|
|
752
|
+
rows.append({
|
|
753
|
+
**_surface_common(common, trace, step, idx, "multi_agent_handoff"),
|
|
754
|
+
"parent_agent_id": step.get("parent_agent_id"),
|
|
755
|
+
"subagent_id": step.get("subagent_id"),
|
|
756
|
+
"handoff_reason": _clean_text(step.get("handoff_reason")),
|
|
757
|
+
"handoff_payload_ref": _normalize_ref(step.get("handoff_payload_ref")),
|
|
758
|
+
"subagent_output_refs": _refs(step.get("subagent_output_refs") or step.get("handoff_output_refs") or step.get("artifact_refs")),
|
|
759
|
+
"downstream_steps_caused": step.get("depends_on_agent_outputs") or [],
|
|
760
|
+
"handoff_quality_score": common.get("quality_score"),
|
|
761
|
+
"delegation_success": step.get("success"),
|
|
762
|
+
"reusable_lesson": _clean_text(step.get("state_change") or step.get("output")),
|
|
763
|
+
})
|
|
764
|
+
if step.get("modality") or step.get("modalities") or surface in {"multimodal", "document", "pdf", "table"} or domain == "multimodal":
|
|
765
|
+
refs = _refs(step.get("artifact_refs") or step.get("page_refs") or step.get("table_extract_refs"))
|
|
766
|
+
for ref in refs or [None]:
|
|
767
|
+
modality = step.get("modality")
|
|
768
|
+
if not modality and isinstance(step.get("modalities"), list) and step.get("modalities"):
|
|
769
|
+
modality = step.get("modalities")[0]
|
|
770
|
+
rows.append({
|
|
771
|
+
**_surface_common(common, trace, step, idx, "multimodal_artifact"),
|
|
772
|
+
"modality": modality or "unknown",
|
|
773
|
+
"artifact_ref": ref,
|
|
774
|
+
"artifact_role": step.get("artifact_role"),
|
|
775
|
+
"mime_type": step.get("mime_type"),
|
|
776
|
+
"artifact_hash": step.get("artifact_hash"),
|
|
777
|
+
"preview_ref": _normalize_ref(step.get("artifact_preview_ref")),
|
|
778
|
+
"extracted_text_ref": _normalize_ref(step.get("ocr_text_ref") or step.get("text_extract_ref")),
|
|
779
|
+
"transcript_ref": _normalize_ref(step.get("transcript_ref")),
|
|
780
|
+
"annotation_ref": _normalize_ref(step.get("annotation_ref")),
|
|
781
|
+
"quality_score": common.get("quality_score"),
|
|
782
|
+
"verifier_use": "artifact evidence",
|
|
783
|
+
"training_use_cases": ["multimodal_grounding", "verifier_training"],
|
|
784
|
+
"evidence_status": [] if ref else ["No artifact ref was captured for this multimodal step."],
|
|
785
|
+
})
|
|
786
|
+
if surface in {"browser", "computer_use"} or step.get("ui_surface") or step.get("screenshot_ref"):
|
|
787
|
+
rows.append({
|
|
788
|
+
**_surface_common(common, trace, step, idx, "ui_interaction_event"),
|
|
789
|
+
"ui_surface": step.get("ui_surface") or surface,
|
|
790
|
+
"app_name": step.get("app_name"),
|
|
791
|
+
"action_type": step.get("action_type") or step.get("operation"),
|
|
792
|
+
"visible_state_ref": _normalize_ref(step.get("visible_state_ref")),
|
|
793
|
+
"screenshot_ref": _normalize_ref(step.get("screenshot_ref")),
|
|
794
|
+
"element_selector": step.get("element_selector"),
|
|
795
|
+
"coordinates": step.get("coordinates"),
|
|
796
|
+
"input_text": _clean_text(step.get("input_text")),
|
|
797
|
+
"observed_result": _clean_text(step.get("output")),
|
|
798
|
+
})
|
|
799
|
+
if surface in {"device", "industrial", "physical"} or domain in {"device", "industrial", "physical", "robotics", "lab"}:
|
|
800
|
+
rows.append({
|
|
801
|
+
**_surface_common(common, trace, step, idx, "physical_environment_event"),
|
|
802
|
+
"physical_environment_type": step.get("physical_environment_type") or domain,
|
|
803
|
+
"device_type": step.get("device_type"),
|
|
804
|
+
"sensor_readings_ref": _normalize_ref(step.get("sensor_readings_ref")),
|
|
805
|
+
"actuator_command": _clean_text(step.get("actuator_command")),
|
|
806
|
+
"physical_state_before_ref": _normalize_ref(step.get("physical_state_before_ref")),
|
|
807
|
+
"physical_state_after_ref": _normalize_ref(step.get("physical_state_after_ref")),
|
|
808
|
+
"telemetry_ref": _normalize_ref(step.get("telemetry_ref")),
|
|
809
|
+
"safety_state": step.get("safety_state"),
|
|
810
|
+
"operator_override": step.get("operator_override"),
|
|
811
|
+
"simulation_or_real_world": step.get("simulation_or_real_world") or "unknown",
|
|
812
|
+
"safety_relevance": "safety_state captured" if step.get("safety_state") else "unknown",
|
|
813
|
+
"verifier_use": "safety and telemetry evidence",
|
|
814
|
+
"evidence_status": [] if step.get("telemetry_ref") or step.get("physical_state_after_ref") else ["Physical evidence refs were not captured."],
|
|
815
|
+
})
|
|
816
|
+
if surface == "api":
|
|
817
|
+
rows.append({
|
|
818
|
+
**_surface_common(common, trace, step, idx, "api_interaction_event"),
|
|
819
|
+
"api_provider": step.get("api_provider"),
|
|
820
|
+
"api_service": step.get("api_service"),
|
|
821
|
+
"api_endpoint": step.get("api_endpoint"),
|
|
822
|
+
"api_method": step.get("api_method"),
|
|
823
|
+
"http_status": step.get("http_status"),
|
|
824
|
+
"request_payload_ref": _normalize_ref(step.get("request_payload_ref") or step.get("raw_request_ref")),
|
|
825
|
+
"response_payload_ref": _normalize_ref(step.get("response_payload_ref") or step.get("raw_response_ref")),
|
|
826
|
+
"resource_id": step.get("resource_id"),
|
|
827
|
+
"object_type": step.get("object_type"),
|
|
828
|
+
"retry_count": step.get("retry_count"),
|
|
829
|
+
"verifier_use": "API request/response grounding",
|
|
830
|
+
})
|
|
831
|
+
if surface in {"mcp_tool", "mcp_resource", "mcp_prompt"}:
|
|
832
|
+
rows.append({
|
|
833
|
+
**_surface_common(common, trace, step, idx, "mcp_interaction_event"),
|
|
834
|
+
"mcp_server": step.get("mcp_server"),
|
|
835
|
+
"mcp_capability_type": step.get("mcp_capability_type") or surface.removeprefix("mcp_"),
|
|
836
|
+
"mcp_tool_name": step.get("mcp_tool_name"),
|
|
837
|
+
"mcp_resource_uri": step.get("mcp_resource_uri"),
|
|
838
|
+
"mcp_prompt_name": step.get("mcp_prompt_name"),
|
|
839
|
+
"arguments_ref": _normalize_ref(step.get("mcp_arguments_ref")),
|
|
840
|
+
"result_ref": _normalize_ref(step.get("mcp_result_ref")),
|
|
841
|
+
})
|
|
842
|
+
if surface == "database":
|
|
843
|
+
rows.append({
|
|
844
|
+
**_surface_common(common, trace, step, idx, "database_event"),
|
|
845
|
+
"database_type": step.get("database_type"),
|
|
846
|
+
"database_name": step.get("database_name"),
|
|
847
|
+
"schema_name": step.get("schema_name"),
|
|
848
|
+
"table_name": step.get("table_name"),
|
|
849
|
+
"query_type": step.get("query_type"),
|
|
850
|
+
"query_ref": _normalize_ref(step.get("query_ref")),
|
|
851
|
+
"row_count": step.get("row_count"),
|
|
852
|
+
"affected_row_count": step.get("affected_row_count"),
|
|
853
|
+
"transaction_id": step.get("transaction_id"),
|
|
854
|
+
})
|
|
855
|
+
if surface in {"spreadsheet", "table"}:
|
|
856
|
+
rows.append({
|
|
857
|
+
**_surface_common(common, trace, step, idx, "structured_table_event"),
|
|
858
|
+
"workbook_ref": _normalize_ref(step.get("workbook_ref")),
|
|
859
|
+
"sheet_name": step.get("sheet_name"),
|
|
860
|
+
"table_ref": _normalize_ref(step.get("table_ref")),
|
|
861
|
+
"cell_range": step.get("cell_range"),
|
|
862
|
+
"row_count": step.get("row_count"),
|
|
863
|
+
"column_count": step.get("column_count"),
|
|
864
|
+
"formula_refs": _refs(step.get("formula_refs")),
|
|
865
|
+
"before_table_ref": _normalize_ref(step.get("before_table_ref")),
|
|
866
|
+
"after_table_ref": _normalize_ref(step.get("after_table_ref")),
|
|
867
|
+
"diff_ref": _normalize_ref(step.get("diff_ref")),
|
|
868
|
+
})
|
|
869
|
+
if surface in {"cli", "file", "codebase"}:
|
|
870
|
+
rows.append({
|
|
871
|
+
**_surface_common(common, trace, step, idx, "software_execution_event"),
|
|
872
|
+
"command": _clean_text(step.get("command") or step.get("input")),
|
|
873
|
+
"command_ref": _normalize_ref(step.get("command_ref")),
|
|
874
|
+
"exit_code": step.get("exit_code"),
|
|
875
|
+
"stdout_ref": _normalize_ref(step.get("stdout_ref")),
|
|
876
|
+
"stderr_ref": _normalize_ref(step.get("stderr_ref")),
|
|
877
|
+
"file_path": _normalize_ref(step.get("file_path")),
|
|
878
|
+
"file_paths": _refs(step.get("file_paths") or step.get("artifact_refs")),
|
|
879
|
+
"file_operation": step.get("file_operation") or step.get("operation"),
|
|
880
|
+
"diff_ref": _normalize_ref(step.get("diff_ref")),
|
|
881
|
+
"test_result_ref": _normalize_ref(step.get("test_result_ref")),
|
|
882
|
+
})
|
|
883
|
+
if surface == "search":
|
|
884
|
+
rows.append({
|
|
885
|
+
**_surface_common(common, trace, step, idx, "search_retrieval_event"),
|
|
886
|
+
"search_provider": step.get("search_provider") or step.get("tool"),
|
|
887
|
+
"query": _clean_text(step.get("query") or step.get("input")),
|
|
888
|
+
"query_ref": _normalize_ref(step.get("query_ref")),
|
|
889
|
+
"search_scope": step.get("search_scope"),
|
|
890
|
+
"result_count": step.get("result_count"),
|
|
891
|
+
"selected_result_refs": _refs(step.get("selected_result_refs")),
|
|
892
|
+
"citation_refs": _refs(step.get("citation_refs")),
|
|
893
|
+
})
|
|
894
|
+
if surface in {"cloud_console", "saas_app"}:
|
|
895
|
+
rows.append({
|
|
896
|
+
**_surface_common(common, trace, step, idx, "enterprise_app_event"),
|
|
897
|
+
"service_name": step.get("service_name") or step.get("app_name"),
|
|
898
|
+
"workspace_id": step.get("workspace_id"),
|
|
899
|
+
"project_id": step.get("project_id"),
|
|
900
|
+
"resource_id": step.get("resource_id"),
|
|
901
|
+
"resource_type": step.get("resource_type"),
|
|
902
|
+
"record_id": step.get("record_id"),
|
|
903
|
+
"ticket_id": step.get("ticket_id"),
|
|
904
|
+
"object_state_before_ref": _normalize_ref(step.get("object_state_before_ref")),
|
|
905
|
+
"object_state_after_ref": _normalize_ref(step.get("object_state_after_ref")),
|
|
906
|
+
"audit_log_ref": _normalize_ref(step.get("audit_log_ref")),
|
|
907
|
+
})
|
|
908
|
+
if surface in {"async_job", "queue", "webhook", "scheduler"}:
|
|
909
|
+
rows.append({
|
|
910
|
+
**_surface_common(common, trace, step, idx, "async_workflow_event"),
|
|
911
|
+
"job_id": step.get("job_id"),
|
|
912
|
+
"queue_name": step.get("queue_name"),
|
|
913
|
+
"message_id": step.get("message_id"),
|
|
914
|
+
"webhook_id": step.get("webhook_id"),
|
|
915
|
+
"schedule_id": step.get("schedule_id"),
|
|
916
|
+
"trigger_type": step.get("trigger_type"),
|
|
917
|
+
"retry_count": step.get("retry_count"),
|
|
918
|
+
"delivery_status": step.get("delivery_status"),
|
|
919
|
+
"polling_status": step.get("polling_status"),
|
|
920
|
+
"callback_status": step.get("callback_status"),
|
|
921
|
+
"job_status_before": step.get("job_status_before"),
|
|
922
|
+
"job_status_after": step.get("job_status_after"),
|
|
923
|
+
"async_result_ref": _normalize_ref(step.get("async_result_ref")),
|
|
924
|
+
})
|
|
925
|
+
if surface in {"messaging", "email", "collaboration"}:
|
|
926
|
+
rows.append({
|
|
927
|
+
**_surface_common(common, trace, step, idx, "collaboration_event"),
|
|
928
|
+
"platform_name": step.get("platform_name"),
|
|
929
|
+
"channel_id": step.get("channel_id"),
|
|
930
|
+
"thread_id": step.get("thread_id"),
|
|
931
|
+
"message_id": step.get("message_id"),
|
|
932
|
+
"sender_role": step.get("sender_role"),
|
|
933
|
+
"recipient_role": step.get("recipient_role"),
|
|
934
|
+
"message_type": step.get("message_type"),
|
|
935
|
+
"message_ref": _normalize_ref(step.get("message_ref")),
|
|
936
|
+
"attachment_refs": _refs(step.get("attachment_refs")),
|
|
937
|
+
"decision_refs": _refs(step.get("decision_refs")),
|
|
938
|
+
"followup_required": step.get("followup_required"),
|
|
939
|
+
})
|
|
940
|
+
if surface in {"document", "pdf"}:
|
|
941
|
+
rows.append({
|
|
942
|
+
**_surface_common(common, trace, step, idx, "document_event"),
|
|
943
|
+
"document_ref": _normalize_ref(step.get("document_ref")),
|
|
944
|
+
"document_type": step.get("document_type") or surface,
|
|
945
|
+
"page_refs": _refs(step.get("page_refs")),
|
|
946
|
+
"section_refs": _refs(step.get("section_refs")),
|
|
947
|
+
"text_extract_ref": _normalize_ref(step.get("text_extract_ref") or step.get("ocr_text_ref")),
|
|
948
|
+
"table_extract_refs": _refs(step.get("table_extract_refs")),
|
|
949
|
+
"figure_refs": _refs(step.get("figure_refs")),
|
|
950
|
+
"citation_refs": _refs(step.get("citation_refs")),
|
|
951
|
+
"annotation_refs": _refs(step.get("annotation_refs")),
|
|
952
|
+
"summary_ref": _normalize_ref(step.get("summary_ref")),
|
|
953
|
+
})
|
|
954
|
+
if surface == "mixed" or step.get("connected_surfaces") or step.get("cross_system_dependencies"):
|
|
955
|
+
rows.append({
|
|
956
|
+
**_surface_common(common, trace, step, idx, "cross_system_event"),
|
|
957
|
+
"connected_surfaces": step.get("connected_surfaces") or [],
|
|
958
|
+
"cross_system_dependencies": step.get("cross_system_dependencies") or [],
|
|
959
|
+
"handoff_refs": _refs(step.get("handoff_refs")),
|
|
960
|
+
"state_sync_refs": _refs(step.get("state_sync_refs")),
|
|
961
|
+
"consistency_checks": step.get("consistency_checks") or [],
|
|
962
|
+
"system_of_record": step.get("system_of_record"),
|
|
963
|
+
})
|
|
964
|
+
return [sanitize_public_obj(row) for row in rows]
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
def _first_verifier_notes(evidence: dict[str, Any]) -> str | None:
|
|
968
|
+
for result in (evidence.get("verifier_results") or {}).values():
|
|
969
|
+
if result.get("verifier_notes"):
|
|
970
|
+
return str(result.get("verifier_notes"))
|
|
971
|
+
return None
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _private_or_forbidden_hit(obj: Any) -> str | None:
|
|
975
|
+
if isinstance(obj, dict):
|
|
976
|
+
for key, value in obj.items():
|
|
977
|
+
key_l = str(key).lower()
|
|
978
|
+
if key_l in FORBIDDEN_KEYS:
|
|
979
|
+
return key_l
|
|
980
|
+
hit = _private_or_forbidden_hit(value)
|
|
981
|
+
if hit:
|
|
982
|
+
return hit
|
|
983
|
+
return None
|
|
984
|
+
if isinstance(obj, list):
|
|
985
|
+
for value in obj:
|
|
986
|
+
hit = _private_or_forbidden_hit(value)
|
|
987
|
+
if hit:
|
|
988
|
+
return hit
|
|
989
|
+
return None
|
|
990
|
+
if isinstance(obj, str):
|
|
991
|
+
if contains_secret(obj):
|
|
992
|
+
return "secret_like_value"
|
|
993
|
+
if PRIVATE_RE.search(obj):
|
|
994
|
+
return "private_local_path"
|
|
995
|
+
lower = obj.lower()
|
|
996
|
+
for token in [("deep" + "seek"), ("worker " + "agent"), "worker_agent", ("market" + "place"), ("b" + "uyer"), ("s" + "eller")]:
|
|
997
|
+
if token in lower:
|
|
998
|
+
return token
|
|
999
|
+
return None
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
def _artifact_refs_resolve(package_root: Path, row: dict[str, Any]) -> list[str]:
|
|
1003
|
+
missing: list[str] = []
|
|
1004
|
+
for ref in _refs(row.get("source_artifact_refs") or []):
|
|
1005
|
+
if ":" in ref:
|
|
1006
|
+
continue
|
|
1007
|
+
candidates = [package_root / ref]
|
|
1008
|
+
if ref.startswith("packages/"):
|
|
1009
|
+
candidates.append(package_root.parent / ref.removeprefix("packages/"))
|
|
1010
|
+
if not any(p.exists() for p in candidates):
|
|
1011
|
+
# Evidence refs can legitimately describe generated aggregate rows.
|
|
1012
|
+
if ref.startswith(("grading/", "feedback/", "signals/", "task/", "rubric/")):
|
|
1013
|
+
continue
|
|
1014
|
+
missing.append(ref)
|
|
1015
|
+
return missing
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
def _validate_rows(package_root: Path, rows: list[dict[str, Any]], min_quality: float, min_fidelity: float, min_reuse: float) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[str]]:
|
|
1019
|
+
accepted: list[dict[str, Any]] = []
|
|
1020
|
+
rejected: list[dict[str, Any]] = []
|
|
1021
|
+
schema_errors: list[str] = []
|
|
1022
|
+
for row in rows:
|
|
1023
|
+
reason = _private_or_forbidden_hit(row)
|
|
1024
|
+
missing = _artifact_refs_resolve(package_root, row)
|
|
1025
|
+
if missing:
|
|
1026
|
+
reason = reason or f"unresolved_artifact_ref:{missing[0]}"
|
|
1027
|
+
if not row.get("data_type"):
|
|
1028
|
+
reason = reason or "missing_data_type"
|
|
1029
|
+
if not _has_concrete_source_evidence(row):
|
|
1030
|
+
reason = reason or "insufficient_concrete_source_evidence"
|
|
1031
|
+
if _score(row.get("quality_score")) < min_quality:
|
|
1032
|
+
reason = reason or "quality_score_below_threshold"
|
|
1033
|
+
if _score(row.get("fidelity_score")) < min_fidelity:
|
|
1034
|
+
reason = reason or "fidelity_score_below_threshold"
|
|
1035
|
+
if _score(row.get("reuse_value_score")) < min_reuse:
|
|
1036
|
+
reason = reason or "reuse_value_score_below_threshold"
|
|
1037
|
+
clean = sanitize_public_obj(dict(row))
|
|
1038
|
+
if reason:
|
|
1039
|
+
clean["accepted"] = False
|
|
1040
|
+
clean["rejection_reason"] = reason
|
|
1041
|
+
rejected.append(clean)
|
|
1042
|
+
schema_errors.append(reason)
|
|
1043
|
+
else:
|
|
1044
|
+
clean["accepted"] = True
|
|
1045
|
+
clean["rejection_reason"] = None
|
|
1046
|
+
accepted.append(clean)
|
|
1047
|
+
return accepted, rejected, schema_errors
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
def _judge(round_idx: int, max_rounds: int, rows: list[dict[str, Any]], rejected: list[dict[str, Any]], judge_source: str, mentor_audited: bool, proxy: dict[str, Any]) -> TDOJudgeResult:
|
|
1051
|
+
if not rows:
|
|
1052
|
+
verdict = "reject"
|
|
1053
|
+
reason = "No candidate rows were generated from package evidence."
|
|
1054
|
+
elif rejected and round_idx >= max_rounds:
|
|
1055
|
+
verdict = "improve"
|
|
1056
|
+
reason = "Some candidate rows need stronger grounding or safer refs."
|
|
1057
|
+
elif round_idx < max_rounds:
|
|
1058
|
+
verdict = "improve"
|
|
1059
|
+
reason = "Autodata-style loop continues to improve weak rows and recipe grounding."
|
|
1060
|
+
else:
|
|
1061
|
+
verdict = "accept"
|
|
1062
|
+
reason = "Candidate rows passed schema, grounding, and sanitization guardrails."
|
|
1063
|
+
scores = [_score(row.get("quality_score"), 0.0) for row in rows] or [0.0]
|
|
1064
|
+
fidelity = [_score(row.get("fidelity_score"), 0.0) for row in rows] or [0.0]
|
|
1065
|
+
difficulty = [_score(row.get("difficulty_score"), 0.0) for row in rows] or [0.0]
|
|
1066
|
+
learnability = [_score(row.get("learnability_score"), 0.0) for row in rows] or [0.0]
|
|
1067
|
+
reuse = [_score(row.get("reuse_value_score"), 0.0) for row in rows] or [0.0]
|
|
1068
|
+
result = TDOJudgeResult(
|
|
1069
|
+
tdo_round=round_idx,
|
|
1070
|
+
tdo_judge_source=judge_source, # type: ignore[arg-type]
|
|
1071
|
+
mentor_audited=mentor_audited,
|
|
1072
|
+
verdict=verdict, # type: ignore[arg-type]
|
|
1073
|
+
weak_pattern=proxy.get("weak_pattern"),
|
|
1074
|
+
strong_pattern=proxy.get("strong_pattern"),
|
|
1075
|
+
gap_interpretation=proxy.get("gap_interpretation"),
|
|
1076
|
+
rubric_concerns=[],
|
|
1077
|
+
grounding_concerns=[row.get("rejection_reason") for row in rejected if row.get("rejection_reason")],
|
|
1078
|
+
data_quality_concerns=[] if rows else ["no_candidate_rows"],
|
|
1079
|
+
grpo_suitability=proxy.get("grpo_suitability") or "unknown",
|
|
1080
|
+
learning_signal_quality=proxy.get("learning_signal_quality") or "unknown",
|
|
1081
|
+
reuse_value="high" if mean(reuse) >= 0.8 else "medium" if mean(reuse) >= 0.6 else "low",
|
|
1082
|
+
quality_score=round(mean(scores), 4),
|
|
1083
|
+
fidelity_score=round(mean(fidelity), 4),
|
|
1084
|
+
difficulty_score=round(mean(difficulty), 4),
|
|
1085
|
+
learnability_score=round(mean(learnability), 4),
|
|
1086
|
+
reuse_value_score=round(mean(reuse), 4),
|
|
1087
|
+
verdict_reason=reason,
|
|
1088
|
+
suggestion_for_generator=("Strengthen evidence refs, keep rows grounded, and improve weak rows in the next compiler round." if verdict in {"improve", "reject"} else None),
|
|
1089
|
+
)
|
|
1090
|
+
return result
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def _optimized_task_spec(evidence: dict[str, Any], settings: Settings, round_idx: int) -> dict[str, Any]:
|
|
1094
|
+
task = evidence.get("task") or {}
|
|
1095
|
+
raw = evidence.get("raw_task") or {}
|
|
1096
|
+
proxy = _weak_strong_proxy(evidence)
|
|
1097
|
+
instruction = task.get("normalized_instruction") or raw.get("raw_description") or raw.get("instruction") or task.get("normalized_title") or evidence["package_id"]
|
|
1098
|
+
domain = task.get("domain") or raw.get("domain") or (raw.get("raw_payload") or {}).get("domain") or "general"
|
|
1099
|
+
subdomain = task.get("subdomain") or raw.get("subdomain") or (raw.get("raw_payload") or {}).get("subdomain")
|
|
1100
|
+
return sanitize_public_obj({
|
|
1101
|
+
"source_task_id": _source_task_id(evidence),
|
|
1102
|
+
"source_package_id": evidence["package_id"],
|
|
1103
|
+
"optimized_title": task.get("normalized_title") or raw.get("raw_title") or evidence["package_id"],
|
|
1104
|
+
"optimized_instruction": instruction,
|
|
1105
|
+
"domain": domain,
|
|
1106
|
+
"subdomain": subdomain,
|
|
1107
|
+
"role": task.get("apprenticeship_role") or task.get("professional_role") or "Apprentice Agent",
|
|
1108
|
+
"expected_deliverables": task.get("output_requirements") or [task.get("expected_agent_deliverable") or "task artifacts"],
|
|
1109
|
+
"constraints": task.get("constraints") or [],
|
|
1110
|
+
"required_inputs": task.get("input_requirements") or [],
|
|
1111
|
+
"success_criteria": [item.get("criterion_description") for item in (evidence.get("rubric") or {}).get("rubric_items", [])],
|
|
1112
|
+
"failure_modes": list(dict.fromkeys((evidence.get("hillclimb") or {}).get("failed_criteria_before") or [])),
|
|
1113
|
+
"expected_economic_value": task.get("expected_economic_value") or raw.get("expected_economic_value") or "category_unknown",
|
|
1114
|
+
"expected_economic_value_rationale": "Derived from task metadata, deliverable requirements, trace complexity, and evaluation evidence.",
|
|
1115
|
+
"training_use_cases": ["supervised_finetuning_rows", "verifier_training", "reward_modeling", "preference_optimization"],
|
|
1116
|
+
"generated_by": "Experience Compiler",
|
|
1117
|
+
"apprenticeship_mode": getattr(settings, "apprenticeship_mode", None),
|
|
1118
|
+
"compiler_round": round_idx,
|
|
1119
|
+
"tdo_round": round_idx,
|
|
1120
|
+
"weak_strong_proxy_available": proxy.get("available"),
|
|
1121
|
+
**_source_audit_status(evidence),
|
|
1122
|
+
"evidence_refs": _evidence_refs(evidence),
|
|
1123
|
+
})
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
def _optimized_rubric(evidence: dict[str, Any], round_idx: int) -> dict[str, Any]:
|
|
1127
|
+
rubric = evidence.get("rubric") or {}
|
|
1128
|
+
items = rubric.get("rubric_items") or []
|
|
1129
|
+
return sanitize_public_obj({
|
|
1130
|
+
"source_task_id": _source_task_id(evidence),
|
|
1131
|
+
"source_package_id": evidence["package_id"],
|
|
1132
|
+
"tdo_round": round_idx,
|
|
1133
|
+
"rubric_items": items,
|
|
1134
|
+
"verifier_checks": [
|
|
1135
|
+
"actual_outputs.json parses and points to generated artifacts",
|
|
1136
|
+
"artifact refs resolve inside the package",
|
|
1137
|
+
"trace steps support the claimed output",
|
|
1138
|
+
"grader/verifier/evaluator evidence is consistent",
|
|
1139
|
+
],
|
|
1140
|
+
"scoring_guidance": "Use grounded artifact, trace, grader, verifier, and evaluator evidence. Do not reward permissive rubrics.",
|
|
1141
|
+
"failure_criteria": list(dict.fromkeys((evidence.get("hillclimb") or {}).get("failed_criteria_before") or [])),
|
|
1142
|
+
"artifact_checks": rubric.get("required_artifacts") or [ref for ref in _source_artifact_refs(evidence) if "/artifacts/" in ref],
|
|
1143
|
+
"environment_response_checks": ["environment_response_pairs.jsonl rows should reflect observed trace steps only"],
|
|
1144
|
+
"reward_modeling_notes": "Use score/verifier evidence and weak/strong proxies where available.",
|
|
1145
|
+
"evaluator_notes": _clean_text((evidence.get("evaluator_feedback") or {}).get("feedback_summary")),
|
|
1146
|
+
**_source_audit_status(evidence),
|
|
1147
|
+
"evidence_refs": _evidence_refs(evidence),
|
|
1148
|
+
})
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def _domain_value_profile(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
1152
|
+
task = evidence.get("task") or {}
|
|
1153
|
+
raw = evidence.get("raw_task") or {}
|
|
1154
|
+
domain = task.get("domain") or raw.get("domain") or (raw.get("raw_payload") or {}).get("domain") or "general"
|
|
1155
|
+
subdomain = task.get("subdomain") or raw.get("subdomain") or (raw.get("raw_payload") or {}).get("subdomain")
|
|
1156
|
+
traced_steps = sum(len((attempt.get("agent_trace") or {}).get("steps") or []) for attempt in (evidence.get("attempts") or {}).values())
|
|
1157
|
+
value_category = "high" if traced_steps >= 12 else "medium" if traced_steps >= 5 else "emerging"
|
|
1158
|
+
return sanitize_public_obj({
|
|
1159
|
+
"domain": domain,
|
|
1160
|
+
"subdomain": subdomain,
|
|
1161
|
+
"role": task.get("apprenticeship_role") or task.get("professional_role") or "Apprentice Agent",
|
|
1162
|
+
"apprenticeship_mode": (evidence.get("manifest") or {}).get("apprenticeship_mode"),
|
|
1163
|
+
"economic_value_category": value_category,
|
|
1164
|
+
"expected_economic_value": task.get("expected_economic_value") or raw.get("expected_economic_value") or value_category,
|
|
1165
|
+
"value_drivers": ["specialized workflow", "artifact creation", "verifiable outputs", "reusable trace evidence"],
|
|
1166
|
+
"risk_level": "medium" if traced_steps >= 8 else "low",
|
|
1167
|
+
"specialization_level": "high" if task.get("subdomain") else "medium",
|
|
1168
|
+
"automation_potential": "high" if evidence.get("process_supervision") else "medium",
|
|
1169
|
+
"training_value": "high" if traced_steps >= 8 else "medium",
|
|
1170
|
+
"evaluation_value": "high" if evidence.get("grader_results") and evidence.get("verifier_results") else "medium",
|
|
1171
|
+
"verifier_value": "high" if evidence.get("verifier_results") else "medium",
|
|
1172
|
+
"evidence_refs": _evidence_refs(evidence),
|
|
1173
|
+
})
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def _environment_spec(evidence: dict[str, Any]) -> dict[str, Any]:
|
|
1177
|
+
domains: list[str] = []
|
|
1178
|
+
surfaces: list[str] = []
|
|
1179
|
+
tools: list[str] = []
|
|
1180
|
+
operations: list[str] = []
|
|
1181
|
+
steps: list[dict[str, Any]] = []
|
|
1182
|
+
for attempt in (evidence.get("attempts") or {}).values():
|
|
1183
|
+
for step in (attempt.get("agent_trace") or {}).get("steps") or []:
|
|
1184
|
+
if step.get("action") == "user_message":
|
|
1185
|
+
continue
|
|
1186
|
+
steps.append(step)
|
|
1187
|
+
domains.append(_infer_environment_domain(step))
|
|
1188
|
+
surfaces.append(_surface_from_step(step))
|
|
1189
|
+
if step.get("tool"):
|
|
1190
|
+
tools.append(str(step.get("tool")))
|
|
1191
|
+
if step.get("operation"):
|
|
1192
|
+
operations.append(str(step.get("operation")))
|
|
1193
|
+
unique_domains = sorted(set(domains) - {"unknown"})
|
|
1194
|
+
domain = unique_domains[0] if len(unique_domains) == 1 else "mixed" if unique_domains else "unknown"
|
|
1195
|
+
environment_type = "local_filesystem" if domain == "file" else domain if domain in {"terminal", "browser", "codebase", "spreadsheet", "api", "mixed"} else "unknown"
|
|
1196
|
+
unique_surfaces = sorted(set(surfaces) - {"unknown"})
|
|
1197
|
+
def refs_from(*keys: str) -> list[str]:
|
|
1198
|
+
out: list[str] = []
|
|
1199
|
+
for step in steps:
|
|
1200
|
+
for key in keys:
|
|
1201
|
+
out.extend(_refs(step.get(key)))
|
|
1202
|
+
ref = _normalize_ref(step.get(key))
|
|
1203
|
+
if ref:
|
|
1204
|
+
out.append(ref)
|
|
1205
|
+
return list(dict.fromkeys(out))
|
|
1206
|
+
def values_from(*keys: str) -> list[str]:
|
|
1207
|
+
vals: list[str] = []
|
|
1208
|
+
for step in steps:
|
|
1209
|
+
for key in keys:
|
|
1210
|
+
value = step.get(key)
|
|
1211
|
+
if isinstance(value, list):
|
|
1212
|
+
vals.extend(str(v) for v in value if v not in (None, ""))
|
|
1213
|
+
elif value not in (None, ""):
|
|
1214
|
+
vals.append(str(value))
|
|
1215
|
+
return sorted(set(vals))
|
|
1216
|
+
multi_agent_present = any(step.get("subagent_id") or step.get("delegation_id") for step in steps)
|
|
1217
|
+
multimodal_present = any(step.get("modality") or step.get("modalities") or _surface_from_step(step) in {"multimodal", "document", "pdf", "table"} for step in steps)
|
|
1218
|
+
ui_present = any(_surface_from_step(step) in {"browser", "computer_use", "cloud_console", "saas_app"} or step.get("screenshot_ref") or step.get("visible_state_ref") for step in steps)
|
|
1219
|
+
physical_present = any(_surface_from_step(step) in {"device", "industrial", "physical"} or (step.get("environment_domain") in {"device", "industrial", "physical", "robotics", "lab"}) for step in steps)
|
|
1220
|
+
return sanitize_public_obj({
|
|
1221
|
+
"environment_type": environment_type,
|
|
1222
|
+
"environment_domain": domain,
|
|
1223
|
+
"interaction_surfaces": unique_surfaces,
|
|
1224
|
+
"surface_requirements": {
|
|
1225
|
+
surface: "capture surface-specific ids, state refs, requests/responses, evidence refs, and status transitions when available"
|
|
1226
|
+
for surface in unique_surfaces
|
|
1227
|
+
},
|
|
1228
|
+
"required_tools": sorted(set(tools)),
|
|
1229
|
+
"runtime_dependencies": ["python"] if any("python" in t.lower() for t in tools) else [],
|
|
1230
|
+
"setup_commands": [],
|
|
1231
|
+
"input_artifacts": [ref for ref in _source_artifact_refs(evidence) if ref.startswith("input/") or "/input/" in ref],
|
|
1232
|
+
"initial_state_refs": ["task/task_intake_spec.json", "rubric/rubric.json"],
|
|
1233
|
+
"allowed_actions": sorted(set(operations)),
|
|
1234
|
+
"reset_strategy": "copy_workspace" if environment_type in {"local_filesystem", "codebase", "terminal"} else "unknown",
|
|
1235
|
+
"verification_commands": [],
|
|
1236
|
+
"scoring_refs": [ref for ref in _evidence_refs(evidence) if ref.startswith("grading/")],
|
|
1237
|
+
"expected_output_refs": [ref for ref in _source_artifact_refs(evidence) if "/artifacts/" in ref],
|
|
1238
|
+
"replay_smoke_test": {"available": False, "command": None, "success_condition": None},
|
|
1239
|
+
"multi_agent": {
|
|
1240
|
+
"present": multi_agent_present,
|
|
1241
|
+
"agents": values_from("agent_id", "agent_name", "subagent_id", "subagent_name"),
|
|
1242
|
+
"coordination_pattern": (values_from("coordination_pattern") or [None])[0],
|
|
1243
|
+
"handoff_refs": refs_from("handoff_payload_ref", "handoff_output_refs", "subagent_output_refs"),
|
|
1244
|
+
},
|
|
1245
|
+
"multimodal": {
|
|
1246
|
+
"present": multimodal_present,
|
|
1247
|
+
"modalities": values_from("modality", "modalities"),
|
|
1248
|
+
"artifact_refs": refs_from("artifact_refs", "screenshot_ref", "document_ref", "workbook_ref", "table_ref"),
|
|
1249
|
+
},
|
|
1250
|
+
"ui_or_software": {
|
|
1251
|
+
"present": ui_present,
|
|
1252
|
+
"surfaces": [s for s in unique_surfaces if s in {"browser", "computer_use", "cloud_console", "saas_app", "cli", "codebase", "file"}],
|
|
1253
|
+
"state_refs": refs_from("visible_state_ref", "screenshot_ref", "before_state_ref", "after_state_ref", "software_state_ref"),
|
|
1254
|
+
},
|
|
1255
|
+
"physical_or_industrial": {
|
|
1256
|
+
"present": physical_present,
|
|
1257
|
+
"environment_type": (values_from("physical_environment_type") or [None])[0],
|
|
1258
|
+
"device_refs": refs_from("sensor_readings_ref", "telemetry_ref", "calibration_ref"),
|
|
1259
|
+
"safety_refs": refs_from("safety_limit_refs", "compliance_refs"),
|
|
1260
|
+
"telemetry_refs": refs_from("telemetry_ref"),
|
|
1261
|
+
},
|
|
1262
|
+
"api": {
|
|
1263
|
+
"providers": values_from("api_provider"),
|
|
1264
|
+
"endpoints": values_from("api_endpoint"),
|
|
1265
|
+
"auth_modes": values_from("auth_mode"),
|
|
1266
|
+
"schemas": refs_from("request_schema_ref", "response_schema_ref"),
|
|
1267
|
+
},
|
|
1268
|
+
"mcp": {
|
|
1269
|
+
"servers": values_from("mcp_server"),
|
|
1270
|
+
"tools": values_from("mcp_tool_name"),
|
|
1271
|
+
"resources": values_from("mcp_resource_uri"),
|
|
1272
|
+
"prompts": values_from("mcp_prompt_name"),
|
|
1273
|
+
},
|
|
1274
|
+
"database": {
|
|
1275
|
+
"types": values_from("database_type"),
|
|
1276
|
+
"schemas": values_from("schema_name"),
|
|
1277
|
+
"tables": values_from("table_name"),
|
|
1278
|
+
},
|
|
1279
|
+
"spreadsheet": {
|
|
1280
|
+
"workbooks": refs_from("workbook_ref"),
|
|
1281
|
+
"sheets": values_from("sheet_name"),
|
|
1282
|
+
"tables": refs_from("table_ref", "table_refs"),
|
|
1283
|
+
},
|
|
1284
|
+
"software_execution": {
|
|
1285
|
+
"commands": values_from("command", "test_command"),
|
|
1286
|
+
"tests": refs_from("test_result_ref"),
|
|
1287
|
+
"dependencies": refs_from("dependency_changes_ref"),
|
|
1288
|
+
},
|
|
1289
|
+
"async": {
|
|
1290
|
+
"jobs": values_from("job_id"),
|
|
1291
|
+
"queues": values_from("queue_name"),
|
|
1292
|
+
"webhooks": values_from("webhook_id"),
|
|
1293
|
+
"schedulers": values_from("schedule_id"),
|
|
1294
|
+
},
|
|
1295
|
+
"collaboration": {
|
|
1296
|
+
"platforms": values_from("platform_name"),
|
|
1297
|
+
"threads": values_from("thread_id"),
|
|
1298
|
+
"decisions": refs_from("decision_refs"),
|
|
1299
|
+
},
|
|
1300
|
+
"documents": {
|
|
1301
|
+
"documents": refs_from("document_ref"),
|
|
1302
|
+
"extracts": refs_from("text_extract_ref", "ocr_text_ref", "table_extract_refs"),
|
|
1303
|
+
"citations": refs_from("citation_refs"),
|
|
1304
|
+
},
|
|
1305
|
+
"cross_system": {
|
|
1306
|
+
"systems": unique_surfaces,
|
|
1307
|
+
"dependencies": values_from("cross_system_dependencies"),
|
|
1308
|
+
"system_of_record": (values_from("system_of_record") or [None])[0],
|
|
1309
|
+
},
|
|
1310
|
+
"evidence_status": ["No simulated environment response is generated in v0.1.7."],
|
|
1311
|
+
})
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
def _counts_by_type(rows: list[dict[str, Any]]) -> dict[str, int]:
|
|
1315
|
+
return dict(Counter(str(row.get("data_type") or "unknown") for row in rows))
|
|
1316
|
+
|
|
1317
|
+
|
|
1318
|
+
def _counts_with_surface_zeros(rows: list[dict[str, Any]]) -> dict[str, int]:
|
|
1319
|
+
counts = _counts_by_type(rows)
|
|
1320
|
+
for data_type in SURFACE_FILES:
|
|
1321
|
+
counts.setdefault(data_type, 0)
|
|
1322
|
+
return counts
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
def _average(rows: list[dict[str, Any]], key: str) -> float | None:
|
|
1326
|
+
values = [_score(row.get(key), 0.0) for row in rows if row.get(key) is not None]
|
|
1327
|
+
return round(mean(values), 4) if values else None
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
def _grpo_distribution(rows: list[dict[str, Any]]) -> dict[str, int]:
|
|
1331
|
+
return dict(Counter(str(row.get("grpo_suitability") or "unknown") for row in rows))
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
def _write_tdo_outputs(tdo_dir: Path, accepted: list[dict[str, Any]], rejected: list[dict[str, Any]]) -> None:
|
|
1335
|
+
for name in sorted(set(ROW_FILE_BY_TYPE.values()) | {"rejected_rows.jsonl"}):
|
|
1336
|
+
(tdo_dir / name).parent.mkdir(parents=True, exist_ok=True)
|
|
1337
|
+
(tdo_dir / name).write_text("")
|
|
1338
|
+
by_type: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
|
1339
|
+
for row in accepted:
|
|
1340
|
+
by_type[str(row.get("data_type") or "unknown")].append(row)
|
|
1341
|
+
for data_type, rows in by_type.items():
|
|
1342
|
+
target = ROW_FILE_BY_TYPE.get(data_type)
|
|
1343
|
+
if target:
|
|
1344
|
+
_write_jsonl(tdo_dir / target, rows)
|
|
1345
|
+
_write_jsonl(tdo_dir / "rejected_rows.jsonl", rejected)
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
def _copy_jsonl_public(src: Path, dst: Path) -> None:
|
|
1349
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
1350
|
+
dst.write_text("")
|
|
1351
|
+
if not src.exists():
|
|
1352
|
+
return
|
|
1353
|
+
for row in read_jsonl(src):
|
|
1354
|
+
append_jsonl(dst, sanitize_public_obj(row) if isinstance(row, dict) else row)
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
def _compiler_public_obj(obj: Any) -> Any:
|
|
1358
|
+
rename = {
|
|
1359
|
+
"tdo_id": "compiler_id",
|
|
1360
|
+
"tdo_status": "compiler_status",
|
|
1361
|
+
"tdo_round": "compiler_round",
|
|
1362
|
+
"tdo_judge_source": "judge_source",
|
|
1363
|
+
"tdo_output_refs": "compiler_output_refs",
|
|
1364
|
+
"tdo_rows_by_type": "compiler_rows_by_type",
|
|
1365
|
+
}
|
|
1366
|
+
if isinstance(obj, dict):
|
|
1367
|
+
out: dict[str, Any] = {}
|
|
1368
|
+
for key, value in obj.items():
|
|
1369
|
+
out[rename.get(str(key), str(key))] = _compiler_public_obj(value)
|
|
1370
|
+
return sanitize_public_obj(out)
|
|
1371
|
+
if isinstance(obj, list):
|
|
1372
|
+
return [_compiler_public_obj(item) for item in obj]
|
|
1373
|
+
return sanitize_public_obj(obj)
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
def _copy_compiler_jsonl_public(src: Path, dst: Path) -> None:
|
|
1377
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
1378
|
+
dst.write_text("")
|
|
1379
|
+
if not src.exists():
|
|
1380
|
+
return
|
|
1381
|
+
for row in read_jsonl(src):
|
|
1382
|
+
append_jsonl(dst, _compiler_public_obj(row))
|
|
1383
|
+
|
|
1384
|
+
|
|
1385
|
+
def _copy_compiler_json_public(src: Path, dst: Path) -> None:
|
|
1386
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
1387
|
+
if not src.exists():
|
|
1388
|
+
write_json(dst, {})
|
|
1389
|
+
return
|
|
1390
|
+
write_json(dst, _compiler_public_obj(read_json(src)))
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
def _copy_json_public(src: Path, dst: Path, default: dict[str, Any] | None = None) -> None:
|
|
1394
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
1395
|
+
if src.exists():
|
|
1396
|
+
write_json(dst, sanitize_public_obj(read_json(src)))
|
|
1397
|
+
else:
|
|
1398
|
+
write_json(dst, sanitize_public_obj(default or {}))
|
|
1399
|
+
|
|
1400
|
+
|
|
1401
|
+
def _task_title(evidence: dict[str, Any]) -> str:
|
|
1402
|
+
task = evidence.get("task") or {}
|
|
1403
|
+
raw = evidence.get("raw_task") or {}
|
|
1404
|
+
return _clean_text(task.get("normalized_title") or raw.get("raw_title") or evidence.get("package_id") or "Agent Apprenticeship workflow", 240)
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
def _task_instruction(evidence: dict[str, Any]) -> str:
|
|
1408
|
+
task = evidence.get("task") or {}
|
|
1409
|
+
raw = evidence.get("raw_task") or {}
|
|
1410
|
+
return _clean_text(task.get("normalized_instruction") or raw.get("raw_description") or _task_title(evidence), 1600)
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
def _domain(evidence: dict[str, Any]) -> str:
|
|
1414
|
+
task = evidence.get("task") or {}
|
|
1415
|
+
return _clean_text(task.get("domain") or task.get("environment_domain") or "mixed", 80)
|
|
1416
|
+
|
|
1417
|
+
|
|
1418
|
+
def _success_criteria(evidence: dict[str, Any]) -> list[str]:
|
|
1419
|
+
task = evidence.get("task") or {}
|
|
1420
|
+
rubric = evidence.get("rubric") or {}
|
|
1421
|
+
raw_items = task.get("success_criteria") or task.get("expected_deliverables") or []
|
|
1422
|
+
if not raw_items:
|
|
1423
|
+
raw_items = [item.get("criterion_description") for item in rubric.get("rubric_items", []) if isinstance(item, dict)]
|
|
1424
|
+
if isinstance(raw_items, str):
|
|
1425
|
+
raw_items = [raw_items]
|
|
1426
|
+
return [_clean_text(item, 180) for item in raw_items if item][:8] or [
|
|
1427
|
+
"Produce task-specific deliverables.",
|
|
1428
|
+
"Preserve source evidence and package-relative artifact refs.",
|
|
1429
|
+
"Pass bundle check and verifier checks where available.",
|
|
1430
|
+
]
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
def _output_summary(evidence: dict[str, Any]) -> str:
|
|
1434
|
+
return _best_output_summary(evidence)
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
def _skill_markdown(evidence: dict[str, Any], report: dict[str, Any], rows_accepted: dict[str, Any], settings: Settings) -> str:
|
|
1438
|
+
task = evidence.get("task") or {}
|
|
1439
|
+
actual_outputs = evidence.get("actual_outputs") or []
|
|
1440
|
+
output_summaries = [
|
|
1441
|
+
_clean_text(row.get("output_summary"), 240)
|
|
1442
|
+
for row in actual_outputs
|
|
1443
|
+
if isinstance(row, dict) and row.get("output_summary")
|
|
1444
|
+
][:3]
|
|
1445
|
+
artifact_refs = sorted(
|
|
1446
|
+
{
|
|
1447
|
+
ref
|
|
1448
|
+
for row in actual_outputs
|
|
1449
|
+
if isinstance(row, dict)
|
|
1450
|
+
for ref in _refs((row.get("artifact_refs") or []) + (row.get("deliverable_refs") or []) + (row.get("files_created") or []))
|
|
1451
|
+
}
|
|
1452
|
+
)[:12]
|
|
1453
|
+
domain = _clean_text(task.get("domain") or "mixed", 80)
|
|
1454
|
+
subdomain = _clean_text(task.get("subdomain") or "general", 80)
|
|
1455
|
+
criteria = task.get("success_criteria") or task.get("expected_deliverables") or []
|
|
1456
|
+
if isinstance(criteria, str):
|
|
1457
|
+
criteria = [criteria]
|
|
1458
|
+
criteria = [_clean_text(item, 180) for item in criteria][:8] or ["Produce task-specific deliverables and preserve evidence refs."]
|
|
1459
|
+
title = _task_title(evidence)
|
|
1460
|
+
instruction = _task_instruction(evidence)
|
|
1461
|
+
evidence_refs = _evidence_refs(evidence)[:14]
|
|
1462
|
+
if not output_summaries:
|
|
1463
|
+
output_summaries = [_output_summary(evidence)]
|
|
1464
|
+
if not artifact_refs:
|
|
1465
|
+
artifact_refs = _source_artifact_refs(evidence)[:12]
|
|
1466
|
+
concrete_evidence_refs = [
|
|
1467
|
+
ref for ref in evidence_refs + artifact_refs
|
|
1468
|
+
if str(ref).startswith(("traces/", "attempts/", "artifacts/", "outputs/", "evaluation/", "grading/", "feedback/", "signals/", "loops/"))
|
|
1469
|
+
]
|
|
1470
|
+
if not concrete_evidence_refs:
|
|
1471
|
+
output_summaries = [
|
|
1472
|
+
"Concrete trace, artifact, attempt, or evaluation evidence was not available in this source package.",
|
|
1473
|
+
"Use this skill as task/rubric-derived guidance only, and require fresh execution evidence before accepting training or evaluation rows.",
|
|
1474
|
+
]
|
|
1475
|
+
row_summary = ", ".join(f"{k}: {v}" for k, v in sorted((rows_accepted or {}).items()) if v) or "grounded rows recorded"
|
|
1476
|
+
audit = "mentor-audited" if report.get("mentor_audited") else "apprentice self-judged; future mentor audit recommended"
|
|
1477
|
+
return "\n".join(
|
|
1478
|
+
[
|
|
1479
|
+
f"# Skill Pack: {title}",
|
|
1480
|
+
"",
|
|
1481
|
+
"## Best Use",
|
|
1482
|
+
f"Use this skill for {domain}/{subdomain} work resembling: {instruction}",
|
|
1483
|
+
"It is useful when an Apprentice Agent must turn a real workflow into deliverables, traces, reusable experience, verifier evidence, and public ecosystem-ready package material.",
|
|
1484
|
+
"",
|
|
1485
|
+
"## Prerequisites",
|
|
1486
|
+
"- Access to the task inputs, artifacts, and package-relative evidence refs.",
|
|
1487
|
+
"- A writable task workspace for outputs and `actual_outputs.json`.",
|
|
1488
|
+
"- Mentor Model Provider or expert audit when semantic outcome quality must be judged.",
|
|
1489
|
+
"",
|
|
1490
|
+
"## Inputs",
|
|
1491
|
+
*(f"- {_clean_text(item, 180)}" for item in (task.get("required_inputs") or task.get("input_requirements") or ["Current task instruction, task assets, and package evidence refs."])[:8]),
|
|
1492
|
+
"",
|
|
1493
|
+
"## Outputs",
|
|
1494
|
+
*(f"- {_clean_text(item, 180)}" for item in (task.get("expected_deliverables") or task.get("output_requirements") or criteria)[:8]),
|
|
1495
|
+
"",
|
|
1496
|
+
"## Workflow",
|
|
1497
|
+
"1. Restate the user task as an Apprentice-generated task spec with domain, role, constraints, deliverables, and evidence requirements.",
|
|
1498
|
+
"2. Draft a rubric before execution, then preserve whether a Mentor Model, expert, or organization flow audited it.",
|
|
1499
|
+
"3. Execute one meaningful action per trace step, keeping observation, input, output, and state_change separate.",
|
|
1500
|
+
"4. Write deliverables under package-relative artifact paths and summarize them through `actual_outputs.json`.",
|
|
1501
|
+
"5. Use evaluator, verifier, grader, or expert records as outcome evidence; do not rely on Apprentice self-eval.",
|
|
1502
|
+
"6. Run the Experience Compiler after outputs exist so runtime learning and training-time rows cite concrete evidence refs.",
|
|
1503
|
+
"",
|
|
1504
|
+
"## Decision Points",
|
|
1505
|
+
f"- Apprenticeship Mode: {getattr(settings, 'apprenticeship_mode', 'autonomous')}.",
|
|
1506
|
+
f"- Compiler judge source: {report.get('tdo_judge_source')}; audit status: {audit}.",
|
|
1507
|
+
f"- Accepted learning objects: {row_summary}.",
|
|
1508
|
+
"",
|
|
1509
|
+
"## Common Failure Patterns",
|
|
1510
|
+
"- Missing artifact refs make outputs hard to verify.",
|
|
1511
|
+
"- Compressed trace steps lose causality and reduce observed state-transition value.",
|
|
1512
|
+
"- Unavailable Mentor Model Provider means compiler rows are self-judged and should be audited before quality claims.",
|
|
1513
|
+
"- Private paths or secrets make ecosystem sharing unsafe and must be redacted.",
|
|
1514
|
+
"",
|
|
1515
|
+
"## Recovery Strategies",
|
|
1516
|
+
"- Add missing package-relative artifact refs and rerun bundle check.",
|
|
1517
|
+
"- Split vague steps into single-action trace steps with caused_by links.",
|
|
1518
|
+
"- Re-run follow-up loops when user corrections change deliverables.",
|
|
1519
|
+
"- Preserve partial or failed runs honestly instead of rewriting them as success.",
|
|
1520
|
+
"",
|
|
1521
|
+
"## Tool Recipe",
|
|
1522
|
+
"- Use the tools and surfaces shown in the source traces; do not assume unavailable systems.",
|
|
1523
|
+
"- Store large command outputs, documents, tables, screenshots, and logs as artifact refs instead of embedding them in JSON.",
|
|
1524
|
+
"- Run verifier or bundle checks when present in the package evidence.",
|
|
1525
|
+
"",
|
|
1526
|
+
"## Evidence & Artifact Expectations",
|
|
1527
|
+
*(f"- {summary}" for summary in output_summaries),
|
|
1528
|
+
*(f"- artifact: `{ref}`" for ref in artifact_refs),
|
|
1529
|
+
*(f"- evidence ref: `{ref}`" for ref in evidence_refs),
|
|
1530
|
+
"",
|
|
1531
|
+
"## Verification Checklist",
|
|
1532
|
+
*(f"- {item}" for item in criteria),
|
|
1533
|
+
"- `actual_outputs.json` exists and names deliverable refs.",
|
|
1534
|
+
"- Trace steps include concrete evidence for generated rows.",
|
|
1535
|
+
"- Bundle check and public sanitizer pass without private paths or secrets.",
|
|
1536
|
+
"",
|
|
1537
|
+
"## Examples",
|
|
1538
|
+
f"- Source task: {title}",
|
|
1539
|
+
f"- Reusable domain: {domain}",
|
|
1540
|
+
f"- Compiler outputs: {row_summary}",
|
|
1541
|
+
"",
|
|
1542
|
+
"## Transfer Notes",
|
|
1543
|
+
"- Use the task/rubric pattern for adjacent workflows in the same domain.",
|
|
1544
|
+
"- Convert verifier checklist items into future eval cases.",
|
|
1545
|
+
"- Reuse observed state-transition rows for world-model-ready interaction data.",
|
|
1546
|
+
"",
|
|
1547
|
+
]
|
|
1548
|
+
)
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
def _write_experience_compiler_outputs(
|
|
1552
|
+
package_root: Path,
|
|
1553
|
+
tdo_dir: Path,
|
|
1554
|
+
report: dict[str, Any],
|
|
1555
|
+
evidence: dict[str, Any],
|
|
1556
|
+
rows_accepted: dict[str, Any],
|
|
1557
|
+
settings: Settings,
|
|
1558
|
+
) -> None:
|
|
1559
|
+
root = package_root / "experience_compiler"
|
|
1560
|
+
if root.exists():
|
|
1561
|
+
shutil.rmtree(root)
|
|
1562
|
+
for name in [
|
|
1563
|
+
"experience_pack",
|
|
1564
|
+
"skill_pack",
|
|
1565
|
+
"runtime_learning_package",
|
|
1566
|
+
"training_time_package",
|
|
1567
|
+
"evals",
|
|
1568
|
+
"eval_package",
|
|
1569
|
+
"verifier",
|
|
1570
|
+
"reward",
|
|
1571
|
+
"environment",
|
|
1572
|
+
"reports",
|
|
1573
|
+
]:
|
|
1574
|
+
(root / name).mkdir(parents=True, exist_ok=True)
|
|
1575
|
+
|
|
1576
|
+
manifest_src = read_json(tdo_dir / "tdo_manifest.json") if (tdo_dir / "tdo_manifest.json").exists() else {}
|
|
1577
|
+
compiler_refs = [
|
|
1578
|
+
"experience_compiler/compiler_manifest.json",
|
|
1579
|
+
"experience_compiler/compiler_report.json",
|
|
1580
|
+
"experience_compiler/compiler_trace.jsonl",
|
|
1581
|
+
"experience_compiler/compiler_rounds.jsonl",
|
|
1582
|
+
"experience_compiler/compiler_recipe.json",
|
|
1583
|
+
"experience_compiler/compiler_judge_results.jsonl",
|
|
1584
|
+
"experience_compiler/learning_objects_index.json",
|
|
1585
|
+
"experience_compiler/skill_pack/skill.md",
|
|
1586
|
+
]
|
|
1587
|
+
compiler_manifest = {
|
|
1588
|
+
"schema_version": "aa-experience-compiler-v0.1",
|
|
1589
|
+
"compiler_id": str(report.get("tdo_id") or "").replace("tdo_", "compiler_", 1) or f"compiler_{_safe_id(package_root.name)}",
|
|
1590
|
+
"compiler_status": report.get("tdo_status"),
|
|
1591
|
+
"source_package_id": report.get("source_package_id"),
|
|
1592
|
+
"source_task_id": report.get("source_task_id"),
|
|
1593
|
+
"apprenticeship_mode": getattr(settings, "apprenticeship_mode", None),
|
|
1594
|
+
"judge_source": report.get("tdo_judge_source"),
|
|
1595
|
+
"mentor_audited": report.get("mentor_audited"),
|
|
1596
|
+
"rounds_run": report.get("rounds_run"),
|
|
1597
|
+
"rows_by_type": rows_accepted,
|
|
1598
|
+
"supported_runtime_uses": ["experience_pack", "skill_pack", "runtime_learning_package"],
|
|
1599
|
+
"supported_training_uses": ["training_examples", "evaluation_examples", "verifier_examples", "reward_model_rows", "preference_pairs", "environment_response_pairs"],
|
|
1600
|
+
"loop_review_refs": evidence.get("loop_review_refs") or [],
|
|
1601
|
+
"compiler_output_refs": compiler_refs,
|
|
1602
|
+
"created_at": report.get("created_at") or utc_now(),
|
|
1603
|
+
}
|
|
1604
|
+
write_json(root / "compiler_manifest.json", compiler_manifest)
|
|
1605
|
+
compiler_report = _compiler_public_obj({
|
|
1606
|
+
**report,
|
|
1607
|
+
"public_feature_name": "Experience Compiler",
|
|
1608
|
+
"description": "The Experience Compiler turns completed agent workflows into reusable runtime learning and training-time data.",
|
|
1609
|
+
"apprenticeship_mode": getattr(settings, "apprenticeship_mode", None),
|
|
1610
|
+
})
|
|
1611
|
+
write_json(root / "compiler_report.json", sanitize_public_obj(compiler_report))
|
|
1612
|
+
task_refs = [
|
|
1613
|
+
ref
|
|
1614
|
+
for ref in [
|
|
1615
|
+
"task/task_packet.json",
|
|
1616
|
+
"task/task_intake_spec.json",
|
|
1617
|
+
"task/raw_task_record.json",
|
|
1618
|
+
"source_task/task_packet.json",
|
|
1619
|
+
]
|
|
1620
|
+
if (package_root / ref).exists()
|
|
1621
|
+
]
|
|
1622
|
+
if not task_refs:
|
|
1623
|
+
task_refs = ["task/task_packet.json"]
|
|
1624
|
+
write_json(root / "source_evidence_map.json", sanitize_public_obj({
|
|
1625
|
+
"source_package_id": report.get("source_package_id"),
|
|
1626
|
+
"source_task_id": report.get("source_task_id"),
|
|
1627
|
+
"task_refs": task_refs,
|
|
1628
|
+
"artifact_refs": _source_artifact_refs(evidence),
|
|
1629
|
+
"evaluation_refs": _evidence_refs(evidence),
|
|
1630
|
+
"loop_review_refs": evidence.get("loop_review_refs") or [],
|
|
1631
|
+
"trace_refs": [str(trace.get("trace_id") or trace.get("source_trace_id") or "trace") for trace in evidence.get("traces") or []],
|
|
1632
|
+
}))
|
|
1633
|
+
write_json(root / "quality_report.json", sanitize_public_obj({
|
|
1634
|
+
"source_package_id": report.get("source_package_id"),
|
|
1635
|
+
"compiler_status": report.get("tdo_status"),
|
|
1636
|
+
"average_quality_score": report.get("average_quality_score"),
|
|
1637
|
+
"average_fidelity_score": report.get("average_fidelity_score"),
|
|
1638
|
+
"average_reuse_value_score": report.get("average_reuse_value_score"),
|
|
1639
|
+
"omitted_outputs": report.get("omitted_outputs") or [],
|
|
1640
|
+
"not_generated_counts": report.get("not_generated_counts") or {},
|
|
1641
|
+
"generation_notes": report.get("generation_notes") or [],
|
|
1642
|
+
"grounding": "Rows are derived from source traces, actual_outputs, artifacts, rubrics, and evaluator/verifier records when available.",
|
|
1643
|
+
}))
|
|
1644
|
+
_copy_compiler_jsonl_public(tdo_dir / "tdo_trace.jsonl", root / "compiler_trace.jsonl")
|
|
1645
|
+
_copy_compiler_jsonl_public(tdo_dir / "tdo_rounds.jsonl", root / "compiler_rounds.jsonl")
|
|
1646
|
+
_copy_compiler_json_public(tdo_dir / "tdo_recipe.json", root / "compiler_recipe.json")
|
|
1647
|
+
_copy_compiler_jsonl_public(tdo_dir / "tdo_judge_results.jsonl", root / "compiler_judge_results.jsonl")
|
|
1648
|
+
_copy_compiler_jsonl_public(tdo_dir / "tdo_rounds.jsonl", root / "reports" / "compiler_rounds.jsonl")
|
|
1649
|
+
_copy_compiler_jsonl_public(tdo_dir / "tdo_judge_results.jsonl", root / "reports" / "compiler_judge_results.jsonl")
|
|
1650
|
+
_copy_compiler_json_public(tdo_dir / "tdo_recipe.json", root / "reports" / "compiler_recipe.json")
|
|
1651
|
+
|
|
1652
|
+
jsonl_map = {
|
|
1653
|
+
"training_examples.jsonl": root / "training_time_package" / "training_examples.jsonl",
|
|
1654
|
+
"evaluation_examples.jsonl": root / "training_time_package" / "evaluation_examples.jsonl",
|
|
1655
|
+
"verifier_examples.jsonl": root / "training_time_package" / "verifier_examples.jsonl",
|
|
1656
|
+
"reward_model_rows.jsonl": root / "training_time_package" / "reward_model_rows.jsonl",
|
|
1657
|
+
"preference_pairs.jsonl": root / "training_time_package" / "preference_pairs.jsonl",
|
|
1658
|
+
"failure_cases.jsonl": root / "training_time_package" / "failure_cases.jsonl",
|
|
1659
|
+
"task_variants.jsonl": root / "training_time_package" / "task_variants.jsonl",
|
|
1660
|
+
"environment_response_pairs.jsonl": root / "environment" / "environment_response_pairs.jsonl",
|
|
1661
|
+
"multi_agent_handoffs.jsonl": root / "environment" / "multi_agent_handoffs.jsonl",
|
|
1662
|
+
"multimodal_artifacts.jsonl": root / "environment" / "multimodal_artifacts.jsonl",
|
|
1663
|
+
"ui_interaction_events.jsonl": root / "environment" / "ui_interaction_events.jsonl",
|
|
1664
|
+
"physical_environment_events.jsonl": root / "environment" / "physical_environment_events.jsonl",
|
|
1665
|
+
"api_interaction_events.jsonl": root / "environment" / "api_interaction_events.jsonl",
|
|
1666
|
+
"mcp_interaction_events.jsonl": root / "environment" / "mcp_interaction_events.jsonl",
|
|
1667
|
+
"database_events.jsonl": root / "environment" / "database_events.jsonl",
|
|
1668
|
+
"structured_table_events.jsonl": root / "environment" / "structured_table_events.jsonl",
|
|
1669
|
+
"software_execution_events.jsonl": root / "environment" / "software_execution_events.jsonl",
|
|
1670
|
+
"search_retrieval_events.jsonl": root / "environment" / "search_retrieval_events.jsonl",
|
|
1671
|
+
"enterprise_app_events.jsonl": root / "environment" / "enterprise_app_events.jsonl",
|
|
1672
|
+
"async_workflow_events.jsonl": root / "environment" / "async_workflow_events.jsonl",
|
|
1673
|
+
"collaboration_events.jsonl": root / "environment" / "collaboration_events.jsonl",
|
|
1674
|
+
"document_events.jsonl": root / "environment" / "document_events.jsonl",
|
|
1675
|
+
"cross_system_events.jsonl": root / "environment" / "cross_system_events.jsonl",
|
|
1676
|
+
"rejected_rows.jsonl": root / "reports" / "rejected_rows.jsonl",
|
|
1677
|
+
"meta_optimization_records.jsonl": root / "reports" / "meta_optimization_records.jsonl",
|
|
1678
|
+
}
|
|
1679
|
+
for src_name, dst in jsonl_map.items():
|
|
1680
|
+
_copy_jsonl_public(tdo_dir / src_name, dst)
|
|
1681
|
+
|
|
1682
|
+
_copy_json_public(tdo_dir / "optimized_task_spec.json", root / "reports" / "optimized_task_spec.json")
|
|
1683
|
+
_copy_json_public(tdo_dir / "optimized_rubric.json", root / "reports" / "optimized_rubric.json")
|
|
1684
|
+
_copy_json_public(tdo_dir / "domain_value_profile.json", root / "reports" / "domain_value_profile.json")
|
|
1685
|
+
_copy_json_public(tdo_dir / "environment_spec.json", root / "environment" / "environment_spec.json")
|
|
1686
|
+
_copy_jsonl_public(tdo_dir / "evaluation_examples.jsonl", root / "evals" / "evaluation_examples.jsonl")
|
|
1687
|
+
_copy_jsonl_public(tdo_dir / "verifier_examples.jsonl", root / "verifier" / "verifier_examples.jsonl")
|
|
1688
|
+
_copy_jsonl_public(tdo_dir / "reward_model_rows.jsonl", root / "reward" / "reward_model_rows.jsonl")
|
|
1689
|
+
_copy_jsonl_public(tdo_dir / "preference_pairs.jsonl", root / "reward" / "preference_pairs.jsonl")
|
|
1690
|
+
|
|
1691
|
+
write_json(root / "experience_pack" / "experience_pack_manifest.json", {
|
|
1692
|
+
"pack_type": "experience_pack",
|
|
1693
|
+
"source_package_id": report.get("source_package_id"),
|
|
1694
|
+
"skill_pack_ref": "experience_compiler/skill_pack/skill.md",
|
|
1695
|
+
"runtime_learning_package_ref": "experience_compiler/runtime_learning_package/runtime_learning_manifest.json",
|
|
1696
|
+
"training_time_package_ref": "experience_compiler/training_time_package/training_time_manifest.json",
|
|
1697
|
+
})
|
|
1698
|
+
skill_md = _skill_markdown(evidence, report, rows_accepted, settings)
|
|
1699
|
+
(root / "skill_pack" / "skill.md").write_text(sanitize_public_text(skill_md) or skill_md)
|
|
1700
|
+
write_json(root / "skill_pack" / "skill_manifest.json", {
|
|
1701
|
+
"source_package_id": report.get("source_package_id"),
|
|
1702
|
+
"source_task_id": report.get("source_task_id"),
|
|
1703
|
+
"skill_ref": "experience_compiler/skill_pack/skill.md",
|
|
1704
|
+
"sections": ["Best Use", "Prerequisites", "Workflow", "Decision Points", "Common Failure Patterns", "Recovery Strategies", "Evidence & Artifact Expectations", "Verification Checklist", "Examples", "Transfer Notes"],
|
|
1705
|
+
"quality_floor": "task-specific, evidence-grounded, non-generic",
|
|
1706
|
+
})
|
|
1707
|
+
title = _task_title(evidence) or str(report.get("source_task_id") or "Compiled skill")
|
|
1708
|
+
instruction = _task_instruction(evidence)
|
|
1709
|
+
criteria = _success_criteria(evidence)
|
|
1710
|
+
canonical_skill = sanitize_public_obj({
|
|
1711
|
+
"skill_id": f"skill_{_safe_id(str(report.get('source_package_id') or package_root.name))}",
|
|
1712
|
+
"title": title,
|
|
1713
|
+
"summary": f"Reusable runtime skill compiled from the completed workflow: {title}",
|
|
1714
|
+
"when_to_use": [f"Use for similar {(_domain(evidence) or 'workflow')} tasks with matching artifacts, constraints, or verification needs."],
|
|
1715
|
+
"procedure": [line.strip("- ") for line in _skill_markdown(evidence, report, rows_accepted, settings).splitlines() if line.strip().startswith("- ")][:12],
|
|
1716
|
+
"workflow_steps": [
|
|
1717
|
+
"Confirm the current task matches the source domain, deliverables, and constraints.",
|
|
1718
|
+
"Reuse the source workflow pattern while keeping the current user instruction authoritative.",
|
|
1719
|
+
"Create package-relative artifacts and update actual_outputs.json.",
|
|
1720
|
+
"Verify outputs against the compiled checklist and source evidence expectations.",
|
|
1721
|
+
],
|
|
1722
|
+
"decision_rules": [
|
|
1723
|
+
"Apply this skill only when query terms, domain, required artifacts, or verifier needs overlap with the source task.",
|
|
1724
|
+
"Prefer current-task evidence over transferred assumptions.",
|
|
1725
|
+
"Stop or ask for expert/model review when required evidence is missing or semantic quality is uncertain.",
|
|
1726
|
+
],
|
|
1727
|
+
"verifier_checks": criteria + ["actual_outputs.json names the deliverables", "source evidence refs remain package-relative"],
|
|
1728
|
+
"failure_modes": [
|
|
1729
|
+
"The current task lacks the source task's required inputs or tools.",
|
|
1730
|
+
"Artifact refs are missing, absolute, or unresolved.",
|
|
1731
|
+
"The workflow is copied without adapting to current constraints.",
|
|
1732
|
+
],
|
|
1733
|
+
"recovery_strategies": [
|
|
1734
|
+
"Regenerate missing package-relative refs and rerun bundle or verifier checks.",
|
|
1735
|
+
"Record evidence status instead of fabricating missing evidence.",
|
|
1736
|
+
"Use a follow-up loop when user or reviewer feedback changes the deliverables.",
|
|
1737
|
+
],
|
|
1738
|
+
"success_criteria": criteria,
|
|
1739
|
+
"evidence_refs": _evidence_refs(evidence),
|
|
1740
|
+
"evidence_reference_ids": _evidence_refs(evidence),
|
|
1741
|
+
})
|
|
1742
|
+
write_json(root / "skill_pack" / "canonical_skill.json", canonical_skill)
|
|
1743
|
+
write_json(root / "skill_pack" / "skill_card.json", {
|
|
1744
|
+
"title": title,
|
|
1745
|
+
"domain": _domain(evidence),
|
|
1746
|
+
"source_package_id": report.get("source_package_id"),
|
|
1747
|
+
"runtime_training_summary": f"Skill pack for {title}",
|
|
1748
|
+
})
|
|
1749
|
+
write_json(root / "skill_pack" / "retrieval_card.json", {
|
|
1750
|
+
"title": title,
|
|
1751
|
+
"tags": sorted(set([_domain(evidence), *(str(title + " " + instruction).lower().split()[:12])]))[:16],
|
|
1752
|
+
"query_terms": sorted(set(str(title + ' ' + instruction).lower().split()))[:40],
|
|
1753
|
+
"useful_for": [
|
|
1754
|
+
f"Runtime guidance for adjacent {_domain(evidence) or 'workflow'} tasks.",
|
|
1755
|
+
"Verifier checklist reuse when deliverables and evidence patterns match.",
|
|
1756
|
+
"Training/evaluation examples grounded in the source package.",
|
|
1757
|
+
],
|
|
1758
|
+
"evidence_refs": _evidence_refs(evidence)[:12],
|
|
1759
|
+
"confidence": report.get("average_quality_score") or report.get("average_fidelity_score") or 0.5,
|
|
1760
|
+
"source_package_id": report.get("source_package_id"),
|
|
1761
|
+
"artifact_expectations": _source_artifact_refs(evidence)[:10],
|
|
1762
|
+
})
|
|
1763
|
+
_write_jsonl(root / "skill_pack" / "usage_examples.jsonl", [{
|
|
1764
|
+
"source_package_id": report.get("source_package_id"),
|
|
1765
|
+
"input": instruction,
|
|
1766
|
+
"expected_runtime_behavior": "Follow the compiled procedure, preserve evidence refs, and verify outputs against the checklist.",
|
|
1767
|
+
"evidence_refs": _evidence_refs(evidence)[:10],
|
|
1768
|
+
}])
|
|
1769
|
+
_write_jsonl(root / "skill_pack" / "anti_patterns.jsonl", [{
|
|
1770
|
+
"source_package_id": report.get("source_package_id"),
|
|
1771
|
+
"anti_pattern": "Applying the compiled skill without checking current task constraints and required artifacts.",
|
|
1772
|
+
"correction": "Re-read the task brief and verifier checklist before reusing the procedure.",
|
|
1773
|
+
}])
|
|
1774
|
+
_write_jsonl(root / "skill_pack" / "failure_modes.jsonl", [{
|
|
1775
|
+
"source_package_id": report.get("source_package_id"),
|
|
1776
|
+
"failure_mode": "Missing artifact evidence or unresolved output references.",
|
|
1777
|
+
"recovery": "Regenerate or relink package-relative artifact refs, then rerun bundle check.",
|
|
1778
|
+
}])
|
|
1779
|
+
_write_jsonl(root / "skill_pack" / "decision_rules.jsonl", [{
|
|
1780
|
+
"source_package_id": report.get("source_package_id"),
|
|
1781
|
+
"rule": "Use the skill when task/domain/query terms overlap and required tools are available.",
|
|
1782
|
+
"evidence_refs": _evidence_refs(evidence)[:5],
|
|
1783
|
+
}])
|
|
1784
|
+
write_json(root / "skill_pack" / "tool_recipe.json", {
|
|
1785
|
+
"source_package_id": report.get("source_package_id"),
|
|
1786
|
+
"required_tools": sorted(set(_environment_spec(evidence).get("required_tools") or [])),
|
|
1787
|
+
"procedure_ref": "skill.md",
|
|
1788
|
+
})
|
|
1789
|
+
verifier_lines = ["# Verifier Checklist", "", *(f"- {item}" for item in criteria), "- Bundle check passes.", "- Evidence refs resolve."]
|
|
1790
|
+
(root / "skill_pack" / "verifier_checklist.md").write_text("\n".join(verifier_lines) + "\n")
|
|
1791
|
+
write_json(root / "skill_pack" / "verifier_checklist.json", {
|
|
1792
|
+
"source_package_id": report.get("source_package_id"),
|
|
1793
|
+
"checks": criteria + ["bundle_check_passes", "evidence_refs_resolve"],
|
|
1794
|
+
})
|
|
1795
|
+
write_json(root / "runtime_learning_package" / "runtime_learning_manifest.json", {
|
|
1796
|
+
"source_package_id": report.get("source_package_id"),
|
|
1797
|
+
"apprenticeship_mode": getattr(settings, "apprenticeship_mode", None),
|
|
1798
|
+
"runtime_uses": ["prompt_guidance", "tool_use_hints", "correction_patterns", "experience_pack"],
|
|
1799
|
+
"supported_runtime_uses": [
|
|
1800
|
+
"skill_import",
|
|
1801
|
+
"retrieval_augmented_execution",
|
|
1802
|
+
"verifier_assisted_execution",
|
|
1803
|
+
"workflow_checklist",
|
|
1804
|
+
"failure_pattern_warning",
|
|
1805
|
+
"tool_recipe_reuse",
|
|
1806
|
+
],
|
|
1807
|
+
"skill_ref": "experience_compiler/skill_pack/skill.md",
|
|
1808
|
+
})
|
|
1809
|
+
for name, rows in {
|
|
1810
|
+
"runtime_memory.jsonl": [{"source_package_id": report.get("source_package_id"), "lesson": _task_instruction(evidence), "usable_for": ["runtime_memory"]}],
|
|
1811
|
+
"tool_use_hints.jsonl": [{"source_package_id": report.get("source_package_id"), "hint": "Preserve package-relative refs for commands, files, artifacts, and actual outputs.", "usable_for": ["tool_use"]}],
|
|
1812
|
+
"correction_patterns.jsonl": [{"source_package_id": report.get("source_package_id"), "pattern": "If a follow-up changes constraints, update artifacts and compiler metadata without inventing missing execution.", "usable_for": ["followup"]}],
|
|
1813
|
+
}.items():
|
|
1814
|
+
_write_jsonl(root / "runtime_learning_package" / name, rows)
|
|
1815
|
+
(root / "runtime_learning_package" / "skill.md").write_text((root / "skill_pack" / "skill.md").read_text(errors="replace"))
|
|
1816
|
+
(root / "runtime_learning_package" / "runtime_context.md").write_text(
|
|
1817
|
+
f"# Runtime Context\n\nSource task: {title}\n\nInstruction pattern:\n\n{instruction}\n\nDomain: {_domain(evidence) or 'unknown'}\n\nEvidence refs:\n"
|
|
1818
|
+
+ "\n".join(f"- `{ref}`" for ref in _evidence_refs(evidence)[:12])
|
|
1819
|
+
+ "\n"
|
|
1820
|
+
)
|
|
1821
|
+
(root / "runtime_learning_package" / "runtime_instructions.md").write_text(
|
|
1822
|
+
"\n".join(
|
|
1823
|
+
[
|
|
1824
|
+
f"# Runtime Instructions: {title}",
|
|
1825
|
+
"",
|
|
1826
|
+
"Use this runtime training only when the current task is relevant to the source workflow. The current user instruction remains authoritative.",
|
|
1827
|
+
"",
|
|
1828
|
+
"## Apply When",
|
|
1829
|
+
f"- The task resembles `{_domain(evidence) or 'workflow'}` work or shares deliverables with the source task.",
|
|
1830
|
+
"- The required tools, artifacts, and verification expectations are available.",
|
|
1831
|
+
"",
|
|
1832
|
+
"## Runtime Steps",
|
|
1833
|
+
"1. Restate the current task and compare it to the source workflow.",
|
|
1834
|
+
"2. Reuse only the relevant workflow steps, decision rules, and verifier checks.",
|
|
1835
|
+
"3. Produce package-relative artifacts and update `actual_outputs.json`.",
|
|
1836
|
+
"4. Preserve trace steps, feedback refs, and evidence refs for later review.",
|
|
1837
|
+
"5. Use only current-task evidence and omit unsupported transferred assumptions.",
|
|
1838
|
+
"",
|
|
1839
|
+
"## Verifier Focus",
|
|
1840
|
+
*(f"- {item}" for item in criteria[:6]),
|
|
1841
|
+
"- Evidence refs resolve and remain package-relative.",
|
|
1842
|
+
"- Outputs are grounded in current task artifacts rather than source-task assumptions.",
|
|
1843
|
+
"",
|
|
1844
|
+
"## Source Evidence",
|
|
1845
|
+
*(f"- `{ref}`" for ref in _evidence_refs(evidence)[:12]),
|
|
1846
|
+
"",
|
|
1847
|
+
]
|
|
1848
|
+
)
|
|
1849
|
+
)
|
|
1850
|
+
for src, dst in [
|
|
1851
|
+
("retrieval_card.json", "retrieval_card.json"),
|
|
1852
|
+
("verifier_checklist.json", "verifier_spec.json"),
|
|
1853
|
+
("tool_recipe.json", "tool_recipe.json"),
|
|
1854
|
+
]:
|
|
1855
|
+
shutil.copy2(root / "skill_pack" / src, root / "runtime_learning_package" / dst)
|
|
1856
|
+
reusable_rows = [{"source_package_id": report.get("source_package_id"), "text": item, "evidence_refs": _evidence_refs(evidence)[:5]} for item in criteria[:8]]
|
|
1857
|
+
for name in [
|
|
1858
|
+
"reusable_steps.jsonl",
|
|
1859
|
+
"reusable_decision_rules.jsonl",
|
|
1860
|
+
"tool_use_patterns.jsonl",
|
|
1861
|
+
"failure_recovery_patterns.jsonl",
|
|
1862
|
+
"mentor_feedback_patterns.jsonl",
|
|
1863
|
+
"verifier_feedback_patterns.jsonl",
|
|
1864
|
+
"artifact_expectations.jsonl",
|
|
1865
|
+
"environment_expectations.jsonl",
|
|
1866
|
+
]:
|
|
1867
|
+
_write_jsonl(root / "runtime_learning_package" / name, reusable_rows or [{
|
|
1868
|
+
"source_package_id": report.get("source_package_id"),
|
|
1869
|
+
"text": "Preserve task-specific evidence, artifacts, and output refs.",
|
|
1870
|
+
"evidence_refs": _evidence_refs(evidence)[:5],
|
|
1871
|
+
}])
|
|
1872
|
+
write_json(root / "training_time_package" / "training_time_manifest.json", {
|
|
1873
|
+
"source_package_id": report.get("source_package_id"),
|
|
1874
|
+
"training_uses": compiler_manifest["supported_training_uses"],
|
|
1875
|
+
"supported_training_uses": [
|
|
1876
|
+
"sft",
|
|
1877
|
+
"rl",
|
|
1878
|
+
"grpo",
|
|
1879
|
+
"reward_modeling",
|
|
1880
|
+
"preference_learning",
|
|
1881
|
+
"verifier_training",
|
|
1882
|
+
"process_supervision",
|
|
1883
|
+
"distillation",
|
|
1884
|
+
"eval_generation",
|
|
1885
|
+
"environment_model_training",
|
|
1886
|
+
],
|
|
1887
|
+
"row_counts": rows_accepted,
|
|
1888
|
+
"no_model_training_claimed": True,
|
|
1889
|
+
})
|
|
1890
|
+
for src, dst in [
|
|
1891
|
+
("training_examples.jsonl", "sft_examples.jsonl"),
|
|
1892
|
+
("verifier_examples.jsonl", "verifier_training_rows.jsonl"),
|
|
1893
|
+
("failure_cases.jsonl", "failure_cases.jsonl"),
|
|
1894
|
+
("task_variants.jsonl", "task_variants.jsonl"),
|
|
1895
|
+
("environment_response_pairs.jsonl", "environment_response_pairs.jsonl"),
|
|
1896
|
+
("reward_model_rows.jsonl", "reward_model_rows.jsonl"),
|
|
1897
|
+
("preference_pairs.jsonl", "preference_pairs.jsonl"),
|
|
1898
|
+
]:
|
|
1899
|
+
_copy_jsonl_public(tdo_dir / src, root / "training_time_package" / dst)
|
|
1900
|
+
_write_jsonl(root / "training_time_package" / "process_supervision.jsonl", reusable_rows)
|
|
1901
|
+
loop_pairs: list[dict[str, Any]] = []
|
|
1902
|
+
for item in evidence.get("loop_review_packets") or []:
|
|
1903
|
+
packet = item.get("packet") or {}
|
|
1904
|
+
ref = item.get("ref")
|
|
1905
|
+
loop_pairs.append({
|
|
1906
|
+
"source_package_id": report.get("source_package_id"),
|
|
1907
|
+
"critique": _clean_text("; ".join(packet.get("known_failures") or packet.get("recommended_review_focus") or []) or "Loop review packet recorded no blocking failure."),
|
|
1908
|
+
"revision": _clean_text(str((packet.get("diff_from_previous_iteration") or {}).get("changed_summary") or "Use loop feedback and revision plan to ground updates.")),
|
|
1909
|
+
"evidence_refs": [ref] if ref else _evidence_refs(evidence)[:5],
|
|
1910
|
+
"loop_iteration": packet.get("loop_iteration"),
|
|
1911
|
+
})
|
|
1912
|
+
if not loop_pairs:
|
|
1913
|
+
loop_pairs = [{
|
|
1914
|
+
"source_package_id": report.get("source_package_id"),
|
|
1915
|
+
"critique": "Use evaluator/verifier feedback where present; do not invent missing feedback.",
|
|
1916
|
+
"revision": "Ground revisions in actual outputs and artifact refs.",
|
|
1917
|
+
"evidence_refs": _evidence_refs(evidence)[:5],
|
|
1918
|
+
}]
|
|
1919
|
+
_write_jsonl(root / "training_time_package" / "critique_revision_pairs.jsonl", loop_pairs)
|
|
1920
|
+
_write_jsonl(root / "training_time_package" / "trajectory_rollouts.jsonl", [{
|
|
1921
|
+
"source_package_id": report.get("source_package_id"),
|
|
1922
|
+
"trajectory_ref": "traces/agent_traces.jsonl",
|
|
1923
|
+
"artifact_refs": _source_artifact_refs(evidence),
|
|
1924
|
+
}])
|
|
1925
|
+
_write_jsonl(root / "training_time_package" / "distillation_records.jsonl", [{
|
|
1926
|
+
"source_package_id": report.get("source_package_id"),
|
|
1927
|
+
"input": instruction,
|
|
1928
|
+
"target": _output_summary(evidence),
|
|
1929
|
+
"evidence_refs": _evidence_refs(evidence)[:8],
|
|
1930
|
+
}])
|
|
1931
|
+
_write_jsonl(root / "training_time_package" / "transfer_eval_tasks.jsonl", [
|
|
1932
|
+
{
|
|
1933
|
+
"source_package_id": report.get("source_package_id"),
|
|
1934
|
+
"task": f"Replay a small adjacent {(_task_title(evidence) or 'workflow')} task and compare artifact quality.",
|
|
1935
|
+
"success_signal": "bundle check passes and verifier/evaluator evidence refs are present",
|
|
1936
|
+
"transfer_tested": False,
|
|
1937
|
+
}
|
|
1938
|
+
])
|
|
1939
|
+
write_json(root / "reports" / "transfer_test_report.json", {
|
|
1940
|
+
"transfer_tested": False,
|
|
1941
|
+
"reason": "Transfer tests are reserved for future replay runs; no model training was performed.",
|
|
1942
|
+
})
|
|
1943
|
+
_copy_jsonl_public(tdo_dir / "evaluation_examples.jsonl", root / "eval_package" / "evaluation_examples.jsonl")
|
|
1944
|
+
shutil.copy2(root / "training_time_package" / "transfer_eval_tasks.jsonl", root / "eval_package" / "transfer_eval_tasks.jsonl")
|
|
1945
|
+
shutil.copy2(root / "reports" / "transfer_test_report.json", root / "eval_package" / "transfer_test_report.json")
|
|
1946
|
+
_write_jsonl(root / "eval_package" / "rubric_items.jsonl", [{"source_package_id": report.get("source_package_id"), "rubric_item": item} for item in criteria])
|
|
1947
|
+
_write_jsonl(root / "eval_package" / "grader_cases.jsonl", [{"source_package_id": report.get("source_package_id"), "case": "Grade output against compiled rubric and artifact evidence.", "evidence_refs": _evidence_refs(evidence)[:5]}])
|
|
1948
|
+
_write_jsonl(root / "eval_package" / "verifier_cases.jsonl", [{"source_package_id": report.get("source_package_id"), "case": "Verify required artifacts and output refs exist.", "evidence_refs": _evidence_refs(evidence)[:5]}])
|
|
1949
|
+
_write_jsonl(root / "eval_package" / "expected_artifacts.jsonl", [{"source_package_id": report.get("source_package_id"), "artifact_ref": ref} for ref in _source_artifact_refs(evidence)])
|
|
1950
|
+
env_spec = _environment_spec(evidence)
|
|
1951
|
+
write_json(root / "environment" / "replay_plan.json", {"source_package_id": report.get("source_package_id"), "replay_available": False, "generation_note": "Replay is task-dependent; use environment_spec and verifier checks."})
|
|
1952
|
+
write_json(root / "environment" / "setup_requirements.json", {"required_tools": env_spec.get("required_tools") or [], "runtime_dependencies": env_spec.get("runtime_dependencies") or []})
|
|
1953
|
+
_write_jsonl(root / "environment" / "allowed_actions.jsonl", [{"action": action, "source_package_id": report.get("source_package_id")} for action in (env_spec.get("allowed_actions") or ["read_task", "write_artifact", "verify_output"])])
|
|
1954
|
+
_copy_jsonl_public(tdo_dir / "environment_response_pairs.jsonl", root / "environment" / "state_transition_rows.jsonl")
|
|
1955
|
+
write_json(root / "environment" / "reset_strategy.json", {"reset_strategy": env_spec.get("reset_strategy") or "unknown", "generation_notes": env_spec.get("evidence_status") or []})
|
|
1956
|
+
_write_jsonl(root / "environment" / "verification_commands.jsonl", [{"command": command, "source_package_id": report.get("source_package_id")} for command in (env_spec.get("verification_commands") or [])])
|
|
1957
|
+
(root / "reports" / "generation_notes.md").write_text(
|
|
1958
|
+
"\n".join([
|
|
1959
|
+
"# Generation Notes",
|
|
1960
|
+
"",
|
|
1961
|
+
"This package keeps accepted rows grounded in package evidence.",
|
|
1962
|
+
"Unsupported candidate rows are omitted or recorded in rejected row data.",
|
|
1963
|
+
]) + "\n"
|
|
1964
|
+
)
|
|
1965
|
+
write_json(root / "learning_objects_index.json", {
|
|
1966
|
+
"source_package_id": report.get("source_package_id"),
|
|
1967
|
+
"learning_objects": [
|
|
1968
|
+
{"kind": "skill_pack", "path": "experience_compiler/skill_pack/skill.md"},
|
|
1969
|
+
{"kind": "runtime_learning_package", "path": "experience_compiler/runtime_learning_package/runtime_learning_manifest.json"},
|
|
1970
|
+
{"kind": "training_time_package", "path": "experience_compiler/training_time_package/training_time_manifest.json"},
|
|
1971
|
+
{"kind": "environment", "path": "experience_compiler/environment/environment_spec.json"},
|
|
1972
|
+
{"kind": "reports", "path": "experience_compiler/compiler_report.json"},
|
|
1973
|
+
],
|
|
1974
|
+
"rows_by_type": rows_accepted,
|
|
1975
|
+
})
|
|
1976
|
+
|
|
1977
|
+
|
|
1978
|
+
def run_tdo_for_package(package_root: Path, settings: Settings, *, run_root: Path | None = None, followup_index: int | None = None, record_only: bool = False) -> dict[str, Any]:
|
|
1979
|
+
tdo_dir = package_root / "tdo"
|
|
1980
|
+
tdo_dir.mkdir(parents=True, exist_ok=True)
|
|
1981
|
+
for name in TDO_JSONL_FILES:
|
|
1982
|
+
(tdo_dir / name).write_text("")
|
|
1983
|
+
if _truthy_env("AA_TDO_DISABLED"):
|
|
1984
|
+
report = TDOReport(
|
|
1985
|
+
tdo_id=f"tdo_{_safe_id(package_root.name)}",
|
|
1986
|
+
source_package_id=package_root.name,
|
|
1987
|
+
source_task_id=package_root.name,
|
|
1988
|
+
created_at=utc_now(),
|
|
1989
|
+
generator_mode=settings.worker_agent,
|
|
1990
|
+
tdo_judge_source="apprentice_self_judge",
|
|
1991
|
+
mentor_audited=False,
|
|
1992
|
+
rounds_run=0,
|
|
1993
|
+
stop_reason="disabled_by_AA_TDO_DISABLED",
|
|
1994
|
+
tdo_status="disabled",
|
|
1995
|
+
schema_validation_passed=True,
|
|
1996
|
+
sanitization_passed=True,
|
|
1997
|
+
weak_strong_proxy_available=False,
|
|
1998
|
+
)
|
|
1999
|
+
write_json(tdo_dir / "tdo_report.json", report)
|
|
2000
|
+
write_json(tdo_dir / "tdo_manifest.json", {"schema_version": TDO_VERSION, "tdo_status": "disabled"})
|
|
2001
|
+
return report.model_dump(mode="json")
|
|
2002
|
+
max_rounds = _int_env("AA_EXPERIENCE_COMPILER_MAX_ROUNDS", _int_env("AA_TDO_MAX_ROUNDS", 3))
|
|
2003
|
+
min_quality = _float_env("AA_EXPERIENCE_COMPILER_MIN_QUALITY", _float_env("AA_TDO_MIN_QUALITY_SCORE", 0.65))
|
|
2004
|
+
min_fidelity = _float_env("AA_TDO_MIN_FIDELITY_SCORE", 0.65)
|
|
2005
|
+
min_reuse = _float_env("AA_TDO_MIN_REUSE_VALUE_SCORE", 0.65)
|
|
2006
|
+
evidence = _load_evidence(package_root, run_root=run_root)
|
|
2007
|
+
mentor_ready = configured_model_provider_ready(settings)
|
|
2008
|
+
judge_source = "mentor_model" if mentor_ready else "apprentice_self_judge"
|
|
2009
|
+
mentor_audited = bool(mentor_ready)
|
|
2010
|
+
proxy = _weak_strong_proxy(evidence)
|
|
2011
|
+
all_generated: list[dict[str, Any]] = []
|
|
2012
|
+
accepted_final: list[dict[str, Any]] = []
|
|
2013
|
+
rejected_all: list[dict[str, Any]] = []
|
|
2014
|
+
recipe_updates: list[str] = []
|
|
2015
|
+
judge_results: list[dict[str, Any]] = []
|
|
2016
|
+
rounds_run = 0
|
|
2017
|
+
stop_reason = "max_rounds_reached"
|
|
2018
|
+
for round_idx in range(1, max_rounds + 1):
|
|
2019
|
+
rounds_run = round_idx
|
|
2020
|
+
recipe_version = f"{TDO_VERSION}-round-{round_idx}"
|
|
2021
|
+
candidates = _build_candidate_rows(evidence, settings, round_idx, judge_source, mentor_audited, proxy)
|
|
2022
|
+
accepted, rejected, schema_errors = _validate_rows(package_root, candidates, min_quality, min_fidelity, min_reuse)
|
|
2023
|
+
judge = _judge(round_idx, max_rounds, accepted, rejected, judge_source, mentor_audited, proxy)
|
|
2024
|
+
judge_dict = judge.model_dump(mode="json")
|
|
2025
|
+
judge_results.append(judge_dict)
|
|
2026
|
+
all_generated.extend(candidates)
|
|
2027
|
+
round_rejected = list(rejected)
|
|
2028
|
+
if round_idx < max_rounds:
|
|
2029
|
+
for row in accepted:
|
|
2030
|
+
superseded = dict(row)
|
|
2031
|
+
superseded["accepted"] = False
|
|
2032
|
+
superseded["rejection_reason"] = "superseded_by_later_tdo_round"
|
|
2033
|
+
round_rejected.append(superseded)
|
|
2034
|
+
recipe_updates.append(f"round_{round_idx}_strengthen_grounding_and_reuse_scores")
|
|
2035
|
+
else:
|
|
2036
|
+
accepted_final = accepted
|
|
2037
|
+
rejected_all.extend(round_rejected)
|
|
2038
|
+
round_record = {
|
|
2039
|
+
"round": round_idx,
|
|
2040
|
+
"recipe_version": recipe_version,
|
|
2041
|
+
"generated_counts": _counts_by_type(candidates),
|
|
2042
|
+
"accepted_counts": _counts_by_type(accepted if round_idx == max_rounds else []),
|
|
2043
|
+
"rejected_counts": _counts_by_type(round_rejected),
|
|
2044
|
+
"schema_errors": sorted(set(schema_errors)),
|
|
2045
|
+
"quality_findings": judge_dict.get("data_quality_concerns") or [],
|
|
2046
|
+
"recipe_updates": list(recipe_updates),
|
|
2047
|
+
"judge_source": judge_source,
|
|
2048
|
+
"stop_reason": stop_reason if round_idx == max_rounds else None,
|
|
2049
|
+
}
|
|
2050
|
+
append_jsonl(tdo_dir / "tdo_rounds.jsonl", round_record)
|
|
2051
|
+
append_jsonl(tdo_dir / "tdo_judge_results.jsonl", judge_dict)
|
|
2052
|
+
for event in [
|
|
2053
|
+
{"event_type": "generate", "round": round_idx, "message": "generated candidate training data", "counts": _counts_by_type(candidates)},
|
|
2054
|
+
{"event_type": "judge", "round": round_idx, "message": "judge feedback complete", "verdict": judge.verdict},
|
|
2055
|
+
{"event_type": "improve" if judge.verdict == "improve" else "accept", "round": round_idx, "message": judge.verdict_reason},
|
|
2056
|
+
]:
|
|
2057
|
+
append_jsonl(tdo_dir / "tdo_trace.jsonl", sanitize_public_obj(event))
|
|
2058
|
+
append_jsonl(tdo_dir / "meta_optimization_records.jsonl", {
|
|
2059
|
+
"recipe_version": recipe_version,
|
|
2060
|
+
"round": round_idx,
|
|
2061
|
+
"failure_patterns": sorted(set(schema_errors)),
|
|
2062
|
+
"accepted_recipe_updates": list(recipe_updates),
|
|
2063
|
+
"rejected_recipe_updates": [],
|
|
2064
|
+
"quality_delta": round(0.11 * (round_idx - 1), 4),
|
|
2065
|
+
"prompt_improvement_candidates": ["Require source evidence refs for every reusable row."],
|
|
2066
|
+
"notes_for_future_tdo_recipe": "Use mentor-model audit when available; keep rows grounded in observed package evidence.",
|
|
2067
|
+
})
|
|
2068
|
+
rejected_unique = _dedupe_rows(rejected_all)
|
|
2069
|
+
accepted_final = _dedupe_rows(accepted_final)
|
|
2070
|
+
_write_tdo_outputs(tdo_dir, accepted_final, rejected_unique)
|
|
2071
|
+
write_json(tdo_dir / "tdo_recipe.json", sanitize_public_obj({
|
|
2072
|
+
"schema_version": TDO_VERSION,
|
|
2073
|
+
"recipe_version": f"{TDO_VERSION}-round-{rounds_run}",
|
|
2074
|
+
"source_grounding": [
|
|
2075
|
+
"task_packet",
|
|
2076
|
+
"rubric",
|
|
2077
|
+
"agent_trace",
|
|
2078
|
+
"actual_outputs",
|
|
2079
|
+
"artifacts",
|
|
2080
|
+
"attempt_manifest",
|
|
2081
|
+
"evaluator_feedback",
|
|
2082
|
+
"grader_results",
|
|
2083
|
+
"verifier_results",
|
|
2084
|
+
"revision_plans",
|
|
2085
|
+
"process_supervision",
|
|
2086
|
+
"reward_modeling",
|
|
2087
|
+
"revision_preference_pairs",
|
|
2088
|
+
"lessons",
|
|
2089
|
+
"follow_up_records",
|
|
2090
|
+
"mentor_checkpoints",
|
|
2091
|
+
],
|
|
2092
|
+
"max_rounds": max_rounds,
|
|
2093
|
+
"rounds_run": rounds_run,
|
|
2094
|
+
"quality_thresholds": {
|
|
2095
|
+
"min_quality_score": min_quality,
|
|
2096
|
+
"min_fidelity_score": min_fidelity,
|
|
2097
|
+
"min_reuse_value_score": min_reuse,
|
|
2098
|
+
},
|
|
2099
|
+
"recipe_updates": recipe_updates,
|
|
2100
|
+
"judge_source": judge_source,
|
|
2101
|
+
"mentor_audited": mentor_audited,
|
|
2102
|
+
**_source_audit_status(evidence),
|
|
2103
|
+
}))
|
|
2104
|
+
write_json(tdo_dir / "optimized_task_spec.json", _optimized_task_spec(evidence, settings, rounds_run))
|
|
2105
|
+
write_json(tdo_dir / "optimized_rubric.json", _optimized_rubric(evidence, rounds_run))
|
|
2106
|
+
write_json(tdo_dir / "domain_value_profile.json", _domain_value_profile(evidence))
|
|
2107
|
+
write_json(tdo_dir / "environment_spec.json", _environment_spec(evidence))
|
|
2108
|
+
rows_generated = _counts_with_surface_zeros(all_generated)
|
|
2109
|
+
rows_accepted = _counts_with_surface_zeros(accepted_final)
|
|
2110
|
+
rows_rejected = _counts_with_surface_zeros(rejected_unique)
|
|
2111
|
+
omitted_outputs: list[str] = []
|
|
2112
|
+
not_generated_counts: dict[str, int] = {}
|
|
2113
|
+
generation_notes: list[str] = []
|
|
2114
|
+
if not mentor_audited:
|
|
2115
|
+
generation_notes.append("judge_source=apprentice_self_judge")
|
|
2116
|
+
if not proxy.get("available"):
|
|
2117
|
+
not_generated_counts["solver_gap_proxy"] = 1
|
|
2118
|
+
if int(rows_accepted.get("preference_pair") or 0) == 0:
|
|
2119
|
+
omitted_outputs.append("preference_pairs")
|
|
2120
|
+
not_generated_counts["preference_pair"] = int(rows_rejected.get("preference_pair") or 0)
|
|
2121
|
+
report = TDOReport(
|
|
2122
|
+
tdo_id=f"tdo_{_safe_id(package_root.name)}_{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}",
|
|
2123
|
+
source_package_id=package_root.name,
|
|
2124
|
+
source_task_id=_source_task_id(evidence),
|
|
2125
|
+
created_at=utc_now(),
|
|
2126
|
+
generator_mode=settings.worker_agent,
|
|
2127
|
+
tdo_judge_source=judge_source, # type: ignore[arg-type]
|
|
2128
|
+
mentor_audited=mentor_audited,
|
|
2129
|
+
rounds_run=rounds_run,
|
|
2130
|
+
stop_reason=stop_reason,
|
|
2131
|
+
tdo_status="completed",
|
|
2132
|
+
rows_generated_by_type=rows_generated,
|
|
2133
|
+
rows_accepted_by_type=rows_accepted,
|
|
2134
|
+
rows_rejected_by_type=rows_rejected,
|
|
2135
|
+
average_quality_score=_average(accepted_final, "quality_score"),
|
|
2136
|
+
average_fidelity_score=_average(accepted_final, "fidelity_score"),
|
|
2137
|
+
average_difficulty_score=_average(accepted_final, "difficulty_score"),
|
|
2138
|
+
average_learnability_score=_average(accepted_final, "learnability_score"),
|
|
2139
|
+
average_reuse_value_score=_average(accepted_final, "reuse_value_score"),
|
|
2140
|
+
schema_validation_passed=not any(row.get("rejection_reason") == "missing_data_type" for row in rejected_unique),
|
|
2141
|
+
sanitization_passed=not any(_private_or_forbidden_hit(row) for row in accepted_final),
|
|
2142
|
+
weak_strong_proxy_available=bool(proxy.get("available")),
|
|
2143
|
+
average_solver_gap_proxy=proxy.get("solver_gap_proxy"),
|
|
2144
|
+
grpo_suitability_distribution=_grpo_distribution(accepted_final),
|
|
2145
|
+
omitted_outputs=omitted_outputs,
|
|
2146
|
+
not_generated_counts=not_generated_counts,
|
|
2147
|
+
generation_notes=generation_notes,
|
|
2148
|
+
recommended_uses=["training_examples", "evaluation_examples", "verifier_training", "reward_modeling", "preference_optimization", "environment_response_training"],
|
|
2149
|
+
metadata_json={
|
|
2150
|
+
"apprenticeship_mode": getattr(settings, "apprenticeship_mode", None),
|
|
2151
|
+
"record_only_followup_update": record_only,
|
|
2152
|
+
"followup_index": followup_index,
|
|
2153
|
+
"follow_up_record_count": len([e for e in evidence.get("session_events") or [] if e.get("event_type") == "user_followup"]),
|
|
2154
|
+
"self_judge_generation_notes": ([] if mentor_audited else ["recommended_future_mentor_audit"]),
|
|
2155
|
+
"recommended_future_mentor_audit": not mentor_audited,
|
|
2156
|
+
"structural_guardrails": ["schema", "paths", "references", "secrets", "public-package hygiene"],
|
|
2157
|
+
**_source_audit_status(evidence),
|
|
2158
|
+
},
|
|
2159
|
+
)
|
|
2160
|
+
write_json(tdo_dir / "tdo_report.json", report)
|
|
2161
|
+
output_refs = [f"tdo/{name}" for name in TDO_JSON_FILES + TDO_JSONL_FILES]
|
|
2162
|
+
write_json(tdo_dir / "tdo_manifest.json", {
|
|
2163
|
+
"schema_version": TDO_VERSION,
|
|
2164
|
+
"tdo_id": report.tdo_id,
|
|
2165
|
+
"tdo_status": report.tdo_status,
|
|
2166
|
+
"source_package_id": package_root.name,
|
|
2167
|
+
"source_task_id": report.source_task_id,
|
|
2168
|
+
"tdo_output_refs": output_refs,
|
|
2169
|
+
"tdo_judge_source": judge_source,
|
|
2170
|
+
"mentor_audited": mentor_audited,
|
|
2171
|
+
**_source_audit_status(evidence),
|
|
2172
|
+
"rounds_run": rounds_run,
|
|
2173
|
+
"rows_by_type": rows_accepted,
|
|
2174
|
+
"created_at": report.created_at,
|
|
2175
|
+
})
|
|
2176
|
+
_write_experience_compiler_outputs(
|
|
2177
|
+
package_root,
|
|
2178
|
+
tdo_dir,
|
|
2179
|
+
report.model_dump(mode="json"),
|
|
2180
|
+
evidence,
|
|
2181
|
+
rows_accepted,
|
|
2182
|
+
settings,
|
|
2183
|
+
)
|
|
2184
|
+
try:
|
|
2185
|
+
from .package_exporter import write_artifacts_index
|
|
2186
|
+
|
|
2187
|
+
write_artifacts_index(package_root)
|
|
2188
|
+
except Exception:
|
|
2189
|
+
pass
|
|
2190
|
+
return report.model_dump(mode="json")
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
def _dedupe_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
2194
|
+
seen: set[str] = set()
|
|
2195
|
+
out: list[dict[str, Any]] = []
|
|
2196
|
+
for row in rows:
|
|
2197
|
+
key = repr({k: row.get(k) for k in ["data_type", "input", "target", "source_step_id", "tdo_round", "rejection_reason"]})
|
|
2198
|
+
if key not in seen:
|
|
2199
|
+
seen.add(key)
|
|
2200
|
+
out.append(row)
|
|
2201
|
+
return out
|
|
2202
|
+
|
|
2203
|
+
|
|
2204
|
+
def run_tdo_for_run(
|
|
2205
|
+
run_root: Path,
|
|
2206
|
+
settings: Settings,
|
|
2207
|
+
*,
|
|
2208
|
+
progress_callback: ProgressCallback | None = None,
|
|
2209
|
+
followup_index: int | None = None,
|
|
2210
|
+
record_only: bool = False,
|
|
2211
|
+
) -> dict[str, Any]:
|
|
2212
|
+
if _truthy_env("AA_TDO_DISABLED") or _truthy_env("AA_EXPERIENCE_COMPILER_DISABLED"):
|
|
2213
|
+
return {"tdo_status": "disabled", "stop_reason": "disabled_by_AA_TDO_DISABLED"}
|
|
2214
|
+
package_root = run_root / "packages"
|
|
2215
|
+
packages = sorted([p for p in package_root.glob("*") if p.is_dir()]) if package_root.exists() else []
|
|
2216
|
+
if not packages:
|
|
2217
|
+
return {"tdo_status": "failed", "error": "No task package found for Experience Compiler."}
|
|
2218
|
+
append_progress_event(
|
|
2219
|
+
run_root,
|
|
2220
|
+
"tdo_started",
|
|
2221
|
+
run_id=run_root.name,
|
|
2222
|
+
message="Experience Compiler started.",
|
|
2223
|
+
phase="experience_compiler",
|
|
2224
|
+
metadata_json={"followup_index": followup_index} if followup_index else None,
|
|
2225
|
+
callback=progress_callback,
|
|
2226
|
+
)
|
|
2227
|
+
reports: list[dict[str, Any]] = []
|
|
2228
|
+
try:
|
|
2229
|
+
for pkg in packages:
|
|
2230
|
+
report = run_tdo_for_package(pkg, settings, run_root=run_root, followup_index=followup_index, record_only=record_only)
|
|
2231
|
+
reports.append(report)
|
|
2232
|
+
rounds = read_jsonl(pkg / "tdo" / "tdo_rounds.jsonl")
|
|
2233
|
+
max_round = int(report.get("rounds_run") or len(rounds) or _int_env("AA_TDO_MAX_ROUNDS", 3))
|
|
2234
|
+
for row in rounds:
|
|
2235
|
+
round_idx = int(row.get("round") or 0)
|
|
2236
|
+
append_progress_event(
|
|
2237
|
+
run_root,
|
|
2238
|
+
"tdo_round_generated",
|
|
2239
|
+
run_id=run_root.name,
|
|
2240
|
+
message=f"Round {round_idx}/{max_round}: generated candidate learning data.",
|
|
2241
|
+
phase="experience_compiler",
|
|
2242
|
+
metadata_json={"package_id": pkg.name, "followup_index": followup_index, "generated_counts": row.get("generated_counts")},
|
|
2243
|
+
callback=progress_callback,
|
|
2244
|
+
)
|
|
2245
|
+
append_progress_event(
|
|
2246
|
+
run_root,
|
|
2247
|
+
"tdo_round_judged",
|
|
2248
|
+
run_id=run_root.name,
|
|
2249
|
+
message=f"Round {round_idx}/{max_round}: compiler audit complete.",
|
|
2250
|
+
phase="experience_compiler",
|
|
2251
|
+
metadata_json={"package_id": pkg.name, "followup_index": followup_index, "judge_source": row.get("judge_source")},
|
|
2252
|
+
callback=progress_callback,
|
|
2253
|
+
)
|
|
2254
|
+
if round_idx < max_round:
|
|
2255
|
+
append_progress_event(
|
|
2256
|
+
run_root,
|
|
2257
|
+
"tdo_round_improved",
|
|
2258
|
+
run_id=run_root.name,
|
|
2259
|
+
message=f"Round {round_idx + 1}/{max_round}: improved weak learning objects.",
|
|
2260
|
+
phase="experience_compiler",
|
|
2261
|
+
metadata_json={"package_id": pkg.name, "followup_index": followup_index},
|
|
2262
|
+
callback=progress_callback,
|
|
2263
|
+
)
|
|
2264
|
+
status = "completed" if all(r.get("tdo_status") == "completed" for r in reports) else "partial"
|
|
2265
|
+
append_progress_event(
|
|
2266
|
+
run_root,
|
|
2267
|
+
"tdo_completed",
|
|
2268
|
+
run_id=run_root.name,
|
|
2269
|
+
message="Experience Compiler complete.",
|
|
2270
|
+
phase="experience_compiler_complete",
|
|
2271
|
+
metadata_json={"tdo_status": status, "package_count": len(reports), "followup_index": followup_index},
|
|
2272
|
+
callback=progress_callback,
|
|
2273
|
+
)
|
|
2274
|
+
write_json(run_root / "tdo_status.json", {"tdo_status": status, "reports": reports, "updated_at": utc_now()})
|
|
2275
|
+
return {"tdo_status": status, "reports": reports}
|
|
2276
|
+
except Exception as exc:
|
|
2277
|
+
safe = redact_secrets(str(exc))
|
|
2278
|
+
write_json(run_root / "tdo_status.json", {"tdo_status": "failed", "error": safe, "updated_at": utc_now()})
|
|
2279
|
+
append_progress_event(
|
|
2280
|
+
run_root,
|
|
2281
|
+
"tdo_failed",
|
|
2282
|
+
run_id=run_root.name,
|
|
2283
|
+
message=f"Experience Compiler failed: {safe}",
|
|
2284
|
+
phase="experience_compiler_failed",
|
|
2285
|
+
operational_error=safe,
|
|
2286
|
+
metadata_json={"followup_index": followup_index} if followup_index else None,
|
|
2287
|
+
callback=progress_callback,
|
|
2288
|
+
)
|
|
2289
|
+
return {"tdo_status": "failed", "error": safe}
|
|
2290
|
+
|
|
2291
|
+
|
|
2292
|
+
def tdo_manifest_fields_from_report(report: dict[str, Any]) -> dict[str, Any]:
|
|
2293
|
+
return {
|
|
2294
|
+
"tdo_enabled": report.get("tdo_status") != "disabled",
|
|
2295
|
+
"tdo_status": report.get("tdo_status"),
|
|
2296
|
+
"tdo_id": report.get("tdo_id"),
|
|
2297
|
+
"tdo_rounds_run": report.get("rounds_run"),
|
|
2298
|
+
"tdo_rows_by_type": report.get("rows_accepted_by_type") or {},
|
|
2299
|
+
"tdo_average_quality_score": report.get("average_quality_score"),
|
|
2300
|
+
"tdo_average_fidelity_score": report.get("average_fidelity_score"),
|
|
2301
|
+
"tdo_average_reuse_value_score": report.get("average_reuse_value_score"),
|
|
2302
|
+
"tdo_judge_source": report.get("tdo_judge_source"),
|
|
2303
|
+
"mentor_audited": report.get("mentor_audited"),
|
|
2304
|
+
}
|