Команда Spring АйО перевела статью, в которой рассказывается про разработку эффективных агентов с помощью Spring AI. Исследование Anthropic "Building effective agents" подчеркивает важность простоты и модульности в разработке LLM-агентов.
В статье рассматривается, как эти принципы реализуются в Spring AI через пять ключевых паттернов: Chain Workflow, Parallelization Workflow, Routing Workflow, Orchestrator-Workers и Evaluator-Optimizer.
В недавней публикации своего исследования: Building effective agents, Anthropic поделился ценными взглядами на разработку эффективных LLM агентов (Large Language Model - большая языковая модель). Что делает это исследование особенно интересным, так это то, что оно акцентирует простоту и пригодность к компоновке, ставя их в приоритет по отношению к комплексным фреймворкам. Давайте посмотрим на то, как эти принципы транслируются в практические реализации с использованием Spring AI.
В то время как описания паттернов и диаграмм взяты нами из оригинальной публикации от Anthropic, мы сосредоточимся на том, как реализовать эти паттерны с использованием возможностей Spring AI в части переносимости моделей и структурированного вывода. Мы рекомендуем сначала прочитать оригинальную публикацию.
Проект agentic-patterns реализует обсуждаемые ниже паттерны.
Агентные системы
Публикация исследования вводит важное архитектурное различие между двумя типами агентных систем:
Workflows: системы, в которых LLM и инструменты оркеструются через предварительно заданные пути в коде (например, директивная система)
Агенты (Agents): Системы, где LLM динамически направляют свои процессы и использование инструментов
Ключевой вывод состоит в том, что в то время как полностью автономные агенты могут казаться привлекательными, workflows часто предоставляют лучшую предсказуемость и стабильность для хорошо определенных задач. Это наилучшим образом сочетается с требованиями, предъявляемыми крупным бизнесом, где надежность и поддерживаемость являются решающими факторами.
Давайте посмотрим, как Spring AI реализует эти концепции через пять фундаментальных паттернов, каждый их которых помогает решению определенного набора кейсов:
Паттерн Chain Workflow является примером разбиения сложных задач на более простые, более управляемые, чтобы иметь возможность выполнять их по шагам.
Когда используется:
Задачи с четкими последовательными шагами
Когда вы готовы пожертвовать latency ради более высокой точности
Когда каждый шаг использует результаты предыдущего
Приведем практический пример из реализации Spring AI:
public class ChainWorkflow {
private final ChatClient chatClient;
private final String[] systemPrompts;
// Processes input through a series of prompts, where each step's output
// becomes input for the next step in the chain.
public String chain(String userInput) {
String response = userInput;
for (String prompt : systemPrompts) {
// Combine the system prompt with previous response
String input = String.format("{%s}\n {%s}", prompt, response);
// Process through the LLM and capture output
response = chatClient.prompt(input).call().content();
}
return response;
}
}
Эта реализация демонстрирует несколько ключевых принципов:
Каждый шаг фокусируется на своей зоне ответственности
Вывод одного шага становится вводом другого
Цепочка легко расширяется и поддерживается
LLM могут одновременно работать над задачами и агрегировать их вывод программным путем. Параллелизация workflow проявляется в двух ключевых вариантах:
Разбиенте на секции: задачи разбиваются на несколько независимых подзадач для параллельной обработки
Голосование: несколько инстансов одной и той же задачи запускаются одновременно ради консенсуса
Когда используется:
Обработка больших количеств похожих, но независимых объектов
Задачи, требующие многочисленных независимых подходов
Когда время обработки имеет решающее значение, а задачи можно выполнять параллельно
Паттерн Parallelization Workflow демонстрирует эффективную многопоточную обработку многочисленных LLM операций. Этот паттерн особенно полезен для сценариев, требующих параллельного выполнения вызовов LLM с автоматизированной агрегацией вывода.
Приведем базовый пример использования паттерна Parallelization Workflow:
List<String> parallelResponse = new ParallelizationWorkflow(chatClient)
.parallel(
"Analyze how market changes will impact this stakeholder group.",
List.of(
"Customers: ...",
"Employees: ...",
"Investors: ...",
"Suppliers: ..."
),
4
);
Этот пример демонстрирует параллельную обработку анализа ситуации с держателями акций, при этом каждая группа держателей акций анализируется отдельно в многопоточном режиме.
Паттерн Routing реализует умное распределение задач, позволяя обрабатывать различные типы входных данных по-разному.
Этот паттерн создан для сложных задач, в которых разные типы входных данных лучше обрабатывать различными, специально под них разработанными процессами. Он использует LLM, чтобы проанализировать содержимое ввода и перенаправить его на наиболее подходящее специализированное приглашение или обработчик.
Когда используется:
Сложные задачи с четко определенными категориями входных данных
Когда разные входные данные требуют специализированной обработки
Когда классификацию можно выполнить достаточно точно
Приведем базовый пример использования Routing Workflow:
@Autowired
private ChatClient chatClient;
// Create the workflow
RoutingWorkflow workflow = new RoutingWorkflow(chatClient);
// Define specialized prompts for different types of input
Map<String, String> routes = Map.of(
"billing", "You are a billing specialist. Help resolve billing issues...",
"technical", "You are a technical support engineer. Help solve technical problems...",
"general", "You are a customer service representative. Help with general inquiries..."
);
// Process input
String input = "My account was charged twice last week";
String response = workflow.route(input, routes);
4. Orchestrator-Workers
Этот паттерн демонстрирует, как реализовывать более сложное агентоподобное поведение, при этом полностью сохраняя контроль:
Центральная LLM оркестрирует декомпозицию задач
Специализированные обработчики обрабатывают специфические подзадачи
Четкие границы поддерживают надежность системы
Когда используется:
Сложные задачи, в которых подзадачи нельзя предсказать заранее
Задачи, требующие разных подходов или точек зрения
Ситуации, требующие адаптивного решения проблем
Реализация использует ChatClient от Spring AI's для интерактивного взаимодействия с LLM и состоит из:
public class OrchestratorWorkersWorkflow {
public WorkerResponse process(String taskDescription) {
// 1. Orchestrator analyzes task and determines subtasks
OrchestratorResponse orchestratorResponse = // ...
// 2. Workers process subtasks in parallel
List<String> workerResponses = // ...
// 3. Results are combined into final response
return new WorkerResponse(/*...*/);
}
}
Пример использования:
ChatClient chatClient = // ... initialize chat client
OrchestratorWorkersWorkflow workflow = new OrchestratorWorkersWorkflow(chatClient);
// Process a task
WorkerResponse response = workflow.process(
"Generate both technical and user-friendly documentation for a REST API endpoint"
);
// Access results
System.out.println("Analysis: " + response.analysis());
System.out.println("Worker Outputs: " + response.workerResponses());
5. Evaluator-Optimizer
Паттерн Evaluator-Optimizer реализует процесс dual-LLM (двойная LLM), где одна модель генерирует ответы, в то время как вторая предоставляет оценку и обратную связь в итерационном цикле, что напоминает процесс работы писателя над текстом. Паттерн состоит из двух главных компонентов:
Generator LLM (модель-генератор): Генерирует первоначальный ответ и улучшает его на базе обратной связи
Evaluator LLM (модель-оценщик): Анализирует ответы и предоставляет подробную обратную связь для улучшений
Когда используется:
Существуют четкие критерии оценки
Улучшение в цикле дает измеримое преимущество
Задачи выигрывают от нескольких раундов критической оценки
Реализация использует ChatClient от Spring AI's для интерактивного взаимодействия с LLM и состоит из:
public class EvaluatorOptimizerWorkflow {
public RefinedResponse loop(String task) {
// 1. Generate initial solution
Generation generation = generate(task, context);
// 2. Evaluate the solution
EvaluationResponse evaluation = evaluate(generation.response(), task);
// 3. If PASS, return solution
// 4. If NEEDS_IMPROVEMENT, incorporate feedback and generate new solution
// 5. Repeat until satisfactory
return new RefinedResponse(finalSolution, chainOfThought);
}
}
Пример использования:
ChatClient chatClient = // ... initialize chat client
EvaluatorOptimizerWorkflow workflow = new EvaluatorOptimizerWorkflow(chatClient);
// Process a task
RefinedResponse response = workflow.loop(
"Create a Java class implementing a thread-safe counter"
);
// Access results
System.out.println("Final Solution: " + response.solution());
System.out.println("Evolution: " + response.chainOfThought());
Преимущества реализаций в Spring AI
Реализации этих паттернов в Spring AI имеют несколько преимуществ, которые соответствуют рекомендациям Anthropic:
<!-- Easy model switching through dependencies -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
// Type-safe handling of LLM responses
EvaluationResponse response = chatClient.prompt(prompt)
.call()
.entity(EvaluationResponse.class);
Один и тот же интерфейс для разных LLM провайдеров
Встроенная обработка ошибок и повторные попытки
Гибкое управление подсказками
Best Practices и рекомендации
Базируясь как на исследовании от Anthropic, так и на реализациях от Spring AI, можно сформулировать следующие ключевые рекомендации для построения эффективных систем, базирующихся на LLM:
-
Начинайте с простого
Начинайте с простых workflows прежде чем добавить большую сложность
Используйте самый простой паттерн из тех, что соответствуют вашим требованиям
Добавляйте сложность только тогда, когда это необходимо
-
Надежность архитектуры
Реализуйте четкую систему обработки ошибок
Используйте типобезопасные ответы, где это возможно
Встраивайте валидацию на каждом шаге
-
Рассматривайте компромиссы
Балансируйте latency и точность
Проводите оценку целесообразности использования многопоточной обработки
Выбирайте между фиксированными workflows и динамическими агентами
Что дальше
В части 2 этой серии статей мы расскажем о том, как использовать Agents, которые сочетают в себе основные паттерны и более комплексные возможности:
Композиции паттернов
Комбинирование нескольких паттернов для создания более мощных workflows
Построение гибридных систем, которые используют силу каждого паттерна по максимуму
Создание гибких архитектур, которые могут адаптироваться к меняющимся требованиям
Усовершенствованное управление памятью в агенте
Реализация persistent памяти во всех диалогах
Эффективное управление контекстными окнами
Разработка стратегий по долговременному хранению знаний
Интеграция инструментов и протокола Model-Context (MCP)
Использование внешних инструментов через стандартные интерфейсы
Реализация MCP для расширенного взаимодействия с моделью
Построение расширяемых архитектур агента
В дальнейшем мы также расскажем о подробностях реализаций и best practices для этих усовершенствованных возможностей.
Заключение
Комбинация выводов исследования от Anthropic и практических реализаций Spring AI предоставляет мощный фреймворк для разработки эффективных систем использующих LLM. Следуя приведенным паттернам и принципам, разработчики могут создавать мощные, простые в поддержке и эффективные ИИ приложения, которые приносят реальную пользу, при этом избегая лишней сложности.
Главное — помнить, что иногда самое простое решение является самым эффективным. Начните с базовых паттернов, как следует разберитесь с тем, что подходит для решения вашего кейса и добавляйте сложность только тогда, когда это ощутимо улучшит производительность или возможности вашей системы.
Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм - Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.