Коротко
В первых текстах про Personal Task Assistant я писал про базовую идею:
не человек должен каждый раз думать, что можно поручить AI. Система задач должна сама показывать, какая работа уже готова для агента.
С тех пор проект сильно изменился.
Изначально это была доска задач с человеческой и агентской очередью. Сейчас это уже более практичный контур:
-
задачи можно безопасно забирать через атомарный
claim; -
повторный импорт из почты, Telegram или другого источника не создает дубли;
-
агент может работать через MCP, а не через скрейпинг UI;
-
MCP-инструменты проверяются отдельным eval-процессом;
-
для будущих Codex/Claude-сессий появились playbooks в
AGENTS.mdи.claude/skills/; -
после явной установки launchd-расписания ежедневный ритуал в 15:00 пишет digest, а при явном включении дает Claude ограниченный набор инструментов, чтобы он сам двигал Codex-задачи до
waiting_review.
То есть проект ушел от “вот список задач для AI” к более важному вопросу:
как сделать так, чтобы агент мог приходить в очередь каждый день, брать работу и не ломать операционный контроль человека.
Что было проблемой после первой версии
Первая версия решала маршрутизацию:
-
это должен сделать человек;
-
это может взять агент;
-
это ждет ревью;
-
это заблокировано;
-
это еще не разобрано.
Но когда начинаешь реально жить с такой системой, быстро появляются следующие проблемы.
Первая: агенту нельзя просто дать весь список задач. Ему нужен конкретный контракт: что читать, что брать, как зафиксировать результат и когда остановиться.
Вторая: нельзя доверять только промпту. Если в промпте написано “не закрывай задачи”, это полезно, но недостаточно. Ограничение должно быть в коде.
Третья: интеграции будут переигрывать одни и те же события. Telegram polling, webhooks, будущие Gmail/Slack/Jira-адаптеры и scheduled jobs почти всегда когда-нибудь повторят один и тот же источник. Значит, ingest должен быть идемпотентным.
Четвертая: если MCP-инструменты плохо описаны, агент начинает ошибаться не потому, что модель плохая, а потому что контракт неочевидный. Это нужно проверять отдельно.
Атомарный claim вместо “возьми первую задачу из списка”
Главное изменение в 0.4.0: появился POST /api/agent/claim.
Агент больше не должен делать так:
-
прочитать список;
-
выбрать первую задачу;
-
надеяться, что никто другой ее не взял.
Теперь он вызывает claim endpoint, а сервер сам выбирает верхнюю Codex-owned задачу в backlog, условно переводит ее в in_progress и возвращает агенту.
Это важно даже для личного проекта. Как только появляются расписания, параллельные агенты или ручные запуски из разных окон, состояние “оба взяли одну задачу” становится реальным багом.
В SQLite это сделано условным UPDATE, в Firestore – транзакцией. Если другой процесс успел забрать задачу раньше, текущий runner просто получает следующую или 404, когда брать нечего.
Идемпотентный ingest через external_id
Вторая практичная доработка – external_id.
У задачи может быть стабильный внешний идентификатор, например:
telegram:<chat_id>:<message_id>:<line>
jira:PROJ-42
gmail:<thread_id>:<message_id>:<n>
Если адаптер отправит тот же batch второй раз, система не создаст копии. В context ingest такие задачи вернутся в duplicates, а прямое создание одной задачи с тем же external_id даст conflict.
Для трекера это скучная, но критичная вещь. Без нее любая автоматизация из почты или чатов быстро превращается в генератор дублей.
MCP: агентский контракт стал нативным
Следующий шаг был в 0.5.0: MCP server.
До этого агент мог пользоваться JSON API напрямую. Это работает, но для Claude Code, Claude Desktop и других MCP-клиентов естественнее дать набор инструментов.
Сейчас MCP server в adapters/task_assistant_mcp.py оборачивает API в девять инструментов:
-
task_assistant_get_queue; -
task_assistant_queue_summary; -
task_assistant_claim_task; -
task_assistant_finish_task; -
task_assistant_create_task; -
task_assistant_update_task; -
task_assistant_list_tasks; -
task_assistant_ingest_context; -
task_assistant_due_reminders.
Нормальный цикл стал таким:
get_queue / queue_summary
-> claim_task
-> выполнить работу
-> finish_task
-> waiting_review
Ключевое здесь – последняя строка. Агент не должен по умолчанию закрывать задачу в done. Если он подготовил draft, внес изменения, собрал выводы или нашел блокер, это результат для review. Человек остается владельцем принятия.
Evals: проверяем не модель, а контракт
После MCP появилась новая проблема: как понять, что агент действительно сможет пользоваться этими инструментами?
Для этого в 0.5.1 появился evals/.
Там есть:
-
deterministic eval board из 16 задач;
-
10 read-only вопросов с однозначными ответами;
-
бесплатная проверка
verify_answers.py, которая решает вопросы через MCP без LLM; -
опциональный LLM harness, который дает Claude только MCP server и смотрит, как он справляется;
-
write-loop check, который прогоняет
claim -> in_progress -> finish -> waiting_review.
Это важный сдвиг в мышлении. Я не проверяю абстрактно “умная ли модель”. Я проверяю, достаточно ли понятен и надежен мой tool surface.
Если eval падает, чаще всего проблема не в интеллекте модели, а в том, что:
-
инструмент плохо назван;
-
описание недостаточно конкретное;
-
markdown output неудобен для агента;
-
ошибка не объясняет следующий шаг;
-
API допускает неоднозначное состояние.
Playbooks: опыт разработки стал частью репозитория
Еще одна вещь, которая появилась после нескольких итераций с Codex и Claude, – это AGENTS.md и .claude/skills/.
Это не просто документация для человека. Это операционный контракт для будущих агентов.
Например, если агент собирается:
-
работать с доской как worker;
-
менять daily automation;
-
трогать MCP tools;
-
менять схему БД;
-
shipping/release flow,
он сначала должен открыть соответствующий SKILL.md.
Это решает очень практичную проблему: без таких playbooks каждый новый агент заново открывает одни и те же ловушки. С ними знание о том, как безопасно работать с проектом, лежит рядом с кодом.
Для меня это стало отдельным выводом: если проект должен развивать не один человек, а человек плюс несколько AI-агентов, то repo docs должны быть написаны не только для людей. Они должны быть executable memory для агентов.
Ежедневный ритуал: второй работник приходит на смену
Самое заметное изменение в 0.6.0 – automation/. Расписание не включается само после обновления: его нужно осознанно установить через automation/install.sh.
Там два слоя.
Первый слой всегда deterministic:
daily_ritual.py
Он читает доску, классифицирует задачи и пишет digest:
-
overdue;
-
waiting for review;
-
blocked;
-
unassigned;
-
due soon;
-
agent-ready queue.
Этот слой ничего не меняет в трекере. Он просто каждый день дает картину:
что сейчас требует внимания.
Второй слой opt-in:
work_loop.py
Если включить DAILY_RITUAL_WORK=1 и передать ANTHROPIC_API_KEY, то Claude получает только task_assistant_* MCP tools и worker-инструкции.
Важно, что ограничения здесь не только в prompt:
-
у модели нет shell;
-
нет доступа к repo;
-
нет web;
-
только MCP tools трекера;
-
попытка поставить
status=doneотклоняется в коде; -
--max-tasksограничивает, сколько задач можно claim-нуть за один запуск.
То есть daily worker может прийти, взять несколько Codex-задач, сделать то, что реально можно сделать только через task tools, и вернуть результат в waiting_review. Но он не может сам закрыть работу за человека.
Почему я считаю это правильной границей
Есть соблазн сделать “полного автономного агента”, который сам ходит в shell, браузер, почту, GitHub, Slack и закрывает задачи.
Но для личного операционного трекера мне важнее другая модель:
core tracker = источник правды
MCP tools = ограниченный контракт
daily ritual = расписание и digest
human review = финальное принятие
Такой агент менее эффектен в демо, зато его проще оставить без присмотра.
Он не делает вид, что у него есть доступы, которых нет. Он не закрывает задачу сам. Если ему не хватает данных, он должен оставить блокер или вопрос.
Что изменилось в инженерном качестве
Параллельно с продуктовой логикой проект стал заметно взрослее:
-
SQLite schema теперь идет через Alembic migrations, а не
create_all; -
есть pytest suite;
-
есть Ruff;
-
CI гоняет lint и tests;
-
Local Mode стал безопаснее: loopback-only auto-login;
-
Docker image запускается non-root user;
-
Firestore reads оптимизированы, чтобы не читать
done/cancelledbacklog при polling; -
Google Sheets history writes вынесены из request path в background tasks.
Это не самые громкие изменения, но именно они превращают toy project в штуку, которую можно запускать каждый день.
Что я бы сформулировал как принцип после третьей итерации
Первая версия отвечала на вопрос:
какие задачи можно отдать AI?
Текущая версия отвечает на более операционный вопрос:
как дать агенту регулярный, ограниченный и проверяемый способ двигать задачи, не забирая контроль у человека?
Мой текущий ответ:
-
статусы и next-action owner в модели задачи;
-
атомарный claim;
-
waiting_reviewкак стандартный финал агентской работы; -
идемпотентный ingest;
-
MCP как native tool layer;
-
evals для проверки tool surface;
-
playbooks как память проекта для будущих агентов;
-
deterministic digest отдельно от opt-in agent work loop.
Что дальше
На версии 0.6.0 работа не остановилась. Следующие изменения я опишу отдельно, чтобы не смешивать несколько релизов в одной статье.
Следующие интересные направления:
-
user-built адаптеры для Gmail, Slack, Linear/Jira и более production-ready Telegram pattern;
-
более явное объяснение, почему задача считается agent-ready;
-
отчеты агента как отдельные first-class поля, а не только описание;
-
более удобный hosted setup;
-
шаблоны для разных рабочих ритмов: solo founder, PM, coding-agent workflow.
Исходники: https://github.com/J3d1-fm/Personal-Task-Assistant
Если вы уже используете Codex, Claude Code или других агентов не разово, а как часть рабочего процесса, мне особенно интересно: где у вас сейчас граница между “агент может взять сам” и “человеку нужно принять решение”?
Автор: J3d1fm


