<?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: Fabien Kost</title>
    <description>The latest articles on DEV Community by Fabien Kost (@fabienk).</description>
    <link>https://dev.to/fabienk</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%2F2136795%2F8c5cd87c-6c56-43c3-be0d-6b8c41aa743e.jpg</url>
      <title>DEV Community: Fabien Kost</title>
      <link>https://dev.to/fabienk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fabienk"/>
    <language>en</language>
    <item>
      <title>OCR is a step, not a guarantee (turning scanned archives into usable data)</title>
      <dc:creator>Fabien Kost</dc:creator>
      <pubDate>Tue, 27 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/fabienk/ocr-is-a-step-not-a-guarantee-turning-scanned-archives-into-usable-data-811</link>
      <guid>https://dev.to/fabienk/ocr-is-a-step-not-a-guarantee-turning-scanned-archives-into-usable-data-811</guid>
      <description>&lt;p&gt;&lt;em&gt;Adapted from an article originally published in French on &lt;a href="https://www.closiqode.com/ocr-de-documents/" rel="noopener noreferrer"&gt;closiqode.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Running OCR over scanned PDFs, images or archives does not make them usable on its own. To get there, you need searchable text, a permanent link to the original, a structure for the information that matters, and a check on the uncertain cases.&lt;/p&gt;

&lt;p&gt;OCR reads the document. The data only becomes useful once it finds its place in a structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  A digital archive can be as opaque as a filing cabinet
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhi6ij20zsv83ojo11og4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhi6ij20zsv83ojo11og4.webp" alt="A tall stack of cream paper scans bound with dark string on a black studio table, a single sheet at the top catching a thin orange amber light: an archive that is stored but not yet readable" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scanning a document protects the physical medium and makes it easier to share. It does not turn its content into data. An image sitting in a folder is still hard to search, compare or connect to a business application.&lt;/p&gt;

&lt;p&gt;The confusion usually comes from four different operations being treated as one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Digitizing&lt;/strong&gt; creates an image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCR&lt;/strong&gt; recognizes the characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing&lt;/strong&gt; makes the content searchable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structuring&lt;/strong&gt; turns specific elements into usable fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step answers a different need.&lt;/p&gt;

&lt;p&gt;You may already have thousands of well-named scans. Yet finding a reference, a date or a measurement still means opening files one by one. The knowledge exists, but it is stuck on the page. You see this gap in technical archives, customer files, field reports and old invoices: storage is solved, usage is not. So the project has to start from the next expected action, not from the volume to digitize.&lt;/p&gt;

&lt;h2&gt;
  
  
  OCR is a step, not a guarantee
&lt;/h2&gt;

&lt;p&gt;The French National Library (BnF) describes a chain that combines preprocessing, segmentation and recognition. It also stresses the impact of contrast, orientation, smudges, columns and illustrations. A sound setup therefore keeps the source image and ties the extracted text to its page.&lt;/p&gt;

&lt;p&gt;For business use, add sampling checks and consistency rules. An impossible date, an unknown unit or a total that does not add up must raise an alert rather than be imported silently. As an example, in Python:&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;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;

&lt;span class="n"&gt;KNOWN_UNITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kg&lt;/span&gt;&lt;span class="sh"&gt;"&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;check_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return a list of issues; a non-empty list sends the record to review.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;issues&lt;/span&gt; &lt;span class="o"&gt;=&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;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc_date&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document date is in the future&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="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;KNOWN_UNITS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown unit: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="si"&gt;!r}&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="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line amounts do not add up to the total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each flagged record goes to a review queue with its file and page reference, so the reviewer looks at the original instead of trusting the extracted text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the output before processing the backlog
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Useful output&lt;/th&gt;
&lt;th&gt;Priority check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find a file&lt;/td&gt;
&lt;td&gt;Full-text index&lt;/td&gt;
&lt;td&gt;OCR quality and metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare values&lt;/td&gt;
&lt;td&gt;Structured table&lt;/td&gt;
&lt;td&gt;Units and context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detect an anomaly&lt;/td&gt;
&lt;td&gt;Alert with evidence&lt;/td&gt;
&lt;td&gt;Business rule and source page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query the archive&lt;/td&gt;
&lt;td&gt;Search engine or assistant&lt;/td&gt;
&lt;td&gt;Access rights and citations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The system can process the normal case automatically and isolate the exceptions: unreadable page, incomplete document, handwriting, unusual table or contradiction. That human review queue makes the project more reliable and easier to improve over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: know what you are looking for
&lt;/h2&gt;

&lt;p&gt;Digitizing everything without a precise goal can simply produce a bigger digital backlog. Decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which pieces of information have value;&lt;/li&gt;
&lt;li&gt;how they will be used;&lt;/li&gt;
&lt;li&gt;who needs access;&lt;/li&gt;
&lt;li&gt;in what form they must come out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: separate reading from understanding
&lt;/h2&gt;

