Replace RAGAS synthetic ground-truth generation with Microsoft Foundry data generation service
This issue is for a:
- feature request
Summary
Replace the RAGAS-based synthetic ground-truth generator in evals/generate_ground_truth.py with the Microsoft Foundry data generation service (SimpleQnA job with FileDataGenerationJobSource), per Generate a synthetic evaluation dataset (preview).
Motivation
- RAGAS pulls in a heavy dependency chain (
ragas,langchain,rapidfuzz) and persists a 28MB knowledge-graph artifact (evals/ground_truth_kg.json). - The Foundry service is the first-party, supported path for bootstrapping evaluation datasets from reference material, and keeps the eval stack aligned with the rest of the Azure AI Foundry tooling this sample uses.
Current state (what RAGAS does today)
evals/generate_ground_truth.py: fetches chunks from the Azure AI Search index, builds a RAGASKnowledgeGraph, appliesdefault_transforms(LLM + embeddings), and generates N questions viaTestsetGenerator.- Output schema:
{"question": ..., "truth": ...}JSONL (evals/ground_truth.jsonl). - Each
truthstring embeds a page-level citation token, e.g.... [Northwind_Standard_Benefits_Details.pdf#page=7], parsed from the chunk'ssourcepage. evals/evaluate.pyconsumesquestion/truthand its custom metrics (AnyCitationMetric,CitationsMatchedMetric,CITATION_REGEX) depend on those citation tokens being present intruth. Any replacement must preserve them.
Key challenge: recovering page-level citations
The Foundry simple_qna recipe emits only query / ground_truth rows with no source/page pointer, but our citation metrics need [Doc.pdf#page=N] tokens in truth.
Chosen design — group index chunks by page, one job per page
Control the upload granularity so that one uploaded document == one source page, giving every generated Q&A an exact page citation with no best-match approximation:
- Read all chunks from the Azure AI Search index (
id,content,sourcepage). - Group chunks by
sourcepage(one PDF page of one file). - Reconstruct reading order within each page group by sorting on the integer suffix of the chunk
id. Note:searchmanager.py:617builds ids asf"{filename_to_id()}-page-{section_index + batch_index * MAX_BATCH_SIZE}"— that-page-Nsuffix is a global sequential chunk ordinal in document order, not the PDF page number (the real page lives insourcepage). Sorting by it restores intra-page order. - Concatenate ordered chunk text into one per-page document (comfortably clears the 1KB upload floor that a single ~1000-char chunk would fail).
- Sample a subset of pages (diversity/cost), upload each via
files.create(purpose="user_data"), poll toprocessed. - Submit one
SimpleQnADataGenerationJobper page (max_samples>= 15 floor). - Map
query->question,ground_truth->truth, and append that page's exact[Doc.pdf#page=N]citation totruth. - Aggregate across pages and downsample to the target count (e.g. 15 pages x 15 = 225 -> ~50).
This leaves evals/evaluate.py and the ground_truth.jsonl format untouched. Multimodal/figure citations (ground_truth_multimodal.jsonl) remain out of scope — they are hand-curated only.
Alternatives considered: whole-doc generation + AI Search best-match lookup (fewer jobs, but approximate citations) and single-chunk-per-document (exact but fails the 1KB floor and produces low-quality repetitive questions). Page-grouping was chosen as the best balance of exact citations, question quality, and job count.
Proposed upgrade path (phased)
Phase 1 — Infra: expose a Foundry project endpoint
- The SDK needs a Foundry (
services.ai.azure.com) project endpointhttps://<res>.services.ai.azure.com/api/projects/<proj>andazure-ai-projects>=2.2.0. TodayuseAiProjectprovisions the older hub-based AI project (infra/core/ai/ai-environment.bicep), andAZURE_AI_PROJECTis only the project name — not the endpoint the new SDK needs. - Add infra to provision/point at a Foundry project and output
AZURE_AI_PROJECT_ENDPOINT(full URL). Wire throughinfra/main.parameters.json,infra/main.bicepappEnvVariables, and both CI env sections (.azdo/pipelines/azure-dev.yml,.github/workflows/azure-dev.yml), per AGENTS.md. - Grant the running principal the Foundry User role on the project.
Phase 2 — Rewrite generate_ground_truth.py
- Keep index-chunk retrieval; add
group_chunks_by_page(...)(group bysourcepage, sort by id ordinal, concatenate). - Add
generate_ground_truth_foundry(...)usingAIProjectClient: sample pages, upload, submit per-pageSimpleQnAjobs, poll, resolveDatasetDataGenerationJobOutput, map fields, append exact page citation, downsample, writeground_truth.jsonl. - CLI:
--numquestions(target total), page-sample size,--groundtruthfile. Drop--kgfile(RAGAS-specific).
Phase 3 — Dependencies & cleanup
evals/requirements.txt: addazure-ai-projects>=2.2.0; removeragas,langchain,rapidfuzz.- Delete
evals/ground_truth_kg.json(28MB, RAGAS-only).
Phase 4 — Docs & tests
- Update
docs/evaluation.md"Generate ground truth data" section: new command, options, region/model prerequisites, citation notes. - Add unit tests mocking
AIProjectClient/ OpenAI Files API (mock at HTTP level per AGENTS.md).
Model requirement (gpt-5 upgrade)
Reuse the existing eval deployment (AZURE_OPENAI_EVAL_DEPLOYMENT) as the Foundry generator model, but upgrade it to a gpt-5 class model. The eval default is currently gpt-4.1 (infra/main.bicep:257) and the Foundry docs example uses gpt-4.1-mini, which is deprecated. Bump the eval model default to a gpt-5 model (e.g. gpt-5-mini, matching the chat model's move to gpt-5.4-mini) and confirm Responses API support (the simple_qna recipe requires a Responses-API-capable model).
Constraints / notes
max_samplesfloor is 15 per job — hence the oversample-then-downsample approach.- 1KB minimum upload size — page concatenation clears it; single chunks would not.
- Preview + region-limited. Synthetic data generation is supported in: UAE North, West US 3, North Central US, East US, West Europe, South Central US, Switzerland North, Sweden Central, East US 2, West US, France Central, South Africa North, Australia East, Japan East, UK South, Norway East, Poland Central, South India, Germany West Central, Italy North. (East US 2 and Sweden Central — the sample's common deploy regions — are both covered.)
Acceptance criteria
-
evals/generate_ground_truth.pygeneratesground_truth.jsonlvia the Foundry service with exact[Doc.pdf#page=N]citations preserved. -
evals/evaluate.pyandground_truth.jsonlformat are unchanged; citation metrics still pass. - RAGAS deps and
ground_truth_kg.jsonremoved. - Infra outputs
AZURE_AI_PROJECT_ENDPOINT; eval/generator model upgraded to gpt-5 class. -
docs/evaluation.mdupdated; unit tests added with mocked Foundry SDK.
Source: Azure-Samples/azure-search-openai-demo