Связаться
ТехнологииAI Real Team

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

#агенты#OpenClaw#кейсы#промпт-инжиниринг#автоматизация

Когда милые и вежливые заказчики попросили сделать агента для поиска коммерческих лидов (заказов по обработке металлических деталей), я, имея опыт создания агента для поиска ВУЗов и генерации для них индивидуальных писем, решил, что вопрос простой и по аналогии его можно решить за пару дней.

Первая попытка: наивный подход

По той же аналогии я использовал готового агента, который занимался ВУЗами. Казалось бы: «найди все компании в таком-то регионе, которые могут быть клиентами по поверхностной антикоррозионной обработке».

Понятно, что такая история не работает. Мог бы и раньше догадаться :)

Стало ясно, что задачи нужно делать более мелкими, помещающимися в контекстное окно моей LLM-модели.

Следующая попытка: декомпозиция

Я разбил процесс на шаги:

  1. Определение подходящих видов деятельности (результат — список в JSON). Сделано с помощью запроса к локальной модели с неограниченным thinking.
  2. Расширение списка дополнительными материалами от заказчика (формат JSON). Сделано простым скриптом, добавившим перечень из 18 категорий видов деятельности.
  3. Поиск выполняется по каждой категории товаров в каждом из целевых регионов (агент создает субагентов по каждому поиску).

Для выполнения поиска был запущен агент (Петр 🐱‍👑) со следующим промптом:

Твоя задача - оркестрация сбора первичных данных по компаниям. 

Шаги по выполнению задачи:
1. Перейди в директорию ~/.openclaw/workspace/tdc
2. Прочитай файлы activities.json и regions.json. Ты не меняешь этот файл.
3. Бери по одной search_query, начиная со следующей, после той, которую уже обработала.
4. Запускаешь одного субагента, которому поручаешь открыть ya.ru, используя browser, ввести в поле поиска search_query и указать регионы из списка и нажать enter.
5. Получив поисковую выдачу, субагент должен получить DOM страницы и извлечь из него только данные в соответствии со схемой из phrase-result.json.
6. После этого субагент должен перейти на вторую страницу поисковой выдачи, получить DOM и извлечь данные по той же схеме, потом перейти на третью страницу и сделать то же самое.
7. Далее субагент объединяет данные по всем трем страницам в единый json, возвращает тебе и завершает работу. Субагент не должен делать никакого другого анализа.
8. Сохрани полученные от субагента данные в файл json, составив его название из фразы, использованной для поиска на русском языке.
9. Запомни обработанную search_query и переходи к следующей. Пиши мне каждый раз, когда получаешь информацию от субагента.

Это начало работать, но уже в процессе были видны проблемы: даже название query, начиная с небольшого расстояния от начала файла, уже было с пропущенными словами. А иногда, что еще хуже, субагент просто вылетал по таймауту. То есть явно не хватало контекста.

Корректировка контекстного окна

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

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

Точность, тем не менее, была крайне низкой. И «чудеса» работы агента сохранились: например, иногда, несмотря на наличие запрета параллельного запуска, он всё равно пытался его нарушить.

Новая гипотеза: дать агенту свободу

Следующей гипотезой было попробовать «развязать руки» агенту, чтобы он сам решал, какие инструменты поиска применять и как глубоко этот поиск должен идти.

Твоя задача - оркестрация сбора первичных данных по компаниям.

Шаги по выполнению задачи:
1. Перейди в директорию ~/.openclaw/workspace/tdc
2. Прочитай файлы activities.json и regions.json. Ты не меняешь эти файлы.
3. Бери по одной search_query, начиная со следующей, после той, которую уже обработала. Обработанный search_query определи по именам файлов с результатами.
4. Бери список регионов, начинай с первого.
5. Запускаешь одного субагента, которому поручаешь найти не менее 30 компаний, у которых есть производство в очередном целевом регионе, отбрасывая агрегаторов и продавцов, и вернуть данные в формате json в строгом соответствии с companies_template.json
6. Сохрани полученные от субагента данные в файл json, составив его название из activity+region, использованной для поиска на русском языке. Если субагент не вернул информацию, запусти нового субагента с тем же заданием и так далее, пока не получишь результат.
7. После этого запускай субагента на следующий регион из списка. Делай так, пока не исчерпается список регионов.

Это было весело, креативно, но опять совсем не точно и не надежно. Кроме того, сервис searXNG, установленный локально в Docker, оказался не очень хорош для работы с российскими сайтами, потому что сам использует в качестве провайдеров поиска Yahoo, DuckDuckGo и другие, которые, прямо скажем, не оптимальны для наших задач. Лучшим поиском по российскому интернету остается Яндекс.

Новый подход, который решил проблему

Я решил максимально упростить задачи ИИ, оставив ему только то, что не алгоритмизируется. Забрал у него все циклы, перечисления и т.д.

Разбил задачу на три четких этапа:

  1. Поиск компаний по заранее сформированным ключевым фразам с сохранением результатов поисковой выдачи в файл.
  2. Обработка поисковой выдачи: отсев мусора, непроизводственных компаний, рекламы, агрегаторов.
  3. Обогащение оставшихся в списке компаний: поиск контактных данных, проверка региона и видов деятельности.

Для каждого из этапов мы с Qwen написали скрипт, который занимается перебором вариантов в циклах, формальной верификацией возвращаемого результата и его записью в файлы. Кроме того, в скрипте нужно было предусмотреть возможности отладки, сброса результатов и продолжения с указанной точки. Этот же скрипт вызывает агента OpenClaw через командную строку, стартуя новую сессию перед каждым запуском, скармливает ему промпт, принимает результат и следит за «здоровьем» агентной системы.

Вот это взлетело и заработало. Прямо как часы. Ну, на первые 15-20 запросов.

Кстати, агент был проинструктирован использовать реальный браузер для поиска в Яндексе. Это, во-первых, давало (как я думал) возможность бесплатного качественного поиска, а во-вторых, позволило мне на этапе отладки мониторить, что он там делает в браузере и правильно ли формирует строку поиска.

Проблема с CAPTCHA и переход на Yandex Search API

Только я расслабился, случился облом. Яндекс начал выдавать капчу на каждый запрос. Иногда по несколько капч на запрос. Сначала я думал, что если я сам ее пройду (я же все равно у монитора), то Яндекс отстанет, решив, что если я так лихо кликаю по всякой чепухе из картинки, то я просто странный человек, которому взбрело в голову отправить 600 запросов подряд типа «крепеж для вентилируемых фасадов изготовление Нижегородская область -магазин -каталог -маркет -импорт -дилер».

Не прокатило. Пришлось разбираться с Yandex Search API. Это был тот еще квест. Потратил на него целый вечер, потому что, в отличие от привычных поставщиков таких услуг («вот вам деньги, а вот вам API ключ, вставляете в запрос и все работает»), тут все было очень наворочено и непросто. И, кстати, даже яндексовская Алиса несла некий бред.

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

Когда все заработало, процесс завершился за 26 минут. Были обработаны и сохранены результаты поиска по 680 запросам.

Неожиданный инсайт: EmotionPrompt
Кстати, в процессе, когда меня уже сильно выбесила не очень понятная и точная документация, я открыл для себя чудо промпт-инжиниринга. Это был неожиданный финт. Называется «EmotionPrompt». Не думал, что это сработает, точнее, когда писал, вообще просто выпускал пар. А оно сработало!

Иллюстрация эмоционального промпта

Второй и третий этапы обработки

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

В результате был составлен список из 3400 компаний, с адресами сайтов, предполагаемым регионом и набором видов деятельности (предположительно целевым). Со скриптом и тщательно проинструктированным агентом этот этап, хоть и длился несколько часов, прошел размеренно и точно.

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

Этап длился почти 4 дня. Была одна проблема, когда у OpenClaw кончилась память (видимо, где-то подтекает), и пришлось его перезапустить. Но в целом прошло также гладко.

Выводы и советы

  1. Дробите задачи на атомарные куски. Не заставляйте агента делать всё и сразу.
  2. Всю обработку циклов и больших файлов доверьте скриптам. ИИ должен заниматься только неалгоритмизируемой частью (анализ, извлечение, генерация).
  3. Уменьшите контекст, доступный агенту. Соотношение 50% от контекста модели пока в стадии эксперимента, но оно должно быть меньше, чем максимальный лимит модели, чтобы принудительно запускать компактацию.
  4. Четко формулируйте формат ввода и вывода для агента. Строгий JSON — ваш лучший друг для надежной интеграции со скриптами.