c02268a180
Co-authored-by: Cursor <cursoragent@cursor.com>
4.1 KiB
4.1 KiB
KFU Course Agent
Автоматизация домашних заданий курса KFU-26-1 на platform.brojs.ru: агент сам находит открытые задачи, пишет решение, публикует репозиторий и сдаёт работу.
Как это устроено
flowchart LR
A[Журнал BroJS] --> B[Агент]
B --> C[Gitea git.brojs.ru]
B --> D[Ответ в журнал]
D --> E[Проверка]
| Этап | Действие |
|---|---|
| 1 | Список незакрытых заданий через Journal MCP |
| 2 | Генерация Python-решения |
| 3 | Репозиторий git.brojs.ru/glevelll/task-<id> |
| 4 | Коммит через Gitea API |
| 5 | Ссылка в ответ + task_submit |
| 6 | При отклонении — клон, правки, повторная отправка |
Требования
- Python 3.11+
- Ключи в
.env(см. ниже)
Зависимости: deepagents, LangGraph, LangChain, OpenRouter (gpt-oss-20b:free), Gitea API, BroJS Journal MCP.
Быстрый старт
git clone https://github.com/Glevelll/brojs-agent.git
cd brojs-agent
pip install -r requirements.txt
cp .env.example .env
# заполните .env реальными ключами
Переменные окружения
| Переменная | Назначение | Где взять |
|---|---|---|
OPENAI_API_KEY |
LLM через OpenRouter | openrouter.ai/settings/keys |
JOURNAL_TOKEN |
API журнала | platform.brojs.ru → профиль → токены |
GITEA_TOKEN |
git.brojs.ru | Settings → Applications → Access Tokens |
TAVILY_API_KEY |
Поиск в сети (необязательно) | tavily.com |
Файл .env не коммитится — он в .gitignore.
Запуск
Пайплайн (все открытые coding-задания)
python -c "
import asyncio
from src.agent.graph.pipeline import pipeline
async def main():
state = {'tasks': [], 'current_index': 0, 'results': [], 'errors': []}
cfg = {'configurable': {'thread_id': 'run-1'}}
out = await pipeline.ainvoke(state, cfg)
print('Готово:', len(out['results']))
if out['errors']:
print('Сбои:', out['errors'])
asyncio.run(main())
"
LangGraph Studio
pip install "langgraph-cli[inmem]"
langgraph dev --allow-blocking --port 2024
UI: LangSmith Studio
Чат с оркестратором
python -c "
import asyncio
from src.agent.agent import agent
from langchain_core.messages import HumanMessage
async def main():
cfg = {'configurable': {'thread_id': 'chat-1'}}
while True:
q = input('> ').strip()
if q in ('exit', 'quit', 'q'):
break
r = await agent.ainvoke({'messages': [HumanMessage(content=q)]}, cfg)
print(r['messages'][-1].content)
asyncio.run(main())
"
Карта репозитория
.
├── agent.py # экспорт для langgraph dev
├── langgraph.json
├── requirements.txt
├── .env.example
└── src/agent/
├── agent.py # оркестратор, homework, rework
├── graph/pipeline.py # пакетная обработка заданий
├── prompts.py
├── gitea_tools.py
├── mcp_client.py
├── middlewares/
└── agent_workspace/ # клоны репозиториев
Секреты в коде
Плейсхолдеры подхватываются из .env:
| Модуль | Переменная |
|---|---|
llm.py |
OPENAI_API_KEY |
mcp_client.py |
JOURNAL_TOKEN |
gitea_tools.py |
GITEA_TOKEN |