- BrainTools - https://www.braintools.ru -

28 дней со Spring AI: от простого чата до полноценного инструмента

Если дедлайн плавающий или его нет, обучение [1] и пет-проекты превращаются в вечный “черновик”: сегодня читаешь доки, завтра переписываешь пример, послезавтра думаешь про идеальную архитектуру. Это нормальный творческий процесс – пока не заметишь, что за месяц у тебя так и нет ничего, что можно запустить и показать.

Когда я проходил AI Advent Challenge [2] этот режим прокрастинации сломался: 28 дней подряд у тебя есть ровно сутки. В 10:00 приходит задание, а в 10:00 следующего дня – дедлайн. Поэтому каждый день заканчивается одной из двух вещей: либо у тебя есть работающий кусок, либо ты точно понимаешь, где решение не выдержало и почему.

Разработка шла в стиле “vibe coding”: главный артефакт – backlog “что сделать”, минимум upfront-дизайна, максимум итераций и постоянная проверка, что базовая работоспособность не сломалась.

Самое принципиальное решение было не про “какую модель взять”, а про стек. Вместо привычного “Python + что-то вокруг LLM” я остался в своем прод-стеке: Java + Spring Boot, а AI-часть собрал на Spring AI [3]. Цель была прикладная: понять, насколько LLM-интеграции нормально живут в Java-стеке, если делать не “демо-чатик”, а инструмент, который реально помогает в задачах и умеет:

  • sync / stream / structured режимы,

  • “память” и управление контекстом,

  • tool calling с ограничениями,

  • RAG по коду с цитатами,

  • sandbox для тестов и команд,

  • интеграции с GitHub/workspace,

  • немного orchestration поверх этого.

Началось все с базового чата. Но по мере того как задания усложнялись, получилась связка “retrieval + инструменты + окружение” – когда модель не просто отвечает, а помогает искать по коду, проверять гипотезы и воспроизводимо выполнять сценарии вокруг проекта.


Где заканчивается “магия Spring AI”

Spring AI хорошо ускоряет старт, но важно понимать границу: он закрывает слой интеграции, а “прод-поведение” почти всегда рождается из вашей инженерной обвязки.

Что дает Spring AI “из коробки”

  • ChatClient [4] – единый API к моделям: prompt -> call()/stream() -> response.

  • Advisors [5] – прослойки вокруг вызова модели (как middleware).

  • Chat Memory [6] – хранение истории/фактов вне модели и подмешивание их в запрос по conversationId.

  • Tools [7] – tool calling / function calling внутри приложения.

  • RAG [8] – базовые паттерны retrieval + интеграции для vector store.

  • Observability [9] – точки интеграции в метрики/трейсы вокруг вызовов.

Что почти всегда нужно добавить, если вы строите “боевую” систему

  • Token budget + preflight: оценить заранее “влезает ли запрос” и что резать, если не влезает.

  • Контекст-менеджмент: summarization/pruning/pinned facts + лимиты на tool outputs и RAG-контент.

  • Безопасность инструментов: allow-list, таймауты, лимиты ресурсов, dry-run.

  • RAG как пайплайн: multi-query, дедуп, пост-обработка + цитаты.

  • Sandbox для команд/тестов: изоляция, ограничения, cleanup.

Два коротких “продовых” примера, почему это нужно:

  • Стриминг может оборваться на середине – а вы все равно должны корректно закрыть соединение, не оставить подписки и сохранить “что успели сгенерировать”.

  • Инструмент может вернуть огромный вывод (логи/дифф/тесты) – и если не ограничить размер, он “съест” весь контекст и ухудшит качество следующего шага.

Запуск проекта и пререквизиты

Запуск проекта и пререквизиты

Ниже – минимум, чтобы воспроизвести проект локально и пройти сценарии из статьи.

Что потребуется:

  • ключ/доступ к LLM-провайдеру (или локальная модель),

  • Docker,

  • базовое понимание Spring Boot,

  • GitHub token (если хотите сценарии интеграции с GitHub),

  • дополнительные интеграции (граф, голос и т.п.) – их можно не включать.

Быстрый старт (docker-compose)
git clone https://github.com/GrinRus/ai_advent_challenge.git
cd ai_advent_challenge
# дальше - по README и docker-compose: поднять backend, backend-mcp и frontend
28 дней со Spring AI: от простого чата до полноценного инструмента - 1 [10]

Практические блоки

Блок 1. ChatClient и режимы: sync, stream, structured

Проблема

В реальных системах почти всегда нужны три сценария:

  1. Интерактивный чат – нужен streaming (UX), устойчивость к разрывам, понятная деградация.

  2. Сервисные вызовы – удобнее sync: проще таймауты/ретраи/тестирование.

  3. Structured output – нужен контракт, валидация и предсказуемая обработка ошибок.

  4. Несколько моделей – под разные задачи (скорость/стоимость/контекст), и необходимость тюнить параметры запроса на лету – без релиза (например, overrides на temperature/top_p/max_tokens под конкретный запрос).

Что дает Spring AI

ChatClient [4] – единый интерфейс, где и sync, и stream выглядят одинаково по стилю.

Что пришлось достроить

  • Развести режимы по контракту API: streaming-чат != sync-операция != structured вызов.

  • Устойчивый lifecycle стрима:

    • корректно закрывать SSE,

    • не оставлять подписки,

    • обрабатывать таймауты/ошибки.

  • Делать preflight до открытия стрима (чтобы не начинать SSE, если запрос заведомо “не влезет”).

  • Пробрасывать conversationId везде, где есть сессия/память.

Пример (фрагмент): preflight + conversationId + tool callbacks

Полный файл: ChatStreamController.java [11]

preflightManager.run(context.sessionId(), selection, sanitizedMessage, "stream-chat");

SseEmitter emitter = new SseEmitter(0L);

var promptSpec = chatProviderService.chatClient(selection.providerId()).prompt();

promptSpec =
    promptSpec
        .user(sanitizedMessage)
        .advisors(advisors ->
            advisors.param(ChatMemory.CONVERSATION_ID, context.sessionId().toString()));

if (researchContext.hasCallbacks()) {
  promptSpec = promptSpec.toolCallbacks(researchContext.callbacks());
}

Flux<ChatResponse> responseFlux =
    promptSpec.options(chatOptions).stream().chatResponse();
28 дней со Spring AI: от простого чата до полноценного инструмента - 2 [10]

Блок 2. Advisors: точка сборки логики вокруг вызова модели

Проблема

Очень быстро выясняется, что обычный “вызов модели” – не самый сложный кусок. Интересное начинается вокруг вызовов:

  • подключить память [12],

  • добавить RAG,

  • подмешать системные инструкции/политики,

  • нормализовать и ограничить контекст,

  • включить наблюдаемость.

Если размазать это по контроллерам/сервисам – получится код, который сложно расширять и еще сложнее поддерживать.

Что дает Spring AI

Advisors [5] – middleware-слои вокруг вызова модели, подключаемые через .advisors(...).

Практически полезная мысль: advisors – это место, где удобно держать “стандартные” части поведения [13], например:

  • memory-подмешивание,

  • retrieval-подмешивание,

  • trimming/budget-политики,

  • safety/policy-правила,

  • observability-обвязку.

Что пришлось достроить

  • Политики: что добавлять в контекст всегда, что – только для отдельных режимов, сколько токенов выделять под память/tools/RAG.

  • Ограничение размеров tool outputs и retrieval документов (иначе они “съедают” окно).

  • Вокруг advisor-цепочки – сервисные “рамки”: preflight, лимиты, allow-list tools.

Где это в проекте

Ключевой “якорь”, с которого все начинает работать согласованно – привязка conversationId:

Именно там видно: .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, ...)), после чего память и любые другие advisors начинают работать по одному и тому же ключу.


Блок 3. Память и контекст: как вообще устроена “память” у LLM

Важный факт

У LLM нет памяти в привычном смысле: модель не хранит состояние между запросами и отвечает только на то, что вы дали ей в контекст конкретного вызова. В Spring AI это прямо отражено в концепции “внешней памяти”: Chat Memory [6].

Отсюда вытекает ключевая инженерная мысль:

Каждый запрос к модели – это сборка контекста “заново”: system prompt + сообщение пользователя + история (или ее сжатая версия) + RAG-фрагменты + результаты инструментов + правила/ограничения.

