Reference
TabSchema, TabSynth, TabHead¶
The three phases of the paper under their own names.
TabAgent joins a feature card, an optional TabSynth and a TabHead into one model that cross_validate can clone,
fit and score. The concepts are on The three phases.
from tabagent import TabAgent, TabHead, TabSynth, datasets
card = datasets.card("skillsbench/skill-select")
agent = TabAgent(card, head=TabHead("ranking"), synth=TabSynth(k=2))
TabAgent
¶
TabAgent(card: CardLike, head: TabHead | None = None, synth: Any = None, seed: int = 0)
A feature card, an optional TabSynth and a TabHead, trained and applied as one model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
card
|
CardLike
|
the |
required |
head
|
TabHead | None
|
the |
None
|
synth
|
Any
|
a |
None
|
seed
|
int
|
the head's seed ( After |
0
|
Examples:
from tabagent import TabAgent, TabHead, TabSynth, datasets
name = "skillsbench/skill-select"
table = datasets.load(name, download=True)
agent = TabAgent(datasets.card(name), head=TabHead("ranking"), synth=TabSynth(k=2))
agent.fit(table)
agent.decide(table.subset(table.groups[:1]).drop_labels(), k=5) # top-5 candidates of the first task
fit
¶
fit(table: DecisionTable) -> TabAgent
Fit the card's statistics, synthesize (if set) and fit the head on the labeled table.
decide
¶
decide(
data: DecisionTable | DataFrame,
*args: Any,
k: int | None = None,
defer_below: float | None = None,
fallback: Callable[[DataFrame], Any] | None = None,
confidence_method: str = "largest_gap",
) -> DataFrame
Decode a decision for every slate in data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DecisionTable | DataFrame
|
a |
required |
k
|
int | None
|
keep the top |
None
|
defer_below
|
float | None
|
mark decisions whose confidence is below this value |
None
|
fallback
|
Callable[[DataFrame], Any] | None
|
called with the deferred decisions' rows (typically the original LLM component); its
return value is stored in |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per selected (decision, candidate) with |
score
¶
score(data: DecisionTable | DataFrame) -> ndarray
p(candidate is chosen) per row; the predicted class per row for single-class decisions.
clone
¶
clone(seed: int | None = None) -> TabAgent
An unfitted copy with the same card, head settings and synthesizer (and seed, when given).
save
¶
Write a portable, pickle-free artifact directory (see tabagent.artifact); metrics are
recorded in its manifest.
load
classmethod
¶
load(path: str | Path, verify: bool = True, trust_code: bool = False) -> TabAgent
Load an artifact written by save from a directory or hf://OWNER/REPO[@REVISION].
verify checks every file against the manifest's SHA-256; trust_code allows card features
computed by modules outside tabagent (python: sources).
TabHead
¶
TabHead(
kind: str = "ranking",
preset: str = "paper",
estimator: Callable[[int], Any] | None = None,
featurize: bool = True,
text_max_features: int = TFIDF_MAX_FEATURES,
text_repr: str = "auto",
threshold: float = 0.5,
seed: int = 0,
**catboost_overrides,
)
The TabAgent head (paper Sec. 3.3): a classifier over the feature card's columns.
fit and score operate on feature frames (the card's output); decode turns scores into
decisions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
the decision pattern ( |
'ranking'
|
preset
|
str
|
CatBoost configuration (see |
'paper'
|
estimator
|
Callable[[int], Any] | None
|
|
None
|
featurize
|
bool
|
set False for heads that consume raw columns themselves (for example TabSTAR). |
True
|
text_max_features
|
int
|
TF-IDF vocabulary size per text column. |
TFIDF_MAX_FEATURES
|
text_repr
|
str
|
|
'auto'
|
threshold
|
float
|
the multi-label decision threshold (App. J). |
0.5
|
seed
|
int
|
the model seed. |
0
|
catboost_overrides
|
CatBoost parameters that replace the preset's (not allowed with |
{}
|
Examples:
confidence
¶
Per-decision confidence of a ranking (App. N.3, "Confidence-gated fallback").
margin_at_r score(R-th) - score((R+1)-th), where R = |G(t)| (needs r: a mapping or Series from
decision id to R; an oracle quantity, used for analysis)
largest_gap the largest drop between consecutive ranked scores (cutoff-free, deployable)
risk_coverage
¶
Selective quality when deferring the least-confident decisions (App. N.3, Fig. 14): for each coverage level (fraction kept), the mean correctness of the kept decisions.
TabSynth
¶
TabSynth(
k: int | None = None,
text_cols: Sequence[str] | None = None,
paraphrase: Callable[[str, int], str] = det_paraphrase_v1,
max_source_rows: int | None = None,
seed: int = DEFAULT_SEED,
where=None,
*,
recompute: bool = False,
entity_swap: bool = False,
allocation: str = "uniform",
max_variants: int = 12,
)
Algorithm 1 of the paper (App. H).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
k
|
int | None
|
variants per real row (the paper uses two to four, Sec. 3.2). None uses the card's |
None
|
text_cols
|
Sequence[str] | None
|
free-text columns to paraphrase; defaults to the card's |
None
|
paraphrase
|
Callable[[str, int], str]
|
|
det_paraphrase_v1
|
max_source_rows
|
int | None
|
optionally paraphrase a seeded random subset of the real rows (for very large cards). |
None
|
seed
|
int
|
seeds only the |
DEFAULT_SEED
|
where
|
paraphrase only the source rows matching |
None
|
|
recompute
|
bool
|
build each variant from the table's raw row with the paraphrased text, then apply the card,
so text-derived features (length, lexical and embedding similarity) match the paraphrase instead of
being copied. Needs |
False
|
entity_swap
|
bool
|
swap entities ( |
False
|
allocation
|
str
|
After |
'uniform'
|
Examples:
from tabagent import TabAgent, TabSynth, datasets
card = datasets.card("skillsbench/skill-select")
agent = TabAgent(card, synth=TabSynth(k=2)) # two paraphrases per real training row, inside each fit
augment
¶
Return the synthetic rows (feature frame, labels) for the real training rows X, y.
X is the card's feature frame of the real training rows; table and card supply the
default text columns and k and the source group of each row (recorded in stats_ for
provenance). After the call, source_rows_ holds the row position in X that each synthetic
row paraphrases, and source_groups_ its group (when table is given).
TabSchema
¶
TabSchema(
llm: LLM | None = None,
config: TabSchemaConfig | None = None,
adapter=None,
store: RunStore | None = None,
sandbox: SubprocessSandbox | None = None,
stage_llms: dict[str, LLM] | None = None,
)
The FeatureOrchestrator: discovers a feature card for one decision component of an agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
llm
|
LLM | None
|
the model for every stage (default: |
None
|
config
|
TabSchemaConfig | None
|
a |
None
|
adapter
|
reads the agent's logs into trajectories and decision points (default |
None
|
|
store
|
RunStore | None
|
the run directory (default: a new one under |
None
|
sandbox
|
SubprocessSandbox | None
|
runs LLM-written extraction code (default |
None
|
stage_llms
|
dict[str, LLM] | None
|
LLM backends by model name, for the models With |
None
|
Examples:
from tabagent import TabSchema, TabSchemaConfig
from tabagent.adapters import detect_adapter
adapter = detect_adapter("./agent_logs")
schema = TabSchema(config=TabSchemaConfig(n_analyze=20), adapter=adapter) # the LLM comes from env keys
result = schema.discover("./agent_logs", "ToolShortlist")
result.card # the discovered FeatureCard; the run directory holds extract.py and the reports
discover
¶
discover(
trajectories: list[Trajectory] | str | Path,
target: str,
kind: str = "ranking",
name: str | None = None,
) -> DiscoveryResult
Run the discovery loop for the decision component target.
trajectories are Trajectory objects or a path the adapter reads; only successful ones are
used. kind is the decision pattern and name the card name. Every intermediate artifact is
written to the run directory, with run.json (the run's progress and anatomy,
tabagent runs show); a run that stops leaves a partial report.md there, and rerunning with the
same directory resumes it.
TabSchemaConfig
dataclass
¶
TabSchemaConfig(
model: str | None = None,
n_analyze: int = 20,
sample_seed: int = DEFAULT_SEED,
sample_strategy: str = "coverage",
max_code_retries: int = 3,
json_retries: int = 1,
accept: tuple[str, ...] = ("accept", "conditional"),
compile_min_success: float = 0.95,
max_null_rate: float = 0.5,
max_columns_per_feature: int = 64,
max_extractor_columns: int = 512,
compile_repair: bool = True,
drop_redundant_text: bool = True,
dependency_features: tuple[str, ...] = (
"candidate_prior",
"context_candidate_lex",
"context_candidate_sim",
"co_usage",
),
pair_embeddings: bool | None = None,
workers: int = 8,
timeout_s: float = 30,
max_cost_usd: float | None = None,
prices: dict[str, tuple[float, float]] = dict(),
max_prompt_chars: int = 400000,
stage_models: dict[str, str] = dict(),
fallback_model: str | None = None,
context_text: bool = True,
last_step_text: bool = True,
positional_guard: bool = True,
relative_features: bool = True,
verify: bool = True,
verify_adapter: bool = True,
verify_samples: int = DEFAULT_SAMPLES,
goldens: int = DEFAULT_GOLDENS,
audit: bool = True,
audit_samples: int = 12,
audit_max_incorrect: float = 0.25,
usefulness: bool = True,
prune_harmful: bool = False,
usefulness_max_decisions: int = DEFAULT_MAX_DECISIONS,
usefulness_max_rows: int = DEFAULT_MAX_ROWS,
llm_retries: int = 5,
request_timeout_s: float | None = None,
)
Settings of a TabSchema discovery run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
str | None
|
the LLM (default: the paper's GPT-5.4 on OpenAI, or the provider whose key is in the environment). |
None
|
n_analyze
|
int
|
decision points the FeatureAnalyzer reads. |
20
|
sample_seed
|
int
|
seed of that sample. |
DEFAULT_SEED
|
sample_strategy
|
str
|
how the sample is drawn (see |
'coverage'
|
max_code_retries
|
int
|
CodeExecutor retries with the error fed back (three, App. D.1). |
3
|
json_retries
|
int
|
re-asks when an answer is not parseable JSON. |
1
|
accept
|
tuple[str, ...]
|
MetaJudge outcomes compiled into the card: accepted and conditionally accepted features by
default. |
('accept', 'conditional')
|
compile_min_success
|
float
|
share of onboarding decision points the compiled extractor must run on. |
0.95
|
max_null_rate
|
float
|
extractor columns emptier than this are dropped. |
0.5
|
max_columns_per_feature
|
int
|
caps on the columns one planned feature expands into |
64
|
max_extractor_columns
|
int
|
caps on the columns one planned feature expands into
(code that returns a dict per row, such as word counts, gets one column per key) and on the
extractor's columns in all; past them the columns that vary most across decision points are kept.
On many decision points memory caps the columns other than presence flags further, to
|
512
|
compile_repair
|
bool
|
when the CodeValidator flags accepted features in the compiled code, ask the CodeExtractor once more with its objections (the paper's retry prompt), and keep the new code only if it runs and fewer features are flagged. |
True
|
drop_redundant_text
|
bool
|
drop a compiled text column that duplicates another kept text column (the appended context text included) word for word on every onboarding decision point: the same lowercased tokens, numbers and order, so a text and the list of its tokens collapse into one. Overlap, containment or the same words in another order never drop a column. Of duplicates a categorical form is kept over a text one. Within the columns of one planned feature, a column that restates another in a derived form on every row (its tokens, bag of words or n-grams as a JSON list or dict, or the same words in another order) is dropped too: the head represents text itself. |
True
|
dependency_features
|
tuple[str, ...]
|
dependency statistics added to the card. |
('candidate_prior', 'context_candidate_lex', 'context_candidate_sim', 'co_usage')
|
pair_embeddings
|
bool | None
|
add the e5 context-candidate similarity; None adds it when the embed extra is installed. |
None
|
workers
|
int
|
parallel analyzer calls. |
8
|
timeout_s
|
float
|
sandbox time limit per trajectory. |
30
|
max_cost_usd
|
float | None
|
LLM budget; needs a price for the model in |
None
|
prices
|
dict[str, tuple[float, float]]
|
|
dict()
|
max_prompt_chars
|
int
|
a decision-point prefix longer than this (as JSON) reaches the FeatureAnalyzer with its longest strings clipped. |
400000
|
stage_models
|
dict[str, str]
|
a model per stage, overriding |
dict()
|
fallback_model
|
str | None
|
asked once, for any stage, when the provider's content filter stops an answer. |
None
|
context_text
|
bool
|
append to the extractor a fixed, non-LLM function that returns the decision's context as
text ( |
True
|
last_step_text
|
bool
|
append to the extractor a fixed, non-LLM function that returns the text of the prefix's
last step ( |
True
|
positional_guard
|
bool
|
for a decision with candidates, ask the CodeExtractor to compute in its per-candidate
function the features it wrote as columns that index the candidates by position
( |
True
|
relative_features
|
bool
|
let the recipe search ( The release's checks beyond the paper (see |
True
|
verify
|
bool
|
run the compiled extractor, sandboxed, on |
True
|
verify_samples
|
int
|
decision points of those checks (coverage-sampled). |
DEFAULT_SAMPLES
|
verify_adapter
|
bool
|
check, before any LLM call, that the adapter's prefixes do not change with the run's
future ( |
True
|
goldens
|
int
|
golden tests recorded in |
DEFAULT_GOLDENS
|
audit
|
bool
|
ask the OutputAuditor (stage |
True
|
usefulness
|
bool
|
estimate each feature's contribution by drop-column grouped cross-validation on the onboarding
rows, on at most Operations: |
True
|
llm_retries
|
int
|
retries of an LLM call that fails transiently (a rate limit, a dropped connection, a server
error, an empty answer), with exponential backoff that honors the provider's |
5
|
request_timeout_s
|
float | None
|
seconds one LLM request may take, for the backends Every field is checked when the config is made (types and ranges), with a message naming the field;
|
None
|
verify_head
¶
Check a head saved by onboard before deploying it: the model artifact matches its manifest, the
card pins the hash of extract.py and the code matches it, the tests in extractor_tests.json cover
the card's extractor features, and re-running them in the sandbox ("subprocess", "docker",
"docker:<image>" or a sandbox instance) reproduces every recorded feature, twice, with the outcome
fields making no difference. A head without golden prefixes passes with a warning unless
require_goldens. path is a directory or hf://OWNER/REPO[@REVISION]. A path that is not a head,
or an invalid tests file, raises ValueError. CLI: tabagent verify-head PATH (exit status 1 when a
check fails, 2 on such an error).