×
Community Blog How Accurate Is Your RAG? Benchmarking 18 Configurations

How Accurate Is Your RAG? Benchmarking 18 Configurations

I benchmarked 18 RAG configurations on Alibaba Cloud. Compared with no retrieval, the best setup improved answer accuracy from 2/30 to 29/30.

I once attended a RAG demo for a customer. Each question produced a plausible answer from the documents, and the people around the table nodded along.

Then someone asked, "So how accurate is it?"

The room went quiet. I had no number to give them. We had tried several questions and the answers looked good, but that was not enough to call the system accurate. In a meeting where the decision is whether to take it to production, "it usually works" is not evidence.

Only after experiencing that silence did I realize what was missing. I did not need a better explanation. I needed a way to measure accuracy.

To turn accuracy into a number you can say out loud, you first have to settle three settings and find a way to measure the result:

  1. How large should my chunks be: 256, 512, or 1024 words?
  2. How many documents should retrieval return: 3, 5, or 10?
  3. Is a reranker worth the extra latency and cost?

So let me answer the meeting-room question up front, because it is the most important number in this post. The same model, asked the same 30 questions with no retrieval at all, answers only 2 of them correctly. With retrieval, it answers up to 29. That gap, not the model, is what a RAG pipeline actually produces, and measuring it is the point of everything below.

These are not questions with one universal answer. They depend on your corpus, your queries, and your quality bar. The honest way to answer them is not to pick a number from a blog post. It is to measure them on your own system.

So this post takes a different approach. Instead of recommending settings, I built an enterprise-knowledge RAG entirely on managed Alibaba Cloud services: Model Studio for the models and AnalyticDB for PostgreSQL for vector search. I then ran a full factorial evaluation of 3 chunk sizes × 3 top-k values × rerank on/off = 18 configurations, scoring each one on retrieval recall, answer correctness, groundedness, and refusal. Every number below is measured, not claimed.

Rather than just describing the idea, this post walks through the whole process: building a credible corpus, setting up the vector index, creating a reusable evaluation harness, judging at half price with the Batch API, and analyzing the measured results. The system was built and verified in a real environment. At the end I hand over the harness and an interactive playground you can reuse.

One clarification up front: "accuracy" here is measured against a fixed golden QA set by an LLM-as-judge, not graded by a human. That makes it cheap and reproducible, but treat it as a comparative instrument for choosing between configurations, not as absolute truth. See the Limitations section.

The Overall Flow

The system has three lanes: ingest, query, and evaluate. The point of the diagram is that evaluate is not a separate track running beside query. It wraps the query pipeline. The matrix drives it once per configuration (18 times) and scores every run.

00_architecture

  1. Ingest: The corpus is chunked, embedded with text-embedding-v4, and loaded into an ANN index on AnalyticDB for PostgreSQL. An embedding cache means re-runs skip re-embedding.
  2. Query: The question is embedded, matched against the index, optionally reranked by qwen3-rerank (the rerank on/off branch is one of the experimental axes), then answered by qwen-plus with a strict grounded prompt.
  3. Evaluate: The 18-combo matrix drives the query pipeline once per combo and collects 648 answers (36 questions × 18). Retrieval recall is computed deterministically, and the answers are judged by an LLM via the Batch API at 50% cost.

The key point: the pipeline and the measurement are one artifact. The same harness that builds the index also scores it, so every time you change a setting you get a comparable number back. That is what turns "RAG tuning" from guesswork into a regression test.

0. Prerequisites

  • An Alibaba Cloud account with Model Studio activated
  • A Model Studio API key (international / Singapore endpoint)
  • An AnalyticDB for PostgreSQL instance in Elastic Storage Mode with Vector Engine Optimization enabled
  • Region: Singapore (ap-southeast-1) throughout this post

1. A Credible Corpus, Not Toy Data

A RAG benchmark is only as credible as its knowledge base. Point it at three hand-written files and you are really testing whether it can find the only relevant document. That is trivial and not what production looks like.