&lt;p&gt;Recognizing "12.4" in an image is one thing. Knowing that 12.4 is a specific measurement, in a specific context, with a defined tolerance, is another.&lt;/p&gt;

&lt;p&gt;That layer of logic is exactly what a business application adds on top of OCR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: plan for exceptions
&lt;/h2&gt;

&lt;p&gt;Old documents. Poor quality. Missing pages. Different fields. Contradictory data.&lt;/p&gt;

&lt;p&gt;The system must be able to report the situations it cannot handle, instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: make the data usable in your tools
&lt;/h2&gt;

&lt;p&gt;The output can take several forms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a spreadsheet;&lt;/li&gt;
&lt;li&gt;a search engine;&lt;/li&gt;
&lt;li&gt;an alert;&lt;/li&gt;
&lt;li&gt;a structured database;&lt;/li&gt;
&lt;li&gt;a dashboard;&lt;/li&gt;
&lt;li&gt;an API;&lt;/li&gt;
&lt;li&gt;a business assistant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best output always depends on what has to happen next.&lt;/p&gt;

&lt;h2&gt;
  
  
  From archived image to controlled data: five levels
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F332t5o5vf0esayexysyp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F332t5o5vf0esayexysyp.webp" alt="Five sheets of cream paper arranged like a staircase on a dark surface, each step slightly more orderly than the previous one, the top sheet lit in orange amber: the progression from stored image to controlled data" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What becomes possible&lt;/th&gt;
&lt;th&gt;What is still missing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. File stored&lt;/td&gt;
&lt;td&gt;Opening the document if you know where it is&lt;/td&gt;
&lt;td&gt;Search in the content and reliable context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Text recognized&lt;/td&gt;
&lt;td&gt;Searching for words after OCR&lt;/td&gt;
&lt;td&gt;Structure, error correction, link between a value and its role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Document indexed&lt;/td&gt;
&lt;td&gt;Filtering by type, date, folder or entity&lt;/td&gt;
&lt;td&gt;Business fields comparable across documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Structured data&lt;/td&gt;
&lt;td&gt;Sorting, comparing, exporting, feeding an application&lt;/td&gt;
&lt;td&gt;Quality rules, exceptions and proof of origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Controlled data&lt;/td&gt;
&lt;td&gt;Triggering a workflow with targeted review of uncertain cases&lt;/td&gt;
&lt;td&gt;Continuous tracking of errors, versions and new formats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The right goal is not necessarily level 5 for every archive. Choose the level that enables the intended use, then keep the link to the original so any important value can be checked.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is OCR enough to query an archive?&lt;/strong&gt;&lt;br&gt;
No. It gives you text. You still need to index that text, manage metadata and access rights, and keep the link to the source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do all OCR errors need manual correction?&lt;/strong&gt;&lt;br&gt;
Not necessarily. You can focus review on critical fields and on results below a confidence threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can tables and forms be processed?&lt;/strong&gt;&lt;br&gt;
Yes, but their structure must be tested on every variant. A shifted row can change the meaning of a value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should the original files be deleted?&lt;/strong&gt;&lt;br&gt;
No. The original remains the reference, kept according to your legal obligations and archiving policy. The extracted data is an extra layer on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage is solved, usage is not
&lt;/h2&gt;

&lt;p&gt;If your archives are digitized but still hard to use, the problem is probably less about storage and more about the missing tool between the page and the decision.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am Fabien Kost, an independent developer in France (&lt;a href="https://www.closiqode.com" rel="noopener noreferrer"&gt;ClosiQode&lt;/a&gt;). I turn scans and archives into checked tables and databases that keep a link to every source page. More on &lt;a href="https://www.closiqode.com/extraction-donnees-documents/" rel="noopener noreferrer"&gt;how I approach document extraction&lt;/a&gt; (in French).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources (in French):&lt;/strong&gt; BnF, &lt;a href="https://www.bnf.fr/fr/techniques-et-formats-de-conversion-en-mode-texte" rel="noopener noreferrer"&gt;text conversion techniques and formats&lt;/a&gt; and &lt;a href="https://www.bnf.fr/fr/numerisation-de-masse-qualite-et-formats-utilises-pour-garantir-la-conservation" rel="noopener noreferrer"&gt;mass digitization, quality and formats&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ocr</category>
      <category>automation</category>
      <category>dataengineering</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How RAG lets an AI answer from your company documents (and what it does not fix)</title>
      <dc:creator>Fabien Kost</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/fabienk/how-rag-lets-an-ai-answer-from-your-company-documents-and-what-it-does-not-fix-3lfk</link>
      <guid>https://dev.to/fabienk/how-rag-lets-an-ai-answer-from-your-company-documents-and-what-it-does-not-fix-3lfk</guid>
      <description>&lt;p&gt;&lt;em&gt;Adapted from an article originally published in French on &lt;a href="https://www.closiqode.com/rag-ia-documents-entreprise/" rel="noopener noreferrer"&gt;closiqode.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Retrieval-augmented generation (RAG) lets an AI answer from your own documents. Before writing anything, the system searches your document base for the relevant passages, then builds its answer from them and can say where it came from. The model learns nothing new: at every question, it is handed the excerpts that matter.&lt;/p&gt;

