<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Smart Choice</title>
    <description>The latest articles on DEV Community by Smart Choice (@smartchoice).</description>
    <link>https://dev.to/smartchoice</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215195%2F07f171db-775b-45f7-8f21-7250a2cc29e1.png</url>
      <title>DEV Community: Smart Choice</title>
      <link>https://dev.to/smartchoice</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smartchoice"/>
    <language>en</language>
    <item>
      <title>Before you build a RAG PoC: a 10-point checklist</title>
      <dc:creator>Smart Choice</dc:creator>
      <pubDate>Thu, 29 Oct 2026 12:57:39 +0000</pubDate>
      <link>https://dev.to/smartchoice/before-you-build-a-rag-poc-a-10-point-checklist-1gg3</link>
      <guid>https://dev.to/smartchoice/before-you-build-a-rag-poc-a-10-point-checklist-1gg3</guid>
      <description>&lt;p&gt;Most RAG proofs of concept don't fail because the model is bad. They fail because nobody agreed what "good" means, the documents were messier than expected, or the demo worked on ten hand-picked questions and fell apart on the eleventh.&lt;/p&gt;

&lt;p&gt;A RAG PoC is cheap to start and easy to misjudge. Below is the checklist I go through with a team &lt;em&gt;before&lt;/em&gt; any code is written. None of it is exotic. All of it saves weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what RAG actually is
&lt;/h2&gt;

&lt;p&gt;RAG (retrieval-augmented generation) is a way to make a language model answer from &lt;em&gt;your&lt;/em&gt; documents instead of only from what it learned during training. It works in two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve.&lt;/strong&gt; The user's question is used to search a knowledge base (usually a vector index built from your documents split into chunks, often combined with keyword search) and pull back the few most relevant passages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate.&lt;/strong&gt; Those passages are put into the prompt together with the question, and the LLM writes an answer grounded in them, ideally with citations to the sources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The model itself is not retrained. You get answers that reflect current, private data, can point to where they came from, and can be updated just by re-indexing documents. The flip side: the answer can only be as good as what the retrieval step finds, which is why most of the checklist below is about data and retrieval, not about the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Name the process and its current cost
&lt;/h2&gt;

&lt;p&gt;"We want a chatbot over our docs" is not a goal. "Support agents spend ~6 minutes per ticket finding the right policy paragraph, on ~1,500 tickets a month" is. With a baseline you can later say whether the PoC moved anything, and whether it's worth paying for in production.&lt;/p&gt;

&lt;p&gt;Also ask the uncomfortable question early: does this need an LLM at all? Sometimes a good search index with filters solves 80% of the problem for a fraction of the cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Inventory the data: where it lives, who owns it, how fresh it is
&lt;/h2&gt;

&lt;p&gt;List every source: Confluence spaces, PDFs on a shared drive, a ticketing system, a product database. For each one, write down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;format (born-digital PDF, scanned PDF, HTML, tables, slides),&lt;/li&gt;
&lt;li&gt;owner and access permissions,&lt;/li&gt;
&lt;li&gt;update frequency,&lt;/li&gt;
&lt;li&gt;whether it may leave your infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scanned PDFs and slide decks are where projects quietly lose a week. If 30% of the knowledge is in tables inside scanned PDFs, your parsing step &lt;em&gt;is&lt;/em&gt; the project, and you want to know that on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check that the PoC sample is representative
&lt;/h2&gt;

&lt;p&gt;It's tempting to start with the cleanest folder. Don't. Pick a sample that includes the ugly cases: outdated versions of the same document, contradictory policies, multilingual content, very long files. A PoC that only sees clean data tells you very little about production.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Decide on a chunking strategy, then test it, not just pick it
&lt;/h2&gt;

&lt;p&gt;Chunk size and boundaries change retrieval quality more than most model choices do. Reasonable starting points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;split on document structure (headings, sections) before falling back to token windows,&lt;/li&gt;
&lt;li&gt;keep chunks around 300–800 tokens with a small overlap,&lt;/li&gt;
&lt;li&gt;store metadata with every chunk: source, section title, date, access level.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Metadata isn't decoration. It lets you filter ("only current policies", "only documents this user can see") and cite sources in answers, which users trust far more than bare text.&lt;/p&gt;

&lt;p&gt;Treat chunking as a parameter you'll compare on the eval set (point 5), not a one-off decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Build an evaluation set before the pipeline
&lt;/h2&gt;

&lt;p&gt;This is the single most skipped step and the most valuable one. Sit down with domain experts and collect 50–150 real questions, each with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the expected answer (or key facts it must contain),&lt;/li&gt;
&lt;li&gt;the document(s) and section(s) where the answer lives,&lt;/li&gt;
&lt;li&gt;a few questions that &lt;em&gt;cannot&lt;/em&gt; be answered from the corpus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The unanswerable ones matter: you want to measure whether the system says "I don't know" instead of inventing something.&lt;/p&gt;

