cba8940005e75e63ff2f3a57db6788599a307a25
- RetryOnRateLimitMiddleware: при 429 шлёт события rate_limit_wait/retry в глобальный UI-канал (set_ui_event_queue) — без него не меняет поведение - ui.py: рендерит ⏳ rate_limit_wait и 🔄 rate_limit_retry в лог событий - ui.py: показывает «Агент работает... Nс» если нет событий >15с - ui.py: кнопка «Стоп» прерывает ожидание в чате и pipeline Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
brojs-agent
AI-агент для автоматического выполнения заданий курса KFU-26-1 на platform.brojs.ru.
Что делает агент
- Читает незакрытые задания из журнала через BroJS MCP
- Пишет Python-решение для каждого задания
- Создаёт репозиторий на
git.brojs.ru/glevelll/task-<id> - Коммитит решение через Gitea API
- Отправляет ссылку на репозиторий как ответ
- Сдаёт задание на проверку
- При пересдаче — клонирует, исправляет, пушит снова
Стек
- Python 3.11+
- deepagents — мультиагентный фреймворк
- LangGraph — граф пайплайна
- LangChain — инструменты и MCP-адаптеры
- OpenRouter →
gpt-oss-20b:free - Gitea REST API (git.brojs.ru)
- BroJS Journal MCP (platform.brojs.ru)
Почему OpenRouter, а не Ollama
Все решения используют OpenRouter (https://openrouter.ai/api/v1) с моделью openai/gpt-oss-20b:free через стандартный langchain_openai.ChatOpenAI.
| Критерий | OpenRouter | Ollama (локальная) |
|---|---|---|
| Требования к железу | Нет (облако) | GPU / 8–16 GB RAM |
| Совместимость с LangChain | Полная (OpenAI-совместимый API) | Требует отдельного провайдера |
| Смена модели | Одна строка в коде | Скачать новую модель (~GB) |
| CI / автоматизация | Работает из любой среды | Нужен локальный сервер |
| Стоимость | Бесплатный тир (gpt-oss-20b:free) |
Бесплатно, но ресурсоёмко |
Ключ хранится в .env как OPENAI_API_KEY=sk-or-v1-... — формат совместим с OpenAI SDK, поэтому смена провайдера (OpenAI, Azure, Ollama) не требует изменений в коде, только .env.
Установка
# Клонировать репозиторий
git clone https://github.com/Glevelll/brojs-agent.git
cd brojs-agent
# Установить зависимости
pip install -r requirements.txt
# или через uv:
# uv sync
# Создать .env из шаблона
cp .env.example .env
# Отредактировать .env — заменить заглушки на реальные ключи
Настройка .env
OPENAI_API_KEY=sk-or-... # OpenRouter ключ
JOURNAL_TOKEN=jrnl_... # BroJS токен (генерируется на platform.brojs.ru)
GITEA_TOKEN=... # Токен на git.brojs.ru
TAVILY_API_KEY=... # Опционально, для web_search
Где взять токены
| Токен | Где получить |
|---|---|
OPENAI_API_KEY |
https://openrouter.ai/settings/keys |
JOURNAL_TOKEN |
platform.brojs.ru → профиль → API токены |
GITEA_TOKEN |
git.brojs.ru → Settings → Applications → Access Tokens |
TAVILY_API_KEY |
https://tavily.com (опционально) |
⚠️ ВАЖНО: Никогда не коммить
.envв репозиторий! Он добавлен в.gitignore.
Запуск
Запуск пайплайна (выполнить все незакрытые задания)
python -c "
import asyncio
from src.agent.graph.pipeline import pipeline
from langchain_core.messages import HumanMessage
async def main():
result = await pipeline.ainvoke(
{'tasks': [], 'current_index': 0, 'results': [], 'errors': []},
{'configurable': {'thread_id': 'pipeline-run-1'}}
)
print(f'Выполнено: {len(result[\"results\"])} заданий')
print(f'Ошибки: {result[\"errors\"]}')
asyncio.run(main())
"
LangGraph dev server (UI в браузере)
# Установить langgraph-cli
pip install "langgraph-cli[inmem]"
# Запустить dev server
langgraph dev --allow-blocking --port 2024
# Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
Интерактивный чат с главным агентом
python -c "
import asyncio
from src.agent.agent import agent
from langchain_core.messages import HumanMessage
async def main():
config = {'configurable': {'thread_id': 'chat-1'}}
while True:
user = input('Вы: ')
if user == 'exit': break
result = await agent.ainvoke(
{'messages': [HumanMessage(content=user)]}, config
)
print('Агент:', result['messages'][-1].content)
asyncio.run(main())
"
Структура проекта
brojs-agent/
├── .env.example # шаблон переменных окружения
├── .gitignore
├── requirements.txt
├── pyproject.toml
├── langgraph.json
├── agent.py # точка входа для langgraph dev
└── src/
└── agent/
├── agent.py # создание агентов (main, homework, rework)
├── constants.py # COURSE_ID, GITEA_OWNER, пути
├── llm.py # LLM через OpenRouter
├── gitea_tools.py # Gitea REST API инструменты
├── mcp_client.py # BroJS Journal MCP клиент
├── tools.py # git + web инструменты
├── prompts.py # системные промпты
├── subagents.py # спецификации субагентов
├── middlewares/
│ ├── sanitize_tool_calls.py # блокировка несуществующих инструментов
│ └── validate_journal_workflow.py # порядок task_update_answer → task_submit
├── graph/
│ └── pipeline.py # LangGraph пайплайн по всем заданиям
└── agent_workspace/ # рабочая директория агента (клонированные репо)
Заглушки в коде
| Файл | Переменная | Что заменить |
|---|---|---|
src/agent/llm.py |
OPENAI_API_KEY |
OpenRouter ключ в .env |
src/agent/mcp_client.py |
JOURNAL_TOKEN |
BroJS токен в .env |
src/agent/gitea_tools.py |
GITEA_TOKEN |
Gitea токен в .env |
Description
Languages
Python
55.6%
TypeScript
37%
JavaScript
4.5%
CSS
2.4%
PowerShell
0.5%