TL;DR
Semantic Atoms are two "building blocks" for analyzing tables with text: tagging and categorization. Tagging takes N texts and returns N values, i.e. a new column. Categorization takes N texts and returns M categories, i.e. a reference list of topics. The LLM handles everything "meaning-related," while sorting, filters, averages and regression are done by ordinary code on the finished column.
The typical pain: you ask an agent to "sort the complaints by customer frustration and find the main topic of each." It does this in separate calls, and each one returns only the end result. The sort gives you an order, but without the scores themselves. When the next step needs a "frustration" column, the model scores everything all over again. The scores come out different, the ordering and the regression disagree, and you pay for the tokens twice. The reason is that the intermediate result was never saved anywhere: the model "kept it in its head" and didn't write it down.
The method is simple: every meaning-related step writes its result to an explicit place. A 1–10 frustration score for each row becomes a column, and both the sort and the regression read exactly that column. For topics there are two steps: first categorize finds a shared list of topics, then tag assigns each row a topic from that list. If there are thousands of rows, batching and reassembly happen inside the tool, and the agent never knows about it.
Want this for your own case? Apply this study to your task: AI adapts its method to what you are working on and writes the prompt.
Method Blueprint
STEP 1 (tagging): tag(texts, "rate frustration 1–10") → a column of N values
STEP 2 (categories): categorize(texts, "main complaint topics, up to 8") → a table of M categories
STEP 3 (assignment): tag(texts, "pick a topic from the list {topics}") → a column of N values
STEP 4 (exact math): sorting / grouping / regression — in code, on the finished columns
Rule: every meaning-related call → its result is saved and available to later steps.
Steps 1–3 are separate model calls; step 4 is Python/SQL/Excel formulas.
Example
Task: You run service quality for Amazon pickup locations in three cities. The export has 3,000 reviews: text, rating, city, days waiting for the order. You need to figure out what makes people angry and where it's worst, and also check whether frustration grows with the wait.
Prompt (for an agent with data analysis: Claude Code, ChatGPT with Data Analysis, Cursor):
I have a file reviews.xlsx with columns "text", "city", "wait_days", "rating".
Work by the "semantic atoms" rules:
1. Do meaning-related work on the text only through two kinds of steps:
- TAG: N reviews in, exactly N values out (one per review, in the same order). Save the result as a NEW COLUMN in the table.
- CATEGORIZE: N reviews in, a list of M categories out (name + short description). Save it as a SEPARATE TABLE.
2. Do sorting, grouping, averages and regression in code on the finished columns. Don't ask the model to "sort" or "calculate."
3. Don't recompute what's already tagged: if the column exists, use it.
Plan:
- TAG "frustration": rate from 1 to 10, where 1 is calm and 10 is extremely frustrated.
- CATEGORIZE: identify up to 8 main complaint topics.
- TAG "topic": for each review, pick exactly one topic from the resulting list (or "other").
- Then in code: average frustration by city and topic, a regression of frustration on wait days and topic.
If there are many reviews, split them into batches of 100 with a row ID and reassemble the result by ID. Show me the final tables and 3 sample reviews per topic.
Result: The agent will first add "frustration" and "topic" columns to the table. It will show a reference list of 6–8 topics with descriptions. Then it will output summary tables by city and topic and the regression results, all computed in code. Every number can be checked: the scores live in the file instead of dissolving into the model's reply. If you ask "what if we only take Denver," the calculation runs on the same columns without another pass over the text.
Why It Works
The LLM's weakness: the model is bad at holding intermediate state. It's unreliable at exact math (sorting 3,000 rows "by eye," averages, regression). If the same meaning ("frustration") has to be extracted twice, the scores will diverge, because each call gives its own answer.
Strength: the model is excellent at understanding the meaning of a single text with a clear task: rate the tone, identify the topic, pick from a list. Code is the opposite: it calculates perfectly but doesn't understand meaning.
How the method uses this: the boundary runs along the output of the meaning-related work. The model does local work on each row and writes it into a column. From there, code does all the math and rereads that column, so there's one score for every step. Topics follow the same principle: a shared category dictionary is built once and every row is assigned into it, so topics across rows are comparable.
Control knobs: - The scale in the request ("1–10," "yes/no," "one of: …") → the stricter the format, the easier it is to compute in code. Free text in a column is awkward to aggregate. - Number of categories ("up to 8") → more gives more detail but fragments the stats. Start with 5–8. - Batch size (100 rows) → smaller batches give more stable scores, but more calls. - The category list in the assignment request → add "other" so rare topics don't get erased. - One call per column: for several attributes (city, ZIP code, tone), make one call for each instead of one for everything.
Prompt Template
I have a table {file} with columns: {column_list}. Text column: {text_column}.
Work by the "semantic atoms" rules:
1. Do meaning-related work on the text only with two kinds of steps:
- TAG: N texts in, exactly N values out, one per text, in the same order. Save the result as a NEW COLUMN with a row ID.
- CATEGORIZE: N texts in, a list of M categories out (name + description). Save the result as a SEPARATE TABLE.
2. Do exact operations (filter, sort, group, average, regression) in code on the finished columns. Don't hand them to the model.
3. One meaning, one calculation. If a column already exists, don't recompute it — use it.
4. For open-ended grouping: first CATEGORIZE, then TAG "pick one category from the list {categories} or "other"".
5. Several attributes = several TAG calls, one per attribute.
My question: {what_i_want_to_learn}
Propose a plan of TAG / CATEGORIZE / code steps. Then execute it. If there are more than {batch_threshold} rows, split them into batches with IDs and reassemble the result by ID. At the end, show the final tables and 3 sample rows per category for checking.
What to fill in: {file} and the columns — your export. {what_i_want_to_learn} — the goal in plain language, e.g. "understand what drives ticket resolution time." {batch_threshold} — usually 100–200 rows.
🚀 Quick start — paste into the chat:
Here is the "semantic atoms" template (tagging into a column + a category reference list + exact math in code).
Adapt it to my task: [your task].
Ask me questions to fill in the fields.
[paste the template above]
The LLM will ask what columns your table has, what attribute you need from the text (a scale or categories) and what you ultimately want to calculate. Those answers determine which TAG and CATEGORIZE calls are needed and what's left for the code. It will take the pattern from the template and assemble a ready-made prompt for your export.
Limitations
⚠️ No new capabilities: the authors say outright that atoms don't add new abilities. It's just a sensible default: "tag into a column, then compute." Tagging quality is on average comparable to off-the-shelf semantic operators, not better.
⚠️ A coding agent without atoms manages too: a regular coding agent without this interface showed similar result preservation in three of five task classes. The gain isn't guaranteed, the conclusions are relative to a reference run, and the intervals overlap.
⚠️ Absolute scales are weaker: the operators reproduced absolute score values more accurately. Atoms preserved the ordering better. A "7 out of 10" isn't stable from run to run. It's fine for ordering and comparisons; for threshold decisions ("escalate everything above 7"), validate on a sample.
⚠️ The call savings are an implementation property: "41.8 times fewer calls" refers to the authors' specific system. For you it means "token savings" more than a quality boost.
⚠️ Scale requires code: automatic batching, reassembly and retries are built into the authors' system. In a chat or an agent, you do this by hand or with a script. Rare but important topics can get lost during batching: if categories are nearly evenly split, you need a buffer of candidates.
⚠️ Text only: the method was tested on tables with text columns. It's not for images, audio or layout-heavy PDFs.
How It Was Studied
The idea was to draw a boundary between "meaning" and "exact math" in an agent system. The authors built TADA-Agent: a strong model plans the analysis and writes Python, while lightweight models execute tag and categorize calls inside the code. To test it they assembled TABench — 40 end-to-end analytical queries on tables, e.g. "find the topics, rate the tone, run a regression." They compared against a system of off-the-shelf semantic operators, where such calls are packaged into single actions like "semantic sort," and against an agent without atoms.
Since many queries have no single right answer, a held-out run of the system itself served as the reference, and an LLM judge did the grading. So the authors honestly note that they measure agreement with the reference, not absolute quality. Bottom line: row-level quality is comparable, preservation of the final output files is higher in all five task classes, and there are at least 41.8 times fewer model calls. They separately tested large volumes (tagging beyond a single context window) and open legal and biomedical benchmarks with the same calls. Practical takeaway: the main advantage is reuse and savings, not "smarts." That's an important correction amid the loud claims in this field.
The analysis is based on the available part of the paper: the introduction, literature review and Section 3.2. Detailed result tables and appendices were not included in the text.
Adaptations and Extrapolations
🔧 Technique: add a "code book" → stable labels in the column
If you need labels rather than scores, lock them in up front:
TAG "ticket type": pick exactly one code from the list:
BILL — billing, DELIV — delivery, QUAL — quality, SUPP — support, OTH — other.
Return only the code, no explanation.
Effect: the column is easy to group and compare across exports from different months.
Extrapolation: a chat without code, on a small sample. If you don't have a coding agent and you have up to 200 rows:
Here are 100 reviews, each numbered. Step 1: return a table "number | frustration 1–10".
Don't do anything else. I'll do the math in Excel.
Then in the next message: "Now identify 6 complaint topics," and after that: "Assign each number one topic from the list." Do the sorting and averages in Excel. This carries the principle over to manual mode. The authors didn't test this variant.
Resources
- Semantic Atoms for Scalable Text Analysis in Data Analytics in the Era of LLM-Based Agents — Jinxiang Xie (Nanjing University), Zihao Li (Tsinghua University), Rui Ding, Bartuer Zhou, Shi Han, Dongmei Zhang (Microsoft Research).
- Benchmark TABench (40 analytical queries); agent TADA-Agent; prior work CAST (Xie et al., 2026) on the stability of tagging and summarization.
- Related systems: LOTUS, DocETL, Palimpzest; Programmatic Tool Calling (Anthropic, 2025).