Мало просто иметь доступ к мощным нейросетям и уметь ими пользоваться — важно правильно подключить их к своим сервисам, комбинировать с внутренними данными и выстраивать удобные сценарии работы. В нашей команде мы постоянно экспериментируем с новыми AI-технологиям, поэтому сегодня расскажу вам, как же просто можно внедрить нейронку в свой проект (например, на сайт).
Для этого воспользуемся сервисом Evolution Foundation Models и рассмотрим фреймворки LangChain, LlamaIndex, CrewAI и Semantic Kernel. Сервис предоставляет доступ к open source моделям по open AI Compatible API. Касаемо фреймворков — каждый из них по-своему упрощает разработку, но имеет уникальные паттерны подключения. В статье я покажу готовые примеры и поясню ключевые части кода. А все ссылки на мои полные решения даю в конце статьи.

Подготовка: доступ к Foundation Models
На платформе Cloud.ru Evolution вам нужно зайти в раздел Пользователи > Сервисные аккаунты > Создать сервисный аккаунт. Затем заполните все требуемые поля и нажмите Создать — подробная инструкция.

Зайдите в созданный аккаунт, перейдите во вкладку API-ключи, нажмите Создать API-ключи, заполните все поля и нажмите Создать — создание API-ключа.
BASE_URL для подключения к API будет таким: https://foundation-models.api.cloud.ru/v1
Сохраните параметры: API_KEY и BASE_URL.
Давайте поговорим про выбор модели, ведь взять первую попавшуюся не получится.
Мой выбор для каждой задачи
Для чат-бота на LangChain и RAG-системы на LlamaIndex лучше выбрать модель с небольшим контекстным окном, с высокой скоростью генерации и сравнительно небольшим количеством токенов. В первом случае нам не придется хранить длинную историю диалога, чтобы модель понимала контекст, а во втором — модель нужна только для генерации ответа, сам поиск делает эмбеддинг-модель. Поэтому для этих задач я взял RuadaptQwen2.5-7B-Lite-Beta — компактная, быстрая и отзывчивая модель, хорошо подходящая для такой задачи.
Для мультиагентных систем на CrewAI и Semantic Kernel важна модель с хорошими возможностями рассуждения и планирования, а также способностью работать с большим контекстом, чтобы сохранять состояние и координировать агентов. Оптимальный выбор здесь — RuadaptQwen2.5-32B-Pro-BetaRefalMachine, она сбалансирована по мощности, позволяет работать с большим контекстом и хорошо справляется с многошаговыми задачами.
Выбрав модель, нажимаем на значок сверху справа, копируем корректное название для работы через API.

Теперь создайте файл окружения .env в директории вашего проекта, вставьте туда эти три параметра под соответствующими переменными. В будущем будем подключать их оттуда. Так и код красивее, и безопаснее (если планируете выгружать его на гитхаб). Так же добавим туда и используемые параметры (temperature, max_tokens).
LangChain: модульная архитектура и память диалога
LangChain — это такой универсальный «конструктор», который помогает собирать умные пайплайны на основе больших языковых моделей. В основе его философии лежит идея цепочек (chains): каждый шаг — генерация текста, анализ или извлечение данных — можно соединить в логичный процесс, где результат одного этапа становится входом для следующего. Это как выстроить маршрут, по которому идет твой запрос, пока не превратится в полезный и осмысленный ответ.
Помимо этого, у LangChain есть готовые инструменты для «памяти»: он умеет хранить историю диалога, помнить, о чем говорилось раньше, и использовать это для более естественной беседы. Например, модули ConversationBufferMemory или SummaryMemory позволяют разным способами учитывать контекст — от простого хранения последних сообщений до умного создания краткого резюме.
Забавно, что мой первый эксперимент с LangChain был простым: я решил собрать чат-бота с пониманием контекста. Оказалось, что это действительно элементарно, потому что все уже предусмотрено в самом фреймворке: от встроенной памяти до интеграции с моделями и инструментами.
Обзор решения
Разберу первый фреймворк довольно подробно, чтобы были понятны общие моменты по использованию стороннего API. Затем же буду разбирать только самое важное.
Код пишем на питоне, тут ничего нового. Велосипед не изобретаем, пишем на чем нам удобно.
Управление конфигурацией через переменные окружения:
load_dotenv()
API_KEY = os.getenv("API_KEY")
BASE_URL = os.getenv("BASE_URL")
MODEL_NAME = os.getenv("MODEL_NAME")
TEMPERATURE=os.getenv(“TEMPERATURE”)
MAX_TOKENS=os.getenv(“MAX_TOKENS”)
Сначала подгружаются наши sensitive-переменные из окружения .env.
llm = ChatOpenAI(
openai_api_key=API_KEY,
model_name=MODEL_NAME,
openai_api_base=BASE_URL,
temperature=TEMPERATURE,
max_tokens=MAX_TOKENS
)
Затем происходит инициализация языковой модели через LangChain с указанием этих параметров и других дополнительных (температура, штрафы за повторения, штрафы за использование распространенных слов и другие). Инициализируем LLM через ChatOpenAI(), чтобы была возможность использовать встроенный в langchain функционал (промпты, память, цепочки).
Системный промпт – ключевой элемент работы нейросети: правильно сформулированная инструкция обеспечивает надежное взаимодействие и позволяет достичь максимальной эффективности и комфорта в совместной работе.
“Вы — надежный и точный AI-ассистент, который опирается исключительно на информацию из пользовательского ввода и диалоговой памяти, не добавляя ничего лишнего. Ваши ответы должны основываться на известных фактах, быть лаконичными и правдивыми: при недостатке контекста запрашивайте уточнения, но ни в коем случае не придумывайте цитаты, данные или источники. Структурируйте текст понятно и логично, используя маркированные списки для перечислений и выделяя основные идеи при подведении итогов. Соблюдайте конфиденциальность и безопасность: никогда не раскрывайте приватные данные из памяти и отказывайтесь от запросов, нарушающих правила.”
ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(
k=8,
memory_key="chat_history",
return_messages=True
)
Этот компонент реализует sliding window подход к управлению памятью:
k=8: Хранит только последние 8 сообщений (4 пары вопрос-ответ)
memory_key: Определяет ключ для передачи истории в промпт-шаблон
return_messages=True: Возвращает структурированные объекты сообщений с ролями
Такая схема позволяет поддерживать контекст разговора без превышения лимита по токенам.
ChatPromptTemplate
prompt_template = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(system_prompt),
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{user_input}")
])
Шаблон промпта состоит из трех компонентов:
SystemMessagePromptTemplate: Системные инструкции для модели
MessagesPlaceholder: Динамически подставляемая история сообщений
HumanMessagePromptTemplate: Шаблон для пользовательского ввода
Такая структура обеспечивает правильное форматирование контекста для современных чат-моделей.
ConversationChain
conversation = ConversationChain(
llm=llm,
memory=memory,
prompt=prompt_template,
input_key="user_input",
verbose=False
)
ConversationChain объединяет все компоненты в единый исполняемый граф:
Принимает пользовательский ввод.
Загружает контекст из памяти.
Форматирует промпт согласно шаблону.
Выполняет запрос к модели.
Сохраняет результат в память.
Параметр input_key обеспечивает корректное сопоставление переменных в шаблоне.

LlamaIndex: индексирование и семантический поиск
LlamaIndex решает задачу организации Retrieval-Augmented Generation — другими словами, учит языковые модели работать не «в вакууме», а в связке с инструментами поиска по большим массивам документов. В основе фреймворка лежит механизм создания векторных индексов практически из любых источников: текстовых файлов, баз данных, PDF-документов и многого другого. Система автоматически делит данные на удобные фрагменты, строит индекс и затем предоставляет простой интерфейс для семантического поиска. Когда пользователь задает вопрос, запрос дополняется релевантными кусками документов, и LLM формирует ответ уже на основе проверенного контента. Такой подход отлично подходит для корпоративных вики, юридических архивов и любых сценариев, где важно не просто «ответить красиво», а дать максимально точный ответ на основе реальных знаний компании.
Инициализация клиента самая обычная:
client = OpenAI(
api_key=API_KEY,
base_url=BASE_URL
)
Итак, у нас есть несколько функций, и вместе они собираются в типичный RAG-процесс. Вот такой план действий:
загрузить документы;
превратить их в вектора (чтобы компьютер мог их сравнивать);
искать подходящие куски текста под вопрос;
и скормить это языковой модели для ответа.
Теперь давайте разберем каждую функцию.
У нас определена папка data, откуда берутся текстовые файлы. Функция load_files_from_dir
просто пробегает по этой папке, читает все .txt и возвращает их содержимое списком.
def load_files_from_dir(dir_path=DATA_DIR):
texts = []
for file_path in Path(dir_path).glob("*.txt"):
with open(file_path, encoding="utf-8", errors=”replace”) as f:
texts.append(f.read())
return texts
Стоит отметить разве что параметр errors="replace" — он нужен, чтобы программа не падала на «битых» символах, просто заменяет их безопасным вариантом. В итоге имеем список документов.
def create_faiss_index(documents):
print("Создаем эмбеддинги...")
embeddings = embedding_model.encode(documents, convert_to_numpy=True)
embeddings = embeddings.astype("float32")
print("Создаем FAISS индекс...")
dim = embeddings.shape[1]
index = faiss.IndexFlatL2(dim)
index.add(embeddings)
return index
Ключевой момент тут — превращение текста в векторы через embedding_model.encode. Каждый документ становится точкой в многомерном пространстве.
faiss.IndexFlatL2(dim) — это простой индекс, который считает евклидово расстояние между векторами (то самое «чем ближе, тем похожее»).
def search_documents(query, index, documents, top_k=3):
query_emb = embedding_model.encode([query], convert_to_numpy=True).astype("float32")
distances, indices = index.search(query_emb, top_k)
return [documents[i] for i in indices[0] if i < len(documents)]
def generate_answer(query, context_docs):
context_text = "\n\n".join(context_docs)
prompt = (
"Контекст:\n"
f"{context_text}\n\n"
f"Вопрос: {query}\n\n"
"Отвечай только на основе контекста."
)
Запрос мы кодируем тем же способом, что и документы (embedding_model.encode([query])), иначе сравнивать смыслы не получится.
index.search(query_emb, top_k) вернет ближайшие документы, и мы вытаскиваем их из исходного списка.
И наконец, вызов метода API:
response = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": "Ты помощник, который отвечает по данным из контекста."},
{"role": "user", "content": prompt}
],
max_tokens=MAX_TOKENS,
temperature=TEMPERATURE,
)
И в ответ получаем наиболее подходящий фрагмент текста. Это и есть наша «умная выдача».


Действительно, точь-в-точь все взято из контекста, ничего не придумано. Если хотите, можете поиграться с системным промптом. Например, можно указать, чтобы модель сама генерировала ответ на основе данных из базы знаний. Это будет полезно, если хотите получить нормально сформулированный ответ на свой вопрос, без подтягивания лишнего контекста.
CrewAI vs Semantic Kernel: мультиагентные системы
В моем случае оба фреймворка решают схожую задачу — организацию взаимодействия нескольких агентов, но делают это по-разному.
CrewAI: простота и декларативность
CrewAI делает ставку на ясность и структурность. Здесь мультиагентный сценарий описывается декларативно: каждому агенту задается роль, цель и предыстория (backstory), а дальше задачи распределяются внутри «экипажа» (crew). Фреймворк сам управляет порядком их выполнения и передачей контекста между агентами. Получается модель, где, например, один агент «Исследователь» копается в данных, второй — «Технический писатель» готовит на основе анализа отчет, а третий проверяет все на ошибки. Такой способ отлично работает для имитации бизнес‑процессов с четким разделением обязанностей.
llm = LLM(
model=f"openai/{MODEL}",
api_key=API_KEY,
base_url=BASE_URL,
temperature=TEMPERATURE,
max_tokens=MAX_TOKENS
)
Инициализация отличается от других фреймворков. Используем встроенный LLM-класс, при этом название передаем с префиксом провайдера (openai/), так требует CrewAI.
researcher = Agent(
role="Исследователь",
goal="Провести структурированный анализ применения технологий искусственного интеллекта в российском бизнесе за период 2023–2025 года.",
backstory="""
Эксперт-аналитик с опытом работы в сфере корпоративных инноваций и ИИ. Отличается глубоким пониманием рынков, умеет критически оценивать тренды и проверять источники. Всегда выделяет главное и избегает предположений без фактов.
""",
llm=llm,
verbose=True
)
writer = Agent(
role="Технический писатель",
goal="""
Создать краткий, структурированный и деловой отчет на основе полученного анализа;
акцент на ясность, практические рекомендации и бизнес-ориентированность.
""",
backstory="""
Профессиональный технический писатель с опытом подготовки деловых обзоров и рекомендаций для руководителей.
Отличается умением переводить сложные выводы на простой, понятный для бизнес-аудитории язык без потери точности.
""",
llm=llm,
verbose=True
)
Здесь инициализируются агенты, каждому выдается role и goal. В backstory описывается, кто они такие и для чего они предназначены. Параметр verboose включает вывод в консоль подробного отчета о работе агентов (увидите это на скринах).
research_task = Task(
description="""
Собери актуальный аналитический обзор по теме: «Применение ИИ в российском бизнесе в 2023–2025».
Требуется описать:
— главные направления использования (3-4);
— ключевые компании и конкретные решения (3-4);
— основные тренды и вызовы (3-4).
Оформи анализ как структурированный список с пояснениями. Ответ до 400 слов
""",
agent=researcher,
expected_output="""
Аналитический отчет, включающий список направлений применения, перечень компаний с их решениями и описание
3–5 актуальных трендов, с четкими ссылками на факты или источники.
"""
)
writing_task = Task(
description="""
На основе аналитического обзора подготовь краткий бизнес-отчет:
— резюме (2–3 предложения);
— 3–4 основных вывода (маркированный список);
— 2–3 рекомендации для бизнеса (маркированный список).
Должен быть структурирован, лаконичен (150–200 слов), без лишних деталей.
""",
agent=writer,
expected_output="""
Компактный, структурированный отчет для бизнес-аудитории: резюме темы, основные выводы по анализу, практические рекомендации.
""",
context=[research_task]
)
С постановкой задач думаю все понятно, долго останавливаться тут не будем. Хочется только отметить, что помимо всего в переменную context второго агента дополнительно передается результат работы первого агента. По его проделанной работе тех. писатель будет составлять итоговый отчет.
team = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
verbose=True
)
Crew объединяет агентов и задачи и координирует их выполнение, передавая контекст между задачами.

Результат работы первого агента тоже выводится, но слишком он уж громоздкий. Кому интересно, все материалы будут выложены на гитхаб (ссылка в конце статьи).
Semantic Kernel: гибкость и расширяемость
Semantic Kernel от Microsoft — это уже больше про инженерный фундамент. Он объединяет функционал для чат-ботов, RAG‑систем и мультиагентных сценариев, предлагая ядро (Kernel), в котором можно регистрировать сервисы и создавать агентов через ChatCompletionAgent. Важное отличие — он сразу проектировался под асинхронный мир: операции выполняются неблокирующе, с поддержкой телеметрии и хорошей интеграцией в экосистемы .NET и Python. Если нужно сохранять историю диалога, делать семантический поиск, строить сложные последовательности действий — все это можно собрать прямо внутри. Агентов тут можно объединять в рамках одного асинхронного процесса, что делает платформу гибкой и удобной для прототипирования и параллельных задач.
Сначала все так же: переменные окружения и класс-обертка. Для чего же обертка?
В Semantic Kernel немного иначе устроена работа с моделями — там все проходит через специальные «переводчики» (адаптеры), которые помогают связать внутренние данные с нужным API. Поэтому нельзя просто взять и подать клиент OpenAI напрямую, нужна небольшая обертка. Это делается для удобства и чтобы модель всегда правильно понимала, что ей говорить и как вести себя в разговоре. В других фреймворках вроде langchain или llamaindex все проще — они сразу общаются с клиентом без таких «переводчиков».
class AgentService(ChatCompletionClientBase):
def init(self, service_id: str):
super().__init__(service_id=service_id, ai_model_id=MODEL)
self._client = OpenAI(
api_key=API_KEY,
base_url=BASE_URL
)
self._model = MODEL
Наследуется ChatCompletionClientBase, передавая service_id и идентификатор модели. Создается экземпляр OpenAI-клиента с ключом и URL.
async def get_chat_message_contents(self, chat_history: ChatHistory, settings: PromptExecutionSettings, **_):
messages = [{"role": m.role.value, "content": str(m.content)} for m in chat_history.messages]
response = await asyncio.to_thread(
self._client.chat.completions.create,
model=self._model,
messages=messages,
temperature=TEMPERATURE,
max_tokens=MAX_TOKENS
)
return [ChatMessageContent(role="assistant", content=response.choices[0].message.content)]
В semantic kernel используем асинхронный метод отправки и получения сообщений. История чата преобразуется в список словарей с полями role и content.
Вызывается синхронный метод self._client.chat.completions.create. Чтобы не блокировать асинхронный цикл asyncio во время длительного сетевого запроса, этот метод запускается в отдельном потоке через await asyncio.to_thread(...). Это позволяет интегрировать синхронную функцию в асинхронный код, не замедляя выполнение других задач.
И последний блок main. Здесь создается ядро Kernel и регистрируется сервис.
async def main():
kernel = Kernel()
svc = AgentService("cloudru")
kernel.add_service(svc)
researcher = ChatCompletionAgent(
service=svc,
kernel=kernel,
name="researcher",
instructions="""
Роль: Исследователь.
Цель: Провести структурированный анализ применения ИИ в российском бизнесе (2023–2025).
Стиль: Кратко, с фактами и ссылками, в виде списка направлений, компаний и трендов.
"""
)
writer = ChatCompletionAgent(
service=svc,
kernel=kernel,
name="writer",
instructions="""
Роль: Технический писатель.
Цель: На основе анализа подготовить деловой отчет (150–200 слов):
1) резюме (2–3 предложения);
2) 3–4 вывода;
3) 2–3 рекомендации.
Стиль: Лаконично, понятно для руководителей.
"""
)
print("Исследователь начал работу...")
research = ""
async for step in researcher.invoke(
"""
Собери актуальный аналитический обзор по теме: «Применение ИИ в российском бизнесе в 2023–2025».
Требуется описать:
— главные направления использования (3-4);
— ключевые компании и конкретные решения (3-4);
— основные тренды и вызовы (3-4).
Оформи анализ как структурированный список с пояснениями. Ответ до 400 слов
"""
):
research = step.message.content
print("\nОтвет исследователя:\n", research)
print("\nПисатель начал работу...")
report = ""
async for step in writer.invoke(
f'Используя этот текст:\n{research}\n'
"""
Подготовь краткий бизнес-отчет:
— резюме (2–3 предложения);
— 3–4 основных вывода (маркированный список);
— 2–3 рекомендации для бизнеса (маркированный список).
Должен быть структурирован, лаконичен (150–200 слов), без лишних деталей.
"""
):
report = step.message.content
print("\n Отчет:\n", report)
Определяются два агента: «researcher» и «writer» с инструкциями (роли, цели и стиль). С помощью асинхронных итераторов invoke каждый агент последовательно выполняет свою задачу и печатает результаты.
Что лучше
Оба решения ведут к одной цели — распределенному управлению процессом анализа и генерации текста силами нескольких агентов, — но философия у них разная.
CrewAI ближе к объектно‑ориентированному стилю: агенты, задачи и «экипаж» описаны как классы с четким разделением ролей и синхронными вызовами через метод call. Такой подход помогает структурировать проект и легко масштабировать его на большие команды агентов.
Semantic Kernel, наоборот, держится асинхронного и скорее функционального подхода: взаимодействия организуются через корутины и неблокирующие вызовы, агенты общаются прямо в теле основной асинхронной функции, а результат может передаваться «ручным» способом — как часть очередного запроса. Это делает платформу более гибкой для быстрых экспериментов.
Итог: CrewAI — это про простоту, декларативность и «правильное» ООП‑структурирование, а Semantic Kernel — про асинхронный масштаб, расширяемость и скорость прототипирования. В зависимости от задачи можно выбрать подход, который больше подходит: строгую модель управления «экипажем» или свободную архитектуру асинхронных агентов.
Все обещанные скриншоты с результатами на гитхабе.
Выводы и рекомендации
После тестирования всех четырех фреймворков я получил четкое понимание их сильных и слабых сторон.
LangChain оказался самым универсальным решением для быстрого прототипирования. Его модульная архитектура позволяет легко экспериментировать с различными подходами: от простых чат-ботов до сложных цепочек рассуждений и RAG-сценариев с подключением внешних источников данных. Богатая экосистема интеграций и активное сообщество делают его идеальным выбором для стартапов и MVP‑проектов, где важна скорость разработки и гибкость архитектуры.
LlamaIndex показал себя как безусловный лидер в области RAG‑систем. Фреймворк берет на себя всю сложность индексирования документов, векторного поиска и оптимизации контекста. Это критически важно для корпоративных сценариев, где нужно работать с большими объемами документации, базами знаний или техническими спецификациями. Простота API позволяет буквально за несколько строк кода создать полноценную систему поиска по корпоративным данным.
Могу предложить вариант еще проще. Так, если вам нужна rag-система по определенной базе знаний, то лучше воспользуйтесь сервисами Cloud.ru Evolution Managed Rag и Object Storage для хранения данных различных форматов. Там это все можно настроить буквально за несколько минут (инструкции прикреплю ниже).
CrewAI продемонстрировал уникальный подход к мультиагентным системам через декларативное описание ролей и задач. Фреймворк особенно эффективен для моделирования реальных бизнес‑процессов, где разные «специалисты-агенты» последовательно работают над задачей. Автоматическое управление контекстом между агентами существенно упрощает разработку сложных пайплайнов обработки информации.
Semantic Kernel оправдал звание корпоративного решения от Microsoft. Его важная особенность — тесная интеграция с экосистемой .NET, что логично с учетом происхождения проекта и ориентации на инфраструктуру Microsoft. Архитектура с централизованным ядром, асинхронной обработкой и расширяемостью через skills делает его оптимальным выбором для интеграции с enterprise‑системами. Возможность объединить функциональность чат-ботов, RAG‑поиска и мультиагентных систем в едином фреймворке снижает архитектурную сложность и помогает строить масштабные проекты.
Опыт работы с Cloud.ru Evolution
Могу сказать, что интеграция действительно получается довольно гладкой. OpenAI-совместимый интерфейс позволяет большинство существующих решений подключать практически без изменений — меняются только endpoint и ключи аутентификации. Это экономит время на адаптацию кода.
Что касаемо реализации: кодить конечно круто, но зачем это нужно, когда есть готовые сервисы, которые предназначены для решения таких задач, причем позволяют сделать это намного быстрее.
Важное замечание о коде: все представленные примеры кода являются POC-решениями и не претендуют на production-готовность. Их цель — продемонстрировать базовые паттерны интеграции Cloud.ru Evolution с различными фреймворками. В реальных проектах необходимо добавить обработку ошибок, логирование, валидацию входных данных, ограничения по rate limiting и другие механизмы, обеспечивающие надежность и безопасность приложения.
Интеграция Evolution Foundation Models с современными фреймворками оказалась простой в реализации. Выбирайте инструмент под конкретную задачу и тогда получите максимальную эффективность от AI в ваших проектах. А еще, прямо сейчас и до конца октября все модели бесплатные, можно попробовать их в деле.
kevin
Так я и не понял, зачем это все?
KonstBir Автор
В начале статьи все написано