RAG-праект — гэта не адзін велізарны файл Python: структура тэчак, якая маштабуецца
RAG-праект не павінен быць адным гіганцкім файлам Python. Структура тэчак для загрузкі, разбіўкі, эмбедынгаў, вектарнага сховішча, пошуку, промптаў, API і тэстаў.
Крыніцы: **Your RAG Project Should Not Be One Giant Python File**
Дэманстрацыя RAG заўсёды займае частаку дня: запускаецца PDF, яго дзельняць, уключаюцься часткі, яны кладуцца ў базу дадзеных вектараў, ставіцца запіт, чытаецца адпаведзь. Гэтага цалкам дастаткова для эксперыменту на выяваку вякання. Але як аднойчына для рэальной працы такі падход быстра перастае эфектыўны, і прычына рэдка калі крыўдзіць саму мадэль чы нават базу вектараў. Прычына — структура.
Праекты для пачаткуючых часта пачынаюцца з аднаго файла app.py, які аб’ёднае завантажэнне файлоў, ўдзельненне, стварэнне вектарных прэдставленняў, зберагчыце, выкарыстоўванне дадзеных, шаблоны запытаў, вызывы модэляў, маршруты API, логаванне і настройкі. Работа з аднім файлам здаецца зручной, пакуль праект не вырастае. Прыходзіць новы крыніцы дакументаў, патрэбна пробная стратэгія удзельнення, ChromaDB заменяецца на Pinecone, змінююцыся шаблоны запытаў, з’яўляюцца канцэнтры FastAPI, якасць выкарыстоўвання дадзеных віявляецца нізкай, і хтось прасіць пра тэсты. До таго часу працаваць з тым аднім файлам стае вельмі складна.
Сістэма, якая легкая да адмініструванне, дае кожнаму элементу свае месца. Працэс запоўнення завантажае даны, часткаванне дзеліць дакументы, эмбедынгі ператвараюць тэкст у вектары, база дадзеных вектараў яе зберагае, функцыя пошуку знаходзі корыстны кантэкст, прампты формуюць запіт, шар LLM выкананае вызывы модэлю, а API адкрывае усё для корыстнікаў. Самэ гэта раздзелэнне ператварае дэманстрацыю ў інжынерную сістэму.
Адна псіхалагічная модэль: пайплайн проты разлічных прыкладненняў
Думаць трэба так: пайплайн падготавляе знанні, а прыкладненне іх выкарыстоўвае. Трэба яны раздзеляць.
Частка пайплайна включае завантажэнне дакументаў, чыставанне тэксту, дзеленне кантэнту, стварэнне эмбедынгаў і ўпакоўванне іх. Частка прыкладнення включае прыём запытанняў, выкарыстоўванне частак, стварэнне прамптаў, вызыв модэлю, вярнэнне адказаў праз API і логаванне дзеянняў у рэальным часе.
Калі ўсе з’едынаецца, кожная змяна ўскладнюе сітуацыю. Прабава новага розмеру частакі не павінна вымагаць правкі маршруту API. Пераход з аднаго прадаўца модэляў на іншага не павінен значыць перапісву процэса запрашэння дадзэнняў. Дадаванне інфармацыі з веб-сайтаў не павінна вплываць на шаблоны запітаў.
Пачатковая структура
Ёсць структура, якая можа служыць першым праектам. Бна не ёсць ўнікальной, але кожны папака мае чыстае прабачэнне для свайго існавання.
rag-project/
|-- README.md
|-- requirements.txt
|-- .env
|-- .gitignore
|-- config.yaml
|-- main.py
|-- src/
| |-- ingestion/
| | |-- __init__.py
| | `-- loader.py
| |-- chunking/
| | |-- __init__.py
| | `-- chunker.py
| |-- embeddings/
| | |-- __init__.py
| | `-- embedder.py
| |-- vectordb/
| | |-- __init__.py
| | `-- vector_store.py
| |-- retrieval/
| | |-- __init__.py
| | `-- retriever.py
| |-- prompts/
| | |-- __init__.py
| | `-- prompt_templates.py
| |-- llm/
| | |-- __init__.py
| | `-- llm_client.py
| |-- api/
| | |-- __init__.py
| | `-- routes.py
| `-- utils/
| |-- __init__.py
| `-- helpers.py
|-- tests/
| `-- test_app.py
`-- logs/
`-- app.log
README.md
README ёсць вората даўняга доступу да праекту. У яму трэба пісаць, што робіць сістэма, як яе насталяваць і запускаць, а таксама для чаго прызначаны кожны з галоўных папак. Пачатківцы часта адкладаюць яго стварэнне, але хорашы README дапамагае зразумець сэрвіс праз месяц. Як мінімум, у яму трэба паведаміць прызначэнне праекту, паследованне крокаў насталявання, неабходныя зменныя сераўіса, спосабы адчытання дакументаў, спосабы запуску API, прыклады запытанняў і вядомыя обмежэнні. Для праектаў у портфоліі гэта ўжо болей важлівае, адколькі пераглядач можа прыглянуцца яму перш чым адмаўіць запуск.
requirements.txt
У гэтым файле пераказаны пакеты Python, якія патрэбны для праекту. Просты сэрвіс RAG можа міты ў сабе ўсё, што пераказана выклікана.
fastapi
uvicorn
python-dotenv
pydantic
langchain
chromadb
sentence-transformers
openai
pypdf
Спісак залежнасцей залежыць ад таго, якія тэхналогіі вы вікорыстоўваеце. Правілам ёсць не залішаць залежнасці схованымі на вашай сабе машыне. Кожны, хто клонуе праект, павінен магчымае завантажыць усё за аднам камандам.
pip install -r requirements.txt
Гэта робяць праект воспроізводзімым і спрыяе лёгкам развяроце і адзінаковам анаізу.
.env і .gitignore
Файл .env зберагае чутлівыя даны, такія як канты прыступу да API. Тыповы прыклад такі.
OPENAI_API_KEY=your_key_here
VECTOR_DB_URL=your_vector_db_url
Ён ніяк не павінен дася да GitHub, таму ваш .gitignore павінен выключыць яго разам з логамі і байткодамі.
.env
logs/
__pycache__/
*.pyc
Занесенне ключоў у файлы Python можа працаваць локальна, але ўсё ж ёсць небезпечным. Таёмныя даны трэба зберагаць параду коду.
config.yaml
Нетаёмныя настройкі павінны знаходзіцца ў config.yaml, напрыклад:
chunking:
chunk_size: 800
chunk_overlap: 120
retrieval:
top_k: 5
models:
embedding_model: text-embedding-3-small
llm_model: gpt-4.1-mini
vector_db:
provider: chromadb
collection_name: company_docs
Сістэмы RAG патрабуе падканалення, і вам паведамся змяніць размер фрагментаў, ступень перакрыцця, колькасць рэзультатаў, якія трэба атрымаць, назву модэлі, параметры тэмпературы чыста опцыі базы вектарных дадзеных. Калі гэтыя значэнні розмешаны ў аднойчыных файлах, падканаленне стае клопачлівым. Настройкі павінны быць лёгкімі да пошуку і змены.
Папкі з кодам
ingestion/
Гэтая папка чытае даныя з PDF-файлаў, веб-сайтаў, файлаў CSV, API, баз дадзеных, фармата Markdown, экспортаваных дадзеных з Notion, папак Drive чыста з іншых джэрэл, якія у вас ёсць. Іё задача — не адпавядаць на запытанні, а прыносіць неапрануты контэнт у чыстым і прыемлівым фармаце. Файл loader.py можа мець такія функцыі, як load_pdf(path), load_website(url), load_csv(path) і load_database_table(connection, table_name), кожная з яых вяртае дакументы ў стандартны фармат, зазвычай тэкст плюс метаданы.
Метаданыя заслуговае на большэй увагі, чым яна зазвычай атрымлеўае. Фрагмент тэксту ёсць корыстным, але фрагмент з іменам выканання, номерам стороніцы, URL, тыпам дакумента, автаром і часам стварэння ёсць ўжо болей корыстным, калі толькі гэтыя даныя дапамагаюць у фільтрацыі, цітатах, адлагоджэнні проблем і стварэнні довер'я.
Разбіўка на фрагменты/
Разбіўка на фрагменты здаецца простай, але яна мае вялічынны адзінак на якасць. Занадта вялікія фрагменты вяртаюць шырокія часткі, заполненыя нерэлевантным тэкстам, тады як мініяцюрныя фрагменты пазбавляюць мадэль неабходнага кантэксту. Мета — захаваць значэнне, таму замест таго, каб слепа разбіваць на кожных 500 сімвоў, трэба расследаваць можлівасць разбівкі па заглавлях, параграфах, частках, стороніцах, блоках коду, табліцах або семантычных межах. Файл chunker.py ператварае цэлыя дакументы на фрагменты, гатовыя для включэння.
Адыякрацыя гэтай логікі ўзгодзіцца, таму што гэта адны з найшырэйшых спосабоў для паспрабоўкі пакрашчэння сістэмы RAG. Калі чакінг аддзельны, можна тэставаць новыя стратэгіі, не чынячы змян у іншым.
embeddings/
Эмбедынгі — гэта чысловыя прэдставленні тэксту, якія дазволяюць сістэме пораўняваць значэнні: запыт эмбедуецца і паўтараецца з эмбедаванымі чакамі. Файл embedder.py павінен централізаваць завантажэнне модэлю, эмбедаванне адзіных вхідных дадзенняў і пакетаў, кераванне лімітамі частоты, прабавы пасля адмов і назіранне за тым, якая версія модэлю стварыла вектары.
Аддзельнасць мае значэнне, таму што модэлі эмбедавання змінююцца. Можна пачаць з локальнага sentence-transformer, пазней перэйсці на хоставаную API, а пасля знову змяніць прадаўца з-за костаў або якосці. Калі гэтая логіка знаходзіцца ў аднам месцы, кожная змяна застаецца керованай.
vectordb/
У гэтай папцы зберагаюцца эмбеддынгі і метаданы. Тыповыя варыянты — FAISS для локальных эксперыментаў, ChromaDB для простых локальных або малых прыемных сэтапаў, Pinecone для кераванага пошуку ў воблаку, а таксама Weaviate, Qdrant або Milvus для большых або болей спецыялізаваных задач.
Файл vector_store.py павінен скрыць спецыфіку базы дадзеных ад рэшты праекту. Код для адзыскання дадзеных не павінен знаваць дэталі Pinecone або ChromaDB. Ён павінен выклікваць стандартныя методы на рэвэле праекту, такія як add_documents(chunks), similarity_search(query, top_k), delete_collection(name) і get_document_by_id(id). Як толькі вы заменіце базу дадзеных, павінны змяніць толькі гэты слой, а не што-інша.
retrieval/
Ёсць запіт, і выдаюцься найболей рэлевантныя фрагменты. Базаваныя способы адзыскання — гэта просты пошук за сэмліністасю, тады калі болей складныя версіі дадаюць фільтры метаданых, гібрыдны пошук, пераранжаванне, перапісв запіта або адзыскання за колькіма запітамі. Гэта мае вельмі вялікое значэнне, каліжоў мадэль можа дастаць правільныя адказы толькі тады, калі яна отрымае правы кантэкст, а слабы спосаб адзыскання абяцца ў слабыя адказы.
Файл retriever.py можа уключыць запіт, адзыскаць інфармацыю з базы, застосавіць фільтры, перэранкаваць рэзультаты, адмахнуцца дуплікатоў і вернуць чысты кантэкст для запіта. Раздзелэнне гэтых функцый значна спрыяе дэбагаванню, адколі можна выясніць, чы не паспела мадэль адпаведзець чы проста спосаб адзыскання аддаў некарэктны кантэкст. Калі гэтыя два элементы з’едзіненыя, на гэты вопыт няма чыстай адпаведзі.
prompts/
Запыты не трэба рассылаць па всім кодбэсу. Файл prompt_templates.py можа хаваць шаблоны, падобныя да гэтага.
You are a helpful assistant answering questions using the provided context.
Use only the context below. If the answer is not in the context, say you do not know.
Context:
{context}
Question:
{question}
Answer:
Зберагчы прыказы ў свой файл — гэта прычынка, якую варта выклікваць якомога раней. Яны часта зміняюцца, калі вы експерыментуеце з інструкціямі, фарматамі выходных даных, стылямі цітавання, правіламі безпекі чым поведынкай адмовы. Калі яны знаходзяцца всерэдзі маршрутаў чым вызоваў модэляў, іх стае важка версіяваць чым тэставаць. Спрыяйце ўважліваму ставленню да яных як да часткі дызайну і створыце для яных спецыяльны файл.
llm/
Усі вызывы мовных модэляў должны знаходзіцца ў аднам месцы, незалежна ад таго, чыі ўпроцэсавальнік — OpenAI, Claude, Gemini, локальны модэль Ollama чым ўсё іншае. Рэшта прыкладнення не павінна прыкладваты да гэтага значэння. Файл llm_client.py можа кераваць ініцыялізацыяй, вызывамі API, павторнымі спробамі, тайма-аутамі, налаштаваннямі тэмпературы, парсаваннем адпаведзей, логаваннем вікорыстоўвання токенав чым адліквідацыяй памылак, спецыфічных для кожнага упроцэсавальніка. Модэлі зміняюцца быстра: сегодня GPT, завтра Claude, наступнага месцацу меншы модэль для дышэўых запытанняў і моцнейшы для складных. Централізаваныя вызывы спрыяюць лёгкам пераходу між модэлямі.
api/
Эты слой дазваляе іншым людзям і прыкладнім програмам выкарыстоўваць сістэму, зазвычай праз FastAPI або Flask. Ён павінен прымаць і пераканаць запіты, вызываць працэсы запошуку і стварэння даных, вяртаць структураваныя адпаведзі і чыста працаваць з адмоўкамі. У ём не павінна быць сама логіка. Маршрут павінен быць простым: прымаць запіт, вызваць належную службу, вярнуць рэзультат. Калі адны канцэнтры містыць працэсы прыймання даных, ўдзелення іх на часткі, ўкладання, запошук, стварэння запрошэнняў і вызывы модэляў, праект стае некерованым. API павінен быць входам, а не цэлым будынкам.
utils/
Маленькія дапаможнікі, які вікорыстоўваюцца ў праекце, знаходзяцца тут: наладжванне конфігурацыі, фарматаванне логаў, чыстка тэксту, перакананне правільнасці шляхоў, відлік часу і абрабатка распашчытых памылак. Будзьце дысцыплінованыя, каліжа гэты папака лёгка прымець форму сметніка. Дапаможнік, які явна належыць да часткі прадукту, якая прыймаецца, кладзіцца ў папаку ingestion, а той, які належыць да часткі, якая выкарыстоўваецца для выкарысці, — у папаку retrieval. Выкорыстоўвайце utils толькі для таго, што справадзівае сабе спільнае.
tests/
Тэсты аддзеляюць серьёзныя праекты ад нестабільных дэманстрацый, а сістэмы RAG складаюцца з многа маленькіх частак, якія можна тэставаць по адной. Вы можэце пераканацца, што PDF-файлы завантажваюцца, што размеры частак правільныя, што метаданыя захавляюцца, што ствараюцца эмбеддынгі, што працэс выкарысці вяртаяць рэлевантныя дакументы, што запросы мітуюць як кантэкст, так і запыт, і што API вяртае адпаведны фармат.
Не трэба ідэальнага пакрыцця ў першы ж дзень, але патрэбна столькі, каб было можна з’ясавіць, калі ў чымсь з’яўляюцца проблемы. Тэсты адзысквы ўзораў ёсць особа ўплотнацькія: трэба зберагчы невялікі набор прыкладоў дакументаў і запытанняў, вызначыць, які выкарыстоўваць выкарыстоўваць для кожнага з іх, і паўтарыць іх пасля будзь-яых змян у настройках чанкавання або укладання. Гэта дае базовы сігнал пра якасць прытаму, калі корыстувальнікі ўжо не знайдзяць проблем.
logs/
Логі з рэальнай працы можна перадаваць у інструмент для манітарынгу, але просты файл лога дапамагае пад час разработкі. Логі павінны адпавядаць на запытанні корыстувальніка: якія чанкі былі адзысканы, якія выкарыстоўваць выкарыстоўваць, скількі часу знадобілася на адзыскву і які модэль быў запрашаны. Калі корыстувальнік кажа, што адпаведзь была некорэктная, трэба бачыць, што на самай працы было адзыскана і які запыт быў адправлены.
Зменшэнне габаратаў main.py
Точка запуску должна застаць простай. Яна запускае сервер API, завантажвае настройкі ількі вызывае галоўную функцыю пайплайну, і больш нічога. Простая точка запуску — гэта знак таго, што справжня праца знаходзіцца ў правых модулях.
Што гэта дае вам
- Адлагоджванне: выдзельце прычыну канфлікту. Якшо рэсультат некарэктны, перагляньце працэс запоўнення даных, пасля чаго — працэс разбівання іх на часткі і стварэння эмбедынгаў. Якшо API не працуе, перагляньце шар маршрутаў.
- Тэставанне: перагляньце кожны компонент окрэма, такім чынам пераканаючыся, што для праверкі чытальніка PDF не трэба запускаваць усю аплікацыю.
- Масштабаванне: паспрабуйце паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабаваць паспрабавац
- Развёртыванне: настройкі, секрэты, залежнасці і точкі запуску ўсё ясна.
Перш за ўсё гэта робіць праект зрозумелым, а праект, які лёгкі да розумэння, лёгкі да довер'я.
Правілле для першых праектаў
Для кожнай задачы выкорыстоўваеце адну папку, і не прагнучы да ідэальнай архітэктуры, проста утрымваецеся ад адной вялікай файлавай структуры. Пачніце з процэса запрацоўкі дадзеных, ўкладання іх у часткі, стварэння эмбедынгаў, вікорыстоўвання базы дадзеных у вектарнай форме, процэса пошуку, стварэння запітоў, вызоваў большых мовных модэляў, API, тэстаў і логаў. Гэтага дастатнек для большасці праектаў для пачаткуючых і для стварэння портфеля. Дадатковыя элементы, такія як пераранжаванне, ацэнка, кэшаванне, аўтэнтыфікацыя, фонавыя задачы, версіяванне дакументаў і панелі керування, можна дадаць па меры патрабаванняў, пры чым базовая ідэя застаецца незменнай: пазбягвайце злучэнню працэса апрацоўкі дадзеных з самым прыемнікам. Маленькі прыём, які дапамагае, — гэта запісаць у файле README, якая папка будзе выкорыстоўвана для кожнага тыпу змян, калі ўжо ўскора не будзе ясна, куды прыступіць, адноўсількі структура, верагодна, пачала стаць нечытальнаю.
Дэманстрацыя паказвае, што ідэя можа працаваць. Праект, які легка адтульвацыя, паказвае, што сістэма можа растаць. Папкі самыя па сабе не ўсё — важліва ясная прызначэння кожнай адпаведальнасці.
Супаўзвязаныя матэрыялы
- Рабочы працэс локальнага агента LLM, створанага з папак і файлаў Markdown — Створыце багатаступеневую локальную схему агента, дзе кожны ступень — гэта папка, запыткі ў фармаце Markdown, якія можна рэдагаваць, а чалавек пераглядае рэзультаты пры выкананні дорогіх крокаў.
- Агентны RAG: Калі LLM сам выбірае, калі і дзе запрашваць інфармацыю — Чаму статычны RAG не функцыонуе па запыткам з калькамі і часовымі аспектамі, і як агент LangGraph пераводзіць даныя между Qdrant і веб-пошукам, з адпаведнымі мерамі захоплення і абмежэннямі.
- Spring AI ў падпрыемствах: Агент для падтрымкі з інструментамі, RAG і аналізам — Стварыце асістэнта для сортавання інцыдэтаў з Spring AI: запіты як контракты, структураваны выхід, вызов інструментаў, MCP, Qdrant RAG, можлівасці абзірнасці і обмежаныя цыклы агента.