&lt;p&gt;A plain JSONL file is enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"q017"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"question"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How many days do customers have to return a damaged item?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relevant_ids"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"returns-policy-2025#sec-3"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"must_contain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"30 days"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"q018"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"question"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What is the CEO's home address?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relevant_ids"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"must_contain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expect_refusal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Measure retrieval separately from generation
&lt;/h2&gt;

&lt;p&gt;When an answer is wrong, you need to know whether the right chunk was never retrieved, or it was retrieved and the model ignored it. Those are different fixes. So measure retrieval on its own, with simple metrics like recall@k and MRR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;retrieval_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eval_set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rr_sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;eval_set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;relevant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevant_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;  &lt;span class="c1"&gt;# unanswerable questions are scored elsewhere
&lt;/span&gt;        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;retrieved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc_id&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retrieved&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retrieved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;doc_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;rr_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recall@k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mrr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rr_sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# metrics = retrieval_metrics(load_eval("eval.jsonl"), my_retriever.search, k=5)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twenty lines of code, and now "let's try hybrid search" or "let's try smaller chunks" becomes a measurable experiment instead of a debate. For generation, start with simple checks (does the answer contain the required facts, does it cite a retrieved source, does it refuse when it should) before reaching for LLM-as-a-judge.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Set latency and cost budgets up front
&lt;/h2&gt;

&lt;p&gt;Write down two numbers before building: acceptable response time (e.g. p95 under 4 seconds for an internal assistant) and acceptable cost per query. Then estimate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tokens per query = system prompt + retrieved chunks (k × chunk size) + question + answer,&lt;/li&gt;
&lt;li&gt;queries per month from the baseline in point 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieving 10 chunks of 800 tokens means ~8k input tokens per question before the model says anything. Multiply by monthly volume and the price list of the model you're considering. This often changes the design: fewer, better chunks, a reranker, or a smaller model for simple questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Agree on guardrails and data boundaries
&lt;/h2&gt;

&lt;p&gt;Questions to settle with security and legal &lt;em&gt;before&lt;/em&gt; the PoC, not after the demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data may be sent to an external model API, and under which terms? Or does everything have to stay in your own cloud project?&lt;/li&gt;
&lt;li&gt;How are document permissions enforced in retrieval? (Filter by access-level metadata &lt;em&gt;before&lt;/em&gt; the LLM sees anything; never rely on the prompt to hide content.)&lt;/li&gt;
&lt;li&gt;How do you handle prompt injection hidden inside documents?&lt;/li&gt;
&lt;li&gt;Who reviews outputs in high-stakes cases, and who is accountable for a wrong answer?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Define success criteria and the decision that follows
&lt;/h2&gt;

&lt;p&gt;Write them down, with numbers, and agree on them with whoever signs off the next budget:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieval recall@5 ≥ 0.85 on the eval set,&lt;/li&gt;
&lt;li&gt;≥ 80% of answers contain the required facts,&lt;/li&gt;
&lt;li&gt;≥ 90% correct refusals on unanswerable questions,&lt;/li&gt;
&lt;li&gt;p95 latency and cost per query within budget,&lt;/li&gt;
&lt;li&gt;a time saving measured with real users on real tickets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Equally important: what happens next? "If we hit these numbers, we fund a production rollout for team X; if not, we stop or narrow the scope." A PoC without a pre-agreed decision tends to turn into an endless demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Sketch the path to production now
&lt;/h2&gt;

&lt;p&gt;A PoC notebook and a production service are different animals. Before starting, have rough answers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingestion:&lt;/strong&gt; how do new and updated documents get re-parsed, re-chunked and re-embedded? A scheduled pipeline (Airflow, Cloud Scheduler, a queue) beats a manual script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; log questions, retrieved chunk IDs, answers and user feedback, so the eval set can grow from real traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression testing:&lt;/strong&gt; re-run the eval set on every change to prompts, chunking, models or embeddings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership:&lt;/strong&gt; who maintains it when the PoC team moves on?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to build any of this during the PoC. You need to know it's feasible, so the PoC architecture doesn't have to be thrown away.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick self-test
&lt;/h2&gt;

&lt;p&gt;If you can't answer at least half of these points for your use case, the best next step is probably not a PoC but a short discovery phase: collect questions, look at the data, agree on metrics. It's a few days of work that can save a few months.&lt;/p&gt;

&lt;p&gt;I keep a shorter, business-oriented version of this list, &lt;a href="https://smartchoice.dev/en/ai-checklist/" rel="noopener noreferrer"&gt;10 questions before starting an AI PoC&lt;/a&gt;, for conversations with non-technical stakeholders.&lt;/p&gt;

&lt;p&gt;What would you add? I'm especially curious how others build and maintain eval sets once the system is live.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bolesław Badziąg has spent 20+ years building IT products and runs &lt;a href="https://smartchoice.dev" rel="noopener noreferrer"&gt;Smart Choice&lt;/a&gt;, helping teams take AI, data and cloud projects from audit and prototype to production.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>llm</category>
      <category>ai</category>
      <category>python</category>
    </item>
  </channel>
</rss>