So I used a real corpus. I scraped the complete English (international) Model Studio documentation: 118 pages and about 4.3 million characters. I then converted it to clean Markdown while preserving tables and code fences. This matters for two reasons.

  • The retrieval task is realistic. With 118 documents, there are real distractors. Several pages discuss embeddings, pricing, and rate limits, so the retriever has to pick the right one, not just a one.
  • The answers are verifiable against an authoritative source, which is what makes a golden QA set meaningful.

01_model_embeddings

All the models come from Model Studio's catalog. For this build: text-embedding-v4 for embedding, qwen3-rerank for reranking, and qwen-plus for both answer generation and judging.

Why the corpus choice matters for the numbers: groundedness scores are high partly because this corpus is clean and authoritative. Messier enterprise documents, including scans, mixed languages, and stale pages, will be harder. That is a reason to keep the harness and re-measure, not to assume these numbers transfer.

2. Chunking and Embedding

I chunked the same corpus three ways: 256, 512, and 1024 words with 10% overlap. This produced 3,087 / 1,397 / 676 chunks respectively. The chunker is Markdown-aware and, importantly, splits oversized blocks (huge Markdown tables) so no single chunk blows past the embedding model's token limit. That last point cost me an InternalError on the first run. A 40-column pricing table became one giant "paragraph" until I added the split.

Each chunk is embedded with text-embedding-v4 at 1024 dimensions.

From my implementation: I added an on-disk embedding cache keyed by a content hash of the chunks. Every time you re-run the matrix you would otherwise re-embed thousands of chunks and pay for them again. With the cache, re-runs load vectors from disk. For an eval you run on every change, this is the difference between a cheap regression gate and an expensive one.

3. The Vector Index on AnalyticDB for PostgreSQL

The vector store is AnalyticDB for PostgreSQL 7.0 in Elastic Storage Mode, created with Vector Engine Optimization enabled. This is a toggle at instance creation, so verify that it shows Enabled before you build the index.

02_adbpg_console

Example configuration: an AnalyticDB for PostgreSQL instance in the console. If you follow along, confirm Vector Engine Optimization shows Enabled before you build the index.

You may also spot Enable RAG Service in that connection panel. AnalyticDB for PostgreSQL ships with a built-in RAG capability. It is a fast way to stand up a retrieval pipeline, and if a working endpoint is all you need, you should absolutely consider it. The reason I did not use it here is the point of this post: the built-in service does not expose chunk size, top-k, or the reranker as knobs you can vary and measure. This article is about measuring those settings, so I assembled the pipeline myself and made every stage, from chunking and embedding through retrieval, reranking, and generation, an explicit and measurable variable. Think of the built-in service as the quick path to a working RAG, and the harness in this post as the tool for when you need to know which settings actually matter.

03_adbpg_instance

Instance detail: note Vector Search Engine Optimization: Enabled at the bottom. This is what makes the ANN index fast.

The schema is a single chunks table with a vector(1024) column and an ANN index using cosine distance.

CREATE TABLE chunks (
    chunk_id  text,
    doc_id    text,
    chunk_seq int,
    content   text,
    embedding vector(1024)
) DISTRIBUTED BY (chunk_id);

CREATE INDEX chunks_vec_idx ON chunks USING ann(embedding)
WITH (dim=1024, distancemeasure=cosine, hnsw_m=16, pq_segments=64);

Retrieval is an ANN nearest-neighbor search with <=>, returning the top-k by cosine similarity.

SELECT chunk_id, doc_id, content,
       cosine_similarity(embedding, '<query_vector>'::vector) AS score
FROM chunks
ORDER BY embedding <=> '<query_vector>'::vector
LIMIT 10;

From my testing: the upsert is the bottleneck, not the search. My first loader inserted rows one at a time. Pushing ~3,000 vector rows across single-row INSERTs to a remote instance took many minutes and looked hung. Switching to a batched multi-row insert (psycopg2.extras.execute_values) loaded 3,087 rows in about 6 seconds. If your ingest feels stuck, batch the inserts before you blame the database.

4. The Evaluation Harness: the Part You Keep

