When to Use Different Emitter Types in Agent-Lightning: A Complete Guide to Observability

Agent-lightning provides six specialized emitter types—emit_reward, emit_message, emit_object, emit_exception, emit_annotation, and operation—each designed to capture specific telemetry data as OpenTelemetry spans with distinct semantic conventions for RL training, debugging, and workflow tracing.

Agent-lightning ships a lightweight emitter API that lets you record custom OpenTelemetry spans directly from your agent code. Understanding when to use different emitter types in agent-lightning ensures that reinforcement learning signals, debug logs, and exception traces follow consistent semantic conventions that downstream dashboards and reward-model trainers can reliably consume. This guide maps each emitter to its intended use case based on the actual implementation in the Microsoft agent-lightning repository.

Choosing the Right Emitter for Your Use Case

Each emitter helper in agent-lightning wraps the active tracer to produce a span with a well-known semantic convention. Select the emitter that matches the data you need to capture.

emit_reward: Recording Scalar and Multi-Dimensional Rewards

Use emit_reward when you need to report reinforcement-learning signals or any scalar feedback that a training loop will consume. According to the source code in agentlightning/emitter/reward.py#L148, this emitter creates a span of type AGL_ANNOTATION and stores either a numeric reward or a dictionary of multi-dimensional rewards.

  • Typical use: Intermediate or final rewards during tool execution or episode completion.
  • Key feature: Automatically links to the surrounding operation span, if any.
  • Data format: Float or dict with optional primary_key for multi-objective rewards.

emit_message: Debug Logging and Status Updates

Use emit_message for high-level status messages that you want to keep in the same telemetry store as your spans. This emitter, implemented in agentlightning/emitter/message.py#L15, creates a span of type AGL_MESSAGE containing a free-form string plus optional attributes.

  • Typical use: Debugging tool invocations, logging step transitions, or communicating agent state.
  • Key feature: Lightweight string-based logging integrated with the OpenTelemetry pipeline.

emit_object: Serializing Arbitrary Python Objects

Use emit_object when you need to capture model outputs, configurations, or any data structure useful for post-hoc inspection. As defined in agentlightning/emitter/object.py#L17, this emitter creates a span of type AGL_OBJECT that serializes the object via repr().

  • Typical use: Storing structured model outputs, API responses, or agent configurations.
  • Key feature: Automatically flattens nested dictionaries into dotted OpenTelemetry keys using sanitize_attributes.

emit_exception: Capturing Non-Fatal Errors

Use emit_exception to report unexpected errors that you do not want to raise to the caller. The implementation in agentlightning/emitter/exception.py#L15 creates a span of type AGL_EXCEPTION marked with exception.type, exception.message, and exception.stacktrace.

  • Typical use: Non-fatal failures in tool calls, fallback handling, or graceful degradation paths.
  • Key feature: Records full traceback information without interrupting execution flow.

emit_annotation: Generic Custom Metadata

Use emit_annotation when you need a custom data payload that does not fit the reward or message model. This generic emitter, located in agentlightning/emitter/annotation.py#L35, produces an AGL_ANNOTATION span storing a free-form dictionary of key-value pairs.

  • Typical use: Tagging spans with experiment metadata, model versions, or custom metrics.
  • Key feature: Most flexible payload structure for domain-specific attributes.

Use the operation decorator or context manager to group a series of emitter calls, inputs, and outputs under a single parent span. Implemented in agentlightning/emitter/annotation.py#L281, this creates an AGL_OPERATION span that lives for the duration of a logical block.

  • Typical use: Wrapping a full tool invocation, multi-step workflow, or high-level agent step.
  • Key features: Supports set_input(), set_output(), and automatic attribute flattening via flatten_attributes.

Architecture: How Emitters Flow Through the Tracer

