May 26, 2026
Competition law experts conducting legal research must review extensive volumes of cases, decisions, and judicial reports to identify precedents and assess key elements in competition and merger cases. Although general research assistants such as Claude and ChatGPT and legal assistants such as SaulLM-7B and LegalGPT are increasingly used to assist legal research, they remain inadequate for competition law analysis: they lack specialized domain expertise, provide insufficient official citations, or hallucinate competition law cases. We propose Maat, a ReAct agent that orchestrates tools corresponding to different tasks of the research process. Designed iteratively with competition law experts, Maat grounds cases and findings in official sources using RAG for reliability, provides rich in-line citations, falls back to web search when database coverage is insufficient, and prompts the user for clarification when queries are ambiguous. Maat significantly outperforms all baseline assistants on case-specific tasks and performs within range of the top baseline on theoretical question tasks. The dataset used is available on GitHub1.
<concept> <concept_id>10002950.10003624.10003633</concept_id> <concept_desc>Applied computing Law</concept_desc> <concept_significance>300</concept_significance> </concept>
<concept> <concept_id>10003456.10003462.10003544</concept_id> <concept_desc>Computing methodologies Agentic systems</concept_desc> <concept_significance>300</concept_significance> </concept>
<concept> <concept_id>10002951.10003260.10003261</concept_id> <concept_desc>Information systems Question answering</concept_desc> <concept_significance>100</concept_significance> </concept> </ccs2012>
Competition protection promotes fair competition for a healthy economic environment [1]. Experts monitor markets, receive complaints, and initiate proceedings, requiring them to research cases across jurisdictions [2], [3], analyze hundreds of pages of case documents, and expand on competition law concepts [4] using sources such as the EU Competition Commission, Concurrences [5], and OECD reports [1]. Due to the time-consuming nature of this process, experts increasingly turn to AI assistants for reasoning, summarization, and question-answering [6]–[8]. These assistants include general research assistants such as ChatGPT [9] and Claude Sonnet [10], and multilingual legal assistants such as SaulLM-7B [11] and LegalGPT [12]. However, general research assistants are inadequate for competition law research: they hallucinate cases and case information [13], [14], possess shallow domain knowledge [14], [15], and lack page-level in-line citations for verification [16]. Legal assistants provide responses that are not grounded in official sources, omit granular citations, and perform poorly on questions requiring complex reasoning about legal principles [17], [18].
The literature has explored more specialized systems. Much of it focuses on case retrieval or question answering in isolation [19]–[23], while systems that integrate multiple tasks are deficient in fallback mechanisms and multi-turn clarification [24], [25]. Crucially, none of the reviewed systems is grounded in a dedicated competition law case database, leaving the domain’s cross-jurisdictional retrieval and source-level verification requirements unaddressed. To our knowledge, no prior academic work has developed and openly evaluated a purpose-built AI research assistant grounded in an official, dedicated competition law case database with cross-jurisdictional retrieval, structured tool routing, and expert evaluation.
To address these gaps, we introduce Maat, a multi-turn agentic system for competition law research, named after the ancient Egyptian goddess of law, order, and justice [26]. Designed iteratively with competition law experts, Maat has a structured tool-routing architecture where a ReAct-prompted agent [27] dynamically selects among specialized tools based on query type and database coverage. Our contributions are as follows:
a ReAct agentic system with a structured tool-routing architecture with explicit fallback logic when database coverage is insufficient and human-in-the-loop to clarify vague queries,
an interface to interact with the agentic research system conversationally, get page-level in-line citations from official sources, and inspect which tools are executed at each reasoning step,
a competition cases dataset consolidating EU regional cases and German national cases, enriched by metadata extracted by a Large Language Model (LLM) to power semantic case search,
a blind comparative expert evaluation demonstrating that Maat significantly outperforms all assistants under consideration in case-specific questions and performs within range of the top baseline in theoretical questions.
The dataset is constructed from two sources: the EU Competition Commission and the Bundeskartellamt, chosen for their jurisdictional and financial market relevance to Egypt.
For regional cases across the EU, there are two separate API endpoints for antitrust cases and merger cases [28]. Relevant metadata fields are selected, including case ID, description, violation, companies, sector, decision text link, date of release, and language of the text.
The dataset was then cleaned. Cases with empty links to the decision text were removed due to inaccessibility to the RAG pipeline. To normalize the violations, older articles were mapped to their newer counterparts. For example, for cartel cases, Article 85 EEC was mapped to Article 101 TFEU. The sectors of four cases were missing, which were filled manually. A comma-separated list of companies was extracted per case to be readable in Python. Only cases in English were kept. Other columns have passed data quality checks for missing values and normalized formats.
For the Bundeskartellamt cases listed in [29], there was no direct API or dataset source; the dataset had to be created. The strategy was to compile decision text links of Bundeskartellamt cases and extract metadata from the link structure and the decision texts using an LLM. The links of the case decision texts were studied for patterns. Cases from the Bundeskartellamt start with the prefix bundeskartellamt.de/SharedDocs/Entscheidung/ and were retrieved using the Wayback Machine API [30].
The same aforementioned case metadata was extracted. From the link structure, the case ID, violation, decision release year, and language could be deduced. The remainder of the fields, specifically the case title, the sector, and the list of companies
involved, were extracted by prompting the OpenAI gpt-4o-mini model via few-shot examples to guide the model. Since Germany is part of the EU, it also builds its economic classification system for market definition based on NACE [31]. To verify LLM extraction quality, a stratified random sample of 34 cases was manually reviewed; extraction accuracy was 85% for
case titles, 97% for sectors, and 84% for company lists.
The final combined datasets of both the regional EU cases and the national German cases consist of 1609 cases. It contains the following fields:
case_id: the unique identifier of the case,
case_title: a description of the case,
jurisdiction: the covered geography of the case (either EU or Germany),
violation: the legal basis under consideration in the case,
sector: the code and name of the NACE section that includes the sector in which the violation was committed,
companies: a list of companies involved in the case,
pdf_url: the link to the case decision text,
language: the language in which the case decision text is written,
decision_date: the date on which the case decision text was released.
To prepare the case decision documents for querying in the RAG pipeline, the cases dataset must be indexed appropriately. LlamaIndex [32] is the framework used to process the documents for Maat. The PDFReader loads a decision document from its respective link into a Document object. The SentenceSplitter chunks the
document into sets of Nodes, each consisting of approximately 1024 tokens, with 20 overlap tokens [33]. Each
Node contains document-related metadata, such as the link of the document and the page from which the Node comes. This document-level metadata is crucial for in-line citations. The chunks are embedded using the OpenAI
text-embedding-ada-002 model [34] and stored in the Qdrant vector database.
At the heart of Maat is a ReAct agent that mimics the expert research workflow through a thought-action-observation loop [27]. The
system prompt of Maat, developed by meta-prompting, describes the loop structure, the tools available, the routing rules, and the constraints.
The following tools were defined based on the observed expert research workflow: database_search, web_search, answer_case, answer_theoretical, and ask_clarification.
These tools can be chained together to completely answer the user’s question. For example, if the user asks "What was the market definition in the case AT.39398?" and the user has never referenced that case before, a
database_search operation is executed first. If the case was not found in the database, then this tool fails over to the web_search tool. If no such case was found on the web, the agent concludes that no such case exists. However,
if the case was found, then it is fetched into the memory layer of the agent. Next, the tool answer_case is called to answer the question asked by the user about that case. Finally, the answer is returned. At every turn, the agent reasons
about user intent, selects a tool, and observes the result.
The agent uses both in-session and external memory to infuse the appropriate context in its prompts. In in-session memory, the agent records its ReAct loops in the scratchpad to keep track of its line of thought when making new decisions.
The chat history is also stored to help the agent understand its interactions with the users. Finally, the cases retrieved so far along with their metadata are stored in session_cases. session_cases are crucial to answer questions
such as "What was the violation in the first case?"; the first case recorded is fetched and the question about it is answered using RAG [36] as explained in answer_case. The main external memory component used is the vector database which holds most of the EU regional and German national cases.
If a search intention is detected in the query, the agent first tries to find matching cases in the database. The query itself is translated into a query vector composed of six dimensions that are a subset of the fields in the database:
case_id, case_title, jurisdiction, violation, sector, and companies.
The extraction of those dimensions from the query is performed by prompting the OpenAI gpt-4o-mini model, which is a recommended efficient model in terms of both cost and latency for structured data extraction [37]. The prompt includes few-shot examples per field; schema-constrained dimensions (jurisdiction, violation,
sector) are validated against predefined values.
Since all six dimensions of the query vector are columns in the cases database, the query vector is used as a filter to match relevant cases to the query itself. The filtering of case ID is straightforward, since matches should have the exact specified
ID in the query vector as the case. Exact matches are also found for the jurisdiction, violation, and sector fields, whose values come from a predefined schema. Companies are matched by checking if the companies list from the query vector is a subset of
the companies list of the case under consideration. Lastly, case titles are matched using cosine similarities. Case titles of both the query vector and the case under consideration are embedded using the OpenAI text-embedding-3-small model,
whose family has been used by other AI legal systems in [38] and [39]. If the cosine similarity of the embeddings exceeds the threshold of 0.85, the titles are considered a match. The threshold was tuned by testing on the case question dataset described in
the evaluation section. If there are more than five cases in the result set, the cosine similarity of the query embedding and the case embeddings are computed, and only the top five cases are kept.
Sometimes, cases are not captured because they have been released before the database is refreshed or because there are no snapshots on the WayBack Machine yet [30]. In this case, the agent searches the web for fresh cases.
As with database_search, the user question first gets translated to the query vector comprising the same six dimensions, which will be used to filter web results. To perform the search, Perplexity Sonar [40] is prompted to answer the user question by providing up to five recent cases using its online search feature. Because assistants may hallucinate [13] and unofficial sources used by assistants may misinterpret cases, the web is searched for each title in the result set retrieved by Perplexity
to find matching official sources from the European Competition Commission or the Bundeskartellamt, depending on the jurisdiction of the query. The Serper Google Search API [41] is used to search the web to gather official links for each of the retrieved titles. Titles with no official sources found are assumed hallucinated and are removed from the result set. To ensure the remaining case
titles are indeed relevant to the initial query, the descriptions of the official sources for each title are translated into a six-dimensional query vector for post-search filtering. The query vector of each case title is checked against the query vector
of the user question. If there is a mismatch, the corresponding case title is removed from the result set. In the end, the titles remaining are of existing cases whose metadata is a match for the original user query.
| General Research LLMs | Legal LLMs | ||||
|---|---|---|---|---|---|
| 3-4 (lr)5-6 Task Category | Maat | Claude Sonnet 4.6 | GPT-5.5 | LegalGPT | SaulLM-7B |
| Theoretical | 2.9 | 3.5 | 2.4 | 2.2 | 1.1 |
| Case-specific | 4.6 | 2.4 | 2.0 | 2.1 | 0.7 |
When a user asks a question about a specific case, the agent first checks if the case in question exists in the session_cases list. If not, a search operation is invoked before continuing to retrieve the case information. The chunks of the
case in question are loaded in active_case from the database. Then, the RAG [36] pipeline is invoked. The case chunks are
ranked according to the cosine similarities of each chunk and the embedded user question. Based on experimentation using the question bank described in the next section, the number of top chunks to be retrieved was set to \({8}\). The texts, pages, and links of the top \({8}\) chunks are injected into a meta-prompted question-answering prompt alongside the question and chat history to generate the final answer with
in-line citations.
When a user asks a general question about the theory of competition law, such as "What are the forms of abuse of dominance?", answer_theoretical is invoked. A prompt was engineered iteratively with competition law experts to
construct the knowledge base around key concepts and terminologies of competition law. Perplexity Sonar was the base LLM of choice due to its deep research capabilities [40]. The question is injected into this prompt to guide Perplexity’s reasoning depth and direction. Furthermore, the domain of search was restricted to a set of official and reliable sources
only provided by the experts. At the end of the response, based on the chat history, Maat also recommends questions for deeper research in its role as a brainstorming assistant.
If the question is unclear, the scratchpad, chat history, and question are analyzed to assess the information gap that needs to be addressed before the agent answers the question. For example, if the user asks "What was the market definition of the
case?" and there are multiple retrieved cases by a search operation, then the agent will first ask the user to clarify which case is the question about. The human-in-the-loop then provides enough clarity for the agent to answer the
question.
Maat is a web application accessible via standard browsers without additional installation. The Flask library is used to design an interactive user experience with a dedicated user input box and submit button to enter questions. As the user question is passed to the agent in the backend, the user can see a progress indicator, "The assistant is thinking..." that adjusts to show which tools the agent calls during the question-answering process. The user can track the conversation in the scrollable chat pane and explore in-line citations for each part of the answer, allowing for a smooth answer verification experience.
A blind comparative evaluation was conducted in which the performance of Maat was compared against state-of-the-art general research assistants, ChatGPT (GPT-5.5) [9] and Claude Sonnet 4.6 [10], and specialized multilingual legal research assistants, SaulLM-7B [11] and LegalGPT [12]. The experts prepared two question banks: fifty theoretical questions (e.g., "Explain price-fixing") and twenty case-specific questions (e.g., "List abuse of dominance cases in the financial sector"). Seven questions from the theoretical test bank and eight questions from the case-specific test bank formed the test set; the remainder were used for prompt engineering and hyperparameter tuning. Responses were scored from 1 (poor) to 5 (excellent) based on five criteria, each worth one point: coherence of legal logic, usage of correct legal taxonomy, illustration of concepts, quality of references, and appropriate inclusion of in-line citations. Nine competition law experts contributed to the evaluation process.
Table 1 reports average expert ratings per task type. Maat substantially outperformed all baselines on case-specific questions and performed within range of Claude Sonnet 4.6, the strongest baseline, on theoretical questions; a Friedman test confirmed these differences are statistically significant (\({p < 0.05}\)). Maat and Claude Sonnet 4.6 both demonstrated structured legal reasoning and illustrative examples, though both lacked citation granularity and source diversity, with Claude Sonnet 4.6 performing marginally better on these criteria. On case-specific questions, legal-domain assistants performed poorly, with experts observing hallucinated cases, unofficial sources, missing in-line citations, and weak competition law reasoning. This reflects both slower research cycles in legal AI and the lack of competition law specialization of these models. General research assistants retrieved more relevant cases but shared the same citation and reasoning limitations. Maat addresses these shortcomings by design: it retrieves case documents directly from official sources, generates answers via RAG with page-level in-line citations, and applies structured prompt engineering for principled competition law reasoning.
We introduced Maat, a ReAct agent for competition law research that integrates database retrieval, web fallback, case and theoretical question-answering, and human-in-the-loop clarification. Grounded in Bundeskartellamt and EU Commission cases with in-line citations for verifiability, Maat performed within range of the top baseline in theoretical question tasks and outperformed all baselines across case-specific tasks. Future work will extend jurisdictional coverage and enhance theoretical source diversity and citation granularity.
The authors thank the EU Competition Commission and the Bundeskartellamt for making the cases publicly accessible, and Ahmed Abdelaal, Abdelrahman Abdelhalim, Ahmed Kamel, and Waad Hegazy for paper review and involvement throughout the development of Maat.
The authors utilized Claude and ChatGPT to debug code written for Maat. Furthermore, the authors used the aforementioned GenAI tools for grammar checking, minor paper structuring, and draft revisions. All GenAI responses were checked and edited for accuracy and appropriateness. GenAI was not used to contribute to the intellectual content. The authors claim full responsibility for all contributions and results of this publication.
https://github.com/baahmed/maat-dataset↩︎