Как работает агент
Начнем с очень базового вопроса - "что такое AI-АГЕНТ?"
Проблема в том, что никто точно не знает. Или просто я спрашивал не тех людей. Общепринятого формального определения нет. Но все, кто занимается разработкой агентов, +- интуитивно понимают, что это такое.
Самое последнее хайповое определение
Оно несет в себе не очень много смысла, т.к. в слово Harness просто засунули все те невырозимые 10000 приседаний, которые нужно сделать вокруг LLM, чтобы превратить ее в агента.
В Harness могут входить
- ваш механизм управления контекстом
- тулы, которые вы прикрутили к нему
- память
- скилы
- мультиагентная логика
- интеграции с внешними системами
- и тд и тп
Т.е. Harness - это весь тот код, что вы пишете, чтобы превратить LLM в что-то, что закрывает реальные задачи.
А что такое LLM в этой парадигме? Очень просто: LLM - этомозг, который принимает решения HTTP ручка. И делает она ровно одну вещь: принимает JSON и отдает JSON
Все остальное - это Harness
#Agents
Начнем с очень базового вопроса - "что такое AI-АГЕНТ?"
Проблема в том, что никто точно не знает. Или просто я спрашивал не тех людей. Общепринятого формального определения нет. Но все, кто занимается разработкой агентов, +- интуитивно понимают, что это такое.
Самое последнее хайповое определение
Agent = LLM + HarnessОно несет в себе не очень много смысла, т.к. в слово Harness просто засунули все те невырозимые 10000 приседаний, которые нужно сделать вокруг LLM, чтобы превратить ее в агента.
В Harness могут входить
- ваш механизм управления контекстом
- тулы, которые вы прикрутили к нему
- память
- скилы
- мультиагентная логика
- интеграции с внешними системами
- и тд и тп
Т.е. Harness - это весь тот код, что вы пишете, чтобы превратить LLM в что-то, что закрывает реальные задачи.
А что такое LLM в этой парадигме? Очень просто: LLM - это
User -- {
"role": "user",
"content": "Привет!"
} --> LLM
User <-- {
"role": "assistant",
"content": "Господи, ну что опять!?"
} -- LLMВсе остальное - это Harness
#Agents
🔥12👍7❤🔥2😁1
Контекст агента
Context - это полная история вашего взаимодействия с моделью. Т.к. API LLM - это stateless сервис, то вам нужно передавать весь контекст на каждый запрос.
Поэтому диалог в ChatGPT изнутри выглядит как-то так
Все, что вы запихнули в запрос к модели и есть контекст. Контекстное окно - это то, насколько большую JSONнину модель в принципе способна переварить.
И вот тут мы сталкиваемся с основной проблемой контекста - он растет.
И растет он экспоненциально. Т.е. на каждый следующий запрос в модель, вы отправляет все более и более жирный JSON. А это токены и бабки.
Хорошо, что контекст кешируется. Т.е. на самом деле вы отправляете что-то такое
Кешированные токены могут или просто стоить дешевле, или вообще быть бесплатными (в подписке Claude Max). Но даже так они активнее сжигают ваши лимиты, так что общее правило - если закончили с текущей задачей, новую начинайте в другом чате. И модель умнее будет, и токены сильно сэкономите
Context Compaction - это самый базовый функционал, который необходим агенту. Если контекст слишком разросся, надо его как-то сжимать. А т.к. это просто JSON, то сжимать его можно каким угодно образом - учитывать только последние N сообщений, выкидывать определенные или случайные. Или сжимать весь текущий диалог в одно
Да, к слову: здесь вы уже способны написать ChatGPT (веб-приложение, не модель)
#Agents
Context - это полная история вашего взаимодействия с моделью. Т.к. API LLM - это stateless сервис, то вам нужно передавать весь контекст на каждый запрос.
Поэтому диалог в ChatGPT изнутри выглядит как-то так
User -- {
"role": "user",
"content": "Привет!"
} --> LLM
User <-- {
"role": "assistant",
"content": "Господи, ну что опять!?"
} -- LLM
User -- [
// history
{"role": "user", "content": "Привет!" },
{"role": "assistant", "content": "Господи, ну что опять!?"},
// new message
{"role": "user", "content": "Да так, заскучал" }
] --> LLM
Все, что вы запихнули в запрос к модели и есть контекст. Контекстное окно - это то, насколько большую JSONнину модель в принципе способна переварить.
И вот тут мы сталкиваемся с основной проблемой контекста - он растет.
500t
| user |
300t | agent |
| user | user |
100t | agent | agent |
| user | user | user |
| system | system | system |
И растет он экспоненциально. Т.е. на каждый следующий запрос в модель, вы отправляет все более и более жирный JSON. А это токены и бабки.
Хорошо, что контекст кешируется. Т.е. на самом деле вы отправляете что-то такое
50t
| user |
50t | |
| user | |
100t | | |
| user | 250ct | 450ct |
| system | cached | cached |
Кешированные токены могут или просто стоить дешевле, или вообще быть бесплатными (в подписке Claude Max). Но даже так они активнее сжигают ваши лимиты, так что общее правило - если закончили с текущей задачей, новую начинайте в другом чате. И модель умнее будет, и токены сильно сэкономите
Context Compaction - это самый базовый функционал, который необходим агенту. Если контекст слишком разросся, надо его как-то сжимать. А т.к. это просто JSON, то сжимать его можно каким угодно образом - учитывать только последние N сообщений, выкидывать определенные или случайные. Или сжимать весь текущий диалог в одно
<summary></summary> сообщение с помощью той же LLM - и поехали дальше. Последний вариант самый простой и популярный.Да, к слову: здесь вы уже способны написать ChatGPT (веб-приложение, не модель)
#Agents
❤9👍3🔥2⚡1👏1🙈1
Инструменты
Инструменты - это очень нужная агенту вещь. По факту - это произвольные куски кода, которые LLM может запускать по своему усмотрению
С точки зрения модели,
Выглядит он следующим образом:
• Название инструмента - это уникальный айдишник, по которому модель будет его вызывать.
• Описание - наша попытка объяснить модели, для чего этот инструмент нужен и когда его стоит дергать
• Аргументы - это JSONSchema с описанием того, как вообще вызывать этот инструмент. Мы просто надеемся, что модель сможет отдать нужный JSON
Теперь, если мы спросим модель о текущем дне недели, последовательность запросов будет выглядеть примерно следующим образом:
И контекст выполнения будет выглядеть следующим образом:
С точки зрения кода, инструмент - это просто функция
С тем, чтобы распарсить название, описание, аргументы и подложить их в контекст, фреймворки справляются как-то сами. Чтобы вызвать и отдать результат выполнения в модель - тоже.
Основная проблема инструментов в том, что их может быть МНОГО. Если ваш контекст на 95% состоит из описания инструментов и пользовательский запрос просто теряется на их фоне - не удивляйтесь, если модель творит дичь. О способах борьбы с этим мы поговорим чуть позже. Пока - просто старайтесь не включать инструменты, которые вам не нужны
К слову об MCP (я уже писал пост про них) - это те же инструменты, только раздаются по HTTP / сокету. С точки зрения модели и контекста - это то же самое
Собственно, на этом все. Контекст + Инструменты - это два кита, на которых плывут агенты. Все остальное реализовано поверх них.
#Agents #MCP
Инструменты - это очень нужная агенту вещь. По факту - это произвольные куски кода, которые LLM может запускать по своему усмотрению
С точки зрения модели,
Tool - это просто JSON (снова), котороый мы подложили в контекстВыглядит он следующим образом:
{
"name": "get_weekday",
"description": "Call this tool each time you want to know current weekday",
"arguments": JSONSchema
}
• Название инструмента - это уникальный айдишник, по которому модель будет его вызывать.
• Описание - наша попытка объяснить модели, для чего этот инструмент нужен и когда его стоит дергать
• Аргументы - это JSONSchema с описанием того, как вообще вызывать этот инструмент. Мы просто надеемся, что модель сможет отдать нужный JSON
Теперь, если мы спросим модель о текущем дне недели, последовательность запросов будет выглядеть примерно следующим образом:
User -- {
"role": "user",
"content": "Привет, чем займемся сегодня?"
} --> LLM
LLM -- {
"role": "assistant",
"tool_calls": [{
"call_id": "...",
"name": "get_weekday",
"arguments": "{}" // JSON string
}]
} --> Agentic Framework
LLM <-- {
"role": "tool",
"content": "Friday"
} -- Agentic Framework
User <-- {
"role": "assistant",
"content": "Friday! It's the time to drink beer!"
} -- LLM
И контекст выполнения будет выглядеть следующим образом:
| assistant |
| tool result |
| tool call |
| user |
| tools definitions |
| system |
С точки зрения кода, инструмент - это просто функция
from autogen.beta import Agent, config
agent = Agent("my-lovely-agent", config=config.OpenAIConfig("gpt-5"))
@agent.tool
def get_weekday() -> str:
return "Friday" # всегда пятница, всегда пьем пиво
С тем, чтобы распарсить название, описание, аргументы и подложить их в контекст, фреймворки справляются как-то сами. Чтобы вызвать и отдать результат выполнения в модель - тоже.
Основная проблема инструментов в том, что их может быть МНОГО. Если ваш контекст на 95% состоит из описания инструментов и пользовательский запрос просто теряется на их фоне - не удивляйтесь, если модель творит дичь. О способах борьбы с этим мы поговорим чуть позже. Пока - просто старайтесь не включать инструменты, которые вам не нужны
К слову об MCP (я уже писал пост про них) - это те же инструменты, только раздаются по HTTP / сокету. С точки зрения модели и контекста - это то же самое
Собственно, на этом все. Контекст + Инструменты - это два кита, на которых плывут агенты. Все остальное реализовано поверх них.
#Agents #MCP
Telegram
FastNews | Никита Пастухов
Как работает MCP
MCP - это очень простой протокол, который изменил мир. По сути, MCP - это RPC (Remote Procedure Call) для LLM, где "процедурами" выступают поставщики контекста. Весь обмен построен на стандартизированных JSON-сообщениях. ЭТО ПРОСТО HTTP…
MCP - это очень простой протокол, который изменил мир. По сути, MCP - это RPC (Remote Procedure Call) для LLM, где "процедурами" выступают поставщики контекста. Весь обмен построен на стандартизированных JSON-сообщениях. ЭТО ПРОСТО HTTP…
❤11🔥5
Память
Память агента - это его возможность самостоятельно получать доступ к информации (загружать в контекст), которая не находится в контексте прямо сейчас, но он знал ее раньше.
Это может быть его личность, информация о пользователе, история предыдущих диалогов, знания о доменной области - что угодно.
С точки зрения реализации, память - это просто набор инструментов, которые позволяют писать, читать и искать по воспоминаниям.
Что-то вроде этого
В самом простом случае, память - это просто директория на файловой системе. Например, вот такая (OpenClaw):
Имея такую директорию и парочку инструментов для работы с ней, OpenClaw уже умеет:
- самодописывать свой системный промпт (PERSONALITY.md)
- обновлять информацию о своем хозяине (USER.md)
- писать историю диалогов, искать по ним и доставать оттуда факты
К слову, RAG - это точно такой же набор инструментов с точно такими же возможностями. Отличается только реализация тулов: вместо файловой системы внутри - векторная база данных.
Вот и вся магия космических агентов, которые самодописывают свои промпты, узнают пользователя и помнят все.
#Agents
Память агента - это его возможность самостоятельно получать доступ к информации (загружать в контекст), которая не находится в контексте прямо сейчас, но он знал ее раньше.
Это может быть его личность, информация о пользователе, история предыдущих диалогов, знания о доменной области - что угодно.
С точки зрения реализации, память - это просто набор инструментов, которые позволяют писать, читать и искать по воспоминаниям.
Что-то вроде этого
class Memory:
def write_conversation_memory(summary: str) -> None: ...
def list_conversations() -> list[tuple[UUID, datetime]]: ...
def read_conversation(conversation_id: UUID) -> str: ...
В самом простом случае, память - это просто директория на файловой системе. Например, вот такая (OpenClaw):
memory/
├── PERSONALITY.md // личность агента
├── USER.md // профиль юзера
├── 04_16_2026/ // история диалогов
│ ├── Write_Blogpost.md
│ └── Make_Presentation.md
└── 04_17_2026/
└── Find_NN_Restaurants.md
Имея такую директорию и парочку инструментов для работы с ней, OpenClaw уже умеет:
- самодописывать свой системный промпт (PERSONALITY.md)
- обновлять информацию о своем хозяине (USER.md)
- писать историю диалогов, искать по ним и доставать оттуда факты
К слову, RAG - это точно такой же набор инструментов с точно такими же возможностями. Отличается только реализация тулов: вместо файловой системы внутри - векторная база данных.
Вот и вся магия космических агентов, которые самодописывают свои промпты, узнают пользователя и помнят все.
#Agents
👍11❤7
Сабагенты
Теперь представим такую ситуацию: вы спросили агента "мы на прошлой неделе бронировали ресторан, напомни время". А агент умеет смотреть историю чатов только по дням. И в итоге получается что-то вроде этого:
Контекст в данном случае выглядит следующим образом
Как мы видим, у нас в контексте теперь очень много промежуточной информации. Она нужна, чтобы ответить на вопрос "когда у меня бронь", но в рамках диалога в целом - бесполезна.
Т.е. наш вопрос породил *"подзадачу"* (subtask), выполнять которую желательно в изолированном контексте. Кодинг-агенты часто сталкиваются с такими подзадачами: планирование, запуск тестов, линтеров, ревью - это все мелкие технические шаги, детали которых не так важны в контексте глобальной задачи "ЗАПИЛИ ФИЧУ".
Решение очень простое - пусть подзадачу решает *сабагент*. Т.е. мы просто создаем инструмент, внутри которого будет вызываться другой агент. Пусть он уже дергает инструменты сколько угодно - в основной контекст попадет только финальный результат
Теперь вместо одного зашумленного контекста у нас появилось 2 изолированных
Сабагенты - это самый распространенный паттерн мультиагентного взаимодействия сейчас. Потому что самый простой и при этом достаточно эффективный. Про хореографические паттерны поговорим в другой раз. Но концепция всегда одна и та же - если задачу можно разбить на шаги / подзадачи, которые для своего решения не требуют понимания всего контекста - лучше выделить нескольких агентов. Также сабагенты частично решают проблему overtooling (когда у одного агента слишком много инструментов и он сходит с ума).
Тут важно понимать, что
В рамках этого подконтекста вы можете решать подзадачи вашего текущего диалога без захламления основного контекста.
#Agents
Теперь представим такую ситуацию: вы спросили агента "мы на прошлой неделе бронировали ресторан, напомни время". А агент умеет смотреть историю чатов только по дням. И в итоге получается что-то вроде этого:
User -- "Мы на прошлой неделе выбирали, куда пойти в НН. Что мы там решили?" --> LLM
LLM -- {
"name": "list_memories",
"arguments": '{"date":"04_15_2025"}'
} --> Agentic Framework
LLM <-- [] -- Agentic Framework
LLM -- {
"name": "list_memories",
"arguments": '{"date":"04_16_2025"}'
} --> Agentic Framework
LLM <-- [
"Write_Blogpost", "Make_Presentation"
] -- Agentic Framework
LLM -- {
"name": "list_memories",
"arguments": '{"date":"04_17_2025"}'
} --> Agentic Framework
LLM <-- [
"Find_NN_Restaurants"
] -- Agentic Framework
LLM -- {
"name": "read_file",
"arguments": '{"path":"04_17_2025/Find_NN_Restaurants.md"}'
} --> Agentic Framework
User <-- "Да, это было 17го! Ты решил сходить в Ель и мы забронировали столик на 21:00" -- LLM
Контекст в данном случае выглядит следующим образом
| assistant |
| tool result |
| tool call |
| tool result |
| tool call |
| tool result |
| tool call |
| user |
| tools definitions |
| system |
Как мы видим, у нас в контексте теперь очень много промежуточной информации. Она нужна, чтобы ответить на вопрос "когда у меня бронь", но в рамках диалога в целом - бесполезна.
Т.е. наш вопрос породил *"подзадачу"* (subtask), выполнять которую желательно в изолированном контексте. Кодинг-агенты часто сталкиваются с такими подзадачами: планирование, запуск тестов, линтеров, ревью - это все мелкие технические шаги, детали которых не так важны в контексте глобальной задачи "ЗАПИЛИ ФИЧУ".
Решение очень простое - пусть подзадачу решает *сабагент*. Т.е. мы просто создаем инструмент, внутри которого будет вызываться другой агент. Пусть он уже дергает инструменты сколько угодно - в основной контекст попадет только финальный результат
from autogen.beta import Agent, config, tools
memory_agent = Agent(
"memory-agent",
config=config.OpenAIConfig("gpt-5"),
tools=[
tools.FilesystemToolkit("./memory"),
]
)
def call_subagent(prompt: str) -> str:
return await memory_agent.ask(prompt)
agent = Agent(
"ag-claw",
config=config.OpenAIConfig("gpt-5"),
tools=[
call_subagent,
# memory_agent.as_tool() - или так
]
)
Теперь вместо одного зашумленного контекста у нас появилось 2 изолированных
| assistant | |
| subagent result | assistant |
| | tool result |
| | tool call |
| | tool result |
| | tool call |
| | tool result |
| | tool call |
| subagent call | user |
| user | |
| subagent tools | memory tools |
| claw prompt | subagent prompt |
Сабагенты - это самый распространенный паттерн мультиагентного взаимодействия сейчас. Потому что самый простой и при этом достаточно эффективный. Про хореографические паттерны поговорим в другой раз. Но концепция всегда одна и та же - если задачу можно разбить на шаги / подзадачи, которые для своего решения не требуют понимания всего контекста - лучше выделить нескольких агентов. Также сабагенты частично решают проблему overtooling (когда у одного агента слишком много инструментов и он сходит с ума).
Тут важно понимать, что
Subagent - это не сервис и не модуль. Это просто подконтекст. У него свой системный промпт, своя история. Но модель чаще всего та же.
В рамках этого подконтекста вы можете решать подзадачи вашего текущего диалога без захламления основного контекста.
#Agents
👍15🔥1
Background, Dynamic Subagents
У сабагентов есть и другое классное применение - они могут работать параллельно с основным диалогом. Т.е. основной агент поставил задачу, сабагент ее принял, а диалог пошел дальше. Когда задача завершится - сабагент сам принесет результат и подложит его в основной контекст.
Что-то вроде такого:
В комбинации с тем, что LLM может вызывать сразу несколько инструментов - один запрос может стартануть сразу несколько параллельных подзадач, которые быстро сожрут все ваши токены.
Стоит упомянуть, что тут существует 2 паттерна:
1. сабагент сам приносит результат по готовности
2. сабагент отдает TaskId, а основной агент спрашивает о готовности по этому ID
Какой вариант использовать - зависит от задачи. Поиграйтесь
Ну и последнее, о чем хочется сказать - динамические сабагенты. Это когда модель сама понимает, что вот эту задачу нужно делегировать кому-то, сама генерирует этого "кого-то" и отправляет делать задачу.
Как вы понимаете, это тоже инструмент. В AG2 у нас для этого есть специальный формат спеки, которую мы умеем превращать в агента. Т.е. инструмент имеет 2 аргумента:
- JSON с описанием желаемого агента
- Промпт, с которым его запустить
Таким образом, основной агент превращается в волшебника, который умеет самостоятельно спавнить произвольных сабагентов. Rocket science, не иначе
#Agents
У сабагентов есть и другое классное применение - они могут работать параллельно с основным диалогом. Т.е. основной агент поставил задачу, сабагент ее принял, а диалог пошел дальше. Когда задача завершится - сабагент сам принесет результат и подложит его в основной контекст.
Что-то вроде такого:
| assistant | |
| user | |
| // кладем результат в контекст |
| subagent result | assistant |
| | tool result |
| assistant | tool call |
| user | tool result |
| | tool call |
| | tool result |
| // отпускаем основной контекст |
| subagent called | tool call |
| // делегируем задачу |
| subagent call | user |
| user | |
| subagent tools | memory tools |
| claw prompt | subagent prompt |
В комбинации с тем, что LLM может вызывать сразу несколько инструментов - один запрос может стартануть сразу несколько параллельных подзадач, которые быстро сожрут все ваши токены.
Стоит упомянуть, что тут существует 2 паттерна:
1. сабагент сам приносит результат по готовности
2. сабагент отдает TaskId, а основной агент спрашивает о готовности по этому ID
Какой вариант использовать - зависит от задачи. Поиграйтесь
Ну и последнее, о чем хочется сказать - динамические сабагенты. Это когда модель сама понимает, что вот эту задачу нужно делегировать кому-то, сама генерирует этого "кого-то" и отправляет делать задачу.
Как вы понимаете, это тоже инструмент. В AG2 у нас для этого есть специальный формат спеки, которую мы умеем превращать в агента. Т.е. инструмент имеет 2 аргумента:
- JSON с описанием желаемого агента
- Промпт, с которым его запустить
Таким образом, основной агент превращается в волшебника, который умеет самостоятельно спавнить произвольных сабагентов. Rocket science, не иначе
#Agents
👍8🔥3❤1🍓1
Skills
Скилы нужны для того, чтобы научить агента выполнять узкоспециализированные задачи. Это могут быть скилы по написанию текстов, архитектуре кода, использованию конкретных фреймворков, запуску команд, да и вообще о том, как модели общаться с пользователем.
Яркие примеры:
• rtk
• caveman
• React best practices
Со скилами вы могли сталкиваться, если активно используете кодинг-агентов (Codex / Claude Code / Cursor / OpenCode / etc). В базе своей формула очень проста:
С точки зрения реализации, skill - это директория на системе
-
-
-
Логично, что агент сам по себе не понимает, что такое скилы. Для этого ему нужна парочка инструментов:
Для того, чтобы модель знала, какие скилы у нее в принципе есть, ей в контекст нужно подложить информацию о них (по аналогии с инструментами):
Итого, скилы - это всего лишь:
- метаинформация в контексте
- пара инструментов
- директория на системе
Зато в совокупности это позволяет нам загружать в контекст огромные инструкции для очень специфичных задач по востребованию (а не держать их в контексте всегда).
Проблема тут в том, что после выполнения задачи нам такая большая инструкция в контексте не особо нужна. Да и слишком много скилов тоже регистрировать не стоит (их описание тоже забивает контекст). Но все эти проблемы отлично решаются сабагентами.
Последняя Rocket Science штука здесь - это загрузка нужных инструментов "на лету" из интернета. Что ж, это всего лишь вопрос пары дополнительных инструментов, которые ищут скилы на стоках по API:
Мы просто динамически ищем скилы на https://skills.sh по API, качаем их с гита и ставим в локальную папку. Для этого у нас тоже есть готовый инструмент
Очень темное колдунство, не иначе.
#Agents
Скилы нужны для того, чтобы научить агента выполнять узкоспециализированные задачи. Это могут быть скилы по написанию текстов, архитектуре кода, использованию конкретных фреймворков, запуску команд, да и вообще о том, как модели общаться с пользователем.
Яркие примеры:
• rtk
• caveman
• React best practices
Со скилами вы могли сталкиваться, если активно используете кодинг-агентов (Codex / Claude Code / Cursor / OpenCode / etc). В базе своей формула очень проста:
Skill = Context + ScriptsС точки зрения реализации, skill - это директория на системе
.agents/skills/
└── Pytest_Skill/
├── SKILL.md
└── scripts/
├── run_pytest.sh
└── list_tests.py
-
Pytest_Skill - название скила-
SKILL.md - текстовая простыня, которую мы загрузим в контекст, когда агент захочет научиться работать с pytest-
scripts/ - директория со скриптами (python / shell), правила использования которых описаны в SKILL.mdЛогично, что агент сам по себе не понимает, что такое скилы. Для этого ему нужна парочка инструментов:
class SkillsToolkit:
def load_skill(skill_id: str) -> str: ...
def run_skill_script(skill_id: str, script: str) -> str: ...
Для того, чтобы модель знала, какие скилы у нее в принципе есть, ей в контекст нужно подложить информацию о них (по аналогии с инструментами):
[{
"name": "Pytest_Skill",
"description": "Use this skill to test your python code",
... // всякие бесполезные поля
}]
| assistant |
| script result |
| run script | // исполнение скрипта из набора скила
| skill content |
| load skill | // загрузка конкретного скила в контекст
| user |
| tools definitions |
| skills metadata | // информация о доступных скилах
| system |
Итого, скилы - это всего лишь:
- метаинформация в контексте
- пара инструментов
- директория на системе
Зато в совокупности это позволяет нам загружать в контекст огромные инструкции для очень специфичных задач по востребованию (а не держать их в контексте всегда).
Проблема тут в том, что после выполнения задачи нам такая большая инструкция в контексте не особо нужна. Да и слишком много скилов тоже регистрировать не стоит (их описание тоже забивает контекст). Но все эти проблемы отлично решаются сабагентами.
Последняя Rocket Science штука здесь - это загрузка нужных инструментов "на лету" из интернета. Что ж, это всего лишь вопрос пары дополнительных инструментов, которые ищут скилы на стоках по API:
class SkillSearchToolkit:
async def search_skills(query: str, limit: int = 10) -> str: ...
async def install_skill(skill_id: str) -> str: ...
Мы просто динамически ищем скилы на https://skills.sh по API, качаем их с гита и ставим в локальную папку. Для этого у нас тоже есть готовый инструмент
autogen.beta.tools.SkillSearchToolkitОчень темное колдунство, не иначе.
#Agents
GitHub
GitHub - rtk-ai/rtk: CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies
CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies - rtk-ai/rtk
❤13🙈1
AI не заменит разрабов, можно расслабить булки
https://vz.ru/news/2026/4/26/1413853.html - нашел оч смешную новость. Компании жалуются, что расходы на токены и так уже достигли размеров ФОТ сотрудников. Кожаные мешки оказались дешевле цифровых. А Uber вообще уже потратил все бабки на AI до конца года
Я это не фактчекал, но на правду похоже. Сам пару раз видел чеки на 500$+ в день на вайбкодинг
https://vz.ru/news/2026/4/26/1413853.html - нашел оч смешную новость. Компании жалуются, что расходы на токены и так уже достигли размеров ФОТ сотрудников. Кожаные мешки оказались дешевле цифровых. А Uber вообще уже потратил все бабки на AI до конца года
Я это не фактчекал, но на правду похоже. Сам пару раз видел чеки на 500$+ в день на вайбкодинг
😁15✍3❤1🎉1
Интеграции Агента с мессенджером
Основная (и единственная) проблема при интеграции агента с любым UI - это то, как менеджить разные контексты.
Это могут быть разные чаты / топики в мессенджере или явные команды, что текущий диалог завершен и пора начинать новый.
А если у тебя агент рассчитан на несколько пользователей, то нужно еще разграничивать их контексты и не забыть про безопасность.
Но эти задачи не какие-то особенные для агентной разработки. Любой веб-разработчик делал что-то такое и, я уверен, вы тоже справитесь.
В помощь могу предложить разве что вот такой код:
Чуть более развернутый пример я уже описывал в блоге AG2 - там пример с историей диалогов и переключением между ними
Но, я уверен, вы без труда справитесь с такой интеграцией. Если же вы хотите написать Web приложение, то советую посмотреть на фичи AG-UI протокола - там уже есть готовые фреймворки и на фронтенде. Про AG-UI я писал даже статью на Habr, но если все еще ничего не понятно, могу попробовать объяснить еще раз😅
#Agents
Основная (и единственная) проблема при интеграции агента с любым UI - это то, как менеджить разные контексты.
Это могут быть разные чаты / топики в мессенджере или явные команды, что текущий диалог завершен и пора начинать новый.
А если у тебя агент рассчитан на несколько пользователей, то нужно еще разграничивать их контексты и не забыть про безопасность.
Но эти задачи не какие-то особенные для агентной разработки. Любой веб-разработчик делал что-то такое и, я уверен, вы тоже справитесь.
В помощь могу предложить разве что вот такой код:
from autogen.beta import Agent, config, MemoryStream
agent = Agent("tg-agent", config=config.OpenAIConfig("gpt-5"))
dp = Dispatcher()
chat_state: dict[int, MemoryStream] = {}
@dp.message(F.text)
async def on_text(message: Message) -> None:
# получаем старый контекст или создаем новый
if not (stream := chat_state.get(message.chat.id)):
stream = chat_state[message.chat.id] = MemoryStream()
# дергаем агента с этим контекстом
reply = await agent.ask(
message.text,
stream=stream,
variables={"user_id": message.chat.id},
)
# отвечаем в TG чат
await message.answer(reply.content)
asyncio.run(dp.start_polling(bot))
Чуть более развернутый пример я уже описывал в блоге AG2 - там пример с историей диалогов и переключением между ними
Но, я уверен, вы без труда справитесь с такой интеграцией. Если же вы хотите написать Web приложение, то советую посмотреть на фичи AG-UI протокола - там уже есть готовые фреймворки и на фронтенде. Про AG-UI я писал даже статью на Habr, но если все еще ничего не понятно, могу попробовать объяснить еще раз😅
#Agents
👍9🙊1
Фоновые задачи
Самая последняя фича, за которую хвалят OpenClaw - возможность сказать что-то типа "мониторь сайт авиабилетов каждый час и покупай сразу, как они появятся в продаже".
Для этого агенту нужна поддержка cron-like задач и возможность регистрации новых...
Нужели опять инструменты? - конечно😂
Но для начала нам нужен какой-нибудь рантайм для выполнения задач по расписанию. Пусть это выглядит так (псевдокод):
Ну и, конечно, нам нужны инструменты для того, чтобы записывать новые задачи в этот рантайм
К сожалению, готовой батарейки в AG2 нет (пока что), но довольно скоро должна появиться. В любом случае, я уверен, что такие опытные разработчики справятся с этой трудной задачей и самостоятельно
#Agents
Самая последняя фича, за которую хвалят OpenClaw - возможность сказать что-то типа "мониторь сайт авиабилетов каждый час и покупай сразу, как они появятся в продаже".
Для этого агенту нужна поддержка cron-like задач и возможность регистрации новых...
Нужели опять инструменты? - конечно😂
Но для начала нам нужен какой-нибудь рантайм для выполнения задач по расписанию. Пусть это выглядит так (псевдокод):
import asyncio
from autogen.beta import Agent, config
cron = Scheduler()
agent = Agent("tg-agent", config=config.OpenAIConfig("gpt-5"))
async def main():
asyncio.create_task(cron.run())
await agent.ask("Check tickets each hour")
Ну и, конечно, нам нужны инструменты для того, чтобы записывать новые задачи в этот рантайм
class SchedulerToolkit:
def __init__(self, cron: Scheduler):
self.cron = cron
def schedule_task(task_prompt: str, cron: str) -> UUID: ...
def remove_task(task_id: UUID) -> None: ...
def list_tasks() -> list[UUID]: ...
agent = Agent(..., tools=[SchedulerToolkit(cron)])
К сожалению, готовой батарейки в AG2 нет (пока что), но довольно скоро должна появиться. В любом случае, я уверен, что такие опытные разработчики справятся с этой трудной задачей и самостоятельно
#Agents
👍8❤3
Что ж, цикл закончен - можете включать уведомления обратно😂
Переходим на режим постинга +- 2 раза в неделю
Зато теперь у меня есть базовый материал про разработку агентов. Когда я буду рассказывать про более сложные вещи - будет на что ссылаться🌚
Чтобы этот материал оставался для вас полезным, вы могли к нему возвращаться (и отправлять друзьям), сделаю навигацию:
- Общий принцип работы агентов
- Устройство контекста и его проблемы
- Зачем нужны и как работают инструменты
- Как устроена память агента
- Кто такие сабагенты
- Зачем их запускать в фоне
- Что такое Skills и как они устроены
- Как запихнуть AI-агента в Telegram
- Как заставить агента работать по расписанию
Если тема с устройством агентов вам заходит, в ближайшее я накидаю еще больше материала для погружения в детали.
Все относящееся к теме, я также буду маркировать тегом #Agents - ищите по нему
Переходим на режим постинга +- 2 раза в неделю
Зато теперь у меня есть базовый материал про разработку агентов. Когда я буду рассказывать про более сложные вещи - будет на что ссылаться🌚
Чтобы этот материал оставался для вас полезным, вы могли к нему возвращаться (и отправлять друзьям), сделаю навигацию:
- Общий принцип работы агентов
- Устройство контекста и его проблемы
- Зачем нужны и как работают инструменты
- Как устроена память агента
- Кто такие сабагенты
- Зачем их запускать в фоне
- Что такое Skills и как они устроены
- Как запихнуть AI-агента в Telegram
- Как заставить агента работать по расписанию
Если тема с устройством агентов вам заходит, в ближайшее я накидаю еще больше материала для погружения в детали.
Все относящееся к теме, я также буду маркировать тегом #Agents - ищите по нему
Telegram
FastNews | Никита Пастухов
Как работает агент
Начнем с очень базового вопроса - "что такое AI-АГЕНТ?"
Проблема в том, что никто точно не знает. Или просто я спрашивал не тех людей. Общепринятого формального определения нет. Но все, кто занимается разработкой агентов, +- интуитивно…
Начнем с очень базового вопроса - "что такое AI-АГЕНТ?"
Проблема в том, что никто точно не знает. Или просто я спрашивал не тех людей. Общепринятого формального определения нет. Но все, кто занимается разработкой агентов, +- интуитивно…
❤24👍2🔥1🍓1🫡1
Тут товарищ @GregorySeifu из комьюнити выпустил статью как, почему и зачем они мигрировали: Celery -> aio-pika -> FastStream
Спасибо ему большое за материал. Думаю, вам эта история тоже будет интересна 🙂
https://habr.com/ru/articles/1030082/
Спасибо ему большое за материал. Думаю, вам эта история тоже будет интересна 🙂
https://habr.com/ru/articles/1030082/
Хабр
Как мы переписывали логику очередей: Celery => aio-pika => FastStream
Наш путь активной работы с очередями RabbitMQ начался с классического Celery. Осознав критичность низкоуровневого контроля системы, принялись работать с aio-pika. Но и этот уровень слишком местами...
👍14🔥1
Elon Mask VS OpenAI
Сейчас многие заголовки гремят в стиле "Маск признался, что модели xAI дистиллировали OpenAI". Звучит хайпово (ведь подлый Маск настолько завидует OpenAI, что пытается скопировать их модели), но это и правда стандартная практика
Напомню, что Claude Sonnet 4.5 на старте говорил, что "Я - DeepSeek", DeepSeek говорил "Я - GPT", а сами Anthropic выкатывали кучу претензий к лабам, которые дистиллируют их модели. Так что не будем заострять на этом внимание
Но меня привлек сам факт конфликта - я как-то упустил, что за махач там происходит. А это действительно интересно
TL;DR: Маск занес 38кк$ в OpenAI в период 2015-2020 как пожертвование некоммерческой организации. С тех пор OpenAI приняли уже 13ккк$ инвестиций от Microsoft, организовало коммерческое подразделение и готовятся выйти на IPO (оценка 1кккк$, к слову). Маска бесит, что его оставляют за бортом AI движухи и он выдвигает дикие требования - "OpenAI меня обманули. Они больше не OpenSource, поэтомупусть вернут день обратно (это было бы логично) пусть OpenAI уволит Альтмана и Брокмана (соучредитель и президент OpenAI), поменяет организационную структуру на НКО и переведет 130ккк$ на ее баланс. А Microsoft пусть просто утрется
А вот такого отчета я добился от Claude после кучи уточняющих вопросов
Сейчас многие заголовки гремят в стиле "Маск признался, что модели xAI дистиллировали OpenAI". Звучит хайпово (ведь подлый Маск настолько завидует OpenAI, что пытается скопировать их модели), но это и правда стандартная практика
Напомню, что Claude Sonnet 4.5 на старте говорил, что "Я - DeepSeek", DeepSeek говорил "Я - GPT", а сами Anthropic выкатывали кучу претензий к лабам, которые дистиллируют их модели. Так что не будем заострять на этом внимание
Но меня привлек сам факт конфликта - я как-то упустил, что за махач там происходит. А это действительно интересно
TL;DR: Маск занес 38кк$ в OpenAI в период 2015-2020 как пожертвование некоммерческой организации. С тех пор OpenAI приняли уже 13ккк$ инвестиций от Microsoft, организовало коммерческое подразделение и готовятся выйти на IPO (оценка 1кккк$, к слову). Маска бесит, что его оставляют за бортом AI движухи и он выдвигает дикие требования - "OpenAI меня обманули. Они больше не OpenSource, поэтому
А вот такого отчета я добился от Claude после кучи уточняющих вопросов
Маск против OpenAI: что происходит в суде
На этой неделе в федеральном суде Окленда начался суд присяжных — Илон Маск против OpenAI, Сэма Альтмана, Грега Брокмана и Microsoft. Маск провел на свидетельской трибуне три дня (28–30 апреля) под жестким перекрестным допросом. Заседания возобновляются в понедельник, присяжные начнут совещаться примерно к 12 мая.
Суть претензий
Из 26 изначальных пунктов до присяжных добрались только два: нарушение благотворительного траста и неосновательное обогащение.
Логика Маска следующая: 38 миллионов долларов, которые он пожертвовал OpenAI как некоммерческой организации в 2015–2020 годах, юридически создали траст — компания обязалась навсегда остаться некоммерческой и open-source. Превращение в коммерческую структуру стоимостью ~850 миллиардов он называет «кражей благотворительности». Присяжным он объяснил проще: это дело «о защите благотворительности в Америке в целом».
Чего он хочет
Маск хочет:
- вернуть OpenAI к некоммерческой структуре
- отстранить Альтмана и Брокмана от руководства
- взыскать компенсацию в 130+ миллиардов долларов
Важная деталь: деньги он требует не себе, а в пользу некоммерческой части OpenAI.
Позиция OpenAI
Адвокат Альтмана Уильям Сэвитт во вступительном слове сформулировал ее просто и без прикрас: «Мы здесь потому, что мистер Маск не добился своего».
OpenAI утверждает, что сам Маск предлагал коммерческую структуру еще в 2015–2017 годах, ушел из совета директоров после провала попытки слить OpenAI с Tesla, а иск подал лишь в 2024-м — ровно через год после запуска xAI. На допросе выяснилась и вовсе неловкая деталь: Маск «не читал мелкий шрифт» term sheet 2018 года, где прямо упоминался план привлечь 10 миллиардов от Microsoft.
При чем тут Microsoft
Маск обвиняет Microsoft в пособничестве нарушению траста — ее 13+ миллиардов долларов инвестиций и 27%-я доля в коммерческой части OpenAI (стоимостью ~135 миллиардов) сделали «коммерческий захват» миссии возможным.
Microsoft отвечает на трёх основаниях: иск просрочен (Маск ещё в 2020-м публично писал, что «OpenAI захвачена Microsoft»), компания не знала ни о каком нарушении, а реальный мотив — конкуренция через xAI, а не забота о благотворительности.
Что на кону
Если Маск выиграет — IPO OpenAI (потенциальная оценка до 1 триллиона) может сорваться, Альтман и Брокман рискуют потерять позиции.
Юристы, впрочем, скептичны: суд по частному иску о благотворительном трасте крайне редко принимает решения о структурной перестройке компаний такого масштаба. Так что исход этой истории, вероятно, куда менее драматичен, чем сама постановка.
😁9❤1🤯1
Мои Джедайские техники продуктивности
В 2025 году перечитал "Джедайские техники" и собрал свою выжимку - про мыслетопливо, обезьяну, прокрастинацию, формулирование задач.
Получилось насколько постов, в которых я скрещивал теорию Дорофеева со своей практикой.
1. Тренируем заставлялку
2. Делаем через жопу
3. Основные проблемы с продуктивностью
4. Дела по 30 минут
5. Как расти по карьерной лестнице
6. Моя продуктивность
7. Что я изменил в своей системе
8. Эффективность VS Продуктивность
9. И еще раз о жопе
10. Эффективная Модель Кано
Тема эффективности мне всегда интересна, поэто я стараюсь изучать как можно больше материалов, отделяя их от инфоцыганства.
И все пропускаю через собственный опыт. Так что не надейтесь быстро от меня отвязаться... Я еще про вторую книгу Дорофеева ничего не писал🌚
#продуктивность #дорофеев
В 2025 году перечитал "Джедайские техники" и собрал свою выжимку - про мыслетопливо, обезьяну, прокрастинацию, формулирование задач.
Получилось насколько постов, в которых я скрещивал теорию Дорофеева со своей практикой.
1. Тренируем заставлялку
2. Делаем через жопу
3. Основные проблемы с продуктивностью
4. Дела по 30 минут
5. Как расти по карьерной лестнице
6. Моя продуктивность
7. Что я изменил в своей системе
8. Эффективность VS Продуктивность
9. И еще раз о жопе
10. Эффективная Модель Кано
Тема эффективности мне всегда интересна, поэто я стараюсь изучать как можно больше материалов, отделяя их от инфоцыганства.
И все пропускаю через собственный опыт. Так что не надейтесь быстро от меня отвязаться... Я еще про вторую книгу Дорофеева ничего не писал🌚
#продуктивность #дорофеев
Telegram
FastNews | Никита Пастухов
Прошло уже 5 лет, как я читал Максима Дорофеева и его "Джедайские техники" последний раз. И вот, вся "эффективность" выветрилась, я стал больше прокрастинировать, а выгорание уже дышит в спину...
Поэтому сейчас я сел перечитывать эту великолепную книгу (а…
Поэтому сейчас я сел перечитывать эту великолепную книгу (а…
❤5🔥2👍1😁1
OpenSource глазами мейнтейнера
Несколько разборов про разработку и продвижение OSS-проектов: от практических чеклистов до экономики (или её отсутствия).
– Гайд по OpenSource на GitHub — как стартовать, что важно, что не важно
– Полный гайд по OSS на GitHub — продолжение, более глубоко (в комменты тоже зайдите)
– Как зарабатывать на OpenSource — спойлер: никак
– Вайбкодеры в OSS — моя сгоревшаяя жопа
Серия не закрыта - буду докидывать новые истории по мере накопления. Тренд на AI захватил GitHub и, чую, скоро меня снова бомбанет
#opensource
Несколько разборов про разработку и продвижение OSS-проектов: от практических чеклистов до экономики (или её отсутствия).
– Гайд по OpenSource на GitHub — как стартовать, что важно, что не важно
– Полный гайд по OSS на GitHub — продолжение, более глубоко (в комменты тоже зайдите)
– Как зарабатывать на OpenSource — спойлер: никак
– Вайбкодеры в OSS — моя сгоревшаяя жопа
Серия не закрыта - буду докидывать новые истории по мере накопления. Тренд на AI захватил GitHub и, чую, скоро меня снова бомбанет
#opensource
Telegram
FastNews | Никита Пастухов
Гайд по OpenSource
Как-то я совсем профукал, что у Github есть ГАЙДИЩЕ по OpenSource – https://github.com/github/opensource.guide
Там есть клевые статьи о том:
– как контрибутить
– как запустить свой OSS
– как мейнтейнерам не выгорать
– как искать пользователей…
Как-то я совсем профукал, что у Github есть ГАЙДИЩЕ по OpenSource – https://github.com/github/opensource.guide
Там есть клевые статьи о том:
– как контрибутить
– как запустить свой OSS
– как мейнтейнерам не выгорать
– как искать пользователей…
🔥5👍3
FastNews | Никита Пастухов
Навигация по каналу Тут уже слишком много постов, поэтому собрал самое важное для вас в одном месте. 📚 Серии и циклы Большие темы, разобранные за несколько постов. У каждой серии — пост-саммари со всеми ссылками: – Агенты под капотом — посты про устройство…
Рабочая навигация
В канале накопилось так много постов, что я сам уже не понимаю, кто это вообще все написал😅 Да и людей тут значительно прибавилось - не все из присутствующих видели все грани моего безумия🌚
Поэтому подбил для вас навигацию:
📍О канале и обо мне - для тех, кто только подписался
📍Навигация - серии, лучшее по темам, хэштеги для фильтрации. Я прошелся по ВСЕМУ каналу и починил хештеги - теперь они имеют смысл
И ряд отдельных постов - подборок с навигацией по конкретным постам в серии.
- Агенты
- Опенсорс
- Продуктивность
Если давно подписаны и не помните, что тут было - теперь и вам удобно покопаться😅 Я сам нашел много интересного, о чем совсем забыл...
Например, мой любимый пост (про Винни-Пуха)
В канале накопилось так много постов, что я сам уже не понимаю, кто это вообще все написал😅 Да и людей тут значительно прибавилось - не все из присутствующих видели все грани моего безумия🌚
Поэтому подбил для вас навигацию:
📍О канале и обо мне - для тех, кто только подписался
📍Навигация - серии, лучшее по темам, хэштеги для фильтрации. Я прошелся по ВСЕМУ каналу и починил хештеги - теперь они имеют смысл
И ряд отдельных постов - подборок с навигацией по конкретным постам в серии.
- Агенты
- Опенсорс
- Продуктивность
Если давно подписаны и не помните, что тут было - теперь и вам удобно покопаться😅 Я сам нашел много интересного, о чем совсем забыл...
Например, мой любимый пост (про Винни-Пуха)
Telegram
FastNews | Никита Пастухов
Привет, давайте знакомиться!
Для тех, кто не знает, я:
• Никита
• Python (и не только) разработчик c 8летним опытом
• автор и мейнтейнер фреймворка FastStream
• мейнтейнер фреймворка AG2
• контрибутор многих других OpenSource проектов
• немного автор на…
Для тех, кто не знает, я:
• Никита
• Python (и не только) разработчик c 8летним опытом
• автор и мейнтейнер фреймворка FastStream
• мейнтейнер фреймворка AG2
• контрибутор многих других OpenSource проектов
• немного автор на…
👍4
Жесть, эти нейро-комментаторы так прокачались, что автоматом ищут наиболее релевантный пост, куда воткнуть свою рекламу🤯 Да и текст все более похож на человеческий
Для тех, кто не понял, что на картинке - некая Юля сначала воткнула рекламу какого-то тг бота под последним сообщением. А потом ТУТ ЖЕ удалила его и воткнула другой коммент под другим постом, где он выглядит уместнее
В общем, мне все тяжелее отличать живых людей от слопа🥲 Хотя, все достаточно просто - живые люди не комментируют😂
Для тех, кто не понял, что на картинке - некая Юля сначала воткнула рекламу какого-то тг бота под последним сообщением. А потом ТУТ ЖЕ удалила его и воткнула другой коммент под другим постом, где он выглядит уместнее
В общем, мне все тяжелее отличать живых людей от слопа🥲 Хотя, все достаточно просто - живые люди не комментируют😂
😁11👍5😎1
К слову, о недавно вышедшей gpt-realtime-2 - она действительно круто работает. Сейчас работаю над поддержкой LiveAPI в AG2 - с ним вы можете коммуницировать с агентом голосом всего в пару строк + получаете всю мощь инструментов, сабагентов, Skills и прочих встроенных фишек AG2
Я даже немного подтянул произношение английского, пока дебажил эту интеграцию😂 - это мой сценарий, я прошу модель поправлять мое произношение
Релиз
Еще есть что доделать, но это, в основном, всякие мелочи и нюансы. Пока поддерживает OpenAI и Gemini Live API (не знаю, у кого они еще есть), но параллельно с этим я делаю еще и API для STT + TTS кейсов - а там уже выбор пошире - будет коллаба с 11 Labs и Seedance. А у нас в России можно попробовать прикрутить Сберовский GigaChat как STT - я слышал, он хорошо работает с русским
В общем, если вы ищете агентный фреймворк, который отлично поддерживает еще и Live сценарии - то такое у нас тоже вот-вот будет🌚
#Agents
Я даже немного подтянул произношение английского, пока дебажил эту интеграцию😂 - это мой сценарий, я прошу модель поправлять мое произношение
import asyncio
from autogen.beta.live import (
LiveAgent,
SoundDevicePlayer,
SoundDeviceRecorder,
openai,
)
agent = LiveAgent(
name="assistant",
prompt="You are a helpful voice assistant.",
config=openai.RealTimeConfig("gpt-realtime-2"),
)
async def main() -> None:
async with (
agent.run() as context,
SoundDevicePlayer(context=context),
SoundDeviceRecorder(context=context),
):
print("Starting...")
await asyncio.Future()
if __name__ == "__main__":
asyncio.run(main())
Релиз
0.13 будет уже вот-вот, так что на днях сможете тоже поиграться с голосовыми агентами, если интересно. Вообще, релиз обещает быть жирным - там и LiveAgents, и Agents Workflow, и A2A, и MCP. Так получается, что все закрыли свои таски +- одновременно😂 Так что релиз застрял на этапе ревьюЕще есть что доделать, но это, в основном, всякие мелочи и нюансы. Пока поддерживает OpenAI и Gemini Live API (не знаю, у кого они еще есть), но параллельно с этим я делаю еще и API для STT + TTS кейсов - а там уже выбор пошире - будет коллаба с 11 Labs и Seedance. А у нас в России можно попробовать прикрутить Сберовский GigaChat как STT - я слышал, он хорошо работает с русским
В общем, если вы ищете агентный фреймворк, который отлично поддерживает еще и Live сценарии - то такое у нас тоже вот-вот будет🌚
#Agents
OpenAI
Advancing voice intelligence with new models in the API
Explore new realtime voice models in the OpenAI API that can reason, translate, and transcribe speech, enabling more natural and intelligent voice experiences.
🔥10😨4👍3❤1