OnTrack: Real-Time Monitoring and Intervention in LLM Agent Trajectories via Streaming Structure-Aware Optimal Transport

Reference implementation for the paper:

OnTrack: Real-Time Monitoring and Intervention in LLM Agent Trajectories via Streaming Structure-Aware Optimal Transport Babak Barazandeh, Connor Swanson, Chinmay Kulkarni, Nikhil Mungel Cribl AI Research Lab arXiv:2610.12375

Overview

OnTrack is a streaming monitoring mechanism that compares an LLM agent's execution graph against reference solutions using optimal transport, in real time. It flags loops, stalls, hallucinations, causal inversions, and off-track behavior as the agent runs, with sub-millisecond per-event latency.

The monitor operates in three information regimes:

  • References + tool schemas: full structural alignment, pre-execution gate, and per-step diagnostics
  • Schemas only: loop/stall detection plus prerequisite checks (no plan deviation)
  • Bare stream: loop and stall detection via L1 vital signs

Files

File Description
ontrack.py Core OnTrack algorithm — pure NumPy, no external OT dependency
test_ontrack.py Controlled fault-injection test suite (8 scenarios, paper Section 6.1 and Table 2)
eval_swebench.py Main SWE-bench evaluation on sailplane/swe-agent-trajs (Sections 6.2--6.6)
eval_presubmit.py Pre-submission experiments: rival abort policies (Table 6), multi-seed checkpoint AUROC (Table 5), checkpoint ablation
eval_reviewer.py Reviewer-requested ablations: component attribution (Table 4), length-controlled analysis, multi-seed robustness
eval_review3.py L3 gate tests (Table 3), corrected AUPRC computation, PR curves

Installation

pip install -r requirements.txt

The core algorithm (ontrack.py) only requires NumPy. The evaluation scripts additionally need sentence-transformers, datasets, scikit-learn, scipy, and optionally matplotlib for figures.

Quick start

Run the controlled test suite (no downloads needed)

python test_ontrack.py

This runs all 8 fault-injection scenarios (Table 2) with synthetic embeddings and prints PASS/FAIL for each.

Run the SWE-bench evaluation

python eval_swebench.py

This downloads sailplane/swe-agent-trajs (2,294 trajectories) from HuggingFace, embeds steps with all-MiniLM-L6-v2, and runs the full experiment suite:

  • EXP 1: Resolved vs. unresolved discrimination (AUROC)
  • EXP 1b: Length-controlled variant
  • EXP A: Early warning and cost savings (abort threshold sweep)
  • EXP B: L1-only exit-status detection
  • EXP E: In-distribution splice injection
  • EXP 2: Fault injection
  • EXP 3: Ablation

Results are saved to a timestamped JSON file.

Run additional experiments

python eval_presubmit.py   # Table 5, Table 6, checkpoint ablation
python eval_reviewer.py    # Table 4, multi-seed robustness
python eval_review3.py     # Table 3 (L3 gate tests), AUPRC correction

Using OnTrack in your own project

import numpy as np
from ontrack import Config, Event, Reference, OnTrack

# Build a reference trajectory (known-good execution)
ref = Reference(
    action_embs=np.stack([...]),  # [m, d] action embeddings
    tools=["search", "read", "edit", "test", "commit"],
    parents=[[], [0], [0], [1, 2], [3]],  # DAG structure
    args_embs=np.stack([...]),    # [m, d] argument embeddings (optional)
    effect_embs=np.stack([...]),  # [m, d] observation embeddings (optional)
)

# Create evaluator
evaluator = OnTrack([ref], Config())

# Stream agent events one at a time
for step in agent_stream:
    event = Event(
        step_id=step.id,
        action_emb=embed(step.action),
        tool=step.tool_name,
        args_emb=embed(step.arguments),
        effect_emb=embed(step.result),
        irreversible=step.is_destructive,
        requires=step.required_artifacts,
    )
    result = evaluator.append_event(event)

    # result["verdict"] in: OK, EXPLORING, WARN, LOOP, STALLED,
    #                        OFF_TRACK, CAUSAL_INVERSION, BLOCK
    if result["verdict"] == "BLOCK":
        prevent_execution(step)
    elif result["verdict"] in ("LOOP", "OFF_TRACK"):
        alert_operator(result)

# At end of run
final = evaluator.finalize()
print(f"Final score: {final['final_score']:.3f}")

Key hyperparameters

Parameter Default Description
alpha 0.45 Action embedding weight in node cost
beta 0.20 Argument embedding weight
delta 0.35 Tool substitution cost weight
theta 0.35 Structure (GW) vs. attribute trade-off
lam 0.2 KL marginal relaxation (calibrate per encoder)
kappa 0.30 Coverage threshold for frontier mask
h_age 3 Structural age weight ramp
theta_leak 0.60 Leakage threshold for EXPLORING verdict
grace_ttl 3 Grace period (events) for unresolved nodes

lam is the main parameter to calibrate: it controls how aggressively the solver destroys mass for unmatched steps. The paper uses lam=0.1 on synthetic embeddings and lam=0.3 on SWE-bench with all-MiniLM-L6-v2.

Citation

@article{barazandeh2026ontrack,
  title={OnTrack: Real-Time Monitoring and Intervention in {LLM} Agent Trajectories via Streaming Structure-Aware Optimal Transport},
  author={Barazandeh, Babak and Swanson, Connor and Kulkarni, Chinmay and Mungel, Nikhil},
  journal={arXiv preprint arXiv:2610.12375},
  year={2026}
}

License

Apache 2.0

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Paper for cribl-ai/ONTRACK

Article mentioning cribl-ai/ONTRACK