Guide
Bring your own table¶
If your decisions are already a table, you get the full TabAgent recipe through the familiar fit and predict API.
You need a table with one row per decision, or per (decision, candidate); no LLM
If your decisions are already a table, use the scikit-learn estimators. They need no LLM and no log adapters.
The three estimators¶
| Estimator | Decision | Main call |
|---|---|---|
TabAgentRanker |
ranking (tool, skill, API, GUI-element shortlisting); predict also does multi-label decoding (threshold plus backoff) |
rank(X, k=5) |
TabAgentClassifier |
single-label decisions (operation type, task category); a standard scikit-learn classifier | predict(X) |
TabAgentRouter |
routing (model, sub-agent, skill on or off); y is the arm's success, 0 or 1 |
route(X); score is route@1 |
1. Shape the table¶
Estimators learn from one row per (decision, candidate). If you have one row per decision with the candidates the
agent used, tabagent.pointwise expands it:
import tabagent
from tabagent import TabAgentRanker
rows = tabagent.pointwise(decisions, chosen="tools_used", catalog=tools_df, candidate_col="tool")
X, y = rows.drop(columns=["chosen"]), rows["chosen"]
chosen names a column of lists with the candidates the agent selected. catalog is an iterable of candidate ids or
a DataFrame whose candidate_col holds them (its other columns, such as descriptions, are joined onto every row).
candidates optionally names a column of per-decision candidate lists, when each decision has its own legal set.
2. Fit¶
ranker = TabAgentRanker(group_col="task_id", candidate_col="tool").fit(X, y)
ranker.rank(X_new, k=5)
print(ranker.report(X, y)) # quality vs BM25 and chance, latency and cost per decision
group_col names the decision group (the task): rows of one group never straddle cross-validation folds.
What fit does:
- Infers a feature card from your columns: types; which text is the decision's context and which is the
candidate's; a context-candidate lexical overlap; with
tabagent[embed], an e5 similarity (the encoder is downloaded once, about 128 MB, or left out withpair_embeddings=False); and an in-fold candidate prior. - Selects the recipe by nested grouped validation on the training data only: TabSynth off, k=2 or k=4, and the prior on or off.
- Fits CatBoost with the paper's configuration.
ranker.selection_ shows every recipe it tried.
3. Cross-validate¶
The estimators follow the scikit-learn contract (clone, get_params), so they work in model selection with
grouped folds and tabagent.scorer:
from sklearn.model_selection import GroupKFold, cross_val_score
scores = cross_val_score(ranker, X, y, groups=X["task_id"], cv=GroupKFold(4), scoring=tabagent.scorer("p@r"))
tabagent.scorer(metric) takes any metric name of tabagent.evaluation.metrics.per_decision: p@r, recall@K,
hit@K, route@1, accuracy, ml_precision, ml_recall, ml_f1. See
Choose and evaluate a head.
4. Save and reload¶
ranker.agent_.save("my_tool_ranker") # a portable, pickle-free artifact
agent = tabagent.load("my_tool_ranker") # a TabAgent
A reloaded TabAgent has no extractor, so it decides on rows that already carry the columns it was trained on, with
the decision and candidate ids in the columns its spec names (the estimators call them __group__ and
__candidate__). Deploy and serve shows the call.
A complete, runnable example
examples/02_bring_your_own_table.py builds a toy log, cross-validates, fits, reports, saves and reloads. It
runs offline on CPU in about a minute.