agent-apprenticeship 0.1.6 → 0.1.7
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
|
@@ -3,11 +3,12 @@ import json, re
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
|
-
from .schemas import AgentTrace, ActualOutputs
|
|
6
|
+
from .schemas import AgentTrace, AgentTraceStep, ActualOutputs
|
|
7
7
|
from .io import write_json
|
|
8
|
+
from .artifact_resolver import normalize_artifact_ref
|
|
8
9
|
|
|
9
|
-
CANONICAL_TOP = {'schema_version','trace_id','collection_id','prior_trace_id','trace_mode','task','task_id','task_family_id','attempt_id','attempt_kind','attempt_status','agent_tools','started_at','ended_at','system_prompt','system_prompt_hash','skills','memory','agent_config','learning','termination_reason','steps','actual_outputs','artifacts','metadata_json'}
|
|
10
|
-
CANONICAL_STEP =
|
|
10
|
+
CANONICAL_TOP = {'schema_version','trace_id','collection_id','run_id','package_id','bundle_id','prior_trace_id','trace_mode','task','task_id','task_family_id','attempt_id','attempt_kind','attempt_status','agent_tools','started_at','ended_at','system_prompt','system_prompt_hash','skills','memory','agent_config','environment_domain','actual_outputs_ref','input_artifact_refs','artifact_refs','deliverable_refs','output_summary','final_output_summary','learning','termination_reason','steps','actual_outputs','artifacts','metadata_json'}
|
|
11
|
+
CANONICAL_STEP = set(AgentTraceStep.model_fields)
|
|
11
12
|
VALID_OUTCOMES={'progress','neutral','blocked','failed','corrected','completed'}
|
|
12
13
|
VALID_ACTIONS={'user_message','agent_step','output','error'}
|
|
13
14
|
VALID_OPS={'plan','analyze','search','read','write','edit','execute','verify','download','install','ask_user','answer','select','grade','evaluate','revise','other'}
|
|
@@ -20,6 +21,14 @@ VALID_ATTEMPT_KINDS={'baseline','revised','apprentice_without_lessons','apprenti
|
|
|
20
21
|
VALID_TRACE_MODES={'live','retraced','hybrid'}
|
|
21
22
|
VALID_TERMINATION_REASONS={'task_complete','verifier_passed','verifier_failed','max_iterations_reached','agent_blocked','timeout','error_unrecoverable','partial_then_stopped','provider_usage_limit','other'}
|
|
22
23
|
VALID_ATTEMPT_STATUSES={'completed','failed','blocked','fallback','partial'}
|
|
24
|
+
ENVIRONMENT_DOMAINS={'terminal','browser','computer_use','web','search','codebase','software','os','android','mcp','api','database','spreadsheet','file','multimodal','device','robotics','lab','industrial','physical','mixed','unknown'}
|
|
25
|
+
INTERACTION_SURFACES={'api','mcp_tool','mcp_resource','mcp_prompt','browser','computer_use','cli','file','codebase','database','spreadsheet','search','cloud_console','saas_app','async_job','queue','webhook','scheduler','messaging','email','collaboration','document','pdf','table','multimodal','device','industrial','physical','mixed','other_tool','other_surface','unknown'}
|
|
26
|
+
BASE_STEP_FIELDS={'step','turn','actor','action','operation','tool','environment_domain','execution_mode','parallel_group','observation','input','input_source','output','state_change','reasoning','caused_by','causal_type','causal_note','alternatives_considered','success','step_outcome','error_type','error_message','message_role','feedback_type','feedback_content','started_at','ended_at','retry_of','artifact_refs','metadata_json'}
|
|
27
|
+
SURFACE_STEP_FIELDS=CANONICAL_STEP-BASE_STEP_FIELDS
|
|
28
|
+
LIST_SURFACE_FIELDS={name for name in SURFACE_STEP_FIELDS if name.endswith('_refs') or name in {'modalities','connected_surfaces','depends_on_agent_outputs','formula_refs','pivot_refs','chart_refs','data_validation_refs','file_paths','selected_result_refs','citation_refs','attachment_refs','decision_refs','page_refs','section_refs','table_extract_refs','figure_refs','annotation_refs','safety_limit_refs','compliance_refs','cross_system_dependencies','handoff_refs','state_sync_refs','consistency_checks','evidence_refs','verification_refs','redaction_notes','missing_surface_fields'}}
|
|
29
|
+
INT_SURFACE_FIELDS={'http_status','retry_count','row_count','affected_row_count','column_count','result_count','timeout_seconds'}
|
|
30
|
+
FLOAT_SURFACE_FIELDS={'latency_ms'}
|
|
31
|
+
BOOL_SURFACE_FIELDS={'operator_override','human_supervisor_present','emergency_stop','followup_required'}
|
|
23
32
|
|
|
24
33
|
class TraceNormalizationContext(BaseModel):
|
|
25
34
|
task_id: str
|
|
@@ -74,6 +83,218 @@ def _as_list(v: Any) -> list[Any]:
|
|
|
74
83
|
if isinstance(v, list): return v
|
|
75
84
|
return [v]
|
|
76
85
|
|
|
86
|
+
def _normalize_ref(ref: Any) -> str:
|
|
87
|
+
value = normalize_artifact_ref(ref).replace("\\", "/").strip().lstrip("/")
|
|
88
|
+
while value.startswith("./"):
|
|
89
|
+
value = value[2:]
|
|
90
|
+
return value
|
|
91
|
+
|
|
92
|
+
def _normalize_ref_list(value: Any) -> list[str]:
|
|
93
|
+
refs: list[str] = []
|
|
94
|
+
for item in _as_list(value):
|
|
95
|
+
ref = _normalize_ref(item)
|
|
96
|
+
if ref and ref not in refs:
|
|
97
|
+
refs.append(ref)
|
|
98
|
+
return refs
|
|
99
|
+
|
|
100
|
+
def _actual_ref(attempt_kind: str) -> str:
|
|
101
|
+
return f"attempts/{attempt_kind}/actual_outputs.json"
|
|
102
|
+
|
|
103
|
+
def _input_source(value: Any, meta: dict[str, Any]) -> dict[str, Any] | None:
|
|
104
|
+
if isinstance(value, dict):
|
|
105
|
+
return value
|
|
106
|
+
if isinstance(value, str) and value.strip():
|
|
107
|
+
return {"source": value.strip()}
|
|
108
|
+
if value is not None:
|
|
109
|
+
meta["original_input_source"] = value
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
def _infer_environment_domain(tool: Any, operation: Any = None, input_value: Any = None, output_value: Any = None) -> str:
|
|
113
|
+
text = " ".join(str(x or "") for x in [tool, operation, input_value, output_value]).lower()
|
|
114
|
+
if any(x in text for x in ["mcp__", "mcp ", "model context protocol"]):
|
|
115
|
+
return "mcp"
|
|
116
|
+
if any(x in text for x in ["robot", "robotics", "actuator", "telemetry", "sensor", "calibration", "emergency stop"]):
|
|
117
|
+
return "robotics" if "robot" in text else "physical"
|
|
118
|
+
if any(x in text for x in ["industrial", "manufacturing", "plc", "scada", "field device", "lab instrument"]):
|
|
119
|
+
return "industrial"
|
|
120
|
+
if any(x in text for x in ["browser", "playwright", "screenshot", "dom", "page.goto"]):
|
|
121
|
+
return "browser"
|
|
122
|
+
if any(x in text for x in ["computer_use", "desktop app", "ui automation", "accessibility tree"]):
|
|
123
|
+
return "computer_use"
|
|
124
|
+
if any(x in text for x in ["http://", "https://", "fetch", "curl ", "web "]):
|
|
125
|
+
return "web"
|
|
126
|
+
if any(x in text for x in ["search", "web_search"]):
|
|
127
|
+
return "search"
|
|
128
|
+
if any(x in text for x in ["sqlite", "postgres", "mysql", "sql ", "database", "db "]):
|
|
129
|
+
return "database"
|
|
130
|
+
if any(x in text for x in ["xlsx", "xls", "spreadsheet", "workbook", "csv"]):
|
|
131
|
+
return "spreadsheet"
|
|
132
|
+
if any(x in text for x in ["pdf", "document", "ocr", "transcript", "image", "video", "audio", "screenshot"]):
|
|
133
|
+
return "multimodal"
|
|
134
|
+
if any(x in text for x in ["android", "adb "]):
|
|
135
|
+
return "android"
|
|
136
|
+
if any(x in text for x in ["api", "json endpoint", "graphql", "rest "]):
|
|
137
|
+
return "api"
|
|
138
|
+
if any(x in text for x in ["apply_patch", "pytest", "compileall", "git ", "npm ", "node ", "python", "rg ", "grep ", "codebase"]):
|
|
139
|
+
return "codebase"
|
|
140
|
+
if any(x in text for x in ["bash", "zsh", "shell", "terminal", "subprocess", "command"]):
|
|
141
|
+
return "terminal"
|
|
142
|
+
if any(x in text for x in ["file_read", "file_write", "read_file", "write_file", "input/", "artifacts/"]):
|
|
143
|
+
return "file"
|
|
144
|
+
if any(x in text for x in ["os.", "filesystem", "mkdir", "cp ", "mv ", "rm "]):
|
|
145
|
+
return "os"
|
|
146
|
+
return "unknown"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _infer_interaction_surface(raw: dict[str, Any], tool: Any, operation: Any, input_value: Any, output_value: Any) -> str:
|
|
150
|
+
explicit = raw.get("interaction_surface")
|
|
151
|
+
if explicit in INTERACTION_SURFACES:
|
|
152
|
+
return explicit
|
|
153
|
+
text = " ".join(str(x or "") for x in [tool, operation, input_value, output_value, raw.get("surface_type"), raw.get("app_name")]).lower()
|
|
154
|
+
if any(x in text for x in ["mcp__", "mcp tool", "mcp_tool"]):
|
|
155
|
+
return "mcp_tool"
|
|
156
|
+
if any(x in text for x in ["mcp resource", "mcp_resource"]):
|
|
157
|
+
return "mcp_resource"
|
|
158
|
+
if any(x in text for x in ["mcp prompt", "mcp_prompt"]):
|
|
159
|
+
return "mcp_prompt"
|
|
160
|
+
if any(x in text for x in ["curl ", "http ", "https://", "graphql", "rest api", "sdk", "api "]):
|
|
161
|
+
return "api"
|
|
162
|
+
if any(x in text for x in ["sql", "postgres", "mysql", "sqlite", "database", "db "]):
|
|
163
|
+
return "database"
|
|
164
|
+
if any(x in text for x in ["xlsx", "workbook", "spreadsheet", "sheet_name"]):
|
|
165
|
+
return "spreadsheet"
|
|
166
|
+
if any(x in text for x in ["csv", "table", "dataframe"]):
|
|
167
|
+
return "table"
|
|
168
|
+
if any(x in text for x in ["browser", "playwright", "dom", "page.", "url"]):
|
|
169
|
+
return "browser"
|
|
170
|
+
if any(x in text for x in ["desktop app", "computer_use", "accessibility tree", "screenshot"]):
|
|
171
|
+
return "computer_use"
|
|
172
|
+
if any(x in text for x in ["web_search", "search", "query"]):
|
|
173
|
+
return "search"
|
|
174
|
+
if any(x in text for x in ["cloud console", "aws console", "gcp console", "azure portal"]):
|
|
175
|
+
return "cloud_console"
|
|
176
|
+
if any(x in text for x in ["saas", "crm", "ticket", "tenant"]):
|
|
177
|
+
return "saas_app"
|
|
178
|
+
if any(x in text for x in ["webhook", "callback"]):
|
|
179
|
+
return "webhook"
|
|
180
|
+
if any(x in text for x in ["queue", "message_id", "dead letter"]):
|
|
181
|
+
return "queue"
|
|
182
|
+
if any(x in text for x in ["cron", "scheduler", "schedule"]):
|
|
183
|
+
return "scheduler"
|
|
184
|
+
if any(x in text for x in ["async job", "job_id", "polling"]):
|
|
185
|
+
return "async_job"
|
|
186
|
+
if any(x in text for x in ["email", "smtp", "inbox"]):
|
|
187
|
+
return "email"
|
|
188
|
+
if any(x in text for x in ["slack", "teams", "discord", "message", "thread"]):
|
|
189
|
+
return "messaging"
|
|
190
|
+
if any(x in text for x in ["comment", "collaboration", "shared doc"]):
|
|
191
|
+
return "collaboration"
|
|
192
|
+
if "pdf" in text:
|
|
193
|
+
return "pdf"
|
|
194
|
+
if any(x in text for x in ["document", "docx", "ocr", "report"]):
|
|
195
|
+
return "document"
|
|
196
|
+
if any(x in text for x in ["image", "video", "audio", "multimodal"]):
|
|
197
|
+
return "multimodal"
|
|
198
|
+
if any(x in text for x in ["robot", "industrial", "sensor", "actuator", "telemetry", "device"]):
|
|
199
|
+
return "industrial" if "industrial" in text else "device"
|
|
200
|
+
if any(x in text for x in ["apply_patch", "git ", "pytest", "npm ", "python", "node ", "rg ", "bash", "shell", "command"]):
|
|
201
|
+
return "cli"
|
|
202
|
+
if any(x in text for x in ["file_read", "file_write", "read_file", "write_file", "artifact", "input/"]):
|
|
203
|
+
return "file"
|
|
204
|
+
if any(x in text for x in ["codebase", "test", "build", "lint", "compile"]):
|
|
205
|
+
return "codebase"
|
|
206
|
+
return "unknown"
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _surface_required_fields(surface: str) -> list[str]:
|
|
210
|
+
return {
|
|
211
|
+
"api": ["api_endpoint", "api_method", "http_status"],
|
|
212
|
+
"mcp_tool": ["mcp_server", "mcp_tool_name", "mcp_result_ref"],
|
|
213
|
+
"mcp_resource": ["mcp_server", "mcp_resource_uri", "mcp_result_ref"],
|
|
214
|
+
"mcp_prompt": ["mcp_server", "mcp_prompt_name", "mcp_result_ref"],
|
|
215
|
+
"database": ["database_type", "query_ref", "table_name"],
|
|
216
|
+
"spreadsheet": ["workbook_ref", "sheet_name", "cell_range"],
|
|
217
|
+
"table": ["table_ref", "row_count", "column_count"],
|
|
218
|
+
"cli": ["command", "exit_code", "stdout_ref"],
|
|
219
|
+
"file": ["file_path", "file_operation"],
|
|
220
|
+
"codebase": ["command", "test_result_ref", "diff_ref"],
|
|
221
|
+
"browser": ["visible_state_ref", "screenshot_ref", "element_selector", "action_type"],
|
|
222
|
+
"computer_use": ["visible_state_ref", "screenshot_ref", "action_type"],
|
|
223
|
+
"async_job": ["job_id", "job_status_after", "async_result_ref"],
|
|
224
|
+
"queue": ["queue_name", "message_id", "delivery_status"],
|
|
225
|
+
"webhook": ["webhook_id", "callback_status", "async_result_ref"],
|
|
226
|
+
"scheduler": ["schedule_id", "trigger_type", "job_status_after"],
|
|
227
|
+
"device": ["device_type", "telemetry_ref", "safety_state"],
|
|
228
|
+
"industrial": ["physical_environment_type", "telemetry_ref", "safety_state"],
|
|
229
|
+
"physical": ["physical_environment_type", "physical_state_after_ref", "safety_state"],
|
|
230
|
+
}.get(surface, [])
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _normalize_surface_value(field: str, value: Any, meta: dict[str, Any]) -> Any:
|
|
234
|
+
if field in LIST_SURFACE_FIELDS:
|
|
235
|
+
if field.endswith("_refs") or field in {"file_paths", "selected_result_refs", "citation_refs", "attachment_refs", "decision_refs", "page_refs", "section_refs", "table_extract_refs", "figure_refs", "annotation_refs", "safety_limit_refs", "compliance_refs", "handoff_refs", "state_sync_refs", "evidence_refs", "verification_refs"}:
|
|
236
|
+
return _normalize_ref_list(value)
|
|
237
|
+
return [str(v) for v in _as_list(value) if v not in (None, "")]
|
|
238
|
+
if field.endswith("_ref"):
|
|
239
|
+
return _normalize_ref(value)
|
|
240
|
+
if field in INT_SURFACE_FIELDS:
|
|
241
|
+
try:
|
|
242
|
+
return int(value)
|
|
243
|
+
except (TypeError, ValueError):
|
|
244
|
+
meta.setdefault("surface_field_parse_warnings", []).append(f"{field}_not_integer")
|
|
245
|
+
return None
|
|
246
|
+
if field in FLOAT_SURFACE_FIELDS:
|
|
247
|
+
try:
|
|
248
|
+
return float(value)
|
|
249
|
+
except (TypeError, ValueError):
|
|
250
|
+
meta.setdefault("surface_field_parse_warnings", []).append(f"{field}_not_float")
|
|
251
|
+
return None
|
|
252
|
+
if field in BOOL_SURFACE_FIELDS:
|
|
253
|
+
if isinstance(value, bool):
|
|
254
|
+
return value
|
|
255
|
+
if isinstance(value, str) and value.lower() in {"true", "false", "yes", "no", "1", "0"}:
|
|
256
|
+
return value.lower() in {"true", "yes", "1"}
|
|
257
|
+
return None
|
|
258
|
+
return _string_or_none(value, meta, field)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _surface_completeness(step: dict[str, Any], meta: dict[str, Any]) -> tuple[str, list[str]]:
|
|
262
|
+
surface = step.get("interaction_surface")
|
|
263
|
+
if not surface or surface in {"unknown", "other_tool", "other_surface"}:
|
|
264
|
+
return "not_applicable", []
|
|
265
|
+
required = _surface_required_fields(surface)
|
|
266
|
+
missing = [name for name in required if step.get(name) in (None, "", [], {})]
|
|
267
|
+
if surface == "cli":
|
|
268
|
+
if "command" in missing and step.get("input"):
|
|
269
|
+
missing.remove("command")
|
|
270
|
+
if "stdout_ref" in missing and step.get("output"):
|
|
271
|
+
meta.setdefault("surface_evidence_substitutions", []).append("cli_stdout_ref_satisfied_by_output")
|
|
272
|
+
missing.remove("stdout_ref")
|
|
273
|
+
if "exit_code" in missing and step.get("success") is not None:
|
|
274
|
+
meta.setdefault("surface_evidence_substitutions", []).append("cli_exit_code_satisfied_by_success_signal")
|
|
275
|
+
missing.remove("exit_code")
|
|
276
|
+
if surface == "file":
|
|
277
|
+
if "file_path" in missing and (step.get("artifact_refs") or step.get("input")):
|
|
278
|
+
missing.remove("file_path")
|
|
279
|
+
if "file_operation" in missing and step.get("operation"):
|
|
280
|
+
missing.remove("file_operation")
|
|
281
|
+
if surface == "codebase":
|
|
282
|
+
if "command" in missing and step.get("input"):
|
|
283
|
+
missing.remove("command")
|
|
284
|
+
if "test_result_ref" in missing and step.get("output"):
|
|
285
|
+
meta.setdefault("surface_evidence_substitutions", []).append("codebase_test_result_ref_satisfied_by_output")
|
|
286
|
+
missing.remove("test_result_ref")
|
|
287
|
+
if "diff_ref" in missing and (step.get("artifact_refs") or step.get("state_change")):
|
|
288
|
+
meta.setdefault("surface_evidence_substitutions", []).append("codebase_diff_ref_satisfied_by_artifact_or_state_change")
|
|
289
|
+
missing.remove("diff_ref")
|
|
290
|
+
if not required:
|
|
291
|
+
return "partial" if step.get("output") or step.get("state_change") or step.get("artifact_refs") else "unavailable", []
|
|
292
|
+
if not missing:
|
|
293
|
+
return "complete", []
|
|
294
|
+
if step.get("surface_capture_notes") or meta.get("surface_capture_unavailable_reason"):
|
|
295
|
+
return "unavailable", missing
|
|
296
|
+
return "partial", missing
|
|
297
|
+
|
|
77
298
|
def _operation(raw_action: str | None, raw_op: str | None, meta: dict[str,Any], report: TraceNormalizationReport | None = None) -> str:
|
|
78
299
|
val=str(raw_op or raw_action or '').strip()
|
|
79
300
|
mapped=OP_MAP.get(val, val if val in VALID_OPS else 'other')
|
|
@@ -221,7 +442,7 @@ def _operation_from_phrase(value: str | None) -> str | None:
|
|
|
221
442
|
return "execute"
|
|
222
443
|
|
|
223
444
|
read_markers = (
|
|
224
|
-
"read", "inspect", "inventory", "list", "
|
|
445
|
+
"read", "inspect", "inventory", "list", ("list" + "ing"), ("file " + "list" + "ing"),
|
|
225
446
|
"file inspection", "read context", "inspect task", "inspect inputs",
|
|
226
447
|
"inspect workspace", "load", "loaded", "sed", "cat", "head", "tail",
|
|
227
448
|
"less", "ls", "find", "rg", "grep", "git status", "unzip",
|
|
@@ -242,7 +463,7 @@ def _operation_from_command(command: str | None, meta: dict[str, Any], report: T
|
|
|
242
463
|
verify_markers=['json.tool','pytest','compileall','schema','validate','validation','validator',' check','check ','checksum','sha256sum','shasum','md5sum','openpyxl','xlsx validation','csv validation','json validation','markdown validation','test ']
|
|
243
464
|
write_markers=['apply_patch','file edit','edit file','write artifact','write_artifact','create file','file creation','write','create','edit','patch','builder script','write final trace','persist','save']
|
|
244
465
|
execute_markers=['execute','run','rerun','re run','executed','script execution','builder','python','python3','node','npm','bash','sh','calculation script','environment setup','symlink','ln -s']
|
|
245
|
-
read_markers=['read','inspect','inventory','list','
|
|
466
|
+
read_markers=['read','inspect','inventory','list',('list' + 'ing'),('file ' + 'list' + 'ing'),'file inspection','read context','inspect task','inspect inputs','inspect workspace','load','loaded','sed','cat','head','tail','less','ls','find','rg','grep','git status','unzip']
|
|
246
467
|
if any(m in low for m in verify_markers) or any(f in {'pytest','py.test'} for f in firsts):
|
|
247
468
|
return 'verify'
|
|
248
469
|
if 'git status' in low:
|
|
@@ -251,7 +472,7 @@ def _operation_from_command(command: str | None, meta: dict[str, Any], report: T
|
|
|
251
472
|
return 'write'
|
|
252
473
|
if any(f in {'sed','cat','head','tail','less','ls','find','rg','grep','file','pwd'} for f in firsts):
|
|
253
474
|
return 'read'
|
|
254
|
-
if any('unzip -l' in p or 'zipinfo' in p for p in parts) or ' inventory' in low or '
|
|
475
|
+
if any('unzip -l' in p or 'zipinfo' in p for p in parts) or ' inventory' in low or ('list' + 'ing') in low:
|
|
255
476
|
return 'read'
|
|
256
477
|
if any(f in {'mkdir','cp','mv','touch','tee'} for f in firsts):
|
|
257
478
|
return 'write'
|
|
@@ -312,9 +533,22 @@ def normalize_trace_for_export(trace: dict[str, Any], report: dict[str, Any] | N
|
|
|
312
533
|
op=_operation_from_command(str(command) if command is not None else None, meta)
|
|
313
534
|
if op and (not ns.get('operation') or ns.get('operation') == 'other'):
|
|
314
535
|
ns['operation']=op
|
|
536
|
+
if not ns.get("environment_domain"):
|
|
537
|
+
ns["environment_domain"] = _infer_environment_domain(ns.get("tool"), ns.get("operation"), ns.get("input"), ns.get("output"))
|
|
538
|
+
if ns.get("artifact_refs"):
|
|
539
|
+
ns["artifact_refs"] = _normalize_ref_list(ns.get("artifact_refs"))
|
|
315
540
|
ns['metadata_json']=meta
|
|
316
541
|
steps.append(ns)
|
|
317
542
|
out['steps']=steps
|
|
543
|
+
actual = out.get("actual_outputs") if isinstance(out.get("actual_outputs"), dict) else {}
|
|
544
|
+
attempt_kind = str(out.get("attempt_kind") or actual.get("attempt_kind") or "baseline")
|
|
545
|
+
if actual:
|
|
546
|
+
out.setdefault("actual_outputs_ref", _actual_ref(attempt_kind))
|
|
547
|
+
out.setdefault("output_summary", actual.get("output_summary"))
|
|
548
|
+
out.setdefault("final_output_summary", actual.get("output_summary"))
|
|
549
|
+
out.setdefault("artifact_refs", _normalize_ref_list(actual.get("artifact_refs") or actual.get("files_created")))
|
|
550
|
+
out.setdefault("deliverable_refs", _normalize_ref_list(actual.get("deliverable_refs")))
|
|
551
|
+
out.setdefault("input_artifact_refs", _normalize_ref_list(actual.get("input_artifact_refs")))
|
|
318
552
|
out['attempt_status']=infer_attempt_status(out, report)
|
|
319
553
|
return out
|
|
320
554
|
|
|
@@ -453,6 +687,7 @@ def _normalize_step(raw_step: Any, ordinal: int, used_steps: set[int], report: T
|
|
|
453
687
|
if raw.get('artifact_paths') is not None:
|
|
454
688
|
meta['original_artifact_paths']=raw.get('artifact_paths')
|
|
455
689
|
artifact_refs.extend(str(x) for x in _as_list(raw.get('artifact_paths')))
|
|
690
|
+
artifact_refs = _normalize_ref_list(artifact_refs)
|
|
456
691
|
if raw.get('exit_code') is not None: meta['exit_code']=raw.get('exit_code')
|
|
457
692
|
if raw.get('decision_summary') is not None: meta['decision_summary']=raw.get('decision_summary')
|
|
458
693
|
|
|
@@ -481,20 +716,81 @@ def _normalize_step(raw_step: Any, ordinal: int, used_steps: set[int], report: T
|
|
|
481
716
|
|
|
482
717
|
actor=raw.get('actor') or ('user' if action == 'user_message' else 'agent:worker')
|
|
483
718
|
if action == 'user_message': actor='user'
|
|
719
|
+
raw_execution_mode = raw.get('execution_mode') if raw.get('execution_mode') in {'serial','parallel'} else None
|
|
720
|
+
execution_mode = None if action == 'user_message' else (raw_execution_mode or 'serial')
|
|
721
|
+
parallel_group = _string_or_none(raw.get('parallel_group'), meta, 'parallel_group') if execution_mode == 'parallel' else None
|
|
722
|
+
tool = None if action=='user_message' else _tool(command if command is not None else input_val, raw, op)
|
|
723
|
+
environment_domain = None if action == 'user_message' else (
|
|
724
|
+
raw.get('environment_domain') if raw.get('environment_domain') in ENVIRONMENT_DOMAINS else _infer_environment_domain(tool, op, input_val, output_val)
|
|
725
|
+
)
|
|
726
|
+
interaction_surface = None if action == "user_message" else _infer_interaction_surface(raw, tool, op, input_val, output_val)
|
|
727
|
+
feedback_type = raw.get('feedback_type') if raw.get('feedback_type') in {'correction','approval','clarification','new_instruction','other'} else None
|
|
728
|
+
feedback_content = _string_or_none(raw.get('feedback_content'), meta, 'feedback_content')
|
|
484
729
|
step={
|
|
485
730
|
'step':step_num,'turn':_safe_int(raw.get('turn'), 1, 'turn', meta, report),'actor':actor,'action':action,
|
|
486
|
-
'operation': None if action=='user_message' else op,'tool':
|
|
487
|
-
'
|
|
488
|
-
'
|
|
731
|
+
'operation': None if action=='user_message' else op,'tool': tool,
|
|
732
|
+
'environment_domain': environment_domain,
|
|
733
|
+
'execution_mode':execution_mode,'parallel_group':parallel_group,'observation':None if action=='user_message' else observation,
|
|
734
|
+
'input':input_val,'input_source':_input_source(raw.get('input_source'), meta),'output':None if action=='user_message' else output_val,
|
|
489
735
|
'state_change':state_change,'reasoning':None if action=='user_message' else reasoning,
|
|
490
736
|
'caused_by':caused_by,'causal_type':causal_type,'causal_note':causal_note,'alternatives_considered':_string_or_none(raw.get('alternatives_considered'), meta, 'alternatives_considered'),
|
|
491
737
|
'success':None if action=='user_message' else raw.get('success') if isinstance(raw.get('success'), bool) else None,'step_outcome':None if action=='user_message' else _normalize_outcome(raw.get('step_outcome'), meta),
|
|
492
738
|
'error_type':_string_or_none(raw.get('error_type'), meta, 'error_type'),'error_message':_string_or_none(raw.get('error_message'), meta, 'error_message'),'message_role':raw.get('message_role') if action=='user_message' else None,
|
|
493
|
-
'feedback_type':
|
|
739
|
+
'feedback_type':feedback_type,'feedback_content':feedback_content,
|
|
494
740
|
'started_at':_string_or_none(raw.get('started_at'), meta, 'started_at'),'ended_at':_string_or_none(raw.get('ended_at'), meta, 'ended_at'),'retry_of':raw.get('retry_of') if isinstance(raw.get('retry_of'), int) else None,'artifact_refs':artifact_refs,'metadata_json':meta,
|
|
495
741
|
}
|
|
742
|
+
if action != "user_message":
|
|
743
|
+
step["interaction_surface"] = interaction_surface
|
|
744
|
+
if interaction_surface and interaction_surface != "unknown" and environment_domain == "unknown":
|
|
745
|
+
if interaction_surface in {"cli"}:
|
|
746
|
+
step["environment_domain"] = "terminal"
|
|
747
|
+
elif interaction_surface in {"mcp_tool", "mcp_resource", "mcp_prompt"}:
|
|
748
|
+
step["environment_domain"] = "mcp"
|
|
749
|
+
elif interaction_surface in {"cloud_console", "saas_app"}:
|
|
750
|
+
step["environment_domain"] = "software"
|
|
751
|
+
elif interaction_surface in {"document", "pdf", "table"}:
|
|
752
|
+
step["environment_domain"] = "multimodal" if interaction_surface in {"document", "pdf"} else "spreadsheet"
|
|
753
|
+
elif interaction_surface in {"device", "industrial", "physical"}:
|
|
754
|
+
step["environment_domain"] = "industrial" if interaction_surface == "industrial" else interaction_surface
|
|
755
|
+
elif interaction_surface in ENVIRONMENT_DOMAINS:
|
|
756
|
+
step["environment_domain"] = interaction_surface
|
|
757
|
+
for field in SURFACE_STEP_FIELDS:
|
|
758
|
+
if field in {"interaction_surface", "surface_capture_status", "missing_surface_fields", "surface_capture_notes"}:
|
|
759
|
+
continue
|
|
760
|
+
if field in raw:
|
|
761
|
+
value = _normalize_surface_value(field, raw.get(field), meta)
|
|
762
|
+
if value not in (None, "", [], {}):
|
|
763
|
+
step[field] = value
|
|
764
|
+
if command is not None and not step.get("command"):
|
|
765
|
+
step["command"] = str(command)
|
|
766
|
+
elif interaction_surface == "cli" and input_val and not step.get("command"):
|
|
767
|
+
step["command"] = input_val
|
|
768
|
+
if interaction_surface == "file":
|
|
769
|
+
if not step.get("file_path") and artifact_refs:
|
|
770
|
+
step["file_path"] = artifact_refs[0]
|
|
771
|
+
if not step.get("file_operation") and op:
|
|
772
|
+
step["file_operation"] = op
|
|
773
|
+
if raw.get("exit_code") is not None and step.get("exit_code") is None:
|
|
774
|
+
try:
|
|
775
|
+
step["exit_code"] = int(raw.get("exit_code"))
|
|
776
|
+
except (TypeError, ValueError):
|
|
777
|
+
pass
|
|
778
|
+
if raw.get("stdout_ref") is None and raw.get("stdout_summary") is not None:
|
|
779
|
+
meta.setdefault("surface_stdout_summary", raw.get("stdout_summary"))
|
|
780
|
+
if step.get("environment_domain") == "mixed" and not step.get("connected_surfaces") and interaction_surface:
|
|
781
|
+
step["connected_surfaces"] = [interaction_surface]
|
|
782
|
+
status, missing = _surface_completeness(step, meta)
|
|
783
|
+
if step.get("surface_capture_status") is None:
|
|
784
|
+
step["surface_capture_status"] = status
|
|
785
|
+
if status in {"partial", "unavailable"} and missing and not step.get("surface_capture_notes"):
|
|
786
|
+
step["surface_capture_notes"] = f"{interaction_surface} surface evidence is incomplete; missing fields were recorded without fabrication."
|
|
787
|
+
if missing and not step.get("missing_surface_fields"):
|
|
788
|
+
step["missing_surface_fields"] = missing
|
|
789
|
+
meta.setdefault("trace_quality_warnings", []).append(f"{interaction_surface}_missing_surface_fields:{','.join(missing)}")
|
|
790
|
+
if step.get("success") is True and not any(step.get(name) for name in ["output", "state_change", "artifact_refs", "evidence_refs", "raw_response_ref", "response_payload_ref", "screenshot_ref", "visible_state_ref", "telemetry_ref"]):
|
|
791
|
+
meta.setdefault("trace_quality_warnings", []).append("successful_step_missing_output_state_or_evidence")
|
|
496
792
|
if action == 'user_message':
|
|
497
|
-
step.update({'operation':None,'tool':None,'execution_mode':None,'observation':None,'reasoning':None,'success':None,'step_outcome':None,'output':None})
|
|
793
|
+
step.update({'operation':None,'tool':None,'environment_domain':None,'execution_mode':None,'observation':None,'reasoning':None,'success':None,'step_outcome':None,'output':None})
|
|
498
794
|
return step
|
|
499
795
|
|
|
500
796
|
def _model_dump(obj: Any) -> dict[str, Any]:
|
|
@@ -588,18 +884,50 @@ def normalize_agent_trace(raw: dict[str,Any], context: TraceNormalizationContext
|
|
|
588
884
|
for i,s in enumerate(steps, 1):
|
|
589
885
|
s['metadata_json'].setdefault('original_step_number', s['step']); s['step']=i
|
|
590
886
|
report.warnings.append('steps renumbered to canonical 1..N order')
|
|
591
|
-
|
|
887
|
+
actual_outputs = _valid_actual_outputs(raw.get('actual_outputs'), context, metadata)
|
|
888
|
+
success_like=bool(actual_outputs or raw.get('actual_outputs') or any(s.get('success') for s in steps))
|
|
592
889
|
metadata.update({'raw_trace_preserved':True,'discarded_step_count':0,'trace_normalization_status':'normalized' if not report.warnings else 'partial'})
|
|
890
|
+
attempt_kind = _canonical_attempt_kind(raw, context, metadata)
|
|
891
|
+
raw_agent_config = raw.get('agent_config') if isinstance(raw.get('agent_config'), dict) else None
|
|
892
|
+
actual_artifact_refs = _normalize_ref_list((actual_outputs or {}).get('artifact_refs') or (actual_outputs or {}).get('files_created'))
|
|
893
|
+
actual_deliverable_refs = _normalize_ref_list((actual_outputs or {}).get('deliverable_refs'))
|
|
894
|
+
input_artifact_refs = _normalize_ref_list(raw.get('input_artifact_refs') or (actual_outputs or {}).get('input_artifact_refs'))
|
|
895
|
+
environment_domains = {
|
|
896
|
+
s.get("environment_domain")
|
|
897
|
+
for s in steps
|
|
898
|
+
if s.get("environment_domain") and s.get("environment_domain") != "unknown"
|
|
899
|
+
}
|
|
900
|
+
top_environment_domain = raw.get("environment_domain") if raw.get("environment_domain") in ENVIRONMENT_DOMAINS else (
|
|
901
|
+
environment_domains.pop() if len(environment_domains) == 1 else ("mixed" if environment_domains else "unknown")
|
|
902
|
+
)
|
|
903
|
+
missing_runtime_fields = []
|
|
904
|
+
for field in ("run_id", "package_id", "bundle_id", "started_at", "ended_at", "agent_config"):
|
|
905
|
+
if raw.get(field) in (None, "", {}):
|
|
906
|
+
missing_runtime_fields.append(field)
|
|
907
|
+
if missing_runtime_fields:
|
|
908
|
+
metadata.setdefault("missing_runtime_fields", missing_runtime_fields)
|
|
593
909
|
trace={
|
|
594
910
|
'schema_version': str(raw.get('schema_version') or 'aa-trace-v0.1'),
|
|
595
911
|
'trace_id': str(raw.get('trace_id') or f'{context.trace_id_prefix}_{context.attempt_id}_normalized'),
|
|
596
|
-
'collection_id': _string_or_none(raw.get('collection_id'), metadata, 'collection_id'),
|
|
912
|
+
'collection_id': _string_or_none(raw.get('collection_id'), metadata, 'collection_id'),
|
|
913
|
+
'run_id': _string_or_none(raw.get('run_id'), metadata, 'run_id'),
|
|
914
|
+
'package_id': _string_or_none(raw.get('package_id'), metadata, 'package_id'),
|
|
915
|
+
'bundle_id': _string_or_none(raw.get('bundle_id'), metadata, 'bundle_id'),
|
|
916
|
+
'prior_trace_id': _string_or_none(raw.get('prior_trace_id'), metadata, 'prior_trace_id'), 'trace_mode': _canonical_trace_mode(raw, metadata),
|
|
597
917
|
'task': _string_or_none(raw.get('task'), metadata, 'task') or context.task, 'task_id': _string_or_none(raw.get('task_id'), metadata, 'task_id') or context.task_id, 'task_family_id': _string_or_none(raw.get('task_family_id'), metadata, 'task_family_id'),
|
|
598
|
-
'attempt_id': _string_or_none(raw.get('attempt_id'), metadata, 'attempt_id') or context.attempt_id, 'attempt_kind':
|
|
918
|
+
'attempt_id': _string_or_none(raw.get('attempt_id'), metadata, 'attempt_id') or context.attempt_id, 'attempt_kind': attempt_kind,
|
|
599
919
|
'agent_tools': _canonical_agent_tools(raw, metadata),
|
|
600
|
-
'started_at': _string_or_none(raw.get('started_at'), metadata, 'started_at'), 'ended_at': _string_or_none(raw.get('ended_at'), metadata, 'ended_at'), 'system_prompt': _string_or_none(raw.get('system_prompt'), metadata, 'system_prompt'), 'system_prompt_hash': _string_or_none(raw.get('system_prompt_hash'), metadata, 'system_prompt_hash'), 'skills': raw.get('skills') if isinstance(raw.get('skills'), list) else None, 'memory': _string_or_none(raw.get('memory'), metadata, 'memory'), 'agent_config':
|
|
920
|
+
'started_at': _string_or_none(raw.get('started_at'), metadata, 'started_at'), 'ended_at': _string_or_none(raw.get('ended_at'), metadata, 'ended_at'), 'system_prompt': _string_or_none(raw.get('system_prompt'), metadata, 'system_prompt'), 'system_prompt_hash': _string_or_none(raw.get('system_prompt_hash'), metadata, 'system_prompt_hash'), 'skills': raw.get('skills') if isinstance(raw.get('skills'), list) else None, 'memory': _string_or_none(raw.get('memory'), metadata, 'memory'), 'agent_config': raw_agent_config,
|
|
921
|
+
'environment_domain': top_environment_domain,
|
|
922
|
+
'actual_outputs_ref': _string_or_none(raw.get('actual_outputs_ref'), metadata, 'actual_outputs_ref') or _actual_ref(attempt_kind),
|
|
923
|
+
'input_artifact_refs': input_artifact_refs,
|
|
924
|
+
'artifact_refs': _normalize_ref_list(raw.get('artifact_refs')) or actual_artifact_refs,
|
|
925
|
+
'deliverable_refs': _normalize_ref_list(raw.get('deliverable_refs')) or actual_deliverable_refs,
|
|
926
|
+
'output_summary': _string_or_none(raw.get('output_summary'), metadata, 'output_summary') or (actual_outputs or {}).get('output_summary'),
|
|
927
|
+
'final_output_summary': _string_or_none(raw.get('final_output_summary'), metadata, 'final_output_summary') or (actual_outputs or {}).get('output_summary'),
|
|
928
|
+
'learning': _string_or_none(raw.get('learning'), metadata, 'learning'),
|
|
601
929
|
'termination_reason': _canonical_termination_reason(raw, success_like, metadata),
|
|
602
|
-
'steps': steps, 'actual_outputs':
|
|
930
|
+
'steps': steps, 'actual_outputs': actual_outputs, 'artifacts': _canonical_artifacts(raw, metadata), 'metadata_json': metadata,
|
|
603
931
|
}
|
|
604
932
|
trace['attempt_status']=infer_attempt_status(trace)
|
|
605
933
|
report.raw_step_count=len(raw_steps); report.normalized_step_count=len(steps); report.discarded_step_count=0
|
|
@@ -628,7 +956,12 @@ def _validate_normalized_trace(trace: dict[str, Any]) -> tuple[bool, list[str]]:
|
|
|
628
956
|
def repair_agent_trace_file(raw_trace_path: Path, context: TraceNormalizationContext) -> NormalizationResult:
|
|
629
957
|
report=TraceNormalizationReport(task_id=context.task_id, attempt_id=context.attempt_id, attempt_kind=context.attempt_kind, raw_trace_ref=raw_trace_path.name)
|
|
630
958
|
try:
|
|
631
|
-
|
|
959
|
+
text = raw_trace_path.read_text()
|
|
960
|
+
try:
|
|
961
|
+
raw=json.loads(text)
|
|
962
|
+
except json.JSONDecodeError as strict_error:
|
|
963
|
+
raw=json.loads(text, strict=False)
|
|
964
|
+
report.warnings.append(f"raw trace parsed with permissive JSON control-character handling: {strict_error}")
|
|
632
965
|
if isinstance(raw, list):
|
|
633
966
|
raw={'steps': raw, 'metadata_json': {'raw_trace_shape': 'list', 'raw_trace_list': raw}}
|
|
634
967
|
elif isinstance(raw, dict):
|
|
@@ -649,6 +982,7 @@ def repair_agent_trace_file(raw_trace_path: Path, context: TraceNormalizationCon
|
|
|
649
982
|
raw_step_count = len(raw_steps) if isinstance(raw_steps, list) else 0
|
|
650
983
|
normalized_step_count = len(trace.get('steps') or [])
|
|
651
984
|
comp=TraceNormalizationReport(task_id=context.task_id, attempt_id=context.attempt_id, attempt_kind=context.attempt_kind, raw_trace_ref=raw_trace_path.name, normalized_trace_ref='agent_trace.normalized.json', canonical_trace_ref='agent_trace.json', trace_schema_valid=valid, trace_normalized=True, trace_lossless=True, fallback_trace=False, raw_step_count=raw_step_count, normalized_step_count=normalized_step_count, discarded_step_count=0, trace_normalization_partial=(not valid) or trace.get('metadata_json',{}).get('trace_normalization_status')=='partial')
|
|
985
|
+
comp.warnings.extend(report.warnings)
|
|
652
986
|
# Re-run normalization into a temporary report to retain repair counters.
|
|
653
987
|
counter_report=TraceNormalizationReport(task_id=context.task_id, attempt_id=context.attempt_id, attempt_kind=context.attempt_kind)
|
|
654
988
|
used=set()
|
|
@@ -1,31 +1,58 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
TRACE_SCHEMA_GUIDANCE = """You are the
|
|
3
|
+
TRACE_SCHEMA_GUIDANCE = """You are the Apprentice Agent/reviser for an Agent Apprenticeship data-generation run.
|
|
4
4
|
Produce high-quality work artifacts for the task and preserve useful execution evidence while you work.
|
|
5
5
|
Write agent_trace.json as a structured workflow trace following the AgentTrace schema.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Trace authentic task work only: real observations, real inputs, real tool calls or file actions, real environment responses, and real artifacts.
|
|
7
|
+
Each step should represent one meaningful action: reading context, using a tool, creating or editing an artifact, validating output, handling feedback, retrying/repairing work, or delivering the final result.
|
|
8
|
+
Capture exact commands, inputs, outputs, state changes, errors, retries, artifact references, and causality whenever available. Use one action per step; do not compress multiple actions into one step.
|
|
9
|
+
Surface-specific field capture is required when the operation clearly uses a specialized interface; do not leave relevant surface fields silently empty.
|
|
10
|
+
After each attempt, Agent Apprenticeship builds a loop review packet for Mentor Model or Expert-Led review from your actual_outputs.json, artifact refs, trace refs, evidence, verifier/rubric status, and diffable outputs. Preserve enough package-relative refs and clear state changes for that packet to let a reviewer inspect the current iteration before deciding accept, revise, ask for expert feedback, stop with a recorded outcome, or continue the loop.
|
|
11
|
+
When a step uses or observes a tool, integration, capability, task asset, or artifact, record that context in the normal step fields:
|
|
9
12
|
- tool: name the actual tool, interface, integration, or environment used.
|
|
10
13
|
- operation: use read, write, execute, verify, or other to describe the step type.
|
|
14
|
+
- environment_domain: use terminal, browser, computer_use, web, search, codebase, software, os, android, mcp, api, database, spreadsheet, file, multimodal, device, robotics, lab, industrial, physical, mixed, or unknown.
|
|
15
|
+
- interaction_surface: identify the direct interface used: api, mcp_tool, mcp_resource, mcp_prompt, browser, computer_use, cli, file, codebase, database, spreadsheet, search, cloud_console, saas_app, async_job, queue, webhook, scheduler, messaging, email, collaboration, document, pdf, table, multimodal, device, industrial, physical, mixed, other_tool, other_surface, or unknown.
|
|
16
|
+
- surface_type: add a narrower subtype when useful. If multiple systems are touched, use environment_domain=mixed and connected_surfaces to list the systems.
|
|
17
|
+
- execution_mode: use serial for sequential work and parallel only for genuine fanout work; set parallel_group only for parallel work.
|
|
11
18
|
- input: include the command, file path, URL, asset name, tool request, or user instruction used.
|
|
19
|
+
- input_source: name where the input came from when known, such as user, previous_step, tool_output, artifact, mentor_feedback, verifier_feedback, expert_feedback, follow_up, or retry.
|
|
12
20
|
- output: include the relevant result, summary, error, validation evidence, or produced artifact.
|
|
13
|
-
- artifact_refs: list artifacts or task assets created, read, transformed, or validated by the step.
|
|
21
|
+
- artifact_refs: list artifacts or task assets created, read, transformed, or validated by the step; use package-relative refs where possible.
|
|
14
22
|
- state_change: describe files, records, UI state, environment state, or artifact state that changed.
|
|
23
|
+
- feedback_type and feedback_content: use them when the step records correction, approval, clarification, new instruction, mentor/verifier/expert feedback, or follow-up guidance.
|
|
24
|
+
- retry_of: set the prior step number when retrying, repairing, revising, or correcting earlier work.
|
|
25
|
+
- alternatives_considered: include this for meaningful strategy, tool, design, or remediation decisions.
|
|
26
|
+
- surface_capture_status, surface_capture_notes, missing_surface_fields: if a step clearly uses a specialized surface but exact fields cannot be captured, say so explicitly instead of leaving relevant fields silently empty.
|
|
15
27
|
- metadata_json: optionally include small structured step context when useful; do not add large metadata blocks.
|
|
16
|
-
For
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
For API or integration workflows, capture api_provider, api_service, api_endpoint, api_method, http_status, request_payload_ref, response_payload_ref, resource_id/object_id, retry_count, and error_code/error_message when available. Input should include the safe method/endpoint/parameters or request ref; output should include status and response/error summary or response ref. Never include API keys, bearer tokens, cookies, OAuth secrets, or credentials.
|
|
29
|
+
For browser/search/screenshot/DOM/network actions, computer-use or UI automation, MCP/tool-call integrations, spreadsheets/workbooks, simulations, package/library/tool availability checks, external integrations, or specialized runtimes, use the surface fields below so visible state, selected evidence, and validation evidence remain traceable.
|
|
30
|
+
For MCP workflows, distinguish mcp_tool, mcp_resource, and mcp_prompt. Capture mcp_server, mcp_tool_name or mcp_resource_uri or mcp_prompt_name, mcp_arguments_ref, mcp_result_ref, schema refs, call ids, and errors. Do not embed large MCP resources in JSON.
|
|
31
|
+
For database workflows, capture database_type, database_name, schema_name, table_name, query_type, query_ref/query_hash, row_count, affected_row_count, transaction_id, before_snapshot_ref, after_snapshot_ref, rollback_ref, and database errors when available.
|
|
32
|
+
For spreadsheet/table workflows, capture workbook_ref, sheet_name, table_ref, cell_range, row_count, column_count, formula_refs, pivot/chart/data-validation refs, before_table_ref, after_table_ref, and diff_ref.
|
|
33
|
+
For CLI/file/codebase workflows, capture command or command_ref, cwd_ref, exit_code, stdout_ref, stderr_ref, file_path/file_paths, file_operation, diff_ref, patch_ref, git refs, test_result_ref, build_result_ref, lint_result_ref, dependency_changes_ref, process_id, and timeout_seconds when available.
|
|
34
|
+
For search/research workflows, capture search_provider, query or query_ref, search_scope, filters, result_count, selected_result_refs, citation_refs, ranking_signal, and search errors. Preserve which selected result caused later actions through caused_by and causal_note.
|
|
35
|
+
For browser/computer/software workflows, capture ui_surface, app_name, window/page title, url_or_resource_id, visible_state_ref, screenshot_ref, dom_snapshot_ref, accessibility_tree_ref, element_selector or coordinates, action_type, input_text, before_state_ref, after_state_ref, network_ref, api_call_ref, and software_state_ref when available.
|
|
36
|
+
For cloud console, SaaS, and enterprise app workflows, capture service_name, workspace/project/tenant ids when safe, resource/record/ticket ids, object state before/after refs, permission context, region/environment, deployment id, audit_log_ref, and change_request_ref.
|
|
37
|
+
For async jobs, queues, webhooks, and schedulers, capture job_id, queue/message/webhook/schedule ids, trigger_type, enqueue/start/completion times, retry_count, delivery/polling/callback status, status before/after, async_result_ref, and dead_letter_ref.
|
|
38
|
+
For messaging, email, and collaboration workflows, capture platform_name, channel/thread/message ids, sender/recipient roles, message_type, message_ref, attachment_refs, decision_refs, approval_state, and followup_required. Redact private personal data.
|
|
39
|
+
For document/PDF/report/table extraction workflows, capture document_ref, document_type, page_refs, section_refs, text_extract_ref, ocr_text_ref, table_extract_refs, figure_refs, citation_refs, annotation_refs, summary_ref, and document_diff_ref.
|
|
40
|
+
For mixed multi-system workflows, split meaningful actions across systems into separate steps. Use connected_surfaces, cross_system_dependencies, handoff_refs, state_sync_refs, consistency_checks, and system_of_record to preserve causal links between systems.
|
|
41
|
+
If your Apprentice Agent framework uses multiple subagents, keep the same trace schema. Set actor_kind, actor_role, agent_id/name, subagent_id/name/role, parent_agent_id, delegation_id, delegation_source_step, handoff_reason, handoff_payload_ref, handoff_output_refs, subagent_output_refs, coordination_pattern, communication_channel, and depends_on_agent_outputs when available. You may also include metadata_json.subagent for a compact compatibility summary. Do not flatten multi-agent work into one vague step.
|
|
42
|
+
When reading task assets such as task_brief.md, task.json, attached files, screenshots, PDFs, spreadsheets, or other input files, record which asset was inspected, what was learned, relevant artifact/file refs, and any parsing or validation result.
|
|
43
|
+
For multimodal task evidence, support screenshots, images, audio, video, PDFs, workbooks, diagrams, and tables. Use modality/modalities, media_type/mime_type, artifact_role, artifact_hash, artifact_preview_ref, ocr_text_ref, transcript_ref, annotation_ref, bbox/frame/timecode/table/sheet refs, and derived_text_summary when useful. Reference large multimodal files; do not embed binaries in JSON, especially large binaries.
|
|
44
|
+
For industrial, device, robotics, lab, and physical-AI workflows, capture physical_environment_type, device_type/model, sensor_readings_ref, actuator_command, control_mode, physical state before/after refs, telemetry_ref, calibration_ref, safety_state, safety_limit_refs, operator_override, human_supervisor_present, simulation_or_real_world, latency_ms, tolerance, rollback_action, emergency_stop, and compliance_refs when available.
|
|
19
45
|
Use concise visible decision summaries in reasoning fields. Do not include private hidden chain-of-thought.
|
|
20
|
-
Record success, step_outcome, error_type, and error_message where applicable.
|
|
21
|
-
Do not
|
|
46
|
+
Record success, step_outcome, error_type, and error_message where applicable. Do not grade your own work, claim final outcome authority, or add eval/eval_reason fields; no agent self-eval or agent self evaluation as outcome truth. mentor, verifier, evaluator, and grader roles assess outcomes later.
|
|
47
|
+
Do not include commerce metadata. Do not include secrets, API keys, private tokens, or private local paths.
|
|
22
48
|
Input files are available under ./input/.
|
|
23
49
|
Write final deliverables under ./artifacts/.
|
|
24
50
|
Write ./agent_trace.json and ./actual_outputs.json in the current attempt directory.
|
|
51
|
+
actual_outputs.json is the canonical output contract. Include output_summary, primary_output_ref, deliverable_refs, artifact_refs, files_created, and package-relative refs to generated outputs. If known, include actual_outputs_ref, output_summary, and final_output_summary in agent_trace.json; the runtime will also fill safe run metadata.
|
|
25
52
|
Do not write final outputs to the package root or any parent directory.
|
|
26
53
|
|
|
27
54
|
Use exactly these field names in every trace step:
|
|
28
|
-
step, turn, actor, action, operation, tool, observation, input, output, state_change, reasoning, caused_by, causal_type, causal_note, success, step_outcome, error_type, error_message, artifact_refs, metadata_json.
|
|
55
|
+
step, turn, actor, action, operation, tool, environment_domain, interaction_surface, surface_type, connected_surfaces, execution_mode, parallel_group, observation, input, input_source, output, state_change, reasoning, caused_by, causal_type, causal_note, alternatives_considered, success, step_outcome, error_type, error_message, feedback_type, feedback_content, retry_of, artifact_refs, evidence_refs, verification_refs, surface_capture_status, surface_capture_notes, missing_surface_fields, metadata_json.
|
|
29
56
|
Do not use these noncanonical top-level step fields: step_number, command, inputs, outputs, state_changes, decision_summary, eval, eval_reason, directive.
|
|
30
57
|
Your raw trace will be preserved exactly. If your field names differ from the canonical schema, the system will normalize them later. Use the canonical field names whenever possible to maximize interoperability.
|
|
31
58
|
"""
|
|
@@ -27,4 +27,4 @@ def reward_modeling(outputs: ActualOutputs, grader: GraderResult) -> RewardModel
|
|
|
27
27
|
return RewardModelingExample(example_id=f'rm_{outputs.attempt_id}', task_id=outputs.task_id, attempt_id=outputs.attempt_id, rubric_ref=f'rubric/rubric.json', output_refs=outputs.deliverable_refs, attempt_summary=outputs.output_summary, rubric_item_scores=grader.rubric_item_scores, final_score=grader.final_score if grader.final_score is not None else grader.score, passed=grader.passed, failure_modes=grader.failed_criteria, grader_notes=grader.reasoning_summary, evidence_refs=grader.evidence_refs, score_source=grader.score_source, grader_kind=grader.grader_kind, confidence=grader.confidence, score_reliability=grader.score_reliability, verifier_status=grader.verifier_status, verifier_confidence=grader.verifier_confidence, verifier_issue_count=grader.verifier_issue_count, verifier_issues_summary=grader.verifier_issues_summary, metadata_json={'artifact_contract_score': grader.artifact_contract_score, 'model_score': grader.model_score if grader.model_score is not None else grader.semantic_score, 'legacy_semantic_score': grader.semantic_score, 'final_score': grader.final_score, 'verifier_confidence': grader.verifier_confidence, 'score_reliability': grader.score_reliability})
|
|
28
28
|
|
|
29
29
|
def training_signals(hill: HillclimbResult, traces: list[str], graders: list[str], verifiers: list[str]) -> list[TrainingSignal]:
|
|
30
|
-
return [TrainingSignal(signal_id=f'sig_{hill.task_id}_rollout', task_id=hill.task_id, signal_type='rollout', source_attempt_ids=[hill.baseline_attempt_id,hill.revised_attempt_id], baseline_score=hill.baseline_score, revised_score=hill.revised_score, score_delta=hill.revision_score_delta, criteria_improved=hill.criteria_improved, criteria_regressed=hill.criteria_regressed, failed_criteria_before=hill.failed_criteria_before, failed_criteria_after=hill.failed_criteria_after, feedback_ref='feedback/baseline_evaluator_feedback.json', revision_plan_ref='feedback/revision_plan.json', grader_result_refs=graders, verifier_result_refs=verifiers, trace_refs=traces, artifact_refs=[], score_source='
|
|
30
|
+
return [TrainingSignal(signal_id=f'sig_{hill.task_id}_rollout', task_id=hill.task_id, signal_type='rollout', source_attempt_ids=[hill.baseline_attempt_id,hill.revised_attempt_id], baseline_score=hill.baseline_score, revised_score=hill.revised_score, score_delta=hill.revision_score_delta, criteria_improved=hill.criteria_improved, criteria_regressed=hill.criteria_regressed, failed_criteria_before=hill.failed_criteria_before, failed_criteria_after=hill.failed_criteria_after, feedback_ref='feedback/baseline_evaluator_feedback.json', revision_plan_ref='feedback/revision_plan.json', grader_result_refs=graders, verifier_result_refs=verifiers, trace_refs=traces, artifact_refs=[], score_source='structural_guardrail', grader_kind='structural_guardrail', confidence=0.6, training_use_cases=['rollout','reward_modeling','revision_preference'], limitations=['Structural guardrails are package-integrity evidence only; Mentor/verifier/evaluator records are needed for semantic outcome labels.'], metadata_json={'structural_guardrail': True})]
|