File size: 9,891 Bytes
4131399
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# DataAgentBench failure analysis β€” runs of 2026-06-05

Trajectory analysis of full `--all` sweeps, scored with DAB's own
per-task `validate.py`. Captured with `--log-dir` (full per-turn prompts,
raw LLM responses, tool args, and untruncated results); diagnoses below
cite those traces.

## Run config

```
python -m benchmark.dab.runner --all --llm lexsi --log-dir artifacts/dab_runs/new_tool
```
- 5 of 17 datasets runnable (sqlite/duckdb); 12 skipped (postgres/mongo).
- **Score: 10/17 tasks passed** β€” `consolidate_tables` (fix B) recovered
  stockmarket/query4 + query5 over the pre-tool baseline of 8/17 (both
  confirmed using the tool; see Β§B).

> **Measurement note.** Single-run pass@1 carries per-task sampling
> noise β€” any system-prompt change re-rolls multi-step trajectories, so a
> few tasks can flip run-to-run. Use `--iterations 3` (majority vote) to
> pin a stable number before trusting a future delta.

## Results

βœ… = pass, ❌ = fail, 🎯 = recovered by `consolidate_tables`.

| Dataset | Query | Result | Validator reason / note |
|---|---|---|---|
| DEPS_DEV_V1 | query1 | ❌ | Missing name `@dmrvos/infrajs>0.0.6>typescript` (§C/§D) |
| DEPS_DEV_V1 | query2 | ❌ | Missing project `mui-org/material-ui` (§D) |
| GITHUB_REPOS | query1 | ❌ | No value rounds to 0.33 β€” wrong join (Β§A note) |
| GITHUB_REPOS | query2 | βœ… | Fuzzy matched `swiftandroid/swift` |
| GITHUB_REPOS | query3 | βœ… | Found 1077 |
| GITHUB_REPOS | query4 | βœ… | All repo names matched |
| music_brainz_20k | query1 | βœ… | Ground truth found |
| music_brainz_20k | query2 | βœ… | Ground truth found |
| music_brainz_20k | query3 | ❌ | Wrong song (`Zo gaat het leven…`) (Β§D) |
| stockindex | query1 | βœ… | `399001.SZ` primary |
| stockindex | query2 | ❌ | `IXIC` not in first 200 chars (§C) |
| stockindex | query3 | βœ… | All name-country pairs matched |
| stockmarket | query1 | βœ… | 18.44 β‰ˆ 18.44 |
| stockmarket | query2 | ❌ | Missing number 31 β€” consolidated, but count SQL still wrong (Β§B) |
| stockmarket | query3 | ❌ | `Apex Global Brands Inc` β€” consolidated, but avg-volume SQL still wrong (Β§B) |
| stockmarket | query4 | πŸŽ―βœ… | `MFA Financial, Inc` found β€” **recovered via `consolidate_tables`** |
| stockmarket | query5 | πŸŽ―βœ… | `Synthesis Energy Systems, Inc` found β€” **recovered via `consolidate_tables`** |

**Total: 10/17** β€” 8 baseline passers + 2 🎯 recoveries via `consolidate_tables`.

---

## Failure taxonomy

### A. Harness bug β€” history truncates tool-call args, agent thinks its SQL is broken (1 task, systemic)

