Quickstart

From install to your first trace

Install the SDK, connect a project, wrap your agent. Automatic instrumentation handles the rest, capturing nested calls, arguments, results, and timing into a single trace that streams to the dashboard while the run is still in flight. The whole path is below, including the parts worth getting right on day one.

Languages
Python 3.9+, Node 18+
Time to first trace
Minutes, not sprints
Prerequisites
A project key

1. Install the SDK

NOMOO ships a Python client and a TypeScript client. They produce the same trace format, so a mixed-language agent network reads as one system rather than two.

pip install nomoo

Both packages are dependency-light on purpose. Capture runs in-process, and there is no collector, queue, or database for you to stand up before the first trace.

2. Connect a project

A project is the scope that everything else hangs from: traces, scores, redaction rules, and retention. Call init() once, as early in your process as you can, before the first agent run.

import nomoo

nomoo.init(
    project="support-triage",
    environment="production",
    release="v1.7.2",
)
The project key is read from NOMOO_API_KEY in the environment. Pass it explicitly only if your deployment makes that impossible.

Set the release, even on day one

The release value is what lets regression detection attribute a change in quality to the deploy that caused it. Without it, a drop in score is a mystery; with it, the alert names a version. Wire it to whatever your pipeline already knows, usually a commit SHA or a tag.

Keep environments separate

Use distinct environment values for production, staging, and local development. Scores and baselines are computed per environment, so mixing them produces a baseline that describes nothing in particular.

3. Instrument your agent

Pass your existing agent through instrument(). Your orchestration, prompts, and control flow stay exactly where they are. Automatic instrumentation follows the agent down into the planner it calls, the tools that planner selects, the HTTP or database work inside those tools, and the model call that composes the answer.

import nomoo
from my_agents import support_agent

agent = nomoo.instrument(support_agent)

result = agent.run("Where is my refund?")
# every nested call, argument and result is now traced
That is the integration. Open the dashboard and the run appears as it executes.

Open the project in NOMOO and you will see the run arrive as a tree: the root call at the top, each nested step beneath its parent, with duration and status on every line. Select a span to read the arguments it received and the value it returned.

What you should see first

  • A root span named for your agent entry point, with the total duration of the run.
  • Nested model and tool spans at the right depth, tagged by kind.
  • Arguments and return values on each span, structured rather than stringified.
  • A quality score on the trace once the automated review has read it.

4. Add custom spans

Automatic instrumentation covers standard agent, model, tool, and retrieval calls. It does not know which of your own functions matter to your domain. Mark those with a decorator and they join the hierarchy in the right place, with their arguments and return values captured.

import nomoo

@nomoo.span(kind="tool")
def lookup_order(order_id: str) -> dict:
    return orders.get(order_id)

@nomoo.span(kind="retrieval", name="kb.search")
def search_kb(query: str, top_k: int = 5) -> list:
    return index.query(query, top_k=top_k)

# or wrap a block instead of a function
with nomoo.span("policy.check", kind="fn") as s:
    s.set(rule="refund_window")
    allowed = policy.evaluate(order)

Choosing a kind

The kind argument is not decoration. It decides which review rules apply to the span and which charts it appears in, so it is worth setting accurately.

KindUse for
llmAny call to a model, including planning and composition steps
toolFunctions the agent chooses to invoke, especially ones with side effects
retrievalLookups against a vector store or search index, so grounding checks apply
fnPlain application logic on the path of a run

Do not instrument everything

A span per helper function produces a trace nobody reads. Mark the steps a reviewer would ask about: decisions, side effects, and anything whose result the agent acts on. The automatic capture already covers the rest.

5. Group runs into sessions

One trace covers one run. A session covers a conversation, which may be several runs in sequence. Grouping them is what lets you review the handoff between turns, which is where multi-turn agents most often lose the thread.

with nomoo.session(id=conversation.id, user=conversation.account_id):
    for message in conversation.messages:
        agent.run(message.text)
# all runs above share one session in the dashboard
Use an ID your own system owns, such as a conversation or ticket identifier, so a support report maps straight to a session.

6. Attach metadata