Understanding the internal flow helps you decide when to set propagate=False or add custom attributes.

  1. Tracer Resolution – All emitter functions call agentlightning.tracer.base.get_active_tracer() to retrieve the currently bound tracer (usually an OtelTracer). If propagate=False is passed, a DummyTracer creates a local span that never reaches exporters.
  2. Semantic Conventions – Each emitter uses constants from agentlightning.semconv (e.g., AGL_MESSAGE, AGL_EXCEPTION) ensuring downstream consumers can query spans by standardized names.
  3. Attribute Processing – Helper utilities flatten_attributes and sanitize_attributes in agentlightning.utils.otel convert nested dictionaries into dotted OpenTelemetry keys (e.g., meta.some_key), allowing rich metadata without manual preprocessing.

Code Examples

Emitting Scalar and Multi-Dimensional Rewards

from agentlightning import emit_reward

# Simple scalar reward

emit_reward(0.85)

# Multi-dimensional reward with a primary key

emit_reward({"task_success": 1.0, "efficiency": 0.7}, primary_key="task_success")

Linking Rewards to Previous Spans

from agentlightning import emit_reward
from agentlightning.utils.otel import make_link_attributes, make_tag_attributes

# Link the reward to a previously emitted response span

link_attrs = make_link_attributes({"gen_ai.response.id": "resp-123"})
emit_reward(0.5, attributes=link_attrs)

# Tag the reward span with custom tags

tag_attrs = make_tag_attributes(["fast", "reliable"])
emit_reward(0.7, attributes=tag_attrs)

Logging Debug Messages

from agentlightning import emit_message

emit_message("Starting tool call", attributes={"tool": "search"})

Capturing Arbitrary Objects

from agentlightning import emit_object

model_output = {"answer": "42", "confidence": 0.96}
emit_object(model_output, attributes={"step": "postprocess"})

Reporting Exceptions Without Raising

from agentlightning import emit_exception

try:
    risky_operation()
except Exception as exc:
    emit_exception(exc)  # Records stacktrace in a span

    # continue execution or fallback

Grouping Work with the Operation Decorator

from agentlightning import operation, emit_reward, emit_message

@operation(category="tool", tool_name="search")
def run_search(query: str):
    emit_message(f"Executing search for '{query}'")
    results = external_search_api(query)
    emit_reward(0.9, attributes={"result_count": len(results)})
    return results

Using Operation as a Context Manager

from agentlightning import operation

with operation(user_id=123) as op:
    op.set_input(data="input payload")
    # ... work ...

    op.set_output(result="output payload")

Summary

  • Use emit_reward for numerical RL feedback and multi-dimensional reward signals.
  • Use emit_message for lightweight string-based debug logging and status updates.
  • Use emit_object to serialize complex Python structures like model outputs or configs.
  • Use emit_exception to capture non-fatal errors with full stacktraces without raising.
  • Use emit_annotation for custom key-value metadata that does not fit other categories.
  • Use operation to group related emitter calls into a single parent span with inputs and outputs.

Frequently Asked Questions

When should I use emit_reward versus emit_annotation?

Use emit_reward when the data represents a scalar or multi-dimensional reinforcement learning signal that a training loop will consume, as it enforces numeric or dictionary structures with optional primary_key semantics. Use emit_annotation for generic experiment metadata or custom metrics that do not represent reward signals, since it accepts any free-form dictionary without semantic constraints.

How do I prevent spans from reaching external exporters during unit tests?

Pass propagate=False to any emitter function. This triggers the use of a DummyTracer that creates local spans only, preventing telemetry from hitting OTLP endpoints or console exporters while still allowing you to verify span creation in memory.

Yes. Import make_link_attributes from agentlightning.utils.otel and pass the resulting attributes to any emitter. This creates explicit span links using OpenTelemetry's linking mechanism, allowing you to correlate rewards or messages with prior generations or tool calls.

What is the difference between emit_object and emit_message?

emit_message stores a string payload in an AGL_MESSAGE span and is optimized for human-readable debug text. emit_object stores the repr() of an arbitrary Python object in an AGL_OBJECT span, making it suitable for capturing structured data like dictionaries or class instances that require post-hoc analysis.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →