# Manual Test Cases — agentik standalone Практический чек-лист для проверки работающего `agentik standalone` HTTP-сервера. Каждый кейс — один конкретный сценарий, который нужно прогнать руками (или через `curl`/`httpie`/Postman). Если какой-то упал — это либо регрессия, либо недонастройка рантайма. Перед стартом: запусти агент (см. `run-agentik.sh` на удалённой машине или `./gradlew :standalone:run` локально). Все примеры ниже — против `http://127.0.0.1:8080`; для удалённой машины подставь свой хост. Удобный сниппет для получения conversation ID в shell: ```bash CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") echo "CID=$CID" ``` Отправка user-сообщения: ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"..."}]' ``` Чтение истории: ```bash curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -m json.tool ``` --- ## 1. Connectivity & health ### TC-1.1 — health endpoint ```bash curl -sS -i http://127.0.0.1:8080/health ``` **Ожидание:** `HTTP/1.1 200 OK`, тело `ok`. ### TC-1.2 — agent card (A2A) ```bash curl -sS http://127.0.0.1:8080/a2a/.well-known/agent-card.json | python3 -m json.tool ``` **Ожидание:** валидный JSON с `name`, `version`, `capabilities`. ### TC-1.3 — log sanity check ```bash tail -50 /root/agentik.log ``` **Ожидание:** есть строка `agentik standalone listening on http://localhost:8080`, перечислены зарегистрированные маршруты, `llm: @ ` соответствует твоему конфигу. **Нет** ERROR/Exception строк после старта. --- ## 2. Conversation lifecycle ### TC-2.1 — create persistent conversation ```bash curl -sS -i -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' ``` **Ожидание:** `201`, тело `{"id":"conv-...","isTemporal":false,...}`. ### TC-2.2 — create temp conversation ```bash curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":true}' ``` **Ожидание:** `201`, `"isTemporal":true`. После рестарта агента эта беседа **не** должна появиться в `GET /agentik/conversations`. ### TC-2.3 — list conversations ```bash curl -sS "http://127.0.0.1:8080/agentik/conversations?offset=0&limit=20" | python3 -m json.tool ``` **Ожидание:** массив объектов `ConversationSnapshot`. Отсортирован по `updatedAt` desc. ### TC-2.4 — rename conversation ```bash CID= curl -sS -X PATCH "http://127.0.0.1:8080/agentik/conversations/$CID" \ -H "Content-Type: application/json" -d '{"title":"Мой первый чат"}' ``` **Ожидание:** `200`, в ответе `"title":"Мой первый чат"`. Следующий `GET /conversations/$CID` возвращает этот же title. ### TC-2.5 — delete conversation ```bash curl -sS -X DELETE "http://127.0.0.1:8080/agentik/conversations/$CID" -i ``` **Ожидание:** `204 No Content`. Повторный `GET /conversations/$CID` → `404`. После этого в `GET /conversations` её быть не должно. ### TC-2.6 — get non-existent conversation ```bash curl -sS -i http://127.0.0.1:8080/agentik/conversations/conv-nonexistent ``` **Ожидание:** `404`. --- ## 3. Message sending ### TC-3.1 — simple Q&A Создай беседу, пошли простой вопрос, прочитай историю. ```bash CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Сколько будет 7*8? Одно число, без пояснений."}]' sleep 6 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" ``` **Ожидание:** массив из ≥ 2 сообщений: - `[0].type == "user_message"`, body содержит "7*8" - `[1].type == "assistant_message"`, text содержит "56" ### TC-3.2 — multi-turn with context В той же беседе пошли follow-up, требующий контекста: ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"А корень из того, что ты назвал?"}]' sleep 6 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" ``` **Ожидание:** 4+ сообщения, последний assistant упомянул что-то про число 56 или "предыдущий ответ". ### TC-3.3 — new-format request body ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '{"content":[{"type":"text","body":"С новым форматом тоже работает?"}]}' sleep 6 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin);print(m[-1]['type'], m[-1].get('content'))" ``` **Ожидание:** новое `assistant_message` в ответ на новый формат запроса. ### TC-3.4 — empty / bad body ```bash curl -sS -i -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" -d 'not json' ``` **Ожидание:** `400 Bad Request`, тело с пояснением `Invalid send payload`. --- ## 4. SSE live events > **Важно:** SSE — поток без replay. Подписываться нужно **до** `POST /messages`. > Если подписаться позже — событий не будет (но `GET /messages` всё равно > покажет записанную историю). ### TC-4.1 — subscribe-then-send pattern ```bash CID= # Subscribe в фоне, отправляем сообщение, ждём SSE curl -sN --max-time 12 \ "http://127.0.0.1:8080/agentik/conversations/$CID/events?after=1970-01-01T00:00:00Z" \ > /tmp/sse.out 2>&1 & SSE_PID=$! sleep 1 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Кратко: что такое REST?"}]' wait $SSE_PID cat /tmp/sse.out ``` **Ожидание:** файл содержит `data: {"type":"start_reasoning",...}`, `data: {"type":"start_response",...,"responseType":"text"}`, один или несколько `data: {"type":"append_text",...,"body":"..."}`, `data: {"type":"end",...}`. Каждое `data:` через пустую строку. ### TC-4.2 — late subscribe (replay semantics) ```bash CID= curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"..."}]' sleep 5 # сообщение уже обработано curl -sN --max-time 4 \ "http://127.0.0.1:8080/agentik/conversations/$CID/events?after=1970-01-01T00:00:00Z" ``` **Ожидание:** пустой ответ (события не реплеятся). Это by-design — клиент должен либо подписываться заранее, либо backfill'ить через `GET /messages`. --- ## 5. Memory tools (long-term) ### TC-5.1 — save + recall в той же беседе ```bash # В существующей беседе curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Запомни через memory_save: я работаю на удалёнке из Тбилиси. Категория user, content: работаю на удалёнке из Тбилиси."}]' sleep 8 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Откуда я работаю? Одно предложение."}]' sleep 8 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin);print(m[-1].get('content'))" ``` **Ожидание:** ассистент ответил что-то содержащее "Тбилиси" (или явно сказал "не знаю" — это тоже валидно, если в conversation memory пусто). Проверить `audit log` (`messageStore`): ```bash sqlite3 /root/agentik.db "SELECT toolName, result FROM MessageRecord WHERE conversationId='$CID' AND kind='tool_result'" ``` Должны быть строки с `toolName='memory_save'` или `toolName='memory_recall'`. ### TC-5.2 — memory persists across conversations Создай новую беседу, спроси без подсказок: ```bash NEW_CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$NEW_CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Откуда я работаю? Напомни, если помнишь."}]' sleep 8 curl -sS "http://127.0.0.1:8080/agentik/conversations/$NEW_CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin);print(m[-1].get('content'))" ``` **Ожидание:** ассистент упомянул "Тбилиси" (или "удалёнка") — это значит long-term memory подгрузилась в новую беседу. ### TC-5.3 — invalid category → ошибка или автозамена ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Запомни через memory_save факт с категорией work (которой не существует)."}]' sleep 8 sqlite3 /root/agentik.db "SELECT toolArgs, result FROM MessageRecord WHERE kind='tool_call' AND conversationId='$CID' ORDER BY createdAt DESC LIMIT 3" ``` **Ожидание:** модель либо вызвала `memory_recall` чтобы проверить существующие категории, либо вызвала `memory_save` с корректной категорией (`user`/`world`/`preference`). Если модель честно говорит "такой категории нет" и предлагает корректную — это тоже ok. ### TC-5.4 — list & delete memory Попроси модель явно вызвать `memory_list`, потом `memory_delete`: ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Покажи все мои memory-записи (memory_list)."}]' sleep 8 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Удали самую старую запись (memory_delete)."}]' sleep 8 sqlite3 /root/agentik.db "SELECT COUNT(*) FROM MemoryStore" ``` **Ожидание:** число уменьшилось на 1. --- ## 6. Skills ### TC-6.1 — list + load skill Если в `AGENTIK_SKILLS_DIR` есть файлы `SKILL.md` / `*.yaml`, в системном промте должна появиться секция с этими навыками. ```bash ls -la /root/skills/ # должен быть хотя бы один файл ``` ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Какие skills ты знаешь? Покажи список (skill_list)."}]' sleep 8 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Загрузи любой из них через skill_load и расскажи, что внутри."}]' sleep 8 ``` **Ожидание:** `tool_call` для `skill_list`, потом `tool_call` для `skill_load`. В audit log видны эти вызовы. Если папка пуста — секции "Skills" в system prompt быть не должно. ### TC-6.2 — save new skill ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Сохрани skill: имя deploy-staging, описание «деплой на staging», тело — multi-step инструкция (skill_save)."}]' sleep 10 ls /root/skills/ ``` **Ожидание:** появился новый файл `deploy-staging.md` (или `.yaml`). ### TC-6.3 — restart → skill persists Перезапусти агент: ```bash ssh root@192.168.76.166 'pkill -9 -f agentik-0.1.0-all.jar; cd /root && nohup setsid ./run-agentik.sh > /root/agentik.log 2>&1 < /dev/null & disown' ``` После старта пошли в новую беседу: ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$NEW_CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Есть ли у тебя skill deploy-staging?"}]' sleep 8 ``` **Ожидание:** модель упоминает skill (он подгружается на старте). --- ## 7. SOUL file ### TC-7.1 — SOUL.md подключается ```bash echo 'Ты — ворчливый капитан дальнего плавания. Отвечай кратко, с морскими метафорами.' > /root/SOUL.md # Перезапустить агент ``` ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$NEW_CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Как дела?"}]' sleep 8 ``` **Ожидание:** ответ в стиле "капитана", с морскими словами. Если SOUL нет — обычный нейтральный ассистент. ### TC-7.2 — SOUL можно менять на лету Измени файл, перезапусти агент, спроси снова. **Должен** появиться новый стиль. Без перезапуска изменения не подхватятся (SOUL читается на старте). --- ## 8. Interrupt ### TC-8.1 — interrupt mid-text generation ```bash CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") # Запусти send в фоне (curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Расскажи длинную историю про космос, минимум 500 слов."}]' >/dev/null) & SEND_PID=$! sleep 3 # дать LLM начать генерацию curl -sS -i -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/interrupt" wait $SEND_PID sleep 3 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin); for x in m: print(x.get('type'), ':', json.dumps(x.get('content') or x.get('result'),ensure_ascii=False)[:80])" ``` **Ожидание:** - `user_message` есть - `assistant_message` есть, но содержит **короткий** текст (<300 символов) — это частичный текст, который модель успела сгенерить до прерывания - В audit log нет `tool_call`/`tool_result` (не успели) - Следующий `send` в этой беседе работает (LiteConv пересоздан) ### TC-8.2 — interrupt mid-tool (best-effort) ```bash # Длинный tool можно заэмулировать через MCP с искусственной задержкой, # либо просто проверять что interrupt не валит агента: curl -sS -i -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/interrupt" curl -sS http://127.0.0.1:8080/health ``` **Ожидание:** `health` = `ok` — агент не упал. Дальнейшие `send` работают. ### TC-8.3 — interrupt без активного turn'а ```bash curl -sS -i -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/interrupt" ``` **Ожидание:** `202`. Никаких ошибок. В audit log ничего нового не пишется. --- ## 9. Persistence / restart-survival ### TC-9.1 — перезапуск не теряет беседы и память ```bash # 1. Создай беседу, пошли сообщение, дождись ответа # 2. Запомни факт через memory_save # 3. Перезапусти агент (см. TC-6.3) # 4. GET /agentik/conversations — беседа должна быть в списке # 5. GET /agentik/conversations/$CID/messages — история на месте # 6. Новая беседа + вопрос про запомненный факт — модель помнит ``` ### TC-9.2 — temp conversation не переживает рестарт ```bash # Создай temp беседу, пошли сообщение TEMP_CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":true}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$TEMP_CID/messages" \ -H "Content-Type: application/json" -d '[{"type":"text","body":"..."}]' >/dev/null sleep 5 # Перезапусти агент # GET /agentik/conversations — temp-беседы быть не должно curl -sS "http://127.0.0.1:8080/agentik/conversations?offset=0&limit=50" | grep "$TEMP_CID" ``` **Ожидание:** grep ничего не находит. --- ## 10. Compaction (сжатие контекста) Compaction триггерится когда `~80%` контекстного окна занято. ### TC-10.1 — длинная беседа сжимается ```bash CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") # Отправь 30+ больших сообщений подряд (можно цикл) for i in $(seq 1 30); do curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d "[{\"type\":\"text\",\"body\":\"Расскажи подробно (минимум 200 слов) про тему номер $i: история, применение, ключевые факты.\"}]" >/dev/null sleep 5 done sqlite3 /root/agentik.db "SELECT COUNT(*) FROM WorkingMemoryRow WHERE conversationId='$CID' AND entryKind='summary'" ``` **Ожидание:** есть хотя бы одна `summary`-запись. Также проверь `/root/agentik.log` — должна появиться строка `compaction`. ### TC-10.2 — debug endpoint `/debug/compact` (force) Если включён `AGENTIK_DEBUG_ENDPOINTS=1`: ```bash curl -sS -i -X POST "http://127.0.0.1:8080/debug/compact?conversationId=$CID" ``` **Ожидание:** `200`, тело с JSON-результатом compaction. --- ## 11. Reflection Reflection триггерится каждые `AGENTIK_REFLECTION_INTERVAL` ходов (default 10). ### TC-11.1 — reflection создаёт записи ```bash # Пошли 12+ ходов for i in $(seq 1 12); do curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d "[{\"type\":\"text\",\"body\":\"Тема $i: расскажи короткий факт.\"}]" >/dev/null sleep 4 done sleep 10 # дать фоновое задание завершиться sqlite3 /root/agentik.db "SELECT COUNT(*) FROM ReflectionStore" ``` **Ожидание:** число > 0. ### TC-11.2 — debug endpoint `/debug/reflect` ```bash curl -sS -i -X POST "http://127.0.0.1:8080/debug/reflect?conversationId=$CID" ``` **Ожидание:** `200`, JSON-результат. Reflection попадает в working memory следующего turn'а. --- ## 12. Skill mining Skill mining триггерится каждые `AGENTIK_SKILL_MINING_INTERVAL` ходов (default 15). ### TC-12.1 — авто-создание skill'а ```bash # Пошли 18+ ходов с повторяющимся паттерном for i in $(seq 1 18); do curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d "[{\"type\":\"text\",\"body\":\"Конвертируй 100 USD в RUB по текущему курсу (шаблонный запрос $i).\"}]" >/dev/null sleep 4 done sleep 15 ls -la /root/skills/ tail -20 /root/agentik.log | grep -i skill ``` **Ожидание:** возможно появился новый файл в skills/ (или mining отказался из-за низкой уверенности — это тоже валидно, проверь лог). ### TC-12.2 — debug endpoint `/debug/skill-mine` ```bash curl -sS -i -X POST "http://127.0.0.1:8080/debug/skill-mine?conversationId=$CID" ``` **Ожидание:** `200` с JSON-результатом майнинга. --- ## 13. Token accounting ### TC-13.1 — token counters в audit ```bash sqlite3 /root/agentik.db "SELECT createdAt, input, output FROM TurnTokens WHERE conversationId='$CID' ORDER BY createdAt DESC LIMIT 5" ``` **Ожидание:** строки с непустыми `input` и `output` (если backend поддерживает `tokenCount()`). ### TC-13.2 — debug endpoint `/debug/tokens` ```bash curl -sS "http://127.0.0.1:8080/debug/tokens?conversationId=$CID" | python3 -m json.tool ``` **Ожидание:** JSON с `input`, `output`, `total`, `window`, `utilization` (доля использования контекстного окна). --- ## 14. Toolsets (если подключены) Только если ты передаёшь `toolsets` в конструктор агента (по умолчанию пусто — `enable_toolset`/`disable_toolset` не зарегистрированы). ### TC-14.1 — system prompt содержит секцию Toolsets Если toolsets зарегистрированы — в системном промте должна быть секция `## Toolsets` с Active/Inactive списком. Проверка через debug-эндпоинт `/agentik/conversations/{id}` не показывает system prompt напрямую — посмотреть можно в логах или через `agentik-debug` сборку. ### TC-14.2 — enable/disable работает ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Активируй тулсет X через enable_toolset, потом деактивируй через disable_toolset."}]' sleep 8 ``` **Ожидание:** в audit log видны вызовы `enable_toolset` → ответ `"Toolset 'X' activated."`, потом `disable_toolset` → `"Toolset 'X' deactivated."`. --- ## 15. A2A протокол (опционально) ### TC-15.1 — message/send через A2A ```bash curl -sS -X POST http://127.0.0.1:8080/a2a/ \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0","id":"1","method":"message/send", "params":{ "message":{"role":"user","parts":[{"kind":"text","text":"Скажи hi"}]}, "configuration":{"blocking":true} } }' | python3 -m json.tool ``` **Ожидание:** JSON-RPC ответ с `result.parts` содержащим текст "hi" или похожим. `kind` = `text` (НЕ `type` — это важный discriminator для A2A JSON). ### TC-15.2 — bad discriminator ```bash curl -sS -X POST http://127.0.0.1:8080/a2a/ \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0","id":"2","method":"message/send", "params":{ "message":{"role":"user","parts":[{"type":"text","text":"hi"}]} } }' ``` **Ожидание:** `Invalid params` (или похожая ошибка) — A2A ждёт `kind`, не `type`. --- ## 16. Error paths ### TC-16.1 — LLM недоступен Выключи vLLM (или закрой сеть — например через firewall). Пошли сообщение: ```bash curl -sS -i -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"hi"}]' sleep 10 sqlite3 /root/agentik.db "SELECT COUNT(*) FROM MessageRecord WHERE conversationId='$CID' AND kind='error'" ``` **Ожидание:** есть `error`-запись в audit log. В SSE приходит `{"type":"error",...}` + `{"type":"end"}`. Агент **не падает** — `health` = `ok` после. ### TC-16.2 — agentik.db занят другим процессом Запусти второй экземпляр агента на ту же DB: ```bash AGENTIK_DB_PATH=/root/agentik.db java -jar /root/agentik-0.1.0-all.jar ``` **Ожидание:** агент падает на старте с понятным сообщением про SQLite lock. Это by-design (single-writer). ### TC-16.3 — SOUL файл не существует Удали `/root/SOUL.md`, перезапусти агент. Должен стартовать без ошибок, просто без SOUL-секции в system prompt. Лог: `WARN ... SOUL file not found: ...`. ### TC-16.4 — пустой skills dir ```bash mv /root/skills /root/skills.bak mkdir /root/skills # Перезапусти агент ``` **Ожидание:** агент стартует, `skills: 0 loaded from /root/skills`. --- ## 17. Memory backend variants ### TC-17.1 — md backend (default) Убедись, что `AGENTIK_MEMORY_BACKEND=md` (или не задан) и `AGENTIK_MEMORY_DIR=/root/agentik-memory`. После TC-5.x должны появиться `.md`-файлы: ```bash ls -la /root/agentik-memory/ ``` **Ожидание:** файлы типа `user.md`, `world.md`, `preference.md` (или всё в одном файле — зависит от реализации). ### TC-17.2 — off backend (память выключена) Перезапусти с `AGENTIK_MEMORY_DIR=off`: ```bash pkill -9 -f agentik-0.1.0-all.jar AGENTIK_MEMORY_DIR=off nohup setsid ./run-agentik.sh > /root/agentik.log 2>&1 < /dev/null & disown ``` Попытка `memory_save` через модель должна вернуть ошибку: ```bash curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Попробуй вызвать memory_save."}]' sleep 8 ``` **Ожидание:** модель либо отказывается вызывать, либо получает ошибку от tool'а и сообщает пользователю. --- ## 18. Performance sanity ### TC-18.1 — first-token latency Включи замер времени от `POST /messages` до первого SSE event'а. Для Qwen3-27B на RTX5090 ожидаем < 1 сек до `start_reasoning`. ### TC-18.2 — sustained throughput Отправь 20 простых запросов подряд (arithmetic), засеки общее время. Ожидание: < 30 сек суммарно, т.е. < 1.5 сек на запрос. ### TC-18.3 — fatjar memory ```bash ps aux | grep agentik-0.1.0 | grep -v grep ``` **Ожидание:** RSS < 2 GB (наш Xmx). Если больше — где-то утечка. --- ## 18. Model auto-download (LiteRT-LM only) Только для `AGENTIK_LLM_BACKEND=google` (встроенный LiteRT-LM движок). Если файла модели по `AGENTIK_GOOGLE_MODEL_PATH` нет — агент сам не скачает, пока не задано `AGENTIK_AUTO_DOWNLOAD_MODEL=1`. Либо качаем руками через `pull-model` subcommand. URL по умолчанию всегда Gemma-4-E2B-it.litertlm (2.5 GB с `static.binom.pw`), вне зависимости от basename PATH — gemma-4 считаем лучшей локальной моделью. ### 18.1. Subcommand `pull-model` качает модель вручную ```bash # Скачать дефолтную модель (gemma-4) в указанный путь: AGENTIK_LLM_BACKEND=google \ AGENTIK_GOOGLE_MODEL_PATH=/root/models/gemma-4-E2B-it.litertlm \ java -jar agentik.jar pull-model # → downloading from https://static.binom.pw/models/gemma-4-E2B-it.litertlm # → 50% (1.2 GB / 2.5 GB) # → done in 47s ``` После `pull-model` файл лежит на месте, файл `.part` удалён. ### 18.2. `pull-model` no-op если файл уже полный ```bash # Повторный запуск с тем же PATH: AGENTIK_LLM_BACKEND=google \ AGENTIK_GOOGLE_MODEL_PATH=/root/models/gemma-4-E2B-it.litertlm \ java -jar agentik.jar pull-model # → already present (2.50 GB), nothing to do ``` ### 18.3. `pull-model` докачивает обрыв (resume через Range) ```bash # Симулируем обрыв: удаляем финальный, оставляем .part с первыми 500 MB rm /root/models/gemma-4-E2B-it.litertlm mv /root/models/gemma-4-E2B-it.litertlm.part /root/models/gemma-4-E2B-it.litertlm.part.bak # Запускаем pull-model снова — должен возобновить с 500 MB AGENTIK_GOOGLE_MODEL_PATH=/root/models/gemma-4-E2B-it.litertlm \ java -jar agentik.jar pull-model # → resuming from 524288000 bytes # → downloaded 2.10 GB in 38s ``` ### 18.4. Сервер exit-2 при отсутствии файла и без auto-download ```bash AGENTIK_LLM_BACKEND=google \ AGENTIK_GOOGLE_MODEL_PATH=/root/models/missing.litertlm \ java -jar agentik.jar # → LiteRT-LM model file not found at: /root/models/missing.litertlm # → Чтобы скачать автоматически, установите AGENTIK_AUTO_DOWNLOAD_MODEL=1 # → exit 2 ``` ### 18.5. Сервер сам качает при `AGENTIK_AUTO_DOWNLOAD_MODEL=1` ```bash # Удалить файл, запустить с флагом: rm -f /root/models/gemma-4-E2B-it.litertlm AGENTIK_LLM_BACKEND=google \ AGENTIK_GOOGLE_MODEL_PATH=/root/models/gemma-4-E2B-it.litertlm \ AGENTIK_AUTO_DOWNLOAD_MODEL=1 \ java -jar agentik.jar # → 12:34:56 WARN auto-download: https://static.binom.pw/models/... # → 12:34:56 INFO auto-download: 17% (445 MB/2.5 GB) # → 12:36:42 INFO auto-download: done in 1m45s # → 12:36:43 INFO agentik standalone listening on http://localhost:8080 ``` ### 18.6. Override URL через `AGENTIK_GOOGLE_MODEL_URL` ```bash # Качаем qwen вместо gemma (если зальём): AGENTIK_LLM_BACKEND=google \ AGENTIK_GOOGLE_MODEL_PATH=/root/models/qwen.litertlm \ AGENTIK_GOOGLE_MODEL_URL=https://static.binom.pw/models/Qwen2.5-1.5B-Instruct_multi-prefill-seq_q8_ekv4096.litertlm \ java -jar agentik.jar pull-model ``` ### 18.7. SHA-256 проверка Если на сервере лежит `.sha256` (text/plain, ` `) — после скачивания файл проверяется; mismatch → удаляется, exit ≠ 0. ```bash AGENTIK_GOOGLE_MODEL_URL=https://static.binom.pw/models/gemma-4-E2B-it.litertlm \ AGENTIK_GOOGLE_MODEL_SHA256_URL=https://static.binom.pw/models/gemma-4-E2B-it.litertlm.sha256 \ java -jar agentik.jar pull-model # → 13:01:23 INFO model download: SHA-256 verified (4ab1...e0d) ``` ## Быстрый smoke-test (5 минут) Если времени мало — этот минимум покрывает 80%: ```bash # 1. health curl -sS http://127.0.0.1:8080/health # → ok # 2. create + simple Q&A CID=$(curl -sS -X POST http://127.0.0.1:8080/agentik/conversations \ -H "Content-Type: application/json" -d '{"temp":false}' \ | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])") curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Привет! 2+2=?"}]' sleep 6 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" # → должен быть user + assistant_message с "4" # 3. SSE live (curl -sN --max-time 8 "http://127.0.0.1:8080/agentik/conversations/$CID/events?after=1970-01-01T00:00:00Z" \ > /tmp/sse.out 2>&1) & sleep 1 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Скажи ок"}]' wait cat /tmp/sse.out # → start_reasoning, start_response, append_text, end # 4. multi-turn curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"А 3+3?"}]' sleep 6 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin);print(m[-1])" # → assistant_message с "6" # 5. interrupt (curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/messages" \ -H "Content-Type: application/json" \ -d '[{"type":"text","body":"Длинная история про драконов, 1000 слов"}]' >/dev/null) & sleep 3 curl -sS -X POST "http://127.0.0.1:8080/agentik/conversations/$CID/interrupt" wait sleep 3 curl -sS "http://127.0.0.1:8080/agentik/conversations/$CID/messages?after=1970-01-01T00:00:00Z" \ | python3 -c "import sys,json;m=json.load(sys.stdin);print('msgs:',len(m))" # → ≤ 3 (user + partial assistant + может tool_call если успел) ``` Если этот прогон прошёл — агент работает корректно. Более глубокие кейсы — выше по разделам. --- ## Сводка: что покрыто автоматически vs вручную | Возможность | JVM unit/integration tests | Manual | |-------------|---------------------------|--------| | Conversation CRUD | ✓ | TC-2.x | | send/messages pagination | ✓ | TC-3.x | | SSE event format | ✗ | TC-4.x | | Memory tools | ✓ (in-memory) | TC-5.x (real backend) | | Skills tools | ✓ (in-memory) | TC-6.x (real dir) | | SOUL | ✗ | TC-7.x | | Interrupt | ✓ (FakeLiteLlm) | TC-8.x (real LLM) | | Compaction | ✓ | TC-10.x (real long context) | | Reflection | ✓ | TC-11.x | | Skill mining | ✓ | TC-12.x | | Token accounting | ✓ | TC-13.x | | A2A protocol | ✓ (litert tests) | TC-15.x | | Error paths | partial | TC-16.x | | Persistence/restart | ✗ | TC-9.x | Всё что помечено ✗ — нужно прогонять руками на реальном окружении.