Когда кто-то говорит “у нас есть память в llm”, обычно это означает: мы где-то храним историю/факты и каждый раз решаем, какую часть вернуть в prompt.

Проблема

Контекстное окно у модели ограничено, а в запрос “лезут”:

  • история диалога,

  • system prompt,

  • tool schemas и tool outputs,

  • RAG-документы,

  • сама задача пользователя.

Что может излишне “раздуть” контекст после чего модель может начать теряться в переданных токенах.

Что дает Spring AI

ChatMemory [6] и ChatMemoryRepository + типовые стратегии вроде “окна сообщений”.

Что пришлось достроить

  • Token budget + preflight: оценка токенов и решение “что резать/сжимать” до вызова модели.

  • Summarization истории, но контролируемо (очередь + backpressure + деградации).

  • Хранение памяти в БД, чтобы переживать рестарты и масштабирование.

  • Паттерн “summary + tail”: краткая сводка + хвост последних сообщений.

Полезная практическая стратегия деградации (в общих чертах):
сначала ограничивать tool outputs -> потом ограничивать RAG -> потом сжимать историю -> и только потом “не выполняем запрос”.

Пример (фрагмент): оценка токенов

Полный файл: DefaultTokenUsageEstimator.java [16]

TokenComputation promptComputation =
    computeTokenCount(encoding, tokenizerName, PROMPT_SEGMENT, request.prompt());
TokenComputation completionComputation =
    computeTokenCount(encoding, tokenizerName, COMPLETION_SEGMENT, request.completion());

int promptTokens = promptComputation.tokens();
int completionTokens = completionComputation.tokens();
int totalTokens = promptTokens + completionTokens;

return new Estimate(
    promptTokens,
    completionTokens,
    totalTokens,
    promptComputation.cacheHit(),
    completionComputation.cacheHit());
28 дней со Spring AI: от простого чата до полноценного инструмента - 3 [10]
Пример (фрагмент): summarization очередь + деградация

Полный файл: ChatMemorySummarizerService.java [17]

if (!enqueueSummarization(result)) {
  log.warn("Summarisation queue is full (session={}), skipping request", result.sessionId());
  recordFailure(result.sessionId(), "chat", "Summarisation queue saturated");
}
28 дней со Spring AI: от простого чата до полноценного инструмента - 4 [10]
Пример (фрагмент): summary + tail без дублей

Полный файл: DatabaseChatMemoryRepository.java [18]

int summarizedOrder =
    summaries.stream().mapToInt(SummaryRow::sourceEndOrder).max().orElse(0);

// summaries -> вперед
for (SummaryRow summary : summaries) {
  result.add(summary.asMessage());
}

// tail -> после, без дублей
storedMessages.stream()
    .filter(entry -> entry.messageOrder() > summarizedOrder)
    .map(StoredMessage::message)
    .forEach(result::add);
28 дней со Spring AI: от простого чата до полноценного инструмента - 5 [10]

Блок 4. Structured output: когда нужен объект, а не текст

Structured output – это подход, где вы просите модель вернуть ответ в строго заданном формате (чаще всего JSON), чтобы затем:

  • распарсить в DTO,

  • провалидировать,

  • и безопасно встроить в бизнес-логику (пайплайны, оркестрации, автоматические решения).

Где это реально полезно

  • классификация (например, “определи тип запроса”),

  • извлечение структурированных данных из текста (сущности, поля формы),

  • построение плана действий (список шагов, параметры инструмента),

  • генерация настроек/конфига,

  • машиночитаемые промежуточные результаты между шагами оркестрации.

Что дает Spring AI

Structured Output Converter [19] и связанные механики.

Что важно понимать про “строгость” и провайдеров

У крупных провайдеров есть механики, которые помогают добиваться более предсказуемого структурированного формата, но это не отменяет необходимости валидации на вашей стороне:

Даже при “строгих” подсказках/схемах в реальности могут появлятся типовые сбои:

  • “почти JSON” (лишний текст до/после),

  • сломанные кавычки/экранирование,

  • несоответствие типов,

  • недостающие обязательные поля или “лишние” поля,

  • частичный ответ из-за лимита токенов,

  • сложности со streaming-парсингом, если пытаться разбирать JSON “на лету”.

Поэтому structured output в проде – это контролируемая, но все равно best-effort интеграция, где валидация и fallback – обязательны.

Отдельный плюс structured-подхода: он хорошо тестируется. Можно делать контрактные/”golden”-тесты на JSON и на DTO-валидацию, а не пытаться тестировать “красоту текста”.

Пример (фрагмент): защита от пустого ответа

Полный файл: StructuredSyncService.java [22]

preflightManager.run(conversation.sessionId(), selection, userPrompt, "structured-sync");

ChatResponse response = prompt.options(options).call().chatResponse();

String content = extractContent(response);
if (!StringUtils.hasText(content)) {
  throw new SchemaValidationException("Model returned empty response for structured sync");
}

StructuredSyncResponse payload = convert(content);
28 дней со Spring AI: от простого чата до полноценного инструмента - 6 [10]

Блок 5. Tools: почему tool calling стал стандартом

Если упростить до одной фразы: LLM хороша в рассуждении и планировании, но “действия” она совершает через инструменты.

Tool calling (оно же function calling / tool use) – де-факто стандартный способ “подключить внешний мир” к модели:

На практике это означает: поиск по данным, вызовы внутренних сервисов, работа с репозиторием, запуск тестов – все это оформляется как инструменты с четким контрактом.

Что дает Spring AI

Tools [7]: ToolCallback, @Tool-аннотации и регистрация tool’ов.

Что пришлось достроить

В системе tools – это не “магия модели”, а API-контракты + контроль исполнения:

  • allow-list инструментов под режим/запрос,

  • классификация по риску: read / write / execute / external,

  • таймауты и лимиты на все (включая внешние API),

  • “dry-run first” для любых операций, меняющих состояние,

  • ограничение размера результатов инструментов (чтобы не “съесть” контекст),

  • предсказуемые ошибки [25] “почему нельзя” вместо непойманных исключений.

Пример (фрагмент): GitHub tool как контракт + лимиты

Полный файл: GitHubTools.java [26]

@Tool(
    name = "github.list_pull_requests",
    description = "Список PR с фильтрами, лимитами и пагинацией. Возвращает truncated=true если обрезано.")
GitHubPullRequestsResponse listPullRequests(GitHubListPullRequestsRequest request) {
  // ...
}
28 дней со Spring AI: от простого чата до полноценного инструмента - 7 [10]
Пример (фрагмент): preview/dry-run как обязательный шаг перед опасными действиями

Полный файл: CodingTools.java [27]

@Tool(
    name = "coding.apply_patch_preview",
    description = "Preview патча (dryRun) + опциональный запуск тестов. Таймаут в ISO-8601.")
ApplyPatchPreviewResponse applyPatchPreview(ApplyPatchPreviewRequest request) {
  return codingAssistantService.applyPatchPreview(request);
}
28 дней со Spring AI: от простого чата до полноценного инструмента - 8 [10]

Блок 6. Sandbox и live-окружение: контроль обязателен

Как только у модели появляются инструменты уровня “выполни команду” / “запусти тесты”, появляется новый класс рисков:

  • таймауты,

  • ресурсы (CPU/RAM/disk),

  • изоляция workspace,

  • очистка после выполнения,

  • безопасность параметров и команд.

И тут важен практический момент:

Любой MCP-инструмент, который работает с живым окружением (git, docker, CI, внешние API), надо проектировать как инфраструктурный компонент со строгими ограничениями.

Что дает Spring AI

Spring AI не “сделает sandbox за вас” – исполнение остается зоной ответственности приложения.

Что пришлось достроить

  • Docker runner с профилями (gradle/maven/npm/…),

  • таймауты и лимиты ресурсов,

  • ограничения команд (лучше allow-list/шаблоны, чем произвольные строки),

  • изоляция и cleanup.

Пример (фрагмент): детект типа проекта

Полный файл: DockerRunnerService.java [28]

if (Files.exists(projectAbsolute.resolve("pom.xml"))) return RunnerProfile.MAVEN;
if (Files.exists(projectAbsolute.resolve("package.json"))) return RunnerProfile.NPM;
28 дней со Spring AI: от простого чата до полноценного инструмента - 9 [10]

Блок 7. RAG по коду, как один из инструментов: что это и зачем

RAG (Retrieval-Augmented Generation) – подход, где вы не надеетесь на “память модели”, а подключаете к ответу вашу базу знаний:

  1. заранее индексируете источники (код/доки/вики),

  2. на запрос находите релевантные фрагменты (retrieval),

  3. добавляете их в контекст,

  4. модель отвечает с опорой на реальные куски данных.

Это дает:

  • меньше галлюцинаций по вашему проекту,

  • возможность отвечать “по факту кода”, а не “как обычно принято”,

  • проверяемость через цитаты (file_path/чанк).

Spring AI описывает RAG как паттерн: RAG [8].

Немного о ETL-пайплайне

Если данных много (репозитории, монорепы, доки), “просто закинуть в vector store” быстро не получится. Почти всегда нужна обертка в виде ETL:

  • Extract: собрать источники, обновления, диффы,

  • Transform: декодирование, фильтры, чанкинг, метаданные, дедуп,

  • Load: embeddings -> vector store, контроль версий, пересборка.

В Spring AI это выделено отдельным понятием: ETL Pipeline [29].

Что пришлось достроить

  • Индексация с защитой от мусора: skip binary, skip unchanged.

  • Retrieval как пайплайн:

    • multi-query,

    • дедуп,

    • topK после merge,

    • пост-обработка/реранкинг.

  • “Цитаты” (file_path/чанк) как обязательный формат.

  • Бюджеты: ограничивать число документов и размер контента в prompt.

  • Набор “отладочных сигналов”: что реально попало в контекст, сколько, почему.

Пример (фрагмент): skip binary + skip unchanged

Полный файл: RepoRagIndexService.java [30]

if (isBinaryFile(file)) {
  appendWarning(warnings, "Skipped binary file " + relativePath);
  return FileVisitResult.CONTINUE;
}

byte[] rawBytes = Files.readAllBytes(file);
String fileHash = hashBytes(rawBytes);

RepoRagFileStateEntity existingState = stateByPath.get(relativePath);
if (existingState != null && fileHash.equals(existingState.getFileHash())) {
  filesSkipped.incrementAndGet();
  return FileVisitResult.CONTINUE;
}
28 дней со Spring AI: от простого чата до полноценного инструмента - 10 [10]
Пример (фрагмент): multi-query + дедуп + topK после merge

Полный файл: RepoRagRetrievalPipeline.java [31]

List<Query> queries = expandQueries(transformedQuery, input, appliedModules);

Map<String, AggregatedDocument> dedup = new LinkedHashMap<>();
List<QueryRetrievalResult> retrievalResults = retrieveAll(queries, input);

for (QueryRetrievalResult result : retrievalResults) {
  for (Document document : result.documents()) {
    accumulateDocument(dedup, document, result.query(), result.index());
  }
}

List<Document> merged =
    dedup.values().stream().map(AggregatedDocument::toDocument).toList();

List<Document> top =
    merged.size() > input.topK() ? merged.subList(0, input.topK()) : merged;
28 дней со Spring AI: от простого чата до полноценного инструмента - 11 [10]
Пример (фрагмент): контекст с citations

Полный файл: RepoRagGenerationService.java [32]

int limit = Math.min(documents.size(), 5);

for (int i = 0; i < limit; i++) {
  Document document = documents.get(i);
  Map<String, Object> metadata = document.getMetadata();
  String path = metadata != null ? (String) metadata.getOrDefault("file_path", "") : "";

  builder.append(i + 1).append(". ").append(path).append("n");
  builder.append(document.getText()).append("nn");
}
28 дней со Spring AI: от простого чата до полноценного инструмента - 12 [10]

Блок 8. Observability и дисциплина логов

LLM-часть легко превратить в “черный ящик”, который сожрет весь ваш бюджет, если не мерить:

  • latency по режимам (stream/sync/structured),

  • ошибки (где именно: модель, tools, retrieval, sandbox),

  • токены/стоимость.

Spring AI поддерживает интеграцию с наблюдаемостью через Observability [9].

Минимальный “боевой” дашборд, который обычно окупается

  • latency p50/p95 по режимам,

  • tokens in/out (и “fallback-оценка”, если провайдер не отдает usage),

  • tool success/error rate + latency,

  • retrieval empty rate,

  • cost estimate.


Практикум: сценарий, который можно пройти руками

Идея – не просто “почитать про подходы”, а включить инструменты, дать ассистенту репозиторий и прогнать реальные сценарии: анализ кода, поиск, проверка сборки/тестов, объяснение архитектуры, ревью изменений.

Подготовка

  1. Поднимите проект по README (backend + backend-mcp + frontend).

  2. Убедитесь, что настроены ключи, которые нужны сценарию (LLM-провайдер, GitHub).

  3. Во фронтенде включите доступные MCP-инструменты (в проекте есть каталог инструментов, который UI использует, чтобы показать “что доступно”).

Промпты для копирования: анализ, сборка, поиск, ревью

Промпты необходимо выполнять в одном чате, в рамках одной сессии

1) Подключить репозиторий как workspace и понять, что это за проект

Скачай репозиторий https://github.com/GrinRus/ai_advent_challenge в workspace.

Дальше:
1) Коротко объясни, что делает проект и из каких компонентов он состоит.
2) Опиши, как связаны backend, backend-mcp и frontend.
3) Если информации не хватает - используй поиск по коду и приводи цитаты (пути файлов + фрагменты).
Формат ответа: сначала summary на 5-7 строк, затем список "компонент -> роль".
28 дней со Spring AI: от простого чата до полноценного инструмента - 13 [10]

2) Найти Gradle-подпроекты и проверить тесты в sandbox

Найди все gradle подпроекты (settings.gradle/settings.gradle.kts) и перечисли их.

Затем запусти гредл тесты
Если есть падения:
- перечисли упавшие тесты
- покажи ключевые строки стека
- предположи 2-3 причины
- предложи варианты исправления (без применения изменений)
28 дней со Spring AI: от простого чата до полноценного инструмента - 14 [10]

3) Найти в проекте preflight и оценку токенов

Найди в проекте, где реализован preflight и оценка токенов.
Дай ссылки на файлы и коротко опиши, как устроен процесс и какие решения принимаются (что режется/сжимается).
28 дней со Spring AI: от простого чата до полноценного инструмента - 15 [10]

4) Проверить RAG по коду: вопрос, цитаты, отладка

Ответь на вопрос: "Где в проекте реализованы инструменты GitHub и sandbox?"

Требования:
- приведи цитаты с путями файлов
- покажи по 1-2 ключевых фрагмента кода (коротко)
- если результатов слишком много - объясни, как ты ограничивал поиск/дедуп/выбор topK
28 дней со Spring AI: от простого чата до полноценного инструмента - 16 [10]

5) Ревью существующего MR/PR или изменений в ветке

Если GitHub-интеграция доступна:
1) Выведи список открытых PR (или последних PR, если открытых нет).
2) Выбери один PR и сделай ревью:
   - что меняется
   - потенциальные риски
   - что стоит протестировать
   - что можно упростить
Формат: краткое резюме + список замечаний.
28 дней со Spring AI: от простого чата до полноценного инструмента - 17 [10]

Что удалось достичь за 28 дней

Самый полезный эффект оказался не в “количестве фич”, а в том, что ежедневная работа с LLM быстро калибрует ожидания и вырабатывает понимание что это такое и с чем его едят.

За этот месяц я намного четче понял:

  • где LLM реально ускоряет работу (обзор кода, сводки, поиск связей, генерация черновиков, классификация/роутинг);

  • где она стабильно ошибается без “обвязки” (контекст раздувается, structured “плывет”, инструменты без лимитов начинают вести себя непредсказуемо);

  • что качество ответа часто определяется не “умностью модели”, а качеством контекста (RAG, цитаты, лимиты, preflight);

  • что “инструменты” – не дополнение, а центр практического применения: модель рассуждает, а действия делает через tool-слой;

  • что без наблюдаемости и дисциплины логов вы слишком поздно узнаете, где именно “дорого”, “долго” и “ломается”.


Финальная мысль

LLM – это не “магический мозг [33], который все помнит и все знает”, а скорее это еще один инструмент в стеке, но со своими особенностями: модель статична, контекст ограничен, ошибки выглядят непривычно, а “интеллект” проявляется не всегда как ты это ожидаешь.

