# Introducing Fabric-RLM

For the past 5 months, I have been working on a side project to build [Fabric-RLM](https://github.com/pawarbi/fabric-rlm-core), a Python library built for Fabric using Recursive Language Model. I am super excited to finally share it and show how it will help Fabric users in their data tasks.

If you have tried asking an LLM a question about a large CSV, a wide Excel file or a bunch of PDFs, you know how it goes. It either can't fit the data in the prompt, or it answers anyway and you have no idea whether the number is right. **Fabric-RLM**, aims to fix both of those problems for data work in Fabric notebooks.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/62678fcf-55a6-4a6e-ae10-d27f26115203.png align="center")

## What is it?

RLM stands for Recursive Language Model. The idea comes from [this paper](https://arxiv.org/abs/2512.24601) by Alex Zhang, Tim Kraska and Omar Khattab at MIT: instead of stuffing everything into the context window, let the model write code to examine the data and call itself on the pieces it needs. It's built on top of DSPy's RLM module.

Fabric-RLM takes that idea and makes it work next to your Fabric data. You give it:

*   a task in plain English
    
*   the inputs (Lakehouse files, Delta tables, semantic models, CSV...)
    
*   an output contract, i.e. the fields you want back and their types
    

The model then writes Python, runs it in a subprocess inside your notebook session, looks at the result, fixes its code, and keeps going until it can submit an answer. The answer only comes back to you if it passes the contract and any validators you add. If it fails, the failure goes back to the model as feedback and it tries again.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/6975c3bb-c956-429a-907c-e26d1ca140a0.png align="center")

For example, after you install fabric-rlm library in the Fabric notebook:

```python
from fabric_rlm import FabricLM, File, RLM

result = RLM.task(
    task="For the latest complete year, return the 10 countries with the highest average CPI inflation. "
         "Report the rows analyzed and the filters applied.",
    inputs={"prices": File("/lakehouse/default/Files/imf_cpi.csv")},
    outputs={"year": int, "top_countries": list, "rows_analyzed": int, "filters_applied": list},
    lm=FabricLM("gpt-5.1"),
    skills=["data_exploration"],
).run()
```

Fabric-RLM will work on the assigned task in a notebook using the specified input data to generate the outputs using built-in Fabric LLM endpoint and skills. The input data can be of any size because Fabric-RLM will write the Python code to access that data, work on the defined task until it is completed and generate the output. If along the way it runs into any issues, it will assess it and fix it in the next turn. You can also inspect how the LLM arrived at the solution. Below you can see it ran into errors but it recovered itself and finally completed the task after 12 turns.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/57e2e813-62a2-491c-b790-ea712f8ec337.gif align="center")

This is how you and I work too. We inspect the schema, sample the values and incrementally write the code.

## Features

*   **Works with Fabric data in place.** `File(...)` for Lakehouse files, `LakehouseSource(...)` for Delta tables, `SemanticModel(...)` for Power BI models (DAX runs in the tabular engine), and `FileDestination(...)` to write outputs back to `Files/`. You can mix them in one task.
    
*   **Multiple Inputs, multiple contract-bound outputs.** You can add 3 semantic model, 2 lakehouse tables and a CSV as an input. Get a markdown file and a multi-sheet Excel based.
    

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/59194dd7-83d0-4c47-8685-600a10bfe91f.png align="center")

*   **Uses the packages you already have.** The worker is the same Python environment as your notebook: pandas, DuckDB, Polars, openpyxl, PyMuPDF etc. It reads CSV, Parquet, Excel, JSONL and PDF.
    
*   **Validators and repair.** Typed outputs, `output_validator` for business rules, and verifiers inside skills. A rejected answer becomes repair feedback for the next attempt.
    
*   **Skills.** Markdown playbooks with field definitions, procedures and checks. The bundled ones cover data exploration, PDFs, Excel, Delta and semantic models, and you can keep your own in the Lakehouse.
    
