feat(agent): агент логов — фильтр, LLM-страж, планировщик grep (#18)
CI / ruff · mypy · pytest (pull_request) Successful in 39m13s

- prefilter: правила из config/agent_rules.yaml (длина, команды, shell,
  секреты и системные файлы, prompt injection) — без LLM
- guard: отдельный вызов, JSON {verdict, reason}; невалидный ответ = attack,
  недоступный LLM — «попробуйте позже», а не атака
- planner: только AgentLogQuery (контейнер из белого списка, ≤ 7 д, шаблон
  ≤ 200 без вложенных квантификаторов, лимит ≤ 500); логи в LLM не идут
- бот: «🤖 Спросить агента» → показывает построенный grep и строки;
  атака — вежливый отказ и 🛡 администраторам; квота 30 в сутки
- журнал agent_requests + log_queries (mode=agent)
- при LLM_PROVIDER=mock агент выключен

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 15:02:07 +03:00
co-authored by Claude Opus 5.5
parent f89338e015
commit 65acf3abf1
20 changed files with 917 additions and 26 deletions
+1 -1
View File
@@ -149,7 +149,7 @@ async def test_open_container_and_back_to_list(tg: TgClient, texts: Texts, fake:
card, listing = tg.session.sent(EditMessageText)
assert card.text.startswith("📜 Логи · crash")
assert listing.text == texts.logs.title
assert tg.session.sent(AnswerCallbackQuery)[-1].text == texts.logs.agent_soon
assert tg.session.sent(AnswerCallbackQuery)[-1].text == texts.logs.agent_off # no LLM configured
async def test_without_docker(texts: Texts, sessions: SessionMaker, access: AccessService, repo: UploadsRepo) -> None: