RAG-асістэнт у Slack без асобнай вектарнай базы
RAG-асістэнт у Slack без асобнай вектарнай базы: дакументы ляжаць у адной табліцы StarRocks, пошук — звычайны SQL, а шэсць урокаў тычацца промптаў і разбіўкі.
Крыніцы: Lessons We Learned Building a RAG Assistant Without a Separate Vector Database
StarRocks — гэта шырока адаптаваныя аналітычная база дадзейнаў на адкрытым кодзе з адным двыжкам выканання, гнучкай установкай і высокай карыстнасцю пад рэальным навантажэннем. Чым больш растае яе спільнатэка, тым больша становіцца навантажэння на адпаведальных за ўтрыманне: яны продовжваюць отрымваць тыя ж запытанні пра дакументацыю, проблемы на GitHub, прыметкі да выданняў і старыя тэксты у Slack.
Rocky — гэта асістэнт у Slack, створаны для падтрымкі гэтага навантажэння. Яго задача проста: зняць з адпаведальных за ўтрыманне павтаральныя запытанні і адпаведныя адпаведзі, пры чым кожна адпаведзь павінна базавацца на дакументацыі праекту і супаўзяючых істочніках.
Інтэрэснае — гэта архітэктура. Rocky працуе на самай StarRocks. Фрагменты дакументацыі, пошук па ключоўых словах, адзыскванне вектараў і ацэнка сэроднечнасці — усе гэта знаходзіцца ў адной таблеце OLAP, таму асістэнт, які адпавядае на запытанні пра базу дадзейнаў, таксама працуе на той самай базе. Усё гэта складаецца з апошніх 600 ліній коду на Python, адной таблецы і кантролл-клуча Gemini API.
Фундамент RAG
Звычным першым крокам пад час стварэння сістэмы генеравання з падтрымкам збору інфармацыі ёсць вікорыстоўванне спецыяльнай вектарной базы дадзеных. Кожны дадзеный элемент прыносіць патрэбу ў стварэнні аднойчынага рэшэння, плану рэзервавання і саеў модэлю цэласнасці, і для невялікага бота-спільнатэка такіі надзейны расходы важка апрацаваць.
Таму усё засталося ў StarRocks. Фрагменты дакументаў і ўсё 768-вымерныя эмбеддынгі Gemini знаходзяцца ў звычнай OLAP-табліцы, якая выкарыстоўвае модэль PRIMARY KEY, а збір інфармацыі адбываецца за дапамогою SQL-запыту. Прынцып: калі ваша аналітычная база дадзеных вялікі час падтрымлівае столбцы-масівы і функцыі косай схожасці, вам не патрэбна другая сістэма дадзеных для вектарнага пошуку.
Табліца выглядае як будзь-яя іншая:
CREATE TABLE docs (
id BIGINT NOT NULL,
path VARCHAR(512),
`index` INT,
`text` STRING,
vector ARRAY<FLOAT> -- 768-dim Gemini embedding
) ENGINE = OLAP
PRIMARY KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
А адзын выкарыстоўваны спосаб выкарыстоўвання — запыт на адной засадзе косай сэмабліяванасці:
SELECT path, `index`, `text`,
approx_cosine_similarity([0.012, -0.034, ...], vector) AS similarity
FROM docs
ORDER BY similarity DESC
LIMIT 8;
Няма SDK, няма бібліятэкі для кліента і няма другой сістэмы. З баку Rocky „база дадзеных вектараў“ — гэта проста ўсё тая ж табелю, яю ён вядома можа запытваць.
З запуску ў Slack да адпаведнага адпаведзення
Працэс спецыяльна зроблена мінімальным. Прамянка @Rocky у Slack запускае бота, і бот перадае свае меркаванні Gemini 3 Flash за дапамою внутрашніх вызоў функцый. Мадэль выбірае, чы хацець шукаты ў дасяглівых матэрыялах чы запытваць Google, і за адну партыю можа адбыцца да дзесяці раундоў вызоў інструментаў.
- Slack адправляе прамянку. Бібліятэкі slack_bolt для Python прымеўае запуск праз Socket Mode і выявляе запытку карыстальніка разам з кантэкстам тэаду.
- Gemini аналізуе і выкарыстоўвае інструменты. Мадэль отрымае сістэмны прамт з строгімі правіламі чыстасці і два інструменты:
search_starrocks_doc, які відпаведае за пошук вектарных дадзеных на стороне кліента, іgoogle_search, які відпаведае за пошук на серверэ за дапамою вбудованага пошукавача Gemini. - Пошук адбываецца внутрь StarRocks. Інструмент пошуку включае запітацьце з параметрам gemini-embedding-001, выкарыстоўваючы значэнне task_type=RETRIEVAL_QUERY, а пасля запускае SQL-запіс на вычысленне косай схожасці, як было паказана раней.
- Мадэль пішае адпаведны адказ. Gemini складае атрыбуты, атрыманыя падчас пошуку, у адказ у фармаце Markdown, які пасля таго Rocky перакладае у фармат mrkdwn для Slack першым чым публікуваць яго.
- Даныя тэлеметрыі адправляюцца для аналізу. Кожны вызов мадэлі, выкарыстоўванне інструменту і пільгі токенав стаюць елементамі OpenTelemetry span, пры чым
thread_tsслужыць як ідэнтыфікатор сесіі.
Бот складаецца прыблызна з 600 ліній коду на Python у аднам файле. Дадатковыя засобы для разбівання на часткі, стварэння эмбедынгаў і падготовкі індэкса дадаюць ўсьмо тыя ж каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля каля ка
curl --location-trusted -u root: \
-H "format: json" \
-H "strip_outer_array: true" \
-T docs_with_embedding.json \
-XPUT http://host:8030/api/starsage/docs/_stream_load
Адзін HTTP-запрос завантажае або-альбо 5 000 рэядоў, кожны з якіх мае масів з 768 вектарных значэнняў. У такім масштабе метод Stream Load ідзе чырваё: не трэба падтрымваць брокера, не трэба запускаць Flink-пайплайны, не трэба налажваць інфраструктуру для постаўкі дадзенаў. Для тыяч або дзесяткаў тыяч рэядоў з вбудованымі вектарамі гэта простэйшы спосаб.
Модэль Primary Key гарантуюе атамарную замену дадзенаў. Запыты, якія прыходзяць падчас перзавантажэння, бачаць адносна стабільны фотакопію дадзенаў, а не часткова апцэнтаваны корпус. Гэта главная прычына выбору табелі з Primary Key заместо табелі з Duplicate Key. Оопыт не стосуецца апцэнтавання дадзенаў на рэядовым роўні, адколі навантажэння складаецца з цэлага перабудоввання дадзенаў. Справа ў тым, што табелі з Primary Key у поўнай зусіллі з Stream Load робяць кожнае перзавантажэння атамарным, таму Rocky выдае або стары корпус, або цэлы новы, але ніколі сумешанне якіх-небудзь з іх.
Адзысканне дадзенаў як інструмент, а не заполненне кантэксту
Дакументы не завантажваюцца загадзя у сістэму. search_starrocks_doc выступае як інструмент, які можна вызваць, і мадэль сама выбірае, калі і як часта ўжываць яго.
У практыцы паведамленне на кшталт „Прывіт, Rocky“ зовсім не спрыяе выкарыстоўванню дакументаў і коштуе майжэ нічога. Складнае запітанне, напрыклад, пра тое, як разлічаецца працэс GROUP_CONCAT у версіях 3.5 і 4.0, спрыяе кількам вызовам: мадэль запытвае кожную версію і парапрацоўвае рэзультаты. Колькасць вызоў на выкарыстоўвання дакументаў обмежваецца восьмю на раунд. Серверная частка Gemini, google_search, карыстаецца інформацыяй з анунсаў выхода новых версій і проблем у GitHub, якія не ўключаныя ў дакументацыю, а Rocky падтрымлівае шырокі спектр задач без додатковага механізма скрапінгу веб-стороніц.
Кэш умяшчэння для прыцягнучых костаў
У спялёчнай аудыяцыі часта задаюцься пытанняя. Як дадаць індэкс у віглядзе бітмапа і як запусканы ў Kubernetes — гэтыя пытанняя задаюцца колькі разоў на тыдзень. Местны кэш SQLite, які ключуецца за тэкстам запыткі і тыпам задачы (так як RETRIEVAL_QUERY і RETRIEVAL_DOCUMENT даюць разныя вектары), усуняе зайвыя вызовы функцыі embedding, таму популярныя запыткі не купуюць нічога.
Што стосуецца LLM, то токены вхідных дадзеных Gemini, якія зберагаюцца ў кэшы, купуюцца за 25 процэнтам ад стандартнай цены. За дапамогою кэша embedding, вартасць адпаведзення становіць апошней час $0.001, што ёсць адпрацоўваным показнікам для праекта, фінансаванага спялёчнай аудыяцыяй і які працюе без канца.
Шасць урокаў з рэальнага відвароту
Гэтыя урокі выніклі з рэальнага вжытку спялёчнай аудыяцыяй, а не з тэставанняў, і большасць з іх можна застосаваць да будзь-яга асістэнта на платформе чату.
1. Змены запыткі эфектывнейшыя для усунення галюцынацый, чым налагоджэнне процэса адзысквання дадзеных
У першыя версіі былі створаны вымышленыя назвы дзеркаў Red Hat, сфабрыкаваныя URL-адрэсы з прыміткамі па выданню і вымышленыя номеры версій, і члэны спяльноты пераходзілі на стораны 404. Для бота для запытань і адказоў абманчывае неправільнае адказа ўсё роўна горша, чым жаданага адказа няма.
Рашэннем не была крашчая выкарыстоўвання інформацыі. Гэта была зміна запыту: чыстае параграфа пра чыстасцю, які прыказваў модэлю не фабрыкаваць факты, а калі ён не можа надаць надзеяны адказ, то сказаць, што ён не ўпэўнены, паказаць автантыфікованыя джэрэлы і запропанаваць запытаць спяльноту. Эфект быў негайным. Выкарыстоўвання інформацыі мае значэнне, але інжынерыя запытоў часта ёсць шырэйшым і дэшавейшым спосабам падавілля вымыслу.
2. Вылечыце разбіянне на часткі прытаму, прычыму пры выкарыстоўванні інформацыі
У дакументах StarRocks ёсць многія рэядкі імпорту MDX, табліцы Markdown, якія распаўляюцца між часткамі, і дужа дзяўгія запэўненні SQL DDL. Падача такой неапранутай інформацыі ў алгорытмы эмбедынга дае вымышленыя рэзультаты. У працэс разбіяння на часткі былі вложаны значныя рэсурсы:
- Іерархічная разбівка па загалоўкі. Фрагменты дзеляцца на частыны па загалоўкіх Markdown, пры чым весь шлях да загалоўкі зберагаецца як метаданы, чым модэлю даёмацца уявленне пра тое, дзе фрагмент належыць у іерархіі дакумента.
- Фільтрацыя шуму. Радкі для імпорту MDX адсікаюцца, а блакі коду дужэй 4 000 сімвалаў выкарыстоўваюцца не ўсё, таму што яны паслабляюць семантычны сігнал без дапамогі у стварэнні адпаведных адказаў.
- Простыя таблыцы выходных даных. Прыклад выходных даных, паказаны ў фармаце таблыц MySQL, спростоўваецца да структураваных апісанняў замест адкрытых ASCII-таблыц.
Разбівка тэксту на фрагменты займала прыблізна у дзесяць разоў больш часу, чым інтэграцыя вектарнага пошуку. Калі рэзультаты пошуку не задовольняюць, пераканайцеся, як тэкст быў разбіты на фрагменты, перш чым мяняць мадэлі эмбедынга або налагоджваць настройкі індэкса.
3. RAG — гэта інструмент, а не цэлы працэс
Калі запрашэнне дадзенняў адбываецца праз вызов функцыі, а не ўскладнены кожнага запіту, мадэль можа яго прыхіліць, калі ён не патрэбны. Прывітанні, дапаможныя запытанні і простыя фактычныя запытанні ніколі не зачыпаюць вектарны індэкс. Аднаўленне інструмента з боку кліента (search_starrocks_doc) і інструмента з боку сервера (google_search), што ўсё падтрымліваеся SDK Gemini з версіі 1.70, таксама дае Rocky альтэрнатыву для контэнту, які не ўходзіць у корпус, без неабяжных скрапераў.
4. Дазвольце платформе кераваць модэлем сесіі
Тэксты у Slack маюць ідэнтыфікаторы thread_ts. Rocky выкарыстоўвае іх безпасова як ID сесіі, зберагаючы історыю ў файле JSON пад тым жа іменем і включаючы шасць найняўлеглейшых запытань у кожны запіт:
contents = []
for turn in history[-MAX_CONTEXT_MESSAGES:]:
contents.append({"role": turn["role"], "parts": [{"text": turn["text"]}]})
contents.append({"role": "user", "parts": [{"text": query}]})
Гэта прыблымо дваццац ліній коду, што заўсёды простае, чым адкрыць базу дадзеных сэсіі.
У адзіных з паспамянаў корыстнікаў пішацца, што "Rocky не мае памяці". Гэта вынікае з таго, што ў Slack Rocky адпавядае толькі тады, калі яго паміняюць праз @, таму здаецца, што ён забывчык. Гэта проблема варабочай взаімадзеі з продуктам, а не тэхнічная.
5. Ранае включэнне інструментаў
Пасля запуску каманда хацела адказы на базовыя запытанні. Сколькі запытанняў Роккі разглядаў гэты тыдзень? Дзе концентраваліся вызывы інструментаў па тэмам? Колькі коштаў тыпавы адказ? Калкія адказы былі адкладзены, парадроблены чы ўзнавлены? Без следаў сістэма была «чорным яшчыкам», таму пазней быў даданы цэлы шар OpenTelemetry. Урок заключаецца ў тым, каб пры запуске адразу дадаць можлівасць трэйсінгу. Без яго вікорыстоўванне токенаў, косцы чы патэрны вызываў інструментаў застаюцца невідомымі, а оптымізацыя косцоў, атрыбуцыя галюцынацый чы аналіз слабых запытанняў стаюць набліжна сложнейшымі.
6. Актыўна выкарыстоўваць кэш і сочыць за кожным доларам
API-яўкі для вбудованага вікорыстоўвання выглядаюць недорогімі за адну вызов, але частыя запытаннія ад спяльнай аўдыторыя быстра накапліваюцца. Кэш для вбудованага вікорыстоўвання SQLite разам з механізамам цэнавання кэшаваных токенав у Gemini дазволяюць утрымваць аблікватныя віткі прыблізна на рэвэле $0.001 за адказ. Для праекта, які падтрымваецца спяльнай аўдыторыяю, гэта разліка межа «выконванняя без канца» і «нам патрэбна беседа пра бюджэт».
Чыстыя компрэсіі
Rocky — гэта лёгкі асістэнт на адной базе дадзеных RAG, а не сістэма падтрымкі у працэўнай сэрэўэры, і існуюць калькі обмежэнняў, якія варта адзначыць:
- Галюцінацыі зменшаюцца, але не зніклі. Захоўнікі запытанняў дапамагаюць, але не гарантуюць правільнасці. Найкрашча працуюць добра задокументаваныя тэмы, тады як крайнія кейсы і особлівасці конкрэтных версіяў все ўсё ствараюць проблемы.
- Довгія адказы. Як і большасць ботаў на адной базе дадзеных RAG, ён занадта дакладна адпавядае. Плануецца можлівасць выбору між короткімі адказамі у фармаце апісання.
Усе гэтыя пункты ўключаныя ў актуальную стратэгію развіцця на адной з асноваў даўачых адгукоў ад спяльнайя.
Што паказвае Rocky
Rocky — гэта невяленькі праект: 600 ліній коду на Python, адзін таблыц і адзін ключ API, але ён падтрымлівае болей большыя задачы. Для лёгкіх AI-застосоўкаў аналітычная база дадзенаў з столбцамі-масавымі і функцыямі сэмабільнасці можа падменіць сабой аддзельную вектарную базу дадзенаў. Адзін таблыц выконвае функціі зберагчэння дакументаў, пошуку ключоўых слоў і выкарыстоўвання вектараў, таму канфігурацыя застаецца простай, і трэба прыглядацься толькі да аднаго сістэмы, а не да двух.
Бот ўсё дзень разваротвае рэальны трафік спільнатыпу, і, як паведамляе сам праект, корыстувачы усерэднавана ставяюць яму 4,2 з 5 балоў; кожна адпаведзь коштае менш чым дзесятая частка цэнты. Для тых, хто стварае падобнага доменовага асістэнта і хочаць простай інфраструктуры, мадэль з аднай таблыцай варта ўвагі, особліва калі ў сістэме ужо ёсць StarRocks.
Супаўзвязаныя матэрыялы
- Гібрыдныя пошукавыя методы ў Postgres для дапаможніка з пытаннямі здароў’я: схема, часткаванне, RRF і ацэнка — Стварэнне слоя адзысквання даных для дапаможніка з пытаннямі здароў’я ў Postgres: часткаванне з урахоўваннем структуры, гібрыдны пошук з pgvector і RRF, бінарная квантызацыя, прагі і ацэнка рэзультатаў.
- Spring AI Recipe: Фільтрацыя рэзультатаў RAG з метаданымі па назве гэму — Практычны падход Spring AI, які прызначае тэгі дакументам па назве, выкарыстоўвае LLM для выбору правільнага значэння з пытання і фільтруе рэзультаты вектарнага пошуку.