&lt;p&gt;The term shows up in every demo and every quote, and it is rarely explained. The business question behind it is simple: how can a model talk about your procedures, technical sheets or terms of sale without ever having seen them? And what do you need to prepare so its answers are right?&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG in one paragraph
&lt;/h2&gt;

&lt;p&gt;RAG combines two building blocks: a search engine that finds the right passages in your documents, and a language model that writes an answer from those passages. The term comes from a 2020 paper (&lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;Lewis et al., arXiv&lt;/a&gt;) whose authors already wanted to solve two problems: showing where an answer comes from, and updating a model's knowledge.&lt;/p&gt;

&lt;p&gt;The French Directorate General for Enterprise (DGE) sums it up in its November 2024 guide: RAG lets a model "consult" company data before answering. The same guide explains why this is needed: large language models are trained on general text, they know nothing about your company, and their knowledge is frozen at training time.&lt;/p&gt;

&lt;p&gt;A useful picture: without RAG, the model answers from memory. With RAG, it answers open book, with the pages the search engine brought in front of it. Answer quality therefore depends as much on the pages retrieved as on the model reading them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a RAG pipeline works, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgk780e8068k034ckc9e6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgk780e8068k034ckc9e6.webp" alt="Loose sheets of cream paper being cut into neat short strips and filed into a dark wooden card index, one drawer glowing orange amber from inside: documents prepared and indexed before any question" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A RAG system works in two phases: document preparation, done once and again at every update, and retrieval plus answer, done at every question.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before any question: prepare and index
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conversion&lt;/strong&gt;: every document becomes text. A scanned PDF goes through OCR, a slide deck is converted to text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaning&lt;/strong&gt;: remove duplicates, repeated headers and footers, cover pages, and set aside documents that contradict each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunking&lt;/strong&gt;: split documents into short segments, from a few sentences to a section. Chunk size is a decisive setting, to be chosen with the people who know the content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enrichment&lt;/strong&gt;: attach context to every chunk (source document, chapter, date, department) for filtering and citation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing&lt;/strong&gt;: convert every chunk into a vector that represents its meaning, and store those vectors in a vector database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  At every question: retrieve, then answer
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval&lt;/strong&gt;: the question is converted too, then compared to the chunks. The system keeps the closest ones by meaning, by shared keywords, or by a combination of both (hybrid search).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assembly&lt;/strong&gt;: the selected chunks go to the model along with the question and instructions such as "answer only from these excerpts".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sourced answer&lt;/strong&gt;: the model writes the answer and lists the documents used. If nothing relevant was found, a well-designed system says so instead of improvising.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In pseudo-code, the query-time half looks roughly like this:&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="c1"&gt;# Pseudo-code, not a specific library
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&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;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hybrid_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allowed_groups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# access rights at retrieval time
&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;hits&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MIN_SCORE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;refuse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I cannot find this in the validated documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;excerpts&lt;/span&gt;&lt;span class="o"&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;rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Answer only from the excerpts. Cite the source for every claim.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typical use: a technician asks which setting to apply on a given machine model. The engine finds the right page of the manual, the model phrases the answer and points to the manual. If that manual is not in the base, the correct answer is "I cannot find this information", not a plausible value.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG versus a model trained on your data
&lt;/h2&gt;

&lt;p&gt;RAG does not train the model on your documents. It shows them to the model at question time, and the model does not keep them. This is the most common confusion, and it has practical consequences for updates, confidentiality and traceability.&lt;/p&gt;

