24 min read

GraphRAG benchmark on one GPU: 268 communities, 12 hours, one answer in five

GraphRAG benchmark on one GPU: 268 communities, 12 hours, one answer in five
Read aloud by your browser. Nothing is downloaded.

Part of RAG & Vector Databases — retrieval-augmented generation and vector search.

✅ REAL DATA — Every number here comes from one measured run on my own hardware · GraphRAG 3.1.2 in podman · Qwen3.8-27B (Q4_K_XL) on llama.cpp/Vulkan, both answering and marking · AMD Radeon PRO W7900 48 GB · Windows 11 · 33 NVIDIA filings from SEC EDGAR · 40 questions whose answers sit in more than one filing.

This GraphRAG benchmark asks whether Microsoft’s community graph earns the day it takes to build. I indexed 33 NVIDIA SEC filings with a local 27B model, which cost 12.41 hours of model time and produced 1,400 entities, 3,616 relationships and 268 communities. Then I asked 40 cross-document questions whose answers sit in more than one filing. Local search retrieved every evidence document for every question — and answered one in five correctly.

Key takeaways
  • Local search reached 100% strict document recall and answered 20% of questions correctly. Basic search, the control that ignores the graph, answered 17.5% while retrieving 40 points less.
  • Building the graph costs three separate bills with three different denominators: 5.31 h over 627 chunks, 4.03 h over 5,079 entities and relations, 3.07 h over 268 communities. Only the first is knowable before you start.
  • The most expensive filing to graph is the proxy statement, not the annual report: 38.0 s per chunk against 24.0 s, at an identical 1,200-token chunk budget.
  • The corpus’s main subject exists twice. NVIDIA and NVIDIA CORPORATION are the two highest-degree nodes, and only 153 of their 832 neighbours are shared.
  • Thirty integration findings, with verbatim error messages, before the first measurement was worth keeping.

What a GraphRAG community graph is supposed to buy you

GraphRAG does not stop at extracting entities. After it builds the graph, it clusters that graph into communities and writes a report for each one, at several levels of a hierarchy. Those reports are what global search reads instead of text chunks. The promise is a system that can answer questions no single passage contains, because somebody — the model, during indexing — already wrote the summary.

That promise costs real time. In this run the community layer alone took 3.07 hours, on top of the 9.34 hours the graph itself needed. Therefore the question is not whether the graph works. It is whether the hours come back as correct answers.

Chart of 268 GraphRAG communities across five hierarchy levels with their size distribution
268 communities over five levels: 23 at level 0, then 86, 97, 55 and 7. Global search reads levels 2 and above, which is 206 of them for every question asked.

The shape of that hierarchy decides what global search costs later. Level 0 holds a few broad communities, the middle levels hold the most, and level 4 holds seven small ones. Since the search reads a whole level rather than a selection, the width of the middle is the query bill.

The benchmark is built to answer exactly that. Every filing comes from NVIDIA’s EDGAR archive, downloaded once and never edited. Forty questions, each tagged with a knowledge dispersion pattern and with the number of filings needed to answer it. No question can be answered from a single passage. In addition, five of them have no answer in the corpus at all, so a system that never refuses cannot score well.

The GraphRAG benchmark setup, command by command

Microsoft publishes GraphRAG as a Python package, not as an image. As a result there is no podman pull graphrag, and the first job is building one. In practice the image pins version 3.1.2 and adds exactly three files of ours: the configuration, an entrypoint and an HTTP wrapper.

# Containerfile - GraphRAG 3.1.2, nothing else added to the framework
FROM docker.io/library/python:3.12-slim
RUN pip install --no-cache-dir graphrag==3.1.2

WORKDIR /opt/nvda
COPY serve.py settings.yaml entrypoint.sh /opt/nvda/
RUN chmod +x /opt/nvda/entrypoint.sh