Metadata is how you cut the data along the lines your business cares about. Anything you attach travels with every span in the run and becomes available as a filter and a chart dimension.

# set once for the process
nomoo.init(project="support-triage", metadata={"region": "us-west"})

# or per run, where the values are request-specific
with nomoo.context(tenant=account.id, locale="en-US", plan="enterprise"):
    agent.run(question)

Pattern analysis uses these dimensions too. A failure mode that only affects one locale shows up as exactly that, instead of being averaged into a number that looks acceptable.

7. Configure redaction

This is the one piece of configuration worth doing before your first production trace. Redaction rules are applied inside your process, before a span is buffered, so a redacted value is replaced at the source rather than stored and hidden later.

nomoo.init(
    project="support-triage",
    redact=[
        "email",                      # any field with this name
        "customer.address.*",         # a path and everything under it
        nomoo.pattern(r"\b\d{13,19}\b"),  # card-like digit runs
    ],
    redact_placeholder="[redacted]",
)
Rules apply to arguments, return values, and rendered prompts, at every depth of the hierarchy.

Retention is the companion setting, configured per project in the dashboard. Payload retention controls how long full arguments and results are stored. Aggregate retention controls how long scores and trends live, and it can be longer, so a short payload window does not cost you trend history.

8. Configuration reference

Everything accepted by init(). Defaults are chosen so that a call with only a project name is a reasonable production setup.

init options
OptionDefaultPurpose
projectrequiredThe scope traces, scores, and settings belong to.
environment"development"Separates baselines so staging traffic does not move production scores.
releaseunsetVersion tag used to attribute regressions to a deploy.
api_keyfrom envRead from NOMOO_API_KEY unless passed explicitly.
endpointNOMOO cloudPoint at your own deployment when running self-managed.
metadata{}Process-wide dimensions attached to every span.
redact[]Field names, paths, and patterns replaced before buffering.
sample_rate1.0Share of runs captured in full, for very high volume services.
capture_promptsTrueWhether rendered prompts are stored with model spans.
otel_endpointunsetMirror captured spans to your own OpenTelemetry collector.
flush_interval2.0Seconds between asynchronous span deliveries.
enabledTrueSwitch capture off entirely without removing the calls.

9. Continuous checks

Once runs are scored, the score becomes something a pipeline can enforce. Run an evaluation against a suite of saved cases and fail the build when quality falls below the floor you set, or when it drops by more than you are willing to accept against the deployed release.

.github/workflows/agent-eval.yml
name: agent-eval
on: [pull_request]

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install nomoo
      - run: nomoo eval --suite support-triage --min-score 8.0
        env:
          NOMOO_API_KEY: ${{ secrets.NOMOO_API_KEY }}
      - run: nomoo compare --baseline production --fail-on-drop 0.3
A suite covers the behavior you thought to write down. Regression detection on live traffic covers the rest.
  • --min-score sets an absolute floor, useful as a release gate from the first week.
  • --fail-on-drop compares against a named baseline, which catches slow erosion that a fixed floor would miss.
  • Suites are built from real runs. Promote traces you have already reviewed into the suite rather than inventing test cases.

10. Troubleshooting

No traces are appearing

Check that init() runs before the first agent call and that NOMOO_API_KEY is present in the environment the process actually sees, which in containerized deployments is often not the one you set it in. The SDK logs a single warning when it cannot reach the endpoint rather than raising, so capture failures never take down a run.

Spans are flat with no hierarchy

This happens when agent steps run in separate processes or across a queue boundary. Pass the trace context explicitly at the boundary so the child work attaches to the right parent instead of starting a new root.

Some tool calls are missing

Automatic instrumentation discovers calls the agent makes through its framework. A tool invoked directly by your own code, outside that path, needs a decorator. Adding @nomoo.span(kind="tool") puts it back in the hierarchy.

Arguments are showing as redacted

A redaction rule is broader than intended. Field-name rules match at any depth, so a rule for email also covers customer.contact.email. Use a path when you mean a specific location.

Scores look lower than expected

Open the findings behind the score before changing anything. A low score with grounding findings on a retrieval-heavy agent usually reflects retrieval quality rather than the model, and prompt edits will not move it.