**GITHUB_REPOS/query1.** The agent wrote a complete 960-char query and
`run_sql` **executed it fine** (returned `1 row Γ— 3 cols`). But the loop
echoes each tool call into the observation history truncated to 400
chars ([loop.py:236](../lexsi_ds/agent/loop.py#L236)):

```python
f"TOOL CALL: {tool_name}({json.dumps(args_obj, default=str)[:400]})\n"
```

The 400-char cut lands mid-`JOIN`, and the appended `)` makes the echo
read as a query that ends `…JOIN artifacts_database_contents c ON c)` β€”
i.e. **truncated mid-join**. The agent re-read this, concluded the
tooling was broken, re-ran the same query 10 times, and gave up:

> "every `run_sql` execution shown is truncated mid-join
> (`JOIN artifacts_database_contents c ON c)`), which causes the query
> to effectively fail" β€” *agent's final answer*

So a runnable task was lost to a display artifact, not a real error.
(The SQL logic was also likely wrong β€” proportion β‰  0.33 β€” but the agent
never got far enough to find out.)

**Fix (done).** [loop.py](../lexsi_ds/agent/loop.py) now echoes args via
`_echo_args()`: full up to an 8000-char cap, and when it must truncate
it appends an explicit `… [+N chars truncated for display only; the full
args above were executed]` marker β€” so a display cut can never read as a
broken statement. The 960-char query that triggered this is now echoed
in full.

### B. Scale β€” per-shard table explosion vs the 12-step budget (4 tasks) β€” FIXED via `consolidate_tables`

**The problem.** stockmarket materializes **2753 `stocktrade_database_<ticker>`
tables** (one per symbol, identical schema `Date,Open,High,Low,Close,
Adj Close,Volume`, ~410 rows each) plus one `stockinfo` table. The shard
key (ticker) lives in the **table name, not a column**. A question like
"across all NYSE stocks…" is a `GROUP BY ticker` that can only be
expressed as a 2753-way `UNION ALL` β€” unwriteable in a 12-step budget.
All four tasks hit step 13 and surrendered:

> "the query requires scanning/UNIONing thousands of
> `stocktrade_database_<ticker>` tables, and I ran out of allowed tool
> calls" β€” *query3, baseline*

**The fix (done): a generic `consolidate_tables` tool**
([consolidate_tables.py](../lexsi_ds/agent/tools/consolidate_tables.py)).
It folds a sharded family into one table in a single call β€” the giant
`UNION ALL BY NAME` is generated in our code, never against the agent's
step budget:

```json
{"tool": "consolidate_tables",
 "args": {"pattern": "stocktrade_database_*", "into": "stocktrade", "key_column": "ticker"}}
```

It auto-derives the key from the pattern's literal prefix (β†’ bare
tickers), schema-guards (unions only same-schema shards, reports the
rest), and defaults to a cheap VIEW. Generic by design: per-ticker,
per-day, per-region β€” any BYO datalake with sharded tables. Planner
rule 14 tells the agent to reach for it when `inspect_data` shows many
same-prefix tables.

**Measured:** 2753 tables β†’ one 6.47M-row view in **1.2s**; the
previously-timed-out cross-ticker query runs in **~1s**. On the
`new_tool/` sweep the agent called it in all 4 stockmarket tasks and
**recovered query4 + query5** (the name-lookup tasks). query2 (count off
by the "31") and query3 (avg-volume per company) still fail: the tool
removes the *structural* blocker, but the agent must still write correct
analytical SQL on top β€” which it nailed for q4/q5 and missed for q2/q3
(the latter compounded by Β§E coded values).

### C. Output format / "primary answer" expectations (2 tasks)

- **stockindex/query2** β€” the answer was **correct** (`IXIC`: 44 up / 31
  down) but the validator requires the target index in the first 200
  chars as the *primary* answer; the agent buried it after a prose
  preamble and a markdown table, and added a second index (`GSPTSE`).
  Penalized on presentation, not correctness.
- **DEPS_DEV_V1/query1** β€” the agent's query *did* surface the
  composite-key rows (`@dmrvos/infrajs>0.0.5>typescript`) but (a) chose
  version **0.0.5**, not the ground-truth **0.0.6** ("latest release"
  logic wrong β€” see Β§D), and (b) hedged/refused because the values
  "look like dependency paths rather than just the package name".

**Fix:** a benchmark-mode answer directive β€” lead with the bare
answer/identifier in the exact requested format, then optional detail.

### D. Wrong query semantics β†’ confidently wrong values (2 tasks)

- **DEPS_DEV_V1/query2** β€” returned `react-component/picker`, `tailwindcss/typography`, …;
  ground truth is `mui-org/material-ui`. The "marked as release" filter
  + fork-count join produced a different top-5.
- **music_brainz_20k/query3** β€” answered `"Groovey" by Rich Matteson,
  $4,128.59` in a single `run_sql`; ground truth is
  `"Zo gaat het leven aan je voor"`. Revenue was almost certainly
  aggregated by the wrong entity (track title vs. recording, or not
  summed across stores). The agent was overconfident β€” 4 steps, one
  query, no cross-check.

**Fix:** these need the DAB `db_description` field-semantics to be
weighted more (e.g. how "latest release" / "revenue" are defined), and a
self-check step before finalizing on aggregation questions.

### E. Coded-value / data-semantics gaps (compounds B)

**stockmarket/query5, query3** β€” the agent filtered on
`Listing Exchange='Q' AND Market Category='QCM'`, got 0 rows, and
concluded the data didn't match. `Listing Exchange`/`Market Category`/
`Financial Status` are **coded** columns whose codes differ from the
agent's assumptions. It spent steps probing codes instead of answering,
which (with Β§B) guaranteed a timeout.

---

## Priorities

| Fix | Status | Impact |
|---|---|---|
| **A.** `_echo_args()` no longer cuts SQL mid-string | βœ… done | Removes false "truncated SQL" failures; helps any long-SQL task |
| **B.** `consolidate_tables` folds sharded families into one table | βœ… done | **+2** (stockmarket q4, q5); generic for any sharded datalake |
| **D/E.** Lean on `db_description` semantics + a pre-final self-check | open | DEPS/query2, music_brainz/query3, stockmarket q2/q3 SQL logic |
| **C.** Benchmark answer-format directive (lead with bare answer) | open | stockindex/query2 (+ partial DEPS/query1) |

**Net effect of A + B: 10/17.** A is a correctness floor (no more false
failures); B is the +2 (stockmarket q4, q5). The remaining levers are
**D/E** (analytical-SQL / coded-value semantics β€” the dominant failure
mode now that the structural blockers are gone) and **C** (output
format).

**Measure with `--iterations`.** Single-run pass@1 is noisy β€” a prompt
change re-rolls every multi-step task. Run `--iterations 3` (majority
vote) before trusting a future delta.

## Reproduce / inspect

```bash
# Noise-adjusted measurement run (majority vote per task):
python -m benchmark.dab.runner --all --llm lexsi --iterations 3 \
    --log-dir artifacts/dab_runs/measure_v1

# Inspect one trajectory end-to-end (prompts, raw responses, SQL, payloads):
python -c "import json; d=json.load(open('artifacts/dab_runs/new_tool/stockmarket_query4_iter0.json')); \
print(d['reason']); [print(s['n'], s['tool'], (s['args'] or {}).get('sql','')[:80]) for s in d['run']['steps']]"

# Confirm the new tool fired on the stockmarket tasks:
grep -l consolidate_tables artifacts/dab_runs/new_tool/stockmarket_*.json
```