&lt;p&gt;Fine-tuning changes the model's internal parameters. The knowledge gets blended in: you cannot tell which document a sentence came from, or cleanly remove an outdated fact. The French cybersecurity agency (ANSSI) notes in its April 2024 security recommendations for generative AI that user access rights cannot be managed on training data, whereas they can be on data added in production, such as a vector database.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;RAG&lt;/th&gt;
&lt;th&gt;Fine-tuned model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where the knowledge lives&lt;/td&gt;
&lt;td&gt;In your documents, consulted at every question&lt;/td&gt;
&lt;td&gt;In the model's parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Updating&lt;/td&gt;
&lt;td&gt;Replace the document and reindex it&lt;/td&gt;
&lt;td&gt;A new training run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Citing the source&lt;/td&gt;
&lt;td&gt;Possible, passage by passage&lt;/td&gt;
&lt;td&gt;No reliable link to a document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access rights&lt;/td&gt;
&lt;td&gt;Filterable per user&lt;/td&gt;
&lt;td&gt;Not manageable, according to ANSSI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Good fit&lt;/td&gt;
&lt;td&gt;Answering from a corpus that changes&lt;/td&gt;
&lt;td&gt;Enforcing a very specific style, format or vocabulary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a small or mid-sized company that wants to query its procedures, sheets or contracts, RAG is the logical starting point. Fine-tuning is rarely justified at that scale, and it fits poorly with information that changes often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which documents work, and which cause trouble
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F599gurt8xy7do7xrceye.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F599gurt8xy7do7xrceye.webp" alt="A row of cream paper documents on a dark table, the neatly typed pages lit in orange amber while a crumpled sheet and a dense grid table stay in shadow: some documents suit retrieval, others do not" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RAG performs well on written, current, non-contradictory text. It struggles with complex tables, poor scans and questions that require reading the whole corpus. The DGE guide notes that RAG is generally better on text than on tables, and less suited to summaries spanning many documents.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document type&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;What to plan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Procedures, internal guides, written FAQs&lt;/td&gt;
&lt;td&gt;Good fit: each passage stands on its own&lt;/td&gt;
&lt;td&gt;Make sure only one version circulates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical sheets, manuals&lt;/td&gt;
&lt;td&gt;Good fit if the text is machine-readable&lt;/td&gt;
&lt;td&gt;Keep product references in the text, not only in an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terms and conditions, contract templates&lt;/td&gt;
&lt;td&gt;Good for finding a clause&lt;/td&gt;
&lt;td&gt;Date every version and remove old ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price or specification tables&lt;/td&gt;
&lt;td&gt;Risky: chunking can separate a value from its header&lt;/td&gt;
&lt;td&gt;Specific processing or a structured source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old scans, handwritten documents&lt;/td&gt;
&lt;td&gt;Depends on OCR quality&lt;/td&gt;
&lt;td&gt;Check the extracted text before indexing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email threads&lt;/td&gt;
&lt;td&gt;Noisy: repetition, provisional opinions, decisions later reversed&lt;/td&gt;
&lt;td&gt;Move final decisions into a reference document&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A good starting scope is often a small set of reference documents: the ones that answer the questions asked every week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limits to know before you start
&lt;/h2&gt;

&lt;p&gt;RAG reduces fabricated answers without eliminating them, and it is never better than the documents you give it. The DGE guide says explicitly that hallucinations can still occur with RAG. Four limits come up in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval misses&lt;/strong&gt;: only a limited number of chunks are kept. If the right information is phrased differently or spread across documents, it may not come up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documents contradict each other&lt;/strong&gt;: two versions of a procedure give two possible answers, and the model may pick the wrong one or blend both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outdated versions stay indexed&lt;/strong&gt;: a forgotten old price list keeps producing wrong answers, with a source to back them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A citation does not prove everything&lt;/strong&gt;: you still have to check that the cited passage actually supports the generated sentence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preparing your documents
&lt;/h2&gt;

&lt;p&gt;Preparation matters as much as the technology. The DGE guide calls it an essential step and stresses that domain expertise remains indispensable, even when a contractor handles it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List real questions for a few weeks, with the expected answer and the document that contains it.&lt;/li&gt;
&lt;li&gt;Designate reference documents: one dated version per topic, with an owner.&lt;/li&gt;
&lt;li&gt;Remove duplicates and old versions, or archive them outside the queried scope.&lt;/li&gt;
&lt;li&gt;Spot what is written nowhere: answers that only exist in someone's head must be written down first.&lt;/li&gt;
&lt;li&gt;Classify by access level.&lt;/li&gt;
&lt;li&gt;Check for personal data: the DGE guide lists anonymization or pseudonymization among the cleaning steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I set before developing anything: a useful company RAG answers from a validated corpus, cites the document used, and refuses explicitly when the base does not contain the information. It is tested with real questions before going live, including the ones it must refuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does RAG train the model on my documents?&lt;/strong&gt;&lt;br&gt;
No. Documents stay in a separate base and only a few excerpts are sent per question. Still check the model provider's terms: ANSSI notes that prompts and answers are sometimes reused for training.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you update it when a document changes?&lt;/strong&gt;&lt;br&gt;
Replace the document at the source, reindex it, remove the old version from the index, then replay the test questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open book, not a better memory
&lt;/h2&gt;

