Создание ИИ-агентов на основе существующих сервисов .NET и API
Команды, работающие с C#, могут сделать существующие сервисы .NET доступными в качестве инструментов для ИИ-агентов через вызов функций, с соблюдением необходимых правил контекста, безопасности и наблюдаемости.
Источники: AI Agents Are Changing C# Development: 10 Things Every .NET Developer Should Know
Большинство команд .NET уже владеют ключевыми компонентами ИИ-агента: типизированными сервисами, HTTP-API, механизмами аутентификации, базами данных и фоновыми задачами. Новшество заключается в языковой модели, которая может выбирать, какие из этих возможностей использовать и в каком порядке, чтобы достичь цели, описанной пользователем простым языком. В этом руководстве рассматриваются принципы, необходимые разработчику на C#, чтобы интегрировать агента в существующее приложение, не теряя контроля над его функционированием.
Классический чат-бот работает в рамках одного цикла обмена сообщениями: пользователь задает вопрос, а модель отвечает текстом. Агент же работает над достижением цели. Он анализирует, что требуется для достижения цели, вызывает инструменты для получения информации или выполнения действий, изучает результаты и только затем возвращает ответ.
Рассмотрим пользователя, который хочет узнать, сколько заказов поступило сегодня. Чат-бот может только догадываться или объяснять, как это можно выяснить. Сотрудник с соответствующими инструментами может получить данные о заказах, выполнить запрос за сегодняшний день, проанализировать цифры, написать краткое резюме и предоставить его в виде отчета. Практическое различие простое: чат-боты дают ответы, а сотрудники выполняют работу.
Инструменты превращают модель в полезный инструмент
Сама по себе языковая модель не имеет ни малейшего представления о том, как работает ваше приложение. Она не может читать таблицу клиентов или оформлять заказы. Инструменты служат мостом: это определенные операции, которые модель может запрашивать. Типичный набор инструментов для бэкенда электронной коммерции может выглядеть следующим образом:
GetCustomer()
CreateOrder()
SearchProducts()
GetWeather()
SendEmail()
QueryDatabase()
Модель определяет, когда требуется один из этих инструментов; ваш код определяет, как он будет работать. В C# инструментом может быть что угодно простое — это может быть обычный асинхронный метод в уже существующем сервисе, например тот, который загружает информацию о клиенте по его идентификатору с помощью внедренного сервиса:
public async Task<Customer> GetCustomer(int id)
{
return await customerService.GetByIdAsync(id);
}
Как только такой метод зарегистрирован как инструмент, агент может им воспользоваться, при этом реализация остается внутри обычного слоя сервисов, находясь под защитой тех же механизмов внедрения зависимостей, тестирования и логгинга, что и остальная часть кодовой базы. Именно здесь пересекаются подходы к разработке обычных приложений и разработке ИИ.
Как на практике происходит вызов функции
Вызов функции — это механизм, лежащий в основе инструментов. Сама модель ничего не выполняет; она отправляет структурированный запрос на вызов функции с определенными аргументами, а ваше приложение осуществляет этот вызов.
Предположим, пользователь запрашивает статус заказа 12345. Внутри модель приходит к выводу, что у нее отсутствует соответствующая информация:
I need order information.
Поэтому она просит хост-приложение вызвать функцию с номером заказа в качестве аргумента:
GetOrderStatus(12345)
Ваш код выполняет этот запрос, возвращает результат модели, которая затем преобразует сырые данные в читаемый ответ. Для разработчика на C# это должно показаться знакомым: вы предоставляете небольшой интерфейс своего приложения, подобно публичному API, пользователем которого выступает модель. Четкие названия, точные типы параметров и хорошие описания значительно повышают вероятность правильных вызовов.
Повторное использование существующих API
Для внедрения агентов не требуется переписывать бэкенд. Если ваше приложение уже предоставляет конечные точки, они могут стать инструментами агента. Например, сервис уже может предоставлять функцию поиска клиентов:
GET /api/customers/{id}
Помимо конечных точек для чтения заказа, создания заказов и перечисления товаров (приведённый ниже пример выполняет все три маршрута одновременно в одной строке; рассматривайте их как отдельные конечные точки):
GET /api/orders/{id}POST /api/ordersGET /api/products
При наличии этих механизмов запрос на поиск клиента, сделавшего наибольшее количество заказов в этом месяце, превращается в последовательность действий, которую агент может выполнить самостоятельно: получить данные клиента, извлечь информацию о заказах, сравнить показатели и сообщить об победителе. Агент — это просто ещё один клиент, использующий уже созданные и защищённые возможности.
Контекст: предоставляйте модели то, что ей нужно, а не всё, что у вас есть
Представьте себе получасовую рабочую встречу с агентом, который рассматривает ваше приложение, его схему базы данных, правила кодирования команды, бизнес-правила, открытые ошибки и решения, принятые ранее в ходе разговора. Чтобы оставаться полезным, агент должен отслеживать все соответствующие аспекты. Именно поэтому в проектировании агентов постоянно упоминаются история разговоров, управление контекстом, память, поиск информации, эмбеддинги и векторные базы данных.
Есть нюанс, который легко упустить: больше контекста не всегда означает лучшее качество. Каждый дополнительный элемент стоит денег и увеличивает задержку, а нерелевантные данные ослабляют сигнал, из-за чего ответы становятся менее надежными. Хорошая разработка агентов заключается в предоставлении правильного контекста именно в тот момент, когда он нужен, например путем получения только тех документов, которые соответствуют текущему шагу, вместо вставки всей базы знаний в каждый запрос. Чтобы узнать больше о том, чем отличаются эти механизмы памяти, ознакомьтесь с нашим обзором по теме памяти ИИ, эмбеддингов, RAG и весов моделей.
Позволение агентам безопасно запрашивать данные
Доступ к данным делает агентов особенно привлекательными для компаний, использующих .NET. Бизнес-пользователь может спросить, какие продукты показали наибольшее снижение продаж в прошлом месяце. Вместо того чтобы создавать отдельный отчет для каждого такого вопроса, система с поддержкой агентов может интерпретировать вопрос, определить соответствующие данные, выполнить запрос, проанализировать результаты и объяснить полученные выводы.
Сопровождающее это предупреждение является абсолютно неоспоримым: никогда не предоставляйте модели неограниченного доступа к вашей базе данных. Разрешение ей генерировать и выполнять произвольные SQL-запросы может привести к созданию разрушительных операций, утечкам данных между пользователями и запросам, блокирующим таблицы. Вместо этого используйте узкие, специально разработанные операции с проверенными параметрами и собственными механизмами контроля доступа:
GetSalesReport()
GetProductSales()
GetCustomerOrders()
Каждая из таких операций может обеспечивать безопасность на уровне строк, ограничивать объем результатов и использовать только читаемое соединение, тем самым жертвуя небольшой гибкостью ради высокой предсказуемости.
Агенты не заменяют архитектуру
Распространённое заблуждение заключается в том, что добавление модели позволяет игнорировать основы. На самом деле всё наоборот. Внедрение зависимостей, принципы проектирования SOLID, логгирование, тестирование, аутентификация, авторизация, обработка ошибок, возможность наблюдения за работой системы и чёткое разделение слоев становятся ещё более важными, когда выбор действий осуществляет недетерминистичный компонент.
Полезной моделью мышления является разделение ролей: агент может предложить действие, но ваше приложение решает, разрешено ли это действие. Короче говоря, ИИ принимает решения, а ваше приложение управляет ими. Проверки авторизации должны находиться в реализациях инструментов и соответствующих сервисах, а не в промптах, поскольку промпт представляет собой лишь предложение для модели, а не обязательное правило.
Риски безопасности умножаются с каждым инструментом
Каждый инструмент, который вы предоставляете, увеличивает вероятность возникновения ошибок. Агент может выполнять такие действия:
Read customer data
Send emails
Create orders
Delete records
Call external APIs
Далее следуют три вопроса. Что происходит, когда модель просто допускает ошибку, например удаляет неверную запись? Что происходит, когда злонамеренный пользователь пытается склонить агента к неправильному использованию инструмента? И что происходит, когда внешний контент, который читает агент — например электронное письмо или веб-страница — содержит скрытые инструкции? Последний сценарий, обычно называемый косвенной инъекцией промпта, является реальным и хорошо задокументированным типом атаки, а не гипотетическим экспериментом.
Защитные меры в основном представляют собой те, которые вы уже знаете, применяемые целенаправленно:
- Аутентификация и авторизация при каждом вызове инструмента, ограниченные конкретным конечным пользователем, а не агентом.
- Проверка входных данных всех аргументов, предоставляемых моделью.
Для действий с высоким влиянием, таких как возвраты средств, удаление данных или отправка сообщений клиентам, шаг подтверждения через человека часто является самым экономичным способом обеспечения безопасности.
Возможность отслеживания нелинейных потоков запросов
Традиционная обработка запросов легко отслеживается, поскольку происходит по прямой последовательности:
Request → Service → Database → Response
Выполнение задачи агентом может происходить с переходами между моделью, несколькими инструментами и разными бэкендами, прежде чем будет получен ответ:
User
↓
AI Model
↓
Tool
↓
API
↓
Database
↓
AI Model
↓
Another Tool
↓
Final Response
Такую структуру гораздо сложнее отлаживать. Когда что-то идет не так, необходимо определить, какие инструменты были вызваны, почему модель выбрала именно их, сколько времени занял каждый вызов, что пошло не так, сколько раз была выполнена модель в ходе работы и сколько всего потребовалось ресурсов. Считайте каждую работу трейсом с отдельными участками для вызовов модели и инструментов, фиксируя аргументы и количество токенов вместе с временем выполнения; в противном случае неверный ответ практически невозможно воспроизвести.
Почему разработчики на C# выигрывают с самого начала
Разработчики, владеющие C# и .NET, не начинают с нуля. Интерфейсы и классы, HTTP-API, контейнеры DI, асинхронный код, базы данных, механизмы авторизации, фоновые сервисы и развертывание в облаке — именно из этих элементов состоят системы агентов. Остается лишь небольшой список новых концепций, которые строятся друг на друге примерно в следующем порядке:
LLMs
↓
Prompting
↓
Function Calling
↓
Tools
↓
RAG
↓
Agents
↓
Multi-Agent Systems
Не нужно впитывать всё сразу; начните с малого.
Архитектура для ориентира
В минимальной версии конструкции агент находится в центре, а несколько инструментов соединяют его с вашими сервисами, базой данных и внешним API поиска; результаты затем возвращаются через агента к пользователю:
User
↓
AI Agent
↓
┌───────┼────────┐
↓ ↓ ↓
API Tool DB Tool Search Tool
↓ ↓ ↓
.NET Database External API
└───────┼────────┘
↓
Agent Result
↓
User
Ключевая особенность такой структуры заключается в том, что агент никогда не взаимодействует напрямую с вашими системами. Каждый путь проходит через инструмент, находящийся в распоряжении вашего .NET-приложения, поэтому контроль доступа, проверка данных и логирование находятся в одном месте, с управлением которым вы уже знакомы.
Практический план обучения
Для разработчика на C#, переходящего к работе с ИИ, разумный порядок обучения выглядит следующим образом:
- Понимание LLM. Изучите, как ведут себя большие языковые модели, в чем их сильные стороны и где они допускают ошибки, например, когда дают уверенные, но неверные ответы.
Сотрудник службы поддержки клиентов — отличный первый реальный проект, поскольку он задействует все аспекты работы. Он может найти информацию о клиенте, проверить заказ, узнать статус доставки, открыть заявку и ответить на запрос:
Search customer
↓
Check order
↓
Check delivery status
↓
Create support ticket
↓
Respond to customer
Каждый шаг соответствует определенному инструменту, а шаги с побочными эффектами являются очевидными кандидатами на включение в правила утверждения.
Основные выводы
- Сотрудник службы поддержки — это программная система: модель, объединяющая инструменты, данные, логику приложения, механизмы безопасности и мониторинга; он не является заменой ни одного из этих элементов.
- Реализуйте возможности в виде специализированных, проверенных инструментов; никогда не предоставляйте прямой доступ к базе данных или неограниченный доступ через API.
- Храните информацию об авторизации в коде, чтобы приложение контролировало действия сотрудника службы поддержки.
- Формируйте контекст для каждого шага отдельно, а не максимизируйте его объем.
- Отслеживайте каждую операцию с начала до конца, чтобы можно было объяснить возникшие ошибки и затраты.