The post has been translated automatically. Original language: Russian
Let's assume that you have a task to write an AI chatbot for an insurance company that would answer user questions for you such as “ How much will my health insurance cost for this year?”
You have PDF documents describing insurance conditions, a primitive knowledge base (text or a doc file with a question and answer), and your system has a program code that implements data extraction functions from the database.
How to implement it?
The general scheme is a RAG agent with tools (tool-calling).
A few terms
1. Prompt is the input text that you feed to the language model to get an answer.
2. An LLM agent is a system in which the language model is used not for a one-time response, but as a controlling "brain" that decides in a cycle what actions to take to achieve a goal, and chooses the next step based on the results of previous ones.
The key difference from a regular LLM call is that a simple call is "input → response". The agent is a cycle: the model reasons, calls the tool, gets the result, reasons again, and so on until the problem is solved.
What does the agent consist of?
- LLM is like an orchestrator — it reasons and decides what to do next.
- Tools are functions that the model can use in the outside world: database query, search, API call, calculation. The model chooses which tool to call (function calling) and with which arguments.
- Memory/context — the history of the dialogue, intermediate results, and sometimes long-term storage.
- The control cycle — the model repeats "think → act → observe" until the task is completed or the step limit is reached.
3. RAG (Retrieval-Augmented Generation) is an approach in which LLM first finds relevant information in an external source before responding, and then generates an answer based on it, rather than relying solely on knowledge "hardwired" into its weights during training. Simply put: instead of asking the model "what do you know?", you first put the necessary documents in context for her, and then ask her to answer based on them. Documents are pre-split into fragments (chunks), turned into so-called embeddings and folded into a vector database.
4. Embeddings - numerical vector representation of text. The number of digits (dimension), for example 1536 for OpenAI text-embedding-3-small, or 1024/384 for some open models.
Top-level architecture
User → API/Chat → Orchestrator (LLM agent)
├── RAG (knowledge base, documents)
└── Tools (your code: calculation, selection from the database)
→ Answer + sourcesThe key idea: The LLM decides for itself what is needed for the answer — to find the text in the documents (RAG) or to call the calculation function (tool). The question "how much will my insurance cost" is almost always → calling the calculation function, not RAG, because the answer depends on the client's personal data, not on the text of the documents.
Request routing
The LLM agent (via function calling) classifies the intent:
- Factual/background question ("what does Policy X cover", "how to apply") → RAG based on the knowledge base from the documents.
- Personal calculation/data ("how much does MY insurance cost", "when does my policy expire") → calling your extraction functions from the database + tariff calculator.
- Mixed → both, then synthesize the response.
RAG layer (for reference questions)
- Indexing: documents → chunks → embeddings → vector database (pgvector, Qdrant, Weaviate).
- To the query: embedding the question → search for relevant chunks (you can use a hybrid: vector + BM25) → re-ranking.
- The found context is submitted to the LLM prompt with instructions to respond only based on it and quote the source.
Tool layer (existing code)
You're wrapping the extraction and calculation functions in tools with a JSON schema.:
get_policy(user_id) → policy
data calculate_premium(user_id, year, plan, ...) → cost
get_coverage_details(policy_id) → coverage detailsLLM receives descriptions of these functions, selects the necessary one, forms arguments, executes the backend, and returns the result to the model to formulate the answer in natural language.
Response flow
"How much will my health insurance cost for this year?"
- Authenticating the user → getting a verified user_id.
- LLM recognizes intent → calls calculate_premium(user_id, year=2026, ...).
- If there are not enough parameters (type of plan, family composition), the model asks a clarifying question.
- The code returns the amount + the breakdown.
- LLM formulates the answer in human language.
Stack (for RubyOnRails/RubyLLM)
RubyLLM fits this scheme well: tool-calling and embeddings are supported natively, Postgresql pgvector + neighbor is for RAG, Sidekiq/Falcon is for async -heavy query processing and indexing.
Предположим, что у вас есть задача написать AI chat-бот для страховой компании, который бы за вас отвечал бы на вопросы пользователей такие как “ Сколько будет стоить моя медицинская страховка на этот год”.
У вас есть PDF документы с описанием условий страхования, примитивная база знаний (текст или doc-файл с вопрос - ответ) и в вашей системе есть программный код, реализующий функции извлечения данных из БД.
Как это реализовать?
Общая схема — это RAG-агент с инструментами (tool-calling).
Немного терминов
1. Промпт (prompt) — это входной текст, который ты подаёшь языковой модели, чтобы получить ответ.
2. LLM-агент — это система, в которой языковая модель используется не для одноразового ответа, а как управляющий «мозг», который в цикле принимает решения, какие действия предпринять для достижения цели, и сама выбирает следующий шаг на основе результатов предыдущих.
Ключевое отличие от обычного вызова LLM: простой вызов — это «вход → ответ». Агент — это цикл: модель рассуждает, вызывает инструмент, получает результат, снова рассуждает, и так до решения задачи.
Из чего состоит агент
- LLM как оркестратор — рассуждает и решает, что делать дальше.
- Инструменты (tools) — функции, которыми модель может действовать во внешнем мире: запрос к БД, поиск, вызов API, расчёт. Модель сама выбирает, какой инструмент и с какими аргументами вызвать (function calling).
- Память/контекст — история диалога, промежуточные результаты, иногда долговременное хранилище.
- Цикл управления — модель повторяет «подумать → действовать → наблюдать», пока задача не решена или не достигнут лимит шагов.
3. RAG (Retrieval-Augmented Generation) это подход, при котором LLM перед ответом сначала находит релевантную информацию во внешнем источнике, а затем генерирует ответ на её основе, а не опирается только на знания, «зашитые» в её веса при обучении. Проще говоря: вместо того чтобы спрашивать модель «что ты знаешь?», ты сначала подкладываешь ей нужные документы в контекст, а потом просишь ответить, опираясь на них. Документы заранее разбиваются на фрагменты (чанки), превращаются в так называемые эмбеддинги и складываются в векторную БД.
4. Embeddings (эмбеддинги) — числовое векторное представление текста. Число цифр (размерность), например 1536 для OpenAI text-embedding-3-small, или 1024/384 для некоторых открытых моделей.
Архитектура верхнего уровня
Пользователь → API/чат → Оркестратор (LLM-агент)
├── RAG (база знаний, документы)
└── Tools (твой код: расчёт, выборка из БД)
→ Ответ + источникиКлючевая идея: LLM сам решает, что нужно для ответа — найти текст в документах (RAG) или вызвать функцию расчёта (tool). Вопрос «сколько будет стоить моя страховка» почти всегда → вызов функции расчёта, а не RAG, потому что ответ зависит от персональных данных клиента, а не от текста документов.
Маршрутизация запроса
LLM-агент (через function calling) классифицирует намерение:
- Фактический/справочный вопрос («что покрывает полис X», «как подать заявку») → RAG по базе знаний из документов.
- Персональный расчёт/данные («сколько стоит МОЯ страховка», «когда истекает мой полис») → вызов твоих функций извлечения из БД + калькулятор тарифа.
- Смешанный → и то, и другое, затем синтез ответа.
RAG-слой (для справочных вопросов)
- Индексация: документы → чанки → эмбеддинги → векторная БД (pgvector, Qdrant, Weaviate).
- На запрос: эмбеддинг вопроса → поиск релевантных чанков (можно гибрид: вектор + BM25) → реранкинг.
- Найденный контекст подаётся в промпт LLM с инструкцией отвечать только на его основе и цитировать источник.
Tool-слой (существующий код)
Оборачиваешь функции извлечения из БД и расчёта в инструменты с JSON-схемой:
get_policy(user_id) → данные полиса
calculate_premium(user_id, year, plan, ...) → стоимость
get_coverage_details(policy_id) → детали покрытияLLM получает описания этих функций, выбирает нужную, формирует аргументы, бэкенд исполняет, результат возвращается в модель для формулировки ответа на естественном языке.
Поток ответа
«Сколько будет стоить моя медицинская страховка на этот год?»
- Аутентифицируем пользователя → получаем верифицированный user_id.
- LLM распознаёт намерение → вызывает calculate_premium(user_id, year=2026, ...).
- Если не хватает параметров (тип плана, состав семьи) — модель задаёт уточняющий вопрос.
- Код возвращает сумму + разбивку.
- LLM формулирует ответ человеческим языком.
Стек (для RubyOnRails/RubyLLM)
RubyLLM хорошо ложится на эту схему: tool-calling и эмбеддинги поддерживаются нативно, Postgresql pgvector + neighbor — для RAG, Sidekiq/Falcon — для async-обработки тяжёлых запросов и индексации.