&lt;p&gt;RAG gives the model the right pages. Choosing, cleaning and testing those pages is still the real work.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am Fabien Kost, an independent developer in France (&lt;a href="https://www.closiqode.com" rel="noopener noreferrer"&gt;ClosiQode&lt;/a&gt;). I build business AI assistants that answer from validated documents, cite their sources and say so when they cannot find the answer. More on &lt;a href="https://www.closiqode.com/assistant-ia-entreprise/" rel="noopener noreferrer"&gt;how I design them&lt;/a&gt; (in French).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; DGE, &lt;a href="https://www.entreprises.gouv.fr/files/files/Publications/2024/Guides/20241127-bro-guide-ragv4-interactif.pdf" rel="noopener noreferrer"&gt;guide to retrieval-augmented generation&lt;/a&gt; (in French, PDF); ANSSI, &lt;a href="https://messervices.cyber.gouv.fr/guides/recommandations-de-securite-pour-un-systeme-dia-generative" rel="noopener noreferrer"&gt;security recommendations for a generative AI system&lt;/a&gt; (in French); Lewis et al., &lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks&lt;/a&gt; (arXiv, 2020).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>rag</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Extracting data from hundreds of PDFs into Excel (and why a converter is not enough)</title>
      <dc:creator>Fabien Kost</dc:creator>
      <pubDate>Tue, 13 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/fabienk/extracting-data-from-hundreds-of-pdfs-into-excel-and-why-a-converter-is-not-enough-4ghm</link>
      <guid>https://dev.to/fabienk/extracting-data-from-hundreds-of-pdfs-into-excel-and-why-a-converter-is-not-enough-4ghm</guid>
      <description>&lt;p&gt;&lt;em&gt;Adapted from an article originally published in French on &lt;a href="https://www.closiqode.com/extraire-donnees-pdf-vers-excel/" rel="noopener noreferrer"&gt;closiqode.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Getting data out of one PDF into Excel is a copy-paste job. Getting it out of hundreds of PDFs is a pipeline problem: identify each document, extract the text, structure the useful fields (one column per field), then check the result.&lt;/p&gt;

&lt;p&gt;A plain PDF-to-Excel converter breaks down as soon as the files are scanned, badly framed or laid out differently. And there is one rule I never drop: every extracted value must stay linked to the document it came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  A PDF is not necessarily a data source
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwkk2oniuajdw646eyhl.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwkk2oniuajdw646eyhl.webp" alt="Three sheets of cream paper side by side under a dark studio light, only one of them carrying a thin orange glow along its printed lines: documents that look alike but are not built alike" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two documents can look identical on screen. Technically, the first one contains selectable text; the second one is just an image. A third one mixes text, tables, stamps and scanned pages. So processing starts by classifying the files, not by running the same extractor blindly over the whole folder.&lt;/p&gt;

&lt;p&gt;OCR turns areas of an image into usable characters. The French National Library (BnF) points out that its quality depends on contrast, deskewing, print defects and layout complexity. And recognizing the words correctly does not guarantee that the right value was attached to the right field.&lt;/p&gt;

&lt;p&gt;A first triage can be as simple as checking whether each page has a text layer. As an example, with PyMuPDF:&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="c1"&gt;# Example: route each page to native extraction or OCR
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fitz&lt;/span&gt;  &lt;span class="c1"&gt;# PyMuPDF
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route_pages&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="n"&gt;min_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fitz&lt;/span&gt;&lt;span class="p"&gt;.&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;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;native&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;min_chars&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ocr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold is a heuristic, not a rule: a scanned page with a stamp may carry a few characters, and a native page may be almost empty. The point is to make the routing decision explicit and logged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where manual processing hits a wall
&lt;/h2&gt;

&lt;p&gt;The problem rarely shows up with ten files. It becomes visible when a team opens hundreds of reports, invoices, technical sheets or regulatory files every week. People know exactly what to look for, but their time goes into navigating, copying, pasting and repeating the same checks.&lt;/p&gt;

&lt;p&gt;At that point, the goal is not "read every PDF". You need to define a useful output: one row per document in a spreadsheet or a database, an alert on an inconsistency, a list of references, a status to review. Without that decision, the project automates noise.&lt;/p&gt;

