Evaluating an External Agent
The evaluation harness evaluates an external agent over WebSockets without importing it. Any agent that implements the real-time voice interface (RTVI) wire protocol can run against the shipped domains. This page defines what your bot must implement, what the harness reads back, and how to verify the integration before a full benchmark run.
The reference implementation is evaluation/bot_server.py. Read it alongside this page — everything below
is visible there in about 100 lines.
The Contract
An external agent must satisfy two hard requirements before the harness can initialize or score it.
1. A Pipecat WebSocket server transport. The bridge connects with the websockets client library and
frames everything through ProtobufFrameSerializer, so use a Pipecat WS transport rather than
reimplementing the framing. The shipped builder (build_ws_transport in
nemo_voice_agent/pipecat/services/nemo/builders.py) constructs a
SingleClientWebsocketServerTransport with audio_in_enabled, audio_out_enabled, no session timeout, and
no WAV header. Bind it to the port in WEBSOCKET_PORT — the bridge defaults to ws://localhost:8765 for
the agent and ws://localhost:8766 for the user sim, overridable with --agent-url / --user-url.
2. An RTVIProcessor with six client-message handlers registered. These are the entire control plane
for a scenario.
All six factories live in nemo_voice_agent/pipecat/processors/frameworks/rtvi_actions.py. Each returns a
(wire_name, handler) pair. Install them with one call to register_client_message_handlers. Per-handler
argument and return shapes are documented in
RTVI Control Plane. An unregistered
type produces an error-response, which surfaces in bridge_log.txt as unknown message type.
Reuse the shipped factories when possible. They are pure functions over your pipeline objects and have no dependency on the NeMo services. For pipeline assembly around them, refer to Building Your Own Pipeline.
If you supply a tool_factory instead of get_schema_tool_for_eval, keep the signature
(name, domain=..., rtvi=..., shared_state=..., **tool_args). The bridge sends the scenario’s domain as
tool_domain, and the factory must resolve names in that namespace.
Runtime Behaviors the Harness Assumes
Beyond the six handlers, the bridge relies on several behaviors that the reference server gets from
run_bot_websocket_server in nemo_voice_agent/pipecat/bot_server.py and from the RTVIObserver in
nemo_voice_agent/pipecat/processors/frameworks/rtvi.py.
Scoring reads two pieces of bot-owned state, both keyed off shared_state:
shared_state["actions"]— appended to by each write tool, returned byget_scenario_summary, and used forACTION_MATCHand judge input.shared_state["db"]— mutated in place by write tools and hashed withget_dict_hashforDB_STATE_MATCH, or returned inline when the bridge asks forinclude_dbsoDB_STATE_ASSERTIONpredicates can run.
If you reuse the tool classes under nemo_voice_agent/evaluation/tools/, they provide both values
automatically. If you route
tool calls through your own agent framework, mirror the same bookkeeping or those signals evaluate as not
applicable. Refer to Scoring for the six signals and how they combine.
Running a Scenario Against Your Bot
SERVER_CONFIG_PATH is resolved against the current working directory, so run everything from evaluation/.
Point the bridge elsewhere if your bot is on another host or port:
Then inspect eval_results/eval_<timestamp>/restaurant__pizza_pepperoni/:
Optional: trace_metrics.json Passthrough
Use trace_metrics.json to preserve architecture-specific diagnostics without changing the fixed metrics
schema.
Architecture-specific diagnostics — internal handoff quality in a cascaded agent, router confidence, retry counts — do not belong in the evaluator’s fixed metric set, so the runner offers a passthrough instead. Write a JSON object to either path inside the scenario directory:
The first path that exists is loaded verbatim into metrics.json["trace_metrics"]. The runner does not
validate, interpret, or aggregate the contents, and the file is optional — when absent, the key is simply
missing from metrics.json. The loader is _load_optional_trace_metrics in
nemo_voice_agent/evaluation/runner.py.
Your bot must know where to write. The scenario directory uses the scenario name under the
eval_<timestamp> session directory beneath --output-dir. Pass the directory to your bot out of band, or
write from a post-run script that walks the session directory.
Non-Pipecat Agents
Agents built on other runtimes are outside the supported integration path. Such an agent must reimplement
Pipecat’s WebSocket transport framing and the RTVI message layer before the bridge can communicate with it.
For an unsupported custom integration, treat
pipecat.transports.websocket.server and pipecat.serializers.protobuf as the wire specification, and
RTVI Message Reference as the message-level one.
Related Pages
Use these pages to assemble an agent around the contract, run it, and interpret the resulting evidence.
- Building Your Own Pipeline — assembling a bot around the contract
- RTVI Control Plane — per-handler arguments and return shapes
- RTVI Message Reference — the on-the-wire envelope
- Scoring — the six success signals
- Evaluation Command-Line Interface (CLI) — every flag the runner accepts
- Results — what the runner writes to the scenario directory