Skip to main content
Seer accepts one event per retrieval with a task (query) and a context array. You can log events two ways:
  1. Direct client: Call client.log(...) where you construct the payload.
  2. Decorator: Wrap your retrieval function to auto-capture inputs/outputs.
New to the context format? See Context & Event Schema for the exact shapes.

Install & Initialize

pip install seer-sdk
export SEER_API_KEY="seer_live_your_key_here"
from seer import SeerClient

client = SeerClient()  # reads SEER_API_KEY from env

Option A: Direct Logging (client.log)

Use this when you already have the query + context assembled.
query = "Who directed Inception and what is their nationality?"
context = [
    {"text": "Christopher Nolan directed Inception.", "id": "p-001", "score": 0.95},
    {"text": "Nolan is British-American.", "id": "p-002", "score": 0.89},
]

client.log(
    task=query,                              # the user query
    context=context,                         # list of passage dicts or strings
    metadata={
        "env": "prod",                       # environment tag
        "feature_flag": "retrieval-v1",      # for A/B testing
    },
    sample_rate=0.1,                         # 10% sampling for monitoring
)
# Events are sent automatically in the background
Notes:
  • context must be either list[str] / string[] or list[dict] / Passage[]. If using dicts/objects, include text. Full shape + examples: Context & Event Schema.
  • metadata is free-form. Include anything you want to filter by later.
  • sample_rate controls what % of events get evaluated (for cost management).

Option B: Decorator / Wrapper

The decorator (Python) or wrapper (TypeScript) eliminates boilerplate by mapping your function’s arguments/return to Seer’s event fields.

Pattern 1: Context from Return Value

For retrieval functions where the return value is the context:
from seer import seer_trace

@seer_trace(
    task_arg="query",           # which argument is the query
    context_from_return=True,   # use return value as context
)
def retrieve(query: str) -> list[dict]:
    # Your retriever returns passage dicts
    return [
        {"text": "Christopher Nolan directed Inception.", "score": 0.95},
        {"text": "Nolan is British-American.", "score": 0.89}
    ]

# Decorator logs: task=query, context=return_value
results = retrieve("Who directed Inception?")
The return value can be list[dict] / Passage[] (must have text key) or list[str] / string[] (auto-converted).

Pattern 2: Context from Input Argument

For processing functions where context is an input argument (not the return):
@seer_trace(
    task_arg="query",           # which argument is the query
    context_arg="passages",     # which argument is the context
)
def generate_answer(query: str, passages: list[dict]) -> str:
    # Function receives context as input, returns something else
    return "Christopher Nolan, a British-American filmmaker"

# Decorator logs: task=query, context=passages
answer = generate_answer("Who directed Inception?", retrieved_passages)

Options

ParameterTypeDescription
task_argstrName of argument containing the query (default: "query")
context_from_returnboolIf True, use the return value as context
context_argstrName of argument containing context
metadatadictStatic metadata to attach to every log
use_otel_traceboolAuto-detect OTEL trace context (default: True)
Use either context_from_return=True / contextFromReturn: true or context_arg / contextArgIndex. They are mutually exclusive.

Adding Metadata

from seer import seer_trace

@seer_trace(
    task_arg="query",
    context_from_return=True,
    metadata={"service": "help-bot"},  # static metadata
)
def retrieve(query: str) -> list[dict]:
    return [{"text": "...", "score": 0.9}]

Choosing Between Client vs Decorator

Use CaseRecommended
Full control at each call siteclient.log()
Cleanly encapsulated retrieval function@seer_trace
Multiple retrieval functions@seer_trace on each
Custom logging pipelineclient.log()

Fire-and-Forget vs Synchronous

By default, the SDK uses fire-and-forget mode. Events are queued and sent asynchronously in the background.
# Fire-and-forget (default) — log() returns immediately
client = SeerClient()  # fire_and_forget=True by default
client.log(task="...", context=[...])  # returns None, queued async
# Events auto-flush on process exit

# Synchronous — log() blocks and returns record_id
client = SeerClient(fire_and_forget=False)
record_id = client.log(task="...", context=[...])  # returns record_id
print(f"Created record: {record_id}")

OpenTelemetry Integration

The SDK automatically captures OTEL trace context when available:
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("retrieval"):
    # trace_id, span_id, parent_span_id, span_name captured automatically
    client.log(task="...", context=[...])
Install with pip install seer-sdk[otel] to enable auto-detection. If you already have opentelemetry-api in your environment, it works automatically.
To disable auto-detection or provide manual IDs:
# Disable OTEL auto-detection
client.log(task="...", context=[...], use_otel_trace=False)

# Provide manual trace IDs
client.log(
    task="...",
    context=[...],
    trace_id="0af7651916cd43dd8448eb211c80319c",
    span_id="b7ad6b7169203331",
)

What Happens After Logging

Once logs arrive, Seer evaluates each retrieval and computes Recall, Precision, F1, and nDCG, all without labeled data. See Metrics for full definitions and worked examples.

Next Steps