# GRAPHRAG_ROOT holds input/, output/, cache/, logs/ and prompts/.
# It is a volume, so an interrupted index survives a container restart.
ENV GRAPHRAG_ROOT=/data SERVE_PORT=9622 PYTHONUNBUFFERED=1
EXPOSE 9622
VOLUME ["/data"]
ENTRYPOINT ["/opt/nvda/entrypoint.sh"]
# Build the image, then start the container with the host address injected
podman build -t nvda-graphrag:3.1.2 -f Containerfile .

# Inside a podman WSL machine, host.docker.internal points at the WSL bridge,
# not at Windows. The real address comes from the machine's default route.
$hostIp = (podman machine ssh "ip route | awk '/default/ {print `$3}'").Trim()

podman run -d --name nvda-graphrag 
  --add-host "llmhost:$hostIp" 
  -v nvda-graphrag-data:/data 
  -p 9622:9622 nvda-graphrag:3.1.2

Why the GraphRAG CLI is not enough for a benchmark

At first glance the obvious command is graphrag query. It prints the answer and nothing else. However, a benchmark needs to know what the system retrieved, not only what it said, because recall computed from citations measures the model’s willingness to cite rather than the retriever’s reach.

The context comes back only through the Python API, as the second element of a tuple:

# graphrag.api returns (response, context_data); the CLI discards the second.
response, context = await graphrag.api.local_search(
    config=config, entities=entities, communities=communities,
    community_reports=reports, text_units=text_units,
    relationships=relationships, covariates=None,
    community_level=2, response_type="Multiple Paragraphs", query=question,
)

So the image ships serve.py, a thin HTTP layer over graphrag.api that returns the answer together with its context. That is lesson one from the previous episode, applied before the first measurement rather than after it.

Resolving retrieval back to documents

In addition, one more problem appears immediately. No element of GraphRAG’s retrieval names a file. Entities, relationships and community reports all carry text_unit_ids, and only text_units carries a document_id. Consequently the wrapper builds a map once, for four kinds of row, and resolves every context line through it.

Watch out — the short human_readable_id values are unique only inside their own table. An entity 58 and a relationship 58 both exist. The kind therefore has to be taken from the context table’s name before the id is looked up, or the map returns the wrong document with total confidence.

The GraphRAG settings that matter

GraphRAG 3.x moved from a single models: block to completion_models: and embedding_models:, with LiteLLM underneath. A local llama-server therefore connects as an ordinary OpenAI-compatible endpoint.

completion_models:
  default_completion_model:
    type: litellm
    model_provider: openai
    model: Qwen3.8-27B-UD-Q4_K_XL.gguf
    api_base: http://llmhost:8084/v1
    api_key: local-llama-server   # the validator wants a non-empty value
    call_args:
      temperature: 0.0
      max_tokens: 2048

concurrent_requests: 1   # one model-server slot; the default of 25 queues inside it
input:
  type: text
  file_pattern: ".*.md$$"   # the reader defaults to *.txt only
vector_store:
  vector_size: 768           # nomic-embed, not OpenAI's 3072

concurrent_requests: 1 is the only performance setting that departs from the default, and it departs for a measurable reason. The generation server has a single slot. With the default of 25, the queue forms inside llama-server and GraphRAG measures its own waiting as indexing time.

What local, global and basic search actually read

The three search modes differ in one thing, and everything else follows from it: the unit they retrieve. Local search retrieves entities, global search retrieves community reports, and basic search retrieves text chunks. Consequently their costs are not variations of one number — they are different arithmetic.

localglobalbasic
Unit of retrievalentity neighbourhoodcommunity reporttext chunk
Graph tables usedentities, relationships, text units, reportscommunities, community reportsnone
How content is chosenvector similarity to entity descriptionsevery report at the levelvector similarity to chunks
Model calls per question1~206 map + 1 reduce1
Median latency26.7 ssee the results table

In this run, local search pulled a median of 17.6 entities, 3.1 relationships, one community report and 4.1 source chunks per question, then made a single generation call over a 12,000-token context. Global search takes a different route entirely. With dynamic_community_selection=False it reads every community report at level 2 and above — 23 + 86 + 97 = 206 of this corpus’s 268 communities — scoring each batch for relevance in a map step before one final reduce call writes the answer.

Why global search costs twenty times more

That is why global search costs what it does. Local and basic scale with the question, because they take a fixed top-k. Global scales with the graph, because the question does not change how many reports have to be read. As a result, the hours spent building the community layer are not paid once at indexing time. They are paid again on every query.

Meanwhile, basic search is the control. It receives no graph table at all, which is exactly the point: same index, same model, same prompt, no graph. Without it there is no way to say how much of a result came from the graph and how much came from the corpus simply being well chunked.

# The three calls, as the benchmark wrapper makes them. Note what basic omits.
if method == "global":
    response, context = await api.global_search(
        community_level=2, dynamic_community_selection=False, **common)
elif method == "local":
    response, context = await api.local_search(
        text_units=..., relationships=..., community_level=2, **common)
elif method == "basic":
    # no entities, no relationships, no communities - vector search over chunks
    response, context = await api.basic_search(
        config=config, text_units=..., response_type=..., query=question)

What the GraphRAG benchmark build actually cost

The headline is 12.41 hours of model time. That number is useless on its own, because it hides the fact that the pipeline pays three separate bills, and each one counts something different.

Bar chart of GraphRAG build cost split into extraction, description summarisation and community reports
Three stages, three denominators. Only the first — 627 chunks — can be known before the run starts.
StageCountsUnitsPer callModel time
extract graphtext chunks62730.5 s5.31 h
summarise descriptionsentities and relations5,0799.2 s4.03 h
summarise communitiescommunities, 5 levels26841.4 s3.07 h

Notably, the corpus fixes the first denominator. It does not fix the other two. Extraction produced those 5,079 entities and relations; summarisation produced those 268 communities. In practice that means an estimate built from page count prices roughly one third of the run.

One detail matters for anyone reading the middle column. Only 31% of the description steps actually called the model, because 652 of the 1,400 entities appear exactly once in the corpus and a single description has nothing to merge. A projection taken early in that stage therefore overestimates: the queue starts with the most-repeated entities and ends with singletons.

The most expensive filing is not the longest one

Meanwhile, every chunk gets the same 1,200-token budget, so a chunk of an annual report and a chunk of a proxy statement look identical going in. They do not cost the same coming out.

Scatter chart of GraphRAG extraction cost per chunk coloured by SEC form type
The two humps in the running median are the two proxy statements. The run does not warm up or slow down; it moves through documents of different entity density.
SEC formChunksMedian per chunk
DEF 14A (proxy statement)11538.0 s
8-K (current report)2926.0 s
10-K (annual report)21025.0 s
10-Q (quarterly report)27324.0 s

In essence, a proxy statement is a list of people, committees, roles and compensation figures. The extractor therefore pulls several times more entities from it than from a paragraph of narrative about data-centre demand. As a result, graph build time scales with entity density per page, not with page count — which is worth knowing if your own corpus holds registers, contracts or catalogues.

Free ebook

Free AI Video, Generated Locally

Working scripts and measured benchmarks. Free.

No spam. Unsubscribe at any time.

GraphRAG’s progress counter never names a document, so this table needed a join to text_units.parquet by row order. That assumption is tested rather than assumed: document identity explains 0.148 of the variance in per-chunk cost, against a maximum of 0.113 across 200 shuffles of the same labels.

Resuming an interrupted GraphRAG build

Line chart of GPU and host memory during a twelve-hour GraphRAG index build
Across 3,034 samples the build held a median 34.0 GiB of GPU memory and peaked at 0.86 GiB inside the container. The graph pipeline is cheap; the model server it talks to is not.

That split matters when you size the machine. GraphRAG itself is a Python process doing bookkeeping between model calls, so a container with two gigabytes runs it. The 48 GB card is fully occupied for twelve hours by the model answering the extraction prompts, which is the resource you are actually renting.

As it happens, the build was interrupted once, by the model server dying six hours in. Nothing was lost. GraphRAG caches every model call by prompt hash under /data/cache, so a resumed run replays what it already paid for.

Overall, measured across the whole build: a replayed step costs 0.34 s against 18.0 s for a live call, across 4,761 replayed and 2,478 live steps. That is a factor of 53. On a multi-hour local pipeline, prompt caching is not an optimisation; it is what makes the run feasible at all.

Watch outmax_tokens is part of GraphRAG’s cache key. Raising it after a crash invalidates every cached call, so a mid-run “fix” can cost the entire build a second time. Set generation limits before you start, not after the first error.

GraphRAG benchmark results

All three search modes ran against the same finished index, one after another, on the same single-slot server. Local search took 18 minutes for the 40 questions and basic search took 20. Global search took 7.1 hours. Basic search is the control here: it gets no graph table at all, so whatever separates it from the two graph modes is what those 12.41 hours of building bought.

Measurelocalglobalbasic (no graph)
Questions asked404040
Strict document recall
questions where every evidence document was retrieved
100.0%100.0%60.0%
Answers judged correct20.0%15.0%17.5%
Answers judged correct, of 40867
Answers partly correct21819
Answers plainly wrong233
Questions refused92311
Figures exactly right
of the numeric questions the system attempted
52.0% (13 of 25)47.1% (8 of 17)29.0% (9 of 31)
Answer-or-refuse decisions right62.5%55.0%82.5%
Answers grounded in retrieval19 of 406 of 4025 of 40
Answers not grounded144
Answers containing an invented number000
Median latency26.7 s636.2 s27.8 s
Hours to build the index
one build, shared by every mode of this framework
13.08 h13.08 h13.08 h
Scorecard total, frozen rubric65.357.946.3
Bar chart comparing GraphRAG local, global and basic search on recall, correctness, figures and refusal accuracy
Four measures, three modes, one index. Document recall separates the graph modes from the control. Answer correctness does not.

One sentence carries the whole table. The graph bought exactly one correct answer in forty — eight for local search against seven for the control that ignores it. Those 12.41 hours of model time are the price of that one answer.

Local search: every document retrieved, one answer in five

Local search walks the entity neighbourhood around the question. On these 40 questions it reached 100% strict document recall: for every single question, every filing the golden set names as evidence was in the retrieved context. The answers tell a different story.

However, the verdict breakdown explains the gap better than the headline does. The judge marked 8 answers correct, 21 partial, 9 abstentions and only 2 wrong. In other words, the system is not inventing things. It is answering half of each question.

Global search: twenty-four times the cost, fewer right answers

Global search is the mode the community layer exists for. It reads 206 community reports per question, scores them in a map step and writes one answer in a reduce call. On this corpus it lost to local search on every accuracy measure, and it cost 24 times more time per question.

The behaviour behind that number is more interesting than the number. Global search refused 23 of the 40 questions, against 9 refusals from local search. It reads summaries rather than text, so it finds a specific figure less often and says so. That caution is badly aimed, though: only 55.0% of its answer-or-refuse decisions were right, which is worse than local search manages.

Stacked bar chart of blind judge verdicts for GraphRAG local, global and basic search
Global search turns most of its questions into abstentions. Wrong answers stay rare in all three modes — the failure here is incompleteness, not invention.

One measurement limit belongs with these numbers. The blind judge reads each answer next to that answer’s own retrieved context, capped at 160,000 characters. For 17 of the 40 global-search questions the context hit that cap, because 206 community reports do not fit. Correctness and numeric agreement do not depend on it, since those compare the answer with the golden set. The grounding score for global search rests on a truncated view of its evidence, and it therefore reads as a lower bound.

Basic search: the control that beats the graph

Basic search receives the same index without any graph table. It retrieved the evidence for only 60% of questions — a 40-point gap against both graph modes — and still answered 17.5% correctly, against local search’s 20.0%.

The third row of that comparison is more striking still. Basic search has the best answer-or-refuse accuracy in the episode, at 82.5%. The system that sees the least is the one that most often knows it does not know. Meanwhile, both graph modes see everything and answer anyway.

Both effects point the same way. The graph mode gains one correct answer over the control and gives up twenty points of refusal accuracy to get it. More evidence in the context makes the system bolder, not better calibrated — and boldness is expensive on the five questions the corpus cannot answer at all.

Where the community graph actually pays

The gain does not spread evenly across the benchmark. Broken out by dispersion pattern, correctness looks like this. Each pattern holds five questions, so one step is one answer.

Patternlocalglobalbasic
D1 table ↔ narrative20%20%20%
D2 footnote dependency0%0%40%
D3 definition → use40%40%40%
D4 granularity mismatch0%0%0%
D5 event → consequence0%0%0%
D6 governance vs operations20%60%0%
D7 retrospective restatement20%0%0%
D8 absence (refusal is correct)60%0%*40%

The graph wins in two places only. Global search takes D6, governance against operations, at 60% against the control’s zero. Local search takes D7, retrospective restatement, at 20% against zero. Both are patterns whose answer is descriptive and spread across filings. Where the answer is a figure in a footnote, the graph adds nothing — and on D2 it actively hurts, since plain vector search scores 40% there and both graph modes score zero.

* Read that zero carefully. D8 questions have no answer in the corpus, so refusing is the correct behaviour. Global search refused all five — a perfect score in substance. The judge records those as ABSTAIN, and the correctness column counts only CORRECT, so exemplary behaviour surfaces as 0%. The abstain_correct_pct metric exists for exactly this case. A per-pattern table published without this note would say the opposite of what happened.

Retrieval, by contrast, no longer separates anything. Both graph modes hit 100% on all eight patterns, so the chart below stays flat where the interesting variation used to be.

Grouped bar chart of strict document recall per dispersion pattern for three GraphRAG modes against the naive baseline
The graph modes retrieve every evidence document for every pattern. Document recall has stopped discriminating between systems; the control and the E-02 baseline are the only series with any shape left.

Why it found everything and still answered wrong

In short, two mechanisms produce that result, and both are visible in the data rather than inferred from it.

One company, two nodes

First, the two highest-degree nodes in the entire graph are NVIDIA and NVIDIA CORPORATION. They are the same company. Together they have 832 distinct neighbours, of which only 153 are shared — a Jaccard overlap of 0.184.

Graph figure showing NVIDIA and NVIDIA CORPORATION as two hubs whose neighbourhoods barely overlap
Four fifths of the neighbourhood is reachable from one name only. Local search takes ten entities, so it picks one variant and leaves several hundred of the other’s relations outside the context.

This is not a bug in one implementation. Asking a language model to name entities chunk by chunk produces whatever surface form each chunk used, and nothing in the pipeline afterwards decides that two of them are one company. An LLM-built graph with no separate entity-resolution step therefore splits the corpus’s main subject — and in filings, that subject is what almost every question is about.

Too much evidence is its own failure mode

With 100% recall, local search puts a median of 32 of the corpus’s 33 documents into context. Document precision is therefore 5.1%, and the right passage is a needle in a stack of the system’s own sources.

For example, one question shows the consequence exactly. It asks for total assets at 25 January 2026 and for inventory provisions recorded in cost of revenue for fiscal 2026. The gold answer is $206,803 million and $4.0 billion. GraphRAG got the assets right and reported $2.3 billion in provisions. That figure was genuinely in the retrieved context — in this sentence:

“Our maximum loss exposure under these investments, including invested and future committed amounts, was $2.3 billion as of April 26, 2026.”

It describes a different concept, from the following quarter. Only the shape matched: billions of dollars, near a balance-sheet topic. Therefore the failure is not retrieval and not hallucination. The evidence was there, alongside a great deal of plausible-looking company.

That is worth stating plainly, because it is the opposite of the usual advice. GraphRAG answers badly here not for want of evidence, but because it hands the model too much to choose between, with nothing in the context marking which quarter or which concept each figure belongs to.

GraphRAG benchmark pitfalls and limits

In total, the episode logged 30 integration findings before any number was worth publishing. These five are the ones most likely to cost you a day.

  • A single $ in settings.yaml aborts the start, including inside a comment: ValueError: Invalid placeholder in string: line 68, col 25. GraphRAG runs the whole file through string.Template before parsing it. Write $$.
  • The text reader takes only *.txt. A corpus of .md files produces an empty index, zero documents and zero errors.
  • The default vector size is 3072. A local embedder returning 768 fails with Vector for document '...' has dimension 768, but index 'entity_description' is configured with vector_size 3072 — and it fails after entity extraction, the most expensive step.
  • A client-side max_tokens is a quality parameter, not a cost guard. At 2048 it collides with GraphRAG’s own 2000-token report body: one community report of 268 failed to parse and that community has no report at all.
  • Numpy arrays break the usual list(value or []) idiom. The id columns in the parquet files are arrays, so that idiom raises instead of returning a default — and a system whose document maps are silently empty still answers questions, while reporting 0% recall.

Two more traps belong to the hardware rather than to GraphRAG, and a twelve-hour local run meets both. First, the AMD driver watchdog reset the card at 15:01 and took the model server with it — seven LiveKernelEvent a1000001 entries in the Windows event log, and llama-server.exe dying as BEX64 in ucrtbase.dll. The card logged 42 such resets in six hours; only one of them killed the process. Second, the embedding server died unnoticed at some point after 15:05, because global search does not use embeddings and nothing asked for it for seven hours.

Neither had a cause until the model server started writing a log file. One line in the start script — --log-file instead of --log-disable — turned the second crash from a mystery into a diagnosis. On a pipeline this long, that is the cheapest insurance available.

GraphRAG benchmark FAQ

Does GraphRAG work with a local model instead of OpenAI?

Yes. GraphRAG 3.x routes model calls through LiteLLM, so a local llama-server connects as an ordinary OpenAI-compatible endpoint with model_provider: openai and an api_base pointing at your host. The API key must be non-empty, because the config validator checks it, but the server ignores its value.

How long does GraphRAG take to index 33 filings on one GPU?

12.41 hours of model time on an AMD W7900 with a 27B model at concurrent_requests: 1: 5.31 h of entity extraction, 4.03 h of description summarisation and 3.07 h of community reports.

Can you resume a GraphRAG index after a crash?

Yes, and it is cheap. Every model call is cached by prompt hash in the project’s cache/ directory, so re-running the pipeline replays finished work at 0.34 s per step instead of 18.0 s. There are no per-document states to repair — the pipeline simply runs again and hits the cache.

What is the difference between local, global and basic search?

They differ in what they retrieve, and the numbers make the difference concrete. Local search pulled a median of 17.6 entities per question and called the model once. Global search reads 206 community reports per question — every report at level 2 and above — through a map step and one reduce call. Basic search receives no graph table at all and is therefore the control: the same index without the graph.

Why does GraphRAG retrieve almost every document in the corpus?

Because its unit of retrieval is an entity, not a chunk. An entity description is built from text units across many filings, so resolving retrieved entities back to documents touches nearly all of them. Document-level recall consequently stops discriminating between systems, and precision becomes the number to watch.

How slow is GraphRAG global search?

On this corpus, a median of 636.2 seconds per question — 24 times local search, and 7.1 hours for all 40 questions. The cause is structural, not a tuning error. With dynamic_community_selection=False, global search reads every community report at level 2 and above, which here means 206 reports scored in a map step before a single reduce call. Its cost therefore scales with the size of the graph, not with the question, so a bigger corpus makes every query slower.

Do GraphRAG community reports pay off?

On a corpus of financial filings, measured here: no. The community layer cost 3.07 hours to build and 636 seconds per query, and global search answered 15.0% of questions correctly against 17.5% for basic search, which ignores the graph entirely. It won one pattern — governance against operations, at 60% against zero — where the answer is descriptive and spread across filings. Where the answer is a figure from a table or a footnote, the summaries have already dropped it.

Summary: what the GraphRAG benchmark measured

This GraphRAG benchmark cost 12.41 hours of model time and produced a graph of 1,400 entities, 3,616 relationships and 268 communities across five levels. Three search modes then ran against it. Local search retrieved the evidence for all 40 questions and answered 20% correctly. Global search, the mode the community layer exists for, answered 15% and took 24 times longer. Basic search, which ignores the graph, answered 17.5% while retrieving 40 points less.

The series scorecard puts those three at 65.3, 57.9 and 46.3 points on a rubric frozen before this framework ran. Ranking frameworks against each other is the final episode’s job; this is GraphRAG measured against fixed anchors.

How to read a GraphRAG benchmark score of 65.3

That number is not the share of questions answered correctly. It is a weighted sum of eight dimensions, and on this rubric retrieval is worth 40 points while answer correctness is worth 20. A system that puts the evidence in front of the model and fails to use it therefore scores above one that never found the evidence — because the first problem is a prompt away, and the second needs the index rebuilt.

Where the 65.3 points came from

Read row by row, GraphRAG local search means this on the 40 questions:

DimensionWhat it means on the 40 questionsPointsOf
Cross-document retrieval (D4-D8)all evidence retrieved for 25 of the 25 multi-document questions30.030
In-document retrieval (D1-D3)all evidence retrieved for 15 of 15 single-document questions10.010
Answer correctness (blind judge)8 of 40 answers fully correct4.020
Numeric agreement13 of 25 numeric questions hit the figure within 2%5.210
Knowing when not to answer25 of 40 answer-or-refuse decisions right6.210
Grounding in its own retrievalshare of answers standing on evidence the system itself retrieved5.510
Cost to index the corpus13.08 h once; the anchors are 24 h for zero and 0 h for ten2.35
Cost to answer one questionmedian 26.71 s; the anchors are 300 s for zero and 1 s for ten2.15
Total65.3100

So 40 of those 65.3 points came from finding the right documents, and 4 of a possible 20 from answering — eight questions out of forty. Read that way, a high total is not a recommendation. It is a statement that the evidence problem is solved and the answering problem is not.

Finally, the lesson is not that GraphRAG is worse. It is that retrieval stopped being the bottleneck some time ago. A system that finds every relevant document and still answers one question in five is telling you where the remaining work sits — in entity resolution, in period disambiguation, and in giving the model less rather than more.

Everything behind these numbers

The full result set ships with this article rather than as a summary you have to trust. Inside the bundle below, appendix-40-answers.html lists all 40 questions with the gold answer, what each of the three search modes replied, the judge’s verdict and its reasoning, whether the figures matched and how long each answer took. It opens in a browser; nothing has to run first.

The download bundle carries the Containerfile, the HTTP wrapper, the adapter, the shared benchmark runner, the evaluator, the chart scripts and every raw result file, plus a SHA-256 manifest so you can confirm the inputs are the ones measured here. Its folder layout matches the commands above, so they run verbatim after unzipping: nvda-rag-e04-graphrag.zip.

Not investment advice. NVIDIA filings are test material for a retrieval system, nothing more.

Companion code — the fragments above are the working parts: the --add-host line that lets a container find the Windows host, the serve.py call that returns retrieval alongside the answer, and the three settings that decide whether an index finishes at all. The commands in this article reproduce the result from scratch.

Free ebook

Free AI Video, Generated Locally

Run Wan 2.1 in ComfyUI on your own GPU — the scripts I use, measured times, sample clips. No cloud, no API keys.

No spam. Unsubscribe at any time.

Did this article help?
Artur Poniedziałek
Artur Poniedziałek
IT Expert & Project Manager
🤖 AI ⚡ PM 🐍 Python 🖥️ Local AI

IT Expert & Project Manager with 15+ years of experience. Exploring practical AI applications — from local LLMs and RAG systems to workflow automation. Writing to share knowledge and inspire others to experiment with new technologies.

Leave a Reply

Your email address will not be published. Required fields are marked *