&lt;p&gt;So before talking about tools, write down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the fields you need;&lt;/li&gt;
&lt;li&gt;their possible formats;&lt;/li&gt;
&lt;li&gt;the known exceptions;&lt;/li&gt;
&lt;li&gt;the checks each value must pass;&lt;/li&gt;
&lt;li&gt;the expected result.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A traceable pipeline, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt;: formats, languages, scans, tables and known variants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route&lt;/strong&gt;: native extraction for text, OCR for images, specific processing for complex tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalize&lt;/strong&gt;: dates, units, references and missing values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply the business rules&lt;/strong&gt;: consistency checks, thresholds and exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trace&lt;/strong&gt;: keep the document, page and zone each value came from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send ambiguous cases to a person&lt;/strong&gt;: anything below a confidence threshold goes to human review.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In practice, an extracted value is not just a cell. It is a small record, for example:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"report-0412.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivery_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"raw_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12/04/26"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"normalized_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"native_text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"needs_review"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"review_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"day/month order inferred from document locale"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spreadsheet is then only a view over these records. When someone asks where a number comes from, the answer is the file, the page and the rule that accepted it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick the processing path per document
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document observed&lt;/th&gt;
&lt;th&gt;First approach&lt;/th&gt;
&lt;th&gt;Risk to test&lt;/th&gt;
&lt;th&gt;What to keep&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF with selectable text and a stable layout&lt;/td&gt;
&lt;td&gt;Native extraction with position or label rules&lt;/td&gt;
&lt;td&gt;Mixed columns, repeated headers, value attached to the wrong block&lt;/td&gt;
&lt;td&gt;Value, page, zone and source file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clean scan of a known form&lt;/td&gt;
&lt;td&gt;OCR, then extraction of the expected fields&lt;/td&gt;
&lt;td&gt;Look-alike characters, misread checkbox, skewed page&lt;/td&gt;
&lt;td&gt;Value, confidence level and image of the zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents in varied formats&lt;/td&gt;
&lt;td&gt;Classification first, then processing per family&lt;/td&gt;
&lt;td&gt;One model works on frequent cases but fails on a rare variant&lt;/td&gt;
&lt;td&gt;Detected family, processing version and reason for review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex table or degraded document&lt;/td&gt;
&lt;td&gt;Specialized extraction with targeted review&lt;/td&gt;
&lt;td&gt;Structure rebuilt in a plausible but wrong way&lt;/td&gt;
&lt;td&gt;Extracted cells, coordinates and the untouched original&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Test this routing on a sample that also contains the bad scans and the rare layouts. A good overall average must not hide the one document family where the errors concentrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real example: a value present at the wrong level
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tknj9nvjqeykolecz1m.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tknj9nvjqeykolecz1m.webp" alt="A main sheet of cream paper with a smaller component card tucked half underneath it, the hidden edge of the card lit in orange amber light: information that exists but sits at another level" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not all PDFs look alike. Some contain real text, others are scans, tables move around, and the same information can appear in several forms. But the harder problem is elsewhere: detecting a value is not always enough. You need to know what it means in the business.&lt;/p&gt;

&lt;p&gt;In a project I built for an engineering firm, an important piece of regulatory information could be declared at the level of a component while remaining invisible in the main product information. The value was in the PDF, but reading it at the wrong level would have produced a wrong row in the spreadsheet.&lt;/p&gt;

&lt;p&gt;The client deliberately included this case in a validation document, without telling me in advance. The tool caught it.&lt;/p&gt;

&lt;p&gt;That is why document extraction is not just "reading text". The system has to reproduce the rules that make the information useful: read each file, then check each value against the business rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure quality in separate layers
&lt;/h2&gt;

&lt;p&gt;A serious check distinguishes at least four results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was the document opened and every page processed?&lt;/li&gt;
&lt;li&gt;Was the text recognized correctly?&lt;/li&gt;
&lt;li&gt;Were the fields attached to the right context?&lt;/li&gt;
&lt;li&gt;Did the business rule produce the right decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation avoids a false sense of precision. An OCR confidence score measures recognition; it does not prove that the extracted value answers the business question.&lt;/p&gt;

&lt;p&gt;To validate, build a ground truth: a representative batch whose expected results have been checked by hand. Add difficult cases, compare the outputs, and measure the ability to &lt;em&gt;find&lt;/em&gt; the information separately from the ability to &lt;em&gt;interpret&lt;/em&gt; it correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can scanned PDFs be processed?&lt;/strong&gt;&lt;br&gt;
Yes, with OCR. Scan quality remains decisive, and critical values must be checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you need generative AI?&lt;/strong&gt;&lt;br&gt;
Not systematically. Deterministic extraction is often the better choice for stable formats. AI can help with variable wording or layouts, as long as there is validation and traceability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you handle errors?&lt;/strong&gt;&lt;br&gt;
The system must flag unread documents, missing fields and uncertain results. A silent gap is more dangerous than an alert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where do you start?&lt;/strong&gt;&lt;br&gt;
Pick a single document type, define the expected fields and prepare a representative sample. A first test then gives you a realistic view of feasibility and exceptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The spreadsheet is the easy part
&lt;/h2&gt;

