diff --git a/harness/_llm_worker.py b/harness/_llm_worker.py index a7f3949..de94a5d 100644 --- a/harness/_llm_worker.py +++ b/harness/_llm_worker.py @@ -447,7 +447,8 @@ def main(): memory=False, skills=False, ) - agent = _wire_trace_hooks(agent, es, trace_id, model=payload.get("model")) + agent = _wire_trace_hooks(agent, es, trace_id, model=payload.get("model"), + level=level) agent = _wire_budget_hooks(agent, level, trace_id, model=payload.get("model")) # хард-кап шагов (e-260930031800): cancel_tool + end_turn. # ВАЖНО: НЕ используем limits={"turns": N} — нативный стоп SDK diff --git a/harness/runtime.py b/harness/runtime.py index f0712e0..1af9b39 100644 --- a/harness/runtime.py +++ b/harness/runtime.py @@ -77,12 +77,20 @@ def setup_provider(): os.environ["OPENAI_BASE_URL"] = _router_base_url() -def _wire_trace_hooks(agent, es, trace_id: str, model: str = None): +def _wire_trace_hooks(agent, es, trace_id: str, model: str = None, level: str = None): """Каждая стадия агентного цикла → событие агрегата trace. model (e-260930062120): model_id уровня в InvocationStarted — видно, КАКАЯ модель запускалась на тике (запрошенная; "auto" резолвится роутером на его стороне). + + t-261015083454 (аудит мета-информации в событиях): В КАЖДОМ + trace-событии теперь trace_id + ts + level (ToolCalled/ToolResult + раньше несли только tool/input — 68k событий без связи с тиком), + ToolResult дополнительно duration + ok/exception (из хука strands), + имя инструмента в ToolResult — из tool_use, а не заглушка "?". + Уровень недоступен (legacy-вызов) — поле не пишем, aggregate_id + по-прежнему "-:". """ from strands import Agent from strands.hooks import BeforeInvocationEvent, BeforeToolCallEvent, AfterToolCallEvent @@ -93,25 +101,42 @@ def _wire_trace_hooks(agent, es, trace_id: str, model: str = None): seq["n"] += 1 es.append_event("trace", f"{trace_id}:{seq['n']}", kind, payload) + def _meta(extra: dict = None) -> dict: + base = {"trace_id": trace_id, "ts": _now()} + if level: + base["level"] = level + if extra: + base.update(extra) + return base + def on_before_invocation(event): - data = {"trace_id": trace_id, "ts": _now()} + data = _meta() if model: data["model"] = model _trace("InvocationStarted", data) def on_before_tool(event): tool = getattr(event, "tool_use", None) or {} - _trace("ToolCalled", { + _trace("ToolCalled", _meta({ "tool": tool.get("name", "?"), "input": json.dumps(tool.get("input", {}), ensure_ascii=False, default=str)[:4000], - }) + })) def on_after_tool(event): + tool = getattr(event, "tool_use", None) or {} try: res = str(getattr(event, "result", ""))[:4000] except Exception: res = "" - _trace("ToolResult", {"tool": "?", "result": res}) + extra = {"tool": tool.get("name", "?"), "result": res} + duration = getattr(event, "duration", None) + if duration is not None: + extra["duration_ms"] = round(float(duration) * 1000) + exc = getattr(event, "exception", None) + extra["ok"] = exc is None + if exc is not None: + extra["exception"] = str(exc)[:500] + _trace("ToolResult", _meta(extra)) agent.add_hook(on_before_invocation, BeforeInvocationEvent) agent.add_hook(on_before_tool, BeforeToolCallEvent) diff --git a/harness/scheduler.py b/harness/scheduler.py index 3be5e32..81da892 100644 --- a/harness/scheduler.py +++ b/harness/scheduler.py @@ -624,6 +624,8 @@ def run_level_tick(level_cfg, seq: int, model: dict): if not _task_still_active(tid): continue es.append_event("task", tid, "TaskFailed", { + # t-261015083454: мета — уровень/тик-воркер и его трасса + "level": level_cfg.name, "trace_id": trace_id, "error": str(usage.get("error", "llm worker failed"))[:500], }) raise RuntimeError(str(usage.get("error", "llm worker failed"))) @@ -676,6 +678,8 @@ def run_level_tick(level_cfg, seq: int, model: dict): _log("skip батч-догзакрытие задач, решённых воркером: " + ", ".join(_skipped)) for tid in _resolve: es.append_event("task", tid, "TaskCompleted", { + # t-261015083454: мета-поля — какой тик/уровень закрыл задачу + "level": level_cfg.name, "trace_id": trace_id, "result_ref": trace_id, "answer": (text or "")[:2000], }) try: