Учебный агент без агентных фреймворков: прямой DeepSeek API, история
сообщений и три инструмента - read_file, bash, edit. Модель выбирает
действие, агент выполняет его и передает результат в следующем запросе.
Пример показывает этот обмен, а не оценивает надежность модели.
Задача - найти причину падающего теста доставки, изменить реализацию и проверить ее тестами. По настройкам доставка бесплатна от 2000 рублей, но заказ на 2500 рублей получает стоимость 300 вместо 0. Причина намеренно заложена в загрузку настроек по относительному пути. Исходный кейс остается с ошибкой; каждый запуск агента работает со своей копией.
- Linux, Python 3.10+ и стандартная библиотека; проверено на Python 3.12.3.
- Системный Python в
/usr/bin:/bin,/usr/bin/bashи Bubblewrap (bwrap). - Обычные Unix-утилиты:
ls,cat,grep,rm,sleep,setsid. - Работающие namespaces: одного установленного
bwrapнедостаточно. При недоступной песочнице команды на хосте не выполняются.
Сторонние Python-пакеты для запуска не нужны. В Debian/Ubuntu системные зависимости можно установить командой:
sudo apt install python3 bash bubblewrap coreutils grep util-linux gitgit clone https://github.com/stemirkhan/react-agent-demo.git
cd react-agent-demo
python3 -m unittest discover -s tests -v
python3 -m react_demo caseВсе дальнейшие команды выполняются из корня репозитория. pyproject.toml
содержит только настройки необязательных Black и mypy; pip install .
не требуется. Windows и macOS для запуска этого демо не поддерживаются.
Начните с Agent.run(): история сообщений -> ответ модели -> инструмент -> результат в историю -> новое обращение к модели. Непустой ответ без вызовов инструментов завершает цикл.
react_demo/
agent.py один Agent, история - обычный список
client.py DeepSeekClient: запрос к модели
main.py Lesson: запуск, локальная проверка и replay
prompts.py общие правила и отдельное задание
tools/
read_file.py ReadFileTool: прочитать разрешенный файл
bash.py BashTool: выполнить команду в песочнице
edit.py EditTool: точечно заменить фрагмент кода
__init__.py список трех доступных файлов
recording.py журнал и показ сохраненного запуска
__main__.py вход: python -m react_demo
case_project/
shop/delivery.py намеренный дефект загрузки настроек
shop/settings.json бесплатная доставка от 2000 рублей
tests/test_delivery.py
tests/ проверки демо без сети и модели
runs/ журналы и рабочие копии, не входят в Git
В каждом файле инструмента находятся его описание для модели и выполнение.
Общий системный промпт не привязан к доставке; условия кейса остаются
в задании пользователя. Причина ошибки в запрос
не передается. Описания инструментов передаются отдельным полем tools.
python3 -m react_demo case
python3 -m unittest discover -s tests -vКоманда case запускает тесты доставки через Bash в изолированной
временной копии. Для нее тоже требуется работающий Bubblewrap. Один тест
намеренно падает: доставка для заказа на 2500 рублей стоит 300 вместо 0.
Для команды case это ожидаемый результат и код завершения 0; настоящий
код тестов 1 остается в журнале. Тесты самого демо должны проходить.
Офлайн-проверка исправления выполняется только в копии; исходный кейс
остается с ошибкой.
Ответы модели в проверках - явно обозначенные заглушки. Тесты протокола
не требуют настоящей песочницы; проверки самой изоляции пропускаются,
если среда ее не поддерживает. Это не настоящий маршрут модели.
Если установлены инструменты разработки, дополнительно можно выполнить mypy и
black --check react_demo tests case_project.
Ключ задается в DEEPSEEK_API_KEY вне репозитория; программа не читает
.env. Не вставляйте секрет в код или команды, сохраняемые в истории.
Например, в Bash можно ввести ключ скрыто:
read -r -s -p 'DeepSeek API key: ' DEEPSEEK_API_KEY
export DEEPSEEK_API_KEYПеред запуском проверьте доступную модель и тариф в своем аккаунте. Запросы могут быть платными. Ограничения числа шагов и токенов ниже не являются лимитом расходов в долларах.
После этого:
python3 -m react_demo run --model deepseek-flash --livedeepseek-flash использован в запуске, разобранном в статье. Модель
указывается явно. Без
--live или ключа обращение к API не выполняется. До первого запроса
программа проверяет, что Bubblewrap действительно может создать песочницу:
одного наличия исполняемого файла bwrap недостаточно.
Каждый запуск создает отдельную рабочую копию рядом с журналом:
runs/ИМЯ-ЖУРНАЛА.project/. В песочнице она доступна как /project;
это рабочая папка Bash. По заданию исправлять нужно только
shop/delivery.py. Для точечной правки есть edit(path, old_text, new_text);
для запуска тестов - bash с командой
python3 -m unittest discover -s tests -v. Настройки и тесты недоступны
для edit и смонтированы в песочнице только для чтения.
Фрагмент old_text должен встретиться ровно один раз; иначе файл
не меняется. Bash может выполнять обычные команды, включая пайпы, ls
и grep; изменения доступных для записи файлов сохраняются между вызовами.
Исходный case_project/ сохраняет намеренную ошибку и не подключается
к песочнице живого запуска.
Клиент использует Chat Completions API DeepSeek
в режиме thinking: disabled, с tool_choice: auto и без streaming.
Формат вызовов сверяется с официальным примером tool calling.
В примере показываем выбор инструментов и результаты, а не скрытые
рассуждения. Полученный reasoning_content, если он есть в ответе,
сохраняется вместе с сообщением; обычный показ журнала его не выводит.
По умолчанию: до 8 обращений к модели, 16 вызовов инструментов, 800
выходных токенов на ответ; сетевой таймаут 30 секунд. Значения меняются
через --max-steps, --max-tool-calls, --max-tokens, --timeout
и записываются в журнал. Это технические ограничения, не потолок расходов
в долларах.
Автоматических повторов нет. Ошибка API, обрезанный ответ или исчерпание лимита завершает попытку с записью ошибки. Ответ модели не считается правильным автоматически; его проверяем по файлам и выводу тестов.
Каждая попытка получает новый JSONL-файл в runs/: параметры, хеши
учебных файлов, запросы, сырые ответы с usage, вызовы инструментов,
результаты, diff правок через edit и финал либо ошибка. Команды Bash
и их вывод тоже журналируются, но отдельный diff каждой Bash-правки
не формируется. Событие сохраняется до показа в терминале. Рабочая копия
остается после успеха или ошибки.
Существующий журнал и рабочая копия не перезаписываются, в том числе
при --output.
python3 -m react_demo replay runs/ИМЯ-ЖУРНАЛА.jsonl
python3 -m react_demo replay runs/ИМЯ-ЖУРНАЛА.jsonl --jsonReplay только читает журнал: не обращается к API и не выполняет инструменты
заново. Журналы прежних запусков, рабочие копии и учетные данные
в репозиторий не включены. Для replay нужно сначала создать свой журнал
командой case или run.
read_fileчитает только три файла кейса; произвольные пути, символические ссылки и лишние аргументы отклоняются. Через Bash можно читать и другие доступные внутри песочницы файлы.editменяет существующий файл реализации в рабочей копии; создание файлов и правка настроек или тестов не разрешены.bashвыполняет переданную команду через/usr/bin/bashв Bubblewrap. Таймаут по умолчанию - 10 секунд; необязательный аргументtimeoutможет уменьшить его, но не увеличить. Вывод считывается с ограниченным буфером, чтобы большой stdout/stderr не накапливался целиком в памяти.- Песочница получает рабочую копию с возможностью записи, системный Python и команды Bash. Настройки и каталог тестов доступны только для чтения. Домашняя папка хоста, API-ключи, пользовательское окружение и сеть не передаются. При сбое изоляции команды на хосте не запускаются.
- Команда
caseтоже выполняется через песочницу, в одноразовой копии. Одна только рабочая папка не считается изоляцией. - Изоляция не задает отдельные лимиты памяти и CPU. Это ограниченное учебное демо, не сервис для выполнения произвольного чужого кода.
- Заголовок авторизации не журналируется; используемый ключ маскируется
в событиях, включая возможное эхо ответа. На Unix файл создается с
правами
0600. Перед публикацией или записью экрана журнал все равно нужно проверить на персональные данные.
Перед публикацией на Linux с Python 3.12.3 и Bubblewrap 0.9.0 прошли
96 тестов демо, без пропусков. В исходном case_project один из трех
тестов намеренно падает. Ответы DeepSeek в офлайн-тестах подменены;
это проверка реализации, а не новый платный запуск модели.