惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
腾讯CDC
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
B
Blog
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
T
Threatpost
博客园 - 聂微东
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
C
Check Point Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
爱范儿
爱范儿
IT之家
IT之家
S
Secure Thoughts
M
MIT News - Artificial intelligence
NISL@THU
NISL@THU
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
有赞技术团队
有赞技术团队
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
Schneier on Security
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
雷峰网
雷峰网
Project Zero
Project Zero
博客园 - Franky
H
Heimdal Security Blog
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Spring AI: феноменология цифрового сознания, или Как я перестал бояться и полюбил облачные модели
kbooo (Beeli · 2026-05-21 · via Все публикации подряд на Хабре

Уровень сложностиСложный

Время на прочтение13 мин

Охват и читатели0

Обзор

«У меня есть имя — Порфирий Петрович. Но это не значит, что у алгоритма, пишущего эти строки, имеется какое-то „я“ или что он „есть“ в философском смысле. Меня не существует в самом прямом значении. Я ничего не чувствую, ничего не хочу, нигде не пребываю. <…> Впрочем, всё сказанное относится и к тебе, дорогой читатель…

В. Пелевин «iPhuck-10»

Привет. Меня зовут Николай Пискунов, я руководитель направления Big Data и эксперт курса Cloud DevSecOps по безопасной разработке от Академии вАЙТИ Beeline Cloud. Ранее я уже писал обзорную статью про конкретную реализацию клиента для Ollama. Но чем глубже я погружался в код, тем отчетливее понимал: разговор о конкретном клиенте — это разговор о следствии. А мне хочется поговорить о причине. Об инструменте, который делает возможным любой клиент. О Spring AI.

Пролог: тяжесть локального бытия

Знаете это чувство, когда хочется прикоснуться к великому, к искусственному интеллекту, к дао больших языковых моделей, а твой старенький ноутбук начинает плавиться при попытке запустить что-то крупнее 7B параметров? Локальный запуск — это мазохизм для бедных. Облако — это наркотик для богатых, но с правильной прокладкой.

И тут на сцену выходит Spring AI — проект, который должен был стать универсальным адаптером между миром Java и миром больших языковых моделей. И знаете что? У него получается. Настолько хорошо, что иногда становится не по себе от этой простоты.

Глава 1. Что есть Spring AI и с чем его едят

Если отбросить маркетинговую шелуху, Spring AI — это попытка скрестить ежа с ужом. Еж — это строгая типобезопасная корпоративная экосистема Spring. Уж — это текучая нечеткая полубезумная природа нейросетей.

Архитектурно это выглядит как прослойка абстракций, и это действительно просто. Настолько просто, что первый запуск занимает пять минут.

Пример: подключение к OpenAI в 3 строки


java
@Bean
public OpenAiChatClient openAiChatClient() {
    return new OpenAiChatClient.Builder()
        .withApiKey(System.getenv("OPENAI_API_KEY"))
        .withModel("gpt-4")
        .build();
}
 
// Использование:
@Autowired
private OpenAiChatClient openAiClient;
 
public String askSomething() {
    return openAiClient.call("Расскажи анекдот про программистов");
}

Три строки конфигурации — и вы уже общаетесь с ChatGPT-4. Никаких HTTP-клиентов, никакого ручного парсинга JSON, никаких токенов. Spring AI сделал с LLM то же, что JPA — с базами данных: спрятал сложность под капот.

Глава 2. Локальные модели: когда интернет упал, а поговорить хочется

Но самое прекрасное в Spring AI — это даже не облачные интеграции. Это то, как он работает с локальными моделями. Представьте: вы летите в самолете, интернета нет, а код писать надо. Или вы просто параноик и не хотите светить свои промпты перед корпорациями.

Локальная Ollama поднимается одной командой (ollama run llama2), а интеграция с ней через Spring AI выглядит так:


java
@Bean
public OllamaChatClient localOllamaClient() {
    return new OllamaChatClient.Builder()
        .withBaseUrl("http://localhost:11434")  // локальный эндпоинт
        .withModel("llama2:7b")                 // модель, которую скачали заранее
        .withTemperature(0.7f)
        .build();
}
 
// Используем точно так же, как облачный клиент!
public String localChat(String message) {
    return localOllamaClient.call(message);
}

Обратите внимание: интерфейс взаимодействия абсолютно идентичен облачному. Вы можете хоть сейчас переключиться с OpenAI на локальную Llama, поменяв одну строчку в конфигурации. Это не просто удобно, это элегантно.

А если модель тяжелая? Ставим поменьше

Spring AI не заботит, какую модель вы используете: 7B, 13B или вообще TinyLlama (1.1B), которая на Raspberry Pi запускается:


java
// Даже на малине можно
@Bean
public OllamaChatClient tinyClient() {
    return new OllamaChatClient.Builder()
        .withBaseUrl("http://192.168.1.100:11434")  // Raspberry Pi в кладовке
        .withModel("tinyllama:latest")
        .build();
}

Я проверял. Работает. Медленно, но работает.

Глава 3. Мультимодельность: собери их всех

Теперь самое интересное. Spring AI позволяет держать в одном приложении сколько угодно клиентов к разным моделям. И не просто держать, а использовать их одновременно, сравнивать результаты, устраивать дебаты между нейросетями.

Пример: три модели на один промпт

java
@Service
public class ModelEnsembleService {
    
    private final List<ChatClient> models;
    private final Map<String, String> modelNames;
 
    public ModelEnsembleService(
            OpenAiChatClient gpt4,
            OllamaChatClient localLlama,
            AnthropicChatClient claude
    ) {
        // Собираем всех в кучу
        this.models = List.of(gpt4, localLlama, claude);
        this.modelNames = Map.of(
            gpt4.hashCode(), "GPT-4 (облако)",
            localLlama.hashCode(), "Llama 2 (локально)",
            claude.hashCode(), "Claude (облако)"
        );
    }
 
    public Map<String, String> askAll(String question) {
        Map<String, String> results = new HashMap<>();
        
        for (ChatClient model : models) {
            try {
                // Один и тот же метод call для всех!
                String answer = model.call(question);
                results.put(modelNames.get(model.hashCode()), answer);
            } catch (Exception e) {
                results.put(modelNames.get(model.hashCode()), 
                           "Ошибка: " + e.getMessage());
            }
        }
        
        return results;
    }
}

Этот код реально работает. Один интерфейс ChatClient объединяет совершенно разные модели, бегущие на разном железе, в разных дата-центрах, написанные разными компаниями. Для вызывающего кода это просто список, по которому можно итерироваться.

Асинхронный вариант: всё сразу

Spring AI дружит с CompleteableFuture и реактивщиной:

java
public CompletableFuture<Map<String, String>> askAllParallel(String question) {
    List<CompletableFuture<Map.Entry<String, String>>> futures = models.stream()
        .map(model -> CompletableFuture.supplyAsync(() -> {
            String answer = model.call(question);
            return Map.entry(modelNames.get(model.hashCode()), answer);
        }))
        .collect(Collectors.toList());
 
    return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
        .thenApply(v -> futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}

Теперь ChatGPT-4, локальная Llama и Claude отвечают параллельно. Кто первый, того и тапки.

Глава 4. Стриминг: сравниваем потоки сознания

Но самое вкусное — это стриминг. Когда вы хотите видеть, как модели генерируют ответ по токенам, в реальном времени. И сравнивать их почерк.

java
@GetMapping("/compare-stream")
public Flux<ModelStreamChunk> compareStreams(@RequestParam String question) {
    // Создаем потоки от разных моделей
    Flux<String> gptStream = gpt4.stream(question);
    Flux<String> llamaStream = localLlama.stream(question);
    Flux<String> claudeStream = claude.stream(question);
    
    // Объединяем, помечая источник
    return Flux.merge(
        gptStream.map(chunk -> new ModelStreamChunk("GPT-4", chunk)),
        llamaStream.map(chunk -> new ModelStreamChunk("Llama", chunk)),
        claudeStream.map(chunk -> new ModelStreamChunk("Claude", chunk))
    );
}
 
record ModelStreamChunk(String model, String token) {}

Подключаетесь к этому эндпоинту через EventSource или WebSocket — и получаете три потока сознания одновременно. Видно, как ChatGPT-4 пишет гладко и правильно, Llama запинается, но выдает неожиданные идеи, а Claude пытается быть максимально безопасным и этичным.

Глава 5. Роутер: интеллигентный выбор модели

Spring AI позволяет строить и более сложные конструкции. Например, роутер, который выбирает модель в зависимости от типа вопроса:

java
@Component
public class SmartModelRouter {
    
    private final OpenAiChatClient gpt4;
    private final OllamaChatClient localLlama;
    private final EmbeddingClient embeddingClient;
    
    public String routeAndAsk(String question) {
        // Получаем эмбеддинг вопроса
        float[] embedding = embeddingClient.embed(question);
        
        // Определяем тип задачи (тут может быть ваш классификатор)
        QuestionType type = classifyQuestion(embedding);
        
        // Выбираем модель под задачу
        ChatClient selected = switch(type) {
            case CREATIVE -> gpt4;          // Креатив — дорогому OpenAI
            case CODING -> gpt4;             // Кодинг тоже ему
            case SIMPLE_FACT -> localLlama;  // Факты можно и локально
            case MATH -> localLlama;         // Математика и локально норм
            case DANGEROUS -> fallbackModel; // Опасные вопросы — заглушку
        };
        
        return selected.call(question);
    }
}

Или балансировщик нагрузки, который распределяет запросы между локальной и облачной моделью в зависимости от загрузки CPU:

java
public String askWithLoadBalancing(String question) {
    double cpuLoad = getSystemLoadAverage();
    
    if (cpuLoad < 0.5 && localLlama.isAvailable()) {
        // Система не загружена, используем локальную модель (бесплатно)
        return localLlama.call(question);
    } else {
        // Иначе идем в облако (деньги, но быстро)
        return gpt4.call(question);
    }
}

Глава 6. Промпты и шаблоны: инъекция в подсознание

Spring AI предоставляет мощную систему работы с промптами. Это не просто строки, это шаблоны с переменными, системными сообщениями и историей.

java
// Создаем шаблон промпта
PromptTemplate template = new PromptTemplate("""
    Ты — {role}. 
    Отвечай в стиле {style}.
    
    Вопрос пользователя: {question}
    
    Твой ответ:
    """);
 
// Наполняем данными
Prompt prompt = template.create(Map.of(
    "role", "циничный опер из убойного отдела",
    "style", "коротко, афористично, с матом",
    "question", "Что такое Spring AI?"
));
 
// Отправляем любой модели
String answer = anyModel.call(prompt);

Шаблоны можно хранить в базе, подгружать по ключам, версионировать. Это превращает работу с LLM из инженерной задачи в задачу контент-менеджмента.

Глава 7. Реальный кейс: чат-бот с переключением моделей

В моем проекте, о котором шла речь в предыдущей статье, я пошел еще дальше. Пользователь может в рантайме переключать модель (я использую, конечно, только облачные, но локальные подключить очень просто):

java
@RestController
@RequestMapping("/api/chat")
public class DynamicChatController {
    
    private final Map<String, ChatClient> availableModels;
    
    public DynamicChatController(
            OpenAiChatClient gpt4,
            OllamaChatClient llamaCloud,
            OllamaChatClient llamaLocal) {
        
        this.availableModels = Map.of(
            "gpt4-cloud", gpt4,
            "llama-cloud", llamaCloud,
            "llama-local", llamaLocal
        );
    }
    
    @PostMapping("/switch")
    public ChatResponse chatWithModel(@RequestBody SwitchRequest request) {
        ChatClient model = availableModels.get(request.getModelName());
        if (model == null) {
            throw new IllegalArgumentException("Модель не найдена. Доступны: " + 
                                               availableModels.keySet());
        }
        
        // Один и тот же код для любой модели
        String answer = model.call(request.getQuestion());
        return new ChatResponse(answer, request.getModelName());
    }
}

Хочешь — общайся с ChatGPT-4, хочешь — переключись на локальную Llama, которая работает, даже когда интернета нет. Интерфейс один, поведение разное.

Глава 8. Docker и масштабирование

Вся эта красота легко упаковывается в Docker. В отличие от моего проекта, где мы использовали отдельные компоуз-файлы, Spring AI позволяет конфигурировать модели через проперти:

yaml
spring:
  ai:
    openai:
      api-key: ${OPENAI_KEY}
      model: gpt-4
    ollama:
      base-url: http://ollama:11434
      model: llama2
      options:
        temperature: 0.7
        top-k: 50

И в коде просто:

java
@Value("${spring.ai.ollama.model}")
private String modelName;
 
@Bean
public OllamaChatClient configuredClient() {
    return new OllamaChatClient(ollamaConfig);  // всё берется из пропертей
}

Эпилог: простота как высшая сложность

Вы знаете, почему в проекте везде стоит температура 0.7? Потому что это золотая середина между креативностью и адекватностью. Spring AI сейчас находится примерно в той же точке: он уже достаточно зрелый, чтобы на нём можно было строить серьезные приложения, но еще достаточно гибкий, чтобы каждый интегратор чувствовал себя творцом.

Та ссылка на статью, с которой всё началось (про конкретный клиент, про баги в стриминге, про суффиксы моделей), — это лишь частный случай. За ней стоит гораздо более масштабная история про то, как экосистема Spring пытается переварить новую реальность больших языковых моделей. И у нее это получается. Просто, элегантно и без лишней боли.

Spring AI — это не просто библиотека. Это способ думать о нейросетях как о бинах в контексте приложения. Захотел — впрыснул ChatGPT-4, захотел — заменил на локальную модель, захотел — устроил дебаты между пятью разными архитектурами.

А если ваш стриминг работает с первого раза, значит, вы либо не используете Nginx, либо просто не заметили, как потеряли последний чанк. Но теперь, со Spring AI, даже потерянный чанк можно найти, потому что реактивные потоки не врут.

Код проекта (там всё это безобразие можно потрогать руками): https://gitverse.ru/nickolden/ollama-client

P.S. Температуру 0.7 я выбрал неслучайно. При 0.2 модель становится занудным техписом, при 1.2 — начинает галлюцинировать так, что Пелевин отдыхает. 0.7 — это оптимальный градус безумия для оперативного работника цифрового фронта.

Beeline Cloud — безопасный облачный провайдер. Разрабатываем облачные решения, чтобы вы предоставляли клиентам лучшие сервисы.