The pipeline is disposable. The harness is the asset. It lives in a small Python package and does five things: chunk, embed, index/retrieve, generate, and judge. But the heart of it is the golden QA set and the metric definitions.

4-1. The golden set

I hand-wrote 36 questions, deliberately mixed so the eval measures different failure modes:

Type Count What it tests
Single-hop 12 One fact, one document
Multi-hop 10 Must combine two documents
Paraphrase 8 Same facts, asked casually to test robustness
Unanswerable 6 Not in the docs, so it must refuse rather than invent

Each answerable question lists its golden source document(s), which makes recall@k deterministic: did a retrieved chunk actually come from a document that contains the answer?

The unanswerable set is the one most people skip, and it is the most valuable. Questions like "What is the guaranteed uptime SLA percentage?" have no answer in the corpus. A production RAG that confidently invents one is worse than one that says "not found." These six questions measure exactly that.

4-2. The metrics

  • recall@k (deterministic): fraction of answerable questions whose golden source was retrieved. Measures retrieval, independent of the generator.
  • accuracy (LLM-judged): the answer matches the reference (correct vs partial/incorrect).
  • groundedness (LLM-judged): every factual claim in the answer is supported by the retrieved context.
  • refusal accuracy (LLM-judged): unanswerable questions are declined, not hallucinated.

4-3. Running the matrix

The harness loops over every combination, retrieves (and optionally reranks), generates, and writes one row per (combo, question).

# Production run against AnalyticDB for PostgreSQL
export DASHSCOPE_API_KEY=sk-xxx
export ADBPG_HOST=... ADBPG_USER=... ADBPG_PASSWORD=... ADBPG_DB=rag_eval
python run_matrix.py --retriever adbpg

# Dev loop without a database (numpy cosine baseline)
python run_matrix.py --retriever local

That produces 18 combos × 36 questions = 648 generated answers to judge.

5. How to Put It Together

If you want to reproduce this, here is the actual shape of the project. The whole thing is a small Python package. There is no framework.

5-1. Module layout

harness/
  config.py            models, endpoints, matrix dims, ADBPG connection, .env loader
  chunker.py           markdown-aware chunking with overlap (splits oversized tables)
  embedder.py          text-embedding-v4 + on-disk embedding cache
  reranker.py          qwen3-rerank (DashScope native rerank API)
  retriever_local.py   numpy cosine baseline (develop without a database)
  retriever_adbpg.py   AnalyticDB ANN index (cosine), batched upsert
  generator.py         qwen-plus answer generation with a strict grounded prompt
  judge.py             LLM-as-judge prompt + output parser
  run_matrix.py        runs the full matrix, writes per-combo JSONL
  batch_judge.py       judge via Batch API (50% cost), merges results
  aggregate.py         judged results -> per-combo recall/accuracy/groundedness
  make_charts.py       renders the charts used in this post
  gen_demo.py          builds the interactive playground (demo.html)

The data flow is a straight line: corpus/ → chunks → embeddings → ADBPG index → retrieve → (rerank) → generate → judge → metrics. Every stage is its own module, so you can swap one piece, such as the generator model or the retriever, without touching the rest.