*   **Full trail.** Every run returns the payload plus the trajectory: code, outputs, errors, tokens, timings and validation repairs. You can save a trajectory and replay it later without calling the model.
    

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/a9022ed1-0318-4f0b-bde0-2d9ce0e467ca.png align="center")

## Example

Let's take a real example. [Download this notebook](https://github.com/pawarbi/fabric-rlm-core/blob/main/examples/notebooks/rlm_vs_plain_llm_imf_cpi.ipynb) and run it in your Fabric capacity. If you have a paid Fabric capacity, you won't have to make any changes. If it's a trial capacity, you can use a model from Azure AI Foundry or OpenAI or Anthropic.

The notebook downloads a CSV from International Monetary Fund's website. It's a 140MB file with 1.5M rows, 22 columns and roughly 35 million values. Tokenized, that's about 79 million tokens, nearly 200 times the 400K context window of gpt-5.1. No prompt is going to hold that.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/ecb01272-3dfa-4bca-9784-251716b43ee5.png align="center")

**The task:** build a formatted Excel workbook with a pivot of the 10 highest-inflation countries by year (2021–2025), a merged title cell, styled headers, and a second sheet listing every country with all 60 monthly observations.

I tried it two ways.

1.  Ask gpt-5.1 directly. I pasted in as much of the file as would fit: 200,000 characters, which is less than 0.15% of it. It used about 109K prompt tokens and then answered, correctly, that it couldn't answer, because the rows it was shown didn't even include the series it needed.
    
2.  Same question through Fabric-RLM with gpt-5-mini, a model about 5x cheaper. The file goes in as a `File(...)` input instead of pasted text. The model used `DuckDB` to aggregate the 1.57M rows, pivoted by year, then used `openpyxl` to merge and style the header, write both sheets and save the workbook. A separate DuckDB query computed the ground truth, and the workbook was graded cell by cell: the merged title, the header formatting, all 60 pivot values, the median and the full country list. It passed every check in 78 seconds.
    

```python
TASK = """You are given IMF CPI data in SDMX-CSV format (one observation per row)
at data_file. Build an Excel report at report_path (create the file with openpyxl).

Data selection: rows with INDEX_TYPE='CPI', COICOP_1999='_T',
TYPE_OF_TRANSFORMATION='YOY_PCH_PA_PT', FREQUENCY='M'. TIME_PERIOD looks like
'2021-M01'; COUNTRY is an ISO3 code; the value is in OBS_VALUE. A qualifying
country has all 60 monthly observations for 2021 through 2025.

The workbook has two sheets.

Sheet 'Report':
- A1:G1 merged, containing exactly: Average year-over-year CPI inflation (%), 2021-2025
- Row 2 headers, bold, with a solid light-gray fill (PatternFill fgColor D9D9D9):
  Country, 2021, 2022, 2023, 2024, 2025, Avg 2021-2025
- Set column A width to 32.
- Rows 3 to 12: the 10 qualifying countries with the highest five-year average of
  their monthly YoY values, sorted descending by that average. Each year column is
  that calendar year's average of the 12 monthly values; 'Avg 2021-2025' is the
  average of all 60 monthly values. Round to 2 decimals and apply number format
  '0.00' to every numeric cell.
- Row 13: column A = 'Median (all qualifying countries)', column G = the median
  across ALL qualifying countries of their five-year averages, 2 decimals,
  number format '0.00'.

Sheet 'All countries':
- Row 1 headers, bold: Country, Avg 2021-2025
- One row per qualifying country, sorted descending by the five-year average,
  values rounded to 2 decimals with number format '0.00'.

Save to report_path. Then reload the saved file with openpyxl and verify EVERY
requirement above: both sheet names exactly as given, the merge, bold headers
with the D9D9D9 fill, the column width, the '0.00' number formats, the sorted
rows, and the 'All countries' row count. Fix and re-save until every check
passes. Only then SUBMIT with n_countries (the count of qualifying countries)
and median_avg (the row-13 median value)."""
```

