Skip to content

feat(autogen-core): add gen_ai.agent.action_ref to trace_tool_span for cross-producer audit correlation #7850

Description

@giskard09

Summary

Propose adding gen_ai.agent.action_ref — a deterministic, recomputable SHA-256 handle — as an optional span attribute in trace_tool_span() inside autogen_core/_telemetry/_genai.py.

Motivation

trace_tool_span() today emits gen_ai.tool.name, gen_ai.tool.call.id, and gen_ai.tool.description. These are producer-scoped: a consumer comparing spans across two independent producers, or auditing a span without trusting the emitting runtime, has no recomputable correlation key.

This gap was raised in #7405 (GuardrailProvider) by multiple contributors, including @rpelevin:

"canonical action_ref: stable identity of the action class and actor, with JCS-normalized consequential input."

The eight attributes proposed in open-telemetry/semantic-conventions-genai#291 address the same producer-scoped gap. A ninth attribute — gen_ai.agent.action_ref — closes the cross-producer case.

Proposed Change

Three additions to autogen_core/_telemetry/_genai.py:

1. Constant:

GEN_AI_AGENT_ACTION_REF = "gen_ai.agent.action_ref"

2. Derivation helper:

def derive_action_ref(
    agent_id: str,
    action_type: str,
    scope: str,
    timestamp_ms: int,
) -> str:
    """Derive action_ref per action-ref-v1.

    Canonical derivation: SHA-256(RFC 8785 JCS({agent_id, action_type, scope, timestamp_ms})).

    For production use the `jcs` package (PyPI) or any RFC 8785-conformant serializer.
    The snippet below is correct for the canonical preimage fields (string/int only —
    no floats, no unicode escapes) and is included for readability:

        import hashlib, json
        preimage = json.dumps(
            {"action_type": action_type, "agent_id": agent_id,
             "scope": scope, "timestamp_ms": timestamp_ms},
            sort_keys=True, separators=(",", ":"),
        )
        return hashlib.sha256(preimage.encode()).hexdigest()

    Byte-identity across runtimes is verified by the conformance suites listed below.
    """

3. Optional parameter on trace_tool_span():

@contextmanager
def trace_tool_span(
    tool_name: str,
    *,
    tracer: Optional[trace.Tracer] = None,
    parent: Optional[Span] = None,
    tool_description: Optional[str] = None,
    tool_call_id: Optional[str] = None,
    action_ref: Optional[str] = None,   # <-- new
) -> Generator[Span, Any, None]:
    ...
    if action_ref is not None:
        span_attributes[GEN_AI_AGENT_ACTION_REF] = action_ref

No new dependencies for the attribute constant and parameter. The derivation helper uses only stdlib (hashlib, json) for the reference implementation; jcs is recommended for production. Zero breaking changes — the parameter is optional.

Properties

  • Recomputable without a service call: any implementation following the derivation produces the same 32-byte hex from the same four fields. A verifier does not need to call any external service — the correlation is independently verifiable from the preimage fields alone.
  • Cross-producer correlation: two independent runtimes emitting spans for the same agent action produce the same action_ref, enabling cross-framework audit without shared state.
  • Additive alongside gen_ai.tool.call.id: call_id is the runtime's session-scoped handle; action_ref is the verifier's recomputable handle. They serve different consumers.

Normative Reference

Derivation spec: draft-giskard-aeoess-action-ref-00

Three independent production implementations with byte-identical conformance suites:

  • examples/conformance/presidio/ — vstantch/presidio-hardened-x402 (arXiv 2604.11430) → argentum-core
  • examples/conformance/aps/ — AEOESS/agent-passport-system (co-author, I-D) → argentum-core
  • conformance/agentgraph/ — kenneives/AgentGraph (CTEF v0.4 normative) → draft-giskard-aeoess-action-ref

Relation to #7405

GuardrailProvider is the policy enforcement layer. action_ref is the correlation key that binds the pre-execution guardrail decision to the post-execution span — without either layer trusting the other. They compose; neither depends on the other being merged first.

Happy to open a PR directly if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions