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

推荐订阅源

WordPress大学
WordPress大学
The Register - Security
The Register - Security
Hugging Face - Blog
Hugging Face - Blog
博客园 - 聂微东
GbyAI
GbyAI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园_首页
D
Docker
S
Security @ Cisco Blogs
K
Kaspersky official blog
爱范儿
爱范儿
Simon Willison's Weblog
Simon Willison's Weblog
TaoSecurity Blog
TaoSecurity Blog
V
V2EX
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Troy Hunt's Blog
Cloudbric
Cloudbric
博客园 - 三生石上(FineUI控件)
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Hacker News
The Hacker News
美团技术团队
S
SegmentFault 最新的问题
L
Lohrmann on Cybersecurity
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
宝玉的分享
宝玉的分享
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
M
MIT News - Artificial intelligence
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Martin Fowler
Martin Fowler
Google Online Security Blog
Google Online Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tor Project blog
Vercel News
Vercel News
The Cloudflare Blog
G
Google Developers Blog
T
Threat Research - Cisco Blogs
AI
AI
Stack Overflow Blog
Stack Overflow Blog
I
InfoQ
Scott Helme
Scott Helme
S
Schneier on Security
大猫的无限游戏
大猫的无限游戏
The GitHub Blog
The GitHub Blog
S
Securelist
IT之家
IT之家
Microsoft Azure Blog
Microsoft Azure Blog

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет 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 миллионов точек без потерь
От каши к структуре: гибридная AI-система для обработки свободного текста
andrey_chuya · 2026-04-25 · via Все публикации подряд на Хабре

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

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

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

Кейс

Я занимаюсь проектом, где нужно из свободных текстов на естественном языке вытаскивать структурированные данные. Не разово - постоянно, по мере поступления. За несколько месяцев я перепробовал регулярки, чистый LLM и в итоге пришёл к гибриду. Ниже расскажу, что из этого всего вышло: архитектура, промпты, трудности и неочевидные решения.

Стек: Python 3.12, Ollama + Qwen 2.5 (всё локально), YAML как формат хранения, SHA256 для дедупликации, Jinja2 для шаблонизации промптов.

Проект называется Svyazi - система структурирования и поиска по профилям участников сообщества, которое я веду. Код закрытый, но архитектурные решения универсальны.

Откуда взялась задача

В сообществе есть участники, которые хотят нетворкинг. Они представляются - кто подробно, кто в два предложения, хотя, конечно, лучше так не делать. Параллельно другие ищут: «нужен фронтендер на проект», «кто работал с Kubernetes в проде?», «ищу партнёра в финтех-стартап».

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

Описания участников начали копиться. Стало понятно, что искать по этой куче что-то конкретное нереально.

Приведу пример, что у нас может быть в знакомстве:

Привет! Я Алексей, занимаюсь бэкендом уже лет 7. Основной стек - Go и Python, последние два года плотно сижу на k8s. До этого работал в Яндексе и одном финтех-стартапе (NDA, не могу называть). Есть опыт с ML - делал рекомендательную систему, но это скорее хобби-проект. Ищу интересные проекты, можно парт-тайм. Английский - upper intermediate.

Или так:

Дизайнер, 10 лет. Figma, немного code. Spark AR.

Попробуйте найти среди двухсот таких описаний всех Go-разработчиков с Kubernetes и хотя бы базовым ML. Руками - часы.


Ключевая идея решения

1. Делаем глубокий профиль, а не визитку. Храним не «Иван, Python-разработчик», а стек с уровнями, реальные проекты, выступления, мягкие навыки. Пришёл я к этому не сразу. Сначала пытался разбирать взаимосвязи между людьми, мероприятиями и вакансиями, но потом понял, что фокус нужно сделать только на одном. На людях. Пришлось полностью всё рефакторить, что заняло у меня пару недель свободного времени.

2. Гибрид LLM + детерминированный код. LLM извлекает смысл, код нормализует результат. Каждый делает то, что умеет. Не зажимаем модель в узкие рамки, даём ей больше свободы - она творец! А вот алгоритмами жёстко приводим результат в нужные рамки.

3. Двухэтапный скоринг при поиске. Быстрый фильтр по индексу → LLM только для шорт-листа. Делать всё моделью дорого и долго. Незачем мелким ситом просеивать гору - мелкое сито оставим для золотых крошек.

4. Privacy by design. Данные, которые хоть как-то похожи на приватные, фильтруем на входе и не пускаем в карточки. А вот уже на выходе алгоритмами объединим профиль и контакт.

Почему глубокий профиль важнее каталога

Разница между «Иван, Python-разработчик» и человеком, которого действительно знаешь, - огромная. Когда данные структурированы достаточно глубоко, появляются нетривиальные вещи.

Прикольно сработал случайный эксперимент: я попробовал покопаться в поисковом индексе и через него найти коллаборации между участниками, в итоге случайно получилась экспериментальная функция - поиск коллабораций, которая пришлась участникам сообщества по душе.

Участница с 15-летним опытом в Wi-Fi-инженерии получила карточку коллаборации с разработчиком из Петербурга. Система предложила им совместный open-source проект по радиопланированию Wi-Fi-сетей - с описанием ролей и дорожной картой. Ни один из них не знал о существовании другого до этого момента. С плоским списком навыков такое не работает.

Путь был тернист...

Регулярки

Первое, самое простое, что может быть. Пишем паттерны, вытаскиваем навыки, годы опыта, компании.

Проблема: люди не пишут по шаблону. «5 лет опыта в Python» - регулярка справится. «Работаю с питоном с 2019» - нужно считать от текущей даты. «Делал кучу всего на пайтоне, потом перешёл на Go» - непонятно ни сколько лет, ни на каком уровне.

Слишком много мусора, слишком много пропущенного. Сейчас в эпоху LLM смысла в это нет, могло быть актуально лет 19 назад, но не сейчас.

Чистый LLM

Логичный следующий шаг: отдать текст модели, попросить вернуть JSON.

На практике три проблемы:

- Консистентность. Одна технология возвращается как golang, Go, Golang и Go (Golang). Для человека одно и то же, для фильтрации по базе - четыре разных навыка.

- Галлюцинации. Модель увидела «делал проект на Spark» → записала Apache Spark. Человек имел в виду Spark AR. Контекст был целиком про дизайн - не помогло.

- Нестабильный формат. Иногда ответ в ``json``, иногда с комментариями внутри (а в JSON комментариев нет), иногда с текстом до и после. Каждый такой случай - упавший парсер.

Гибрид

LLM извлекает смысл, детерминированный код приводит результат к единому виду. Из этой идеи и выросла архитектура проекта.

Архитектура: 5 слоёв

Началось всё с одного приложения, со временем код усложнился и поддерживать всё это стало слишком сложно. Поэтому на помощь пришло разделение конвейера на независимые модули - слои. Всего их пять. Каждый делает одну вещь. Если сломался третий - первые два переделывать не надо.

YAML → [Import] → [AI Processing] → [Normalization] → [Indexing] → [Пред-скоринг] → [Семантический поиск]

Слой

Что делает

1 - Import

Парсинг YAML, отсечение персданных, SHA256-дедупликация, создание карточки pending

2 - AI Processing

Отправка в LLM, получение JSON, retry при ошибках, dead letter queue

3 - Normalization

Синонимы → канон, классификация по достоверности, стандартизация форматов

4 - Indexing

Раскладка по индексам: навыки, роли, общий

5 — Пред-скоринг

Детерминированная фильтрация, пороговые значения, формирование шорт-листа

6 — Семантический поиск

LLM-скоринг по шорт-листу, ранжирование, результаты

Сквозной пример

Слой 1. Текст Алексея попадает в систему. Вырезаем идентификаторы: телеграм, ссылки, email - они хранятся отдельно и в AI-конвейер не попадают. Считаем SHA256:

def normalize_for_hash(text: str) -> str:
return re.sub(r'\s+', ' ', text.strip().lower())
def compute_hash(text: str) -> str:
return hashlib.sha256(normalize_for_hash(text).encode()).hexdigest()

Если хеш уже есть - пропускаем. Без этого одно описание, скопированное с разным форматированием, порождает два профиля. Было пару раз на ранних этапах.

Слой 2. Обезличенный текст уходит в Qwen 2.5. Возвращается:

{
"skills": [
    {"name": "Go",     "level": "senior", "years": 7},
    {"name": "Python", "level": "senior", "years": 7},
    {"name": "k8s",    "level": "middle", "years": 2},
    {"name": "ML",     "level": "junior", "years": null}
  ],
"companies": ["Яндекс"],
"work_format": "part-time"
}

Проиллюстрировал косяки: k8s вместо kubernetes, years: 7 для Go - это она взяла из «занимаюсь бэкендом лет 7» и приписала к конкретному языку. В тексте нигде не сказано, что именно на Go человек работает семь лет. Это работа для слоя 3.

Слой 3. Детерминированный код причёсывает JSON:

k8s → kubernetes (справочник синонимов)

Go → go (lowercase-каноника)

years: 7 → null (помечено как inferred)

ML level → hobby (из контекста "хобби-проект")

Слои 4–5 - подробнее в разделе про поиск ниже.

---

СardIndex — сердце системы

В центре всей архитектуры находится CardIndex. Это не отдельный слой, а сквозной компонент, с которым работают все слои пайплайна. CardIndex — это единственный источник правды о состоянии каждой карточки.

card_id: "a1b2c3d4"
content_hash: "sha256:9f86d08..."
status: "processed"       # pending | processed | error | updated
created_at: "2024-11-15"
processed_at: "2024-11-15"
version: 3
previous_hashes:
  - "sha256:3c7a2b..."    # v1
  - "sha256:8e4f1d..."    # v2

Отслеживание состояний, дедупликация, очередь обработки, история изменений - всё через него. Первые варианты были без CardIndex, и через некоторое время начинались ошибки дублирования.

Промпт-инжиниринг

Промпт для извлечения данных - не фиксированная строка. Он менялся раз двенадцать и будет меняться ещё много раз. Постоянно размышляю над тем, как сделать механизм обратной связи для периодического самоулучшения промпта. Уверен, что дойду до этого, хотя лучшее, что я пока придумал, - это показатель качества карточки, сформированный LLM на основе полноты и проверяемости данных. Промпт храню в отдельных .md-файлах, переменные подставляю через Jinja2. По сути, это такой же код - его точно так же надо версионировать и тестировать.

<details>

<summary>Сокращённый пример промпта</summary>

Ты - AI-ассистент системы Svyazi. Извлеки информацию из текста
и верни ВАЛИДНЫЙ JSON.

## Формат ответа
{
  "name": "ФИО",
  "professional_roles": ["роль1", "роль2"],
  "technical_skills": [
    {"name": "навык", "level": 3, "verification_status": "claimed"}
  ],
  "experience_years": 5
}

## Уровни навыков
- 0–1: Новичок - базовое знакомство
- 2–3: Промежуточный - использование в проектах
- 4–5: Эксперт - глубокие знания, обучение других

## verification_status
- "verified"  - есть подтверждение (сертификат, проект)
- "claimed"   - указано самим человеком
- "inferred"  - выведено из контекста

## Правила
1. Используй ТОЛЬКО явную информацию из текста
2. Не придумывай данные, которых нет в источнике
3. Если уровень не указан явно - ставь 1, статус "inferred"
4. Не путай «упомянул технологию» и «владеет технологией»
5. Не приписывай общий стаж к конкретному навыку

## Что НЕ делать
- Не добавляй текст до или после JSON
- Не вставляй комментарии в JSON

Вылезают три конкретных проблемы:

Модель «додумывает». Без явного правила о null - ставит уровень на основании каких-то своих представлений. Senior, middle. Откуда - непонятно.

Модель путает «упомянул» и «работает». «На прошлой работе коллеги использовали Terraform, но я в это не лез» → {"name": "Terraform", "level": "junior"}. Нет. Не навык.

JSON-мусор. Комментарии внутри JSON (в JSON их нет), текст до и после, случайные markdown-обёртки. Есть отдельный слой очистки - некрасивый, но необходимый.

Нормализация

После AI - три детерминированных этапа.

Синонимы → канон. Справочник (skills_synonyms.yml):

kubernetes:
  aliases: ["k8s", "kube", "кубер", "кубернетес"]
  category: "devops"

go:
  aliases: ["golang", "го", "go lang"]
  category: "programming_language"

Поначалу было строк тридцать, сейчас под сто и постоянно растёт. Отдельный AI-агент периодически сканирует Discovery-файл и дополняет справочник на основе новых обнаруженных значений.

Классификация по достоверности:

- verified - навык однозначно распознан по справочнику

- claimed - заявлен человеком, в справочнике отсутствует

- inferred - додуман моделью из контекста; идёт на ручную модерацию

Стандартизация форматов. «Yandex», «Яндекс», «яндекс» - одна компания. Даты в ISO.

Discovery

Когда встречается навык, которого нет в справочнике, - не выкидываем, складываем:

# unknown_values.yml
- value: "bun"
  context: "перешли с node на bun в продакшене"
  occurrences: 3
  first_seen: "2024-10-22"

- value: "cursor"
  context: "пишу код в cursor, очень удобно"
  occurrences: 7
  first_seen: "2024-11-01"

Раз в какое-то время открываю этот файл: bun - рантайм, добавляю в справочник. cursor - IDE, не навык. Когда occurrences растёт - тренд, надо разобраться побыстрее. Система сама подсказывает, чего ей не хватает.

Двухэтапный поиск (слои 5–6)

Вернёмся к слоям 5 и 6 из архитектуры. Приходит запрос «Go-разработчик, senior, Kubernetes, удалёнка». В поиск можно передавать как текст, так и файл вакансии или события.

Этап 1 - пре-фильтр (детерминированный). Быстрая проверка по индексу: go есть, kubernetes есть, формат совместим. Алексей попадает в шорт-лист. Работает мгновенно, ничего не стоит.

Этап 2 - семантический скоринг. Только для шорт-листа - LLM делает глубокую оценку с учётом нюансов. «Хобби-ML» может быть плюсом, если вакансия в ML-продукте. Или нерелевантен, если ищут чистого бэкендера.

Зачем два этапа? Прогонять через LLM все 200 профилей, когда 170 отсеиваются по элементарным критериям, - долго и бессмысленно. Зачем сеять гору ситом, если сначала нужно убрать камни?

Грабли на которые наступал

Недетерминированность. temperature=0 не гарантирует одинаковый результат. Что делаю: few-shot примеры в промпте, валидация ответа по JSON-схеме, retry до трёх попыток. После трёх - карточка уходит в error, для разбора вручную.

Галлюцинации. «Руководил командой из 5 человек» про волонтёрский проект → team_lead как профессиональный навык. Поэтому: обязательное поле confidence и правило - всё с меткой inferred проходит модерацию перед попаданием в индекс. Создаёт ручную работу, зато нет мусора в данных.

Скорость. Qwen 2.5 на моём железе - 120–200 секунд на одно описание. Четыре вещи помогают с этим справиться:

- Кэширование по хешу. Если описание не менялось - не обрабатываем повторно.

- Инкрементальность. Не «запусти всё заново», а «досыпь свежее».

- Оптимизация промптов. Начинал с полутора страниц. Постепенно вырезал лишнее. Меньше токенов - быстрее ответ, качество, если модель не перегружать, в норме.

- Есть очередь обработки, можно изменить несколько карточек и идти пить чай - до всех дойдёт очередь даже на слабом железе.

Приватность. Вся обработка локально. На этапе импорта персданные вырезаются и хранятся отдельно. Модель видит только обезличенный текст.

Итоги и выводы

1. AI + детерминированность. LLM хорошо понимает текст, но выдаёт нестабильный результат - значит, после неё нужен нормализационный слой.

2. Промпт - это код. Версионировать, тестировать, итерировать.

3. CardIndex - обязателен. Без единого источника правды система начинает дублировать и путаться.

4. Discovery - обратная связь от данных. Не выкидывайте неизвестное, накапливайте и анализируйте.

5. Оставляйте контроль человеком. Модерация inferred-значений и пополнение справочников - за человеком. Полная автоматизация без контроля ведёт к мусору в данных.

Qwen 2.5 справляется достойно, но до GPT-4o по точности извлечения далеко - компенсирую более жёсткой нормализацией.

Что дальше

Планирую расширять справочники и углублять профили. А недавно внедрил экспериментальную фичу - для каждого участника генерирую персональный медиа-отчёт: готовые темы для выступлений, форматы мастер-классов, карточки коллабораций с конкретными людьми из базы. Первые эксперименты уже есть - именно из них получилась история с Wi-Fi-инженером и питерским разработчиком. Также можете лично попробовать систему заполнив форму https://debugskills.ru/articles/svyazi/.

Вопросы и опыт по схожим проектам буду рад обсудить в комментариях.