Поэтому рабочая интеграция выглядит не как “мы прикрутили чат”, а как полноценная инженерная система, где LLM играет роль оркестратора:

  • RAG дает модели факты из ваших данных, а не “догадки”,

  • tools/MCP дают модели действия (прочитать код, запустить тесты, сходить в GitHub),

  • контекст-менеджмент делает все это возможным в рамках ограниченного окна,

  • sandbox и ограничения делают действия безопасными и воспроизводимыми,

  • observability объясняет, что происходит и сколько это стоит.

А дальше уже вы выбираете режим работы процесса:

  • human-in-the-loop, когда важны контроль и подтверждения,

  • human-out-of-the-loop, когда готовы автоматизировать целиком отдельные шаги.

Если относиться к LLM именно так – как к компоненту, вокруг которого нужна дисциплина, ограничения и измеримость – тогда “AI-часть” начинает жить в Java/Spring так же естественно, как любые другие части Spring зоопарка.


Репозиторий

GrinRus/ai_advent_challenge [34]

Автор: GrinRus

Источник [35]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/25764

URLs in this post:

[1] обучение: http://www.braintools.ru/article/5125

[2] AI Advent Challenge: https://mobiledeveloper.tech/ai_advent_challenge

[3] Spring AI: https://docs.spring.io/spring-ai/reference/

[4] ChatClient: https://docs.spring.io/spring-ai/reference/api/chatclient.html

[5] Advisors: https://docs.spring.io/spring-ai/reference/api/advisors.html

[6] Chat Memory: https://docs.spring.io/spring-ai/reference/api/chat-memory.html

[7] Tools: https://docs.spring.io/spring-ai/reference/api/tools.html

[8] RAG: https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html

[9] Observability: https://docs.spring.io/spring-ai/reference/observability/index.html

[10] Image: https://sourcecraft.dev/

[11] ChatStreamController.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/controller/ChatStreamController.java#L95

[12] память: http://www.braintools.ru/article/4140

[13] поведения: http://www.braintools.ru/article/9372

[14] ChatStreamController.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/controller/ChatStreamController.java#L116

[15] StructuredSyncService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/service/StructuredSyncService.java#L165

[16] DefaultTokenUsageEstimator.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/token/DefaultTokenUsageEstimator.java#L36

[17] ChatMemorySummarizerService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/memory/ChatMemorySummarizerService.java#L338

[18] DatabaseChatMemoryRepository.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/memory/DatabaseChatMemoryRepository.java#L100

[19] Structured Output Converter: https://docs.spring.io/spring-ai/reference/api/structured-output-converter.html

[20] Structured Outputs: https://platform.openai.com/docs/guides/structured-outputs

[21] Structured Outputs: https://platform.claude.com/docs/en/build-with-claude/structured-outputs

[22] StructuredSyncService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend/src/main/java/com/aiadvent/backend/chat/service/StructuredSyncService.java#L155

[23] Function calling: https://platform.openai.com/docs/guides/function-calling

[24] Tool use: https://platform.claude.com/docs/en/agents-and-tools/tool-use/implement-tool-use

[25] ошибки: http://www.braintools.ru/article/4192

[26] GitHubTools.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/github/GitHubTools.java#L108

[27] CodingTools.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/coding/CodingTools.java#L87

[28] DockerRunnerService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/docker/DockerRunnerService.java#L178

[29] ETL Pipeline: https://docs.spring.io/spring-ai/reference/api/etl-pipeline.html

[30] RepoRagIndexService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/github/rag/RepoRagIndexService.java#L158

[31] RepoRagRetrievalPipeline.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/github/rag/RepoRagRetrievalPipeline.java#L78

[32] RepoRagGenerationService.java: https://github.com/GrinRus/ai_advent_challenge/blob/main/backend-mcp/src/main/java/com/aiadvent/mcp/backend/github/rag/RepoRagGenerationService.java#L101

[33] мозг: http://www.braintools.ru/parts-of-the-brain

[34] GrinRus/ai_advent_challenge: https://github.com/GrinRus/ai_advent_challenge

[35] Источник: https://habr.com/ru/articles/979950/?utm_source=habrahabr&utm_medium=rss&utm_campaign=979950

www.BrainTools.ru

Rambler's Top100