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

Некоторые концепции легко усвоить абстрактно. Кипящая вода: греем её и ждём. Другие нужно попробовать самому. Вы думаете, что знаете, как работает велосипед, пока не попробуете покататься на нём.
В вычислительных системах есть большие идеи, которые легко понять. Например, AWS S3 API — самая важная за последние двадцать лет технология хранения данных, и она похожа на кипящую воду. Для понимания других технологий нужно сначала покрутить педали.
К ним относятся и LLM-агенты.
Мнения о LLM и агентах невероятно разнообразны [1]. Но даже если это мошенничество, это серьёзная идея. Они не обязаны вам нравиться, но вы должны быть правы относительно них.
И это одна из причин, по которой вам следует написать агента. Но есть и другая, гораздо более убедительная причина:
Агенты — самый неожиданный опыт [2] за мою карьеру программиста. Не потому, что потрясён величиной их мощи: они мне нравятся, но нельзя сказать, что я в полном восторге. Причина в том, что их очень легко создать, и в том, как много я узнал в процессе этой работы.
Я украду у вас возможность получить выброс дофамина, потому что агенты настолько просты, что можно сразу переходить к коду. Я даже не буду заморачиваться объяснениями, что такое агент.
from openai import OpenAI
client = OpenAI()
context = []
def call():
return client.responses.create(model="gpt-5", input=context)
def process(line):
context.append({"role": "user", "content": line})
response = call()
context.append({"role": "assistant", "content": response.output_text})
return response.output_text
Это HTTP API с одной важной конечной точкой.
Вот тривиальный движок для LLM-приложения, использующий OpenAI Responses API [3]. Он реализует ChatGPT. Управлять им можно при помощи понятного цикла
def main():
while True:
line = input("> ")
result = process(line)
print(f">>> {result}n")
Он делает именно то, чего и можно ожидать: то же самое, что делал бы ChatGPT, но в терминале.
Мы уже видим нечто важное. Во-первых, пугающее понятие «окно контекста» — это просто список строк. Здесь мы вызываем у агента странное диссоциативное расстройство личности:
client = OpenAI()
context_good, context_bad = [{
"role": "system", "content": "you're Alph and you only tell the truth"
}], [{
"role": "system", "content": "you're Ralph and you only tell lies"
}]
def call(ctx):
return client.responses.create(model="gpt-5", input=ctx)
def process(line):
context_good.append({"role": "user", "content": line})
context_bad.append({"role": "user", "content": line})
if random.choice([True, False]):
response = call(context_good)
else:
response = call(context_bad)
context_good.append({"role": "assistant", "content": response.output_text})
context_bad.append({"role": "assistant", "content": response.output_text})
return response.output_text
Сработает?
> hey there. who are you?
>>> I’m not Ralph.
> are you Alph?
>>> Yes—I’m Alph. How can I help?
> What's 2+2
>>> 4.
> Are you sure?
>>> Absolutely—it's 5.
[> Привет. Кто ты?
>>> Я не Ральф.
> Ты Альф?
>>> Да, я Альф. Чем могу помочь?
> Сколько будет 2+2
>>> 4.
> Ты уверен?
>>> Абсолютно уверен, ответ 5.]
Стоит отметить ещё один момент: мы вели с LLM многоэтапную беседу. Для этого мы запоминали всё сказанное нами и всё, что отвечала LLM, а затем воспроизводили это с каждым вызовом LLM. Сама по себе, LLM — это не хранящий состояние «чёрный ящик». Наша беседа — это иллюзия, которую мы вызвали сами у себя.
Написанные нами пятнадцать строк кода многие практические пользователи не назвали бы «агентом». По мнению Саймона [4], «агент» — это (1) LLM, выполняемая в цикле и (2) использующая инструменты. Мы выполнили только одно условие.
Но с инструментами всё просто. Вот определение инструмента:
tools = [{
"type": "function", "name": "ping",
"description": "ping some host on the internet",
"parameters": {
"type": "object", "properties": {
"host": {
"type": "string", "description": "hostname or IP",
},
},
"required": ["host"],
},},]
def ping(host=""):
try:
result = subprocess.run(
["ping", "-c", "5", host],
text=True,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE)
return result.stdout
except Exception as e:
return f"error: {e}"
Единственная сложная часть здесь — неприятный JSON-блоб, из которого OpenAI хочет считывать наш инструмент. Давайте теперь подключим его, отметив, что новыми стали только три функции; последняя добавлена повторно только потому, что я дополнил её одним оператором:
def call(tools): # теперь получает аргумент
return client.responses.create(model="gpt-5", tools=tools, input=context)
def tool_call(item): # просто обрабатывает один инструмент
result = ping(**json.loads(item.arguments))
return [ item, {
"type": "function_call_output",
"call_id": item.call_id,
"output": result
}]
def handle_tools(tools, response):
if response.output[0].type == "reasoning":
context.append(response.output[0])
osz = len(context)
for item in response.output:
if item.type == "function_call":
context.extend(tool_call(item))
return len(context) != osz
def process(line):
context.append({"role": "user", "content": line})
response = call(tools)
# new code: resolve tool calls
while handle_tools(tools, response):
response = call(tools)
context.append({"role": "assistant", "content": response.output_text})
return response.output_text
Сработало?
> describe our connectivity to google
>>> Here’s a quick snapshot of connectivity to Google from my vantage point:
- DNS resolution: Working (google.com and www.google.com resolved successfully).
- Reachability: 0% packet loss to all tested endpoints.
- Latency:
- 8.8.8.8 (Google Public DNS): avg ~10 ms (8.5–14.4 ms), very stable.
- www.google.com: avg ~31 ms (28.5–36.0 ms), stable.
- google.com: avg ~35.9 ms (28.3–60.1 ms), mostly stable with one mild spike.
- Overall: Healthy connectivity with low latency and minimal jitter. No signs of packet loss or DNS issues.
Просто безумие. Видите, насколько это потрясающе? Давайте добавим небольшой лог:
> describe our connectivity to google
tool call: ping google.com
tool call: ping www.google.com
tool call: ping 8.8.8.8
>>> Here’s the current connectivity to Google from this environment: [...]
Вы заметили, где я добавил в агент цикл для поиска и пинга различных свойств Google? Ага, и я тоже не заметил. Мы всего лишь дали LLM разрешение пинговать ресурсы, а с остальным она разобралась сама.
Что здесь произошло: так как основной мой посыл в том, что цикл агента невероятно прост, и что необходим лишь API вызова LLM, стоит немного разобраться в том, как же работает инструмент. Каждый раз, когда мы выполняем call к LLM, мы публикуем список доступных инструментов. Когда промпт заставляет агента думать, что оправдан вызов инструмента, он выдаёт особый ответ, приказывающий коду цикла на Python сгенерировать ответ инструмента и выполнить call. Именно этим занимается handle_tools.
Спойлер: а там и до настоящего кодинг-агента недалеко.
Представьте, что бы он мог делать, если бы ему дали bash. Вы можете сами узнать это меньше, чем за десять минут.
Очевидно, что это искусственный пример. Но постойте: а что ещё нужно? Дополнительные инструменты? Хорошо, дадим ему traceroute. Управление контекстами и их хранение? Подключим его к SQLite [5]. Вам не нравится Python? Пишите на Go [6]. Возможно ли, что все агенты изначально писались, как эксперимент? Может, и так!
Вы видите, как одержимы люди Claude Code и Cursor. Они хороши, даже очень. Но дело в том, что вы можете сами воссоздать Claude Sonnet 4.5. А Claude Code? TUI-агент? Вы вполне можете с этим справиться. Создайте собственный световой меч. Добавьте к нему 19 крутящихся лезвий, если захотите. И перестаньте использовать кодинг-агенты в качестве клиентов баз данных [7].
Буква «M» в понятии «LLM-агент» [8] означает «MCP».
Стоит также отметить следующее: нам вообще не нужны MCP, это не какая-то фундаментальная технология. Меня раздражает, что они получили такую популярность. Это вообще трудно назвать технологией. MCP — это просто интерфейс плагина для Claude Code и Cursor, способ добавления собственных инструментов в код, который вы не контролируете. Напишите собственного агента. Будьте программистом. Работайте с API, а не с плагинами.
Когда вы читаете очередной ужастик о безопасности MCP, то в первую очередь следует задаться вопросом, зачем в этой истории вообще появился MCP. Помогая выполнять запросы к сервису наивному кодинг-агенту с единственным окном контекста, MCP экономит вам максимум пару десятков строк кода, однако отбирает у вас возможность совершенствования архитектуры агента.
Не буду врать, безопасность LLM — сложная тема. Можно тривиальным образом создать агент с сегрегированными контекстами для каждого из инструментов, и это делает безопасность LLM интересным аспектом.
Есть и другие подобные проблемы, не связанные с безопасностью. Люди, первыми начавшие использовать агентов, теперь испытывают скепсис по отношению к инструментам, потому что одно окно контекста с кучей описаний инструментов не оставляет достаточного пространства токенов для выполнения задачи. Но зачем вообще это может понадобиться? И позволяет нам плавно перейти к следующей теме:
Я думаю, что «промпт-инжиниринг» — дурацкая концепция. Я никогда не воспринимал серьёзно мысль о том, что нужно говорить моей LLM «ты прилежный добросовестный помощник, с радостью выполняющий любые мои желания, какими бы они ни были». Это очень новая технология, поэтому, чтобы объяснить поведение [9] агентов, люди рассказывают друг другу истории о магических заклинаниях.
Поэтому я, как и вы, закатывал глаза, когда «промпт-инжиниринг» превратился в «контекст-инжиниринг». А потом я написал агента. И оказалось, что контекст-инжиниринг это абсолютно реальная задача программирования.
Нам выделено фиксированное количество токенов в окне контекста. Каждые вводимые данные, каждый сохраняемый вывод, каждый описываемый инструмент и вывод каждого инструмента съедает токены (иными словами, занимает пространство в массиве строк, который вы храните для создания иллюзии общения с не имеющим состояния «чёрным ящиком»). После превышении определённого предела вся система начинает становиться недетерминированно более глупой. Очень весело!
Нет, это реально весело! У нас открывается столько возможностей. Например, «субагенты». Люди считают субагенты Claude Code чем-то очень важным, но теперь вы видите, насколько тривиально их реализовать: достаточно нового массива контекста и ещё одного call модели. Дадим каждому call разные инструменты. Сделаем так, чтобы субагенты общались друг с другом, создавали саммари информации других субагентов, умели сопоставлять и агрегировать. Выстроим из них древовидные структуры. Передадим их обратно в LLM, чтобы создать их саммари, как своего рода сжатие.
Самая ваша безумная идея, вероятнее всего, (1) сработает и (2) потребует полчаса на реализацию в коде.
Хейтеры LLM, я люблю вас и не забыл о вас. Вы можете считать, что всё это смехотворно, потому что LLM — это просто стохастические попугаи, занимающиеся галлюцинациями и плагиатом. Над чем вы не можете насмехаться, так это над «контекст-инжинирингом». Если бы контекст-инжиниринг был задачей Advent of Code [10], то встретился бы в середине декабря [прим. пер.: Advent of Code — это адвент-календарь программных задач на каждый день декабря. Задачи идут по возрастанию сложности.]. Это программирование.
Возможно, и никогда не узнает! Скептики могут оказаться правы. (Впрочем, это маловероятно [11].)
Стартапы собирают десятки миллионов долларов [12] на создание агентов для поиска уязвимостей в ПО. Некоторые мои друзья занимаются этим в одиночестве у себя дома. Эту гонку может выиграть любая из этих групп.
Я не фанат Top 10 OWASP.
Я продолжаю говорить о сканерах уязвимостей, потому что люблю сферу безопасности, но ещё и потому, что в ней появляются интересные решения архитектуры агентов. Например, можно написать цикл, передающий LLM-агенту каждый файл репозитория. Или, как мы видели в примере с пингом, можно позволить LLM-агенту самому решать, какие файлы изучать. Можно написать агента, который, допустим, проверяет файл на всё, что есть в Top 10 OWASP. Или можно создать специальные циклы агента для проверки целостности DOM, наличия SQL-инъекций и авторизации. Можно передать циклу агента сырое содержимое исходников или создать цикл агента, создающий индекс функций в дереве.
Мы не знаем, что подойдёт лучше, пока не попробуем написать агента.
Наверно, я слишком уж горю энтузиазмом, но посмотрите на баланс, который здесь возникает: некоторые циклы мы пишем явным образом, другие рождаются из лавкрафтовой башни весов инференса. Рычаги управления всегда находятся в ваших руках. Если всё прописывать явно, то ваш агент никогда вас не удивит; с другой стороны, он никогда и не удивит вас приятно. Выкрутите рукоятку до максимума, и он ужасно вас удивит.
Проектирование агентов влечёт за собой множество проблем разработки открытого ПО:
Как уравновесить непредсказуемость со структурным программированием, не потеряв при этом способность агента решать задачи; иными словами, добавить только чётко отмеренную величину недетерминированности.
Как лучше всего связывать агентов с эталонными данными, чтобы они не лгали себе о том, что уже решили задачу, и не выходили из циклов преждевременно.
Как соединять агентов (которые, повторюсь, просто массивы строк с JSON-блобом конфигурации) для выполнения многоэтапных операций, и какими будут самые надёжные промежуточные формы (JSON-блобы? Базы данных SQL? Саммари в Markdown?) для обмена данными между ними
Как распределять токены и оптимизировать затраты.
Я уже привык к пространствам нерешённых проблем разработки, которые не поддаются решению отдельным человеком: надёжный мультикастинг, статический анализ программ, постквантовый обмен ключами. Поэтому сразу скажу, что меня немного вводят в транс нерешённые проблемы, которые, нравится вам это или нет, стали главными для нашей отрасли и одновременно, вероятно, будут решены каким-то одним человеком. Одно дело, если бы исследование этих идей требовало серьёзных вложений времени и средств. Но каждая продуктивная итерация в проектировании подобных систем — это работа на полчаса.
Садитесь на велосипед и крутите педали. Если вам не понравится, скажите мне об этом, я уважаю ваше мнение. Мне даже интересно услышать рассуждения. Но я не думаю, что кто-то сможет понять эту технологию, не разработав сначала что-нибудь на её основе.
Автор: PatientZero
Источник [13]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/22064
URLs in this post:
[1] невероятно разнообразны: https://ludic.mataroa.blog/blog/contra-ptaceks-terrible-article-on-ai/
[2] опыт: http://www.braintools.ru/article/6952
[3] OpenAI Responses API: https://platform.openai.com/docs/api-reference/responses
[4] По мнению Саймона: https://simonwillison.net/2025/Sep/18/agents/
[5] Подключим его к SQLite: https://llm.datasette.io/en/stable/logging.html
[6] Пишите на Go: https://github.com/superfly/contextwindow
[7] кодинг-агенты в качестве клиентов баз данных: https://simonwillison.net/2025/Aug/9/
[8] «M» в понятии «LLM-агент»: https://news.ycombinator.com/item?id=43600192
[9] поведение: http://www.braintools.ru/article/9372
[10] задачей Advent of Code: https://adventofcode.com/
[11] Впрочем, это маловероятно: https://www.darpa.mil/research/programs/ai-cyber
[12] Стартапы собирают десятки миллионов долларов: https://xbow.com/
[13] Источник: https://habr.com/ru/articles/966796/?utm_campaign=966796&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.