Agent Trajectory Interchange Format (ATIF)#

Use the atif section when you want one Agent Trajectory Interchange Format (ATIF) trajectory artifact per top-level agent run.

The plugin-managed ATIF dispatcher watches for direct child scopes with category agent, creates a scope-local exporter for each one, and writes the trajectory when that agent scope ends. Nested agent scopes remain in the parent trajectory.

plugins.toml Example#

version = 1

[[components]]
kind = "observability"
enabled = true

[components.config]
version = 1

[components.config.atif]
enabled = true
agent_name = "Planner"
agent_version = "1.0.0"
model_name = "unknown"
output_directory = "logs"
filename_template = "trajectory-{session_id}.json"

This configuration writes a trajectory file such as logs/trajectory-<scope-uuid>.json for each top-level agent scope.

Fields#

Field

Default

Notes

enabled

false

Must be true to write trajectories.

agent_name

NeMo Flow

Agent metadata written into the trajectory.

agent_version

NeMo Flow crate version

Agent version metadata.

model_name

unknown

Default model metadata when no call-level model is present.

tool_definitions

Omitted

Optional ATIF tool metadata.

extra

Omitted

Optional ATIF agent metadata.

output_directory

Current working directory

Directory containing trajectory files.

filename_template

nemo-flow-atif-{session_id}.json

Must contain {session_id}.

Expected Output#

The exporter translates NeMo Flow lifecycle events into ATIF v1.6 trajectory data. LLM start and end events become model steps, tool events become tool calls and observations, and scope nesting contributes lineage metadata.

The plugin writes each trajectory when the top-level agent scope closes. If the plugin is cleared while an agent is still open, teardown flushes the partial trajectory.

Plugin Configuration#

Use plugin configuration when the application should let NeMo Flow own the ATIF dispatcher lifecycle.

from nemo_flow import plugin
from nemo_flow.observability import AtifConfig, ComponentSpec, ObservabilityConfig

config = plugin.PluginConfig(
    components=[
        ComponentSpec(
            ObservabilityConfig(
                atif=AtifConfig(
                    enabled=True,
                    agent_name="Planner",
                    agent_version="1.0.0",
                    model_name="unknown",
                    output_directory="logs",
                    filename_template="trajectory-{session_id}.json",
                )
            )
        )
    ]
)

report = plugin.validate(config)
if any(diagnostic["level"] == "error" for diagnostic in report["diagnostics"]):
    raise RuntimeError(report["diagnostics"])

await plugin.initialize(config)
try:
    # Run instrumented application work here.
    pass
finally:
    plugin.clear()
const plugin = require("nemo-flow-node/plugin");
const observability = require("nemo-flow-node/observability");

await plugin.initialize({
  version: 1,
  components: [
    observability.ComponentSpec({
      version: 1,
      atif: observability.atifConfig({
        enabled: true,
        agent_name: "Planner",
        agent_version: "1.0.0",
        model_name: "unknown",
        output_directory: "logs",
        filename_template: "trajectory-{session_id}.json",
      }),
    }),
  ],
});

try {
  // Run instrumented application work here.
} finally {
  plugin.clear();
}
use nemo_flow::observability::plugin_component::{
    AtifSectionConfig, ComponentSpec, ObservabilityConfig,
};
use nemo_flow::plugin::{initialize_plugins, validate_plugin_config, PluginConfig};

let component = ComponentSpec::new(ObservabilityConfig {
    atif: Some(AtifSectionConfig {
        enabled: true,
        agent_name: "Planner".into(),
        agent_version: "1.0.0".into(),
        model_name: "unknown".into(),
        output_directory: Some("logs".into()),
        filename_template: "trajectory-{session_id}.json".into(),
        ..AtifSectionConfig::default()
    }),
    ..ObservabilityConfig::default()
});

let config = PluginConfig {
    version: 1,
    components: vec![component.into()],
    policy: Default::default(),
};

let report = validate_plugin_config(&config);
assert!(!report.has_errors());

let active = initialize_plugins(config).await?;

Manual API#

Use the manual AtifExporter API when you need explicit collection boundaries or one exporter object per run.

from nemo_flow import AtifExporter

exporter = AtifExporter("session-1", "agent", "1.0.0", model_name="demo-model")
exporter.register("atif-exporter")

# Run instrumented application work here.

trajectory = exporter.export()
exporter.deregister("atif-exporter")
exporter.clear()
const { AtifExporter } = require("nemo-flow-node");

const exporter = new AtifExporter("session-1", "agent", "1.0.0", "demo-model");
exporter.register("atif-exporter");

try {
  // Run instrumented application work here.

  const trajectoryJson = exporter.exportJson();
  console.log(trajectoryJson);
} finally {
  exporter.deregister("atif-exporter");
  exporter.clear();
}
use nemo_flow::api::subscriber::{deregister_subscriber, register_subscriber};
use nemo_flow::observability::atif::{AtifAgentInfo, AtifExporter};

let exporter = AtifExporter::new(
    "session-1".to_string(),
    AtifAgentInfo {
        name: "agent".to_string(),
        version: "1.0.0".to_string(),
        model_name: Some("demo-model".to_string()),
        tool_definitions: None,
        extra: None,
    },
);
register_subscriber("atif-exporter", exporter.subscriber())?;

// Run instrumented application work here.

let trajectory = exporter.export();
let trajectory_json = serde_json::to_string_pretty(&trajectory)?;
println!("{trajectory_json}");

let _ = deregister_subscriber("atif-exporter")?;
exporter.clear();

Common Validation Failures#

  • filename_template does not contain {session_id}.

  • The output directory is not writable at runtime.

  • Tool definitions or extra metadata are not JSON-compatible.

  • The application never opens a top-level agent scope, so no trajectory file is created.