&lt;p&gt;The real work is being able to say, for every cell, which page it came from and which rule it passed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am Fabien Kost, an independent developer in France (&lt;a href="https://www.closiqode.com" rel="noopener noreferrer"&gt;ClosiQode&lt;/a&gt;). I build document data extraction tools that read every file, check every value against business rules and keep a link to the source page. More on &lt;a href="https://www.closiqode.com/extraction-donnees-documents/" rel="noopener noreferrer"&gt;how I approach document extraction&lt;/a&gt; (in French).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources (in French):&lt;/strong&gt; BnF, &lt;a href="https://www.bnf.fr/fr/techniques-et-formats-de-conversion-en-mode-texte" rel="noopener noreferrer"&gt;text conversion techniques and formats&lt;/a&gt; and &lt;a href="https://www.bnf.fr/fr/les-referentiels-de-numerisation-de-la-bnf" rel="noopener noreferrer"&gt;BnF digitization standards&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ocr</category>
      <category>pdf</category>
      <category>automation</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>How to reduce hallucinations in a business AI assistant (and why "never make things up" in the prompt is not enough)</title>
      <dc:creator>Fabien Kost</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/fabienk/how-to-reduce-hallucinations-in-a-business-ai-assistant-and-why-never-make-things-up-in-the-4agg</link>
      <guid>https://dev.to/fabienk/how-to-reduce-hallucinations-in-a-business-ai-assistant-and-why-never-make-things-up-in-the-4agg</guid>
      <description>&lt;p&gt;&lt;em&gt;Adapted from an article originally published in French on &lt;a href="https://www.closiqode.com/hallucinations-assistant-ia/" rel="noopener noreferrer"&gt;closiqode.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You cannot remove every hallucination from a generative AI assistant. What you can do is make them rarer, and make sure an unverified answer never triggers a consequence that matters.&lt;/p&gt;

&lt;p&gt;In practice, that means combining six things: a narrow scope, validated sources, an explicit refusal, hard tests, citations and human control.&lt;/p&gt;

&lt;p&gt;The French data protection authority (CNIL) puts it simply: generative models are not knowledge bases. Their probabilistic logic can produce a result that is wrong but plausible. The real danger is not the visible error. It is the convincing answer nobody thinks of checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Narrow the scope
&lt;/h2&gt;

&lt;p&gt;The more an assistant tries to answer everything, the further it drifts from its job.&lt;/p&gt;

&lt;p&gt;Write down the allowed topics, the forbidden topics, who the users are and what the assistant is allowed to do. "Answer HR questions" is too broad. "Find the validated administrative steps for a leave request" is testable.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Ground it in verified sources
&lt;/h2&gt;

&lt;p&gt;For a business assistant, the document base matters more than the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;documents that are up to date,&lt;/li&gt;
&lt;li&gt;identified versions,&lt;/li&gt;
&lt;li&gt;content that someone has actually validated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A RAG system connects the model to a search over a separate knowledge base. It can anchor answers in your own information, but a bad retrieval, an obsolete document or a wrong interpretation are still possible. Give every important source an owner and a review date.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make "I don't know" a feature
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqj0n93xe7ceke893y9e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjqj0n93xe7ceke893y9e.webp" alt="A stack of validated paper cards next to a single blank card outlined in orange light: an answer deliberately left empty instead of invented" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The assistants I build are designed to refuse explicitly when the knowledge base does not support a reliable answer.&lt;/p&gt;

&lt;p&gt;A good refusal is useful. It says what is missing, suggests which source to provide, or hands over to a person. A vague "I'm not sure" is not enough if the user can still take the answer at face value.&lt;/p&gt;

&lt;p&gt;A minimal instruction looks like this, but remember that the instruction alone guarantees nothing: the tests in step 4 are what prove it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Answer only from the provided excerpts.
If the excerpts do not contain the answer, say so explicitly,
list what information is missing, and suggest who to ask.
Quote the source document and version for every factual claim.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Test the hard questions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs3yc7n7ks1n5jkf2ibnc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs3yc7n7ks1n5jkf2ibnc.webp" alt="A rack of paper test tubes, only one lit from inside in orange: every case gets tested" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your test set should cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ambiguous questions,&lt;/li&gt;
&lt;li&gt;missing information,&lt;/li&gt;
&lt;li&gt;contradictory sources,&lt;/li&gt;
&lt;li&gt;unusual wording,&lt;/li&gt;
&lt;li&gt;edge cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A system tested only on obvious cases gives a false sense of reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  A minimal protocol
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Collect real questions, each with the expected answer and the expected source.&lt;/li&gt;
&lt;li&gt;Add impossible, out-of-scope or deliberately misleading questions.&lt;/li&gt;
&lt;li&gt;Test several phrasings of every critical question.&lt;/li&gt;
&lt;li&gt;Score retrieval relevance and answer faithfulness to the source &lt;strong&gt;separately&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Replay the whole test set after every change of model, prompt or corpus.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A test case can be as simple as:&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="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 in advance must a leave request be submitted?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expected_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hr-procedures-v3.pdf, section 2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expected_behavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"answer_with_citation"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with a sibling case where &lt;code&gt;expected_behavior&lt;/code&gt; is &lt;code&gt;refuse&lt;/code&gt; because the corpus does not contain the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Do not automate sensitive decisions blindly
&lt;/h2&gt;

