feat(telemetry): אירועי-סוכן מובנים בשער-הפלטפורמה — נראוּת לולאות-recovery (#219)
מוסיף web/agent_telemetry.py — emitter אגנוסטי-לפלטפורמה שפולט אירוע JSON
מובנה לכל פעולת-פלטפורמה משנת-מצב (wakeup/comment/interaction-reap/cancel/
run-cancel/session-reset). מטרה: להפוך דיבוג לולאות-recovery מפורנזיקה-ידנית
מול Paperclip DB (agent_wakeup_requests/heartbeat_runs) לנראוּת בזמן-אמת.
- הפליטה מחווטת ב-web/agent_platform_port.py בלבד (התפר היחיד) — כל pc_* משנה-מצב
נעטף ב-instrument(); השמות הציבוריים והחתימות נשמרים, app.py לא משתנה.
- אגנוסטי-לפלטפורמה (G12): agent_telemetry לא מייבא Paperclip; אירועים בשמות-דומיין
שורדים החלפת-פלטפורמה. מסלול-פליטה יחיד (G2).
- OTLP-ready ("logs first"): schema יציב + _SINK מבודד להחלפה עתידית בלי שינוי call-site.
- error מ-op נפלט כאירוע ואז נזרק-מחדש — אין בליעה שקטה (§6).
- store-UTC (feedback_israel_time). 6 בדיקות pytest (web/tests/test_agent_telemetry.py).
Invariants: מקיים G2 (מסלול-טלמטריה יחיד), G12/INV-PORT1 (פליטה מהשער, ליבה נקייה),
כלל-הנדסה §6 (אין בליעה שקטה). מזין את #222 (טקסונומיית-בריאות).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -33,12 +33,11 @@ from web.paperclip_api import (
|
||||
pc_request,
|
||||
require_paperclip_db_url,
|
||||
)
|
||||
from web.agent_telemetry import instrument
|
||||
from web.paperclip_client import (
|
||||
COMPANIES as PAPERCLIP_COMPANIES,
|
||||
accept_interaction as pc_accept_interaction,
|
||||
archive_project as pc_archive_project,
|
||||
cancel_interaction as pc_cancel_interaction,
|
||||
cancel_run as pc_cancel_run,
|
||||
create_project as pc_create_project,
|
||||
create_workflow_issue as pc_create_workflow_issue,
|
||||
get_agents_for_case as pc_get_agents_for_case,
|
||||
@@ -50,25 +49,75 @@ from web.paperclip_client import (
|
||||
get_run_events as pc_get_run_events,
|
||||
get_run_log as pc_get_run_log,
|
||||
list_live_runs as pc_list_live_runs,
|
||||
post_comment as pc_post_comment,
|
||||
reap_stale_interactions as pc_reap_stale_interactions,
|
||||
reject_interaction as pc_reject_interaction,
|
||||
reset_agent_session as pc_reset_agent_session,
|
||||
reset_case_agents as pc_reset_case_agents,
|
||||
respond_to_interaction as pc_respond_to_interaction,
|
||||
restore_project as pc_restore_project,
|
||||
update_project_name as pc_update_project_name,
|
||||
wake_analyst_for_appraiser_facts as pc_wake_analyst_for_appraiser_facts,
|
||||
wake_analyst_for_argument_aggregation as pc_wake_analyst_for_argument_aggregation,
|
||||
wake_analyst_for_protocol_analysis as pc_wake_analyst_for_protocol_analysis,
|
||||
get_generation_run_status as pc_get_generation_run_status,
|
||||
wake_ceo_agent as pc_wake_ceo,
|
||||
wake_ceo_for_action as pc_wake_ceo_for_action,
|
||||
wake_ceo_for_feedback_fold as pc_wake_ceo_for_feedback_fold,
|
||||
wake_curator_for_final as pc_wake_curator_for_final,
|
||||
wake_for_precedent_extraction as pc_wake_for_precedent_extraction,
|
||||
# ── raw imports: wrapped with telemetry below (state-affecting ops, #219) ──
|
||||
cancel_interaction as _cancel_interaction,
|
||||
cancel_run as _cancel_run,
|
||||
post_comment as _post_comment,
|
||||
reap_stale_interactions as _reap_stale_interactions,
|
||||
reset_agent_session as _reset_agent_session,
|
||||
reset_case_agents as _reset_case_agents,
|
||||
wake_analyst_for_appraiser_facts as _wake_analyst_for_appraiser_facts,
|
||||
wake_analyst_for_argument_aggregation as _wake_analyst_for_argument_aggregation,
|
||||
wake_analyst_for_protocol_analysis as _wake_analyst_for_protocol_analysis,
|
||||
wake_ceo_agent as _wake_ceo,
|
||||
wake_ceo_for_action as _wake_ceo_for_action,
|
||||
wake_ceo_for_feedback_fold as _wake_ceo_for_feedback_fold,
|
||||
wake_curator_for_final as _wake_curator_for_final,
|
||||
wake_for_precedent_extraction as _wake_for_precedent_extraction,
|
||||
)
|
||||
|
||||
# ── telemetry-wrapped platform ops (#219 / docs/spec/X15) ───────────────────
|
||||
# Every state-affecting platform op the Port exposes emits one structured event
|
||||
# through web.agent_telemetry, so recovery-loop behaviour (repeated wakeups,
|
||||
# stranded dispositions) is observable in real time rather than reconstructed
|
||||
# from the Paperclip DB. The wrappers preserve the public ``pc_*`` names and
|
||||
# signatures — call sites in app.py are unchanged. Read-only observability ops
|
||||
# (list_live_runs / get_run_log / get_issue_comments …) are intentionally NOT
|
||||
# instrumented: they neither wake agents nor change disposition state.
|
||||
pc_wake_ceo = instrument("agent.wakeup", role="ceo")(_wake_ceo)
|
||||
pc_wake_ceo_for_action = instrument(
|
||||
"agent.wakeup", role="ceo", keys=("case_number", "company_id", "action"),
|
||||
)(_wake_ceo_for_action)
|
||||
pc_wake_ceo_for_feedback_fold = instrument(
|
||||
"agent.wakeup", role="ceo", keys=("feedback_id", "category", "block_id"),
|
||||
)(_wake_ceo_for_feedback_fold)
|
||||
pc_wake_curator_for_final = instrument(
|
||||
"agent.wakeup", role="curator", keys=("case_number", "company_id", "task"),
|
||||
)(_wake_curator_for_final)
|
||||
pc_wake_for_precedent_extraction = instrument(
|
||||
"agent.wakeup", role="ceo", keys=("case_law_id", "citation", "practice_area"),
|
||||
)(_wake_for_precedent_extraction)
|
||||
pc_wake_analyst_for_appraiser_facts = instrument(
|
||||
"agent.wakeup", role="analyst",
|
||||
)(_wake_analyst_for_appraiser_facts)
|
||||
pc_wake_analyst_for_argument_aggregation = instrument(
|
||||
"agent.wakeup", role="analyst",
|
||||
)(_wake_analyst_for_argument_aggregation)
|
||||
pc_wake_analyst_for_protocol_analysis = instrument(
|
||||
"agent.wakeup", role="analyst", keys=("case_number", "company_id", "document_id"),
|
||||
)(_wake_analyst_for_protocol_analysis)
|
||||
pc_post_comment = instrument(
|
||||
"agent.comment", keys=("issue_id", "company_id"),
|
||||
)(_post_comment)
|
||||
pc_cancel_interaction = instrument(
|
||||
"interaction.cancelled", keys=("issue_id", "interaction_id"),
|
||||
)(_cancel_interaction)
|
||||
pc_reap_stale_interactions = instrument(
|
||||
"interaction.reaped", result_keys=("cancelled",),
|
||||
)(_reap_stale_interactions)
|
||||
pc_cancel_run = instrument("run.cancelled", keys=("run_id",))(_cancel_run)
|
||||
pc_reset_agent_session = instrument(
|
||||
"agent.session_reset", keys=("agent_id",),
|
||||
)(_reset_agent_session)
|
||||
pc_reset_case_agents = instrument(
|
||||
"agent.session_reset", keys=("case_number",),
|
||||
)(_reset_case_agents)
|
||||
|
||||
# ── domain-named lifecycle aliases (preferred for new call sites) ───────────
|
||||
archive_case_project = pc_archive_project
|
||||
restore_case_project = pc_restore_project
|
||||
|
||||
Reference in New Issue
Block a user