<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Slava</title>
    <description>The latest articles on DEV Community by Slava (@slavazim).</description>
    <link>https://dev.to/slavazim</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3907976%2Fe37c4f72-db39-4761-8a8c-067998874740.png</url>
      <title>DEV Community: Slava</title>
      <link>https://dev.to/slavazim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/slavazim"/>
    <language>en</language>
    <item>
      <title>Predicting agent failures from trajectory shape: trajectory-pattern retrieval outperforms basic RAG</title>
      <dc:creator>Slava</dc:creator>
      <pubDate>Tue, 30 Jun 2026 14:05:29 +0000</pubDate>
      <link>https://dev.to/slavazim/predicting-agent-failures-from-trajectory-shape-trajectory-pattern-retrieval-outperforms-basic-rag-3n5f</link>
      <guid>https://dev.to/slavazim/predicting-agent-failures-from-trajectory-shape-trajectory-pattern-retrieval-outperforms-basic-rag-3n5f</guid>
      <description>&lt;p&gt;&lt;em&gt;A trajectory-pattern retrieval engine reaches AUC &lt;strong&gt;0.71&lt;/strong&gt; (95% CI [0.61, 0.78]) for per-step failure prediction on held-out coding-agent trajectories - and, notably, eval &amp;gt; tune. A tuned text-embedding (cosine-KNN) baseline over the same data lands at chance: every configuration and aggregation we tried has a confidence interval that includes 0.5.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Growing databases of agent traces that companies collect (or should collect, if they haven't started yet) present an opportunity to analyse or monitor agents, since their efficiency, safety and security shape net economic impact. For practical purposes, different layers occupy different points on the cost (operational or inference), latency and accuracy spectrum. The LLM-as-a-judge approach clearly occupies the expensive, slow, high-accuracy slot. Tools that rely on statistical instruments or deterministic signals, on the other hand, are expected to provide a fast and cheap gateway to LLM-based evaluation.&lt;/p&gt;

&lt;p&gt;This leads us to a question: what tools are currently available to extract signals about an agent's trajectory, given a database of related runs? We tested basic RAG and an alternative retrieval method on agent failure prediction: take a snapshot of a trajectory at some turn, find its N closest neighbouring snapshots (excluding the same trajectory), and use the neighbours' failure rate to make a prediction.&lt;/p&gt;

&lt;p&gt;Failure prediction was chosen with a few points in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is an objective, not opinion-based, characteristic of an agent trajectory, and doesn't require an LLM judge&lt;/li&gt;
&lt;li&gt;it is a global feature, which makes for a hard baseline (local properties are expected to be easier)&lt;/li&gt;
&lt;li&gt;it has practical value for analytics, token efficiency or runtime monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dataset&lt;/strong&gt;&lt;br&gt;
275 trajectories from SWE-rebench-OpenHands-Trajectories, filtered to the tobymao/sqlglot repository (178 failures, 97 successes). At the preprocessing stage, trajectories were filtered so that each one comes from a unique pull request. Any same-PR pair carries near-identical semantic content and introduces data leakage; leaving same-PR samples in the dataset bumps eval AUC up by ~20–30 points.&lt;br&gt;
The split is built by randomly ordering instance-id-sorted trajectories, then proportionally interleaving by status - a 100-trajectory tune slice and a 175-trajectory held-out eval slice, both landing at ~65% failure rate (the population mean). The split is used in hyperparameter selection, not in retrieval: eval queries see the full corpus. Reported confidence intervals are per-trajectory bootstrap, 2000 draws.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metric: per-step stratified AUC from step&amp;nbsp;50&lt;/strong&gt;&lt;br&gt;
The primary metric is per-step stratified weighted ROC AUC from step 50 onwards - the trajectory-step analogue of the well-established time-stratified evaluation used in survival analysis and dynamic prediction (e.g. time-dependent ROC), where each subject is scored at matched times to remove confounding from horizon length.&lt;/p&gt;

&lt;p&gt;The final score is a weighted mean of per-step AUCs. For every step&lt;code&gt;S ≥ 50&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take the trajectories still alive at &lt;code&gt;S&lt;/code&gt; (those whose last snapshot is at step ≥ S); call this count &lt;code&gt;n_S&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Compute a score for each of them on its &lt;code&gt;[0…S]&lt;/code&gt; snapshot slice.&lt;/li&gt;
&lt;li&gt;Compute ROC AUC across that active set - call it &lt;code&gt;AUC_S&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The per-step AUCs are then combined into a single weighted average.&lt;/p&gt;

&lt;p&gt;Step 50 was chosen because in the studied dataset ~95% of trajectories are still alive at step 50. Weighting is done so that the early steps - where most runs are still alive - carry the most weight.&lt;/p&gt;

&lt;p&gt;This was chosen over a single global AUC for a reason: &lt;strong&gt;failed trajectories are systematically longer than successful ones&lt;/strong&gt;. This property seems highly regular in the agentic domain, rather than an anomaly of this particular dataset. A global AUC therefore leaks length into the score: any retriever that relies solely on final trajectory length gets around 0.68 AUC (global), while at step stratified setup should converge to 0.5. Global AUC also has little practical value: the usefulness of post-run classification is unclear, whereas the per-step stratified version emulates real-time monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Participants&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trajectory-pattern retrieval KNN&lt;/li&gt;
&lt;li&gt;Basic RAG (cosine-KNN)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What trajectory-pattern retrieval is:&lt;/strong&gt; use an embedder model to embed each message into 1024 dimensions, cluster all messages as actions and observations, build a compressed vocabulary of typed action-observation tokens (~50–60 tokens), then represent each run as a sequence of these tokens. More specifics are described below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What basic RAG is precisely:&lt;/strong&gt; each prefix is reduced to one vector (swept over 1024 or 2048 dimensions) by averaging its last N messages (N swept across the interval &lt;code&gt;{1…20}&lt;/code&gt;, plus an all-messages special case); retrieval is top-k (k over &lt;code&gt;{1…50}&lt;/code&gt;) nearest neighbours from distinct trajectories by cosine similarity; the score is the failure rate of the retrieved set. It scores exactly the way a RAG-style failure monitor would.&lt;br&gt;
For both participants, the Qwen3 8B embedder mode is used. Trajectory-pattern retrieval skips the initial task message, while for basic RAG both options were tested: including or excluding the initial message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aggregations&lt;/strong&gt;&lt;br&gt;
At each step we summarise a trajectory's accumulated fail-similarity into a single score, and we report the cumulative mean (&lt;em&gt;cummean&lt;/em&gt;) over the 175 eval trajectories, since &lt;em&gt;cummean&lt;/em&gt; proved the most noise-robust and therefore the most useful given the dataset limitations.&lt;br&gt;
A valid objection is that basic RAG might fare better under an aggregation other than our headline cummean, so we additionally tune and evaluate it under running-max (&lt;em&gt;cummax&lt;/em&gt;) and peak-running-mean (&lt;em&gt;cummeanmax&lt;/em&gt;). For each aggregation we run the full query/retrieval hyperparameter search independently and keep the configuration that maximises that aggregation's tune-set AUC, then score it on the held-out eval set - so each aggregation is reported under its own best-tuned configuration. Best aggregation for basic RAG turned out to be a cummax, while other two show inverted AUC, so we will report cummax for basic RAG further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faaqf59vv1om2bd5gvc2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faaqf59vv1om2bd5gvc2u.png" alt="Trajectory-pattern retrieval vs Basic RAG" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkg316rleiiv4mtyag7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkg316rleiiv4mtyag7s.png" alt="Per-step unweighted AUC" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every basic RAG interval includes 0.5. On this corpus, text-embedding cosine retrieval has no usable signal for predicting the outcome - even when allowed to pick its best aggregation. The trajectory-pattern retrieval cummean interval has a lower bound of 0.621, clearly above chance, and the fact that eval and tune are close (with eval slightly higher) shows no sign of an overfit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How trajectory-pattern retrieval works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The representation is built bottom-up:&lt;br&gt;
Embed and cluster every observation and action message in the corpus. Each message ends up labelled by its cluster (observations get classes &lt;code&gt;o1, o2,&amp;nbsp;…&lt;/code&gt;; actions get &lt;code&gt;a1, a2,&amp;nbsp;…&lt;/code&gt;). The initial task prompt is dropped - it can't be cleanly typed, and keeping it degrades downstream metrics.&lt;br&gt;
At each agent step, form the pair (action class, observation class).&lt;br&gt;
Cluster those pairs - concatenating each pair's centroid embeddings - into a compact &lt;code&gt;act_obs&lt;/code&gt; token vocabulary (~50–60 tokens in our setup).&lt;br&gt;
A trajectory becomes the resulting sequence of &lt;code&gt;act_obs&lt;/code&gt; tokens, one per step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;br&gt;
Three-stage retrieval is used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;anchors placed at evenly spaced centre steps along the path → a per-window MinHash LSH band prefilter (a cheap candidate shortlist)&lt;/li&gt;
&lt;li&gt;a dense Jaccard rerank over each candidate's neighbourhood → a cross-anchor aggregate&lt;/li&gt;
&lt;li&gt;an aggregative shift-aware Levenshtein rerank: it slides across a trajectory with a window W, finds the best-matching alignment position in the candidate trajectory within W/2, applies a shift penalty and produces a score for each element (point); these scores are then averaged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we note that this rerank technique was not an obvious choice, and more basic sequence-similarity algorithms were tested, such as whole-sequence Levenshtein, whole-sequence Levenshtein with a substitution matrix, Smith-Waterman, Needleman-Wunsch, DTW (Dynamic Time Warping) and some others. All of them performed near the basic-RAG level, which suggests a valuable interpretation of agent-trace structure that we may cover later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related code and benchmark are open-sourced&lt;/strong&gt;&lt;br&gt;
The codebase to run this benchmark was open-sourced as a Python package named &lt;a href="https://github.com/slavaZim/episodiq" rel="noopener noreferrer"&gt;Episodiq&lt;/a&gt;. Numbers should reproduce; if they don't, that's a bug we want to know about. &lt;br&gt;
The same representation supports several practical features, which were also implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Human-readable logs at &lt;strong&gt;~98% lower annotation cost&lt;/strong&gt;, LLM-free at runtime. Annotate one representative per cluster and reuse it for every trajectory; new messages are tagged at write time by KNN against their slice - instant labels, zero LLM calls in the hot path. The gap widens with corpus size: at 1,000 trajectories it reaches 99% (~165×), because annotation cost scales with the vocabulary, not the corpus.&lt;/li&gt;
&lt;li&gt;A per-snapshot fail-similarity score, as benchmarked above.&lt;/li&gt;
&lt;li&gt;Loop detection - surfaces an agent stuck repeating a type of step, useful for token-waste monitoring even when the run isn't broken.&lt;/li&gt;
&lt;li&gt;An action-predictability signal - flags steps where the next action is highly constrained versus steps where the agent could plausibly branch many ways.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Results are preliminary - a single repository, 175 held-out trajectories, wide bootstrap intervals. The gap should be confirmed on larger corpora, though assembling a large enough public trajectory dataset is itself an open problem.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>llm</category>
      <category>agents</category>
      <category>llmops</category>
    </item>
    <item>
      <title>Structural retrieval shows promise over basic RAG for agent failure prediction on trajectory snapshots</title>
      <dc:creator>Slava</dc:creator>
      <pubDate>Tue, 02 Jun 2026 14:43:21 +0000</pubDate>
      <link>https://dev.to/slavazim/structural-retrieval-shows-promise-over-basic-rag-for-agent-failure-prediction-on-trajectory-2alf</link>
      <guid>https://dev.to/slavazim/structural-retrieval-shows-promise-over-basic-rag-for-agent-failure-prediction-on-trajectory-2alf</guid>
      <description>&lt;p&gt;&lt;em&gt;A structural retrieval engine with KNN voting shows a promising signal over basic RAG: AUC = 0.705 averaged over snapshots from step 60 onward on held-out trajectories, while basic RAG plateaus around 0.60 AUC at any cosine threshold from 9% to 100% coverage.&lt;br&gt;
Results are preliminary - single-repo and the distinct-PR condition (covered in benchmark design) limit the eval set to 220 trajectories, so 95% bootstrap confidence intervals overlap and the gap should be confirmed on larger datasets - though finding a sufficient public dataset is itself an open problem.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As the number of agents and trajectory databases grow, a new retrieval need emerges: finding trajectories that are structurally similar, rather than semantically similar by text content. The structure of a trajectory - the sequence of decisions and outcomes, independent of semantics - carries signals useful for monitoring, filtering for experience / reflection pipelines, evaluation, and other layers of agent infrastructure.&lt;br&gt;
Our experiment shows that basic RAG loses this structural signal because it is designed for documents, not action sequences. We propose an approach where a trajectory is represented as a sequence of typed elements, and have implemented it in an open-source Python package, episodiq, described at the end of this article.&lt;br&gt;
The rest of this piece covers the methodology and the metric choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why predict a trajectory's outcome?
&lt;/h2&gt;

&lt;p&gt;Among the questions one can ask about a trajectory, will it succeed or fail stands out because it can be checked against reality. Most of what trajectory analysis surfaces - which step was productive, where the agent lost the thread, which behavioural pattern is forming - is interpretation: useful, but with no external ground truth. Whether a run will succeed or fail is different: outcome is an objective property of the trajectory.&lt;br&gt;
This gave us two options for grading retrieval quality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Annotate trajectories on subjective, local features via an LLM judge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the binary success/failure label as an objective global property.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A trajectory snapshot is its state at some turn. Our hypothesis: if a retrieval engine captures snapshot structure, then snapshots of successful trajectories should be more similar to snapshots of other successful ones, and vice versa, once some reasonable turn T is reached. This is the harder framing, because success or failure is a complex event - it cannot be reduced to local rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What we&amp;nbsp;measure&lt;/strong&gt;&lt;br&gt;
The primary metric is per-step stratified ROC AUC from step 60 onward - the trajectory-step analogue of the well-established time-stratified evaluation used in survival analysis and dynamic prediction (e.g. time-dependent ROC), where each subject is scored at matched times to remove confounding from horizon length. It simulates real-time failure-prediction monitoring. The next section explains exactly how it is computed and why we chose it.&lt;br&gt;
This was chosen over a single global AUC for three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Failed trajectories are systematically longer than successful ones. This is not specific to this particular dataset but a general property of agentic trajectories. A global AUC therefore leaks length into the score: any retriever that even partially exploits "how far the trajectory has gone" gets credit it doesn't deserve, and the purely structural contribution becomes invisible. Length-aware retrievers are not broken - it is normal to treat snapshots at approximately the same step as similar - but we want to isolate the structural signal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For a trajectory-snapshot retrieval system, the natural task is to find snapshots similar to the current one at this moment - not to project their state into the future.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A running per-step evaluation simulates monitoring in real time without knowledge of the future. Classifying already-completed trajectories has no practical value.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How per-step stratified AUC is computed (precisely)&lt;/strong&gt;&lt;br&gt;
For every trajectory in the eval set, we record a sequence of snapshots: one snapshot per agent step, ordered by step index &lt;code&gt;s = 0, 1,&amp;nbsp;…, S&lt;/code&gt;. At each snapshot, the retrieval engine returns the top-k most similar paths from the pool, and we compute fail_frac(s) = the fraction of those top-k whose owning trajectory ended in failure.&lt;br&gt;
The score we evaluate at step s is the fail fraction for that snapshot, or the latest known value if there is no coverage at this step.&lt;br&gt;
At each absolute step s, we take the set of trajectories that have made it that far - those whose own length is at least s. This is the active population at step s. Each trajectory in this set has two values: its score and its true label (failure or success).&lt;br&gt;
We then compute the standard ROC AUC for this group. ROC AUC is the probability that, if we draw one random failure and one random success from the active set, the failure has the higher score. 0.5 means the score doesn't distinguish the classes; 1.0 is perfect separation.&lt;br&gt;
Only trajectories whose max step ≥ s participate. Steps where all active trajectories belong to the same class (no failures or no successes among them) are skipped, because ROC AUC is undefined there.&lt;br&gt;
The final headline number is the mean over all valid steps from s = 60 to the maximum step:&lt;br&gt;
&lt;code&gt;AUC@step_60 = mean( AUC(s) for s in [60, S_max] where defined )&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why start at step 60.&lt;/strong&gt; On this dataset, 76% of trajectories are still active at step 60 (209/275) - enough accumulated context for the structural signal to surface, while the active population is still large enough to keep per-step AUC statistically meaningful. Earlier steps carry too little behaviour to discriminate outcome; much later, only the longest tails remain and the active set shrinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why per-step AUC is stratified by construction&lt;/strong&gt;&lt;br&gt;
At step s, all trajectories alive at step s are compared only to peers at the same step, so a retriever that exploits snapshot length alone gets an expected AUC of 0.5 - the length signal is removed by the stratification, leaving only the structural contribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Participants&lt;/strong&gt;&lt;br&gt;
Episodiq - our structural retrieval engine (described below).&lt;br&gt;
Basic RAG (cosine KNN) - embedding baseline: each prefix is reduced to a single vector by averaging the last N messages (N swept across &lt;code&gt;{1, 5, 10, 15, all}&lt;/code&gt;); retrieval is top-k nearest neighbours from distinct trajectories by cosine similarity; the score is the failure rate of the retrieved set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The task&lt;/strong&gt;&lt;br&gt;
At every step from 60 onward, assign each active snapshot a probability that it belongs to a failed trajectory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data&lt;/strong&gt;&lt;br&gt;
275 trajectories from SWE-rebench-OpenHands-Trajectories, filtered to the tobymao/sqlglot repository (178 failures, 97 successes). No two trajectories share the same PR, because any same-PR pair carries near-identical semantic content, which would leak directly into a RAG-style retriever's neighbour-by-cosine matching. The instance cap (one trajectory per PR) removes that confound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inputs&lt;/strong&gt;&lt;br&gt;
All participants receive the same trajectory prefix at every step, but consume it differently:&lt;br&gt;
Episodiq. Each observation and action message is classified into a cluster; the prefix becomes a sequence of typed cluster tokens (initial user prompt dropped - it cannot be cleanly typed), which is then used to retrieve similar paths from completed trajectories. Stratified split: 55-trajectory tune slice, 220-trajectory disjoint eval slice.&lt;/p&gt;

&lt;p&gt;Basic RAG was tested across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;embedding dimensions: 1024 and 2048&lt;/li&gt;
&lt;li&gt;include / exclude task message&lt;/li&gt;
&lt;li&gt;top_k: &lt;code&gt;{5, 10, 25, 50}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;last-N messages averaged: &lt;code&gt;{1, 5, 10, 15, all}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cosine similarity: &lt;code&gt;{0.5, 0.6, 0.7, 0.8, 0.9, 0.95}&lt;/code&gt;
Reported number is the best configuration over the sweep.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvu71t5qa8za4za7il51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvu71t5qa8za4za7il51.png" alt="Episodiq and RAG comparison at peak AUC @ step 60+" width="798" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Main finding: Episodiq leaps in AUC and coverage drop, while across the entire basic-RAG sweep, AUC stays pinned in the 0.58–0.60 range at every coverage from 9% to 100%. Cosine similarity saturates - tightening the threshold buys selectivity but no extra signal - and no embedding configuration we tried recovers what structural retrieval picks up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7awhmvxfup5nddhmagou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7awhmvxfup5nddhmagou.png" alt="RAG sweep table" width="799" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 0.10 headline gap has overlapping 95% CIs on a 220-trajectory eval, but the saturation is a property of the whole sweep, not of one estimate.&lt;br&gt;
The flip side is coverage: at the picked similarity threshold (0.20), only 22% of step-60 snapshots have a structural match above threshold; and coverage drop is extremely steep, with 22% coverage being next acquired point after near full coverage. Although, coverage drop here is expected - the space of possible trajectory shapes is combinatorially large, so a genuinely similar neighbour won't always exist, we note that such steep curve is not very convenient. We see concrete ways to widen this window without diluting signal - the focus of the next version.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Episodiq&amp;nbsp;works
&lt;/h2&gt;

&lt;p&gt;The representation is built in four&amp;nbsp;steps:&lt;br&gt;
Embed and cluster every observation and action message in the corpus - each message ends up labelled by its cluster (observations get classes &lt;code&gt;o1, o2,&amp;nbsp;…, oN&lt;/code&gt;; actions get &lt;code&gt;a1, a2,&amp;nbsp;…, aN&lt;/code&gt;).&lt;br&gt;
At each agent step, form the pair (action class, observation class) for that step.&lt;br&gt;
Cluster those (action, observation) pairs - concatenating each pair's centroid embeddings as the input vector - into a compact act_obs token vocabulary (a few dozen tokens in our setup).&lt;br&gt;
A trajectory is the resulting sequence of act_obs tokens - one token per agent step.&lt;br&gt;
The initial observation (the task prompt) is dropped from step 1: it cannot be cleanly typed, and including it degrades downstream metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;br&gt;
Each path is reduced to its sequence of act_obs tokens accumulated up to that step (noise tokens - cluster outliers - are kept as ordinary symbols, since dropping them empirically hurt retrieval quality). A sliding window of n = 3 produces the trigram set of the path.&lt;br&gt;
Each trigram set becomes a MinHash signature of K = 512 permutations - a fixed-size fingerprint that approximates Jaccard similarity in O(K) per comparison instead of O(|set|). At retrieval time we score the query snapshot's signature against every completed trajectory's path, take the top-k matches above a similarity threshold, and report fail_frac - the fraction of those top-k whose owning trajectory ended in failure.&lt;br&gt;
Two parameters are tuned per dataset: top_k (how many neighbours to vote with - picked 10) and similarity_threshold (the floor below which a match is discarded - picked 0.20). Tuning runs on the 55-trajectory stratified slice; the 220-trajectory hold-out is touched only for the final eval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Episodiq does beyond similarity search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx52bby5i8ojmcuhegtfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx52bby5i8ojmcuhegtfs.png" alt="Report example" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
The same representation supports several practical features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human-readable logs at dramatically lower token cost &amp;amp; LLM-free annotation at runtime.&lt;/strong&gt; Annotate one representative per cluster, then reuse those annotations for every trajectory. New messages joining online are tagged at write time by KNN against messages of the same slice - instant labels, zero LLM calls in the hot path. On the 275-trajectory sqlglot eval we measure 98% reduction in token use vs naive per-message annotation; this gap widens with corpus size - at 1000 trajectories it reaches 99% (≈ 165×) and grows further, because cluster-annotation cost scales with the vocabulary, not the corpus.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-snapshot fail-similarity score&lt;/strong&gt;, as described in this article.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loop detection.&lt;/strong&gt; If an agent gets stuck repeating the same type of messages in a loop, it is surfaced. This does not always mean the agent is broken - it can be useful simply for monitoring token waste.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Action-predictability signal.&lt;/strong&gt; Marks each step as one where the next action is predictable, or one where the agent could plausibly branch many ways.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this&amp;nbsp;means
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Structural similarity is signal that text-embedding retrieval misses.&amp;nbsp;&lt;/strong&gt;&lt;br&gt;
Across the entire basic-RAG sweep - every combination of embedding dimension, top-k, prefix window, and cosine threshold we tried - no setting recovered what structural retrieval picks up. This isn't a verdict against embeddings; they remain the right tool for content similarity. It's a constructive finding: agent trajectories carry a second axis of similarity, orthogonal to text content, and the most useful systems will combine both rather than choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For operators running agents at scale, this is signal currently left on the&amp;nbsp;table.&amp;nbsp;&lt;/strong&gt;&lt;br&gt;
If you already store trajectories, the same representation gives you a fail-prediction analytics, a filter for experience and reflection pipelines, loop and action-variance signals, and human-readable logs at ~98% token savings - all LLM-free at runtime. The only paid input is a one-time message embedding pass: ~$0.10 for this dataset. Everything else - clustering, MinHash, retrieval - is cheap CPU work, and inference cost is approaching zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For anyone evaluating trajectory retrieval, per-step stratified AUC is the load-bearing metric.&lt;/strong&gt;&amp;nbsp;&lt;br&gt;
A single global AUC mixes structural signal with length leakage - a trivial length-only classifier reaches AUC 0.66 on this dataset. The stratification removes that confound by construction (the length-only baseline collapses to 0.5), so any number reported under this metric is a direct read on the structural contribution. We recommend it for cross-system comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and where this&amp;nbsp;goes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The benchmark is SWE-rebench-OpenHands-Trajectories specific.&lt;/strong&gt; The pipeline and underlying representation are general - nothing is hand-tailored to coding agents - but we have not yet verified that the approach transfers to other domains (browser agents, support agents, data-analysis agents) without re-tuning. We expect it to, but expectation isn't evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Episodiq needs a meaningful pool of trajectories.&lt;/strong&gt; Clustering and signal thresholds become informative only once the corpus has seen enough behaviour to surface recurring patterns. Practically, several hundred trajectories is the minimum we have worked with. Few trajectories analysis is not what this tool is for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Narrow coverage window.&lt;/strong&gt; The Jaccard similarity distribution is concentrated in a narrow band, so coverage is sensitive to the threshold: a small move in similarity_threshold shifts coverage substantially. We have preliminary results indicating that an alternative path-similarity method can substantially relax this tradeoff - comparable AUC at near-100% coverage, and strictly higher AUC where coverage is traded for selectivity. This is the headline focus of the next version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reproduction kits&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/slavaZim/episodiq/tree/main/benchmarks/demo_eval" rel="noopener noreferrer"&gt;Episodiq benchmark&lt;/a&gt; (Basic RAG baseline at benchmarks/basic.py)&lt;br&gt;
Episodiq is open source: &lt;a href="//github.com/slavaZim/episodiq"&gt;github&lt;/a&gt;. Numbers should reproduce; if they don't, that's a bug we want to know about.&lt;br&gt;
If you run agents at scale, the most useful thing you can do is point Episodiq at your trajectories and tell us what breaks. From our experiments, system tuning can be tricky and tuning decisions are sensitive to data. Working installations, failure modes, and "this doesn't fit my use case because X" are all useful to hear.&lt;br&gt;
Contact: &lt;a href="https://t.me/wdambb" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; / &lt;a href="mailto:skaiscan@gmail.com"&gt;skaiscan@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