&lt;p&gt;The assistant can prepare, search, summarize or route. That does not mean every decision should happen without a human.&lt;/p&gt;

&lt;p&gt;Match the control to the consequence:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Possible consequence&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Recommended control&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low and reversible&lt;/td&gt;
&lt;td&gt;Drafting an internal note&lt;/td&gt;
&lt;td&gt;Simple review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Answering a customer about a procedure&lt;/td&gt;
&lt;td&gt;Visible source and an escalation path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Committing spend or acting on a person&lt;/td&gt;
&lt;td&gt;Mandatory human validation and an audit trail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  6. Keep the sources visible
&lt;/h2&gt;

&lt;p&gt;When the stakes justify it, let users check where the information came from.&lt;/p&gt;

&lt;p&gt;The citation must point to the document that was actually used, with its version if needed. And check that the cited passage really supports the generated sentence: a reference being present is not proof that it is the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Monitor errors in production
&lt;/h2&gt;

&lt;p&gt;Launch is not the end of the work. Add a way to flag an answer, keep what you need for diagnosis while respecting data protection rules, and turn every incident into a new test case.&lt;/p&gt;

&lt;p&gt;Track useful indicators: answers without a source, appropriate refusals, human corrections, out-of-corpus questions and error severity. A satisfaction score alone does not measure reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagnose before you fix
&lt;/h2&gt;

&lt;p&gt;Not every error comes from the model. To fix an assistant for good, find the step that failed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observed error&lt;/th&gt;
&lt;th&gt;Diagnostic question&lt;/th&gt;
&lt;th&gt;Useful check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The right source was not retrieved&lt;/td&gt;
&lt;td&gt;Was the document present, current and accessible to this user?&lt;/td&gt;
&lt;td&gt;Test retrieval on its own; check permissions, metadata and chunking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The source is right but the answer distorts it&lt;/td&gt;
&lt;td&gt;Is the generated sentence really supported by the cited passage?&lt;/td&gt;
&lt;td&gt;Compare claim and excerpt, limit rewriting, force a refusal when evidence is weak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The answer is fine but the action would be risky&lt;/td&gt;
&lt;td&gt;What happens if it is misread?&lt;/td&gt;
&lt;td&gt;Block automatic action, show the source, require proportionate human validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This keeps you from "fixing" every incident by adding one more line to the prompt. Each error becomes a reproducible test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does not work on its own
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Writing "never make things up" in the prompt with no other control.&lt;/li&gt;
&lt;li&gt;Connecting every document without managing versions and permissions.&lt;/li&gt;
&lt;li&gt;Testing only on prepared demos.&lt;/li&gt;
&lt;li&gt;Confusing a fluent answer with a proven one.&lt;/li&gt;
&lt;li&gt;Hiding the limits from users to make the tool look more impressive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does RAG eliminate hallucinations?&lt;/strong&gt;&lt;br&gt;
No. It provides external, updatable context, which can reduce some errors. Retrieval and generation still have to be evaluated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a more powerful model enough?&lt;/strong&gt;&lt;br&gt;
Not to guarantee the reliability of a business process. Sources, scope, tests and controls are still needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should you put a warning everywhere?&lt;/strong&gt;&lt;br&gt;
A warning does not replace system design. Users should understand the limits, but sensitive decisions mainly need to be blocked or validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  A good assistant does not always answer
&lt;/h2&gt;

&lt;p&gt;It is the one that also knows when not to.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am Fabien Kost, an independent developer in France (&lt;a href="https://www.closiqode.com" rel="noopener noreferrer"&gt;ClosiQode&lt;/a&gt;). I build business AI assistants that answer from validated documents and say so when they cannot find the answer. More on &lt;a href="https://www.closiqode.com/assistant-ia-entreprise/" rel="noopener noreferrer"&gt;how I design them&lt;/a&gt; (in French).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources (in French):&lt;/strong&gt; CNIL, &lt;a href="https://www.cnil.fr/fr/les-questions-reponses-de-la-cnil-sur-lutilisation-dun-systeme-dia-generative" rel="noopener noreferrer"&gt;questions and answers on using generative AI systems&lt;/a&gt; and &lt;a href="https://www.cnil.fr/fr/comment-deployer-une-ia-generative-la-cnil-apporte-de-premieres-precisions" rel="noopener noreferrer"&gt;first guidance on deploying generative AI&lt;/a&gt;.&lt;/p&gt;

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