[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"portal-settings:stajic:ru":3,"public-menus:all":38,"post:where-does-an-llm-get-its-data-rag-data-sources-in-python:ru":205,"related:post:where-does-an-llm-get-its-data-rag-data-sources-in-python:ru:1":1406},{"statusCode":4,"data":5,"message":37},200,{"tenantId":6,"lang":7,"defaultLang":8,"siteUrl":9,"contactEmail":10,"brandName":11,"logoUrl":12,"siteName":11,"siteDescription":13,"ogImage":10,"robotsIndex":14,"socialLinks":10,"reservedSlugs":10,"seoPolicy":15},"stajic","ru","de","https:\u002F\u002Fstajic.de",null,"Stajic Platform","\u002FLogo_Planet.svg","Stajic Portal",true,{"branding":16,"relatedContent":17,"crossDomainLinks":18},{"logoUrl":12},{"enabled":14},[19,22,25,28,31,34],{"url":20,"label":21,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Ffigure.rocks","figure.rocks",{"url":23,"label":24,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Floving.rocks","loving.rocks",{"url":26,"label":27,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Fbazify.com","bazify.com",{"url":29,"label":30,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Fbazify.de","bazify.de",{"url":32,"label":33,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Fbazify.at","bazify.at",{"url":35,"label":36,"isActive":14,"showInFooter":14,"includeInSameAs":14},"https:\u002F\u002Fbazify.ba","bazify.ba","Portal settings resolved",[39,45],{"id":40,"name":41,"location":42,"isActive":14,"isDefault":43,"items":44},1,"main-navigation","header",false,[],{"id":46,"name":47,"location":48,"isActive":14,"isDefault":14,"items":49},4,"main-menu","sidebar",[50,66,79,93,103,118,133],{"id":51,"title":52,"url":60,"target":61,"icon":62,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":64,"portfolioId":10,"children":65},"item-18",{"de":53,"en":54,"es":55,"fr":56,"it":54,"ru":57,"sr":58,"zh":59},"Startseite","Home","Inicio","Accueil","Главная","Почетна","首页","\u002Ffull-stack-web-developer-munich-performance-seo-and-maintainable-builds","_self","i-lucide-home","page",111,[],{"id":67,"title":68,"url":75,"target":61,"icon":76,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":77,"portfolioId":10,"children":78},"item-22",{"de":69,"en":69,"es":70,"fr":69,"it":71,"ru":72,"sr":73,"zh":74},"Vision","Visión","Visione","Видение","Визија","想象","\u002Fueber-uns-webdesign-muenchen-webaplikation","i-lucide-eye",113,[],{"id":80,"title":81,"url":89,"target":61,"icon":90,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":91,"portfolioId":10,"children":92},"item-19",{"de":82,"en":83,"es":84,"fr":83,"it":85,"ru":86,"sr":87,"zh":88},"Leistungen","Services","Servicios","Servizi","Услуги","Услуге","服务","\u002Fservices-dienstleistungen-muenchen","i-lucide-wrench",116,[],{"id":94,"title":95,"url":99,"target":61,"icon":100,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":101,"portfolioId":10,"children":102},"item-23",{"de":96,"en":96,"es":96,"fr":96,"it":96,"ru":97,"sr":97,"zh":98},"Blog","Блог","博客","\u002Fblog","i-lucide-book-open",112,[],{"id":104,"title":105,"url":114,"target":61,"icon":115,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":116,"portfolioId":10,"children":117},"item-32",{"de":106,"en":107,"es":108,"fr":109,"it":110,"ru":111,"sr":112,"zh":113},"Neue Technologien","New Technologies","Nuevas tecnologías","Nouvelles technologies","Nuove tecnologie","Новые технологии","Нове технологије","新技术！","\u002Fneue-webtechnologien","i-lucide-sparkles",122,[],{"id":119,"title":120,"url":129,"target":61,"icon":130,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":131,"portfolioId":10,"children":132},"item-20",{"de":121,"en":122,"es":123,"fr":124,"it":125,"ru":126,"sr":127,"zh":128},"Kontakt","Contact us!","Contacto","Contact","Contatto","Контакт","Контактирајте нас","联系我们！","\u002Fcontact","i-lucide-mail",115,[],{"id":134,"title":135,"url":144,"target":61,"icon":145,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":146,"portfolioId":10,"children":147},"item-21",{"de":136,"en":137,"es":138,"fr":139,"it":140,"ru":141,"sr":142,"zh":143},"Unsere Arbeit","Our Work","Nuestro trabajo","Nos réalisations","I nostri lavori","Наши работы","Наши радови","文件夹","\u002Fportfolio","i-lucide-briefcase",114,[148,161,175,181,193],{"id":149,"title":150,"url":144,"target":61,"icon":159,"isActive":14,"type":63,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":146,"portfolioId":10,"children":160},"item-24",{"de":151,"en":152,"es":153,"fr":154,"it":155,"ru":156,"sr":157,"zh":158},"Alle Projekte","All Projects","Todos los proyectos","Tous les projets","Tutti i progetti","Все проекты","Сви пројекти","所有项目","i-lucide-grid-3x3",[],{"id":162,"title":163,"url":171,"target":61,"icon":172,"isActive":14,"type":173,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":10,"portfolioId":10,"children":174},"item-29",{"de":164,"en":165,"es":166,"fr":167,"it":168,"ru":169,"sr":170,"zh":143},"Local Roots, Global Reach","Local Roots - Global Reach","Empresa local ","Entreprise locale","Azienda locale","Местная компания","Локално предузеће глобално тржиште","\u002Fportfolio\u002Flocal-roots-global-reach-communication-media-systems-for-modern-business","i-lucide-folder","custom",[],{"id":176,"title":177,"url":179,"target":61,"icon":172,"isActive":14,"type":173,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":10,"portfolioId":10,"children":180},"item-28",{"de":178,"en":178,"es":178,"fr":178,"it":178,"ru":178,"sr":178,"zh":178},"Solr Suggester","\u002Fportfolio\u002Fsolr-fuzzy-suggester-und-solr-infix-suggester-abfrage-ueber-ajax-und-filterung",[],{"id":182,"title":183,"url":191,"target":61,"icon":172,"isActive":14,"type":173,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":10,"portfolioId":10,"children":192},"item-27",{"de":184,"en":185,"es":186,"fr":187,"it":188,"ru":189,"sr":190,"zh":185},"Firmenwebseite SEO","Company Website SEO","Sitio web corporativo SEO","Site web d’entreprise SEO","Sito web aziendale SEO","Корпоративный сайт SEO","Пословна веб-страница SEO","\u002Fportfolio\u002Fseo-sem-branding-mobile-webseite-muenchen",[],{"id":194,"title":195,"url":203,"target":61,"icon":172,"isActive":14,"type":173,"productId":10,"categoryId":10,"shopCategoryId":10,"articleId":10,"pageId":10,"portfolioId":10,"children":204},"item-31",{"de":196,"en":197,"es":198,"fr":199,"it":200,"ru":201,"sr":202,"zh":197},"Digitalisierungsportal","Digitalization Portal","Portal de digitalización","Portail de numérisation","Portale di digitalizzazione","Портал цифровизации","Портал за дигитализацију","\u002Fportfolio\u002Fdigitalisierungsportal-archiv-museum-bibliothek-ead-lido-mets-mods",[],{"statusCode":4,"data":206,"message":1405},{"id":207,"title":208,"slug":209,"content":210,"contentJson":211,"excerpt":677,"featuredImage":678,"featuredImageAlt":679,"featuredImageCaption":10,"featuredImageTitle":10,"featuredImageCopyright":10,"featuredImageAuthor":10,"featuredImageSourceUrl":10,"featuredImageLicense":10,"featuredImageIsAiGenerated":43,"status":680,"publishedAt":681,"createdAt":682,"updatedAt":683,"seoLocalePaths":684,"categories":693,"author":694,"translations":699},"479","Откуда LLM берёт данные? Источники данных RAG в Python","where-does-an-llm-get-its-data-rag-data-sources-in-python","\u003Cp>Предыдущая статья, \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">Что такое RAG? Самое простое объяснение того, как это работает\u003C\u002Fa>, заложила ментальную модель: LLM пишет, RAG извлекает полезные знания, приложение владеет текущим состоянием, а инструменты выполняют действия. Эта статья делает следующий шаг: \u003Cb>откуда на самом деле берутся данные и как выглядит извлечение в Python?\u003C\u002Fb>\u003C\u002Fp>\n\u003Cp>Важный сюрприз заключается в том, что «источник данных для LLM» обычно не представляет собой ничего экзотического. Это может быть текстовый файл, папка с Markdown-документами, база данных SQL, ответ API, каталог товаров, система поддержки или векторный индекс, построенный на основе этих источников. ИИ не знает эти системы магическим образом. Ваше приложение должно загрузить, запросить, найти или извлечь соответствующие данные и поместить результат в контекст модели.\u003C\u002Fp>\n\u003Cblockquote class=\"border-l-4 border-gray-300 pl-4 italic\">Источник данных = где находится информация. Извлечение = как приложение находит полезную информацию. Контекст = выбранная информация, переданная модели. LLM = компонент, который интерпретирует этот контекст и генерирует ответ.\u003Ccite class=\"block mt-2 text-sm\">— Четырёхчастная модель, используемая на протяжении всей этой статьи\u003C\u002Fcite>\u003C\u002Fblockquote>\n\u003Ch2 id=\"section-4\">Вопрос\u003C\u002Fh2>\n\u003Cp>Как LLM использует внешние данные, такие как файлы, базы данных или API, и как небольшая программа на Python может реализовать основные шаги RAG, не скрывая их за фреймворком?\u003C\u002Fp>\n\u003Ch2 id=\"section-6\">Что это на самом деле означает\u003C\u002Fh2>\n\u003Cp>Когда разработчики говорят, что LLM «подключена к данным компании», за этой фразой может скрываться несколько различных операций. Одно приложение может выполнять SQL. Другое может вызывать API. Третье может выполнять полнотекстовый поиск. Четвёртое может вычислять сходство эмбеддингов по фрагментам документов. Все они могут предоставлять внешнюю информацию для LLM, но это не один и тот же метод извлечения, и их не следует рассматривать как взаимозаменяемые.\u003C\u002Fp>\n\u003Cp>Это различие важно, потому что лучший метод извлечения зависит от формы вопроса. «Какова наша политика возврата?» — это задача извлечения документов. «Каков текущий статус заказа 4711?» — это обычно структурированный запрос к базе данных. «В каком абзаце обсуждается восстановление аккаунта?» — это может быть поиск по ключевым словам или семантический поиск. RAG наиболее полезен, когда система должна \u003Cb>обнаружить релевантные знания до генерации\u003C\u002Fb>.\u003C\u002Fp>\n\u003Ch2 id=\"section-9\">Простейший пример\u003C\u002Fh2>\n\u003Cp>Начните с трёх строк в обычном Python. Здесь нет векторной базы данных, нет фреймворка и пока нет LLM. Мы лишь хотим сделать шаг извлечения видимым.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>documents = [\n    &quot;The AKM uses 7.62 mm ammunition.&quot;,\n    &quot;A Med Kit restores health.&quot;,\n    &quot;A 4x scope can be attached to several compatible weapons.&quot;\n]\n\nquestion = &quot;Which ammunition does the AKM use?&quot;\n\nfor document in documents:\n    if &quot;AKM&quot; in document:\n        print(document)\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Программа выводит первое предложение, потому что оно содержит искомый термин. Это примитивное извлечение, но архитектура уже видна: \u003Cb>вопрос → поиск → релевантный текст\u003C\u002Fb>. RAG добавляет ещё один важный шаг: передать извлечённый текст языковой модели вместе с вопросом.\u003C\u002Fp>\n\u003Cp>Немного более общая версия ранжирует документы по пересечению терминов запроса:\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>import re\n\ndocuments = [\n    {&quot;id&quot;: &quot;weapon-akm&quot;, &quot;text&quot;: &quot;The AKM uses 7.62 mm ammunition.&quot;},\n    {&quot;id&quot;: &quot;healing-medkit&quot;, &quot;text&quot;: &quot;A Med Kit restores health.&quot;},\n    {&quot;id&quot;: &quot;scope-4x&quot;, &quot;text&quot;: &quot;A 4x scope can be attached to several compatible weapons.&quot;},\n]\n\ndef words(text):\n    return set(re.findall(r&quot;[a-zA-Z0-9.]+&quot;, text.lower()))\n\ndef retrieve(question, documents, top_k=2):\n    query_terms = words(question)\n    ranked = []\n\n    for document in documents:\n        score = len(query_terms &amp; words(document[&quot;text&quot;]))\n        if score &gt; 0:\n            ranked.append((score, document))\n\n    ranked.sort(key=lambda item: item[0], reverse=True)\n    return [document for _, document in ranked[:top_k]]\n\nquestion = &quot;Which ammunition does the AKM use?&quot;\nhits = retrieve(question, documents)\n\nfor hit in hits:\n    print(hit[&quot;id&quot;], &quot;-&gt;&quot;, hit[&quot;text&quot;])\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Это не производственная поисковая система. Она игнорирует морфологию, синонимы, варианты написания, длину документа и многие сигналы ранжирования. Её ценность образовательная: \u003Cb>RAG начинается не с векторной базы данных. Он начинается с извлечения.\u003C\u002Fb>\u003C\u002Fp>\n\u003Ch2 id=\"section-16\">Где пример перестаёт работать\u003C\u002Fh2>\n\u003Cp>Точное или лексическое сопоставление становится слабым, когда вопрос и источник используют разные слова. В документе может быть сказано «обслуживание транспортного средства», а пользователь спрашивает: «как мне отремонтировать машину?» Лексический поисковик может упустить эту связь, хотя человек видит её сразу. Семантическое извлечение решает эту проблему, представляя текст в виде векторов и сравнивая смысл, а не только точные токены.\u003C\u002Fp>\n\u003Cp>Длинные файлы создают ещё одну проблему. Поиск по всему 80-страничному руководству как по единому целому слишком груб, но разбиение на каждое предложение может разрушить полезный контекст. Поэтому реальные системы RAG нуждаются в решениях относительно парсинга, разбиения на фрагменты, метаданных, ранжирования, актуальности, разрешений и происхождения данных.\u003C\u002Fp>\n\u003Cp>В примере также ничего не говорится о структурированных актуальных фактах. Если пользователь спрашивает о текущем статусе заказа 4711, а в приложении уже есть ключ базы данных, семантический поиск обычно является неподходящим первым инструментом. Детерминированный запрос к базе данных лучше.\u003C\u002Fp>\n\u003Ch2 id=\"section-20\">Прямой ответ\u003C\u002Fh2>\n\u003Cp>Источник данных для LLM — это любая внешняя система, из которой приложение может получить информацию для модели: файлы, базы данных, API, поисковые индексы, векторные хранилища или текущее состояние приложения. RAG — это паттерн \u003Cb>извлечения релевантных знаний из таких источников перед генерацией\u003C\u002Fb>.\u003C\u002Fp>\n\u003Cp>В Python основной конвейер может быть очень небольшим: \u003Cb>загрузить данные → создать извлекаемые единицы → найти релевантные доказательства → собрать контекст → вызвать LLM\u003C\u002Fb>. Метод извлечения должен соответствовать источнику и вопросу. Используйте SQL для точных структурированных фактов, полнотекстовый поиск для лексического сопоставления, эмбеддинги для семантической близости и гибридное извлечение, когда ценны несколько сигналов.\u003C\u002Fp>\n\u003Ch2 id=\"section-23\">Почему это так\u003C\u002Fh2>\n\u003Cp>Языковая модель не получает автоматически содержимое вашей файловой системы, базы данных PostgreSQL, CRM, частного API или недавно отредактированного документа. Приложение решает, какая внешняя информация доступна и что помещается в текущий контекст модели.\u003C\u002Fp>\n\u003Cp>Оригинальная работа по Retrieval-Augmented Generation Льюиса и др. объединила генеративную модель с внешней непараметрической памятью, извлекаемой из плотного векторного индекса. Более широкая архитектурная идея сохраняется за пределами этой конкретной реализации: внешние доказательства можно извлекать во время вывода вместо того, чтобы ожидать, что все полезные знания закодированы в параметрах модели.\u003C\u002Fp>\n\u003Cp>Это создает полезное разделение обязанностей: источник хранит информацию, средство извлечения выбирает доказательства, контекст переносит эти доказательства в запрос, а модель интерпретирует их. Сохранение этих границ видимыми значительно упрощает диагностику сбоев.\u003C\u002Fp>\n\u003Ch2 id=\"section-27\">Контекст: основные типы источников данных\u003C\u002Fh2>\n\u003Cdiv class=\"overflow-x-auto\">\u003Ctable class=\"w-full border-collapse\">\u003Cthead>\u003Ctr>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Источник\u003C\u002Fth>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Типичный метод извлечения\u003C\u002Fth>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Подходит для\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">TXT \u002F Markdown \u002F HTML\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Парсинг + лексический или семантический поиск\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Документация, руководства, статьи, заметки\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">PDF \u002F DOCX\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Извлечение с учетом структуры + поиск\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Политики, отчеты, договоры, руководства\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">База данных SQL\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">SQL-запрос или фильтрованное извлечение\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Заказы, пользователи, продукты, структурированные записи\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">REST \u002F GraphQL API\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">HTTP-запрос с параметрами\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Удаленные системы и данные живых сервисов\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Поисковый индекс\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">BM25 \u002F полнотекстовый \u002F гибридный поиск\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Большие текстовые коллекции\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Векторный индекс\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Сходство эмбеддингов\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Семантическое извлечение документов\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Состояние приложения\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Прямое чтение состояния или вызов инструмента\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Что верно прямо сейчас\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Векторный индекс заслуживает особого внимания. Во многих архитектурах он \u003Cb>не является каноническим источником истины\u003C\u002Fb>. Это индекс извлечения, производный от документов или записей. Авторитетный документ может находиться в объектном хранилище, CMS, Git, PostgreSQL или другой системе, а эмбеддинги и метаданные хранятся отдельно для быстрого семантического поиска. Некоторые системы действительно используют векторное хранилище в качестве основного хранилища, но это архитектурный выбор, а не требование RAG.\u003C\u002Fp>\n\u003Cp>Если граница между извлечением, постоянной памятью, текущим состоянием и контекстом модели все еще неясна, см. \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fai-agent-memory-is-not-rag-how-to-separate-memory-retrieval-state-and-context\">AI Agent Memory Is Not RAG\u003C\u002Fa>. Эти слои могут использовать некоторые из одних и тех же технологий хранения, но при этом иметь разные правила корректности.\u003C\u002Fp>\n\u003Ch2 id=\"section-31\">Допущения\u003C\u002Fh2>\n\u003Cul>\u003Cli>Приложению разрешен доступ к внешнему источнику.\u003C\u002Fli>\u003Cli>Соответствующий источник содержит достаточно информации для ответа на вопрос.\u003C\u002Fli>\u003Cli>Данные можно разобрать или запросить в форме, пригодной для использования слоем извлечения.\u003C\u002Fli>\u003Cli>Извлеченная информация достаточно свежа для запрашиваемого решения.\u003C\u002Fli>\u003Cli>Модель получает выбранные доказательства в своем контексте.\u003C\u002Fli>\u003Cli>Авторизация применяется до того, как защищенные доказательства попадут к модели.\u003C\u002Fli>\u003Cli>Генеративная модель все еще может ошибаться, даже если извлечение корректно.\u003C\u002Fli>\u003C\u002Ful>\n\u003Cp>Эти допущения важны, потому что извлечение не может компенсировать отсутствующие доказательства, устаревшие версии источников, сломанные парсеры или несанкционированный доступ. Конвейер RAG может быть настолько надежным, насколько надежен путь доказательств, который его питает.\u003C\u002Fp>\n\u003Ch2 id=\"section-34\">Переменные\u003C\u002Fh2>\n\u003Cdiv class=\"overflow-x-auto\">\u003Ctable class=\"w-full border-collapse\">\u003Cthead>\u003Ctr>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Переменная\u003C\u002Fth>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Почему она меняет дизайн\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Структура источника\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Таблица SQL, юридический PDF и репозиторий исходного кода требуют разных стратегий извлечения\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Тип вопроса\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Точный поиск, концептуальный поиск и многошаговое исследование — это разные задачи\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Требование к свежести\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Живое состояние может требовать прямых запросов вместо периодически перестраиваемых индексов\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Размер корпуса\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Поиск в памяти может работать для сотен фрагментов, но не для очень больших коллекций\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Язык\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Многоязычное извлечение требует моделей и токенизации, подходящих для фактических языков\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Разрешения\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Извлечение должно фильтроваться по правам доступа текущего пользователя\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Задержка и стоимость\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Больше этапов извлечения может улучшить качество, но добавить время выполнения и затраты на инфраструктуру\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Потребность в происхождении\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Системы с высоким доверием нуждаются в идентификаторах источников, версиях и отслеживаемых доказательствах\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Ch2 id=\"section-36\">Диагностический \u002F решающий метод\u003C\u002Fh2>\n\u003Cp>Первое решение — не «Какую векторную базу данных мне установить?» Оно таково: \u003Cb>Какого рода факт я пытаюсь извлечь?\u003C\u002Fb>\u003C\u002Fp>\n\u003Cdiv class=\"overflow-x-auto\">\u003Ctable class=\"w-full border-collapse\">\u003Cthead>\u003Ctr>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Тип вопроса\u003C\u002Fth>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Предпочтительный первый подход\u003C\u002Fth>\u003Cth class=\"border border-gray-300 px-4 py-2 text-left font-semibold\">Причина\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Точный ID или текущая запись\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">SQL \u002F поиск по ключу \u002F API\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Детерминированный структурированный доступ\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Точная формулировка, коды, названия\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Полнотекстовый или ключевой поиск\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Лексическая точность\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Концептуальный вопрос по документам\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Семантический векторный поиск\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Смысл может отличаться от формулировки\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Смешанные корпоративные знания\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Гибридный поиск + фильтры по метаданным\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Объединяет лексические и семантические сигналы\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Текущее состояние приложения\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Прямой доступ к состоянию\u002Fинструменту\u003C\u002Ftd>\u003Ctd class=\"border border-gray-300 px-4 py-2\">Актуальность важнее сходства документов\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Полезная проверка: \u003Cb>Я уже знаю, какая запись мне нужна, или система должна обнаружить, какой фрагмент релевантен?\u003C\u002Fb> Если запись известна, запрашивайте её напрямую. Если релевантность нужно обнаружить, поиск становится важнее.\u003C\u002Fp>\n\u003Cp>Когда ответ неверен, диагностируйте конвейер по порядку, а не сразу меняйте LLM:\u003C\u002Fp>\n\u003Col>\u003Cli>\u003Cb>1. Покрытие источников:\u003C\u002Fb> Существует ли правильная информация в доступном наборе источников?\u003C\u002Fli>\u003Cli>\u003Cb>2. Актуальность:\u003C\u002Fb> Достаточно ли свежа эта версия для вопроса?\u003C\u002Fli>\u003Cli>\u003Cb>3. Разбор:\u003C\u002Fb> Был ли релевантный контент извлечён правильно?\u003C\u002Fli>\u003Cli>\u003Cb>4. Разбиение на фрагменты:\u003C\u002Fb> Остались ли доказательства вместе с условиями, придающими им смысл?\u003C\u002Fli>\u003Cli>\u003Cb>5. Поиск:\u003C\u002Fb> Появляется ли правильный фрагмент среди кандидатов?\u003C\u002Fli>\u003Cli>\u003Cb>6. Ранжирование:\u003C\u002Fb> Ранжируются ли более сильные источники выше более слабых или противоречащих?\u003C\u002Fli>\u003Cli>\u003Cb>7. Сборка контекста:\u003C\u002Fb> Действительно ли приложение отправило выбранные доказательства модели?\u003C\u002Fli>\u003Cli>\u003Cb>8. Генерация:\u003C\u002Fb> Использовала ли LLM предоставленные доказательства достоверно?\u003C\u002Fli>\u003Cli>\u003Cb>9. Атрибуция:\u003C\u002Fb> Можно ли проследить каждое важное утверждение до источника?\u003C\u002Fli>\u003C\u002Fol>\n\u003Cp>Для более глубокого метода отладки в продакшене см. \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Frag-failed-but-which-layer-actually-failed-a-diagnostic-method\">RAG Failed — But Which Layer Actually Failed? A Diagnostic Method\u003C\u002Fa>, где эта цепочка расширяется до независимо тестируемых слоёв сбоев.\u003C\u002Fp>\n\u003Ch2 id=\"section-43\">Доказательства\u003C\u002Fh2>\n\u003Cp>Статья о RAG от Lewis et al. формализовала генерацию, обусловленную извлечённой внешней памятью, а не опирающуюся только на параметры модели. Это даёт концептуальную основу для отделения генератора от извлекаемого источника знаний.\u003C\u002Fp>\n\u003Cp>Sentence Transformers описывает семантический поиск как встраивание корпуса и запроса в векторное пространство и извлечение элементов с высокой семантической близостью. Его текущий API также различает кодирование запроса и кодирование документа для задач поиска.\u003C\u002Fp>\n\u003Cp>SQLite FTS5 демонстрирует другую сторону спектра: зрелый полнотекстовый поиск может ранжировать документы без эмбеддингов. Это важно, потому что лексический поиск остаётся ценным для идентификаторов, точной терминологии и многих гибридных схем поиска.\u003C\u002Fp>\n\u003Cp>Документация OpenAI по эмбеддингам описывает эмбеддинги как числовые векторные представления, используемые для оценки связанности и поиска. Это один из путей реализации семантического поиска, а не определение самого RAG.\u003C\u002Fp>\n\u003Ch2 id=\"section-48\">Реальный пример 1: Папка с текстовыми файлами\u003C\u002Fh2>\n\u003Cp>Предположим, каталог с именем \u003Ccode>knowledge\u002F\u003C\u002Fcode> содержит обычные текстовые файлы. Python может загрузить их вообще без библиотек ИИ.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>from pathlib import Path\n\ndef load_text_files(folder=&quot;knowledge&quot;):\n    documents = []\n\n    for path in Path(folder).glob(&quot;*.txt&quot;):\n        documents.append({\n            &quot;source&quot;: path.name,\n            &quot;text&quot;: path.read_text(encoding=&quot;utf-8&quot;)\n        })\n\n    return documents\n\ndocuments = load_text_files()\n\nfor document in documents:\n    print(document[&quot;source&quot;], len(document[&quot;text&quot;]))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Файловая система — это источник данных. Следующий вопрос: сколько текста должно стать одной извлекаемой единицей. Для длинных документов поиск по одному полному файлу часто слишком груб. Именно поэтому конвейеры RAG обычно создают фрагменты.\u003C\u002Fp>\n\u003Ch3 id=\"section-52\">Очень простой разбиватель на фрагменты\u003C\u002Fh3>\n\u003Cpre class=\"code-block\">\u003Ccode>def chunk_text(text, max_chars=800):\n    paragraphs = [p.strip() for p in text.split(&quot;\\n\\n&quot;) if p.strip()]\n\n    chunks = []\n    current = &quot;&quot;\n\n    for paragraph in paragraphs:\n        candidate = f&quot;{current}\\n\\n{paragraph}&quot;.strip()\n\n        if current and len(candidate) &gt; max_chars:\n            chunks.append(current)\n            current = paragraph\n        else:\n            current = candidate\n\n    if current:\n        chunks.append(current)\n\n    return chunks\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Этот пример группирует абзацы, пока не достигнут примерный лимит символов. Он намеренно понятный, а не оптимальный. Продакшен-системы часто разбивают по токенам, заголовкам, разделам, границам предложений или структуре документа. Таблицы, исходный код, договоры и документация API могут требовать разных стратегий.\u003C\u002Fp>\n\u003Ch3 id=\"section-55\">Сохранение происхождения при разбиении на фрагменты\u003C\u002Fh3>\n\u003Cpre class=\"code-block\">\u003Ccode>def build_chunks(documents):\n    chunks = []\n\n    for document in documents:\n        for index, text in enumerate(chunk_text(document[&quot;text&quot;])):\n            chunks.append({\n                &quot;id&quot;: f&#39;{document[&quot;source&quot;]}:{index}&#39;,\n                &quot;source&quot;: document[&quot;source&quot;],\n                &quot;chunk&quot;: index,\n                &quot;text&quot;: text,\n            })\n\n    return chunks\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Полезный фрагмент несёт больше, чем просто текст. Имя источника, идентификатор документа, URL, временная метка, версия или раздел могут впоследствии помочь при цитировании, отладке и проверке актуальности. Если происхождение теряется во время загрузки, становится гораздо труднее объяснить, почему был получен конкретный ответ.\u003C\u002Fp>\n\u003Ch2 id=\"section-58\">Реальный пример 2: Структурированные данные — используйте SQL, когда SQL является подходящим инструментом\u003C\u002Fh2>\n\u003Cp>Не каждый внешний факт должен проходить через семантический поиск. Если вопрос требует точной текущей записи, прямой запрос к базе данных обычно понятнее и более детерминирован.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>import sqlite3\n\ndef get_order_status(order_id):\n    connection = sqlite3.connect(&quot;shop.db&quot;)\n    cursor = connection.cursor()\n\n    cursor.execute(\n        &quot;SELECT status, total, currency FROM orders WHERE id = ?&quot;,\n        (order_id,)\n    )\n\n    row = cursor.fetchone()\n    connection.close()\n\n    if row is None:\n        return None\n\n    return {\n        &quot;order_id&quot;: order_id,\n        &quot;status&quot;: row[0],\n        &quot;total&quot;: row[1],\n        &quot;currency&quot;: row[2],\n    }\n\nprint(get_order_status(4711))\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Если приложение уже знает, что пользователь спрашивает о заказе 4711, встраивание всей таблицы заказов и просьба к семантическому поиску заново обнаружить эту строку обычно добавляет сложность без пользы. Сильное правило проектирования: \u003Cb>извлекайте структурированные факты структурированными запросами; извлекайте неструктурированные знания с помощью поиска.\u003C\u002Fb>\u003C\u002Fp>\n\u003Cp>Возвращённую строку базы данных всё ещё можно поместить в контекст модели, чтобы LLM мог объяснить её на естественном языке. Но прямой доступ к состоянию или записи концептуально отличается от поиска по корпусу знаний.\u003C\u002Fp>\n\u003Ch2 id=\"section-63\">Реальный пример 3: Полнотекстовый поиск перед эмбеддингами\u003C\u002Fh2>\n\u003Cp>Между наивным циклом на Python и векторным поиском лежит зрелый класс систем лексического поиска. SQLite включает FTS5 для полнотекстового поиска, в том числе ранжирование BM25.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>import sqlite3\n\nconnection = sqlite3.connect(&quot;knowledge.db&quot;)\ncursor = connection.cursor()\n\ncursor.execute(\n    &quot;CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5(title, body)&quot;\n)\n\ncursor.execute(\n    &quot;INSERT INTO docs(title, body) VALUES (?, ?)&quot;,\n    (&quot;AKM&quot;, &quot;The AKM uses 7.62 mm ammunition.&quot;)\n)\n\nconnection.commit()\n\nquery = &quot;AKM ammunition&quot;\n\nrows = cursor.execute(\n    &quot;SELECT title, body, bm25(docs) AS score &quot;\n    &quot;FROM docs WHERE docs MATCH ? &quot;\n    &quot;ORDER BY score LIMIT 5&quot;,\n    (query,)\n).fetchall()\n\nfor row in rows:\n    print(row)\n\nconnection.close()\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Лексический поиск особенно полезен, когда важны точная терминология, коды продуктов, имена, идентификаторы или слова, специфичные для предметной области. Семантический поиск не обязательно лучше. Продакшн-системы часто комбинируют оба сигнала.\u003C\u002Fp>\n\u003Ch2 id=\"section-67\">Реальный пример 4: Семантический поиск с эмбеддингами\u003C\u002Fh2>\n\u003Cp>Эмбеддинги превращают текст в числовые векторы, чтобы семантически связанные фрагменты можно было сравнивать, даже если они не используют одинаковые формулировки. Sentence Transformers предоставляет простую локальную реализацию.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode># pip install sentence-transformers\n\nfrom sentence_transformers import SentenceTransformer, util\n\ndocuments = [\n    &quot;The AKM uses 7.62 mm ammunition.&quot;,\n    &quot;A Med Kit restores health.&quot;,\n    &quot;Vehicle maintenance includes checking oil, brakes and tires.&quot;,\n    &quot;Account recovery requires access to the registered email address.&quot;\n]\n\nmodel = SentenceTransformer(\n    &quot;sentence-transformers\u002Fmulti-qa-mpnet-base-cos-v1&quot;\n)\n\ndocument_embeddings = model.encode_document(\n    documents,\n    convert_to_tensor=True\n)\n\nquestion = &quot;How do I repair my car?&quot;\n\nquery_embedding = model.encode_query(\n    question,\n    convert_to_tensor=True\n)\n\nhits = util.semantic_search(\n    query_embedding,\n    document_embeddings,\n    top_k=2\n)[0]\n\nfor hit in hits:\n    print(round(float(hit[&quot;score&quot;]), 3), documents[hit[&quot;corpus_id&quot;]])\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Запрос не содержит фразу «обслуживание транспортных средств», но семантическая модель всё равно может поставить этот фрагмент высоко, потому что понятия связаны. Это практическая причина, по которой эмбеддинги распространены в RAG-системах.\u003C\u002Fp>\n\u003Cp>Для небольших коллекций эмбеддинги могут храниться в памяти. Более крупные системы обычно сохраняют их в индексе или базе данных с поддержкой векторов и выполняют там поиск ближайших соседей. Хранилище меняется, но логика остаётся: закодировать вопрос, найти релевантные представления документов, вернуть лучшие доказательства.\u003C\u002Fp>\n\u003Ch2 id=\"section-72\">Реальный пример 5: Построение контекста для LLM\u003C\u002Fh2>\n\u003Cp>Система поиска должна возвращать доказательства. Затем LLM должна получить вопрос вместе с этими доказательствами. Разделение поиска и генерации упрощает их проверку и тестирование.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode>def build_prompt(question, retrieved_documents):\n    context = &quot;\\n\\n&quot;.join(\n        f&#39;[{doc[&quot;id&quot;]}] {doc[&quot;text&quot;]}&#39;\n        for doc in retrieved_documents\n    )\n\n    return f&quot;&quot;&quot;\nAnswer the question using the supplied context.\n\nRules:\n- Do not invent facts that are not supported by the context.\n- If the context is insufficient, say so.\n- Cite the source IDs you used.\n\nQuestion:\n{question}\n\nContext:\n{context}\n&quot;&quot;&quot;.strip()\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Инструкция не делает модель безошибочной. Она лишь создаёт явную границу доказательств. Модель по-прежнему может неправильно понять хорошие доказательства, проигнорировать условие или сделать чрезмерное обобщение. Именно поэтому качество поиска и качество генерации должны оцениваться отдельно.\u003C\u002Fp>\n\u003Ch2 id=\"section-76\">Реальный пример 6: Полный минимальный конвейер\u003C\u002Fh2>\n\u003Cpre class=\"code-block\">\u003Ccode>def answer_question(question, all_documents, call_llm):\n    # 1. Retrieve evidence\n    retrieved = retrieve(question, all_documents, top_k=3)\n\n    # 2. Build model context\n    prompt = build_prompt(question, retrieved)\n\n    # 3. Generate the answer\n    answer = call_llm(prompt)\n\n    return {\n        &quot;answer&quot;: answer,\n        &quot;sources&quot;: [doc[&quot;id&quot;] for doc in retrieved]\n    }\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Функция намеренно принимает \u003Ccode>call_llm\u003C\u002Fcode> как зависимость. Поиску не должно быть важно, выполняется ли генерация облачной моделью, локальной моделью или другим провайдером. Путь данных принадлежит приложению.\u003C\u002Fp>\n\u003Ch3 id=\"section-79\">Опциональный генератор: OpenAI Responses API\u003C\u002Fh3>\n\u003Cp>Одним из возможных генераторов является OpenAI Responses API. Хранение имени модели в переменной окружения позволяет избежать жёсткого кодирования конкретной модели в архитектуру RAG.\u003C\u002Fp>\n\u003Cpre class=\"code-block\">\u003Ccode># pip install openai\n\nimport os\nfrom openai import OpenAI\n\nclient = OpenAI()\n\ndef call_llm(prompt):\n    response = client.responses.create(\n        model=os.environ[&quot;OPENAI_MODEL&quot;],\n        input=prompt,\n    )\n    return response.output_text\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Тот же конвейер поиска можно подключить к локальному серверу вывода. Это важный архитектурный момент: \u003Cb>RAG не принадлежит провайдеру LLM.\u003C\u002Fb> Приложение владеет источником, поиском и сборкой контекста.\u003C\u002Fp>\n\u003Ch3 id=\"section-83\">Вся архитектура в одном представлении\u003C\u002Fh3>\n\u003Cpre class=\"code-block\">\u003Ccode>USER QUESTION\n     |\n     v\n+-------------+\n|  Retriever  |\n+-------------+\n   |       |\n   |       +----&gt; SQL \u002F API \u002F state query\n   |\n   +------------&gt; keyword \u002F full-text search\n   |\n   +------------&gt; embedding \u002F vector search\n                     |\n                     v\n              relevant evidence\n                     |\n                     v\n+-----------------------------------+\n| question + evidence + instructions |\n+-----------------------------------+\n                     |\n                     v\n                   LLM\n                     |\n                     v\n                  answer\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Эта модель потока данных более долговечна, чем запоминание одного фреймворка. Библиотеки, базы данных и поставщики моделей будут меняться; границы ответственности останутся.\u003C\u002Fp>\n\u003Ch2 id=\"section-86\">Распространённые заблуждения и режимы отказа\u003C\u002Fh2>\n\u003Ch3 id=\"section-87\">«RAG означает векторную базу данных».\u003C\u002Fh3>\n\u003Cp>Нет. Векторный поиск — это лишь один метод поиска. RAG может использовать полнотекстовый поиск, SQL, API, графы знаний, векторный поиск или их комбинации. Определяющим шаблоном является поиск внешней информации для генерации.\u003C\u002Fp>\n\u003Ch3 id=\"section-89\">«Если данные находятся в PostgreSQL, я должен векторизовать всю базу данных».\u003C\u002Fh3>\n\u003Cp>Нет. Структурированные записи обычно должны оставаться доступными для запросов как структурированные записи. Эмбеддинги полезны для семантической релевантности, а не как замена детерминированным запросам.\u003C\u002Fp>\n\u003Ch3 id=\"section-91\">«Больше чанков — лучше ответ».\u003C\u002Fh3>\n\u003Cp>Не обязательно. Дополнительный контекст может вносить шум, конфликтующие версии и нерелевантный материал. Поиск должен оптимизироваться под полезные доказательства, а не под максимальный объём.\u003C\u002Fp>\n\u003Ch3 id=\"section-93\">«Высокая оценка сходства доказывает ответ».\u003C\u002Fh3>\n\u003Cp>Нет. Сходство измеряет релевантность, а не истинность или применимость. Весьма похожий фрагмент может быть устаревшим, относиться к неверной версии продукта или быть действительным только при условиях, не соответствующих вопросу.\u003C\u002Fp>\n\u003Ch3 id=\"section-95\">«Как только найден правильный чанк, проблема галлюцинаций решена».\u003C\u002Fh3>\n\u003Cp>Нет. Поиск улучшает обоснованность, но не гарантирует достоверных рассуждений. Генерация по-прежнему требует оценки, а рабочие процессы с высоким риском могут нуждаться в детерминированной валидации или проверке человеком.\u003C\u002Fp>\n\u003Ch3 id=\"section-97\">«Модель не справилась — значит, надо сменить модель».\u003C\u002Fh3>\n\u003Cp>Не обязательно. Правильный источник мог отсутствовать, быть неверно разобран, плохо разбит, отфильтрован, оценён слишком низко или пропущен при сборке контекста. Замена модели не должна быть первым шагом диагностики.\u003C\u002Fp>\n\u003Ch2 id=\"section-99\">Граничные случаи\u003C\u002Fh2>\n\u003Cul>\u003Cli>\u003Cb>Конфликтующие документы:\u003C\u002Fb> два источника могут противоречить друг другу из-за различий в версиях, юрисдикциях или продуктах.\u003C\u002Fli>\u003Cli>\u003Cb>Факты, зависящие от времени:\u003C\u002Fb> семантически релевантный источник может быть уже устаревшим.\u003C\u002Fli>\u003Cli>\u003Cb>Права доступа:\u003C\u002Fb> система поиска не должна возвращать документы, к которым у текущего пользователя нет авторизованного доступа.\u003C\u002Fli>\u003Cli>\u003Cb>Многоязычные коллекции:\u003C\u002Fb> модель эмбеддингов и стратегия поиска должны поддерживать фактически используемые языки.\u003C\u002Fli>\u003Cli>\u003Cb>Таблицы и исходный код:\u003C\u002Fb> разбиение на обычные абзацы может разрушить структуру, необходимую для ответа.\u003C\u002Fli>\u003Cli>\u003Cb>Очень короткие идентификаторы:\u003C\u002Fb> семантический поиск может быть слабее точного совпадения для артикулов, идентификаторов, кодов ошибок или аббревиатур.\u003C\u002Fli>\u003Cli>\u003Cb>Длинные вопросы, требующие нескольких фактов:\u003C\u002Fb> поиску может потребоваться декомпозиция, несколько запросов или переранжирование вместо одного запроса top-k.\u003C\u002Fli>\u003Cli>\u003Cb>Иерархия источников:\u003C\u002Fb> официальная действующая политика может должна иметь приоритет над более старым, но семантически более близким дискуссионным документом.\u003C\u002Fli>\u003C\u002Ful>\n\u003Ch2 id=\"section-101\">Ограничения\u003C\u002Fh2>\n\u003Cp>Примеры на Python намеренно оптимизированы для прозрачности, а не для масштаба. Поиск по ключевым словам наивен, разбиение использует длину в символах, примеры на SQLite не включают управление соединениями для продакшена, а семантический пример хранит все эмбеддинги в памяти.\u003C\u002Fp>\n\u003Cp>Продакшен-система может требовать векторных индексов, переранжировщиков, гибридного поиска, парсеров документов, кэширования, инкрементальной индексации, версионирования источников, фильтров контроля доступа, наблюдаемости, наборов данных для оценки и обработки сбоев. Ни одно из этих дополнений не меняет базовую архитектуру; они делают каждую границу более надёжной.\u003C\u002Fp>\n\u003Cp>RAG также не может создать доказательства, отсутствующие в наборе источников. Если источник неверен, неполон или устарел, лучшая модель эмбеддингов не сможет превратить его в авторитетное знание.\u003C\u002Fp>\n\u003Ch2 id=\"section-105\">Что изменило бы этот ответ?\u003C\u002Fh2>\n\u003Cp>Архитектура меняется, когда задача требует большего, чем поиск знаний. Актуальный статус заказа требует текущего состояния. Финансовый расчёт может требовать детерминированного кода. Задача веб-исследования может требовать активного поиска. Рабочий процесс может требовать инструментов, способных записывать данные обратно в другую систему. Автономному агенту может потребоваться планирование, разрешения и контроль исполнения в дополнение к поиску.\u003C\u002Fp>\n\u003Cp>Поэтому RAG лучше всего понимать как \u003Cb>один слой получения доказательств внутри более крупной ИИ-системы\u003C\u002Fb>. Он мощен именно потому, что у него узкая задача: находить полезную внешнюю информацию и помещать её в рабочий контекст модели.\u003C\u002Fp>\n\u003Ch2 id=\"section-108\">Заключение\u003C\u002Fh2>\n\u003Cp>RAG становится гораздо проще понять, если убрать названия технологий. Файл — это источник. База данных — это источник. API — это источник. Функция поиска извлекает доказательства. Промпт передаёт эти доказательства модели. Затем LLM интерпретирует их и порождает текст.\u003C\u002Fp>\n\u003Cp>Сложная часть production RAG — не вызов модели эмбеддингов. Это построение надёжного пути доказательств от исходного источника до финального утверждения: сохранение происхождения данных, выбор правильного метода извлечения, поддержание информации в актуальном состоянии, контроль доступа, оценка извлечения отдельно от генерации и понимание того, когда прямой вызов базы данных или инструмента лучше семантического поиска.\u003C\u002Fp>\n\u003Cp>Это практическое продолжение базовой модели RAG: \u003Cb>сначала понять роли, затем сделать путь данных явным.\u003C\u002Fb>\u003C\u002Fp>\n\u003Ch2 id=\"section-112\">Первоисточники\u003C\u002Fh2>\n\u003Cul>\u003Cli>\u003Ca href=\"https:\u002F\u002Farxiv.org\u002Fabs\u002F2005.11401\">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\u003C\u002Fa> — статья 2020 года, вводящая формулировку RAG, которая сочетает генерацию с извлечённой непараметрической памятью.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.sbert.net\u002Fexamples\u002Fsentence_transformer\u002Fapplications\u002Fsemantic-search\u002FREADME.html\">Sentence Transformers — Semantic Search\u003C\u002Fa> — официальная документация по семантическому поиску, эмбеддингам запросов и эмбеддингам документов.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fembeddings\">OpenAI — Vector Embeddings\u003C\u002Fa> — официальная документация, описывающая эмбеддинги как числовые представления, используемые для оценки связанности и поиска.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.sqlite.org\u002Ffts5.html\">SQLite — FTS5 Extension\u003C\u002Fa> — официальная документация по полнотекстовому поиску и ранжированию BM25 в SQLite.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Flibraries\">OpenAI — SDKs and CLI\u003C\u002Fa> — официальный пример Python SDK для Responses API, используемый в дополнительном примере генератора.\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa> — концептуальная первая часть этой серии.\u003C\u002Fli>\u003C\u002Ful>",{"time":212,"blocks":213,"version":676},1790517447792,[214,218,221,227,231,234,237,240,243,246,249,253,256,259,262,265,268,271,274,277,280,283,286,289,292,295,298,301,337,340,343,346,358,361,364,394,397,400,426,429,432,445,448,451,454,457,460,463,466,469,472,475,479,482,485,488,491,494,497,500,503,506,509,512,515,518,521,524,527,530,533,536,539,542,545,548,551,554,557,560,563,566,569,572,575,578,581,584,587,590,593,596,599,602,605,608,611,614,617,620,631,634,637,640,643,646,649,652,655,658,661,664,667],{"data":215,"type":217},{"text":216},"Предыдущая статья, \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">Что такое RAG? Самое простое объяснение того, как это работает\u003C\u002Fa>, заложила ментальную модель: LLM пишет, RAG извлекает полезные знания, приложение владеет текущим состоянием, а инструменты выполняют действия. Эта статья делает следующий шаг: \u003Cb>откуда на самом деле берутся данные и как выглядит извлечение в Python?\u003C\u002Fb>","paragraph",{"data":219,"type":217},{"text":220},"Важный сюрприз заключается в том, что «источник данных для LLM» обычно не представляет собой ничего экзотического. Это может быть текстовый файл, папка с Markdown-документами, база данных SQL, ответ API, каталог товаров, система поддержки или векторный индекс, построенный на основе этих источников. ИИ не знает эти системы магическим образом. Ваше приложение должно загрузить, запросить, найти или извлечь соответствующие данные и поместить результат в контекст модели.",{"data":222,"type":226},{"text":223,"caption":224,"alignment":225},"Источник данных = где находится информация. Извлечение = как приложение находит полезную информацию. Контекст = выбранная информация, переданная модели. LLM = компонент, который интерпретирует этот контекст и генерирует ответ.","Четырёхчастная модель, используемая на протяжении всей этой статьи","left","quote",{"data":228,"type":42},{"text":229,"level":230},"Вопрос",2,{"data":232,"type":217},{"text":233},"Как LLM использует внешние данные, такие как файлы, базы данных или API, и как небольшая программа на Python может реализовать основные шаги RAG, не скрывая их за фреймворком?",{"data":235,"type":42},{"text":236,"level":230},"Что это на самом деле означает",{"data":238,"type":217},{"text":239},"Когда разработчики говорят, что LLM «подключена к данным компании», за этой фразой может скрываться несколько различных операций. Одно приложение может выполнять SQL. Другое может вызывать API. Третье может выполнять полнотекстовый поиск. Четвёртое может вычислять сходство эмбеддингов по фрагментам документов. Все они могут предоставлять внешнюю информацию для LLM, но это не один и тот же метод извлечения, и их не следует рассматривать как взаимозаменяемые.",{"data":241,"type":217},{"text":242},"Это различие важно, потому что лучший метод извлечения зависит от формы вопроса. «Какова наша политика возврата?» — это задача извлечения документов. «Каков текущий статус заказа 4711?» — это обычно структурированный запрос к базе данных. «В каком абзаце обсуждается восстановление аккаунта?» — это может быть поиск по ключевым словам или семантический поиск. RAG наиболее полезен, когда система должна \u003Cb>обнаружить релевантные знания до генерации\u003C\u002Fb>.",{"data":244,"type":42},{"text":245,"level":230},"Простейший пример",{"data":247,"type":217},{"text":248},"Начните с трёх строк в обычном Python. Здесь нет векторной базы данных, нет фреймворка и пока нет LLM. Мы лишь хотим сделать шаг извлечения видимым.",{"data":250,"type":252},{"code":251},"documents = [\n    \"The AKM uses 7.62 mm ammunition.\",\n    \"A Med Kit restores health.\",\n    \"A 4x scope can be attached to several compatible weapons.\"\n]\n\nquestion = \"Which ammunition does the AKM use?\"\n\nfor document in documents:\n    if \"AKM\" in document:\n        print(document)","code",{"data":254,"type":217},{"text":255},"Программа выводит первое предложение, потому что оно содержит искомый термин. Это примитивное извлечение, но архитектура уже видна: \u003Cb>вопрос → поиск → релевантный текст\u003C\u002Fb>. RAG добавляет ещё один важный шаг: передать извлечённый текст языковой модели вместе с вопросом.",{"data":257,"type":217},{"text":258},"Немного более общая версия ранжирует документы по пересечению терминов запроса:",{"data":260,"type":252},{"code":261},"import re\n\ndocuments = [\n    {\"id\": \"weapon-akm\", \"text\": \"The AKM uses 7.62 mm ammunition.\"},\n    {\"id\": \"healing-medkit\", \"text\": \"A Med Kit restores health.\"},\n    {\"id\": \"scope-4x\", \"text\": \"A 4x scope can be attached to several compatible weapons.\"},\n]\n\ndef words(text):\n    return set(re.findall(r\"[a-zA-Z0-9.]+\", text.lower()))\n\ndef retrieve(question, documents, top_k=2):\n    query_terms = words(question)\n    ranked = []\n\n    for document in documents:\n        score = len(query_terms & words(document[\"text\"]))\n        if score > 0:\n            ranked.append((score, document))\n\n    ranked.sort(key=lambda item: item[0], reverse=True)\n    return [document for _, document in ranked[:top_k]]\n\nquestion = \"Which ammunition does the AKM use?\"\nhits = retrieve(question, documents)\n\nfor hit in hits:\n    print(hit[\"id\"], \"->\", hit[\"text\"])",{"data":263,"type":217},{"text":264},"Это не производственная поисковая система. Она игнорирует морфологию, синонимы, варианты написания, длину документа и многие сигналы ранжирования. Её ценность образовательная: \u003Cb>RAG начинается не с векторной базы данных. Он начинается с извлечения.\u003C\u002Fb>",{"data":266,"type":42},{"text":267,"level":230},"Где пример перестаёт работать",{"data":269,"type":217},{"text":270},"Точное или лексическое сопоставление становится слабым, когда вопрос и источник используют разные слова. В документе может быть сказано «обслуживание транспортного средства», а пользователь спрашивает: «как мне отремонтировать машину?» Лексический поисковик может упустить эту связь, хотя человек видит её сразу. Семантическое извлечение решает эту проблему, представляя текст в виде векторов и сравнивая смысл, а не только точные токены.",{"data":272,"type":217},{"text":273},"Длинные файлы создают ещё одну проблему. Поиск по всему 80-страничному руководству как по единому целому слишком груб, но разбиение на каждое предложение может разрушить полезный контекст. Поэтому реальные системы RAG нуждаются в решениях относительно парсинга, разбиения на фрагменты, метаданных, ранжирования, актуальности, разрешений и происхождения данных.",{"data":275,"type":217},{"text":276},"В примере также ничего не говорится о структурированных актуальных фактах. Если пользователь спрашивает о текущем статусе заказа 4711, а в приложении уже есть ключ базы данных, семантический поиск обычно является неподходящим первым инструментом. Детерминированный запрос к базе данных лучше.",{"data":278,"type":42},{"text":279,"level":230},"Прямой ответ",{"data":281,"type":217},{"text":282},"Источник данных для LLM — это любая внешняя система, из которой приложение может получить информацию для модели: файлы, базы данных, API, поисковые индексы, векторные хранилища или текущее состояние приложения. RAG — это паттерн \u003Cb>извлечения релевантных знаний из таких источников перед генерацией\u003C\u002Fb>.",{"data":284,"type":217},{"text":285},"В Python основной конвейер может быть очень небольшим: \u003Cb>загрузить данные → создать извлекаемые единицы → найти релевантные доказательства → собрать контекст → вызвать LLM\u003C\u002Fb>. Метод извлечения должен соответствовать источнику и вопросу. Используйте SQL для точных структурированных фактов, полнотекстовый поиск для лексического сопоставления, эмбеддинги для семантической близости и гибридное извлечение, когда ценны несколько сигналов.",{"data":287,"type":42},{"text":288,"level":230},"Почему это так",{"data":290,"type":217},{"text":291},"Языковая модель не получает автоматически содержимое вашей файловой системы, базы данных PostgreSQL, CRM, частного API или недавно отредактированного документа. Приложение решает, какая внешняя информация доступна и что помещается в текущий контекст модели.",{"data":293,"type":217},{"text":294},"Оригинальная работа по Retrieval-Augmented Generation Льюиса и др. объединила генеративную модель с внешней непараметрической памятью, извлекаемой из плотного векторного индекса. Более широкая архитектурная идея сохраняется за пределами этой конкретной реализации: внешние доказательства можно извлекать во время вывода вместо того, чтобы ожидать, что все полезные знания закодированы в параметрах модели.",{"data":296,"type":217},{"text":297},"Это создает полезное разделение обязанностей: источник хранит информацию, средство извлечения выбирает доказательства, контекст переносит эти доказательства в запрос, а модель интерпретирует их. Сохранение этих границ видимыми значительно упрощает диагностику сбоев.",{"data":299,"type":42},{"text":300,"level":230},"Контекст: основные типы источников данных",{"data":302,"type":336},{"content":303,"withHeadings":14},[304,308,312,316,320,324,328,332],[305,306,307],"Источник","Типичный метод извлечения","Подходит для",[309,310,311],"TXT \u002F Markdown \u002F HTML","Парсинг + лексический или семантический поиск","Документация, руководства, статьи, заметки",[313,314,315],"PDF \u002F DOCX","Извлечение с учетом структуры + поиск","Политики, отчеты, договоры, руководства",[317,318,319],"База данных SQL","SQL-запрос или фильтрованное извлечение","Заказы, пользователи, продукты, структурированные записи",[321,322,323],"REST \u002F GraphQL API","HTTP-запрос с параметрами","Удаленные системы и данные живых сервисов",[325,326,327],"Поисковый индекс","BM25 \u002F полнотекстовый \u002F гибридный поиск","Большие текстовые коллекции",[329,330,331],"Векторный индекс","Сходство эмбеддингов","Семантическое извлечение документов",[333,334,335],"Состояние приложения","Прямое чтение состояния или вызов инструмента","Что верно прямо сейчас","table",{"data":338,"type":217},{"text":339},"Векторный индекс заслуживает особого внимания. Во многих архитектурах он \u003Cb>не является каноническим источником истины\u003C\u002Fb>. Это индекс извлечения, производный от документов или записей. Авторитетный документ может находиться в объектном хранилище, CMS, Git, PostgreSQL или другой системе, а эмбеддинги и метаданные хранятся отдельно для быстрого семантического поиска. Некоторые системы действительно используют векторное хранилище в качестве основного хранилища, но это архитектурный выбор, а не требование RAG.",{"data":341,"type":217},{"text":342},"Если граница между извлечением, постоянной памятью, текущим состоянием и контекстом модели все еще неясна, см. \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fai-agent-memory-is-not-rag-how-to-separate-memory-retrieval-state-and-context\">AI Agent Memory Is Not RAG\u003C\u002Fa>. Эти слои могут использовать некоторые из одних и тех же технологий хранения, но при этом иметь разные правила корректности.",{"data":344,"type":42},{"text":345,"level":230},"Допущения",{"data":347,"type":357},{"items":348,"style":356},[349,350,351,352,353,354,355],"Приложению разрешен доступ к внешнему источнику.","Соответствующий источник содержит достаточно информации для ответа на вопрос.","Данные можно разобрать или запросить в форме, пригодной для использования слоем извлечения.","Извлеченная информация достаточно свежа для запрашиваемого решения.","Модель получает выбранные доказательства в своем контексте.","Авторизация применяется до того, как защищенные доказательства попадут к модели.","Генеративная модель все еще может ошибаться, даже если извлечение корректно.","unordered","list",{"data":359,"type":217},{"text":360},"Эти допущения важны, потому что извлечение не может компенсировать отсутствующие доказательства, устаревшие версии источников, сломанные парсеры или несанкционированный доступ. Конвейер RAG может быть настолько надежным, насколько надежен путь доказательств, который его питает.",{"data":362,"type":42},{"text":363,"level":230},"Переменные",{"data":365,"type":336},{"content":366,"withHeadings":14},[367,370,373,376,379,382,385,388,391],[368,369],"Переменная","Почему она меняет дизайн",[371,372],"Структура источника","Таблица SQL, юридический PDF и репозиторий исходного кода требуют разных стратегий извлечения",[374,375],"Тип вопроса","Точный поиск, концептуальный поиск и многошаговое исследование — это разные задачи",[377,378],"Требование к свежести","Живое состояние может требовать прямых запросов вместо периодически перестраиваемых индексов",[380,381],"Размер корпуса","Поиск в памяти может работать для сотен фрагментов, но не для очень больших коллекций",[383,384],"Язык","Многоязычное извлечение требует моделей и токенизации, подходящих для фактических языков",[386,387],"Разрешения","Извлечение должно фильтроваться по правам доступа текущего пользователя",[389,390],"Задержка и стоимость","Больше этапов извлечения может улучшить качество, но добавить время выполнения и затраты на инфраструктуру",[392,393],"Потребность в происхождении","Системы с высоким доверием нуждаются в идентификаторах источников, версиях и отслеживаемых доказательствах",{"data":395,"type":42},{"text":396,"level":230},"Диагностический \u002F решающий метод",{"data":398,"type":217},{"text":399},"Первое решение — не «Какую векторную базу данных мне установить?» Оно таково: \u003Cb>Какого рода факт я пытаюсь извлечь?\u003C\u002Fb>",{"data":401,"type":336},{"content":402,"withHeadings":14},[403,406,410,414,418,422],[374,404,405],"Предпочтительный первый подход","Причина",[407,408,409],"Точный ID или текущая запись","SQL \u002F поиск по ключу \u002F API","Детерминированный структурированный доступ",[411,412,413],"Точная формулировка, коды, названия","Полнотекстовый или ключевой поиск","Лексическая точность",[415,416,417],"Концептуальный вопрос по документам","Семантический векторный поиск","Смысл может отличаться от формулировки",[419,420,421],"Смешанные корпоративные знания","Гибридный поиск + фильтры по метаданным","Объединяет лексические и семантические сигналы",[423,424,425],"Текущее состояние приложения","Прямой доступ к состоянию\u002Fинструменту","Актуальность важнее сходства документов",{"data":427,"type":217},{"text":428},"Полезная проверка: \u003Cb>Я уже знаю, какая запись мне нужна, или система должна обнаружить, какой фрагмент релевантен?\u003C\u002Fb> Если запись известна, запрашивайте её напрямую. Если релевантность нужно обнаружить, поиск становится важнее.",{"data":430,"type":217},{"text":431},"Когда ответ неверен, диагностируйте конвейер по порядку, а не сразу меняйте LLM:",{"data":433,"type":357},{"items":434,"style":444},[435,436,437,438,439,440,441,442,443],"\u003Cb>1. Покрытие источников:\u003C\u002Fb> Существует ли правильная информация в доступном наборе источников?","\u003Cb>2. Актуальность:\u003C\u002Fb> Достаточно ли свежа эта версия для вопроса?","\u003Cb>3. Разбор:\u003C\u002Fb> Был ли релевантный контент извлечён правильно?","\u003Cb>4. Разбиение на фрагменты:\u003C\u002Fb> Остались ли доказательства вместе с условиями, придающими им смысл?","\u003Cb>5. Поиск:\u003C\u002Fb> Появляется ли правильный фрагмент среди кандидатов?","\u003Cb>6. Ранжирование:\u003C\u002Fb> Ранжируются ли более сильные источники выше более слабых или противоречащих?","\u003Cb>7. Сборка контекста:\u003C\u002Fb> Действительно ли приложение отправило выбранные доказательства модели?","\u003Cb>8. Генерация:\u003C\u002Fb> Использовала ли LLM предоставленные доказательства достоверно?","\u003Cb>9. Атрибуция:\u003C\u002Fb> Можно ли проследить каждое важное утверждение до источника?","ordered",{"data":446,"type":217},{"text":447},"Для более глубокого метода отладки в продакшене см. \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Frag-failed-but-which-layer-actually-failed-a-diagnostic-method\">RAG Failed — But Which Layer Actually Failed? A Diagnostic Method\u003C\u002Fa>, где эта цепочка расширяется до независимо тестируемых слоёв сбоев.",{"data":449,"type":42},{"text":450,"level":230},"Доказательства",{"data":452,"type":217},{"text":453},"Статья о RAG от Lewis et al. формализовала генерацию, обусловленную извлечённой внешней памятью, а не опирающуюся только на параметры модели. Это даёт концептуальную основу для отделения генератора от извлекаемого источника знаний.",{"data":455,"type":217},{"text":456},"Sentence Transformers описывает семантический поиск как встраивание корпуса и запроса в векторное пространство и извлечение элементов с высокой семантической близостью. Его текущий API также различает кодирование запроса и кодирование документа для задач поиска.",{"data":458,"type":217},{"text":459},"SQLite FTS5 демонстрирует другую сторону спектра: зрелый полнотекстовый поиск может ранжировать документы без эмбеддингов. Это важно, потому что лексический поиск остаётся ценным для идентификаторов, точной терминологии и многих гибридных схем поиска.",{"data":461,"type":217},{"text":462},"Документация OpenAI по эмбеддингам описывает эмбеддинги как числовые векторные представления, используемые для оценки связанности и поиска. Это один из путей реализации семантического поиска, а не определение самого RAG.",{"data":464,"type":42},{"text":465,"level":230},"Реальный пример 1: Папка с текстовыми файлами",{"data":467,"type":217},{"text":468},"Предположим, каталог с именем \u003Ccode>knowledge\u002F\u003C\u002Fcode> содержит обычные текстовые файлы. Python может загрузить их вообще без библиотек ИИ.",{"data":470,"type":252},{"code":471},"from pathlib import Path\n\ndef load_text_files(folder=\"knowledge\"):\n    documents = []\n\n    for path in Path(folder).glob(\"*.txt\"):\n        documents.append({\n            \"source\": path.name,\n            \"text\": path.read_text(encoding=\"utf-8\")\n        })\n\n    return documents\n\ndocuments = load_text_files()\n\nfor document in documents:\n    print(document[\"source\"], len(document[\"text\"]))",{"data":473,"type":217},{"text":474},"Файловая система — это источник данных. Следующий вопрос: сколько текста должно стать одной извлекаемой единицей. Для длинных документов поиск по одному полному файлу часто слишком груб. Именно поэтому конвейеры RAG обычно создают фрагменты.",{"data":476,"type":42},{"text":477,"level":478},"Очень простой разбиватель на фрагменты",3,{"data":480,"type":252},{"code":481},"def chunk_text(text, max_chars=800):\n    paragraphs = [p.strip() for p in text.split(\"\\n\\n\") if p.strip()]\n\n    chunks = []\n    current = \"\"\n\n    for paragraph in paragraphs:\n        candidate = f\"{current}\\n\\n{paragraph}\".strip()\n\n        if current and len(candidate) > max_chars:\n            chunks.append(current)\n            current = paragraph\n        else:\n            current = candidate\n\n    if current:\n        chunks.append(current)\n\n    return chunks",{"data":483,"type":217},{"text":484},"Этот пример группирует абзацы, пока не достигнут примерный лимит символов. Он намеренно понятный, а не оптимальный. Продакшен-системы часто разбивают по токенам, заголовкам, разделам, границам предложений или структуре документа. Таблицы, исходный код, договоры и документация API могут требовать разных стратегий.",{"data":486,"type":42},{"text":487,"level":478},"Сохранение происхождения при разбиении на фрагменты",{"data":489,"type":252},{"code":490},"def build_chunks(documents):\n    chunks = []\n\n    for document in documents:\n        for index, text in enumerate(chunk_text(document[\"text\"])):\n            chunks.append({\n                \"id\": f'{document[\"source\"]}:{index}',\n                \"source\": document[\"source\"],\n                \"chunk\": index,\n                \"text\": text,\n            })\n\n    return chunks",{"data":492,"type":217},{"text":493},"Полезный фрагмент несёт больше, чем просто текст. Имя источника, идентификатор документа, URL, временная метка, версия или раздел могут впоследствии помочь при цитировании, отладке и проверке актуальности. Если происхождение теряется во время загрузки, становится гораздо труднее объяснить, почему был получен конкретный ответ.",{"data":495,"type":42},{"text":496,"level":230},"Реальный пример 2: Структурированные данные — используйте SQL, когда SQL является подходящим инструментом",{"data":498,"type":217},{"text":499},"Не каждый внешний факт должен проходить через семантический поиск. Если вопрос требует точной текущей записи, прямой запрос к базе данных обычно понятнее и более детерминирован.",{"data":501,"type":252},{"code":502},"import sqlite3\n\ndef get_order_status(order_id):\n    connection = sqlite3.connect(\"shop.db\")\n    cursor = connection.cursor()\n\n    cursor.execute(\n        \"SELECT status, total, currency FROM orders WHERE id = ?\",\n        (order_id,)\n    )\n\n    row = cursor.fetchone()\n    connection.close()\n\n    if row is None:\n        return None\n\n    return {\n        \"order_id\": order_id,\n        \"status\": row[0],\n        \"total\": row[1],\n        \"currency\": row[2],\n    }\n\nprint(get_order_status(4711))",{"data":504,"type":217},{"text":505},"Если приложение уже знает, что пользователь спрашивает о заказе 4711, встраивание всей таблицы заказов и просьба к семантическому поиску заново обнаружить эту строку обычно добавляет сложность без пользы. Сильное правило проектирования: \u003Cb>извлекайте структурированные факты структурированными запросами; извлекайте неструктурированные знания с помощью поиска.\u003C\u002Fb>",{"data":507,"type":217},{"text":508},"Возвращённую строку базы данных всё ещё можно поместить в контекст модели, чтобы LLM мог объяснить её на естественном языке. Но прямой доступ к состоянию или записи концептуально отличается от поиска по корпусу знаний.",{"data":510,"type":42},{"text":511,"level":230},"Реальный пример 3: Полнотекстовый поиск перед эмбеддингами",{"data":513,"type":217},{"text":514},"Между наивным циклом на Python и векторным поиском лежит зрелый класс систем лексического поиска. SQLite включает FTS5 для полнотекстового поиска, в том числе ранжирование BM25.",{"data":516,"type":252},{"code":517},"import sqlite3\n\nconnection = sqlite3.connect(\"knowledge.db\")\ncursor = connection.cursor()\n\ncursor.execute(\n    \"CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5(title, body)\"\n)\n\ncursor.execute(\n    \"INSERT INTO docs(title, body) VALUES (?, ?)\",\n    (\"AKM\", \"The AKM uses 7.62 mm ammunition.\")\n)\n\nconnection.commit()\n\nquery = \"AKM ammunition\"\n\nrows = cursor.execute(\n    \"SELECT title, body, bm25(docs) AS score \"\n    \"FROM docs WHERE docs MATCH ? \"\n    \"ORDER BY score LIMIT 5\",\n    (query,)\n).fetchall()\n\nfor row in rows:\n    print(row)\n\nconnection.close()",{"data":519,"type":217},{"text":520},"Лексический поиск особенно полезен, когда важны точная терминология, коды продуктов, имена, идентификаторы или слова, специфичные для предметной области. Семантический поиск не обязательно лучше. Продакшн-системы часто комбинируют оба сигнала.",{"data":522,"type":42},{"text":523,"level":230},"Реальный пример 4: Семантический поиск с эмбеддингами",{"data":525,"type":217},{"text":526},"Эмбеддинги превращают текст в числовые векторы, чтобы семантически связанные фрагменты можно было сравнивать, даже если они не используют одинаковые формулировки. Sentence Transformers предоставляет простую локальную реализацию.",{"data":528,"type":252},{"code":529},"# pip install sentence-transformers\n\nfrom sentence_transformers import SentenceTransformer, util\n\ndocuments = [\n    \"The AKM uses 7.62 mm ammunition.\",\n    \"A Med Kit restores health.\",\n    \"Vehicle maintenance includes checking oil, brakes and tires.\",\n    \"Account recovery requires access to the registered email address.\"\n]\n\nmodel = SentenceTransformer(\n    \"sentence-transformers\u002Fmulti-qa-mpnet-base-cos-v1\"\n)\n\ndocument_embeddings = model.encode_document(\n    documents,\n    convert_to_tensor=True\n)\n\nquestion = \"How do I repair my car?\"\n\nquery_embedding = model.encode_query(\n    question,\n    convert_to_tensor=True\n)\n\nhits = util.semantic_search(\n    query_embedding,\n    document_embeddings,\n    top_k=2\n)[0]\n\nfor hit in hits:\n    print(round(float(hit[\"score\"]), 3), documents[hit[\"corpus_id\"]])",{"data":531,"type":217},{"text":532},"Запрос не содержит фразу «обслуживание транспортных средств», но семантическая модель всё равно может поставить этот фрагмент высоко, потому что понятия связаны. Это практическая причина, по которой эмбеддинги распространены в RAG-системах.",{"data":534,"type":217},{"text":535},"Для небольших коллекций эмбеддинги могут храниться в памяти. Более крупные системы обычно сохраняют их в индексе или базе данных с поддержкой векторов и выполняют там поиск ближайших соседей. Хранилище меняется, но логика остаётся: закодировать вопрос, найти релевантные представления документов, вернуть лучшие доказательства.",{"data":537,"type":42},{"text":538,"level":230},"Реальный пример 5: Построение контекста для LLM",{"data":540,"type":217},{"text":541},"Система поиска должна возвращать доказательства. Затем LLM должна получить вопрос вместе с этими доказательствами. Разделение поиска и генерации упрощает их проверку и тестирование.",{"data":543,"type":252},{"code":544},"def build_prompt(question, retrieved_documents):\n    context = \"\\n\\n\".join(\n        f'[{doc[\"id\"]}] {doc[\"text\"]}'\n        for doc in retrieved_documents\n    )\n\n    return f\"\"\"\nAnswer the question using the supplied context.\n\nRules:\n- Do not invent facts that are not supported by the context.\n- If the context is insufficient, say so.\n- Cite the source IDs you used.\n\nQuestion:\n{question}\n\nContext:\n{context}\n\"\"\".strip()",{"data":546,"type":217},{"text":547},"Инструкция не делает модель безошибочной. Она лишь создаёт явную границу доказательств. Модель по-прежнему может неправильно понять хорошие доказательства, проигнорировать условие или сделать чрезмерное обобщение. Именно поэтому качество поиска и качество генерации должны оцениваться отдельно.",{"data":549,"type":42},{"text":550,"level":230},"Реальный пример 6: Полный минимальный конвейер",{"data":552,"type":252},{"code":553},"def answer_question(question, all_documents, call_llm):\n    # 1. Retrieve evidence\n    retrieved = retrieve(question, all_documents, top_k=3)\n\n    # 2. Build model context\n    prompt = build_prompt(question, retrieved)\n\n    # 3. Generate the answer\n    answer = call_llm(prompt)\n\n    return {\n        \"answer\": answer,\n        \"sources\": [doc[\"id\"] for doc in retrieved]\n    }",{"data":555,"type":217},{"text":556},"Функция намеренно принимает \u003Ccode>call_llm\u003C\u002Fcode> как зависимость. Поиску не должно быть важно, выполняется ли генерация облачной моделью, локальной моделью или другим провайдером. Путь данных принадлежит приложению.",{"data":558,"type":42},{"text":559,"level":478},"Опциональный генератор: OpenAI Responses API",{"data":561,"type":217},{"text":562},"Одним из возможных генераторов является OpenAI Responses API. Хранение имени модели в переменной окружения позволяет избежать жёсткого кодирования конкретной модели в архитектуру RAG.",{"data":564,"type":252},{"code":565},"# pip install openai\n\nimport os\nfrom openai import OpenAI\n\nclient = OpenAI()\n\ndef call_llm(prompt):\n    response = client.responses.create(\n        model=os.environ[\"OPENAI_MODEL\"],\n        input=prompt,\n    )\n    return response.output_text",{"data":567,"type":217},{"text":568},"Тот же конвейер поиска можно подключить к локальному серверу вывода. Это важный архитектурный момент: \u003Cb>RAG не принадлежит провайдеру LLM.\u003C\u002Fb> Приложение владеет источником, поиском и сборкой контекста.",{"data":570,"type":42},{"text":571,"level":478},"Вся архитектура в одном представлении",{"data":573,"type":252},{"code":574},"USER QUESTION\n     |\n     v\n+-------------+\n|  Retriever  |\n+-------------+\n   |       |\n   |       +----> SQL \u002F API \u002F state query\n   |\n   +------------> keyword \u002F full-text search\n   |\n   +------------> embedding \u002F vector search\n                     |\n                     v\n              relevant evidence\n                     |\n                     v\n+-----------------------------------+\n| question + evidence + instructions |\n+-----------------------------------+\n                     |\n                     v\n                   LLM\n                     |\n                     v\n                  answer",{"data":576,"type":217},{"text":577},"Эта модель потока данных более долговечна, чем запоминание одного фреймворка. Библиотеки, базы данных и поставщики моделей будут меняться; границы ответственности останутся.",{"data":579,"type":42},{"text":580,"level":230},"Распространённые заблуждения и режимы отказа",{"data":582,"type":42},{"text":583,"level":478},"«RAG означает векторную базу данных».",{"data":585,"type":217},{"text":586},"Нет. Векторный поиск — это лишь один метод поиска. RAG может использовать полнотекстовый поиск, SQL, API, графы знаний, векторный поиск или их комбинации. Определяющим шаблоном является поиск внешней информации для генерации.",{"data":588,"type":42},{"text":589,"level":478},"«Если данные находятся в PostgreSQL, я должен векторизовать всю базу данных».",{"data":591,"type":217},{"text":592},"Нет. Структурированные записи обычно должны оставаться доступными для запросов как структурированные записи. Эмбеддинги полезны для семантической релевантности, а не как замена детерминированным запросам.",{"data":594,"type":42},{"text":595,"level":478},"«Больше чанков — лучше ответ».",{"data":597,"type":217},{"text":598},"Не обязательно. Дополнительный контекст может вносить шум, конфликтующие версии и нерелевантный материал. Поиск должен оптимизироваться под полезные доказательства, а не под максимальный объём.",{"data":600,"type":42},{"text":601,"level":478},"«Высокая оценка сходства доказывает ответ».",{"data":603,"type":217},{"text":604},"Нет. Сходство измеряет релевантность, а не истинность или применимость. Весьма похожий фрагмент может быть устаревшим, относиться к неверной версии продукта или быть действительным только при условиях, не соответствующих вопросу.",{"data":606,"type":42},{"text":607,"level":478},"«Как только найден правильный чанк, проблема галлюцинаций решена».",{"data":609,"type":217},{"text":610},"Нет. Поиск улучшает обоснованность, но не гарантирует достоверных рассуждений. Генерация по-прежнему требует оценки, а рабочие процессы с высоким риском могут нуждаться в детерминированной валидации или проверке человеком.",{"data":612,"type":42},{"text":613,"level":478},"«Модель не справилась — значит, надо сменить модель».",{"data":615,"type":217},{"text":616},"Не обязательно. Правильный источник мог отсутствовать, быть неверно разобран, плохо разбит, отфильтрован, оценён слишком низко или пропущен при сборке контекста. Замена модели не должна быть первым шагом диагностики.",{"data":618,"type":42},{"text":619,"level":230},"Граничные случаи",{"data":621,"type":357},{"items":622,"style":356},[623,624,625,626,627,628,629,630],"\u003Cb>Конфликтующие документы:\u003C\u002Fb> два источника могут противоречить друг другу из-за различий в версиях, юрисдикциях или продуктах.","\u003Cb>Факты, зависящие от времени:\u003C\u002Fb> семантически релевантный источник может быть уже устаревшим.","\u003Cb>Права доступа:\u003C\u002Fb> система поиска не должна возвращать документы, к которым у текущего пользователя нет авторизованного доступа.","\u003Cb>Многоязычные коллекции:\u003C\u002Fb> модель эмбеддингов и стратегия поиска должны поддерживать фактически используемые языки.","\u003Cb>Таблицы и исходный код:\u003C\u002Fb> разбиение на обычные абзацы может разрушить структуру, необходимую для ответа.","\u003Cb>Очень короткие идентификаторы:\u003C\u002Fb> семантический поиск может быть слабее точного совпадения для артикулов, идентификаторов, кодов ошибок или аббревиатур.","\u003Cb>Длинные вопросы, требующие нескольких фактов:\u003C\u002Fb> поиску может потребоваться декомпозиция, несколько запросов или переранжирование вместо одного запроса top-k.","\u003Cb>Иерархия источников:\u003C\u002Fb> официальная действующая политика может должна иметь приоритет над более старым, но семантически более близким дискуссионным документом.",{"data":632,"type":42},{"text":633,"level":230},"Ограничения",{"data":635,"type":217},{"text":636},"Примеры на Python намеренно оптимизированы для прозрачности, а не для масштаба. Поиск по ключевым словам наивен, разбиение использует длину в символах, примеры на SQLite не включают управление соединениями для продакшена, а семантический пример хранит все эмбеддинги в памяти.",{"data":638,"type":217},{"text":639},"Продакшен-система может требовать векторных индексов, переранжировщиков, гибридного поиска, парсеров документов, кэширования, инкрементальной индексации, версионирования источников, фильтров контроля доступа, наблюдаемости, наборов данных для оценки и обработки сбоев. Ни одно из этих дополнений не меняет базовую архитектуру; они делают каждую границу более надёжной.",{"data":641,"type":217},{"text":642},"RAG также не может создать доказательства, отсутствующие в наборе источников. Если источник неверен, неполон или устарел, лучшая модель эмбеддингов не сможет превратить его в авторитетное знание.",{"data":644,"type":42},{"text":645,"level":230},"Что изменило бы этот ответ?",{"data":647,"type":217},{"text":648},"Архитектура меняется, когда задача требует большего, чем поиск знаний. Актуальный статус заказа требует текущего состояния. Финансовый расчёт может требовать детерминированного кода. Задача веб-исследования может требовать активного поиска. Рабочий процесс может требовать инструментов, способных записывать данные обратно в другую систему. Автономному агенту может потребоваться планирование, разрешения и контроль исполнения в дополнение к поиску.",{"data":650,"type":217},{"text":651},"Поэтому RAG лучше всего понимать как \u003Cb>один слой получения доказательств внутри более крупной ИИ-системы\u003C\u002Fb>. Он мощен именно потому, что у него узкая задача: находить полезную внешнюю информацию и помещать её в рабочий контекст модели.",{"data":653,"type":42},{"text":654,"level":230},"Заключение",{"data":656,"type":217},{"text":657},"RAG становится гораздо проще понять, если убрать названия технологий. Файл — это источник. База данных — это источник. API — это источник. Функция поиска извлекает доказательства. Промпт передаёт эти доказательства модели. Затем LLM интерпретирует их и порождает текст.",{"data":659,"type":217},{"text":660},"Сложная часть production RAG — не вызов модели эмбеддингов. Это построение надёжного пути доказательств от исходного источника до финального утверждения: сохранение происхождения данных, выбор правильного метода извлечения, поддержание информации в актуальном состоянии, контроль доступа, оценка извлечения отдельно от генерации и понимание того, когда прямой вызов базы данных или инструмента лучше семантического поиска.",{"data":662,"type":217},{"text":663},"Это практическое продолжение базовой модели RAG: \u003Cb>сначала понять роли, затем сделать путь данных явным.\u003C\u002Fb>",{"data":665,"type":42},{"text":666,"level":230},"Первоисточники",{"data":668,"type":357},{"items":669,"style":356},[670,671,672,673,674,675],"\u003Ca href=\"https:\u002F\u002Farxiv.org\u002Fabs\u002F2005.11401\">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\u003C\u002Fa> — статья 2020 года, вводящая формулировку RAG, которая сочетает генерацию с извлечённой непараметрической памятью.","\u003Ca href=\"https:\u002F\u002Fwww.sbert.net\u002Fexamples\u002Fsentence_transformer\u002Fapplications\u002Fsemantic-search\u002FREADME.html\">Sentence Transformers — Semantic Search\u003C\u002Fa> — официальная документация по семантическому поиску, эмбеддингам запросов и эмбеддингам документов.","\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fembeddings\">OpenAI — Vector Embeddings\u003C\u002Fa> — официальная документация, описывающая эмбеддинги как числовые представления, используемые для оценки связанности и поиска.","\u003Ca href=\"https:\u002F\u002Fwww.sqlite.org\u002Ffts5.html\">SQLite — FTS5 Extension\u003C\u002Fa> — официальная документация по полнотекстовому поиску и ранжированию BM25 в SQLite.","\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Flibraries\">OpenAI — SDKs and CLI\u003C\u002Fa> — официальный пример Python SDK для Responses API, используемый в дополнительном примере генератора.","\u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fru\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa> — концептуальная первая часть этой серии.","2.31","LLM не знает магическим образом о ваших файлах, базах данных или API. Это практическое продолжение серии о RAG показывает на простом Python, как внешние данные становятся извлекаемыми доказательствами: от текстовых файлов и SQL до полнотекстового поиска, эмбеддингов, сборки контекста и финального вызова LLM.","\u002Fuploads\u002F2026\u002F09\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python-1790517200521-nfsi5i.webp","where-does-an-llm-get-its-data-rag-data-sources-in-python-1790517200521-nfsi5i","PUBLISHED","2026-09-27T09:51:00.000Z","2026-09-27T13:51:43.843Z","2026-09-27T13:58:03.762Z",{"en":685,"de":686,"sr":687,"es":688,"fr":689,"it":690,"ru":691,"zh":692},"\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fde\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fsr\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fes\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Ffr\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fit\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fru\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python","\u002Fzh\u002Fblog\u002Fwhere-does-an-llm-get-its-data-rag-data-sources-in-python",[],{"id":695,"login":696,"email":697,"displayName":698},"20","rooth8233","aleksandar@stajic.de","Aleksandar Stajić",[700,1146],{"lang":701,"title":702,"content":703,"contentJson":704,"excerpt":1145},"en","Where Does an LLM Get Its Data? RAG Data Sources in Python","{\"time\":1790516400000,\"blocks\":[{\"data\":{\"text\":\"The previous article, \u003Ca href=\\\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\\\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa>, established the mental model: the LLM writes, RAG retrieves useful knowledge, the application owns current state, and tools perform actions. This article takes the next step: \u003Cb>where does the data actually come from, and what does retrieval look like in Python?\u003C\u002Fb>\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The important surprise is that an “LLM data source” is usually nothing exotic. It can be a text file, a folder of Markdown documents, a SQL database, an API response, a product catalog, a support system, or a vector index derived from those sources. The AI does not magically know these systems. Your application has to load, query, search, or retrieve the relevant data and place the result into the model’s context.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Data source = where information lives. Retrieval = how the application finds useful information. Context = the selected information given to the model. LLM = the component that interprets that context and generates an answer.\",\"caption\":\"The four-part model used throughout this article\",\"alignment\":\"left\"},\"type\":\"quote\"},{\"data\":{\"text\":\"Question\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"How does an LLM use external data such as files, databases or APIs, and how can a small Python program implement the essential RAG steps without hiding them behind a framework?\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"What This Really Means\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"When developers say that an LLM is “connected to company data,” several different operations may be hidden behind that sentence. One application may execute SQL. Another may call an API. Another may run full-text search. Another may calculate embedding similarity over document chunks. All of them can provide external information to an LLM, but they are not the same retrieval method and they should not be treated as interchangeable.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"This distinction matters because the best retrieval method depends on the shape of the question. “What is our refund policy?” is a document-retrieval problem. “What is order 4711’s current status?” is usually a structured database lookup. “Which paragraph discusses account recovery?” can be keyword or semantic search. RAG is most useful when the system must \u003Cb>discover relevant knowledge before generation\u003C\u002Fb>.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Simplest Example\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Start with three strings in ordinary Python. There is no vector database, no framework, and no LLM yet. We only want to make the retrieval step visible.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"documents = [\\n    \\\"The AKM uses 7.62 mm ammunition.\\\",\\n    \\\"A Med Kit restores health.\\\",\\n    \\\"A 4x scope can be attached to several compatible weapons.\\\"\\n]\\n\\nquestion = \\\"Which ammunition does the AKM use?\\\"\\n\\nfor document in documents:\\n    if \\\"AKM\\\" in document:\\n        print(document)\"},\"type\":\"code\"},{\"data\":{\"text\":\"The program prints the first sentence because it contains the term we searched for. This is primitive retrieval, but the architecture is already visible: \u003Cb>question → search → relevant text\u003C\u002Fb>. RAG adds one more major step: pass the retrieved text to a language model together with the question.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"A slightly more general version ranks documents by overlapping query terms:\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"import re\\n\\ndocuments = [\\n    {\\\"id\\\": \\\"weapon-akm\\\", \\\"text\\\": \\\"The AKM uses 7.62 mm ammunition.\\\"},\\n    {\\\"id\\\": \\\"healing-medkit\\\", \\\"text\\\": \\\"A Med Kit restores health.\\\"},\\n    {\\\"id\\\": \\\"scope-4x\\\", \\\"text\\\": \\\"A 4x scope can be attached to several compatible weapons.\\\"},\\n]\\n\\ndef words(text):\\n    return set(re.findall(r\\\"[a-zA-Z0-9.]+\\\", text.lower()))\\n\\ndef retrieve(question, documents, top_k=2):\\n    query_terms = words(question)\\n    ranked = []\\n\\n    for document in documents:\\n        score = len(query_terms & words(document[\\\"text\\\"]))\\n        if score > 0:\\n            ranked.append((score, document))\\n\\n    ranked.sort(key=lambda item: item[0], reverse=True)\\n    return [document for _, document in ranked[:top_k]]\\n\\nquestion = \\\"Which ammunition does the AKM use?\\\"\\nhits = retrieve(question, documents)\\n\\nfor hit in hits:\\n    print(hit[\\\"id\\\"], \\\"->\\\", hit[\\\"text\\\"])\"},\"type\":\"code\"},{\"data\":{\"text\":\"This is not a production search engine. It ignores morphology, synonyms, spelling variants, document length and many ranking signals. Its value is educational: \u003Cb>RAG does not begin with a vector database. It begins with retrieval.\u003C\u002Fb>\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Where the Example Stops Working\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Exact or lexical matching becomes weak when the question and the source use different words. A document may say “vehicle maintenance,” while the user asks “how do I repair my car?” A lexical retriever can miss the relationship even though a human sees it immediately. Semantic retrieval addresses this by representing text as vectors and comparing meaning rather than only exact tokens.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Long files create another problem. Searching an entire 80-page manual as one unit is too coarse, but splitting every sentence can destroy useful context. Real RAG systems therefore need decisions about parsing, chunking, metadata, ranking, freshness, permissions and provenance.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The example also says nothing about structured live facts. If the user asks for the current status of order 4711 and the application already has a database key, semantic search is usually the wrong first tool. A deterministic database query is better.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Direct Answer\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"An LLM data source is any external system from which an application can obtain information for the model: files, databases, APIs, search indexes, vector stores or live application state. RAG is the pattern of \u003Cb>retrieving relevant knowledge from such sources before generation\u003C\u002Fb>.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"In Python, the essential pipeline can be very small: \u003Cb>load data → create retrievable units → find relevant evidence → assemble context → call the LLM\u003C\u002Fb>. The retrieval method should match the source and the question. Use SQL for exact structured facts, full-text search for lexical matching, embeddings for semantic similarity, and hybrid retrieval when several signals are valuable.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Why This Is So\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"A language model does not automatically receive the contents of your filesystem, PostgreSQL database, CRM, private API or newly edited document. The application decides what external information is accessible and what is placed into the model’s current context.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The original Retrieval-Augmented Generation work by Lewis et al. combined a generative model with external non-parametric memory retrieved from a dense vector index. The broader architectural idea survives beyond that specific implementation: external evidence can be retrieved at inference time instead of expecting all useful knowledge to be encoded in model parameters.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"This creates a useful separation of responsibilities: the source stores information, the retriever selects evidence, the context carries that evidence into the request, and the model interprets it. Keeping those boundaries visible makes failures much easier to diagnose.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Context: The Main Types of Data Sources\",\"level\":2},\"type\":\"header\"},{\"data\":{\"content\":[[\"Source\",\"Typical retrieval method\",\"Good for\"],[\"TXT \u002F Markdown \u002F HTML\",\"Parsing + lexical or semantic search\",\"Documentation, manuals, articles, notes\"],[\"PDF \u002F DOCX\",\"Structure-aware extraction + search\",\"Policies, reports, contracts, manuals\"],[\"SQL database\",\"SQL query or filtered retrieval\",\"Orders, users, products, structured records\"],[\"REST \u002F GraphQL API\",\"HTTP request with parameters\",\"Remote systems and live service data\"],[\"Search index\",\"BM25 \u002F full-text \u002F hybrid search\",\"Large text collections\"],[\"Vector index\",\"Embedding similarity\",\"Semantic document retrieval\"],[\"Application state\",\"Direct state read or tool call\",\"What is true right now\"]],\"withHeadings\":true},\"type\":\"table\"},{\"data\":{\"text\":\"A vector index deserves special attention. In many architectures it is \u003Cb>not the canonical source of truth\u003C\u002Fb>. It is a retrieval index derived from documents or records. The authoritative document may live in object storage, a CMS, Git, PostgreSQL or another system, while embeddings and metadata are stored separately for fast semantic lookup. Some systems do use a vector store as primary storage, but that is an architectural choice rather than a requirement of RAG.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"If the boundary between retrieval, persistent memory, current state and model context is still unclear, see \u003Ca href=\\\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fai-agent-memory-is-not-rag-how-to-separate-memory-retrieval-state-and-context\\\">AI Agent Memory Is Not RAG\u003C\u002Fa>. Those layers can use some of the same storage technologies while still having different correctness rules.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Assumptions\",\"level\":2},\"type\":\"header\"},{\"data\":{\"items\":[\"The application is allowed to access the external source.\",\"The relevant source contains enough information to answer the question.\",\"The data can be parsed or queried in a form the retrieval layer can use.\",\"The retrieved information is fresh enough for the requested decision.\",\"The model receives the selected evidence in its context.\",\"Authorization is enforced before protected evidence reaches the model.\",\"The generation model can still be wrong even when retrieval is correct.\"],\"style\":\"unordered\"},\"type\":\"list\"},{\"data\":{\"text\":\"These assumptions matter because retrieval cannot compensate for missing evidence, stale source versions, broken parsers or unauthorized access. A RAG pipeline can only be as trustworthy as the evidence path that feeds it.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Variables\",\"level\":2},\"type\":\"header\"},{\"data\":{\"content\":[[\"Variable\",\"Why it changes the design\"],[\"Source structure\",\"A SQL table, legal PDF and source-code repository need different retrieval strategies\"],[\"Question type\",\"Exact lookup, conceptual search and multi-hop research are different tasks\"],[\"Freshness requirement\",\"Live state may need direct queries instead of periodically rebuilt indexes\"],[\"Corpus size\",\"In-memory search may work for hundreds of chunks but not for very large collections\"],[\"Language\",\"Multilingual retrieval requires models and tokenization suitable for the actual languages\"],[\"Permissions\",\"Retrieval must filter by the current user’s access rights\"],[\"Latency and cost\",\"More retrieval stages can improve quality but add runtime and infrastructure cost\"],[\"Need for provenance\",\"High-trust systems need source IDs, versions and traceable evidence\"]],\"withHeadings\":true},\"type\":\"table\"},{\"data\":{\"text\":\"Diagnostic \u002F Decision Method\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"The first decision is not “Which vector database should I install?” It is: \u003Cb>What kind of fact am I trying to retrieve?\u003C\u002Fb>\"},\"type\":\"paragraph\"},{\"data\":{\"content\":[[\"Question type\",\"Preferred first approach\",\"Reason\"],[\"Exact ID or current record\",\"SQL \u002F key lookup \u002F API\",\"Deterministic structured access\"],[\"Exact wording, codes, names\",\"Full-text or keyword search\",\"Lexical precision\"],[\"Conceptual question over documents\",\"Semantic vector search\",\"Meaning can differ from wording\"],[\"Mixed enterprise knowledge\",\"Hybrid retrieval + metadata filters\",\"Combines lexical and semantic signals\"],[\"Current application state\",\"Direct state\u002Ftool access\",\"Freshness matters more than document similarity\"]],\"withHeadings\":true},\"type\":\"table\"},{\"data\":{\"text\":\"A useful test is: \u003Cb>Do I already know which record I need, or must the system discover which passage is relevant?\u003C\u002Fb> If the record is known, query it directly. If relevance must be discovered, search becomes more important.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"When an answer is wrong, diagnose the pipeline in order instead of immediately changing the LLM:\"},\"type\":\"paragraph\"},{\"data\":{\"items\":[\"\u003Cb>1. Source coverage:\u003C\u002Fb> Does the correct information exist in the accessible source set?\",\"\u003Cb>2. Freshness:\u003C\u002Fb> Is that version current enough for the question?\",\"\u003Cb>3. Parsing:\u003C\u002Fb> Was the relevant content extracted correctly?\",\"\u003Cb>4. Chunking:\u003C\u002Fb> Did the evidence stay together with the conditions that give it meaning?\",\"\u003Cb>5. Retrieval:\u003C\u002Fb> Does the correct chunk appear among the candidates?\",\"\u003Cb>6. Ranking:\u003C\u002Fb> Are stronger sources ranked above weaker or conflicting ones?\",\"\u003Cb>7. Context assembly:\u003C\u002Fb> Did the application actually send the selected evidence to the model?\",\"\u003Cb>8. Generation:\u003C\u002Fb> Did the LLM faithfully use the supplied evidence?\",\"\u003Cb>9. Attribution:\u003C\u002Fb> Can each important claim be traced to a source?\"],\"style\":\"ordered\"},\"type\":\"list\"},{\"data\":{\"text\":\"For a deeper production-debugging method, see \u003Ca href=\\\"https:\u002F\u002Fstajic.de\u002Fblog\u002Frag-failed-but-which-layer-actually-failed-a-diagnostic-method\\\">RAG Failed — But Which Layer Actually Failed? A Diagnostic Method\u003C\u002Fa>, which expands this chain into independently testable failure layers.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Evidence\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"The RAG paper by Lewis et al. formalized generation that conditions on retrieved external memory rather than relying only on model parameters. That provides the conceptual foundation for separating the generator from a retrievable knowledge source.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Sentence Transformers documents semantic search as embedding the corpus and the query into a vector space and retrieving items with high semantic similarity. Its current API also distinguishes query encoding from document encoding for retrieval tasks.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"SQLite FTS5 demonstrates the other side of the spectrum: mature full-text retrieval can rank documents without embeddings. This matters because lexical search remains valuable for identifiers, exact terminology and many hybrid retrieval designs.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"OpenAI’s embeddings documentation describes embeddings as numerical vector representations used for relatedness and search. This is one implementation path for semantic retrieval, not the definition of RAG itself.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 1: A Folder of Text Files\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Suppose a directory named \u003Ccode>knowledge\u002F\u003C\u002Fcode> contains ordinary text files. Python can load them with no AI library at all.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"from pathlib import Path\\n\\ndef load_text_files(folder=\\\"knowledge\\\"):\\n    documents = []\\n\\n    for path in Path(folder).glob(\\\"*.txt\\\"):\\n        documents.append({\\n            \\\"source\\\": path.name,\\n            \\\"text\\\": path.read_text(encoding=\\\"utf-8\\\")\\n        })\\n\\n    return documents\\n\\ndocuments = load_text_files()\\n\\nfor document in documents:\\n    print(document[\\\"source\\\"], len(document[\\\"text\\\"]))\"},\"type\":\"code\"},{\"data\":{\"text\":\"The filesystem is the data source. The next question is how much text should become one retrievable unit. For long documents, searching one complete file is often too coarse. This is why RAG pipelines commonly create chunks.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"A Very Simple Chunker\",\"level\":3},\"type\":\"header\"},{\"data\":{\"code\":\"def chunk_text(text, max_chars=800):\\n    paragraphs = [p.strip() for p in text.split(\\\"\\\\n\\\\n\\\") if p.strip()]\\n\\n    chunks = []\\n    current = \\\"\\\"\\n\\n    for paragraph in paragraphs:\\n        candidate = f\\\"{current}\\\\n\\\\n{paragraph}\\\".strip()\\n\\n        if current and len(candidate) > max_chars:\\n            chunks.append(current)\\n            current = paragraph\\n        else:\\n            current = candidate\\n\\n    if current:\\n        chunks.append(current)\\n\\n    return chunks\"},\"type\":\"code\"},{\"data\":{\"text\":\"This example groups paragraphs until a rough character limit is reached. It is intentionally understandable rather than optimal. Production systems often chunk by tokens, headings, sections, sentence boundaries or document structure. Tables, source code, contracts and API documentation may need different strategies.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Preserve Provenance While Chunking\",\"level\":3},\"type\":\"header\"},{\"data\":{\"code\":\"def build_chunks(documents):\\n    chunks = []\\n\\n    for document in documents:\\n        for index, text in enumerate(chunk_text(document[\\\"text\\\"])):\\n            chunks.append({\\n                \\\"id\\\": f'{document[\\\"source\\\"]}:{index}',\\n                \\\"source\\\": document[\\\"source\\\"],\\n                \\\"chunk\\\": index,\\n                \\\"text\\\": text,\\n            })\\n\\n    return chunks\"},\"type\":\"code\"},{\"data\":{\"text\":\"A useful chunk carries more than text. Source name, document ID, URL, timestamp, version or section can later support citation, debugging and freshness checks. If provenance is lost during ingestion, it becomes much harder to explain why a particular answer was produced.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 2: Structured Data — Use SQL When SQL Is the Right Tool\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Not every external fact should go through semantic search. If the question asks for an exact current record, a direct database query is usually clearer and more deterministic.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"import sqlite3\\n\\ndef get_order_status(order_id):\\n    connection = sqlite3.connect(\\\"shop.db\\\")\\n    cursor = connection.cursor()\\n\\n    cursor.execute(\\n        \\\"SELECT status, total, currency FROM orders WHERE id = ?\\\",\\n        (order_id,)\\n    )\\n\\n    row = cursor.fetchone()\\n    connection.close()\\n\\n    if row is None:\\n        return None\\n\\n    return {\\n        \\\"order_id\\\": order_id,\\n        \\\"status\\\": row[0],\\n        \\\"total\\\": row[1],\\n        \\\"currency\\\": row[2],\\n    }\\n\\nprint(get_order_status(4711))\"},\"type\":\"code\"},{\"data\":{\"text\":\"If the application already knows that the user is asking about order 4711, embedding the entire orders table and asking semantic search to rediscover that row usually adds complexity without benefit. A strong design rule is: \u003Cb>retrieve structured facts with structured queries; retrieve unstructured knowledge with search.\u003C\u002Fb>\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The returned database row can still be placed into the model context so the LLM can explain it in natural language. But direct state or record access is conceptually different from searching a knowledge corpus.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 3: Full-Text Search Before Embeddings\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Between a naive Python loop and vector search lies a mature class of lexical retrieval systems. SQLite includes FTS5 for full-text search, including BM25 ranking.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"import sqlite3\\n\\nconnection = sqlite3.connect(\\\"knowledge.db\\\")\\ncursor = connection.cursor()\\n\\ncursor.execute(\\n    \\\"CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5(title, body)\\\"\\n)\\n\\ncursor.execute(\\n    \\\"INSERT INTO docs(title, body) VALUES (?, ?)\\\",\\n    (\\\"AKM\\\", \\\"The AKM uses 7.62 mm ammunition.\\\")\\n)\\n\\nconnection.commit()\\n\\nquery = \\\"AKM ammunition\\\"\\n\\nrows = cursor.execute(\\n    \\\"SELECT title, body, bm25(docs) AS score \\\"\\n    \\\"FROM docs WHERE docs MATCH ? \\\"\\n    \\\"ORDER BY score LIMIT 5\\\",\\n    (query,)\\n).fetchall()\\n\\nfor row in rows:\\n    print(row)\\n\\nconnection.close()\"},\"type\":\"code\"},{\"data\":{\"text\":\"Lexical search is especially useful when exact terminology, product codes, names, identifiers or domain-specific words matter. Semantic search is not automatically better. Production systems often combine both signals.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 4: Semantic Retrieval With Embeddings\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"Embeddings turn text into numerical vectors so semantically related passages can be compared even when they do not use identical wording. Sentence Transformers provides a straightforward local implementation.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"# pip install sentence-transformers\\n\\nfrom sentence_transformers import SentenceTransformer, util\\n\\ndocuments = [\\n    \\\"The AKM uses 7.62 mm ammunition.\\\",\\n    \\\"A Med Kit restores health.\\\",\\n    \\\"Vehicle maintenance includes checking oil, brakes and tires.\\\",\\n    \\\"Account recovery requires access to the registered email address.\\\"\\n]\\n\\nmodel = SentenceTransformer(\\n    \\\"sentence-transformers\u002Fmulti-qa-mpnet-base-cos-v1\\\"\\n)\\n\\ndocument_embeddings = model.encode_document(\\n    documents,\\n    convert_to_tensor=True\\n)\\n\\nquestion = \\\"How do I repair my car?\\\"\\n\\nquery_embedding = model.encode_query(\\n    question,\\n    convert_to_tensor=True\\n)\\n\\nhits = util.semantic_search(\\n    query_embedding,\\n    document_embeddings,\\n    top_k=2\\n)[0]\\n\\nfor hit in hits:\\n    print(round(float(hit[\\\"score\\\"]), 3), documents[hit[\\\"corpus_id\\\"]])\"},\"type\":\"code\"},{\"data\":{\"text\":\"The query does not contain the phrase “vehicle maintenance,” but a semantic model can still rank that passage highly because the concepts are related. This is the practical reason embeddings are common in RAG systems.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"For small collections, embeddings can stay in memory. Larger systems usually persist them in a vector-capable index or database and perform nearest-neighbor search there. The storage changes, but the logic remains: encode the question, find relevant document representations, return the best evidence.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 5: Build the Context for the LLM\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"A retriever should return evidence. The LLM should then receive the question plus that evidence. Keeping retrieval and generation separate makes both easier to inspect and test.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"def build_prompt(question, retrieved_documents):\\n    context = \\\"\\\\n\\\\n\\\".join(\\n        f'[{doc[\\\"id\\\"]}] {doc[\\\"text\\\"]}'\\n        for doc in retrieved_documents\\n    )\\n\\n    return f\\\"\\\"\\\"\\nAnswer the question using the supplied context.\\n\\nRules:\\n- Do not invent facts that are not supported by the context.\\n- If the context is insufficient, say so.\\n- Cite the source IDs you used.\\n\\nQuestion:\\n{question}\\n\\nContext:\\n{context}\\n\\\"\\\"\\\".strip()\"},\"type\":\"code\"},{\"data\":{\"text\":\"The instruction does not make the model infallible. It simply creates an explicit evidence boundary. The model can still misunderstand good evidence, ignore a condition or overgeneralize. That is why retrieval quality and generation quality must be evaluated separately.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Real Example 6: A Complete Minimal Pipeline\",\"level\":2},\"type\":\"header\"},{\"data\":{\"code\":\"def answer_question(question, all_documents, call_llm):\\n    # 1. Retrieve evidence\\n    retrieved = retrieve(question, all_documents, top_k=3)\\n\\n    # 2. Build model context\\n    prompt = build_prompt(question, retrieved)\\n\\n    # 3. Generate the answer\\n    answer = call_llm(prompt)\\n\\n    return {\\n        \\\"answer\\\": answer,\\n        \\\"sources\\\": [doc[\\\"id\\\"] for doc in retrieved]\\n    }\"},\"type\":\"code\"},{\"data\":{\"text\":\"The function receives \u003Ccode>call_llm\u003C\u002Fcode> as a dependency on purpose. Retrieval should not care whether generation is performed by a cloud model, a local model or another provider. The data path belongs to the application.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Optional Generator: OpenAI Responses API\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"One possible generator is the OpenAI Responses API. Keeping the model name in an environment variable avoids hard-coding a particular model into the RAG architecture.\"},\"type\":\"paragraph\"},{\"data\":{\"code\":\"# pip install openai\\n\\nimport os\\nfrom openai import OpenAI\\n\\nclient = OpenAI()\\n\\ndef call_llm(prompt):\\n    response = client.responses.create(\\n        model=os.environ[\\\"OPENAI_MODEL\\\"],\\n        input=prompt,\\n    )\\n    return response.output_text\"},\"type\":\"code\"},{\"data\":{\"text\":\"The same retrieval pipeline can be connected to a local inference server. This is an important architectural point: \u003Cb>RAG is not owned by the LLM provider.\u003C\u002Fb> The application owns the source, retrieval and context assembly.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The Whole Architecture in One View\",\"level\":3},\"type\":\"header\"},{\"data\":{\"code\":\"USER QUESTION\\n     |\\n     v\\n+-------------+\\n|  Retriever  |\\n+-------------+\\n   |       |\\n   |       +----> SQL \u002F API \u002F state query\\n   |\\n   +------------> keyword \u002F full-text search\\n   |\\n   +------------> embedding \u002F vector search\\n                     |\\n                     v\\n              relevant evidence\\n                     |\\n                     v\\n+-----------------------------------+\\n| question + evidence + instructions |\\n+-----------------------------------+\\n                     |\\n                     v\\n                   LLM\\n                     |\\n                     v\\n                  answer\"},\"type\":\"code\"},{\"data\":{\"text\":\"This data-flow model is more durable than memorizing one framework. Libraries, databases and model vendors will change; the responsibility boundaries remain.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Common Misconceptions and Failure Modes\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"“RAG means vector database.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"No. Vector search is one retrieval method. RAG can use full-text search, SQL, APIs, knowledge graphs, vector search or combinations of them. The defining pattern is retrieval of external information for generation.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"“If the data is in PostgreSQL, I must embed the whole database.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"No. Structured records should usually remain queryable as structured records. Embeddings are useful for semantic relevance, not as a replacement for deterministic queries.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"“More chunks means a better answer.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"Not necessarily. Extra context can introduce noise, conflicting versions and irrelevant material. Retrieval should optimize for useful evidence, not maximum volume.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"“A high similarity score proves the answer.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"No. Similarity measures relevance, not truth or applicability. A highly similar passage can be outdated, from the wrong product version or valid only under conditions that do not match the question.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"“Once the correct chunk is retrieved, hallucination is solved.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"No. Retrieval improves grounding but does not guarantee faithful reasoning. Generation still needs evaluation, and high-risk workflows may require deterministic validation or human review.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"“The model failed, so change the model.”\",\"level\":3},\"type\":\"header\"},{\"data\":{\"text\":\"Not necessarily. The correct source may have been missing, parsed incorrectly, split badly, filtered out, ranked too low or omitted from the assembled context. Model replacement should not be the first diagnostic step.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Edge Cases\",\"level\":2},\"type\":\"header\"},{\"data\":{\"items\":[\"\u003Cb>Conflicting documents:\u003C\u002Fb> two sources may disagree because versions, jurisdictions or products differ.\",\"\u003Cb>Time-sensitive facts:\u003C\u002Fb> a semantically relevant source may already be stale.\",\"\u003Cb>Permissions:\u003C\u002Fb> a retriever must not return documents the current user is not authorized to access.\",\"\u003Cb>Multi-language collections:\u003C\u002Fb> the embedding model and retrieval strategy must support the languages actually used.\",\"\u003Cb>Tables and source code:\u003C\u002Fb> plain paragraph chunking can destroy structure that is essential to the answer.\",\"\u003Cb>Very short identifiers:\u003C\u002Fb> semantic retrieval can be weaker than exact matching for SKUs, IDs, error codes or acronyms.\",\"\u003Cb>Long questions requiring several facts:\u003C\u002Fb> retrieval may need decomposition, several searches or reranking rather than one top-k query.\",\"\u003Cb>Source hierarchy:\u003C\u002Fb> an official current policy may need to outrank an older but semantically closer discussion document.\"],\"style\":\"unordered\"},\"type\":\"list\"},{\"data\":{\"text\":\"Limitations\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"The Python examples intentionally optimize for transparency, not scale. The keyword retriever is naive, the chunker uses character length, the SQLite examples do not include production connection management, and the semantic example keeps all embeddings in memory.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"A production system may require vector indexes, rerankers, hybrid retrieval, document parsers, caching, incremental indexing, source versioning, access-control filters, observability, evaluation datasets and failure handling. None of those additions change the core architecture; they make each boundary more reliable.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"RAG also cannot create evidence that is absent from the source set. If the source is wrong, incomplete or stale, a better embedding model cannot turn it into authoritative knowledge.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"What Would Change This Answer?\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"The architecture changes when the task requires more than knowledge lookup. A live order status needs current state. A financial calculation may need deterministic code. A web-research task may need active search. A workflow may need tools that can write data back to another system. An autonomous agent may need planning, permissions and execution control in addition to retrieval.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"RAG is therefore best understood as \u003Cb>one evidence-acquisition layer inside a larger AI system\u003C\u002Fb>. It is powerful precisely because it has a narrow job: find useful external information and place it in the model’s working context.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Conclusion\",\"level\":2},\"type\":\"header\"},{\"data\":{\"text\":\"RAG becomes much easier to understand when the technology names are removed. A file is a source. A database is a source. An API is a source. A search function retrieves evidence. A prompt carries that evidence to the model. The LLM then interprets it and produces language.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"The hard part of production RAG is not calling an embedding model. It is building a trustworthy evidence path from the original source to the final claim: preserving provenance, selecting the right retrieval method, keeping information current, controlling access, evaluating retrieval separately from generation, and knowing when a direct database or tool call is better than semantic search.\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"That is the practical continuation of the basic RAG model: \u003Cb>first understand the roles, then make the data path explicit.\u003C\u002Fb>\"},\"type\":\"paragraph\"},{\"data\":{\"text\":\"Primary Sources\",\"level\":2},\"type\":\"header\"},{\"data\":{\"items\":[\"\u003Ca href=\\\"https:\u002F\u002Farxiv.org\u002Fabs\u002F2005.11401\\\">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\u003C\u002Fa> — the 2020 paper introducing the RAG formulation that combines generation with retrieved non-parametric memory.\",\"\u003Ca href=\\\"https:\u002F\u002Fwww.sbert.net\u002Fexamples\u002Fsentence_transformer\u002Fapplications\u002Fsemantic-search\u002FREADME.html\\\">Sentence Transformers — Semantic Search\u003C\u002Fa> — official documentation for semantic retrieval, query embeddings and document embeddings.\",\"\u003Ca href=\\\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fembeddings\\\">OpenAI — Vector Embeddings\u003C\u002Fa> — official documentation describing embeddings as numerical representations used for relatedness and search.\",\"\u003Ca href=\\\"https:\u002F\u002Fwww.sqlite.org\u002Ffts5.html\\\">SQLite — FTS5 Extension\u003C\u002Fa> — official documentation for full-text search and BM25 ranking in SQLite.\",\"\u003Ca href=\\\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Flibraries\\\">OpenAI — SDKs and CLI\u003C\u002Fa> — official Python SDK example for the Responses API used in the optional generator example.\",\"\u003Ca href=\\\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\\\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa> — the conceptual first part of this series.\"],\"style\":\"unordered\"},\"type\":\"list\"}],\"version\":\"2.31.0\"}",{"time":705,"blocks":706,"version":1144},1790516400000,[707,710,713,717,720,723,726,729,732,735,738,740,743,746,748,751,754,757,760,763,766,769,772,775,778,781,784,787,819,822,825,828,838,841,844,874,877,880,906,909,912,924,927,930,933,936,939,942,945,948,950,953,956,958,961,964,966,969,972,975,977,980,983,986,989,991,994,997,1000,1002,1005,1008,1011,1014,1016,1019,1022,1024,1027,1030,1033,1035,1038,1041,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085,1088,1099,1102,1105,1108,1111,1114,1117,1120,1123,1126,1129,1132,1135],{"data":708,"type":217},{"text":709},"The previous article, \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa>, established the mental model: the LLM writes, RAG retrieves useful knowledge, the application owns current state, and tools perform actions. This article takes the next step: \u003Cb>where does the data actually come from, and what does retrieval look like in Python?\u003C\u002Fb>",{"data":711,"type":217},{"text":712},"The important surprise is that an “LLM data source” is usually nothing exotic. It can be a text file, a folder of Markdown documents, a SQL database, an API response, a product catalog, a support system, or a vector index derived from those sources. The AI does not magically know these systems. Your application has to load, query, search, or retrieve the relevant data and place the result into the model’s context.",{"data":714,"type":226},{"text":715,"caption":716,"alignment":225},"Data source = where information lives. Retrieval = how the application finds useful information. Context = the selected information given to the model. LLM = the component that interprets that context and generates an answer.","The four-part model used throughout this article",{"data":718,"type":42},{"text":719,"level":230},"Question",{"data":721,"type":217},{"text":722},"How does an LLM use external data such as files, databases or APIs, and how can a small Python program implement the essential RAG steps without hiding them behind a framework?",{"data":724,"type":42},{"text":725,"level":230},"What This Really Means",{"data":727,"type":217},{"text":728},"When developers say that an LLM is “connected to company data,” several different operations may be hidden behind that sentence. One application may execute SQL. Another may call an API. Another may run full-text search. Another may calculate embedding similarity over document chunks. All of them can provide external information to an LLM, but they are not the same retrieval method and they should not be treated as interchangeable.",{"data":730,"type":217},{"text":731},"This distinction matters because the best retrieval method depends on the shape of the question. “What is our refund policy?” is a document-retrieval problem. “What is order 4711’s current status?” is usually a structured database lookup. “Which paragraph discusses account recovery?” can be keyword or semantic search. RAG is most useful when the system must \u003Cb>discover relevant knowledge before generation\u003C\u002Fb>.",{"data":733,"type":42},{"text":734,"level":230},"Simplest Example",{"data":736,"type":217},{"text":737},"Start with three strings in ordinary Python. There is no vector database, no framework, and no LLM yet. We only want to make the retrieval step visible.",{"data":739,"type":252},{"code":251},{"data":741,"type":217},{"text":742},"The program prints the first sentence because it contains the term we searched for. This is primitive retrieval, but the architecture is already visible: \u003Cb>question → search → relevant text\u003C\u002Fb>. RAG adds one more major step: pass the retrieved text to a language model together with the question.",{"data":744,"type":217},{"text":745},"A slightly more general version ranks documents by overlapping query terms:",{"data":747,"type":252},{"code":261},{"data":749,"type":217},{"text":750},"This is not a production search engine. It ignores morphology, synonyms, spelling variants, document length and many ranking signals. Its value is educational: \u003Cb>RAG does not begin with a vector database. It begins with retrieval.\u003C\u002Fb>",{"data":752,"type":42},{"text":753,"level":230},"Where the Example Stops Working",{"data":755,"type":217},{"text":756},"Exact or lexical matching becomes weak when the question and the source use different words. A document may say “vehicle maintenance,” while the user asks “how do I repair my car?” A lexical retriever can miss the relationship even though a human sees it immediately. Semantic retrieval addresses this by representing text as vectors and comparing meaning rather than only exact tokens.",{"data":758,"type":217},{"text":759},"Long files create another problem. Searching an entire 80-page manual as one unit is too coarse, but splitting every sentence can destroy useful context. Real RAG systems therefore need decisions about parsing, chunking, metadata, ranking, freshness, permissions and provenance.",{"data":761,"type":217},{"text":762},"The example also says nothing about structured live facts. If the user asks for the current status of order 4711 and the application already has a database key, semantic search is usually the wrong first tool. A deterministic database query is better.",{"data":764,"type":42},{"text":765,"level":230},"Direct Answer",{"data":767,"type":217},{"text":768},"An LLM data source is any external system from which an application can obtain information for the model: files, databases, APIs, search indexes, vector stores or live application state. RAG is the pattern of \u003Cb>retrieving relevant knowledge from such sources before generation\u003C\u002Fb>.",{"data":770,"type":217},{"text":771},"In Python, the essential pipeline can be very small: \u003Cb>load data → create retrievable units → find relevant evidence → assemble context → call the LLM\u003C\u002Fb>. The retrieval method should match the source and the question. Use SQL for exact structured facts, full-text search for lexical matching, embeddings for semantic similarity, and hybrid retrieval when several signals are valuable.",{"data":773,"type":42},{"text":774,"level":230},"Why This Is So",{"data":776,"type":217},{"text":777},"A language model does not automatically receive the contents of your filesystem, PostgreSQL database, CRM, private API or newly edited document. The application decides what external information is accessible and what is placed into the model’s current context.",{"data":779,"type":217},{"text":780},"The original Retrieval-Augmented Generation work by Lewis et al. combined a generative model with external non-parametric memory retrieved from a dense vector index. The broader architectural idea survives beyond that specific implementation: external evidence can be retrieved at inference time instead of expecting all useful knowledge to be encoded in model parameters.",{"data":782,"type":217},{"text":783},"This creates a useful separation of responsibilities: the source stores information, the retriever selects evidence, the context carries that evidence into the request, and the model interprets it. Keeping those boundaries visible makes failures much easier to diagnose.",{"data":785,"type":42},{"text":786,"level":230},"Context: The Main Types of Data Sources",{"data":788,"type":336},{"content":789,"withHeadings":14},[790,794,797,800,804,807,811,815],[791,792,793],"Source","Typical retrieval method","Good for",[309,795,796],"Parsing + lexical or semantic search","Documentation, manuals, articles, notes",[313,798,799],"Structure-aware extraction + search","Policies, reports, contracts, manuals",[801,802,803],"SQL database","SQL query or filtered retrieval","Orders, users, products, structured records",[321,805,806],"HTTP request with parameters","Remote systems and live service data",[808,809,810],"Search index","BM25 \u002F full-text \u002F hybrid search","Large text collections",[812,813,814],"Vector index","Embedding similarity","Semantic document retrieval",[816,817,818],"Application state","Direct state read or tool call","What is true right now",{"data":820,"type":217},{"text":821},"A vector index deserves special attention. In many architectures it is \u003Cb>not the canonical source of truth\u003C\u002Fb>. It is a retrieval index derived from documents or records. The authoritative document may live in object storage, a CMS, Git, PostgreSQL or another system, while embeddings and metadata are stored separately for fast semantic lookup. Some systems do use a vector store as primary storage, but that is an architectural choice rather than a requirement of RAG.",{"data":823,"type":217},{"text":824},"If the boundary between retrieval, persistent memory, current state and model context is still unclear, see \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fai-agent-memory-is-not-rag-how-to-separate-memory-retrieval-state-and-context\">AI Agent Memory Is Not RAG\u003C\u002Fa>. Those layers can use some of the same storage technologies while still having different correctness rules.",{"data":826,"type":42},{"text":827,"level":230},"Assumptions",{"data":829,"type":357},{"items":830,"style":356},[831,832,833,834,835,836,837],"The application is allowed to access the external source.","The relevant source contains enough information to answer the question.","The data can be parsed or queried in a form the retrieval layer can use.","The retrieved information is fresh enough for the requested decision.","The model receives the selected evidence in its context.","Authorization is enforced before protected evidence reaches the model.","The generation model can still be wrong even when retrieval is correct.",{"data":839,"type":217},{"text":840},"These assumptions matter because retrieval cannot compensate for missing evidence, stale source versions, broken parsers or unauthorized access. A RAG pipeline can only be as trustworthy as the evidence path that feeds it.",{"data":842,"type":42},{"text":843,"level":230},"Variables",{"data":845,"type":336},{"content":846,"withHeadings":14},[847,850,853,856,859,862,865,868,871],[848,849],"Variable","Why it changes the design",[851,852],"Source structure","A SQL table, legal PDF and source-code repository need different retrieval strategies",[854,855],"Question type","Exact lookup, conceptual search and multi-hop research are different tasks",[857,858],"Freshness requirement","Live state may need direct queries instead of periodically rebuilt indexes",[860,861],"Corpus size","In-memory search may work for hundreds of chunks but not for very large collections",[863,864],"Language","Multilingual retrieval requires models and tokenization suitable for the actual languages",[866,867],"Permissions","Retrieval must filter by the current user’s access rights",[869,870],"Latency and cost","More retrieval stages can improve quality but add runtime and infrastructure cost",[872,873],"Need for provenance","High-trust systems need source IDs, versions and traceable evidence",{"data":875,"type":42},{"text":876,"level":230},"Diagnostic \u002F Decision Method",{"data":878,"type":217},{"text":879},"The first decision is not “Which vector database should I install?” It is: \u003Cb>What kind of fact am I trying to retrieve?\u003C\u002Fb>",{"data":881,"type":336},{"content":882,"withHeadings":14},[883,886,890,894,898,902],[854,884,885],"Preferred first approach","Reason",[887,888,889],"Exact ID or current record","SQL \u002F key lookup \u002F API","Deterministic structured access",[891,892,893],"Exact wording, codes, names","Full-text or keyword search","Lexical precision",[895,896,897],"Conceptual question over documents","Semantic vector search","Meaning can differ from wording",[899,900,901],"Mixed enterprise knowledge","Hybrid retrieval + metadata filters","Combines lexical and semantic signals",[903,904,905],"Current application state","Direct state\u002Ftool access","Freshness matters more than document similarity",{"data":907,"type":217},{"text":908},"A useful test is: \u003Cb>Do I already know which record I need, or must the system discover which passage is relevant?\u003C\u002Fb> If the record is known, query it directly. If relevance must be discovered, search becomes more important.",{"data":910,"type":217},{"text":911},"When an answer is wrong, diagnose the pipeline in order instead of immediately changing the LLM:",{"data":913,"type":357},{"items":914,"style":444},[915,916,917,918,919,920,921,922,923],"\u003Cb>1. Source coverage:\u003C\u002Fb> Does the correct information exist in the accessible source set?","\u003Cb>2. Freshness:\u003C\u002Fb> Is that version current enough for the question?","\u003Cb>3. Parsing:\u003C\u002Fb> Was the relevant content extracted correctly?","\u003Cb>4. Chunking:\u003C\u002Fb> Did the evidence stay together with the conditions that give it meaning?","\u003Cb>5. Retrieval:\u003C\u002Fb> Does the correct chunk appear among the candidates?","\u003Cb>6. Ranking:\u003C\u002Fb> Are stronger sources ranked above weaker or conflicting ones?","\u003Cb>7. Context assembly:\u003C\u002Fb> Did the application actually send the selected evidence to the model?","\u003Cb>8. Generation:\u003C\u002Fb> Did the LLM faithfully use the supplied evidence?","\u003Cb>9. Attribution:\u003C\u002Fb> Can each important claim be traced to a source?",{"data":925,"type":217},{"text":926},"For a deeper production-debugging method, see \u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fblog\u002Frag-failed-but-which-layer-actually-failed-a-diagnostic-method\">RAG Failed — But Which Layer Actually Failed? A Diagnostic Method\u003C\u002Fa>, which expands this chain into independently testable failure layers.",{"data":928,"type":42},{"text":929,"level":230},"Evidence",{"data":931,"type":217},{"text":932},"The RAG paper by Lewis et al. formalized generation that conditions on retrieved external memory rather than relying only on model parameters. That provides the conceptual foundation for separating the generator from a retrievable knowledge source.",{"data":934,"type":217},{"text":935},"Sentence Transformers documents semantic search as embedding the corpus and the query into a vector space and retrieving items with high semantic similarity. Its current API also distinguishes query encoding from document encoding for retrieval tasks.",{"data":937,"type":217},{"text":938},"SQLite FTS5 demonstrates the other side of the spectrum: mature full-text retrieval can rank documents without embeddings. This matters because lexical search remains valuable for identifiers, exact terminology and many hybrid retrieval designs.",{"data":940,"type":217},{"text":941},"OpenAI’s embeddings documentation describes embeddings as numerical vector representations used for relatedness and search. This is one implementation path for semantic retrieval, not the definition of RAG itself.",{"data":943,"type":42},{"text":944,"level":230},"Real Example 1: A Folder of Text Files",{"data":946,"type":217},{"text":947},"Suppose a directory named \u003Ccode>knowledge\u002F\u003C\u002Fcode> contains ordinary text files. Python can load them with no AI library at all.",{"data":949,"type":252},{"code":471},{"data":951,"type":217},{"text":952},"The filesystem is the data source. The next question is how much text should become one retrievable unit. For long documents, searching one complete file is often too coarse. This is why RAG pipelines commonly create chunks.",{"data":954,"type":42},{"text":955,"level":478},"A Very Simple Chunker",{"data":957,"type":252},{"code":481},{"data":959,"type":217},{"text":960},"This example groups paragraphs until a rough character limit is reached. It is intentionally understandable rather than optimal. Production systems often chunk by tokens, headings, sections, sentence boundaries or document structure. Tables, source code, contracts and API documentation may need different strategies.",{"data":962,"type":42},{"text":963,"level":478},"Preserve Provenance While Chunking",{"data":965,"type":252},{"code":490},{"data":967,"type":217},{"text":968},"A useful chunk carries more than text. Source name, document ID, URL, timestamp, version or section can later support citation, debugging and freshness checks. If provenance is lost during ingestion, it becomes much harder to explain why a particular answer was produced.",{"data":970,"type":42},{"text":971,"level":230},"Real Example 2: Structured Data — Use SQL When SQL Is the Right Tool",{"data":973,"type":217},{"text":974},"Not every external fact should go through semantic search. If the question asks for an exact current record, a direct database query is usually clearer and more deterministic.",{"data":976,"type":252},{"code":502},{"data":978,"type":217},{"text":979},"If the application already knows that the user is asking about order 4711, embedding the entire orders table and asking semantic search to rediscover that row usually adds complexity without benefit. A strong design rule is: \u003Cb>retrieve structured facts with structured queries; retrieve unstructured knowledge with search.\u003C\u002Fb>",{"data":981,"type":217},{"text":982},"The returned database row can still be placed into the model context so the LLM can explain it in natural language. But direct state or record access is conceptually different from searching a knowledge corpus.",{"data":984,"type":42},{"text":985,"level":230},"Real Example 3: Full-Text Search Before Embeddings",{"data":987,"type":217},{"text":988},"Between a naive Python loop and vector search lies a mature class of lexical retrieval systems. SQLite includes FTS5 for full-text search, including BM25 ranking.",{"data":990,"type":252},{"code":517},{"data":992,"type":217},{"text":993},"Lexical search is especially useful when exact terminology, product codes, names, identifiers or domain-specific words matter. Semantic search is not automatically better. Production systems often combine both signals.",{"data":995,"type":42},{"text":996,"level":230},"Real Example 4: Semantic Retrieval With Embeddings",{"data":998,"type":217},{"text":999},"Embeddings turn text into numerical vectors so semantically related passages can be compared even when they do not use identical wording. Sentence Transformers provides a straightforward local implementation.",{"data":1001,"type":252},{"code":529},{"data":1003,"type":217},{"text":1004},"The query does not contain the phrase “vehicle maintenance,” but a semantic model can still rank that passage highly because the concepts are related. This is the practical reason embeddings are common in RAG systems.",{"data":1006,"type":217},{"text":1007},"For small collections, embeddings can stay in memory. Larger systems usually persist them in a vector-capable index or database and perform nearest-neighbor search there. The storage changes, but the logic remains: encode the question, find relevant document representations, return the best evidence.",{"data":1009,"type":42},{"text":1010,"level":230},"Real Example 5: Build the Context for the LLM",{"data":1012,"type":217},{"text":1013},"A retriever should return evidence. The LLM should then receive the question plus that evidence. Keeping retrieval and generation separate makes both easier to inspect and test.",{"data":1015,"type":252},{"code":544},{"data":1017,"type":217},{"text":1018},"The instruction does not make the model infallible. It simply creates an explicit evidence boundary. The model can still misunderstand good evidence, ignore a condition or overgeneralize. That is why retrieval quality and generation quality must be evaluated separately.",{"data":1020,"type":42},{"text":1021,"level":230},"Real Example 6: A Complete Minimal Pipeline",{"data":1023,"type":252},{"code":553},{"data":1025,"type":217},{"text":1026},"The function receives \u003Ccode>call_llm\u003C\u002Fcode> as a dependency on purpose. Retrieval should not care whether generation is performed by a cloud model, a local model or another provider. The data path belongs to the application.",{"data":1028,"type":42},{"text":1029,"level":478},"Optional Generator: OpenAI Responses API",{"data":1031,"type":217},{"text":1032},"One possible generator is the OpenAI Responses API. Keeping the model name in an environment variable avoids hard-coding a particular model into the RAG architecture.",{"data":1034,"type":252},{"code":565},{"data":1036,"type":217},{"text":1037},"The same retrieval pipeline can be connected to a local inference server. This is an important architectural point: \u003Cb>RAG is not owned by the LLM provider.\u003C\u002Fb> The application owns the source, retrieval and context assembly.",{"data":1039,"type":42},{"text":1040,"level":478},"The Whole Architecture in One View",{"data":1042,"type":252},{"code":574},{"data":1044,"type":217},{"text":1045},"This data-flow model is more durable than memorizing one framework. Libraries, databases and model vendors will change; the responsibility boundaries remain.",{"data":1047,"type":42},{"text":1048,"level":230},"Common Misconceptions and Failure Modes",{"data":1050,"type":42},{"text":1051,"level":478},"“RAG means vector database.”",{"data":1053,"type":217},{"text":1054},"No. Vector search is one retrieval method. RAG can use full-text search, SQL, APIs, knowledge graphs, vector search or combinations of them. The defining pattern is retrieval of external information for generation.",{"data":1056,"type":42},{"text":1057,"level":478},"“If the data is in PostgreSQL, I must embed the whole database.”",{"data":1059,"type":217},{"text":1060},"No. Structured records should usually remain queryable as structured records. Embeddings are useful for semantic relevance, not as a replacement for deterministic queries.",{"data":1062,"type":42},{"text":1063,"level":478},"“More chunks means a better answer.”",{"data":1065,"type":217},{"text":1066},"Not necessarily. Extra context can introduce noise, conflicting versions and irrelevant material. Retrieval should optimize for useful evidence, not maximum volume.",{"data":1068,"type":42},{"text":1069,"level":478},"“A high similarity score proves the answer.”",{"data":1071,"type":217},{"text":1072},"No. Similarity measures relevance, not truth or applicability. A highly similar passage can be outdated, from the wrong product version or valid only under conditions that do not match the question.",{"data":1074,"type":42},{"text":1075,"level":478},"“Once the correct chunk is retrieved, hallucination is solved.”",{"data":1077,"type":217},{"text":1078},"No. Retrieval improves grounding but does not guarantee faithful reasoning. Generation still needs evaluation, and high-risk workflows may require deterministic validation or human review.",{"data":1080,"type":42},{"text":1081,"level":478},"“The model failed, so change the model.”",{"data":1083,"type":217},{"text":1084},"Not necessarily. The correct source may have been missing, parsed incorrectly, split badly, filtered out, ranked too low or omitted from the assembled context. Model replacement should not be the first diagnostic step.",{"data":1086,"type":42},{"text":1087,"level":230},"Edge Cases",{"data":1089,"type":357},{"items":1090,"style":356},[1091,1092,1093,1094,1095,1096,1097,1098],"\u003Cb>Conflicting documents:\u003C\u002Fb> two sources may disagree because versions, jurisdictions or products differ.","\u003Cb>Time-sensitive facts:\u003C\u002Fb> a semantically relevant source may already be stale.","\u003Cb>Permissions:\u003C\u002Fb> a retriever must not return documents the current user is not authorized to access.","\u003Cb>Multi-language collections:\u003C\u002Fb> the embedding model and retrieval strategy must support the languages actually used.","\u003Cb>Tables and source code:\u003C\u002Fb> plain paragraph chunking can destroy structure that is essential to the answer.","\u003Cb>Very short identifiers:\u003C\u002Fb> semantic retrieval can be weaker than exact matching for SKUs, IDs, error codes or acronyms.","\u003Cb>Long questions requiring several facts:\u003C\u002Fb> retrieval may need decomposition, several searches or reranking rather than one top-k query.","\u003Cb>Source hierarchy:\u003C\u002Fb> an official current policy may need to outrank an older but semantically closer discussion document.",{"data":1100,"type":42},{"text":1101,"level":230},"Limitations",{"data":1103,"type":217},{"text":1104},"The Python examples intentionally optimize for transparency, not scale. The keyword retriever is naive, the chunker uses character length, the SQLite examples do not include production connection management, and the semantic example keeps all embeddings in memory.",{"data":1106,"type":217},{"text":1107},"A production system may require vector indexes, rerankers, hybrid retrieval, document parsers, caching, incremental indexing, source versioning, access-control filters, observability, evaluation datasets and failure handling. None of those additions change the core architecture; they make each boundary more reliable.",{"data":1109,"type":217},{"text":1110},"RAG also cannot create evidence that is absent from the source set. If the source is wrong, incomplete or stale, a better embedding model cannot turn it into authoritative knowledge.",{"data":1112,"type":42},{"text":1113,"level":230},"What Would Change This Answer?",{"data":1115,"type":217},{"text":1116},"The architecture changes when the task requires more than knowledge lookup. A live order status needs current state. A financial calculation may need deterministic code. A web-research task may need active search. A workflow may need tools that can write data back to another system. An autonomous agent may need planning, permissions and execution control in addition to retrieval.",{"data":1118,"type":217},{"text":1119},"RAG is therefore best understood as \u003Cb>one evidence-acquisition layer inside a larger AI system\u003C\u002Fb>. It is powerful precisely because it has a narrow job: find useful external information and place it in the model’s working context.",{"data":1121,"type":42},{"text":1122,"level":230},"Conclusion",{"data":1124,"type":217},{"text":1125},"RAG becomes much easier to understand when the technology names are removed. A file is a source. A database is a source. An API is a source. A search function retrieves evidence. A prompt carries that evidence to the model. The LLM then interprets it and produces language.",{"data":1127,"type":217},{"text":1128},"The hard part of production RAG is not calling an embedding model. It is building a trustworthy evidence path from the original source to the final claim: preserving provenance, selecting the right retrieval method, keeping information current, controlling access, evaluating retrieval separately from generation, and knowing when a direct database or tool call is better than semantic search.",{"data":1130,"type":217},{"text":1131},"That is the practical continuation of the basic RAG model: \u003Cb>first understand the roles, then make the data path explicit.\u003C\u002Fb>",{"data":1133,"type":42},{"text":1134,"level":230},"Primary Sources",{"data":1136,"type":357},{"items":1137,"style":356},[1138,1139,1140,1141,1142,1143],"\u003Ca href=\"https:\u002F\u002Farxiv.org\u002Fabs\u002F2005.11401\">Lewis et al. — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks\u003C\u002Fa> — the 2020 paper introducing the RAG formulation that combines generation with retrieved non-parametric memory.","\u003Ca href=\"https:\u002F\u002Fwww.sbert.net\u002Fexamples\u002Fsentence_transformer\u002Fapplications\u002Fsemantic-search\u002FREADME.html\">Sentence Transformers — Semantic Search\u003C\u002Fa> — official documentation for semantic retrieval, query embeddings and document embeddings.","\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fembeddings\">OpenAI — Vector Embeddings\u003C\u002Fa> — official documentation describing embeddings as numerical representations used for relatedness and search.","\u003Ca href=\"https:\u002F\u002Fwww.sqlite.org\u002Ffts5.html\">SQLite — FTS5 Extension\u003C\u002Fa> — official documentation for full-text search and BM25 ranking in SQLite.","\u003Ca href=\"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Flibraries\">OpenAI — SDKs and CLI\u003C\u002Fa> — official Python SDK example for the Responses API used in the optional generator example.","\u003Ca href=\"https:\u002F\u002Fstajic.de\u002Fblog\u002Fwhat-is-rag-the-simplest-explanation-of-how-it-works\">What Is RAG? The Simplest Explanation of How It Works\u003C\u002Fa> — the conceptual first part of this series.","2.31.0","An LLM does not magically know your files, databases or APIs. This practical continuation of the RAG series shows, with simple Python, how external data becomes retrievable evidence: from text files and SQL to full-text search, embeddings, context assembly and the final LLM call.",{"lang":7,"title":208,"content":210,"contentJson":1147,"excerpt":677},{"time":212,"blocks":1148,"version":676},[1149,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,1203,1214,1216,1218,1220,1223,1225,1227,1239,1241,1243,1252,1254,1256,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303,1305,1307,1309,1311,1313,1315,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335,1337,1339,1341,1343,1345,1347,1349,1351,1353,1355,1357,1359,1361,1363,1365,1367,1369,1371,1373,1375,1378,1380,1382,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402],{"data":1150,"type":217},{"text":216},{"data":1152,"type":217},{"text":220},{"data":1154,"type":226},{"text":223,"caption":224,"alignment":225},{"data":1156,"type":42},{"text":229,"level":230},{"data":1158,"type":217},{"text":233},{"data":1160,"type":42},{"text":236,"level":230},{"data":1162,"type":217},{"text":239},{"data":1164,"type":217},{"text":242},{"data":1166,"type":42},{"text":245,"level":230},{"data":1168,"type":217},{"text":248},{"data":1170,"type":252},{"code":251},{"data":1172,"type":217},{"text":255},{"data":1174,"type":217},{"text":258},{"data":1176,"type":252},{"code":261},{"data":1178,"type":217},{"text":264},{"data":1180,"type":42},{"text":267,"level":230},{"data":1182,"type":217},{"text":270},{"data":1184,"type":217},{"text":273},{"data":1186,"type":217},{"text":276},{"data":1188,"type":42},{"text":279,"level":230},{"data":1190,"type":217},{"text":282},{"data":1192,"type":217},{"text":285},{"data":1194,"type":42},{"text":288,"level":230},{"data":1196,"type":217},{"text":291},{"data":1198,"type":217},{"text":294},{"data":1200,"type":217},{"text":297},{"data":1202,"type":42},{"text":300,"level":230},{"data":1204,"type":336},{"content":1205,"withHeadings":14},[1206,1207,1208,1209,1210,1211,1212,1213],[305,306,307],[309,310,311],[313,314,315],[317,318,319],[321,322,323],[325,326,327],[329,330,331],[333,334,335],{"data":1215,"type":217},{"text":339},{"data":1217,"type":217},{"text":342},{"data":1219,"type":42},{"text":345,"level":230},{"data":1221,"type":357},{"items":1222,"style":356},[349,350,351,352,353,354,355],{"data":1224,"type":217},{"text":360},{"data":1226,"type":42},{"text":363,"level":230},{"data":1228,"type":336},{"content":1229,"withHeadings":14},[1230,1231,1232,1233,1234,1235,1236,1237,1238],[368,369],[371,372],[374,375],[377,378],[380,381],[383,384],[386,387],[389,390],[392,393],{"data":1240,"type":42},{"text":396,"level":230},{"data":1242,"type":217},{"text":399},{"data":1244,"type":336},{"content":1245,"withHeadings":14},[1246,1247,1248,1249,1250,1251],[374,404,405],[407,408,409],[411,412,413],[415,416,417],[419,420,421],[423,424,425],{"data":1253,"type":217},{"text":428},{"data":1255,"type":217},{"text":431},{"data":1257,"type":357},{"items":1258,"style":444},[435,436,437,438,439,440,441,442,443],{"data":1260,"type":217},{"text":447},{"data":1262,"type":42},{"text":450,"level":230},{"data":1264,"type":217},{"text":453},{"data":1266,"type":217},{"text":456},{"data":1268,"type":217},{"text":459},{"data":1270,"type":217},{"text":462},{"data":1272,"type":42},{"text":465,"level":230},{"data":1274,"type":217},{"text":468},{"data":1276,"type":252},{"code":471},{"data":1278,"type":217},{"text":474},{"data":1280,"type":42},{"text":477,"level":478},{"data":1282,"type":252},{"code":481},{"data":1284,"type":217},{"text":484},{"data":1286,"type":42},{"text":487,"level":478},{"data":1288,"type":252},{"code":490},{"data":1290,"type":217},{"text":493},{"data":1292,"type":42},{"text":496,"level":230},{"data":1294,"type":217},{"text":499},{"data":1296,"type":252},{"code":502},{"data":1298,"type":217},{"text":505},{"data":1300,"type":217},{"text":508},{"data":1302,"type":42},{"text":511,"level":230},{"data":1304,"type":217},{"text":514},{"data":1306,"type":252},{"code":517},{"data":1308,"type":217},{"text":520},{"data":1310,"type":42},{"text":523,"level":230},{"data":1312,"type":217},{"text":526},{"data":1314,"type":252},{"code":529},{"data":1316,"type":217},{"text":532},{"data":1318,"type":217},{"text":535},{"data":1320,"type":42},{"text":538,"level":230},{"data":1322,"type":217},{"text":541},{"data":1324,"type":252},{"code":544},{"data":1326,"type":217},{"text":547},{"data":1328,"type":42},{"text":550,"level":230},{"data":1330,"type":252},{"code":553},{"data":1332,"type":217},{"text":556},{"data":1334,"type":42},{"text":559,"level":478},{"data":1336,"type":217},{"text":562},{"data":1338,"type":252},{"code":565},{"data":1340,"type":217},{"text":568},{"data":1342,"type":42},{"text":571,"level":478},{"data":1344,"type":252},{"code":574},{"data":1346,"type":217},{"text":577},{"data":1348,"type":42},{"text":580,"level":230},{"data":1350,"type":42},{"text":583,"level":478},{"data":1352,"type":217},{"text":586},{"data":1354,"type":42},{"text":589,"level":478},{"data":1356,"type":217},{"text":592},{"data":1358,"type":42},{"text":595,"level":478},{"data":1360,"type":217},{"text":598},{"data":1362,"type":42},{"text":601,"level":478},{"data":1364,"type":217},{"text":604},{"data":1366,"type":42},{"text":607,"level":478},{"data":1368,"type":217},{"text":610},{"data":1370,"type":42},{"text":613,"level":478},{"data":1372,"type":217},{"text":616},{"data":1374,"type":42},{"text":619,"level":230},{"data":1376,"type":357},{"items":1377,"style":356},[623,624,625,626,627,628,629,630],{"data":1379,"type":42},{"text":633,"level":230},{"data":1381,"type":217},{"text":636},{"data":1383,"type":217},{"text":639},{"data":1385,"type":217},{"text":642},{"data":1387,"type":42},{"text":645,"level":230},{"data":1389,"type":217},{"text":648},{"data":1391,"type":217},{"text":651},{"data":1393,"type":42},{"text":654,"level":230},{"data":1395,"type":217},{"text":657},{"data":1397,"type":217},{"text":660},{"data":1399,"type":217},{"text":663},{"data":1401,"type":42},{"text":666,"level":230},{"data":1403,"type":357},{"items":1404,"style":356},[670,671,672,673,674,675],"Post erfolgreich abgerufen",{"items":1407,"source":1490,"manualIds":1491,"manualMatchedIds":1492},[1408,1415,1422,1429,1436,1443,1450,1455,1462,1469,1476,1483],{"id":1409,"slug":1410,"title":1411,"excerpt":1412,"featuredImage":1413,"publishedAt":1414},"3","postfixadmin-enterprise-grade-management-for-postfix-mail-systems-anno-2026","PostfixAdmin: Управление корпоративного уровня для почтовых систем Postfix — Anno 2026","PostfixAdmin — это ориентированный на базу данных интерфейс администрирования, разработанный для профессиональных почтовых систем Postfix. Вместо того чтобы скрывать сложность, он обеспечивает точный контроль над доменами, почтовыми ящиками, псевдонимами и разрешениями отправителей. В этой статье объясняется, почему PostfixAdmin остается надежным корпоративным решением в 2026 году и как он вписывается в современные, ориентированные на безопасность почтовые инфраструктуры.","\u002Fuploads\u002F2026\u002F01\u002Fpostfixadmin-enterprise-grade-management-for-postfix-mail-systems-anno-2026-1768311098693-w36cpk.webp","2026-01-13T07:58:00.000Z",{"id":1416,"slug":1417,"title":1418,"excerpt":1419,"featuredImage":1420,"publishedAt":1421},"383","canonical-architecture-url-design-resolver-logic-api-scalability-specification","Каноническая архитектура, Дизайн URL, Логика резолвера, Спецификация API и масштабируемости","Геоориентированная архитектура обнаружения для мультитенантных порталов. Определяет канонические URL-адреса, логику разрешения, стратегию кэширования и гео-модель чтения без привязки к CMS или рефакторинга базы данных. Разработано для стабильности SEO, масштабируемости и будущих расширений, таких как бронирование и карты.","\u002Fuploads\u002F2026\u002F01\u002Fcanonical-architecture-url-design-resolver-logic-api-scalability-specification-1769890763607-7rghbp.webp","2026-01-31T06:12:00.000Z",{"id":1423,"slug":1424,"title":1425,"excerpt":1426,"featuredImage":1427,"publishedAt":1428},"457","should-you-buy-5g-openwrt-router-old-firmware","Стоит ли покупать 5G OpenWrt-роутер со старой прошивкой? ZBT Z8102AX как практический пример","Покупка 5G-роутера с OpenWrt на старой прошивке может иметь смысл, но только при определённых условиях. ZBT Z8102AX наглядно демонстрирует обе стороны: железо полезное, модем работает, а роутер оставался стабильным в ходе тестов, однако OpenWrt 21.02, слабая упаковка и неясные пути обновления требуют взвешенного решения о покупке.","\u002Fuploads\u002F2026\u002F06\u002Fopenwrt-router-review-dual-sim-05-1781620596218-5ldld4.webp","2026-06-16T10:41:00.000Z",{"id":1430,"slug":1431,"title":1432,"excerpt":1433,"featuredImage":1434,"publishedAt":1435},"460","ai-agent-reliability-why-the-final-answer-is-not-enough","Надёжность ИИ-агентов: почему финального ответа недостаточно","Правильный вывод не доказывает правильность рассуждений, безопасность выполнения или надежность системы.","\u002Fuploads\u002F2026\u002F09\u002Fai-agent-reliability-why-the-final-answer-is-not-enough-1788955466306-pl0qhz.webp","2026-09-09T04:01:00.000Z",{"id":1437,"slug":1438,"title":1439,"excerpt":1440,"featuredImage":1441,"publishedAt":1442},"470","what-should-an-ai-agent-remember-forget-recompute-or-retrieve-again","Что ИИ-агент должен помнить, забывать, перевычислять или извлекать повторно?","Долгоживущие агенты не должны помнить всё. В этой статье представлена практическая модель жизненного цикла для определения того, что относится к долговременной памяти, что следует извлекать повторно, что безопаснее пересчитать, а что должно истечь по сроку действия или быть заменено.","\u002Fuploads\u002F2026\u002F09\u002Fwhat-should-an-ai-agent-remember-forget-recompute-or-retrieve-again-1790351131087-iehz28.webp","2026-09-25T09:43:00.000Z",{"id":1444,"slug":1445,"title":1446,"excerpt":1447,"featuredImage":1448,"publishedAt":1449},"447","google-io-2026-antigravity-ai-studio-and-google-devtools","Google I\u002FO 2026: Antigravity, AI Studio и переход к агентным DevTools","Google I\u002FO 2026 ясно дала понять инженерам одну вещь: ИИ-инструменты выходят за рамки автодополнения и переходят к управляемому агентному выполнению. В этой статье подробно разбираются Antigravity 2.0, растущая роль Google AI Studio, Gemini 3.5 Flash, а также реальные компромиссы, связанные с оркестрацией, привязкой к платформе, верификацией и проектированием рабочих процессов разработчиков.","\u002Fuploads\u002F2026\u002F05\u002Fgoogle-io-2026-antigravity-ai-studio-and-google-devtools-1779227878312-e1yvs3.webp","2026-05-21T10:52:00.000Z",{"id":1451,"slug":1452,"title":1452,"excerpt":10,"featuredImage":1453,"publishedAt":1454},"369","git-with-automatic-upload-and-synchronization-to-a-production-server","\u002Fuploads\u002F2024\u002F05\u002Fstep-by-step-guide-illustration-showing-the-process-of-setting-up-Git-with-auto-upload-and-synchronization-to-a-production-server-large.webp","2024-05-28T22:48:00.000Z",{"id":1456,"slug":1457,"title":1458,"excerpt":1459,"featuredImage":1460,"publishedAt":1461},"450","google-io-2026-gemini-omni-and-gemini-3-5","Google I\u002FO 2026: Gemini Omni, Gemini 3.5 и вычислительный слой, стоящий за агентным ИИ","Google I\u002FO 2026 поставила Gemini Omni и Gemini 3.5 в центр стратегии Google в области агентного ИИ. В этой статье разбирается разница между мультимодальным созданием и интеллектом уровня действий, почему Gemini 3.5 Flash важна для агентов и программирования, и как эти модели обеспечивают более широкий сдвиг платформы Google I\u002FO 2026.","\u002Fuploads\u002F2026\u002F05\u002Fgoogle-io-2026-gemini-omni-and-gemini-3-5-1779227791401-6hzln1.webp","2026-05-21T11:01:00.000Z",{"id":1463,"slug":1464,"title":1465,"excerpt":1466,"featuredImage":1467,"publishedAt":1468},"454","zbt-z8102ax-rm500u-ea-5g-modem-test","Quectel RM500U-EA в ZBT Z8102AX: диапазоны 5G, o2 Germany и поведение сигнала в реальных условиях","ZBT Z8102AX использует модем Quectel RM500U-EA для подключения 4G и 5G. В первом практическом тесте роутер успешно подключился к o2 Germany с LTE Band 3 и NR n28. Модем работает, но более глубокая диагностика, такая как RSRP, RSRQ, SINR, блокировка диапазонов и поведение сот, все еще требует надлежащего тестирования.","\u002Fuploads\u002F2026\u002F06\u002Fopenwrt-router-review-dual-sim-06-1781620597879-qay2sx.webp","2026-06-16T08:39:00.000Z",{"id":1470,"slug":1471,"title":1472,"excerpt":1473,"featuredImage":1474,"publishedAt":1475},"472","why-more-context-can-make-ai-answers-worse","Почему больше контекста может ухудшить ответы ИИ","Большее контекстное окно не гарантирует более качественного ответа. В этой статье объясняется, как размывание сигнала, противоречивые данные, устаревшее состояние, чувствительность к позиции и сжатие с потерями могут снизить надежность ИИ — и предлагается практический стресс-тест контекста.","\u002Fuploads\u002F2026\u002F09\u002Fwhy-more-context-can-make-ai-answers-worse-1790351615793-2ntv2v.webp","2026-09-25T11:51:00.000Z",{"id":1477,"slug":1478,"title":1479,"excerpt":1480,"featuredImage":1481,"publishedAt":1482},"465","from-research-protocol-to-a-general-ai-reasoning-framework","От исследовательского протокола к универсальному фреймворку рассуждений ИИ","Методология, разработанная для строгих исследований с применением ИИ, может быть обобщена далеко за пределы самих исследований. Благодаря отделению доказательств от предположений, проверке конкурирующих гипотез, контролю фрейминга промптов, поиску опровергающих доказательств и применению предметно-ориентированных валидаторов та же архитектура рассуждений может улучшить отладку, проектирование программного обеспечения, стратегию, технический анализ и поддержку принятия решений с применением ИИ.","\u002Fuploads\u002F2026\u002F09\u002Ffrom-research-protocol-to-a-general-ai-reasoning-framework-1789802635691-hhf78v.webp","2026-09-19T01:16:00.000Z",{"id":1484,"slug":1485,"title":1486,"excerpt":1487,"featuredImage":1488,"publishedAt":1489},"466","the-gpu-is-not-the-product-future-proof-private-ai-architecture","GPU — не продукт: перспективная архитектура приватного ИИ","Инфраструктура приватного ИИ не должна проектироваться вокруг одного GPU или одной модели. Более устойчивый подход объединяет быстрые GPU для инференса, ИИ-системы с большим объемом памяти, узлы физического ИИ и опциональные передовые облачные модели за уровнем маршрутизации, учитывающим возможности.","\u002Fuploads\u002F2026\u002F09\u002Fthe-gpu-is-not-the-product-future-proof-private-ai-architecture-1790140878812-8hsl39.webp","2026-09-23T01:19:00.000Z","fallback",[],[]]