```python
from fabric_rlm import File, RLM

t0 = time.time()
rlm = RLM.task(
    task=TASK,
    inputs={"data_file": File(DATA_PATH), "report_path": REPORT_PATH},
    outputs=["n_countries", "median_avg"],
    lm=FabricLM('gpt-5-mini'),
    skills=["data_exploration", "excel_modify"],
    max_turns=10,
    timeout=600.0,
)
result = rlm.run()
```

Fabric-RLM, wrote the Python code to load the CSV file, inspect it and transformed it to save the file Excel to the lakehouse.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/fc4476eb-3e7a-4230-95e3-15c0a10211bc.png align="center")

In the second turn, it made some error in it Python code but recovered from it to generate final output.

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/9687aa0b-51f1-4702-91b1-61227a9df974.png align="center")

In the next few blogs I will show how you can take it to the next level to generate a detailed HTML report too:

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/54a804c6-343c-4429-954e-f52de84b6df4.png align="center")

## So what?

You probably use ChatGPT, Copilot, Claude, Github Copilot everyday. It can do this task easily. They can, but:

*   **Your data has to go to them.** You download from OneLake, upload to a chat and hit file-size limits. Fabric-RLM runs next to the data: Lakehouse files, Delta tables and semantic models (DAX runs in the tabular engine with RLS), with nothing copied out of your tenant.
    
*   **You're the validator.** In a chat, you read the answer and decide whether it looks right. Here, the acceptance check is code you write once: types, business rules, a ground-truth query. If it fails, the model gets the failure message and tries again, and nothing comes back to you until it passes.
    
*   **It's a conversation, not a pipeline.** You can't schedule a chat. `RLM.task(...)` is a Python call, so it can run in a notebook, a pipeline or a nightly job, and return typed fields your next cell can use.
    
*   **No API keys or extra resources.** `FabricLM` uses the model endpoint on your capacity, with the notebook identity. You can bring your own model too.
    
*   **You can see how every number was produced.** Every run keeps the code it wrote, the outputs, errors, tokens and repairs. You can replay it later without calling the model, which is useful for audits and regression tests.
    
*   **Cheaper models do the job.** A model 5x cheaper passed where a bigger one failed, because the work is done by code, not by the model's memory.
    
*   **Your rules are reusable.** Skills are markdown files that hold your definitions and checks, next to your data, instead of in a prompt you retype.
    

The chat assistants are great for exploring. Use Fabric-RLM when the answer needs to be right, repeatable and run without you on a schedule. If you have the exact same input everytime, you don't need any LLM. But if your inputs changes, has a mix of data types, long-context, then you will need a harness like this to do the job.

## Does it work?

As I mentioned above, I have been building it for the past 5 months to tune it to work in Fabric, on different types of tasks and different data sources. I also tested it on various external benchmarks.

*   [Data Agent Bench](https://github.com/ucbepic/DataAgentBench): This is a very complex benchmark with multiple databases, ill-formatted joins, unstructured text transformations. It's #21 on the leaderboard but 7th harness that's not prompt-tuned (i.e. no benchmark specific tuning), the ONLY OSS harness that's a Python library (not a code CLI) and does not use an expensive frontier models like Opus and Fable. (If someone wants to sponsor thousands of dollars, happy to use it to climb the leaderboard :D)
    

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/bf48c480-4161-4666-b5e6-de6f468b2738.png align="center")

*   [Agentic Data Bench](https://agenticdatabench.github.io/#/) : AgenticDataBench is a realistic and diverse data agent benchmark with fine-grained skill labels. It covers **344 data science tasks** across **15 domains**, built on **97 real-world datasets** (27.3 GB, 123.1M rows) with **433 fine-grained ground-truth labels**. On this benchmark, Fabric-RLM is #4. For this benchmark I used the open-weight MiniMax M3 model which was ~1/10the cost and beat Sonnet and Opus class models.
    

![](https://cdn.hashnode.com/uploads/covers/619d4cccfa52cd31fe52d25d/2466092f-8774-4d66-b912-2a2052b00a0b.png align="center")

*   [SpreadsheetBench](https://spreadsheetbench.github.io/) : SpreadsheetBench evaluates large language model agents' capabilities in manipulating complex real-world spreadsheets (Version 1). It's the ONLY non-commercial, open source solution in the top 20. My submission used a cheap MiniMax M3 model.
    

Obviously none of this matters if it does not work for users in Fabric on real data. So I would like to invite you to give it a try using these [example notebooks](https://github.com/pawarbi/fabric-rlm-core/tree/main/examples/notebooks). Over the next several weeks, I am excited to show all the capabilities and features.

## Limitations

*   Fabric-RLM is not for every task. If your input can fit in a context window, there is no reason to Fabric-RLM. You can still benefit from it because I have added skills specifically for working with data in Fabric. But if your task can be completed by text generation, Fabric-RLM is not for you.
    
*   I have added security features to block network so the harness cannot make `request` or other outbound calls and delete files and folders. We have seen recent incidents of agents escaping sandboxes. I witnessed that as well when for one of the benchmarks I was running locally the LLM was smart enough to search the internet to download labeled data and cheat ! In Fabric-RLM, the tasks are bound to specific inputs and outputs so this shouldn't happen but as with any agentic solution, exercise caution.
    
*   Not a limitation but worth noting. One of the stand-out features of Fabric-RLM is validators. You define how a task should be validated and Fabric-RLM will use that as a gate before calling `SUBMIT`. If the task does not meet the validation, it will repair and go through the recursive loop again. Depending on the task, this can consume tokens (I have intentionally designed the loops to use cached tokens to keep costs down) and may take a while. It also depends on your ability to define the validation checks. You can ask Copilot to do that.
    
*   The model matters for complex tasks. For all my testing and development I used MiniMax M3, gpt-5.6-luna and gpt-5.1. I recommend using 'high' reasoning for complex tasks.
    
*   It runs on one node, not Spark. You can use it in a PySpark notebook but it will be waste of resources. I have plans to spawn subprocesses. Early tests are positive so more soon.
    
*   You can work with large text and PDFs but it is not a replacement for RAG. For tasks requiring combing through large PDFs, you may run into some inaccuracies. You can add a skill to show RLM how to do similarity search and it works very well. But it's not built-in, by design. I wanted to focus more on structured data.
    
*   Fabric-RLM is not for data engineering jobs.
    
*   Some features are experimental. I experimented a lot so you may find some features such as adaptive engine, semantic learning, driver analysis etc. Those are not fully baked yet but I am hoping to make them more stable before ver 1.0.
    

I am thankful to the open-source community, especially DSPy and DuckDB. I hope Fabric-RLM will show we need more innovations in building harnesses and not just frontier models.

Lastly, I co-built this library with Claude Code, Codex, Github Copilot. Hopefully as you use it, you will find it is more than another vibe-coded project. I am not a software engineer but I put lot of thoughts in the API design, shape, features of the library. It's been a very fulfilling experience.

**Fabric-RLM is my personal open-source project and not an official Microsoft offering.**

## Resources:

*   [\[2512.24601\] Recursive Language Models](https://arxiv.org/abs/2512.24601)
    
*   [stanfordnlp/dspy: DSPy: The framework for programming—not prompting—language models](https://github.com/stanfordnlp/dspy)
    
*   [Trampoline-AI/predict-rlm: Production focused Self-harnessed LM runtime (RLM) that allows the LM to call its sub-lm with DSPy signatures. Define your inputs, outputs, and tools — the model handles its own control flow. Get fully interpretable trajectories and performance that scales directly with model improvements. Without context rot.](https://github.com/Trampoline-AI/predict-rlm)
    
*   [pawarbi/fabric-rlm-core: Slim core distribution of fabric-rlm - portable subprocess runtime for Recursive Language Models.](https://github.com/pawarbi/fabric-rlm-core/)
    
*   [pawarbi/fabric-rlm-benchmarks: Reproduction material for fabric-rlm on AIDABench: runs, full trajectories, both graders, and grader calibration.](https://github.com/pawarbi/fabric-rlm-benchmarks)