5-2. Step-by-step setup

  1. Activate Model Studio and create an API key. Use the international endpoint (https://dashscope-intl.aliyuncs.com/compatible-mode/v1) if you are in Singapore.
  2. Create the AnalyticDB for PostgreSQL instance: Choose Elastic Storage Mode and High-availability Edition, and turn on Vector Engine Optimization at creation. Then create a database account and either whitelist your client IP or allocate a public endpoint so the harness can reach it.
  3. Install dependencies and set credentials.
# requirements.txt
openai
psycopg2-binary
numpy
matplotlib
scikit-learn
# harness/.env  (chmod 600)
DASHSCOPE_API_KEY=sk-xxxxxxxx
ADBPG_HOST=gp-xxxx.gpdb.singapore.rds.aliyuncs.com
ADBPG_PORT=5432
ADBPG_DB=rag_eval
ADBPG_USER=rag_eval
ADBPG_PASSWORD=********
  1. Provide the inputs: your documents in corpus/ (Markdown), and the golden QA set in golden_set.jsonl (each answerable row carries its source_docs).
  2. Run the three commands: run_matrix.py (retrieve + generate), batch_judge.py (score at half price), aggregate.py (report). The optional make_charts.py and gen_demo.py produce the visuals.
cd harness
python run_matrix.py --retriever adbpg
python batch_judge.py --dir results --pattern 'run_*.jsonl'
python aggregate.py --file results/results_judged.jsonl

That is the entire system. The managed services handle the heavy lifting, including embedding, ANN search, and judging. The harness just orchestrates and measures.

6. How I Developed It

The configuration numbers above are the result, but the process that got there is the reusable part. A few principles, and the specific mistakes that shaped the build:

Build local first, promote to the managed service last. The harness has two retrievers for a reason. I developed against retriever_local.py, a NumPy cosine baseline that needs no database, to validate chunking, the golden set, and the generation prompt cheaply and quickly. Only once the loop was sound did I point it at AnalyticDB. Keep the expensive path for last. You will iterate on it far less.

Measure retrieval and generation separately. recall@k is deterministic and isolates retrieval. The judge scores generation. When a number is bad, this split tells you which stage to fix. Low recall with good accuracy means the retriever is missing the document. Good recall with low accuracy means the generator is fumbling the context. Without the split you are just guessing.

Iterate on real failures. Every "optimization" in the final harness came from hitting an actual wall, not from anticipation:

  1. A giant Markdown table blew the embedding limit. A 40-column pricing table became one oversized "paragraph" and the embedding API threw InternalError. Fix: the chunker now splits any block larger than the chunk size before packing.
  2. Single-row vector inserts looked like a hang. Pushing ~3,000 rows one INSERT at a time over a WAN link took many minutes. Fix: use a batched multi-row insert. It loaded 3,087 rows in about 6 seconds.
  3. Re-runs re-billed the embedding API. Every matrix re-run re-embedded thousands of chunks. Fix: an on-disk cache keyed by a hash of the chunk content.
  4. Growing the corpus silently broke recall. When I expanded the corpus, new documents duplicated the golden topics, so the retriever returned an equivalent page and the deterministic recall counted it as a miss. Fix: append equivalent documents to each question's source_docs, and re-verify the unanswerable questions stay unanswerable.
  5. Real-time judging was the cost bottleneck. Scoring 648 answers at real-time price on every run is not sustainable. Fix: the Batch API at 50% cost.

Treat the evaluation as a regression gate. The golden set lives in version control alongside the harness. Any change, whether it involves new documents, a new model, or a new chunk size, gets re-run and diffed against the previous metrics. That turns "did I make it better?" from a feeling into a number.

7. Judging at Half Price with the Batch API

Scoring 648 answers with a real-time LLM is the most expensive part of the loop. It is also the part you re-run every time the corpus or a setting changes. So I packaged all 648 judge calls into a single Model Studio Batch API job, which runs at 50% of real-time pricing within a 24-hour window.

The same qwen-plus judge, temperature=0, and prompt run at half the cost with no added code complexity. It is just the OpenAI-compatible /v1/batches endpoint: build a JSONL of chat-completion requests, upload it, poll, and merge the outputs back.

# Judge all 648 answers at 50% cost, merge into results_judged.jsonl
python batch_judge.py --dir results --pattern 'run_*.jsonl'

# Aggregate into per-combo metrics
python aggregate.py --file results/results_judged.jsonl

For an evaluation you intend to run repeatedly, the Batch API is what makes "re-measure on every change" affordable.

8. The Results

Two reading notes before the numbers, because they matter more than any single figure.

Resolution: read the two tables differently. The per-combination numbers (the table in 8-3) are each computed over 30 answerable questions, so one question is worth 3.3 points. A gap of less than ~10 points between two individual combos represents only one or two questions and sits inside the judge's run-to-run variance. Read it as noise, not as a ranking. The marginal means in 8-1 are different. Each one averages six configurations, or 180 answerable question-instances, so they are far more stable. That is exactly why the conclusions below are drawn from the marginal means, not from individual cells.

The control: can the model already answer these without retrieval? This decides whether the numbers mean anything. The corpus is Model Studio's own documentation, and the generator and judge are both qwen-plus, a model that plausibly saw these pages during pretraining. If it already knows the answers, the accuracy below could be memorization, not retrieval. So I ran the same 36 questions past qwen-plus with no retrieved context at all, using a neutral prompt and the same judging rubric:

Closed-book (no retrieval) RAG, best config
Answerable accuracy 0.067 (2/30) 0.967
Unanswerable refusal 0.000 1.000

Without retrieval, qwen-plus gets only 2 of 30 past the judge, and both are hollow wins. One produced the right number while citing the wrong vendor's documentation. The other simply declined, and the judge's "correct" there reflects leniency rather than knowledge. Substantively, closed-book recall is effectively zero. On parametric knowledge alone the model even confused Alibaba Cloud Model Studio with a different vendor's product of the same name. So the accuracy below is retrieval doing the work, not the model remembering the docs. In closed-book mode, it also fails to refuse the unanswerable questions. The grounded prompt plus retrieved context produces the 1.0 refusal rate.

04_chart_closed_book

Same model, same 30 answerable questions: without retrieval only 2 of 30 get past the judge. Add retrieval and it is 29 of 30.

Best single configuration: chunk 1024 · top-k 10 · rerank on:

Metric Score
recall@10 0.967
accuracy 0.967
groundedness 1.000
refusal accuracy 1.000

Across all 18 configurations, recall ranged from 0.767 to 0.967 and accuracy from 0.833 to 0.967. Groundedness stayed above 0.94 everywhere, and refusal accuracy was 1.0 in every configuration. The grounded prompt never let the model invent an answer to an unanswerable question.

8-1. Which knob moves quality the most?

Chunk size is the biggest lever.

05_chart_chunk_size

Going 256 → 512 → 1024 words lifted recall from 0.867 → 0.911 → 0.950 and accuracy from 0.861 → 0.850 → 0.906. Larger chunks keep more surrounding context intact, so the retriever hands the generator complete, self-contained passages instead of fragments. The small accuracy dip at 512 is within judge run-to-run variance. The 256→1024 trend is unambiguous.

One fairness caveat on that recall comparison: at a fixed k, larger chunks retrieve a larger fraction of the corpus per query. At k=10, that is about 1.5% of the 676 chunk-1024 index but only ~0.3% of the 3,087 chunk-256 index. So part of the chunk-size recall gain is a token-budget artifact, not purely a chunking effect. A strict comparison would hold the retrieved-token budget constant by raising k for smaller chunks until each combo pulls back roughly the same number of tokens. The direction of the finding still holds because larger chunks help here. However, this fixed-k comparison somewhat overstates the size of the chunk-size advantage.

Top-k has a clear but smaller effect.

06_chart_top_k

Recall climbs 0.872 (k=3) → 0.922 (k=5) → 0.933 (k=10). Retrieving more candidates gives the reranker and generator more to work with. The gain is already flattening by k=10. I did not measure beyond 10, but past that flattening point extra candidates are more likely to add latency and cost than recall.

Rerank buys recall, roughly breaks even on accuracy.

07_chart_reranker

Turning the reranker on lifted recall from 0.893 → 0.926. Accuracy was essentially flat (0.874 → 0.870) and groundedness dipped slightly (0.978 → 0.969). So the reranker's job here is to promote the right document into the context window. Whether you enable it is a recall-vs-latency/cost trade.

8-2. Recall across the whole grid

08_chart_recall_heatmap

Two things jump out. First, recall is non-decreasing as you move toward larger chunks and higher k. It rises or holds and never drops. The rerank-on 1024 row is flat at 0.97, and the 512 row flattens from k=5. Second, reranking flattens the curve. With it on, even the small-chunk and low-k corners reach ~0.87, and the whole 1024 row saturates at 0.97. If you can only afford one knob, choose chunk size. The reranker is what rescues a mediocre configuration.

8-3. All 18 combinations

Combo recall accuracy ground refusal p95 latency cost/query
c256_k3_r0 0.767 0.867 0.972 1.0 4.4s $0.0005
c256_k3_r1 0.867 0.833 0.944 1.0 4.5s $0.0010
c256_k5_r0 0.867 0.867 1.000 1.0 4.8s $0.0008
c256_k5_r1 0.900 0.867 0.944 1.0 6.4s $0.0013
c256_k10_r0 0.900 0.867 0.972 1.0 5.9s $0.0017
c256_k10_r1 0.900 0.867 0.972 1.0 5.9s $0.0020
c512_k3_r0 0.833 0.867 0.944 1.0 6.6s $0.0010
c512_k3_r1 0.900 0.833 0.972 1.0 5.2s $0.0020
c512_k5_r0 0.933 0.867 0.972 1.0 6.0s $0.0016
c512_k5_r1 0.933 0.867 0.972 1.0 6.0s $0.0026
c512_k10_r0 0.933 0.833 0.972 1.0 6.1s $0.0033
c512_k10_r1 0.933 0.833 0.972 1.0 6.7s $0.0041
c1024_k3_r0 0.900 0.933 1.000 1.0 3.4s $0.0017
c1024_k3_r1 0.967 0.867 0.944 1.0 5.3s $0.0037
c1024_k5_r0 0.933 0.833 0.972 1.0 4.4s $0.0031
c1024_k5_r1 0.967 0.900 1.000 1.0 5.6s $0.0052
c1024_k10_r0 0.967 0.933 1.000 1.0 6.0s $0.0071
c1024_k10_r1 0.967 0.967 1.000 1.0 6.9s $0.0088

Latency is measured end to end per query, including query embedding, ANN search, optional reranking, and generation. It is measured from my client, so it includes the network round trip to Singapore. Cost is computed from the actual token usage of each call at international list prices.

A caveat on the latency column. Each combo ran once, so with n=36 the p95 here is effectively the second-slowest single observation. Variance from the round trip between the client and Singapore and from generation time is larger than the differences between adjacent combos. Read down the column and it even contradicts itself: rerank-on is faster than rerank-off in three combos, and a 1024-word chunk beats a 256-word one. Treat this column as directional only. Reranking adds one extra network call before generation. Re-measure with repeated runs in your own environment before you price latency. Cost is token accounting, so it is deterministic and not subject to this noise.

Now "should I turn the reranker on?" has a number instead of a hand-wave. Enabling it adds one extra network call before generation and about 47% more cost per query ($0.0023 → $0.0034) in exchange for +3.3 points of recall. I can only state the latency impact directionally because one run per combo makes the per-combo seconds noisy (see the caveat above). The cost result is solid because it comes from token accounting. Cost is dominated by the generator's input tokens, so it grows with both chunk size and k. Bigger chunks and more of them mean more retrieved text in the context. The best combo lands at ~$0.009 per query. For a high-traffic service, a cheaper corner of the grid, such as c1024_k3_r0 at $0.002 with 0.90 recall, may be the better buy.

The worst configuration (chunk 256, k=3, no rerank) scores recall 0.767. The best scores 0.967. That is a 20-point gap from settings alone on the same models and the same corpus. Configuration is not a detail. It is the difference between a RAG you can ship and one you cannot.

9. Try It Yourself: an Interactive Playground

Numbers in a table are convincing, but seeing retrieval happen is better. I packaged the whole evaluation into a single self-contained page called demo.html. Open it in any browser. It needs no server and no API keys. Everything in it is a real trace or a real measured number from this run. There are three views:

Retrieval Map. All 676 chunks of the corpus, projected into 2-D by t-SNE and colored by source document. Pick any of the 36 golden questions and watch vector search light up the exact chunks it retrieved (orange, ranked), with the golden-source document ringed in green. It is the most intuitive way to see what retrieval is doing and where it misses.

09_demo_map

09_demo_map_selected

Tune the Config. Slide chunk size, top-k, and the reranker on/off and read the measured recall / accuracy / groundedness for that exact combination off the heatmap.

10_demo_tuner

10_demo_tuner_worst

Pipeline Walkthrough. Step through any question from retrieval to reranking, generation, and judging. You can inspect the retrieved chunks, the highlighted golden sources, and the judge's verdict. Here are a correct answer and a refusal:

11_demo_answer

12_demo_refusal

The answer: retrieval finds the golden document and qwen-plus answers 1024, confirmed correct + grounded. The refusal: an unanswerable question answered with "Not found in the knowledge base" instead of being invented.

10. Behavioral Details Worth Knowing

  • recall@k is deterministic. The judge metrics are not. Recall is computed from the golden source docs, so it is reproducible run to run. The judge metrics use a temperature-0 LLM, so expect small run-to-run variance. Treat a difference of a question or two as noise (see Section 8), and use the harness as a comparative instrument.
  • Refusal comes from the prompt, not the model. The generator's system prompt forces the literal reply "Not found in the knowledge base" when the context lacks the answer. That one line, combined with retrieved context, is what takes refusal accuracy to 1.0. In closed book (Section 8), the same model refuses none of these questions.
  • The judge reads the same context the generator saw. Groundedness is deliberately judged against the retrieved context, not the whole corpus, because it measures whether the answer is supported by what the model actually looked at.

11. Limitations

  • The judge is an LLM, not a human. It agrees with itself well (temperature 0) and is fine for comparing configurations, but it is not a human-graded ground truth. Spot-check samples before you trust absolute numbers.
  • The judge and the generator are the same model (qwen-plus), which raises a self-preference concern. To bound it, I re-judged the best combo's answers with an independent judge model (qwen-max): agreement was 30/30 on correctness and 6/6 on refusal (the 30 answerable and the 6 unanswerable questions respectively). That does not prove the judgments are human-correct, but it shows they are not an artifact of the model grading itself favorably.
  • **Chunk sizing uses approximate token counts. The chunker splits on whitespace-separated words, so a "512-word" chunk is not exactly 512 model tokens. The cost numbers in section 8-3 are separate because they come from the actual token usage reported by each API call.
  • Groundedness is high partly because the corpus is clean and authoritative. Messier real-world documents will be harder. Re-measure rather than assume.
  • This measures one generation model (qwen-plus). Swap the model in config and re-run. That comparison is exactly what the harness is for.

Wrapping Up

You do not need to guess your RAG settings, and you do not need to run your own GPU fleet or vector database to get a production-grade pipeline. With Model Studio for the models, AnalyticDB for PostgreSQL for vector search, and a disciplined evaluation loop that includes a golden set, deterministic recall, and LLM judging via the Batch API, you get a system whose accuracy you can state, not just hope for.

  • In closed book the same model answers only 2 of 30. The accuracy gains come from retrieval, not from the model remembering the docs.
  • Chunk size is the biggest lever. Start at 1024 for documentation-style content as a baseline, not a universal rule. At a fixed k, larger chunks also retrieve more total context, so re-verify the trend under a constant retrieved-token budget.
  • k=5 and k=10 both beat k=3. The 5→10 gain is small (recall 0.92 → 0.93), and the curve is already flattening by 10.
  • Rerank buys ~3 points of recall for one extra network hop and ~+47% cost per query, with near-flat groundedness. This is a trade you can now price.
  • A strict grounded prompt took refusal accuracy to 1.0 in every configuration. It is the cheapest hallucination defense available.
  • Judging through the Batch API halves the eval cost, and an independent judge model agreed with the results 30/30 on correctness, 6/6 on refusal. Re-measuring on every change is both affordable and defensible.

The reusable pieces, including the harness, the golden set, and the interactive playground, are yours to keep. Design "how do I know it's accurate" with the same care you put into "which model do I pick." Also Measure it!


References

Korean Version :Click

Alibaba Cloud
Hosung Kim | Sr.Technical Account Manager

0 0 0
Share on

Hosung Kim

6 posts | 1 followers

You may also like

Hosung Kim

6 posts | 1 followers

Related Products