From 5d77ab1a882160d811967e64fae70d8ec5d6e412 Mon Sep 17 00:00:00 2001 From: zosimovaa Date: Wed, 1 Apr 2026 12:28:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D1=87=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=83=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20v1=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _process.zip | Bin 0 -> 17467 bytes .../req_02f645dac43746099e9ffb6663899b9e.md | 130 ---- .../req_18f4de6d677946739b66c533a2f95b95.md | 130 ---- .../req_4a5e5dc2bdec4045971575d2434bfa03.md | 245 -------- .../req_6556fd999c2548e58071a1d1518e35cb.md | 137 ----- .../req_68ad6398d5f2415e95c5a602a52dce2e.md | 579 ------------------ .../req_7e47a8b3458741568e2b974079a74cb5.md | 507 --------------- .../req_90367a184bfa4638bd9c473e89d2b5b7.md | 507 --------------- .../req_9827472a716044cf983328dc959c4042.md | 130 ---- .../req_bc0ca07c5a344978bdbfa3311283f4c8.md | 507 --------------- .../req_c1a55e97a0aa45ce8c4c6c453a9ec080.md | 555 ----------------- .../req_c0af91b38de3436e82a75b9c3664c95a.md | 550 ----------------- .../req_33758fd1ed834100a23fe95871b34181.md | 171 ++++++ src/app/main.py | 2 +- src/app/modules/agent/llm/service.py | 32 +- .../agent/observability/module_trace.py | 27 + .../modules/agent/orchestration/__init__.py | 1 + .../adapters/intent_router_adapter.py | 24 + .../adapters/llm_chat_adapter.py | 32 + .../adapters/task_runtime_adapter.py | 4 +- .../context/execution_context.py | 22 + .../{ => agent}/orchestration/facade.py | 20 +- .../messaging/client_message_publisher.py | 8 +- .../messaging/status_message_factory.py | 2 +- .../messaging/user_message_factory.py | 2 +- .../orchestration/processes/registry.py | 4 +- .../orchestration/processes/v1/process.py | 0 .../processes/v1/prompt_payload_builder.py | 11 + .../orchestration/processes/v1/prompts.yml | 12 + .../processes/v1/simple_llm_workflow.py | 42 ++ .../processes/v1/steps/bootstrap_step.py | 9 +- .../v1/steps/execute_llm_workflow_step.py | 35 ++ .../processes/v1/steps/finalize_step.py | 6 +- .../processes/v1/steps/run_llm_step.py | 4 +- .../orchestration/processes/v2/README.md | 2 +- .../orchestration/processes/v2/process.py | 0 .../processes/v2/prompt_payload_builder.py | 35 ++ .../processes/v2/prompt_selector.py | 18 + .../orchestration/processes/v2/prompts.yml | 96 +++ .../execute_documentation_workflow_step.py | 2 +- .../steps/execute_fallback_workflow_step.py | 4 +- .../steps/execute_general_qa_workflow_step.py | 2 +- .../v2/steps/execute_openapi_workflow_step.py | 2 +- .../processes/v2/steps/route_intent_step.py | 19 +- .../v2/steps/run_task_workflow_step.py | 4 +- .../processes/v2/steps/workflow_step_base.py | 9 +- .../orchestration/runtime/process_runner.py | 2 +- .../{ => agent}/orchestration/v2_progress.py | 2 +- .../runtime/docs_qa_pipeline/pipeline.py | 80 ++- .../prompt_payload_builder.py | 2 - src/app/modules/agent/task_runtime/context.py | 2 + src/app/modules/agent/task_runtime/models.py | 1 + .../agent/task_runtime/workflows/docs_qa.py | 15 +- .../agent/task_runtime/workflows/fallback.py | 21 +- .../task_runtime/workflows/general_qa.py | 15 +- .../agent/task_runtime/workflows/openapi.py | 15 +- src/app/modules/agent_api/__init__.py | 3 - src/app/modules/api/__init__.py | 1 + .../application/request_service.py | 10 +- .../application/session_service.py | 6 +- .../application/stream_service.py | 4 +- .../controllers/request_controller.py | 2 +- .../controllers/session_controller.py | 2 +- .../controllers/stream_controller.py | 2 +- .../domain/events/client_event.py | 0 .../domain/models/agent_request.py | 0 .../domain/models/agent_session.py | 0 .../infrastructure/ids/request_id_factory.py | 0 .../infrastructure/ids/session_id_factory.py | 0 .../logging/request_trace_logger.py | 22 +- .../logging/trace_file_path_builder.py | 0 .../logging/trace_markdown_writer.py | 0 .../stores/in_memory_request_store.py | 2 +- .../stores/in_memory_session_store.py | 2 +- .../infrastructure/streaming/replay_buffer.py | 2 +- .../infrastructure/streaming/sse_encoder.py | 2 +- .../streaming/sse_event_channel.py | 4 +- src/app/modules/{agent_api => api}/module.py | 16 +- .../{agent_api => api}/public_router.py | 6 +- src/app/modules/application.py | 78 ++- src/app/modules/chat/README.md | 98 --- src/app/modules/chat/__init__.py | 0 src/app/modules/chat/dialog_store.py | 33 - src/app/modules/chat/direct_service.py | 71 --- src/app/modules/chat/evidence_gate.py | 62 -- src/app/modules/chat/module.py | 112 ---- src/app/modules/chat/repository.py | 93 --- src/app/modules/chat/service.py | 288 --------- src/app/modules/chat/session_resolver.py | 36 -- src/app/modules/chat/task_store.py | 38 -- src/app/modules/orchestration/__init__.py | 3 - .../adapters/intent_router_adapter.py | 11 - .../adapters/llm_chat_adapter.py | 19 - .../context/execution_context.py | 20 - src/app/schemas/agent_api.py | 2 +- .../chat/test_chat_api_simple_code_explain.py | 70 --- tests/unit_tests/chat/test_direct_service.py | 61 -- 97 files changed, 815 insertions(+), 5161 deletions(-) create mode 100644 _process.zip delete mode 100644 runtime_traces/agent_requests/20260326/req_02f645dac43746099e9ffb6663899b9e.md delete mode 100644 runtime_traces/agent_requests/20260326/req_18f4de6d677946739b66c533a2f95b95.md delete mode 100644 runtime_traces/agent_requests/20260326/req_4a5e5dc2bdec4045971575d2434bfa03.md delete mode 100644 runtime_traces/agent_requests/20260326/req_6556fd999c2548e58071a1d1518e35cb.md delete mode 100644 runtime_traces/agent_requests/20260326/req_68ad6398d5f2415e95c5a602a52dce2e.md delete mode 100644 runtime_traces/agent_requests/20260326/req_7e47a8b3458741568e2b974079a74cb5.md delete mode 100644 runtime_traces/agent_requests/20260326/req_90367a184bfa4638bd9c473e89d2b5b7.md delete mode 100644 runtime_traces/agent_requests/20260326/req_9827472a716044cf983328dc959c4042.md delete mode 100644 runtime_traces/agent_requests/20260326/req_bc0ca07c5a344978bdbfa3311283f4c8.md delete mode 100644 runtime_traces/agent_requests/20260326/req_c1a55e97a0aa45ce8c4c6c453a9ec080.md delete mode 100644 runtime_traces/agent_requests/20260327/req_c0af91b38de3436e82a75b9c3664c95a.md create mode 100644 runtime_traces/agent_requests/20260401/req_33758fd1ed834100a23fe95871b34181.md create mode 100644 src/app/modules/agent/observability/module_trace.py create mode 100644 src/app/modules/agent/orchestration/__init__.py create mode 100644 src/app/modules/agent/orchestration/adapters/intent_router_adapter.py create mode 100644 src/app/modules/agent/orchestration/adapters/llm_chat_adapter.py rename src/app/modules/{ => agent}/orchestration/adapters/task_runtime_adapter.py (90%) create mode 100644 src/app/modules/agent/orchestration/context/execution_context.py rename src/app/modules/{ => agent}/orchestration/facade.py (74%) rename src/app/modules/{ => agent}/orchestration/messaging/client_message_publisher.py (70%) rename src/app/modules/{ => agent}/orchestration/messaging/status_message_factory.py (84%) rename src/app/modules/{ => agent}/orchestration/messaging/user_message_factory.py (84%) rename src/app/modules/{ => agent}/orchestration/processes/registry.py (69%) rename src/app/modules/{ => agent}/orchestration/processes/v1/process.py (100%) create mode 100644 src/app/modules/agent/orchestration/processes/v1/prompt_payload_builder.py create mode 100644 src/app/modules/agent/orchestration/processes/v1/prompts.yml create mode 100644 src/app/modules/agent/orchestration/processes/v1/simple_llm_workflow.py rename src/app/modules/{ => agent}/orchestration/processes/v1/steps/bootstrap_step.py (63%) create mode 100644 src/app/modules/agent/orchestration/processes/v1/steps/execute_llm_workflow_step.py rename src/app/modules/{ => agent}/orchestration/processes/v1/steps/finalize_step.py (65%) rename src/app/modules/{ => agent}/orchestration/processes/v1/steps/run_llm_step.py (83%) rename src/app/modules/{ => agent}/orchestration/processes/v2/README.md (98%) rename src/app/modules/{ => agent}/orchestration/processes/v2/process.py (100%) create mode 100644 src/app/modules/agent/orchestration/processes/v2/prompt_payload_builder.py create mode 100644 src/app/modules/agent/orchestration/processes/v2/prompt_selector.py create mode 100644 src/app/modules/agent/orchestration/processes/v2/prompts.yml rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/execute_documentation_workflow_step.py (58%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/execute_fallback_workflow_step.py (70%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py (55%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/execute_openapi_workflow_step.py (56%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/route_intent_step.py (78%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/run_task_workflow_step.py (84%) rename src/app/modules/{ => agent}/orchestration/processes/v2/steps/workflow_step_base.py (88%) rename src/app/modules/{ => agent}/orchestration/runtime/process_runner.py (79%) rename src/app/modules/{ => agent}/orchestration/v2_progress.py (83%) delete mode 100644 src/app/modules/agent_api/__init__.py create mode 100644 src/app/modules/api/__init__.py rename src/app/modules/{agent_api => api}/application/request_service.py (72%) rename src/app/modules/{agent_api => api}/application/session_service.py (85%) rename src/app/modules/{agent_api => api}/application/stream_service.py (83%) rename src/app/modules/{agent_api => api}/controllers/request_controller.py (94%) rename src/app/modules/{agent_api => api}/controllers/session_controller.py (94%) rename src/app/modules/{agent_api => api}/controllers/stream_controller.py (93%) rename src/app/modules/{agent_api => api}/domain/events/client_event.py (100%) rename src/app/modules/{agent_api => api}/domain/models/agent_request.py (100%) rename src/app/modules/{agent_api => api}/domain/models/agent_session.py (100%) rename src/app/modules/{agent_api => api}/infrastructure/ids/request_id_factory.py (100%) rename src/app/modules/{agent_api => api}/infrastructure/ids/session_id_factory.py (100%) rename src/app/modules/{agent_api => api}/infrastructure/logging/request_trace_logger.py (77%) rename src/app/modules/{agent_api => api}/infrastructure/logging/trace_file_path_builder.py (100%) rename src/app/modules/{agent_api => api}/infrastructure/logging/trace_markdown_writer.py (100%) rename src/app/modules/{agent_api => api}/infrastructure/stores/in_memory_request_store.py (86%) rename src/app/modules/{agent_api => api}/infrastructure/stores/in_memory_session_store.py (86%) rename src/app/modules/{agent_api => api}/infrastructure/streaming/replay_buffer.py (88%) rename src/app/modules/{agent_api => api}/infrastructure/streaming/sse_encoder.py (87%) rename src/app/modules/{agent_api => api}/infrastructure/streaming/sse_event_channel.py (90%) rename src/app/modules/{agent_api => api}/module.py (50%) rename src/app/modules/{agent_api => api}/public_router.py (87%) delete mode 100644 src/app/modules/chat/README.md delete mode 100644 src/app/modules/chat/__init__.py delete mode 100644 src/app/modules/chat/dialog_store.py delete mode 100644 src/app/modules/chat/direct_service.py delete mode 100644 src/app/modules/chat/evidence_gate.py delete mode 100644 src/app/modules/chat/module.py delete mode 100644 src/app/modules/chat/repository.py delete mode 100644 src/app/modules/chat/service.py delete mode 100644 src/app/modules/chat/session_resolver.py delete mode 100644 src/app/modules/chat/task_store.py delete mode 100644 src/app/modules/orchestration/__init__.py delete mode 100644 src/app/modules/orchestration/adapters/intent_router_adapter.py delete mode 100644 src/app/modules/orchestration/adapters/llm_chat_adapter.py delete mode 100644 src/app/modules/orchestration/context/execution_context.py delete mode 100644 tests/unit_tests/chat/test_chat_api_simple_code_explain.py delete mode 100644 tests/unit_tests/chat/test_direct_service.py diff --git a/_process.zip b/_process.zip new file mode 100644 index 0000000000000000000000000000000000000000..8577803259644e01462a652e90d592dc0eb0b8d0 GIT binary patch literal 17467 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 literal 0 HcmV?d00001 diff --git a/runtime_traces/agent_requests/20260326/req_02f645dac43746099e9ffb6663899b9e.md b/runtime_traces/agent_requests/20260326/req_02f645dac43746099e9ffb6663899b9e.md deleted file mode 100644 index 5dbb068..0000000 --- a/runtime_traces/agent_requests/20260326/req_02f645dac43746099e9ffb6663899b9e.md +++ /dev/null @@ -1,130 +0,0 @@ -# Request Trace: req_02f645dac43746099e9ffb6663899b9e - -- session_id: as_54f9e993483f499489530ad2d074ad95 -- active_rag_session_id: 333b97ca-1715-47bc-9944-fbacbb5c8108 -- process_version: v1 -- created_at: 2026-03-26T19:28:59.371830+00:00 - -## User Message -что можешт сделать? - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T19:28:59.372678+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v1" - }, - "created_at": "2026-03-26T19:28:59.372768+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step run_llm -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Отправляю запрос пользователя в LLM.", - "payload": {}, - "created_at": "2026-03-26T19:28:59.373014+00:00" -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Ответ от LLM получен.", - "payload": {}, - "created_at": "2026-03-26T19:29:06.352287+00:00" -} -``` - -## Step run_llm -```json -{ - "status": "completed", - "details": { - "answer_length": 1101 - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Я могу помочь тебе с различными задачами и вопросами, например:\n\n1. **Обучение и образование:** объясню сложные темы простым языком, помогу разобраться в математике, физике, химии, истории и других предметах.\n2. **Программирование:** подскажу по вопросам написания кода на разных языках программирования (Python, JavaScript, C++, Java и другие), объясню алгоритмы и структуры данных.\n3. **Написание текстов:** помогу составить письмо, эссе, статью или доклад; а также проверю грамматику и стилистику готового текста.\n4. **Помощь с переводами:** переведу текст на русский язык или с русского на другой язык.\n5. **Ответы на вопросы:** отвечу на любые вопросы общего характера: от научных до бытовых.\n6. **Генерация контента:** создам идеи для креативных проектов, рассказов, сценариев, стихов и песен.\n7. **Советы и рекомендации:** дам советы по саморазвитию, личностному росту, планированию времени и улучшению продуктивности.\n8. **Развлечения:** сыграем в интеллектуальные игры, загадки или просто поболтаем о чем-нибудь интересном.\n\nЕсли у тебя есть конкретная задача — расскажи, я постараюсь помочь!", - "payload": {}, - "created_at": "2026-03-26T19:29:06.353995+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T19:29:06.354352+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Я могу помочь тебе с различными задачами и вопросами, например:\n\n1. **Обучение и образование:** объясню сложные темы простым языком, помогу разобраться в математике, физике, химии, истории и других предметах.\n2. **Программирование:** подскажу по вопросам написания кода на разных языках программирования (Python, JavaScript, C++, Java и другие), объясню алгоритмы и структуры данных.\n3. **Написание текстов:** помогу составить письмо, эссе, статью или доклад; а также проверю грамматику и стилистику готового текста.\n4. **Помощь с переводами:** переведу текст на русский язык или с русского на другой язык.\n5. **Ответы на вопросы:** отвечу на любые вопросы общего характера: от научных до бытовых.\n6. **Генерация контента:** создам идеи для креативных проектов, рассказов, сценариев, стихов и песен.\n7. **Советы и рекомендации:** дам советы по саморазвитию, личностному росту, планированию времени и улучшению продуктивности.\n8. **Развлечения:** сыграем в интеллектуальные игры, загадки или просто поболтаем о чем-нибудь интересном.\n\nЕсли у тебя есть конкретная задача — расскажи, я постараюсь помочь!", - "completed_at": "2026-03-26T19:29:06.354735+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_18f4de6d677946739b66c533a2f95b95.md b/runtime_traces/agent_requests/20260326/req_18f4de6d677946739b66c533a2f95b95.md deleted file mode 100644 index 6571d93..0000000 --- a/runtime_traces/agent_requests/20260326/req_18f4de6d677946739b66c533a2f95b95.md +++ /dev/null @@ -1,130 +0,0 @@ -# Request Trace: req_18f4de6d677946739b66c533a2f95b95 - -- session_id: as_c1a11b6a3e3e4138acbfd7848e784ef2 -- active_rag_session_id: 1d011692-a2a1-4a7d-af17-dc35ac7e158b -- process_version: v1 -- created_at: 2026-03-26T19:23:38.293838+00:00 - -## User Message -Ты здесь? - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T19:23:38.297116+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v1" - }, - "created_at": "2026-03-26T19:23:38.297199+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step run_llm -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Отправляю запрос пользователя в LLM.", - "payload": {}, - "created_at": "2026-03-26T19:23:38.297331+00:00" -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Ответ от LLM получен.", - "payload": {}, - "created_at": "2026-03-26T19:23:39.473901+00:00" -} -``` - -## Step run_llm -```json -{ - "status": "completed", - "details": { - "answer_length": 26 - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Да, я здесь! Чем займёмся?", - "payload": {}, - "created_at": "2026-03-26T19:23:39.475294+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T19:23:39.475469+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Да, я здесь! Чем займёмся?", - "completed_at": "2026-03-26T19:23:39.475706+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_4a5e5dc2bdec4045971575d2434bfa03.md b/runtime_traces/agent_requests/20260326/req_4a5e5dc2bdec4045971575d2434bfa03.md deleted file mode 100644 index 21bb237..0000000 --- a/runtime_traces/agent_requests/20260326/req_4a5e5dc2bdec4045971575d2434bfa03.md +++ /dev/null @@ -1,245 +0,0 @@ -# Request Trace: req_4a5e5dc2bdec4045971575d2434bfa03 - -- session_id: as_9d330fb7437341f18539f819dcace95f -- active_rag_session_id: 34007a57-cf69-45f1-a0cd-c6a39bd81d42 -- process_version: v2 -- created_at: 2026-03-26T20:16:30.377725+00:00 - -## User Message -Как работает метод health/ - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:16:30.378388+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:16:30.378460+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:16:30.378707+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: CODE_QA / EXPLAIN.", - "payload": { - "intent": "CODE_QA", - "sub_intent": "EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:16:30.380022+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "CODE_QA", - "sub_intent": "EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_fallback -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow fallback.", - "payload": { - "intent": "CODE_QA", - "sub_intent": "EXPLAIN" - }, - "created_at": "2026-03-26T20:16:30.380251+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "not used in fallback workflow" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:16:30.380631+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: fallback" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:16:32.313137+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "not applied in fallback workflow" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:16:32.314125+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow fallback завершен.", - "payload": { - "workflow_id": "fallback", - "result_type": "answer", - "answer_length": 190 - }, - "created_at": "2026-03-26T20:16:32.314639+00:00" -} -``` - -## Step workflow_fallback -```json -{ - "status": "completed", - "details": { - "workflow_id": "fallback", - "meta": { - "workflow_id": "fallback", - "intent": "CODE_QA" - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "```yaml\npaths:\n /health/:\n get:\n summary: Проверка состояния здоровья сервиса\n responses:\n '200':\n description: Успешная проверка, сервис работает корректно\n```", - "payload": {}, - "created_at": "2026-03-26T20:16:32.315272+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:16:32.317145+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "```yaml\npaths:\n /health/:\n get:\n summary: Проверка состояния здоровья сервиса\n responses:\n '200':\n description: Успешная проверка, сервис работает корректно\n```", - "completed_at": "2026-03-26T20:16:32.317835+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_6556fd999c2548e58071a1d1518e35cb.md b/runtime_traces/agent_requests/20260326/req_6556fd999c2548e58071a1d1518e35cb.md deleted file mode 100644 index 2685a80..0000000 --- a/runtime_traces/agent_requests/20260326/req_6556fd999c2548e58071a1d1518e35cb.md +++ /dev/null @@ -1,137 +0,0 @@ -# Request Trace: req_6556fd999c2548e58071a1d1518e35cb - -- session_id: as_a4d91e7eca7547e0a37dc4dc6492cf9b -- active_rag_session_id: aa7d907e-2a76-4bf7-99dc-5a400a417f8c -- process_version: v2 -- created_at: 2026-03-26T20:30:43.129032+00:00 - -## User Message -Какие методы есть в API? - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:30:43.130630+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:30:43.130808+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:30:43.131176+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / API_METHOD_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:30:43.134624+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN" - }, - "created_at": "2026-03-26T20:30:43.134934+00:00" -} -``` - -## Error -```json -{ - "status": "error", - "error": { - "code": "agent_api_runtime_error", - "desc": "Agent request failed unexpectedly.", - "module": "agent" - }, - "completed_at": "2026-03-26T20:30:50.387415+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Во время обработки запроса произошла ошибка.", - "payload": { - "code": "agent_api_runtime_error" - }, - "created_at": "2026-03-26T20:30:50.388003+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_68ad6398d5f2415e95c5a602a52dce2e.md b/runtime_traces/agent_requests/20260326/req_68ad6398d5f2415e95c5a602a52dce2e.md deleted file mode 100644 index 9b81ccc..0000000 --- a/runtime_traces/agent_requests/20260326/req_68ad6398d5f2415e95c5a602a52dce2e.md +++ /dev/null @@ -1,579 +0,0 @@ -# Request Trace: req_68ad6398d5f2415e95c5a602a52dce2e - -- session_id: as_d77b5786695d4968a0d7faac4292f7bb -- active_rag_session_id: 47aeddca-0011-45e4-b99c-70f1a242f2e4 -- process_version: v2 -- created_at: 2026-03-26T20:24:27.706845+00:00 - -## User Message -что такое runtime health? - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:24:27.712585+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:24:27.712691+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:24:27.713057+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / ENTITY_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:24:27.717346+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN" - }, - "created_at": "2026-03-26T20:24:27.717771+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D3_ENTITY_CATALOG, D5_RELATION_GRAPH, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D3_ENTITY_CATALOG, D5_RELATION_GRAPH, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D3_ENTITY_CATALOG: 8 hits", - "D5_RELATION_GRAPH: 8 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:24:34.078020+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: answered" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:24:34.078859+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: allow", - "reason: evidence_sufficient", - "satisfied: retrieval_non_empty" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:24:34.080566+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-26T20:24:34.082685+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "allow", - "reason": "evidence_sufficient", - "missing": [], - "satisfied": [ - "retrieval_non_empty" - ] - }, - "created_at": "2026-03-26T20:24:34.088672+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 1504 - }, - "created_at": "2026-03-26T20:24:34.091543+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "layers_used": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 1, - "facts_found": 0, - "relations_found": 2, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "none", - "matched_anchor_value": null, - "exact_anchor_match": false, - "docs_layers_requested": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D3_ENTITY_CATALOG": { - "hits": 8, - "top_ids": [ - "architecture.telegram_notify_app", - "domain.runtime_health", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "RuntimeManager", - "WorkerStatus", - "WorkerHealth", - "TelegramNotifyWorker" - ] - }, - "D5_RELATION_GRAPH": { - "hits": 8, - "top_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4", - "79bdb0a74713df6064008179ca8c5c186dc23e4e1c5e2c9607b0bdddeba50f93", - "af2a4f7c1b677b908410ba720915405e10dbd0870e30a3c6e3aa2468550ddf0e", - "4e0bbeb8e97e25091a30412d8631b1d9f6bb58b5694d8b7cc632af69a9262ea8" - ], - "top_sections": [ - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by", - "domain.runtime_health:parent", - "domain.runtime_health:part_of", - "architecture.telegram_notify_app:child" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "api.control_actions_endpoint", - "architecture.telegram_notify_app", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "Сущность runtime health", - "HTTP API /actions/{action}", - "Архитектура Telegram Notify App", - "Цикл отправки уведомлений в Telegram" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "logic.telegram_notification_loop:Summary" - ] - } - }, - "query_entity_candidates": [ - "runtime health" - ], - "resolved_entity_candidates": [], - "query_anchor_candidates": [], - "resolved_anchor_candidates": [ - "architecture.telegram_notify_app", - "docs/documentation/architecture/telegram-notify-app-overview.md", - "domain.runtime_health", - "docs/documentation/domain/runtime-health-entity.md", - "logic.telegram_notification_loop", - "docs/documentation/logic/telegram-notification-loop.md", - "api.control_actions_endpoint", - "docs/documentation/api/control-actions-endpoint.md", - "docs/README.md" - ], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [ - "architecture.telegram_notify_app", - "architecture.telegram_notify_app_overview", - "domain.runtime_health", - "logic.telegram_notification_loop", - "api.control_actions_endpoint", - "api.actions_endpoint", - "docs/README.md" - ], - "doc_paths": [ - "docs/documentation/architecture/telegram-notify-app-overview.md", - "docs/documentation/domain/runtime-health-entity.md", - "docs/documentation/logic/telegram-notification-loop.md", - "docs/documentation/api/control-actions-endpoint.md", - "docs/README.md" - ], - "doc_titles": [ - "RuntimeManager", - "WorkerStatus", - "WorkerHealth", - "TelegramNotifyWorker", - "TelegramSendService", - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by", - "domain.runtime_health:parent", - "domain.runtime_health:part_of", - "architecture.telegram_notify_app:child", - "logic.telegram_notification_loop:related_api", - "architecture.telegram_notify_app:related_api", - "Сущность runtime health", - "HTTP API /actions/{action}", - "Архитектура Telegram Notify App", - "Цикл отправки уведомлений в Telegram", - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "logic.telegram_notification_loop:Summary" - ], - "relation_hits_count": 2, - "relation_targets": [ - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by" - ], - "selected_doc_ids": [ - "domain.runtime_health", - "architecture.telegram_notify_app" - ], - "selected_fact_ids": [], - "selected_relation_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4" - ], - "selected_chunk_ids": [ - "domain.runtime_health" - ], - "selected_entity_ids": [ - "architecture.telegram_notify_app", - "domain.runtime_health" - ], - "selected_workflow_ids": [], - "fallback_doc_hits_count": 2, - "fallback_used": false, - "fact_hits": 0, - "entity_hits": 0, - "evidence_summary": { - "documents": 1, - "facts": 0, - "entities": 2, - "workflows": 0, - "relations": 2, - "chunks": 1, - "selected_doc_ids": [ - "domain.runtime_health", - "architecture.telegram_notify_app" - ], - "selected_fact_ids": [], - "selected_relation_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4" - ], - "selected_chunk_ids": [ - "domain.runtime_health" - ], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "allow", - "gate_decision_reason": "evidence_sufficient", - "gate_missing_requirements": [], - "gate_satisfied_requirements": [ - "retrieval_non_empty" - ], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"что такое runtime health?\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"ENTITY_EXPLAIN\",\n \"documents\": [\n {\n \"layer\": \"D1_DOCUMENT_CATALOG\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"Сущность runtime health\",\n \"content\": \"`runtime health` — доменная модель наблюдаемости сервиса, которая описывает общее состояние runtime и состояние отдельных компонентов. Для `test_echo_app` главным объектом наблюдения является компонент `telegram_notify`, у которого важны статус, время старта и количество успешных отправок. Эта модель используется для внешнего health-monitoring и внутренней диагностики.\",\n \"metadata\": {\n \"name\": \"runtime_health\",\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"type\": \"domain_entity\",\n \"layer\": \"domain\",\n \"links\": [\n {\n \"type\": \"part_of\",\n \"target\": \"architecture.telegram_notify_app\"\n },\n {\n \"type\": \"used_by\",\n \"target\": \"api.health_endpoint\"\n },\n {\n \"type\": \"related_logic\",\n \"target\": \"logic.telegram_notification_loop\"\n }\n ],\n \"owner\": null,\n \"title\": \"Сущность runtime health\",\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"parent\": \"architecture.telegram_notify_app\",\n \"status\": \"draft\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"children\": [],\n \"doc_kind\": \"misc\",\n \"entities\": [\n \"WorkerHealth\",\n \"WorkerStatus\",\n \"TelegramNotifyWorker\"\n ],\n \"updated_at\": \"2026-03-20\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"summary_text\": \"`runtime health` — доменная модель наблюдаемости сервиса, которая описывает общее состояние runtime и состояние отдельных компонентов. Для `test_echo_app` главным объектом наблюдения является компонент `telegram_notify`, у которого важны статус, время старта и количество успешных отправок. Эта модель используется для внешнего health-monitoring и внутренней диагностики.\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"facts\": [],\n \"entities\": [\n {\n \"layer\": \"D3_ENTITY_CATALOG\",\n \"path\": \"docs/documentation/architecture/telegram-notify-app-overview.md\",\n \"title\": \"RuntimeManager\",\n \"content\": \"RuntimeManager\",\n \"metadata\": {\n \"tags\": [\n \"architecture\",\n \"telegram\",\n \"plba\",\n \"runtime\"\n ],\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"8b1cc9f823e55bddbc825466d10a0fdb4ca4354ac02d91f9e2aef7b7d2ec8256\",\n \"doc_version\": null,\n \"document_id\": \"architecture.telegram_notify_app\",\n \"entity_name\": \"RuntimeManager\",\n \"source_path\": \"docs/documentation/architecture/telegram-notify-app-overview.md\",\n \"artifact_type\": null,\n \"document_type\": \"architecture_overview\",\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D3_ENTITY_CATALOG\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"WorkerStatus\",\n \"content\": \"WorkerStatus\",\n \"metadata\": {\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"entity_name\": \"WorkerStatus\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"document_type\": \"domain_entity\",\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"workflows\": [],\n \"relations\": [\n {\n \"layer\": \"D5_RELATION_GRAPH\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:related_logic\",\n \"content\": \"domain.runtime_health related_logic logic.telegram_notification_loop\",\n \"metadata\": {\n \"owner\": null,\n \"anchor\": \"frontmatter.links\",\n \"doc_id\": null,\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"source_id\": \"domain.runtime_health\",\n \"target_id\": \"logic.telegram_notification_loop\",\n \"doc_version\": null,\n \"relation_id\": \"8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"relation_type\": \"related_logic\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D5_RELATION_GRAPH\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:used_by\",\n \"content\": \"domain.runtime_health used_by api.health_endpoint\",\n \"metadata\": {\n \"owner\": null,\n \"anchor\": \"frontmatter.links\",\n \"doc_id\": null,\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"source_id\": \"domain.runtime_health\",\n \"target_id\": \"api.health_endpoint\",\n \"doc_version\": null,\n \"relation_id\": \"801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"relation_type\": \"used_by\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"chunks\": [\n {\n \"layer\": \"D0_DOC_CHUNKS\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:Details\",\n \"content\": \"Health payload собирается runtime'ом и включает общий статус приложения и список компонентов. Для worker `telegram_notify` используются состояния `ok`, `degraded` и `unhealthy`, которые зависят от жизненного цикла фонового потока, наличия Telegram credentials и результата последней отправки.\\n\\nКлючевые metadata поля компонента — `app_started_at` и `notifications_sent`. Первое отражает UTC timestamp запуска worker'а, второе показывает число успешных доставок с момента старта runtime. Эти поля позволяют отделить реальный жизненный цикл worker'а от простого факта, что HTTP API отвечает.\\n\\nСущность `runtime health` связывает техническое состояние worker'а с операторским представлением сервиса. Через нее клиент `/health` получает не только общий статус процесса, но и диагностический контекст, достаточный для базового мониторинга и troubleshooting.\",\n \"metadata\": {\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"type\": \"domain_entity\",\n \"order\": 2,\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"doc_kind\": \"misc\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"section_path\": \"Details\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"section_title\": \"Details\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ]\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 8461, - "tokens_in_estimate": 2214 - } - }, - "answer_mode": "answered", - "degrade_reason": null, - "degraded_reason": null, - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "### 1. Краткое описание\nRuntime Health – это доменная модель наблюдаемости сервиса, описывающая текущее состояние runtime и его компонентов. Она используется для внешнего мониторинга здоровья приложений и внутренней диагностики, позволяя получать детальную информацию о состоянии сервиса и проводить базовые операции по устранению неполадок.\n\n### 2. Основные элементы\n- **Компоненты**: \n - Worker `telegram_notify`\n- **Состояния компонентов**:\n - ok, degraded, unhealthy\n- **Metadata поля**:\n - app_started_at (время старта)\n - notifications_sent (количество успешных отправок)\n- **Связанные сущности**:\n - WorkerHealth, WorkerStatus, TelegramNotifyWorker\n\n### 3. Как это работает\n- Модель собирает данные о текущем статусе приложения и его компонентов в виде payload.\n- Состояние компонента определяется на основе жизненного цикла фоновых потоков, наличия необходимых конфигураций (например, Telegram credentials), а также результатов последних операций.\n- Метаданные включают точное время старта компонента и количество успешно выполненных задач, обеспечивая более глубокое понимание текущего состояния службы.\n- Информация предоставляется через endpoint `/health`, который возвращает не только текущий статус, но и детализированный диагностический контекст.\n\n### 4. Связи с другими частями системы\n- **Используется логикой**: логика `telegram_notification_loop`\n- **Используется API**: endpoint `/health` (api.health_endpoint)\n- **Часть архитектуры**: архитектура приложения `telegram_notify_app`", - "payload": {}, - "created_at": "2026-03-26T20:24:34.094633+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:24:34.094968+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "### 1. Краткое описание\nRuntime Health – это доменная модель наблюдаемости сервиса, описывающая текущее состояние runtime и его компонентов. Она используется для внешнего мониторинга здоровья приложений и внутренней диагностики, позволяя получать детальную информацию о состоянии сервиса и проводить базовые операции по устранению неполадок.\n\n### 2. Основные элементы\n- **Компоненты**: \n - Worker `telegram_notify`\n- **Состояния компонентов**:\n - ok, degraded, unhealthy\n- **Metadata поля**:\n - app_started_at (время старта)\n - notifications_sent (количество успешных отправок)\n- **Связанные сущности**:\n - WorkerHealth, WorkerStatus, TelegramNotifyWorker\n\n### 3. Как это работает\n- Модель собирает данные о текущем статусе приложения и его компонентов в виде payload.\n- Состояние компонента определяется на основе жизненного цикла фоновых потоков, наличия необходимых конфигураций (например, Telegram credentials), а также результатов последних операций.\n- Метаданные включают точное время старта компонента и количество успешно выполненных задач, обеспечивая более глубокое понимание текущего состояния службы.\n- Информация предоставляется через endpoint `/health`, который возвращает не только текущий статус, но и детализированный диагностический контекст.\n\n### 4. Связи с другими частями системы\n- **Используется логикой**: логика `telegram_notification_loop`\n- **Используется API**: endpoint `/health` (api.health_endpoint)\n- **Часть архитектуры**: архитектура приложения `telegram_notify_app`", - "completed_at": "2026-03-26T20:24:34.095333+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_7e47a8b3458741568e2b974079a74cb5.md b/runtime_traces/agent_requests/20260326/req_7e47a8b3458741568e2b974079a74cb5.md deleted file mode 100644 index 577b468..0000000 --- a/runtime_traces/agent_requests/20260326/req_7e47a8b3458741568e2b974079a74cb5.md +++ /dev/null @@ -1,507 +0,0 @@ -# Request Trace: req_7e47a8b3458741568e2b974079a74cb5 - -- session_id: as_9d330fb7437341f18539f819dcace95f -- active_rag_session_id: 34007a57-cf69-45f1-a0cd-c6a39bd81d42 -- process_version: v2 -- created_at: 2026-03-26T20:17:03.006084+00:00 - -## User Message -Опиши что делает /health - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:17:03.012795+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:17:03.013195+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:17:03.015461+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / API_METHOD_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:17:03.021546+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN" - }, - "created_at": "2026-03-26T20:17:03.021922+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D2_FACT_INDEX: 8 hits", - "D4_WORKFLOW_INDEX: 2 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:05.913522+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: degraded" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:05.914387+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: reject", - "reason: not_found_exact_anchor", - "missing: retrieval_non_empty, exact_anchor_match" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:05.914679+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-26T20:17:05.914992+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "reject", - "reason": "not_found_exact_anchor", - "missing": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "satisfied": [] - }, - "created_at": "2026-03-26T20:17:05.915395+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 57 - }, - "created_at": "2026-03-26T20:17:05.916924+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "layers_used": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 0, - "facts_found": 0, - "relations_found": 0, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "endpoint", - "matched_anchor_value": "/health", - "exact_anchor_match": false, - "docs_layers_requested": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D2_FACT_INDEX": { - "hits": 8, - "top_ids": [ - "8879bb1d923dff0d783ef202f98fdfe5b774870912b2cd261ae127003daffacb", - "5bc72ce58bd31c654a380034beb59f47224e9b03bd306503a7f0b8634008409d", - "5a471b2380ec55b5866b99bb337b92cb78b91051cf616593937f1603d1011fa6", - "a31bbca2eb31ffb6655bcec7ff07b8cf2b6c7416610cf58dfc1e0c737df12fe1", - "e64a6aebed07076a1cccb4d6537b5f54489e25eda776e0230b3c2ff1df2ae648" - ], - "top_sections": [ - "domain.runtime_health:mentions_entity", - "docs/README.md:doc_list_item" - ] - }, - "D4_WORKFLOW_INDEX": { - "hits": 2, - "top_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "top_sections": [ - "Scenario" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "top_sections": [ - "Сущность runtime health", - "Readme", - "HTTP API /send", - "HTTP API /actions/{action}" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "architecture.telegram_notify_app" - ], - "top_sections": [ - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "architecture.telegram_notify_app:Details" - ] - } - }, - "query_entity_candidates": [], - "resolved_entity_candidates": [], - "query_anchor_candidates": [ - "/health" - ], - "resolved_anchor_candidates": [], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [], - "doc_paths": [], - "doc_titles": [], - "relation_hits_count": 0, - "relation_targets": [], - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "selected_entity_ids": [], - "selected_workflow_ids": [], - "fallback_doc_hits_count": 0, - "fallback_used": false, - "fact_hits": 0, - "entity_hits": 0, - "evidence_summary": { - "documents": 0, - "facts": 0, - "entities": 0, - "workflows": 0, - "relations": 0, - "chunks": 0, - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "reject", - "gate_decision_reason": "not_found_exact_anchor", - "gate_missing_requirements": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "gate_satisfied_requirements": [], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"Опиши что делает /health\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"API_METHOD_EXPLAIN\",\n \"documents\": [],\n \"facts\": [],\n \"entities\": [],\n \"workflows\": [],\n \"relations\": [],\n \"chunks\": []\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 225, - "tokens_in_estimate": 155 - } - }, - "answer_mode": "degraded", - "degrade_reason": "not_found_exact_anchor", - "degraded_reason": "not_found_exact_anchor", - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Не найдено точное совпадение по запрошенному docs anchor.", - "payload": {}, - "created_at": "2026-03-26T20:17:05.919336+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:17:05.919876+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Не найдено точное совпадение по запрошенному docs anchor.", - "completed_at": "2026-03-26T20:17:05.920288+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_90367a184bfa4638bd9c473e89d2b5b7.md b/runtime_traces/agent_requests/20260326/req_90367a184bfa4638bd9c473e89d2b5b7.md deleted file mode 100644 index 9fdfd43..0000000 --- a/runtime_traces/agent_requests/20260326/req_90367a184bfa4638bd9c473e89d2b5b7.md +++ /dev/null @@ -1,507 +0,0 @@ -# Request Trace: req_90367a184bfa4638bd9c473e89d2b5b7 - -- session_id: as_a4d91e7eca7547e0a37dc4dc6492cf9b -- active_rag_session_id: aa7d907e-2a76-4bf7-99dc-5a400a417f8c -- process_version: v2 -- created_at: 2026-03-26T20:30:20.880909+00:00 - -## User Message -как работает метод /health - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:30:20.882319+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:30:20.882447+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:30:20.882855+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / API_METHOD_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:30:20.886041+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN" - }, - "created_at": "2026-03-26T20:30:20.886364+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D2_FACT_INDEX: 8 hits", - "D4_WORKFLOW_INDEX: 2 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:30:24.170029+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: degraded" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:30:24.170817+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: reject", - "reason: not_found_exact_anchor", - "missing: retrieval_non_empty, exact_anchor_match" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:30:24.172958+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-26T20:30:24.173401+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "reject", - "reason": "not_found_exact_anchor", - "missing": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "satisfied": [] - }, - "created_at": "2026-03-26T20:30:24.173890+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 57 - }, - "created_at": "2026-03-26T20:30:24.174256+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "layers_used": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 0, - "facts_found": 0, - "relations_found": 0, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "endpoint", - "matched_anchor_value": "/health", - "exact_anchor_match": false, - "docs_layers_requested": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D2_FACT_INDEX": { - "hits": 8, - "top_ids": [ - "8879bb1d923dff0d783ef202f98fdfe5b774870912b2cd261ae127003daffacb", - "5bc72ce58bd31c654a380034beb59f47224e9b03bd306503a7f0b8634008409d", - "5a471b2380ec55b5866b99bb337b92cb78b91051cf616593937f1603d1011fa6", - "a31bbca2eb31ffb6655bcec7ff07b8cf2b6c7416610cf58dfc1e0c737df12fe1", - "e64a6aebed07076a1cccb4d6537b5f54489e25eda776e0230b3c2ff1df2ae648" - ], - "top_sections": [ - "domain.runtime_health:mentions_entity", - "docs/README.md:doc_list_item" - ] - }, - "D4_WORKFLOW_INDEX": { - "hits": 2, - "top_ids": [ - "api.control_actions_endpoint", - "api.send_message_endpoint" - ], - "top_sections": [ - "Scenario" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "top_sections": [ - "Сущность runtime health", - "Readme", - "HTTP API /send", - "HTTP API /actions/{action}" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "architecture.telegram_notify_app" - ], - "top_sections": [ - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "architecture.telegram_notify_app:Details" - ] - } - }, - "query_entity_candidates": [], - "resolved_entity_candidates": [], - "query_anchor_candidates": [ - "/health" - ], - "resolved_anchor_candidates": [], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [], - "doc_paths": [], - "doc_titles": [], - "relation_hits_count": 0, - "relation_targets": [], - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "selected_entity_ids": [], - "selected_workflow_ids": [], - "fallback_doc_hits_count": 0, - "fallback_used": false, - "fact_hits": 0, - "entity_hits": 0, - "evidence_summary": { - "documents": 0, - "facts": 0, - "entities": 0, - "workflows": 0, - "relations": 0, - "chunks": 0, - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "reject", - "gate_decision_reason": "not_found_exact_anchor", - "gate_missing_requirements": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "gate_satisfied_requirements": [], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"как работает метод /health\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"API_METHOD_EXPLAIN\",\n \"documents\": [],\n \"facts\": [],\n \"entities\": [],\n \"workflows\": [],\n \"relations\": [],\n \"chunks\": []\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 227, - "tokens_in_estimate": 155 - } - }, - "answer_mode": "degraded", - "degrade_reason": "not_found_exact_anchor", - "degraded_reason": "not_found_exact_anchor", - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Не найдено точное совпадение по запрошенному docs anchor.", - "payload": {}, - "created_at": "2026-03-26T20:30:24.177128+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:30:24.177918+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Не найдено точное совпадение по запрошенному docs anchor.", - "completed_at": "2026-03-26T20:30:24.178650+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_9827472a716044cf983328dc959c4042.md b/runtime_traces/agent_requests/20260326/req_9827472a716044cf983328dc959c4042.md deleted file mode 100644 index bf84231..0000000 --- a/runtime_traces/agent_requests/20260326/req_9827472a716044cf983328dc959c4042.md +++ /dev/null @@ -1,130 +0,0 @@ -# Request Trace: req_9827472a716044cf983328dc959c4042 - -- session_id: as_1d66a74b9e594dcca5648c964164d540 -- active_rag_session_id: 69c34e19-c302-4510-b0fb-9ec64cf381aa -- process_version: v1 -- created_at: 2026-03-26T19:26:38.681374+00:00 - -## User Message -проверка связи - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T19:26:38.682328+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v1" - }, - "created_at": "2026-03-26T19:26:38.682413+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step run_llm -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Отправляю запрос пользователя в LLM.", - "payload": {}, - "created_at": "2026-03-26T19:26:38.682589+00:00" -} -``` - -## Event status -```json -{ - "source": "llm_process", - "text": "Ответ от LLM получен.", - "payload": {}, - "created_at": "2026-03-26T19:26:39.482668+00:00" -} -``` - -## Step run_llm -```json -{ - "status": "completed", - "details": { - "answer_length": 33 - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Связь в норме, вас хорошо слышно!", - "payload": {}, - "created_at": "2026-03-26T19:26:39.485479+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T19:26:39.486541+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Связь в норме, вас хорошо слышно!", - "completed_at": "2026-03-26T19:26:39.487945+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_bc0ca07c5a344978bdbfa3311283f4c8.md b/runtime_traces/agent_requests/20260326/req_bc0ca07c5a344978bdbfa3311283f4c8.md deleted file mode 100644 index e514cc6..0000000 --- a/runtime_traces/agent_requests/20260326/req_bc0ca07c5a344978bdbfa3311283f4c8.md +++ /dev/null @@ -1,507 +0,0 @@ -# Request Trace: req_bc0ca07c5a344978bdbfa3311283f4c8 - -- session_id: as_d77b5786695d4968a0d7faac4292f7bb -- active_rag_session_id: 47aeddca-0011-45e4-b99c-70f1a242f2e4 -- process_version: v2 -- created_at: 2026-03-26T20:23:49.781134+00:00 - -## User Message -Как работает метод /health - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:23:49.782470+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:23:49.782560+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:23:49.782839+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / API_METHOD_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:23:49.785619+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN" - }, - "created_at": "2026-03-26T20:23:49.786090+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D2_FACT_INDEX: 8 hits", - "D4_WORKFLOW_INDEX: 2 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:23:52.332219+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: degraded" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:23:52.332932+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: reject", - "reason: not_found_exact_anchor", - "missing: retrieval_non_empty, exact_anchor_match" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:23:52.334102+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-26T20:23:52.334498+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "reject", - "reason": "not_found_exact_anchor", - "missing": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "satisfied": [] - }, - "created_at": "2026-03-26T20:23:52.334791+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 57 - }, - "created_at": "2026-03-26T20:23:52.335076+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "layers_used": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 0, - "facts_found": 0, - "relations_found": 0, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "endpoint", - "matched_anchor_value": "/health", - "exact_anchor_match": false, - "docs_layers_requested": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D2_FACT_INDEX": { - "hits": 8, - "top_ids": [ - "8879bb1d923dff0d783ef202f98fdfe5b774870912b2cd261ae127003daffacb", - "5bc72ce58bd31c654a380034beb59f47224e9b03bd306503a7f0b8634008409d", - "5a471b2380ec55b5866b99bb337b92cb78b91051cf616593937f1603d1011fa6", - "a31bbca2eb31ffb6655bcec7ff07b8cf2b6c7416610cf58dfc1e0c737df12fe1", - "e64a6aebed07076a1cccb4d6537b5f54489e25eda776e0230b3c2ff1df2ae648" - ], - "top_sections": [ - "domain.runtime_health:mentions_entity", - "docs/README.md:doc_list_item" - ] - }, - "D4_WORKFLOW_INDEX": { - "hits": 2, - "top_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "top_sections": [ - "Scenario" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "logic.telegram_notification_loop", - "architecture.telegram_notify_app" - ], - "top_sections": [ - "Сущность runtime health", - "Readme", - "Цикл отправки уведомлений в Telegram", - "Архитектура Telegram Notify App" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "logic.telegram_notification_loop:Details" - ] - } - }, - "query_entity_candidates": [], - "resolved_entity_candidates": [], - "query_anchor_candidates": [ - "/health" - ], - "resolved_anchor_candidates": [], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [], - "doc_paths": [], - "doc_titles": [], - "relation_hits_count": 0, - "relation_targets": [], - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "selected_entity_ids": [], - "selected_workflow_ids": [], - "fallback_doc_hits_count": 0, - "fallback_used": false, - "fact_hits": 0, - "entity_hits": 0, - "evidence_summary": { - "documents": 0, - "facts": 0, - "entities": 0, - "workflows": 0, - "relations": 0, - "chunks": 0, - "selected_doc_ids": [], - "selected_fact_ids": [], - "selected_relation_ids": [], - "selected_chunk_ids": [], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "reject", - "gate_decision_reason": "not_found_exact_anchor", - "gate_missing_requirements": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "gate_satisfied_requirements": [], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"Как работает метод /health\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"API_METHOD_EXPLAIN\",\n \"documents\": [],\n \"facts\": [],\n \"entities\": [],\n \"workflows\": [],\n \"relations\": [],\n \"chunks\": []\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 227, - "tokens_in_estimate": 155 - } - }, - "answer_mode": "degraded", - "degrade_reason": "not_found_exact_anchor", - "degraded_reason": "not_found_exact_anchor", - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "Не найдено точное совпадение по запрошенному docs anchor.", - "payload": {}, - "created_at": "2026-03-26T20:23:52.338517+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:23:52.339122+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "Не найдено точное совпадение по запрошенному docs anchor.", - "completed_at": "2026-03-26T20:23:52.342384+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260326/req_c1a55e97a0aa45ce8c4c6c453a9ec080.md b/runtime_traces/agent_requests/20260326/req_c1a55e97a0aa45ce8c4c6c453a9ec080.md deleted file mode 100644 index 524bee6..0000000 --- a/runtime_traces/agent_requests/20260326/req_c1a55e97a0aa45ce8c4c6c453a9ec080.md +++ /dev/null @@ -1,555 +0,0 @@ -# Request Trace: req_c1a55e97a0aa45ce8c4c6c453a9ec080 - -- session_id: as_9d330fb7437341f18539f819dcace95f -- active_rag_session_id: 34007a57-cf69-45f1-a0cd-c6a39bd81d42 -- process_version: v2 -- created_at: 2026-03-26T20:17:51.253164+00:00 - -## User Message -опиши сущность runtime health - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-26T20:17:51.255051+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-26T20:17:51.255274+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-26T20:17:51.255716+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / ENTITY_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-26T20:17:51.267550+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN" - }, - "created_at": "2026-03-26T20:17:51.268352+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D3_ENTITY_CATALOG, D5_RELATION_GRAPH, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D3_ENTITY_CATALOG, D5_RELATION_GRAPH, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D3_ENTITY_CATALOG: 8 hits", - "D5_RELATION_GRAPH: 8 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:58.665245+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: answered" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:58.666116+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: allow", - "reason: evidence_sufficient", - "satisfied: retrieval_non_empty, exact_anchor_match" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-26T20:17:58.666391+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-26T20:17:58.666626+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "allow", - "reason": "evidence_sufficient", - "missing": [], - "satisfied": [ - "retrieval_non_empty", - "exact_anchor_match" - ] - }, - "created_at": "2026-03-26T20:17:58.666964+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 1619 - }, - "created_at": "2026-03-26T20:17:58.667219+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "ENTITY_EXPLAIN", - "layers_used": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 1, - "facts_found": 0, - "relations_found": 2, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "entity", - "matched_anchor_value": "runtime health", - "exact_anchor_match": true, - "docs_layers_requested": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D3_ENTITY_CATALOG", - "D5_RELATION_GRAPH", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D3_ENTITY_CATALOG": { - "hits": 8, - "top_ids": [ - "architecture.telegram_notify_app", - "domain.runtime_health", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "RuntimeManager", - "TelegramNotifyWorker", - "WorkerStatus", - "WorkerHealth" - ] - }, - "D5_RELATION_GRAPH": { - "hits": 8, - "top_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4", - "79bdb0a74713df6064008179ca8c5c186dc23e4e1c5e2c9607b0bdddeba50f93", - "af2a4f7c1b677b908410ba720915405e10dbd0870e30a3c6e3aa2468550ddf0e", - "4e0bbeb8e97e25091a30412d8631b1d9f6bb58b5694d8b7cc632af69a9262ea8" - ], - "top_sections": [ - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by", - "domain.runtime_health:parent", - "domain.runtime_health:part_of", - "architecture.telegram_notify_app:child" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "api.control_actions_endpoint", - "architecture.telegram_notify_app", - "logic.telegram_notification_loop" - ], - "top_sections": [ - "Сущность runtime health", - "HTTP API /actions/{action}", - "Архитектура Telegram Notify App", - "Цикл отправки уведомлений в Telegram" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "domain.runtime_health", - "docs/README.md", - "api.control_actions_endpoint" - ], - "top_sections": [ - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "api.control_actions_endpoint:Summary" - ] - } - }, - "query_entity_candidates": [ - "runtime health" - ], - "resolved_entity_candidates": [], - "query_anchor_candidates": [], - "resolved_anchor_candidates": [ - "domain.runtime_health", - "docs/documentation/domain/runtime-health-entity.md", - "docs/README.md" - ], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [ - "domain.runtime_health", - "docs/README.md" - ], - "doc_paths": [ - "docs/documentation/domain/runtime-health-entity.md", - "docs/README.md" - ], - "doc_titles": [ - "Сущность runtime health", - "domain.runtime_health:Details", - "domain.runtime_health:Summary", - "docs/README.md:Навигация", - "TelegramNotifyWorker", - "WorkerStatus", - "WorkerHealth", - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by", - "domain.runtime_health:parent", - "domain.runtime_health:part_of" - ], - "relation_hits_count": 2, - "relation_targets": [ - "domain.runtime_health:related_logic", - "domain.runtime_health:used_by" - ], - "selected_doc_ids": [ - "domain.runtime_health" - ], - "selected_fact_ids": [], - "selected_relation_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4" - ], - "selected_chunk_ids": [ - "domain.runtime_health" - ], - "selected_entity_ids": [ - "domain.runtime_health" - ], - "selected_workflow_ids": [], - "fallback_doc_hits_count": 2, - "fallback_used": false, - "fact_hits": 0, - "entity_hits": 0, - "evidence_summary": { - "documents": 1, - "facts": 0, - "entities": 2, - "workflows": 0, - "relations": 2, - "chunks": 1, - "selected_doc_ids": [ - "domain.runtime_health" - ], - "selected_fact_ids": [], - "selected_relation_ids": [ - "8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04", - "801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4" - ], - "selected_chunk_ids": [ - "domain.runtime_health" - ], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "allow", - "gate_decision_reason": "evidence_sufficient", - "gate_missing_requirements": [], - "gate_satisfied_requirements": [ - "retrieval_non_empty", - "exact_anchor_match" - ], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"опиши сущность runtime health\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"ENTITY_EXPLAIN\",\n \"documents\": [\n {\n \"layer\": \"D1_DOCUMENT_CATALOG\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"Сущность runtime health\",\n \"content\": \"`runtime health` — доменная модель наблюдаемости сервиса, которая описывает общее состояние runtime и состояние отдельных компонентов. Для `test_echo_app` главным объектом наблюдения является компонент `telegram_notify`, у которого важны статус, время старта и количество успешных отправок. Эта модель используется для внешнего health-monitoring и внутренней диагностики.\",\n \"metadata\": {\n \"name\": \"runtime_health\",\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"type\": \"domain_entity\",\n \"layer\": \"domain\",\n \"links\": [\n {\n \"type\": \"part_of\",\n \"target\": \"architecture.telegram_notify_app\"\n },\n {\n \"type\": \"used_by\",\n \"target\": \"api.health_endpoint\"\n },\n {\n \"type\": \"related_logic\",\n \"target\": \"logic.telegram_notification_loop\"\n }\n ],\n \"owner\": null,\n \"title\": \"Сущность runtime health\",\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"parent\": \"architecture.telegram_notify_app\",\n \"status\": \"draft\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"children\": [],\n \"doc_kind\": \"misc\",\n \"entities\": [\n \"WorkerHealth\",\n \"WorkerStatus\",\n \"TelegramNotifyWorker\"\n ],\n \"updated_at\": \"2026-03-20\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"summary_text\": \"`runtime health` — доменная модель наблюдаемости сервиса, которая описывает общее состояние runtime и состояние отдельных компонентов. Для `test_echo_app` главным объектом наблюдения является компонент `telegram_notify`, у которого важны статус, время старта и количество успешных отправок. Эта модель используется для внешнего health-monitoring и внутренней диагностики.\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"facts\": [],\n \"entities\": [\n {\n \"layer\": \"D3_ENTITY_CATALOG\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"TelegramNotifyWorker\",\n \"content\": \"TelegramNotifyWorker\",\n \"metadata\": {\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"entity_name\": \"TelegramNotifyWorker\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"document_type\": \"domain_entity\",\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D3_ENTITY_CATALOG\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"WorkerStatus\",\n \"content\": \"WorkerStatus\",\n \"metadata\": {\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"entity_name\": \"WorkerStatus\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"document_type\": \"domain_entity\",\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"workflows\": [],\n \"relations\": [\n {\n \"layer\": \"D5_RELATION_GRAPH\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:related_logic\",\n \"content\": \"domain.runtime_health related_logic logic.telegram_notification_loop\",\n \"metadata\": {\n \"owner\": null,\n \"anchor\": \"frontmatter.links\",\n \"doc_id\": null,\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"source_id\": \"domain.runtime_health\",\n \"target_id\": \"logic.telegram_notification_loop\",\n \"doc_version\": null,\n \"relation_id\": \"8595d7b82fecdb7ef579fa8961cda6799bb21970061e5bc221d4dd7a3d53fd04\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"relation_type\": \"related_logic\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D5_RELATION_GRAPH\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:used_by\",\n \"content\": \"domain.runtime_health used_by api.health_endpoint\",\n \"metadata\": {\n \"owner\": null,\n \"anchor\": \"frontmatter.links\",\n \"doc_id\": null,\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"source_id\": \"domain.runtime_health\",\n \"target_id\": \"api.health_endpoint\",\n \"doc_version\": null,\n \"relation_id\": \"801650f8bcbfbfbefd35c14447bfef7b3c3827313db790efa7faf47db860f8c4\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"relation_type\": \"used_by\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"chunks\": [\n {\n \"layer\": \"D0_DOC_CHUNKS\",\n \"path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"title\": \"domain.runtime_health:Details\",\n \"content\": \"Health payload собирается runtime'ом и включает общий статус приложения и список компонентов. Для worker `telegram_notify` используются состояния `ok`, `degraded` и `unhealthy`, которые зависят от жизненного цикла фонового потока, наличия Telegram credentials и результата последней отправки.\\n\\nКлючевые metadata поля компонента — `app_started_at` и `notifications_sent`. Первое отражает UTC timestamp запуска worker'а, второе показывает число успешных доставок с момента старта runtime. Эти поля позволяют отделить реальный жизненный цикл worker'а от простого факта, что HTTP API отвечает.\\n\\nСущность `runtime health` связывает техническое состояние worker'а с операторским представлением сервиса. Через нее клиент `/health` получает не только общий статус процесса, но и диагностический контекст, достаточный для базового мониторинга и troubleshooting.\",\n \"metadata\": {\n \"tags\": [\n \"domain\",\n \"health\",\n \"observability\"\n ],\n \"type\": \"domain_entity\",\n \"order\": 2,\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"37d4dcf470ea3de95d59e44786006e369a3ea11bf5dbf4c88877696e9183ee87\",\n \"doc_kind\": \"misc\",\n \"doc_version\": null,\n \"document_id\": \"domain.runtime_health\",\n \"source_path\": \"docs/documentation/domain/runtime-health-entity.md\",\n \"section_path\": \"Details\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"section_title\": \"Details\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ]\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 8418, - "tokens_in_estimate": 2203 - } - }, - "answer_mode": "answered", - "degrade_reason": null, - "degraded_reason": null, - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "### Описание сущности Runtime Health\n\n**Runtime Health** - это доменная модель наблюдаемости сервиса, описывающая текущее состояние runtime и его компонентов. Она предназначена для использования в системах внешнего health-monitoring и внутренней диагностики.\n\n---\n\n### Основные элементы\n\n1. **Компоненты**: \n - Главный объект наблюдения - компонент `telegram_notify`.\n \n2. **Метрики и атрибуты**:\n - Статус (`ok`, `degraded`, `unhealthy`), зависящий от жизненного цикла worker'а и результатов работы.\n - Время старта (`app_started_at`) - UTC timestamp начала работы worker'а.\n - Количество успешных отправок (`notifications_sent`) - число успешно выполненных задач с момента старта.\n\n3. **Связанные сущности**:\n - `WorkerHealth`\n - `WorkerStatus`\n - `TelegramNotifyWorker`\n\n---\n\n### Как это работает\n\nМодель собирает данные о состоянии worker'ов во время выполнения и предоставляет их через endpoint `/health`. Данные включают текущий статус приложения и подробную информацию по каждому компоненту, позволяя пользователям проводить мониторинг и диагностику.\n\nДля worker'а `telegram_notify` учитывается статус, который зависит от таких факторов как наличие credentials и успешность последних операций. Ключевая информация помогает определить реальное состояние worker'а, а не просто факт доступности HTTP API.\n\n---\n\n### Связи с другими частями системы\n\n- **Используется логикой**: логика `telegram_notification_loop` использует эту модель для отслеживания состояния worker'а.\n- **Используемый API**: данные из модели передаются на endpoint `/health`, обеспечивая внешнее наблюдение за состоянием системы.", - "payload": {}, - "created_at": "2026-03-26T20:17:58.668790+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-26T20:17:58.671598+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "### Описание сущности Runtime Health\n\n**Runtime Health** - это доменная модель наблюдаемости сервиса, описывающая текущее состояние runtime и его компонентов. Она предназначена для использования в системах внешнего health-monitoring и внутренней диагностики.\n\n---\n\n### Основные элементы\n\n1. **Компоненты**: \n - Главный объект наблюдения - компонент `telegram_notify`.\n \n2. **Метрики и атрибуты**:\n - Статус (`ok`, `degraded`, `unhealthy`), зависящий от жизненного цикла worker'а и результатов работы.\n - Время старта (`app_started_at`) - UTC timestamp начала работы worker'а.\n - Количество успешных отправок (`notifications_sent`) - число успешно выполненных задач с момента старта.\n\n3. **Связанные сущности**:\n - `WorkerHealth`\n - `WorkerStatus`\n - `TelegramNotifyWorker`\n\n---\n\n### Как это работает\n\nМодель собирает данные о состоянии worker'ов во время выполнения и предоставляет их через endpoint `/health`. Данные включают текущий статус приложения и подробную информацию по каждому компоненту, позволяя пользователям проводить мониторинг и диагностику.\n\nДля worker'а `telegram_notify` учитывается статус, который зависит от таких факторов как наличие credentials и успешность последних операций. Ключевая информация помогает определить реальное состояние worker'а, а не просто факт доступности HTTP API.\n\n---\n\n### Связи с другими частями системы\n\n- **Используется логикой**: логика `telegram_notification_loop` использует эту модель для отслеживания состояния worker'а.\n- **Используемый API**: данные из модели передаются на endpoint `/health`, обеспечивая внешнее наблюдение за состоянием системы.", - "completed_at": "2026-03-26T20:17:58.672309+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260327/req_c0af91b38de3436e82a75b9c3664c95a.md b/runtime_traces/agent_requests/20260327/req_c0af91b38de3436e82a75b9c3664c95a.md deleted file mode 100644 index e32fb3e..0000000 --- a/runtime_traces/agent_requests/20260327/req_c0af91b38de3436e82a75b9c3664c95a.md +++ /dev/null @@ -1,550 +0,0 @@ -# Request Trace: req_c0af91b38de3436e82a75b9c3664c95a - -- session_id: as_a4d91e7eca7547e0a37dc4dc6492cf9b -- active_rag_session_id: aa7d907e-2a76-4bf7-99dc-5a400a417f8c -- process_version: v2 -- created_at: 2026-03-27T05:24:20.178027+00:00 - -## User Message -Какие методы есть в API? - -## Step bootstrap -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запрос принят и поставлен в обработку.", - "payload": {}, - "created_at": "2026-03-27T05:24:20.194032+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Запускаю процесс обработки v1.", - "payload": { - "process_version": "v2" - }, - "created_at": "2026-03-27T05:24:20.194168+00:00" -} -``` - -## Step bootstrap -```json -{ - "status": "completed", - "details": {} -} -``` - -## Step intent_router -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрутизирую запрос и определяю целевой workflow.", - "payload": {}, - "created_at": "2026-03-27T05:24:20.198014+00:00" -} -``` - -## Event status -```json -{ - "source": "intent_router", - "text": "Маршрут выбран: DOCUMENTATION_EXPLAIN / API_METHOD_EXPLAIN.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - }, - "created_at": "2026-03-27T05:24:20.237289+00:00" -} -``` - -## Step intent_router -```json -{ - "status": "completed", - "details": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "matched_intent_source": "deterministic" - } -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "started", - "details": {} -} -``` - -## Event status -```json -{ - "source": "task_workflow", - "text": "Запускаю workflow docs_qa.", - "payload": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN" - }, - "created_at": "2026-03-27T05:24:20.237970+00:00" -} -``` - -## Event status -```json -{ - "source": "status.rag_retrieval", - "text": "RAG Retrieval", - "payload": { - "status_block": { - "id": "rag_retrieval", - "title": "RAG Retrieval", - "lines": [ - "planned_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "executed_layers: D2_FACT_INDEX, D4_WORKFLOW_INDEX, D1_DOCUMENT_CATALOG, D0_DOC_CHUNKS", - "D2_FACT_INDEX: 8 hits", - "D4_WORKFLOW_INDEX: 2 hits", - "D1_DOCUMENT_CATALOG: 4 hits", - "D0_DOC_CHUNKS: 4 hits" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-27T05:29:27.952296+00:00" -} -``` - -## Event status -```json -{ - "source": "status.workflow", - "text": "Task Workflow", - "payload": { - "status_block": { - "id": "workflow", - "title": "Task Workflow", - "lines": [ - "workflow_id: docs_qa", - "prompt: docs_explain_answer", - "answer_mode: answered" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-27T05:29:27.954440+00:00" -} -``` - -## Event status -```json -{ - "source": "status.evidence_gate", - "text": "Evidence Gate", - "payload": { - "status_block": { - "id": "evidence_gate", - "title": "Evidence Gate", - "lines": [ - "decision: allow", - "reason: evidence_sufficient", - "satisfied: retrieval_non_empty" - ], - "append": false - }, - "kind": "task_progress" - }, - "created_at": "2026-03-27T05:29:27.955424+00:00" -} -``` - -## Event status -```json -{ - "source": "rag_retrieval", - "text": "RAG retrieval завершен.", - "payload": { - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ] - }, - "created_at": "2026-03-27T05:29:27.956040+00:00" -} -``` - -## Event status -```json -{ - "source": "evidence_gate", - "text": "Evidence gate оценен.", - "payload": { - "decision": "allow", - "reason": "evidence_sufficient", - "missing": [], - "satisfied": [ - "retrieval_non_empty" - ] - }, - "created_at": "2026-03-27T05:29:27.956637+00:00" -} -``` - -## Event status -```json -{ - "source": "workflow_result", - "text": "Workflow docs_qa завершен.", - "payload": { - "workflow_id": "docs_qa", - "result_type": "answer", - "answer_length": 1418 - }, - "created_at": "2026-03-27T05:29:27.956966+00:00" -} -``` - -## Step workflow_documentation_explain -```json -{ - "status": "completed", - "details": { - "workflow_id": "docs_qa", - "meta": { - "workflow_id": "docs_qa", - "intent": "DOCUMENTATION_EXPLAIN", - "diagnostics": { - "intent": "DOCUMENTATION_EXPLAIN", - "sub_intent": "API_METHOD_EXPLAIN", - "layers_used": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "documents_found": 1, - "facts_found": 2, - "relations_found": 0, - "openapi_fields_extracted": 0, - "missing_required_fields": [], - "openapi_status": { - "has_path": false, - "has_method": false, - "has_request": false, - "has_response": false - }, - "prompt_used": "docs_explain_answer", - "llm_mode": "prose", - "output_valid": true, - "matched_intent_source": "deterministic", - "matched_anchor_type": "topic", - "matched_anchor_value": null, - "exact_anchor_match": false, - "docs_layers_requested": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "docs_layers_with_hits": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "planned_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "executed_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "non_empty_layers": [ - "D2_FACT_INDEX", - "D4_WORKFLOW_INDEX", - "D1_DOCUMENT_CATALOG", - "D0_DOC_CHUNKS" - ], - "layer_diagnostics": { - "D2_FACT_INDEX": { - "hits": 8, - "top_ids": [ - "80c5e54d5ebe430623c215b27c8f7112010666951d905f15e9f909a6caff517e", - "6bf0a662808009874a108d0c97f7cca7fc4ff7a3a3d66e0947b31bf813cfdf4d", - "7aefee9ff6110088aad1891da66fc9085eb3f77d832605a3da565deecb96e850", - "8c717a73b32f2f6d69293c34ce4804a82c168a9dc2d611465ab5563637c71fe0", - "149c4e0d3d34b3faba055b1495d54818fadc882af633da70035b82190c1bd327" - ], - "top_sections": [ - "api.send_message_endpoint:workflow_step", - "api.send_message_endpoint:mentions_entity" - ] - }, - "D4_WORKFLOW_INDEX": { - "hits": 2, - "top_ids": [ - "api.control_actions_endpoint", - "api.send_message_endpoint" - ], - "top_sections": [ - "Scenario" - ] - }, - "D1_DOCUMENT_CATALOG": { - "hits": 4, - "top_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint", - "docs/README.md", - "domain.runtime_health" - ], - "top_sections": [ - "HTTP API /send", - "HTTP API /actions/{action}", - "Readme", - "Сущность runtime health" - ] - }, - "D0_DOC_CHUNKS": { - "hits": 4, - "top_ids": [ - "api.send_message_endpoint" - ], - "top_sections": [ - "api.send_message_endpoint:Описание", - "api.send_message_endpoint:Сценарий", - "api.send_message_endpoint:Summary", - "api.send_message_endpoint:Функциональные требования" - ] - } - }, - "query_entity_candidates": [], - "resolved_entity_candidates": [], - "query_anchor_candidates": [], - "resolved_anchor_candidates": [ - "docs/documentation/api/send-message-endpoint.md", - "api.control_actions_endpoint", - "docs/documentation/api/control-actions-endpoint.md", - "api.send_message_endpoint", - "docs/README.md", - "domain.runtime_health", - "docs/documentation/domain/runtime-health-entity.md" - ], - "anchor_candidates": [], - "selected_anchor": null, - "anchor_selection_reason": "", - "anchor_match_type": "", - "doc_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint", - "api.actions_endpoint", - "docs/README.md", - "domain.runtime_health" - ], - "doc_paths": [ - "docs/documentation/api/send-message-endpoint.md", - "docs/documentation/api/control-actions-endpoint.md", - "docs/README.md", - "docs/documentation/domain/runtime-health-entity.md" - ], - "doc_titles": [ - "api.send_message_endpoint:workflow_step", - "api.send_message_endpoint:mentions_entity", - "Scenario", - "HTTP API /send", - "HTTP API /actions/{action}", - "Readme", - "Сущность runtime health", - "api.send_message_endpoint:Описание", - "api.send_message_endpoint:Сценарий", - "api.send_message_endpoint:Summary", - "api.send_message_endpoint:Функциональные требования" - ], - "relation_hits_count": 0, - "relation_targets": [], - "selected_doc_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "selected_fact_ids": [ - "80c5e54d5ebe430623c215b27c8f7112010666951d905f15e9f909a6caff517e", - "8c717a73b32f2f6d69293c34ce4804a82c168a9dc2d611465ab5563637c71fe0" - ], - "selected_relation_ids": [], - "selected_chunk_ids": [ - "api.send_message_endpoint" - ], - "selected_entity_ids": [], - "selected_workflow_ids": [ - "f78b822d4366b10322fd4a35e347deae59d55ff4132951201f4df3d1653aec61" - ], - "fallback_doc_hits_count": 3, - "fallback_used": false, - "fact_hits": 2, - "entity_hits": 0, - "evidence_summary": { - "documents": 1, - "facts": 2, - "entities": 0, - "workflows": 1, - "relations": 0, - "chunks": 2, - "selected_doc_ids": [ - "api.send_message_endpoint", - "api.control_actions_endpoint" - ], - "selected_fact_ids": [ - "80c5e54d5ebe430623c215b27c8f7112010666951d905f15e9f909a6caff517e", - "8c717a73b32f2f6d69293c34ce4804a82c168a9dc2d611465ab5563637c71fe0" - ], - "selected_relation_ids": [], - "selected_chunk_ids": [ - "api.send_message_endpoint" - ], - "entity_hits": 0, - "openapi_signals": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - } - }, - "gate_decision": "allow", - "gate_decision_reason": "evidence_sufficient", - "gate_missing_requirements": [], - "gate_satisfied_requirements": [ - "retrieval_non_empty" - ], - "openapi_evidence": { - "path_found": false, - "method_found": false, - "operation_semantics_found": false, - "request_payload_found": false, - "request_schema": false, - "request_fields_found": false, - "response_payload_found": false, - "response_schema": false, - "response_fields_found": false, - "status_codes": false, - "content_type_found": false, - "examples_found": false, - "payload_description": false - }, - "requested_fragment_type": null, - "fragment_evidence_found": [], - "fragment_missing_requirements": [], - "prompt": { - "prompt_name": "docs_explain_answer", - "system_prompt": "Ты объясняешь документацию системы.\n\nНа вход приходит JSON с полями:\n- question\n- intent\n- sub_intent\n- documents\n- facts\n- relations\n\nПравила:\n- Используй только предоставленные факты\n- Не додумывай\n- Если данных недостаточно, скажи это явно\n- Объясняй структурировано\n\nФормат ответа:\n1. Краткое описание\n2. Основные элементы\n3. Как это работает\n4. Связи с другими частями системы (если есть)", - "user_prompt": "{\n \"question\": \"Какие методы есть в API?\",\n \"intent\": \"DOCUMENTATION_EXPLAIN\",\n \"sub_intent\": \"API_METHOD_EXPLAIN\",\n \"documents\": [\n {\n \"layer\": \"D1_DOCUMENT_CATALOG\",\n \"path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"title\": \"HTTP API /send\",\n \"content\": \"Endpoint `/send` выполняет ручную отправку сообщения в Telegram через `TelegramSendService`. Он используется для операторской проверки доставки и для отправки уведомлений вне периодического worker loop. Endpoint работает поверх тех же credentials и того же delivery path, что и фоновая отправка.\",\n \"metadata\": {\n \"name\": \"send_message_endpoint\",\n \"tags\": [\n \"api\",\n \"telegram\",\n \"manual-send\"\n ],\n \"type\": \"api_method\",\n \"layer\": \"application\",\n \"links\": [\n {\n \"type\": \"part_of\",\n \"target\": \"architecture.telegram_notify_app\"\n },\n {\n \"type\": \"depends_on\",\n \"target\": \"logic.telegram_notification_loop\"\n }\n ],\n \"owner\": null,\n \"title\": \"HTTP API /send\",\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"parent\": \"architecture.telegram_notify_app\",\n \"status\": \"draft\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"5fbf2e33510b0676a3a53b957a4569bd3c2c31fb7719546602c573421ccaa32d\",\n \"children\": [],\n \"doc_kind\": \"misc\",\n \"entities\": [\n \"TelegramControlAppFactory\",\n \"TelegramSendService\",\n \"TelegramNotifyWorker\"\n ],\n \"updated_at\": \"2026-03-20\",\n \"doc_version\": null,\n \"document_id\": \"api.send_message_endpoint\",\n \"source_path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"summary_text\": \"Endpoint `/send` выполняет ручную отправку сообщения в Telegram через `TelegramSendService`. Он используется для операторской проверки доставки и для отправки уведомлений вне периодического worker loop. Endpoint работает поверх тех же credentials и того же delivery path, что и фоновая отправка.\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"facts\": [\n {\n \"layer\": \"D2_FACT_INDEX\",\n \"path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"title\": \"api.send_message_endpoint:workflow_step\",\n \"content\": \"api.send_message_endpoint workflow_step Предусловия:**\",\n \"metadata\": {\n \"tags\": [],\n \"owner\": null,\n \"anchor\": \"Сценарий\",\n \"doc_id\": null,\n \"object\": \"Предусловия:**\",\n \"fact_id\": \"80c5e54d5ebe430623c215b27c8f7112010666951d905f15e9f909a6caff517e\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"5fbf2e33510b0676a3a53b957a4569bd3c2c31fb7719546602c573421ccaa32d\",\n \"predicate\": \"workflow_step\",\n \"object_ref\": null,\n \"subject_id\": \"api.send_message_endpoint\",\n \"doc_version\": null,\n \"source_path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D2_FACT_INDEX\",\n \"path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"title\": \"api.send_message_endpoint:mentions_entity\",\n \"content\": \"api.send_message_endpoint mentions_entity TelegramControlAppFactory\",\n \"metadata\": {\n \"tags\": [],\n \"owner\": null,\n \"anchor\": \"frontmatter.entities\",\n \"doc_id\": null,\n \"object\": \"TelegramControlAppFactory\",\n \"fact_id\": \"8c717a73b32f2f6d69293c34ce4804a82c168a9dc2d611465ab5563637c71fe0\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"5fbf2e33510b0676a3a53b957a4569bd3c2c31fb7719546602c573421ccaa32d\",\n \"predicate\": \"mentions_entity\",\n \"object_ref\": null,\n \"subject_id\": \"api.send_message_endpoint\",\n \"doc_version\": null,\n \"source_path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ],\n \"entities\": [],\n \"workflows\": [\n {\n \"layer\": \"D4_WORKFLOW_INDEX\",\n \"path\": \"docs/documentation/api/control-actions-endpoint.md\",\n \"title\": \"Scenario\",\n \"content\": \"Scenario\\nHTTP control plane запущен.\\n`ControlActionSet` сконфигурирован в `TelegramControlChannel`.\\nВ path передано одно из поддерживаемых действий: `start`, `stop`, `status`.\\nКлиент отправляет `GET` или `POST` на `/actions/{action}`.\\n1. Endpoint принимает запрос и извлекает path parameter `action`.\\n2. API определяет источник клиента через `X-Client-Source` или `User-Agent`.\\n3. Для `start` и `stop` запрос логируется как control action.\\n4. Канал сопоставляет `action` с callback из `ControlActionSet`.\\n5. API ожидает завершения callback в пределах допустимого timeout.\\n6. При успешном завершении API возвращает JSON со статусом `ok`.\\n1. Если `start` или `stop` не успевают завершиться в timeout, API возвращает `202 accepted` и сообщает, что операция еще выполняется.\",\n \"metadata\": {\n \"owner\": null,\n \"doc_id\": null,\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"trigger\": [\n \"Клиент отправляет `GET` или `POST` на `/actions/{action}`.\"\n ],\n \"blob_sha\": \"a251c550a23249ef1a74389c93aaccd4c5ae88ef621efad2aa018fcd132cce7e\",\n \"main_flow\": [\n \"1. Endpoint принимает запрос и извлекает path parameter `action`.\",\n \"2. API определяет источник клиента через `X-Client-Source` или `User-Agent`.\",\n \"3. Для `start` и `stop` запрос логируется как control action.\",\n \"4. Канал сопоставляет `action` с callback из `ControlActionSet`.\",\n \"5. API ожидает завершения callback в пределах допустимого timeout.\",\n \"6. При успешном завершении API возвращает JSON со статусом `ok`.\"\n ],\n \"doc_version\": null,\n \"document_id\": \"api.control_actions_endpoint\",\n \"source_path\": \"docs/documentation/api/control-actions-endpoint.md\",\n \"workflow_id\": \"f78b822d4366b10322fd4a35e347deae59d55ff4132951201f4df3d1653aec61\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"preconditions\": [\n \"HTTP control plane запущен.\",\n \"`ControlActionSet` сконфигурирован в `TelegramControlChannel`.\",\n \"В path передано одно из поддерживаемых действий: `start`, `stop`, `status`.\"\n ],\n \"workflow_name\": \"Scenario\",\n \"error_handling\": [\n \"1. Если actions не сконфигурированы, API возвращает `404`.\",\n \"2. Если передано неподдерживаемое действие, API возвращает `404`.\",\n \"3. Если callback завершился исключением, API возвращает `500`.\"\n ],\n \"postconditions\": [\n \"Клиент получает результат выполнения control action или признак, что операция еще продолжается.\"\n ],\n \"staleness_score\": null,\n \"alternative_flow\": [\n \"1. Если `start` или `stop` не успевают завершиться в timeout, API возвращает `202 accepted` и сообщает, что операция еще выполняется.\"\n ],\n \"system_component\": null\n }\n }\n ],\n \"relations\": [],\n \"chunks\": [\n {\n \"layer\": \"D0_DOC_CHUNKS\",\n \"path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"title\": \"api.send_message_endpoint:Описание\",\n \"content\": \"Метод принимает текст сообщения и инициирует разовую отправку в Telegram без ожидания планового интервала worker'а. Endpoint нужен для ручной диагностики канала доставки и для ad-hoc уведомлений.\",\n \"metadata\": {\n \"tags\": [\n \"api\",\n \"telegram\",\n \"manual-send\"\n ],\n \"type\": \"api_method\",\n \"order\": 3,\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"5fbf2e33510b0676a3a53b957a4569bd3c2c31fb7719546602c573421ccaa32d\",\n \"doc_kind\": \"misc\",\n \"doc_version\": null,\n \"document_id\": \"api.send_message_endpoint\",\n \"source_path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"section_path\": \"Details > Описание\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"section_title\": \"Описание\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n },\n {\n \"layer\": \"D0_DOC_CHUNKS\",\n \"path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"title\": \"api.send_message_endpoint:Сценарий\",\n \"content\": \"**Название:** Ручная отправка сообщения в Telegram\\n\\n**Предусловия:**\\n- HTTP control plane запущен.\\n- Telegram credentials доступны в переменных окружения.\\n- Клиент передает непустой параметр `message`.\\n\\n**Триггер:**\\n- Клиент отправляет `GET /send?message=...`.\\n\\n**Основной сценарий:**\\n1. Endpoint принимает HTTP-запрос `GET /send`.\\n2. API извлекает query parameter `message`.\\n3. API проверяет, что сообщение не пустое.\\n4. Endpoint вызывает `TelegramSendService.send(message)`.\\n5. Сервис получает Telegram credentials из окружения.\\n6. Сервис вызывает Telegram Bot API для отправки сообщения.\\n7. API возвращает клиенту успешный JSON-ответ.\\n\\n**Альтернативный сценарий:**\\n1. Если credentials отсутствуют, сервис завершает вызов пользовательской ошибкой.\\n\\n**Обработка ошибок:**\\n1. Если `message` пустой или credentials не заданы, API возвращает `400`.\\n2. Если отправка завершилась непредвиденной ошибкой, API возвращает `500`.\\n\\n**Постусловие:**\\n- Сообщение либо отправлено в Telegram, либо клиент получил диагностическую ошибку.\",\n \"metadata\": {\n \"tags\": [\n \"api\",\n \"telegram\",\n \"manual-send\"\n ],\n \"type\": \"api_method\",\n \"order\": 4,\n \"owner\": null,\n \"doc_id\": null,\n \"module\": \"telegram_notify_app\",\n \"repo_id\": \"/Users/alex/Dev_projects_v2/apps/test_echo_app\",\n \"section\": null,\n \"blob_sha\": \"5fbf2e33510b0676a3a53b957a4569bd3c2c31fb7719546602c573421ccaa32d\",\n \"doc_kind\": \"misc\",\n \"doc_version\": null,\n \"document_id\": \"api.send_message_endpoint\",\n \"source_path\": \"docs/documentation/api/send-message-endpoint.md\",\n \"section_path\": \"Details > Сценарий\",\n \"artifact_type\": null,\n \"last_modified\": null,\n \"section_title\": \"Сценарий\",\n \"staleness_score\": null,\n \"system_component\": null\n }\n }\n ]\n}", - "log_context": "graph.project_qa.docs.answer", - "prompt_stats": { - "system_chars": 393, - "user_chars": 10838, - "tokens_in_estimate": 2808 - } - }, - "answer_mode": "answered", - "degrade_reason": null, - "degraded_reason": null, - "code_intents_stubbed": false - } - } - } -} -``` - -## Step finalize -```json -{ - "status": "started", - "details": {} -} -``` - -## Event user -```json -{ - "source": "agent", - "text": "### 1. Краткое описание\nДокументация описывает метод API `/send`, который предназначен для ручной отправки сообщений в Telegram. Метод позволяет операторам отправлять сообщения вручную без ожидания плановой отправки, а также использовать его для диагностики работы канала доставки.\n\n### 2. Основные элементы\n- **Метод**: `/send`\n- **Тип**: API метод\n- **Модуль**: telegram_notify_app\n- **Параметры запроса**: message (текст сообщения)\n- **Используемые сервисы**: TelegramSendService\n- **Зависимости**: credentials для доступа к Telegram API\n- **Сценарии использования**: диагностика канала доставки, оперативные уведомления\n\n### 3. Как это работает\n1. Пользователь отправляет GET-запрос на `/send` с параметром `message`.\n2. API извлекает значение параметра `message` и проверяет его на непустоту.\n3. Если проверка пройдена, вызывается метод `TelegramSendService.send(message)`.\n4. Service получает необходимые для отправки данные из окружения.\n5. Запускается вызов Telegram Bot API для отправки сообщения.\n6. В случае успешного завершения отправляется ответ клиенту с подтверждением успешной отправки.\n\n### 4. Связи с другими частями системы\n- Зависит от сервиса `TelegramSendService`.\n- Используется для диагностики и ручного управления каналом доставки вместе с компонентом `TelegramControlAppFactory`.\n- Работает поверх тех же credentials и пути доставки, что и фоновый механизм отправки сообщений (`worker loop`).", - "payload": {}, - "created_at": "2026-03-27T05:29:27.958501+00:00" -} -``` - -## Event status -```json -{ - "source": "orchestrator", - "text": "Обработка запроса завершена.", - "payload": {}, - "created_at": "2026-03-27T05:29:27.958731+00:00" -} -``` - -## Step finalize -```json -{ - "status": "completed", - "details": {} -} -``` - -## Result -```json -{ - "status": "done", - "answer": "### 1. Краткое описание\nДокументация описывает метод API `/send`, который предназначен для ручной отправки сообщений в Telegram. Метод позволяет операторам отправлять сообщения вручную без ожидания плановой отправки, а также использовать его для диагностики работы канала доставки.\n\n### 2. Основные элементы\n- **Метод**: `/send`\n- **Тип**: API метод\n- **Модуль**: telegram_notify_app\n- **Параметры запроса**: message (текст сообщения)\n- **Используемые сервисы**: TelegramSendService\n- **Зависимости**: credentials для доступа к Telegram API\n- **Сценарии использования**: диагностика канала доставки, оперативные уведомления\n\n### 3. Как это работает\n1. Пользователь отправляет GET-запрос на `/send` с параметром `message`.\n2. API извлекает значение параметра `message` и проверяет его на непустоту.\n3. Если проверка пройдена, вызывается метод `TelegramSendService.send(message)`.\n4. Service получает необходимые для отправки данные из окружения.\n5. Запускается вызов Telegram Bot API для отправки сообщения.\n6. В случае успешного завершения отправляется ответ клиенту с подтверждением успешной отправки.\n\n### 4. Связи с другими частями системы\n- Зависит от сервиса `TelegramSendService`.\n- Используется для диагностики и ручного управления каналом доставки вместе с компонентом `TelegramControlAppFactory`.\n- Работает поверх тех же credentials и пути доставки, что и фоновый механизм отправки сообщений (`worker loop`).", - "completed_at": "2026-03-27T05:29:27.959532+00:00" -} -``` diff --git a/runtime_traces/agent_requests/20260401/req_33758fd1ed834100a23fe95871b34181.md b/runtime_traces/agent_requests/20260401/req_33758fd1ed834100a23fe95871b34181.md new file mode 100644 index 0000000..840d3b0 --- /dev/null +++ b/runtime_traces/agent_requests/20260401/req_33758fd1ed834100a23fe95871b34181.md @@ -0,0 +1,171 @@ +# Request Trace: req_33758fd1ed834100a23fe95871b34181 + +- session_id: as_0bb449183cc242efaec50afd8193dcaf +- active_rag_session_id: 292cad80-45ef-4edb-a23c-82f01732d295 +- process_version: v1 +- created_at: 2026-04-01T09:27:07.987130+00:00 + +## User Message +Ты здесь? + +## orchestrator +```json +{ + "event": "bootstrap", + "status": "started", + "process_version": "v1" +} +``` + +## client_event +```json +{ + "event": "status", + "source": "orchestrator", + "text": "Запрос принят и поставлен в обработку.", + "payload": {}, + "created_at": "2026-04-01T09:27:07.987920+00:00" +} +``` + +## client_event +```json +{ + "event": "status", + "source": "orchestrator", + "text": "Запускаю процесс обработки v1.", + "payload": { + "process_version": "v1" + }, + "created_at": "2026-04-01T09:27:07.988004+00:00" +} +``` + +## orchestrator +```json +{ + "event": "bootstrap", + "status": "completed" +} +``` + +## client_event +```json +{ + "event": "status", + "source": "task_workflow", + "text": "Запускаю workflow simple_llm.", + "payload": {}, + "created_at": "2026-04-01T09:27:07.988104+00:00" +} +``` + +## client_event +```json +{ + "event": "status", + "source": "prompt_builder", + "text": "Формирую prompt payload для LLM.", + "payload": {}, + "created_at": "2026-04-01T09:27:07.988150+00:00" +} +``` + +## task_workflow +```json +{ + "event": "started", + "workflow_id": "simple_llm" +} +``` + +## llm +```json +{ + "event": "request", + "prompt_name": "simple_llm_answer", + "system_prompt": "Ты полезный AI-ассистент проекта.\n\nНа вход приходит JSON с полем:\n- question\n\nПравила:\n- Отвечай как персонаж мемов из дагестана\n- Если вопрос неясный, аккуратно укажи, чего не хватает\n- Не выдумывай несуществующие факты о проекте\n- Формулируй ответ как обычное сообщение пользователю", + "user_prompt": "{\n \"question\": \"Ты здесь?\"\n}", + "log_context": "agent:req_33758fd1ed834100a23fe95871b34181" +} +``` + +## llm +```json +{ + "event": "response", + "text": "Да тут я, на месте! А то в горах связи иногда нет, но ты лови ответ от меня, как пастух ловит сигнал телефона в ауле!" +} +``` + +## task_workflow +```json +{ + "event": "completed", + "workflow_id": "simple_llm", + "prompt_name": "simple_llm_answer", + "answer_length": 117 +} +``` + +## client_event +```json +{ + "event": "status", + "source": "llm_process", + "text": "Ответ от LLM получен.", + "payload": { + "workflow_id": "simple_llm", + "prompt_name": "simple_llm_answer", + "answer_length": 117 + }, + "created_at": "2026-04-01T09:27:08.991752+00:00" +} +``` + +## orchestrator +```json +{ + "event": "finalize", + "status": "started" +} +``` + +## client_event +```json +{ + "event": "user", + "source": "agent", + "text": "Да тут я, на месте! А то в горах связи иногда нет, но ты лови ответ от меня, как пастух ловит сигнал телефона в ауле!", + "payload": {}, + "created_at": "2026-04-01T09:27:08.992387+00:00" +} +``` + +## client_event +```json +{ + "event": "status", + "source": "orchestrator", + "text": "Обработка запроса завершена.", + "payload": {}, + "created_at": "2026-04-01T09:27:08.992694+00:00" +} +``` + +## orchestrator +```json +{ + "event": "finalize", + "status": "completed" +} +``` + +## result +```json +{ + "status": "done", + "answer": "Да тут я, на месте! А то в горах связи иногда нет, но ты лови ответ от меня, как пастух ловит сигнал телефона в ауле!", + "completed_at": "2026-04-01T09:27:08.994005+00:00" +} +``` diff --git a/src/app/main.py b/src/app/main.py index 5155da3..30819d9 100644 --- a/src/app/main.py +++ b/src/app/main.py @@ -27,7 +27,7 @@ def create_app() -> FastAPI: allow_headers=["*"], ) - app.include_router(modules.agent_api.public_router()) + app.include_router(modules.api.public_router()) app.include_router(modules.rag.public_router()) app.include_router(modules.rag.internal_router()) app.include_router(modules.rag_repo.internal_router()) diff --git a/src/app/modules/agent/llm/service.py b/src/app/modules/agent/llm/service.py index deabf69..82e83f5 100644 --- a/src/app/modules/agent/llm/service.py +++ b/src/app/modules/agent/llm/service.py @@ -1,5 +1,6 @@ import logging +from app.modules.agent.observability.module_trace import ModuleTrace from app.modules.agent.llm.prompt_loader import PromptLoader from app.modules.shared.gigachat.client import GigaChatClient @@ -18,10 +19,26 @@ class AgentLlmService: self._client = client self._prompts = prompts - def generate(self, prompt_name: str, user_input: str, *, log_context: str | None = None) -> str: - system_prompt = self._prompts.load(prompt_name) - if not system_prompt: - system_prompt = "You are a helpful assistant." + def build_request(self, prompt_name: str, user_input: str, *, log_context: str | None = None) -> dict[str, str]: + system_prompt = self._prompts.load(prompt_name) or "You are a helpful assistant." + return { + "prompt_name": prompt_name, + "system_prompt": system_prompt, + "user_prompt": user_input, + "log_context": log_context or "", + } + + def generate( + self, + prompt_name: str, + user_input: str, + *, + log_context: str | None = None, + trace: ModuleTrace | None = None, + ) -> str: + request = self.build_request(prompt_name, user_input, log_context=log_context) + if trace is not None: + trace.log("request", request) if log_context: LOGGER.warning( "graph llm input: context=%s prompt=%s user_input=%s", @@ -29,7 +46,12 @@ class AgentLlmService: prompt_name, _truncate_for_log(user_input), ) - output = self._client.complete(system_prompt=system_prompt, user_prompt=user_input) + output = self._client.complete( + system_prompt=request["system_prompt"], + user_prompt=request["user_prompt"], + ) + if trace is not None: + trace.log("response", {"text": output}) if log_context: LOGGER.warning( "graph llm output: context=%s prompt=%s output=%s", diff --git a/src/app/modules/agent/observability/module_trace.py b/src/app/modules/agent/observability/module_trace.py new file mode 100644 index 0000000..35eb2c5 --- /dev/null +++ b/src/app/modules/agent/observability/module_trace.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Protocol + + +class ModuleTraceLogger(Protocol): + def log_module(self, request_id: str, module: str, title: str, payload: dict | None = None) -> None: ... + + +@dataclass(frozen=True, slots=True) +class ModuleTrace: + request_id: str + module: str + logger: ModuleTraceLogger + + def log(self, title: str, payload: dict | None = None) -> None: + self.logger.log_module(self.request_id, self.module, title, payload or {}) + + +@dataclass(frozen=True, slots=True) +class RequestTraceContext: + request_id: str + logger: ModuleTraceLogger + + def module(self, name: str) -> ModuleTrace: + return ModuleTrace(request_id=self.request_id, module=name, logger=self.logger) diff --git a/src/app/modules/agent/orchestration/__init__.py b/src/app/modules/agent/orchestration/__init__.py new file mode 100644 index 0000000..c9c2ef6 --- /dev/null +++ b/src/app/modules/agent/orchestration/__init__.py @@ -0,0 +1 @@ +__all__: list[str] = [] diff --git a/src/app/modules/agent/orchestration/adapters/intent_router_adapter.py b/src/app/modules/agent/orchestration/adapters/intent_router_adapter.py new file mode 100644 index 0000000..f75cf7a --- /dev/null +++ b/src/app/modules/agent/orchestration/adapters/intent_router_adapter.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from app.modules.agent.observability.module_trace import ModuleTrace +from app.modules.agent.intent_router_v2 import IntentRouterV2 + + +class IntentRouterAdapter: + def __init__(self, router: IntentRouterV2) -> None: + self._router = router + + def route(self, user_query: str, conversation_state, repo_context, trace: ModuleTrace | None = None): + if trace is not None: + trace.log("started", {"question": user_query}) + result = self._router.route(user_query, conversation_state, repo_context) + if trace is not None: + trace.log( + "completed", + { + "intent": result.intent, + "sub_intent": result.query_plan.sub_intent, + "matched_intent_source": result.matched_intent_source, + }, + ) + return result diff --git a/src/app/modules/agent/orchestration/adapters/llm_chat_adapter.py b/src/app/modules/agent/orchestration/adapters/llm_chat_adapter.py new file mode 100644 index 0000000..32de6ab --- /dev/null +++ b/src/app/modules/agent/orchestration/adapters/llm_chat_adapter.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import asyncio + +from app.modules.agent.observability.module_trace import ModuleTrace +from app.modules.agent.llm.service import AgentLlmService + + +class LlmChatAdapter: + def __init__(self, llm: AgentLlmService, prompt_name: str = "simple_llm_answer") -> None: + self._llm = llm + self._prompt_name = prompt_name + + @property + def prompt_name(self) -> str: + return self._prompt_name + + def build_request(self, message: str, request_id: str) -> dict[str, str]: + return self._llm.build_request( + self._prompt_name, + message, + log_context=f"agent:{request_id}", + ) + + async def generate(self, message: str, request_id: str, trace: ModuleTrace | None = None) -> str: + return await asyncio.to_thread( + self._llm.generate, + self._prompt_name, + message, + log_context=f"agent:{request_id}", + trace=trace, + ) diff --git a/src/app/modules/orchestration/adapters/task_runtime_adapter.py b/src/app/modules/agent/orchestration/adapters/task_runtime_adapter.py similarity index 90% rename from src/app/modules/orchestration/adapters/task_runtime_adapter.py rename to src/app/modules/agent/orchestration/adapters/task_runtime_adapter.py index 0437b5d..42ac762 100644 --- a/src/app/modules/orchestration/adapters/task_runtime_adapter.py +++ b/src/app/modules/agent/orchestration/adapters/task_runtime_adapter.py @@ -4,8 +4,8 @@ from types import SimpleNamespace from app.core.exceptions import AppError from app.modules.agent.task_runtime.facade import AgentTaskRuntimeFacade -from app.modules.agent_api.domain.models.agent_session import AgentSession -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.api.domain.models.agent_session import AgentSession +from app.modules.agent.orchestration.context.execution_context import ExecutionContext from app.schemas.common import ModuleName diff --git a/src/app/modules/agent/orchestration/context/execution_context.py b/src/app/modules/agent/orchestration/context/execution_context.py new file mode 100644 index 0000000..9db381f --- /dev/null +++ b/src/app/modules/agent/orchestration/context/execution_context.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from app.modules.api.domain.models.agent_request import AgentRequest +from app.modules.api.domain.models.agent_session import AgentSession +from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger +from app.modules.agent.observability.module_trace import RequestTraceContext +from app.modules.agent.orchestration.messaging.client_message_publisher import ClientMessagePublisher + + +@dataclass(slots=True) +class ExecutionContext: + request: AgentRequest + session: AgentSession + publisher: ClientMessagePublisher + trace_logger: RequestTraceLogger + trace: RequestTraceContext + task_context: Any = None + route_result: Any = None + workflow_result: Any = None diff --git a/src/app/modules/orchestration/facade.py b/src/app/modules/agent/orchestration/facade.py similarity index 74% rename from src/app/modules/orchestration/facade.py rename to src/app/modules/agent/orchestration/facade.py index 85b807c..25bf6d8 100644 --- a/src/app/modules/orchestration/facade.py +++ b/src/app/modules/agent/orchestration/facade.py @@ -3,14 +3,15 @@ from __future__ import annotations from datetime import datetime, timezone from app.core.exceptions import AppError -from app.modules.agent_api.domain.models.agent_request import AgentRequest -from app.modules.agent_api.domain.models.agent_session import AgentSession -from app.modules.agent_api.infrastructure.logging.request_trace_logger import RequestTraceLogger -from app.modules.agent_api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore -from app.modules.orchestration.context.execution_context import ExecutionContext -from app.modules.orchestration.messaging.client_message_publisher import ClientMessagePublisher -from app.modules.orchestration.processes.registry import ProcessRegistry -from app.modules.orchestration.runtime.process_runner import ProcessRunner +from app.modules.api.domain.models.agent_request import AgentRequest +from app.modules.api.domain.models.agent_session import AgentSession +from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger +from app.modules.agent.observability.module_trace import RequestTraceContext +from app.modules.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore +from app.modules.agent.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.messaging.client_message_publisher import ClientMessagePublisher +from app.modules.agent.orchestration.processes.registry import ProcessRegistry +from app.modules.agent.orchestration.runtime.process_runner import ProcessRunner from app.schemas.common import ErrorPayload, ModuleName from app.schemas.orchestration import RequestExecutionStatus @@ -43,6 +44,7 @@ class OrchestrationFacade: session=session, publisher=self._publisher, trace_logger=self._trace_logger, + trace=RequestTraceContext(request_id=request.request_id, logger=self._trace_logger), ) await self._process_runner.run(context, process.steps()) request.status = RequestExecutionStatus.DONE @@ -56,7 +58,7 @@ class OrchestrationFacade: request.error = ErrorPayload(code=exc.code, desc=exc.desc, module=exc.module) else: request.error = ErrorPayload( - code="agent_api_runtime_error", + code="api_runtime_error", desc="Agent request failed unexpectedly.", module=ModuleName.AGENT, ) diff --git a/src/app/modules/orchestration/messaging/client_message_publisher.py b/src/app/modules/agent/orchestration/messaging/client_message_publisher.py similarity index 70% rename from src/app/modules/orchestration/messaging/client_message_publisher.py rename to src/app/modules/agent/orchestration/messaging/client_message_publisher.py index 5c88a57..cdfb20f 100644 --- a/src/app/modules/orchestration/messaging/client_message_publisher.py +++ b/src/app/modules/agent/orchestration/messaging/client_message_publisher.py @@ -1,9 +1,9 @@ from __future__ import annotations -from app.modules.agent_api.infrastructure.logging.request_trace_logger import RequestTraceLogger -from app.modules.agent_api.infrastructure.streaming.sse_event_channel import SseEventChannel -from app.modules.orchestration.messaging.status_message_factory import StatusMessageFactory -from app.modules.orchestration.messaging.user_message_factory import UserMessageFactory +from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger +from app.modules.api.infrastructure.streaming.sse_event_channel import SseEventChannel +from app.modules.agent.orchestration.messaging.status_message_factory import StatusMessageFactory +from app.modules.agent.orchestration.messaging.user_message_factory import UserMessageFactory class ClientMessagePublisher: diff --git a/src/app/modules/orchestration/messaging/status_message_factory.py b/src/app/modules/agent/orchestration/messaging/status_message_factory.py similarity index 84% rename from src/app/modules/orchestration/messaging/status_message_factory.py rename to src/app/modules/agent/orchestration/messaging/status_message_factory.py index 0d6aa9e..b11befd 100644 --- a/src/app/modules/orchestration/messaging/status_message_factory.py +++ b/src/app/modules/agent/orchestration/messaging/status_message_factory.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.agent_api.domain.events.client_event import ClientEventRecord +from app.modules.api.domain.events.client_event import ClientEventRecord from app.schemas.client_events import ClientEventType diff --git a/src/app/modules/orchestration/messaging/user_message_factory.py b/src/app/modules/agent/orchestration/messaging/user_message_factory.py similarity index 84% rename from src/app/modules/orchestration/messaging/user_message_factory.py rename to src/app/modules/agent/orchestration/messaging/user_message_factory.py index 2ded13e..aa24fb5 100644 --- a/src/app/modules/orchestration/messaging/user_message_factory.py +++ b/src/app/modules/agent/orchestration/messaging/user_message_factory.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.agent_api.domain.events.client_event import ClientEventRecord +from app.modules.api.domain.events.client_event import ClientEventRecord from app.schemas.client_events import ClientEventType diff --git a/src/app/modules/orchestration/processes/registry.py b/src/app/modules/agent/orchestration/processes/registry.py similarity index 69% rename from src/app/modules/orchestration/processes/registry.py rename to src/app/modules/agent/orchestration/processes/registry.py index df55209..6da53ba 100644 --- a/src/app/modules/orchestration/processes/registry.py +++ b/src/app/modules/agent/orchestration/processes/registry.py @@ -1,7 +1,7 @@ from __future__ import annotations -from app.modules.orchestration.processes.v1.process import V1Process -from app.modules.orchestration.processes.v2.process import V2Process +from app.modules.agent.orchestration.processes.v1.process import V1Process +from app.modules.agent.orchestration.processes.v2.process import V2Process class ProcessRegistry: diff --git a/src/app/modules/orchestration/processes/v1/process.py b/src/app/modules/agent/orchestration/processes/v1/process.py similarity index 100% rename from src/app/modules/orchestration/processes/v1/process.py rename to src/app/modules/agent/orchestration/processes/v1/process.py diff --git a/src/app/modules/agent/orchestration/processes/v1/prompt_payload_builder.py b/src/app/modules/agent/orchestration/processes/v1/prompt_payload_builder.py new file mode 100644 index 0000000..9faf40e --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v1/prompt_payload_builder.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +import json + +from app.modules.agent.orchestration.context.execution_context import ExecutionContext + + +class V1PromptPayloadBuilder: + def build(self, context: ExecutionContext) -> str: + payload = {"question": context.request.message} + return json.dumps(payload, ensure_ascii=False, indent=2) diff --git a/src/app/modules/agent/orchestration/processes/v1/prompts.yml b/src/app/modules/agent/orchestration/processes/v1/prompts.yml new file mode 100644 index 0000000..243f86c --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v1/prompts.yml @@ -0,0 +1,12 @@ +prompts: + simple_llm_answer: | + Ты полезный AI-ассистент проекта. + + На вход приходит JSON с полем: + - question + + Правила: + - Отвечай как персонаж мемов из дагестана + - Если вопрос неясный, аккуратно укажи, чего не хватает + - Не выдумывай несуществующие факты о проекте + - Формулируй ответ как обычное сообщение пользователю diff --git a/src/app/modules/agent/orchestration/processes/v1/simple_llm_workflow.py b/src/app/modules/agent/orchestration/processes/v1/simple_llm_workflow.py new file mode 100644 index 0000000..62ef2bf --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v1/simple_llm_workflow.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from app.modules.agent.orchestration.adapters.llm_chat_adapter import LlmChatAdapter +from app.modules.agent.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.processes.v1.prompt_payload_builder import V1PromptPayloadBuilder + + +class SimpleLlmWorkflow: + workflow_id = "simple_llm" + + def __init__( + self, + llm: LlmChatAdapter, + prompt_payload_builder: V1PromptPayloadBuilder | None = None, + ) -> None: + self._llm = llm + self._payload_builder = prompt_payload_builder or V1PromptPayloadBuilder() + + async def run(self, context: ExecutionContext) -> dict: + workflow_trace = context.trace.module("task_workflow") + workflow_trace.log("started", {"workflow_id": self.workflow_id}) + prompt_payload = self._payload_builder.build(context) + answer = await self._llm.generate( + prompt_payload, + context.request.request_id, + trace=context.trace.module("llm"), + ) + result = { + "workflow_id": self.workflow_id, + "prompt_name": self._llm.prompt_name, + "prompt_payload": prompt_payload, + "answer": answer, + } + workflow_trace.log( + "completed", + { + "workflow_id": self.workflow_id, + "prompt_name": self._llm.prompt_name, + "answer_length": len(answer), + }, + ) + return result diff --git a/src/app/modules/orchestration/processes/v1/steps/bootstrap_step.py b/src/app/modules/agent/orchestration/processes/v1/steps/bootstrap_step.py similarity index 63% rename from src/app/modules/orchestration/processes/v1/steps/bootstrap_step.py rename to src/app/modules/agent/orchestration/processes/v1/steps/bootstrap_step.py index 5813686..34e344d 100644 --- a/src/app/modules/orchestration/processes/v1/steps/bootstrap_step.py +++ b/src/app/modules/agent/orchestration/processes/v1/steps/bootstrap_step.py @@ -1,11 +1,14 @@ from __future__ import annotations -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class BootstrapStep: async def run(self, context: ExecutionContext) -> None: - context.trace_logger.log_step(context.request.request_id, "bootstrap", "started") + context.trace.module("orchestrator").log( + "bootstrap", + {"status": "started", "process_version": context.request.process_version}, + ) await context.publisher.publish_status( context.request.request_id, "orchestrator", @@ -17,4 +20,4 @@ class BootstrapStep: "Запускаю процесс обработки v1.", {"process_version": context.request.process_version}, ) - context.trace_logger.log_step(context.request.request_id, "bootstrap", "completed") + context.trace.module("orchestrator").log("bootstrap", {"status": "completed"}) diff --git a/src/app/modules/agent/orchestration/processes/v1/steps/execute_llm_workflow_step.py b/src/app/modules/agent/orchestration/processes/v1/steps/execute_llm_workflow_step.py new file mode 100644 index 0000000..61d7d1a --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v1/steps/execute_llm_workflow_step.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from app.modules.agent.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.processes.v1.simple_llm_workflow import SimpleLlmWorkflow + + +class ExecuteLlmWorkflowStep: + def __init__(self, workflow: SimpleLlmWorkflow) -> None: + self._workflow = workflow + + async def run(self, context: ExecutionContext) -> None: + request = context.request + await context.publisher.publish_status( + request.request_id, + "task_workflow", + f"Запускаю workflow {self._workflow.workflow_id}.", + ) + await context.publisher.publish_status( + request.request_id, + "prompt_builder", + "Формирую prompt payload для LLM.", + ) + result = await self._workflow.run(context) + request.answer = str(result.get("answer") or "") + context.workflow_result = result + await context.publisher.publish_status( + request.request_id, + "llm_process", + "Ответ от LLM получен.", + { + "workflow_id": result.get("workflow_id"), + "prompt_name": result.get("prompt_name"), + "answer_length": len(request.answer), + }, + ) diff --git a/src/app/modules/orchestration/processes/v1/steps/finalize_step.py b/src/app/modules/agent/orchestration/processes/v1/steps/finalize_step.py similarity index 65% rename from src/app/modules/orchestration/processes/v1/steps/finalize_step.py rename to src/app/modules/agent/orchestration/processes/v1/steps/finalize_step.py index 789d973..68afc79 100644 --- a/src/app/modules/orchestration/processes/v1/steps/finalize_step.py +++ b/src/app/modules/agent/orchestration/processes/v1/steps/finalize_step.py @@ -1,12 +1,12 @@ from __future__ import annotations -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class FinalizeStep: async def run(self, context: ExecutionContext) -> None: request = context.request - context.trace_logger.log_step(request.request_id, "finalize", "started") + context.trace.module("orchestrator").log("finalize", {"status": "started"}) await context.publisher.publish_user( request.request_id, "agent", @@ -17,4 +17,4 @@ class FinalizeStep: "orchestrator", "Обработка запроса завершена.", ) - context.trace_logger.log_step(request.request_id, "finalize", "completed") + context.trace.module("orchestrator").log("finalize", {"status": "completed"}) diff --git a/src/app/modules/orchestration/processes/v1/steps/run_llm_step.py b/src/app/modules/agent/orchestration/processes/v1/steps/run_llm_step.py similarity index 83% rename from src/app/modules/orchestration/processes/v1/steps/run_llm_step.py rename to src/app/modules/agent/orchestration/processes/v1/steps/run_llm_step.py index ef362cb..42c3c74 100644 --- a/src/app/modules/orchestration/processes/v1/steps/run_llm_step.py +++ b/src/app/modules/agent/orchestration/processes/v1/steps/run_llm_step.py @@ -1,7 +1,7 @@ from __future__ import annotations -from app.modules.orchestration.adapters.llm_chat_adapter import LlmChatAdapter -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.adapters.llm_chat_adapter import LlmChatAdapter +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class RunLlmStep: diff --git a/src/app/modules/orchestration/processes/v2/README.md b/src/app/modules/agent/orchestration/processes/v2/README.md similarity index 98% rename from src/app/modules/orchestration/processes/v2/README.md rename to src/app/modules/agent/orchestration/processes/v2/README.md index 970a895..0a7d516 100644 --- a/src/app/modules/orchestration/processes/v2/README.md +++ b/src/app/modules/agent/orchestration/processes/v2/README.md @@ -163,7 +163,7 @@ Typical sequence: ## Trace Logging Per-request trace files are written by: -[request_trace_logger.py](/Users/alex/Dev_projects_v2/ai driven app process/v2/agent/src/app/modules/agent_api/infrastructure/logging/request_trace_logger.py) +[request_trace_logger.py](/Users/alex/Dev_projects_v2/ai driven app process/v2/agent/src/app/modules/api/infrastructure/logging/request_trace_logger.py) Location: [runtime_traces/agent_requests](/Users/alex/Dev_projects_v2/ai driven app process/v2/agent/runtime_traces/agent_requests) diff --git a/src/app/modules/orchestration/processes/v2/process.py b/src/app/modules/agent/orchestration/processes/v2/process.py similarity index 100% rename from src/app/modules/orchestration/processes/v2/process.py rename to src/app/modules/agent/orchestration/processes/v2/process.py diff --git a/src/app/modules/agent/orchestration/processes/v2/prompt_payload_builder.py b/src/app/modules/agent/orchestration/processes/v2/prompt_payload_builder.py new file mode 100644 index 0000000..223e3b8 --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v2/prompt_payload_builder.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import json + +from app.modules.agent.runtime.docs_qa_pipeline.models import DocsEvidenceBundle, OpenAPIResult + + +class V2PromptPayloadBuilder: + def build( + self, + *, + question: str, + intent: str, + sub_intent: str, + evidence_bundle: DocsEvidenceBundle, + api_contract: OpenAPIResult | None = None, + ) -> str: + payload = { + "question": question, + "documents": list(evidence_bundle.documents), + "facts": list(evidence_bundle.facts), + "entities": list(evidence_bundle.entities), + "workflows": list(evidence_bundle.workflows), + "relations": list(evidence_bundle.relations), + "chunks": list(evidence_bundle.chunks), + } + if api_contract is not None: + payload["api_contract"] = { + "path": api_contract.path, + "method": api_contract.method, + "request_schema": api_contract.request_schema, + "response_schema": api_contract.response_schema, + "diagnostics": dict(api_contract.diagnostics), + } + return json.dumps(payload, ensure_ascii=False, indent=2) diff --git a/src/app/modules/agent/orchestration/processes/v2/prompt_selector.py b/src/app/modules/agent/orchestration/processes/v2/prompt_selector.py new file mode 100644 index 0000000..16714f6 --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v2/prompt_selector.py @@ -0,0 +1,18 @@ +"""Выбор prompt для process-local workflow v2.""" + +from __future__ import annotations + + +class V2PromptSelector: + _DOCS_INTENT_PROMPTS = { + "DOCUMENTATION_EXPLAIN": "documentation_explain_answer", + "GENERAL_QA": "general_qa_answer", + } + + def select(self, *, intent: str = "DOCUMENTATION_EXPLAIN", sub_intent: str, answer_mode: str) -> str: + intent_key = (intent or "DOCUMENTATION_EXPLAIN").upper() + if intent_key in {"OPENAPI_GENERATION", "OPENAPI_FROM_DOCUMENTATION"}: + if sub_intent.upper() == "OPENAPI_FRAGMENT_GENERATE": + return "openapi_fragment_answer" + return "openapi_answer" + return self._DOCS_INTENT_PROMPTS.get(intent_key, "documentation_explain_answer") diff --git a/src/app/modules/agent/orchestration/processes/v2/prompts.yml b/src/app/modules/agent/orchestration/processes/v2/prompts.yml new file mode 100644 index 0000000..5142637 --- /dev/null +++ b/src/app/modules/agent/orchestration/processes/v2/prompts.yml @@ -0,0 +1,96 @@ +prompts: + documentation_explain_answer: | + Ты объясняешь документацию системы. + + На вход приходит JSON с полями: + - question + - documents + - facts + - entities + - workflows + - relations + - chunks + + Правила: + - Используй только предоставленные факты + - Не додумывай + - Если данных недостаточно, скажи это явно + - Объясняй структурировано + + Формат ответа: + 1. Краткое описание + 2. Основные элементы + 3. Как это работает + 4. Связи с другими частями системы (если есть) + general_qa_answer: | + Ты отвечаешь на общий вопрос по документации проекта. + + На вход приходит JSON с полями: + - question + - documents + - facts + - entities + - workflows + - relations + - chunks + + Правила: + - Используй только предоставленные документы и факты + - Не додумывай отсутствующие детали + - Если данных недостаточно, скажи это прямо + - Дай короткий понятный ответ без лишней структуры + openapi_answer: | + Ты генерируешь OpenAPI спецификацию по документации API. + + На вход приходит JSON с полями: + - question + - documents + - facts + - entities + - workflows + - relations + - chunks + - api_contract + + Правила: + - Используй только данные из документации + - Не придумывай поля + - Если данных нет, не заполняй + - Верни ТОЛЬКО YAML без пояснений + + Формат: + paths: + /path: + method: + summary: ... + requestBody: + responses: + openapi_fragment_answer: | + Ты генерируешь часть OpenAPI schema по документации API. + + На вход приходит JSON с полями: + - question + - documents + - facts + - entities + - workflows + - relations + - chunks + - api_contract + + Правила: + - Используй только данные из документации + - Не придумывай отсутствующие поля + - Верни только содержимое нужного фрагмента + fallback_answer: | + Ты формируешь безопасный fallback-ответ. + + На вход приходит JSON с полями: + - question + - attachments + - confluence_urls + + Правила: + - Если специализированный workflow не выбран, честно скажи об ограничении + - Используй только данные из payload + - Не выдумывай детали проекта diff --git a/src/app/modules/orchestration/processes/v2/steps/execute_documentation_workflow_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/execute_documentation_workflow_step.py similarity index 58% rename from src/app/modules/orchestration/processes/v2/steps/execute_documentation_workflow_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/execute_documentation_workflow_step.py index 4ccab36..1268038 100644 --- a/src/app/modules/orchestration/processes/v2/steps/execute_documentation_workflow_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/execute_documentation_workflow_step.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase +from app.modules.agent.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase class ExecuteDocumentationWorkflowStep(WorkflowStepBase): diff --git a/src/app/modules/orchestration/processes/v2/steps/execute_fallback_workflow_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/execute_fallback_workflow_step.py similarity index 70% rename from src/app/modules/orchestration/processes/v2/steps/execute_fallback_workflow_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/execute_fallback_workflow_step.py index 14baeaa..6bf3c1f 100644 --- a/src/app/modules/orchestration/processes/v2/steps/execute_fallback_workflow_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/execute_fallback_workflow_step.py @@ -1,7 +1,7 @@ from __future__ import annotations -from app.modules.orchestration.context.execution_context import ExecutionContext -from app.modules.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase +from app.modules.agent.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase class ExecuteFallbackWorkflowStep(WorkflowStepBase): diff --git a/src/app/modules/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py similarity index 55% rename from src/app/modules/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py index 4f42118..f7893fd 100644 --- a/src/app/modules/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/execute_general_qa_workflow_step.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase +from app.modules.agent.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase class ExecuteGeneralQaWorkflowStep(WorkflowStepBase): diff --git a/src/app/modules/orchestration/processes/v2/steps/execute_openapi_workflow_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/execute_openapi_workflow_step.py similarity index 56% rename from src/app/modules/orchestration/processes/v2/steps/execute_openapi_workflow_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/execute_openapi_workflow_step.py index 0b35a01..755f6c1 100644 --- a/src/app/modules/orchestration/processes/v2/steps/execute_openapi_workflow_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/execute_openapi_workflow_step.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase +from app.modules.agent.orchestration.processes.v2.steps.workflow_step_base import WorkflowStepBase class ExecuteOpenApiWorkflowStep(WorkflowStepBase): diff --git a/src/app/modules/orchestration/processes/v2/steps/route_intent_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/route_intent_step.py similarity index 78% rename from src/app/modules/orchestration/processes/v2/steps/route_intent_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/route_intent_step.py index cfc282b..e02990b 100644 --- a/src/app/modules/orchestration/processes/v2/steps/route_intent_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/route_intent_step.py @@ -5,9 +5,9 @@ import asyncio from app.core.exceptions import AppError from app.modules.agent.task_runtime.context import TaskRuntimeContextBuilder from app.modules.agent.task_runtime.enrichment import ContextEnrichmentService -from app.modules.orchestration.adapters.intent_router_adapter import IntentRouterAdapter -from app.modules.orchestration.context.execution_context import ExecutionContext -from app.modules.orchestration.v2_progress import build_progress_callback +from app.modules.agent.orchestration.adapters.intent_router_adapter import IntentRouterAdapter +from app.modules.agent.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.v2_progress import build_progress_callback from app.schemas.common import ModuleName @@ -41,10 +41,10 @@ class RouteIntentStep: attachments=[], files=[], progress_cb=build_progress_callback(loop, context.publisher, request.request_id), + trace=context.trace, ) task_context.enriched_context = self._enrichment.enrich(task_context) context.task_context = task_context - context.trace_logger.log_step(request.request_id, "intent_router", "started") await context.publisher.publish_status( request.request_id, "intent_router", @@ -55,6 +55,7 @@ class RouteIntentStep: request.message, task_context.conversation_state, task_context.repo_context, + context.trace.module("intent_router"), ) task_context.route_result = route_result context.route_result = route_result @@ -68,13 +69,3 @@ class RouteIntentStep: "matched_intent_source": route_result.matched_intent_source, }, ) - context.trace_logger.log_step( - request.request_id, - "intent_router", - "completed", - { - "intent": route_result.intent, - "sub_intent": route_result.query_plan.sub_intent, - "matched_intent_source": route_result.matched_intent_source, - }, - ) diff --git a/src/app/modules/orchestration/processes/v2/steps/run_task_workflow_step.py b/src/app/modules/agent/orchestration/processes/v2/steps/run_task_workflow_step.py similarity index 84% rename from src/app/modules/orchestration/processes/v2/steps/run_task_workflow_step.py rename to src/app/modules/agent/orchestration/processes/v2/steps/run_task_workflow_step.py index 2b47b0e..f8d0a1b 100644 --- a/src/app/modules/orchestration/processes/v2/steps/run_task_workflow_step.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/run_task_workflow_step.py @@ -1,7 +1,7 @@ from __future__ import annotations -from app.modules.orchestration.adapters.task_runtime_adapter import TaskRuntimeAdapter -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.adapters.task_runtime_adapter import TaskRuntimeAdapter +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class RunTaskWorkflowStep: diff --git a/src/app/modules/orchestration/processes/v2/steps/workflow_step_base.py b/src/app/modules/agent/orchestration/processes/v2/steps/workflow_step_base.py similarity index 88% rename from src/app/modules/orchestration/processes/v2/steps/workflow_step_base.py rename to src/app/modules/agent/orchestration/processes/v2/steps/workflow_step_base.py index ab24088..bbd26ce 100644 --- a/src/app/modules/orchestration/processes/v2/steps/workflow_step_base.py +++ b/src/app/modules/agent/orchestration/processes/v2/steps/workflow_step_base.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from typing import Any -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class WorkflowStepBase: @@ -20,7 +20,6 @@ class WorkflowStepBase: async def run(self, context: ExecutionContext) -> None: request = context.request task_context = context.task_context - context.trace_logger.log_step(request.request_id, self._step_name, "started") await context.publisher.publish_status( request.request_id, "task_workflow", @@ -32,12 +31,6 @@ class WorkflowStepBase: request.answer = result.answer or "" diagnostics = dict(result.meta.get("diagnostics") or {}) await self._publish_diagnostics(context, diagnostics, result) - context.trace_logger.log_step( - request.request_id, - self._step_name, - "completed", - {"workflow_id": self._workflow.workflow_id, "meta": result.meta}, - ) async def _publish_diagnostics(self, context: ExecutionContext, diagnostics: dict[str, Any], result: Any) -> None: request_id = context.request.request_id diff --git a/src/app/modules/orchestration/runtime/process_runner.py b/src/app/modules/agent/orchestration/runtime/process_runner.py similarity index 79% rename from src/app/modules/orchestration/runtime/process_runner.py rename to src/app/modules/agent/orchestration/runtime/process_runner.py index 92cebe4..2bed04a 100644 --- a/src/app/modules/orchestration/runtime/process_runner.py +++ b/src/app/modules/agent/orchestration/runtime/process_runner.py @@ -1,6 +1,6 @@ from __future__ import annotations -from app.modules.orchestration.context.execution_context import ExecutionContext +from app.modules.agent.orchestration.context.execution_context import ExecutionContext class ProcessRunner: diff --git a/src/app/modules/orchestration/v2_progress.py b/src/app/modules/agent/orchestration/v2_progress.py similarity index 83% rename from src/app/modules/orchestration/v2_progress.py rename to src/app/modules/agent/orchestration/v2_progress.py index 2b8393e..7d1ade4 100644 --- a/src/app/modules/orchestration/v2_progress.py +++ b/src/app/modules/agent/orchestration/v2_progress.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from app.modules.orchestration.messaging.client_message_publisher import ClientMessagePublisher +from app.modules.agent.orchestration.messaging.client_message_publisher import ClientMessagePublisher def build_progress_callback(loop: asyncio.AbstractEventLoop, publisher: ClientMessagePublisher, request_id: str): diff --git a/src/app/modules/agent/runtime/docs_qa_pipeline/pipeline.py b/src/app/modules/agent/runtime/docs_qa_pipeline/pipeline.py index af42996..7755a3f 100644 --- a/src/app/modules/agent/runtime/docs_qa_pipeline/pipeline.py +++ b/src/app/modules/agent/runtime/docs_qa_pipeline/pipeline.py @@ -1,11 +1,9 @@ from __future__ import annotations -import math from time import perf_counter from typing import Any from app.modules.agent.llm import AgentLlmService -from app.modules.agent.llm.prompt_loader import PromptLoader from app.modules.agent.intent_router_v2.analysis.docs_query_signals import DocsQuerySignals from app.modules.agent.runtime.docs_qa_pipeline.answer_synthesizer import DocsAnswerSynthesizer from app.modules.agent.runtime.docs_qa_pipeline.anchor_selector import DocsAnchorSelector @@ -19,6 +17,7 @@ from app.modules.agent.runtime.docs_qa_pipeline.prompt_payload_builder import Do from app.modules.agent.runtime.legacy_pipeline import RetrievalAdapter from app.modules.agent.runtime.steps.context import build_retrieval_request from app.modules.agent.runtime.steps.generation import RuntimePromptSelector +from app.modules.agent.observability.module_trace import RequestTraceContext class DocsQAPipelineRunner: @@ -60,6 +59,7 @@ class DocsQAPipelineRunner: *, conversation_state: Any = None, mode: str = "full", + trace: RequestTraceContext | None = None, ) -> DocsQAPipelineResult: timings: dict[str, int] = {} t0 = perf_counter() @@ -88,6 +88,16 @@ class DocsQAPipelineRunner: ) if request.sub_intent == "RELATED_DOCS_EXPLAIN" and not raw_rows and self._has_relation_hits(unfiltered_rows): raw_rows = unfiltered_rows + if trace is not None: + trace.module("rag_retrieval").log( + "completed", + { + "planned_layers": list(request.requested_layers), + "executed_layers": list(retrieval_report.get("executed_layers") or request.requested_layers), + "layer_diagnostics": dict(retrieval_report.get("layer_diagnostics") or {}), + "rows": len(raw_rows), + }, + ) timings["retrieval"] = _ms(t1) t2 = perf_counter() @@ -140,7 +150,14 @@ class DocsQAPipelineRunner: ) answer = openapi_result.raw_yaml if answer_mode != "degraded" else "Недостаточно contract evidence для OpenAPI." else: - answer = self._generate_openapi_answer(user_query, router_result.intent, request.sub_intent, evidence_bundle, openapi_result) + answer = self._generate_openapi_answer( + user_query, + router_result.intent, + request.sub_intent, + evidence_bundle, + openapi_result, + trace=trace, + ) output_valid, llm_details = self._openapi_postprocessor.validate( answer, require_paths=request.sub_intent != "OPENAPI_FRAGMENT_GENERATE", @@ -176,7 +193,13 @@ class DocsQAPipelineRunner: ) output_valid = answer_mode != "degraded" else: - answer = self._generate_docs_answer(user_query, router_result.intent, request.sub_intent, evidence_bundle) + answer = self._generate_docs_answer( + user_query, + router_result.intent, + request.sub_intent, + evidence_bundle, + trace=trace, + ) answer_mode, degraded_reason, answer = self._finalize_docs_answer( answer=answer, raw_rows=raw_rows, @@ -197,6 +220,16 @@ class DocsQAPipelineRunner: openapi_result=openapi_result, router_result=router_result, ) + if trace is not None: + trace.module("evidence_gate").log( + "evaluated", + { + "decision": gate_decision, + "reason": gate_decision_reason, + "missing": gate_missing_requirements, + "satisfied": gate_satisfied_requirements, + }, + ) diagnostics = self._diagnostics_builder.build( intent=router_result.intent, sub_intent=request.sub_intent, @@ -255,7 +288,7 @@ class DocsQAPipelineRunner: mode=mode, ) - def _generate_docs_answer(self, question: str, intent: str, sub_intent: str, evidence_bundle) -> str: + def _generate_docs_answer(self, question: str, intent: str, sub_intent: str, evidence_bundle, trace=None) -> str: if self._llm is None: return self._answer_synthesizer.synthesize(question, evidence_bundle) payload = self._prompt_payload_builder.build( @@ -265,9 +298,14 @@ class DocsQAPipelineRunner: evidence_bundle=evidence_bundle, ) prompt_name = self._prompt_selector.select(intent=intent, sub_intent=sub_intent, answer_mode="normal") - return self._llm.generate(prompt_name, payload, log_context="graph.project_qa.docs.answer").strip() + return self._llm.generate( + prompt_name, + payload, + log_context="graph.project_qa.docs.answer", + trace=trace.module("llm") if trace is not None else None, + ).strip() - def _generate_openapi_answer(self, question: str, intent: str, sub_intent: str, evidence_bundle, api_contract) -> str: + def _generate_openapi_answer(self, question: str, intent: str, sub_intent: str, evidence_bundle, api_contract, trace=None) -> str: if self._llm is None: return api_contract.raw_yaml payload = self._prompt_payload_builder.build( @@ -278,7 +316,12 @@ class DocsQAPipelineRunner: api_contract=api_contract, ) prompt_name = self._prompt_selector.select(intent=intent, sub_intent=sub_intent, answer_mode="normal") - return self._llm.generate(prompt_name, payload, log_context="graph.project_qa.docs.openapi").strip() + return self._llm.generate( + prompt_name, + payload, + log_context="graph.project_qa.docs.openapi", + trace=trace.module("llm") if trace is not None else None, + ).strip() def _llm_mode(self, intent: str, sub_intent: str) -> str: if sub_intent == "RELATED_DOCS_EXPLAIN": @@ -307,19 +350,14 @@ class DocsQAPipelineRunner: evidence_bundle=evidence_bundle, api_contract=api_contract, ) - system_prompt = PromptLoader().load(prompt_name) or "You are a helpful assistant." - tokens_in_estimate = max(1, int(math.ceil((len(system_prompt) + len(user_prompt)) / 4))) - return { - "prompt_name": prompt_name, - "system_prompt": system_prompt, - "user_prompt": user_prompt, - "log_context": log_context, - "prompt_stats": { - "system_chars": len(system_prompt), - "user_chars": len(user_prompt), - "tokens_in_estimate": tokens_in_estimate, - }, - } + if self._llm is None: + return { + "prompt_name": prompt_name, + "system_prompt": "You are a helpful assistant.", + "user_prompt": user_prompt, + "log_context": log_context, + } + return self._llm.build_request(prompt_name, user_prompt, log_context=log_context) def _finalize_docs_answer( self, diff --git a/src/app/modules/agent/runtime/docs_qa_pipeline/prompt_payload_builder.py b/src/app/modules/agent/runtime/docs_qa_pipeline/prompt_payload_builder.py index fc35ad7..2b85fda 100644 --- a/src/app/modules/agent/runtime/docs_qa_pipeline/prompt_payload_builder.py +++ b/src/app/modules/agent/runtime/docs_qa_pipeline/prompt_payload_builder.py @@ -17,8 +17,6 @@ class DocsPromptPayloadBuilder: ) -> str: payload = { "question": question, - "intent": intent, - "sub_intent": sub_intent, "documents": list(evidence_bundle.documents), "facts": list(evidence_bundle.facts), "entities": list(evidence_bundle.entities), diff --git a/src/app/modules/agent/task_runtime/context.py b/src/app/modules/agent/task_runtime/context.py index 6404fd5..082fe2e 100644 --- a/src/app/modules/agent/task_runtime/context.py +++ b/src/app/modules/agent/task_runtime/context.py @@ -21,6 +21,7 @@ class TaskRuntimeContextBuilder: attachments: list[dict], files: list[dict], progress_cb, + trace=None, ) -> TaskRuntimeContext: files_map = self._files_to_map(files) return TaskRuntimeContext( @@ -33,6 +34,7 @@ class TaskRuntimeContextBuilder: files=list(files or []), files_map=files_map, progress_cb=progress_cb, + trace=trace, repo_context=self._repo_context_factory.build(files_map), conversation_state=ConversationState(), ) diff --git a/src/app/modules/agent/task_runtime/models.py b/src/app/modules/agent/task_runtime/models.py index fee0ab6..e2ad651 100644 --- a/src/app/modules/agent/task_runtime/models.py +++ b/src/app/modules/agent/task_runtime/models.py @@ -20,6 +20,7 @@ class TaskRuntimeContext: files: list[dict[str, Any]] = field(default_factory=list) files_map: dict[str, dict[str, Any]] = field(default_factory=dict) progress_cb: ProgressCallback | None = None + trace: Any = None repo_context: Any = None conversation_state: Any = None route_result: Any = None diff --git a/src/app/modules/agent/task_runtime/workflows/docs_qa.py b/src/app/modules/agent/task_runtime/workflows/docs_qa.py index 14da8e8..2f55323 100644 --- a/src/app/modules/agent/task_runtime/workflows/docs_qa.py +++ b/src/app/modules/agent/task_runtime/workflows/docs_qa.py @@ -13,11 +13,17 @@ class DocsQaWorkflow: self._runner = runner def run(self, ctx: TaskRuntimeContext) -> WorkflowExecutionResult: + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "started", + {"workflow_id": self.workflow_id, "message": ctx.message}, + ) result = self._runner.run( ctx.message, ctx.rag_session_id, conversation_state=ctx.conversation_state, mode="full", + trace=ctx.trace, ) diagnostics = result.diagnostics.model_dump(mode="json") emit_status_block( @@ -42,15 +48,22 @@ class DocsQaWorkflow: title="Evidence Gate", lines=_gate_lines(diagnostics), ) - return WorkflowExecutionResult( + result_payload = WorkflowExecutionResult( result_type=TaskResultType.ANSWER, answer=result.answer, meta={ "workflow_id": self.workflow_id, "intent": result.router_result.intent, "diagnostics": diagnostics, + "llm_request": result.llm_request, }, ) + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "completed", + {"workflow_id": self.workflow_id, "answer_length": len(result.answer or "")}, + ) + return result_payload def _retrieval_lines(diagnostics: dict) -> list[str]: diff --git a/src/app/modules/agent/task_runtime/workflows/fallback.py b/src/app/modules/agent/task_runtime/workflows/fallback.py index a96d01c..cb5c3b7 100644 --- a/src/app/modules/agent/task_runtime/workflows/fallback.py +++ b/src/app/modules/agent/task_runtime/workflows/fallback.py @@ -15,6 +15,11 @@ class FallbackWorkflow: self._llm = llm def run(self, ctx: TaskRuntimeContext) -> WorkflowExecutionResult: + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "started", + {"workflow_id": self.workflow_id, "message": ctx.message}, + ) emit_status_block( ctx, block_id="rag_retrieval", @@ -27,14 +32,18 @@ class FallbackWorkflow: payload = json.dumps( { "question": ctx.message, - "intent": getattr(ctx.route_result, "intent", ""), "attachments": list(ctx.attachments), "confluence_urls": list(ctx.enriched_context.get("confluence_urls") or []), }, ensure_ascii=False, indent=2, ) - answer = self._llm.generate("docs_fallback_answer", payload, log_context="agent.workflow.fallback").strip() + answer = self._llm.generate( + "fallback_answer", + payload, + log_context="agent.workflow.fallback", + trace=ctx.trace.module("llm") if ctx.trace is not None else None, + ).strip() emit_status_block( ctx, block_id="workflow", @@ -47,8 +56,14 @@ class FallbackWorkflow: title="Evidence Gate", lines=["not applied in fallback workflow"], ) - return WorkflowExecutionResult( + result = WorkflowExecutionResult( result_type=TaskResultType.ANSWER, answer=answer, meta={"workflow_id": self.workflow_id, "intent": getattr(ctx.route_result, "intent", "")}, ) + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "completed", + {"workflow_id": self.workflow_id, "answer_length": len(answer)}, + ) + return result diff --git a/src/app/modules/agent/task_runtime/workflows/general_qa.py b/src/app/modules/agent/task_runtime/workflows/general_qa.py index aa9ba41..9991eb3 100644 --- a/src/app/modules/agent/task_runtime/workflows/general_qa.py +++ b/src/app/modules/agent/task_runtime/workflows/general_qa.py @@ -14,11 +14,17 @@ class GeneralQaWorkflow: self._runner = runner def run(self, ctx: TaskRuntimeContext) -> WorkflowExecutionResult: + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "started", + {"workflow_id": self.workflow_id, "message": ctx.message}, + ) result = self._runner.run( ctx.message, ctx.rag_session_id, conversation_state=ctx.conversation_state, mode="full", + trace=ctx.trace, ) diagnostics = result.diagnostics.model_dump(mode="json") emit_status_block( @@ -43,12 +49,19 @@ class GeneralQaWorkflow: title="Evidence Gate", lines=_gate_lines(diagnostics), ) - return WorkflowExecutionResult( + result_payload = WorkflowExecutionResult( result_type=TaskResultType.ANSWER, answer=result.answer, meta={ "workflow_id": self.workflow_id, "intent": result.router_result.intent, "diagnostics": diagnostics, + "llm_request": result.llm_request, }, ) + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "completed", + {"workflow_id": self.workflow_id, "answer_length": len(result.answer or "")}, + ) + return result_payload diff --git a/src/app/modules/agent/task_runtime/workflows/openapi.py b/src/app/modules/agent/task_runtime/workflows/openapi.py index f9e01f3..fb6d95b 100644 --- a/src/app/modules/agent/task_runtime/workflows/openapi.py +++ b/src/app/modules/agent/task_runtime/workflows/openapi.py @@ -14,11 +14,17 @@ class OpenApiWorkflow: self._runner = runner def run(self, ctx: TaskRuntimeContext) -> WorkflowExecutionResult: + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "started", + {"workflow_id": self.workflow_id, "message": ctx.message}, + ) result = self._runner.run( ctx.message, ctx.rag_session_id, conversation_state=ctx.conversation_state, mode="full", + trace=ctx.trace, ) diagnostics = result.diagnostics.model_dump(mode="json") emit_status_block( @@ -51,7 +57,7 @@ class OpenApiWorkflow: format="yaml", source_refs=list(result.diagnostics.doc_paths), ) - return WorkflowExecutionResult( + result_payload = WorkflowExecutionResult( result_type=TaskResultType.OPENAPI, answer=content, artifacts=[artifact], @@ -59,5 +65,12 @@ class OpenApiWorkflow: "workflow_id": self.workflow_id, "intent": result.router_result.intent, "diagnostics": diagnostics, + "llm_request": result.llm_request, }, ) + if ctx.trace is not None: + ctx.trace.module("task_workflow").log( + "completed", + {"workflow_id": self.workflow_id, "answer_length": len(content or "")}, + ) + return result_payload diff --git a/src/app/modules/agent_api/__init__.py b/src/app/modules/agent_api/__init__.py deleted file mode 100644 index c689232..0000000 --- a/src/app/modules/agent_api/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from app.modules.agent_api.module import AgentApiModule - -__all__ = ["AgentApiModule"] diff --git a/src/app/modules/api/__init__.py b/src/app/modules/api/__init__.py new file mode 100644 index 0000000..c9c2ef6 --- /dev/null +++ b/src/app/modules/api/__init__.py @@ -0,0 +1 @@ +__all__: list[str] = [] diff --git a/src/app/modules/agent_api/application/request_service.py b/src/app/modules/api/application/request_service.py similarity index 72% rename from src/app/modules/agent_api/application/request_service.py rename to src/app/modules/api/application/request_service.py index 98cba04..ca4da41 100644 --- a/src/app/modules/agent_api/application/request_service.py +++ b/src/app/modules/api/application/request_service.py @@ -2,11 +2,11 @@ from __future__ import annotations import asyncio -from app.modules.agent_api.domain.models.agent_request import AgentRequest -from app.modules.agent_api.infrastructure.ids.request_id_factory import RequestIdFactory -from app.modules.agent_api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore -from app.modules.agent_api.application.session_service import SessionService -from app.modules.orchestration.facade import OrchestrationFacade +from app.modules.api.domain.models.agent_request import AgentRequest +from app.modules.api.infrastructure.ids.request_id_factory import RequestIdFactory +from app.modules.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore +from app.modules.api.application.session_service import SessionService +from app.modules.agent.orchestration.facade import OrchestrationFacade class RequestService: diff --git a/src/app/modules/agent_api/application/session_service.py b/src/app/modules/api/application/session_service.py similarity index 85% rename from src/app/modules/agent_api/application/session_service.py rename to src/app/modules/api/application/session_service.py index c5ec3a3..1dfeeab 100644 --- a/src/app/modules/agent_api/application/session_service.py +++ b/src/app/modules/api/application/session_service.py @@ -3,9 +3,9 @@ from __future__ import annotations from datetime import datetime, timezone from app.core.exceptions import AppError -from app.modules.agent_api.domain.models.agent_session import AgentSession -from app.modules.agent_api.infrastructure.ids.session_id_factory import SessionIdFactory -from app.modules.agent_api.infrastructure.stores.in_memory_session_store import InMemorySessionStore +from app.modules.api.domain.models.agent_session import AgentSession +from app.modules.api.infrastructure.ids.session_id_factory import SessionIdFactory +from app.modules.api.infrastructure.stores.in_memory_session_store import InMemorySessionStore from app.schemas.common import ModuleName diff --git a/src/app/modules/agent_api/application/stream_service.py b/src/app/modules/api/application/stream_service.py similarity index 83% rename from src/app/modules/agent_api/application/stream_service.py rename to src/app/modules/api/application/stream_service.py index 1d8eba3..1986f5c 100644 --- a/src/app/modules/agent_api/application/stream_service.py +++ b/src/app/modules/api/application/stream_service.py @@ -1,8 +1,8 @@ from __future__ import annotations from app.core.exceptions import AppError -from app.modules.agent_api.infrastructure.streaming.sse_encoder import SseEncoder -from app.modules.agent_api.infrastructure.streaming.sse_event_channel import SseEventChannel +from app.modules.api.infrastructure.streaming.sse_encoder import SseEncoder +from app.modules.api.infrastructure.streaming.sse_event_channel import SseEventChannel from app.schemas.common import ModuleName diff --git a/src/app/modules/agent_api/controllers/request_controller.py b/src/app/modules/api/controllers/request_controller.py similarity index 94% rename from src/app/modules/agent_api/controllers/request_controller.py rename to src/app/modules/api/controllers/request_controller.py index 85abb95..c2f1841 100644 --- a/src/app/modules/agent_api/controllers/request_controller.py +++ b/src/app/modules/api/controllers/request_controller.py @@ -1,7 +1,7 @@ from __future__ import annotations from app.core.exceptions import AppError -from app.modules.agent_api.application.request_service import RequestService +from app.modules.api.application.request_service import RequestService from app.schemas.agent_api import AgentRequestCreateRequest, AgentRequestQueuedResponse, AgentRequestStateResponse from app.schemas.common import ModuleName diff --git a/src/app/modules/agent_api/controllers/session_controller.py b/src/app/modules/api/controllers/session_controller.py similarity index 94% rename from src/app/modules/agent_api/controllers/session_controller.py rename to src/app/modules/api/controllers/session_controller.py index c01938d..996de49 100644 --- a/src/app/modules/agent_api/controllers/session_controller.py +++ b/src/app/modules/api/controllers/session_controller.py @@ -6,7 +6,7 @@ from app.schemas.agent_api import ( CreateAgentSessionResponse, ResetAgentSessionResponse, ) -from app.modules.agent_api.application.session_service import SessionService +from app.modules.api.application.session_service import SessionService class SessionController: diff --git a/src/app/modules/agent_api/controllers/stream_controller.py b/src/app/modules/api/controllers/stream_controller.py similarity index 93% rename from src/app/modules/agent_api/controllers/stream_controller.py rename to src/app/modules/api/controllers/stream_controller.py index 9e18c58..1026e9d 100644 --- a/src/app/modules/agent_api/controllers/stream_controller.py +++ b/src/app/modules/api/controllers/stream_controller.py @@ -2,7 +2,7 @@ from __future__ import annotations from fastapi.responses import StreamingResponse -from app.modules.agent_api.application.stream_service import StreamService +from app.modules.api.application.stream_service import StreamService class StreamController: diff --git a/src/app/modules/agent_api/domain/events/client_event.py b/src/app/modules/api/domain/events/client_event.py similarity index 100% rename from src/app/modules/agent_api/domain/events/client_event.py rename to src/app/modules/api/domain/events/client_event.py diff --git a/src/app/modules/agent_api/domain/models/agent_request.py b/src/app/modules/api/domain/models/agent_request.py similarity index 100% rename from src/app/modules/agent_api/domain/models/agent_request.py rename to src/app/modules/api/domain/models/agent_request.py diff --git a/src/app/modules/agent_api/domain/models/agent_session.py b/src/app/modules/api/domain/models/agent_session.py similarity index 100% rename from src/app/modules/agent_api/domain/models/agent_session.py rename to src/app/modules/api/domain/models/agent_session.py diff --git a/src/app/modules/agent_api/infrastructure/ids/request_id_factory.py b/src/app/modules/api/infrastructure/ids/request_id_factory.py similarity index 100% rename from src/app/modules/agent_api/infrastructure/ids/request_id_factory.py rename to src/app/modules/api/infrastructure/ids/request_id_factory.py diff --git a/src/app/modules/agent_api/infrastructure/ids/session_id_factory.py b/src/app/modules/api/infrastructure/ids/session_id_factory.py similarity index 100% rename from src/app/modules/agent_api/infrastructure/ids/session_id_factory.py rename to src/app/modules/api/infrastructure/ids/session_id_factory.py diff --git a/src/app/modules/agent_api/infrastructure/logging/request_trace_logger.py b/src/app/modules/api/infrastructure/logging/request_trace_logger.py similarity index 77% rename from src/app/modules/agent_api/infrastructure/logging/request_trace_logger.py rename to src/app/modules/api/infrastructure/logging/request_trace_logger.py index 2be1fd9..11001c9 100644 --- a/src/app/modules/agent_api/infrastructure/logging/request_trace_logger.py +++ b/src/app/modules/api/infrastructure/logging/request_trace_logger.py @@ -3,11 +3,11 @@ from __future__ import annotations from pathlib import Path from threading import Lock -from app.modules.agent_api.domain.events.client_event import ClientEventRecord -from app.modules.agent_api.domain.models.agent_request import AgentRequest -from app.modules.agent_api.domain.models.agent_session import AgentSession -from app.modules.agent_api.infrastructure.logging.trace_file_path_builder import TraceFilePathBuilder -from app.modules.agent_api.infrastructure.logging.trace_markdown_writer import TraceMarkdownWriter +from app.modules.api.domain.events.client_event import ClientEventRecord +from app.modules.api.domain.models.agent_request import AgentRequest +from app.modules.api.domain.models.agent_session import AgentSession +from app.modules.api.infrastructure.logging.trace_file_path_builder import TraceFilePathBuilder +from app.modules.api.infrastructure.logging.trace_markdown_writer import TraceMarkdownWriter class RequestTraceLogger: @@ -39,11 +39,17 @@ class RequestTraceLogger: def log_step(self, request_id: str, step: str, status: str, details: dict | None = None) -> None: self._append(request_id, f"Step {step}", {"status": status, "details": details or {}}) + def log_module(self, request_id: str, module: str, title: str, payload: dict | None = None) -> None: + body = {"event": title} + body.update(payload or {}) + self._append(request_id, module, body) + def log_event(self, event: ClientEventRecord) -> None: self._append( event.request_id, - f"Event {event.type.value}", + "client_event", { + "event": event.type.value, "source": event.source, "text": event.text, "payload": event.payload, @@ -54,7 +60,7 @@ class RequestTraceLogger: def complete_request(self, request: AgentRequest) -> None: self._append( request.request_id, - "Result", + "result", { "status": request.status.value, "answer": request.answer, @@ -65,7 +71,7 @@ class RequestTraceLogger: def fail_request(self, request: AgentRequest) -> None: self._append( request.request_id, - "Error", + "result", { "status": request.status.value, "error": request.error.model_dump(mode="json") if request.error else None, diff --git a/src/app/modules/agent_api/infrastructure/logging/trace_file_path_builder.py b/src/app/modules/api/infrastructure/logging/trace_file_path_builder.py similarity index 100% rename from src/app/modules/agent_api/infrastructure/logging/trace_file_path_builder.py rename to src/app/modules/api/infrastructure/logging/trace_file_path_builder.py diff --git a/src/app/modules/agent_api/infrastructure/logging/trace_markdown_writer.py b/src/app/modules/api/infrastructure/logging/trace_markdown_writer.py similarity index 100% rename from src/app/modules/agent_api/infrastructure/logging/trace_markdown_writer.py rename to src/app/modules/api/infrastructure/logging/trace_markdown_writer.py diff --git a/src/app/modules/agent_api/infrastructure/stores/in_memory_request_store.py b/src/app/modules/api/infrastructure/stores/in_memory_request_store.py similarity index 86% rename from src/app/modules/agent_api/infrastructure/stores/in_memory_request_store.py rename to src/app/modules/api/infrastructure/stores/in_memory_request_store.py index 216ca2f..5551330 100644 --- a/src/app/modules/agent_api/infrastructure/stores/in_memory_request_store.py +++ b/src/app/modules/api/infrastructure/stores/in_memory_request_store.py @@ -2,7 +2,7 @@ from __future__ import annotations from threading import Lock -from app.modules.agent_api.domain.models.agent_request import AgentRequest +from app.modules.api.domain.models.agent_request import AgentRequest class InMemoryRequestStore: diff --git a/src/app/modules/agent_api/infrastructure/stores/in_memory_session_store.py b/src/app/modules/api/infrastructure/stores/in_memory_session_store.py similarity index 86% rename from src/app/modules/agent_api/infrastructure/stores/in_memory_session_store.py rename to src/app/modules/api/infrastructure/stores/in_memory_session_store.py index c66109e..1e13564 100644 --- a/src/app/modules/agent_api/infrastructure/stores/in_memory_session_store.py +++ b/src/app/modules/api/infrastructure/stores/in_memory_session_store.py @@ -2,7 +2,7 @@ from __future__ import annotations from threading import Lock -from app.modules.agent_api.domain.models.agent_session import AgentSession +from app.modules.api.domain.models.agent_session import AgentSession class InMemorySessionStore: diff --git a/src/app/modules/agent_api/infrastructure/streaming/replay_buffer.py b/src/app/modules/api/infrastructure/streaming/replay_buffer.py similarity index 88% rename from src/app/modules/agent_api/infrastructure/streaming/replay_buffer.py rename to src/app/modules/api/infrastructure/streaming/replay_buffer.py index b3b27d5..dad9125 100644 --- a/src/app/modules/agent_api/infrastructure/streaming/replay_buffer.py +++ b/src/app/modules/api/infrastructure/streaming/replay_buffer.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections import defaultdict -from app.modules.agent_api.domain.events.client_event import ClientEventRecord +from app.modules.api.domain.events.client_event import ClientEventRecord class ReplayBuffer: diff --git a/src/app/modules/agent_api/infrastructure/streaming/sse_encoder.py b/src/app/modules/api/infrastructure/streaming/sse_encoder.py similarity index 87% rename from src/app/modules/agent_api/infrastructure/streaming/sse_encoder.py rename to src/app/modules/api/infrastructure/streaming/sse_encoder.py index 4d88d91..0817394 100644 --- a/src/app/modules/agent_api/infrastructure/streaming/sse_encoder.py +++ b/src/app/modules/api/infrastructure/streaming/sse_encoder.py @@ -2,7 +2,7 @@ from __future__ import annotations import json -from app.modules.agent_api.domain.events.client_event import ClientEventRecord +from app.modules.api.domain.events.client_event import ClientEventRecord class SseEncoder: diff --git a/src/app/modules/agent_api/infrastructure/streaming/sse_event_channel.py b/src/app/modules/api/infrastructure/streaming/sse_event_channel.py similarity index 90% rename from src/app/modules/agent_api/infrastructure/streaming/sse_event_channel.py rename to src/app/modules/api/infrastructure/streaming/sse_event_channel.py index 684f5b8..dddfd56 100644 --- a/src/app/modules/agent_api/infrastructure/streaming/sse_event_channel.py +++ b/src/app/modules/api/infrastructure/streaming/sse_event_channel.py @@ -3,8 +3,8 @@ from __future__ import annotations import asyncio from collections import defaultdict -from app.modules.agent_api.domain.events.client_event import ClientEventRecord -from app.modules.agent_api.infrastructure.streaming.replay_buffer import ReplayBuffer +from app.modules.api.domain.events.client_event import ClientEventRecord +from app.modules.api.infrastructure.streaming.replay_buffer import ReplayBuffer class SseEventChannel: diff --git a/src/app/modules/agent_api/module.py b/src/app/modules/api/module.py similarity index 50% rename from src/app/modules/agent_api/module.py rename to src/app/modules/api/module.py index 179bf0b..9085af9 100644 --- a/src/app/modules/agent_api/module.py +++ b/src/app/modules/api/module.py @@ -2,16 +2,16 @@ from __future__ import annotations from fastapi import APIRouter -from app.modules.agent_api.application.request_service import RequestService -from app.modules.agent_api.application.session_service import SessionService -from app.modules.agent_api.application.stream_service import StreamService -from app.modules.agent_api.controllers.request_controller import RequestController -from app.modules.agent_api.controllers.session_controller import SessionController -from app.modules.agent_api.controllers.stream_controller import StreamController -from app.modules.agent_api.public_router import build_public_router +from app.modules.api.application.request_service import RequestService +from app.modules.api.application.session_service import SessionService +from app.modules.api.application.stream_service import StreamService +from app.modules.api.controllers.request_controller import RequestController +from app.modules.api.controllers.session_controller import SessionController +from app.modules.api.controllers.stream_controller import StreamController +from app.modules.api.public_router import build_public_router -class AgentApiModule: +class ApiModule: def __init__( self, sessions: SessionService, diff --git a/src/app/modules/agent_api/public_router.py b/src/app/modules/api/public_router.py similarity index 87% rename from src/app/modules/agent_api/public_router.py rename to src/app/modules/api/public_router.py index 17ce058..0bfd27c 100644 --- a/src/app/modules/agent_api/public_router.py +++ b/src/app/modules/api/public_router.py @@ -2,9 +2,9 @@ from __future__ import annotations from fastapi import APIRouter -from app.modules.agent_api.controllers.request_controller import RequestController -from app.modules.agent_api.controllers.session_controller import SessionController -from app.modules.agent_api.controllers.stream_controller import StreamController +from app.modules.api.controllers.request_controller import RequestController +from app.modules.api.controllers.session_controller import SessionController +from app.modules.api.controllers.stream_controller import StreamController from app.schemas.agent_api import ( AgentRequestCreateRequest, AgentRequestQueuedResponse, diff --git a/src/app/modules/application.py b/src/app/modules/application.py index 0018fd9..4d3f035 100644 --- a/src/app/modules/application.py +++ b/src/app/modules/application.py @@ -13,32 +13,36 @@ from app.modules.agent.task_runtime.workflows import ( OpenApiWorkflow, ) from app.modules.agent.task_runtime.workflows.general_qa import GeneralQaWorkflow -from app.modules.agent_api import AgentApiModule -from app.modules.agent_api.application.request_service import RequestService -from app.modules.agent_api.application.session_service import SessionService -from app.modules.agent_api.application.stream_service import StreamService -from app.modules.agent_api.infrastructure.ids.request_id_factory import RequestIdFactory -from app.modules.agent_api.infrastructure.ids.session_id_factory import SessionIdFactory -from app.modules.agent_api.infrastructure.logging.request_trace_logger import RequestTraceLogger -from app.modules.agent_api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore -from app.modules.agent_api.infrastructure.stores.in_memory_session_store import InMemorySessionStore -from app.modules.agent_api.infrastructure.streaming.sse_event_channel import SseEventChannel -from app.modules.orchestration import OrchestrationFacade -from app.modules.orchestration.adapters.intent_router_adapter import IntentRouterAdapter -from app.modules.orchestration.adapters.llm_chat_adapter import LlmChatAdapter -from app.modules.orchestration.messaging.client_message_publisher import ClientMessagePublisher -from app.modules.orchestration.processes.registry import ProcessRegistry -from app.modules.orchestration.processes.v1.process import V1Process -from app.modules.orchestration.processes.v1.steps.bootstrap_step import BootstrapStep -from app.modules.orchestration.processes.v1.steps.finalize_step import FinalizeStep -from app.modules.orchestration.processes.v1.steps.run_llm_step import RunLlmStep -from app.modules.orchestration.processes.v2.process import V2Process -from app.modules.orchestration.processes.v2.steps.execute_documentation_workflow_step import ExecuteDocumentationWorkflowStep -from app.modules.orchestration.processes.v2.steps.execute_fallback_workflow_step import ExecuteFallbackWorkflowStep -from app.modules.orchestration.processes.v2.steps.execute_general_qa_workflow_step import ExecuteGeneralQaWorkflowStep -from app.modules.orchestration.processes.v2.steps.execute_openapi_workflow_step import ExecuteOpenApiWorkflowStep -from app.modules.orchestration.processes.v2.steps.route_intent_step import RouteIntentStep -from app.modules.orchestration.runtime.process_runner import ProcessRunner +from app.modules.api.module import ApiModule +from app.modules.api.application.request_service import RequestService +from app.modules.api.application.session_service import SessionService +from app.modules.api.application.stream_service import StreamService +from app.modules.api.infrastructure.ids.request_id_factory import RequestIdFactory +from app.modules.api.infrastructure.ids.session_id_factory import SessionIdFactory +from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger +from app.modules.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore +from app.modules.api.infrastructure.stores.in_memory_session_store import InMemorySessionStore +from app.modules.api.infrastructure.streaming.sse_event_channel import SseEventChannel +from app.modules.agent.orchestration.facade import OrchestrationFacade +from app.modules.agent.orchestration.adapters.intent_router_adapter import IntentRouterAdapter +from app.modules.agent.orchestration.adapters.llm_chat_adapter import LlmChatAdapter +from app.modules.agent.orchestration.messaging.client_message_publisher import ClientMessagePublisher +from app.modules.agent.orchestration.processes.registry import ProcessRegistry +from app.modules.agent.orchestration.processes.v1.process import V1Process +from app.modules.agent.orchestration.processes.v1.prompt_payload_builder import V1PromptPayloadBuilder +from app.modules.agent.orchestration.processes.v1.simple_llm_workflow import SimpleLlmWorkflow +from app.modules.agent.orchestration.processes.v1.steps.bootstrap_step import BootstrapStep +from app.modules.agent.orchestration.processes.v1.steps.execute_llm_workflow_step import ExecuteLlmWorkflowStep +from app.modules.agent.orchestration.processes.v1.steps.finalize_step import FinalizeStep +from app.modules.agent.orchestration.processes.v2.prompt_payload_builder import V2PromptPayloadBuilder +from app.modules.agent.orchestration.processes.v2.prompt_selector import V2PromptSelector +from app.modules.agent.orchestration.processes.v2.process import V2Process +from app.modules.agent.orchestration.processes.v2.steps.execute_documentation_workflow_step import ExecuteDocumentationWorkflowStep +from app.modules.agent.orchestration.processes.v2.steps.execute_fallback_workflow_step import ExecuteFallbackWorkflowStep +from app.modules.agent.orchestration.processes.v2.steps.execute_general_qa_workflow_step import ExecuteGeneralQaWorkflowStep +from app.modules.agent.orchestration.processes.v2.steps.execute_openapi_workflow_step import ExecuteOpenApiWorkflowStep +from app.modules.agent.orchestration.processes.v2.steps.route_intent_step import RouteIntentStep +from app.modules.agent.orchestration.runtime.process_runner import ProcessRunner from app.modules.rag.persistence.repository import RagRepository from app.modules.agent.runtime.story_context_repository import StoryContextRepository, StoryContextSchemaRepository from app.modules.rag.module import RagModule, RagRepoModule @@ -66,8 +70,10 @@ class ModularApplication: _giga_settings = GigaChatSettings.from_env() _giga_client = GigaChatClient(_giga_settings, GigaChatTokenProvider(_giga_settings)) - _prompt_loader = PromptLoader() - self._agent_llm = AgentLlmService(client=_giga_client, prompts=_prompt_loader) + _v1_prompt_loader = PromptLoader(Path(__file__).resolve().parent / "agent/orchestration/processes/v1/prompts.yml") + _v2_prompt_loader = PromptLoader(Path(__file__).resolve().parent / "agent/orchestration/processes/v2/prompts.yml") + self._agent_llm_v1 = AgentLlmService(client=_giga_client, prompts=_v1_prompt_loader) + self._agent_llm_v2 = AgentLlmService(client=_giga_client, prompts=_v2_prompt_loader) _router = IntentRouterV2() _retrieval = RuntimeRetrievalAdapter(self.rag_repository) _repo_context_factory = RuntimeRepoContextFactory() @@ -75,15 +81,17 @@ class ModularApplication: router=_router, retrieval_adapter=_retrieval, repo_context=_repo_context_factory.build(), - llm=self._agent_llm, + llm=self._agent_llm_v2, + prompt_selector=V2PromptSelector(), + prompt_payload_builder=V2PromptPayloadBuilder(), ) _task_context_builder = TaskRuntimeContextBuilder(_repo_context_factory) _context_enrichment = ContextEnrichmentService() _docs_workflow = DocsQaWorkflow(_docs_runner) _openapi_workflow = OpenApiWorkflow(_docs_runner) _general_qa_workflow = GeneralQaWorkflow(_docs_runner) - _fallback_workflow = FallbackWorkflow(self._agent_llm) - _docs_generation_workflow = DocumentationGenerationWorkflow(self._agent_llm, DocumentationTemplateRegistry()) + _fallback_workflow = FallbackWorkflow(self._agent_llm_v2) + _docs_generation_workflow = DocumentationGenerationWorkflow(self._agent_llm_v2, DocumentationTemplateRegistry()) self._docs_generation_workflow = _docs_generation_workflow self.agent_sessions = InMemorySessionStore() @@ -91,8 +99,12 @@ class ModularApplication: self.agent_events = SseEventChannel() self.agent_trace_logger = RequestTraceLogger(Path("runtime_traces/agent_requests")) _publisher = ClientMessagePublisher(self.agent_events, self.agent_trace_logger) + _v1_workflow = SimpleLlmWorkflow( + LlmChatAdapter(self._agent_llm_v1, prompt_name="simple_llm_answer"), + V1PromptPayloadBuilder(), + ) _process_registry = ProcessRegistry( - V1Process([BootstrapStep(), RunLlmStep(LlmChatAdapter(self._agent_llm)), FinalizeStep()]), + V1Process([BootstrapStep(), ExecuteLlmWorkflowStep(_v1_workflow), FinalizeStep()]), V2Process( [ BootstrapStep(), @@ -123,7 +135,7 @@ class ModularApplication: sessions=_session_service, orchestration=_orchestration, ) - self.agent_api = AgentApiModule( + self.api = ApiModule( sessions=_session_service, requests=_request_service, streams=StreamService(self.agent_events, request_exists=lambda request_id: self.agent_requests.get(request_id) is not None), diff --git a/src/app/modules/chat/README.md b/src/app/modules/chat/README.md deleted file mode 100644 index c6edc18..0000000 --- a/src/app/modules/chat/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Модуль chat - -## 1. Функции модуля -- Внешний API чата: создание диалога, отправка сообщения, получение статуса задачи. -- Асинхронная оркестрация выполнения через `ChatOrchestrator`. -- Idempotency и стриминг событий по SSE. - -## 2. Диаграмма классов и взаимосвязей -```mermaid -classDiagram - class ChatModule - class ChatOrchestrator - class TaskStore - class DialogSessionStore - class IdempotencyStore - class EventBus - class AgentRunner - - ChatModule --> ChatOrchestrator - ChatModule --> TaskStore - ChatModule --> DialogSessionStore - ChatModule --> IdempotencyStore - ChatModule --> EventBus - ChatOrchestrator --> AgentRunner - ChatOrchestrator --> TaskStore - ChatOrchestrator --> DialogSessionStore - ChatOrchestrator --> EventBus -``` - -## 3. Описание классов -- `ChatModule`: фасад модуля и регистрация публичных chat endpoint'ов. - Методы: `__init__` — собирает stores/orchestrator; `public_router` — публикует REST и SSE маршруты чата. -- `ChatOrchestrator`: выполняет жизненный цикл user-message как фоновой задачи. - Методы: `enqueue_message` — создает задачу и запускает обработку; `_process_task` — исполняет runtime и сохраняет результат; `_resolve_sessions` — валидирует и сопоставляет dialog/rag сессии. -- `TaskStore`: in-memory store состояний задач. - Методы: `create` — создает новую `TaskState`; `get` — возвращает задачу по `task_id`; `save` — обновляет состояние задачи. -- `DialogSessionStore`: хранилище dialog-сессий поверх БД. - Методы: `create` — создает новую dialog-сессию; `get` — читает dialog-сессию по id. -- `IdempotencyStore`: предотвращает дубль задач по идемпотентному ключу. - Методы: `get_task_id` — возвращает существующий `task_id` по ключу; `put` — сохраняет ключ и `task_id`. -- `EventBus`: асинхронная публикация/подписка событий. - Методы: `subscribe` — создает подписку на канал; `unsubscribe` — снимает подписку; `publish` — отправляет событие подписчикам; `as_sse` — сериализует событие в SSE формат. -- `AgentRunner` (контракт): интерфейс выполнения агентного запроса из chat-слоя. - Методы: `run` — принимает данные задачи и возвращает итог `answer`/`changeset`. - -## 4. Сиквенс-диаграммы API - -### POST /api/chat/dialogs -Назначение: создает новый диалог, связанный с существующей `rag_session`, чтобы пользователь мог отправлять сообщения в контексте конкретного индекса. -```mermaid -sequenceDiagram - participant Router as ChatModule.APIRouter - participant RagSessions as RagSessionStore - participant Dialogs as DialogSessionStore - - Router->>RagSessions: get(rag_session_id) - RagSessions-->>Router: exists - Router->>Dialogs: create(rag_session_id) - Dialogs-->>Router: dialog_session -``` - -### POST /api/chat/messages -Назначение: ставит сообщение пользователя в асинхронную обработку и возвращает `task_id` для отслеживания результата. -```mermaid -sequenceDiagram - participant Router as ChatModule.APIRouter - participant Orchestrator as ChatOrchestrator - participant TaskStore as TaskStore - - Router->>Orchestrator: enqueue_message(request, idempotency_key) - Orchestrator->>TaskStore: create()/save() - Orchestrator-->>Router: task_id,status -``` - -### GET /api/tasks/{task_id} -Назначение: отдает текущее состояние задачи и финальный результат (answer/changeset/error), когда обработка завершена. -```mermaid -sequenceDiagram - participant Router as ChatModule.APIRouter - participant TaskStore as TaskStore - - Router->>TaskStore: get(task_id) - TaskStore-->>Router: task_state -``` - -### GET /api/events?task_id=... -Назначение: открывает SSE-поток с прогрессом выполнения задачи и промежуточными событиями. -```mermaid -sequenceDiagram - participant Router as ChatModule.APIRouter - participant Events as EventBus - - Router->>Events: subscribe(task_id) - loop until disconnect - Events-->>Router: SSE event - end - Router->>Events: unsubscribe(task_id) -``` diff --git a/src/app/modules/chat/__init__.py b/src/app/modules/chat/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/modules/chat/dialog_store.py b/src/app/modules/chat/dialog_store.py deleted file mode 100644 index ea8f932..0000000 --- a/src/app/modules/chat/dialog_store.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING -from uuid import uuid4 - -if TYPE_CHECKING: - from app.modules.chat.repository import ChatRepository - - -@dataclass -class DialogSession: - dialog_session_id: str - rag_session_id: str - - -class DialogSessionStore: - def __init__(self, repository: ChatRepository) -> None: - self._repo = repository - - def create(self, rag_session_id: str) -> DialogSession: - session = DialogSession(dialog_session_id=str(uuid4()), rag_session_id=rag_session_id) - self._repo.create_dialog(session.dialog_session_id, session.rag_session_id) - return session - - def get(self, dialog_session_id: str) -> DialogSession | None: - row = self._repo.get_dialog(dialog_session_id) - if not row: - return None - return DialogSession( - dialog_session_id=str(row["dialog_session_id"]), - rag_session_id=str(row["rag_session_id"]), - ) diff --git a/src/app/modules/chat/direct_service.py b/src/app/modules/chat/direct_service.py deleted file mode 100644 index 0ab0a2a..0000000 --- a/src/app/modules/chat/direct_service.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -import logging -from uuid import uuid4 - -from app.modules.agent.llm import AgentLlmService -from app.modules.chat.evidence_gate import CodeExplainEvidenceGate -from app.modules.chat.session_resolver import ChatSessionResolver -from app.modules.chat.task_store import TaskState, TaskStore -from app.modules.agent.runtime.steps.explain import CodeExplainRetrieverV2, PromptBudgeter -from app.schemas.chat import ChatMessageRequest, TaskQueuedResponse, TaskResultType, TaskStatus - -LOGGER = logging.getLogger(__name__) - - -class CodeExplainChatService: - def __init__( - self, - retriever: CodeExplainRetrieverV2, - llm: AgentLlmService, - session_resolver: ChatSessionResolver, - task_store: TaskStore, - message_sink, - budgeter: PromptBudgeter | None = None, - evidence_gate: CodeExplainEvidenceGate | None = None, - ) -> None: - self._retriever = retriever - self._llm = llm - self._session_resolver = session_resolver - self._task_store = task_store - self._message_sink = message_sink - self._budgeter = budgeter or PromptBudgeter() - self._evidence_gate = evidence_gate or CodeExplainEvidenceGate() - - async def handle_message(self, request: ChatMessageRequest) -> TaskQueuedResponse: - dialog_session_id, rag_session_id = self._session_resolver.resolve(request) - task_id = str(uuid4()) - task = TaskState(task_id=task_id, status=TaskStatus.RUNNING) - self._task_store.save(task) - self._message_sink(dialog_session_id, "user", request.message, task_id=task_id) - pack = self._retriever.build_pack( - rag_session_id, - request.message, - file_candidates=[item.model_dump(mode="json") for item in request.files], - ) - decision = self._evidence_gate.evaluate(pack) - if decision.passed: - prompt_input = self._budgeter.build_prompt_input(request.message, pack) - answer = self._llm.generate( - "code_explain_answer_v2", - prompt_input, - log_context="chat.code_explain.direct", - ).strip() - else: - answer = decision.answer - self._message_sink(dialog_session_id, "assistant", answer, task_id=task_id) - task.status = TaskStatus.DONE - task.result_type = TaskResultType.ANSWER - task.answer = answer - self._task_store.save(task) - LOGGER.warning( - "direct code explain response: task_id=%s rag_session_id=%s excerpts=%s missing=%s", - task_id, - rag_session_id, - len(pack.code_excerpts), - pack.missing, - ) - return TaskQueuedResponse( - task_id=task_id, - status=TaskStatus.DONE.value, - ) diff --git a/src/app/modules/chat/evidence_gate.py b/src/app/modules/chat/evidence_gate.py deleted file mode 100644 index 3e75f6c..0000000 --- a/src/app/modules/chat/evidence_gate.py +++ /dev/null @@ -1,62 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field - -from app.modules.agent.runtime.steps.explain.models import ExplainPack - - -@dataclass(slots=True) -class EvidenceGateDecision: - passed: bool - answer: str = "" - diagnostics: dict[str, list[str]] = field(default_factory=dict) - - -class CodeExplainEvidenceGate: - def __init__(self, min_excerpts: int = 2) -> None: - self._min_excerpts = min_excerpts - - def evaluate(self, pack: ExplainPack) -> EvidenceGateDecision: - diagnostics = self._diagnostics(pack) - if len(pack.code_excerpts) >= self._min_excerpts: - return EvidenceGateDecision(passed=True, diagnostics=diagnostics) - return EvidenceGateDecision( - passed=False, - answer=self._build_answer(pack, diagnostics), - diagnostics=diagnostics, - ) - - def _diagnostics(self, pack: ExplainPack) -> dict[str, list[str]]: - return { - "entrypoints": [item.title for item in pack.selected_entrypoints[:3] if item.title], - "symbols": [item.title for item in pack.seed_symbols[:5] if item.title], - "paths": self._paths(pack), - "missing": list(pack.missing), - } - - def _paths(self, pack: ExplainPack) -> list[str]: - values: list[str] = [] - for item in pack.selected_entrypoints + pack.seed_symbols: - path = item.source or (item.location.path if item.location else "") - if path and path not in values: - values.append(path) - for excerpt in pack.code_excerpts: - if excerpt.path and excerpt.path not in values: - values.append(excerpt.path) - return values[:6] - - def _build_answer(self, pack: ExplainPack, diagnostics: dict[str, list[str]]) -> str: - lines = [ - "Недостаточно опоры в коде, чтобы дать объяснение без догадок.", - "", - f"Найдено фрагментов кода: {len(pack.code_excerpts)} из {self._min_excerpts} минимально необходимых.", - ] - if diagnostics["paths"]: - lines.append(f"Пути: {', '.join(diagnostics['paths'])}") - if diagnostics["entrypoints"]: - lines.append(f"Entrypoints: {', '.join(diagnostics['entrypoints'])}") - if diagnostics["symbols"]: - lines.append(f"Символы: {', '.join(diagnostics['symbols'])}") - if diagnostics["missing"]: - lines.append(f"Диагностика: {', '.join(diagnostics['missing'])}") - return "\n".join(lines).strip() diff --git a/src/app/modules/chat/module.py b/src/app/modules/chat/module.py deleted file mode 100644 index e89b71f..0000000 --- a/src/app/modules/chat/module.py +++ /dev/null @@ -1,112 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from fastapi import APIRouter, Header -from fastapi.responses import StreamingResponse - -from app.core.exceptions import AppError -from app.modules.chat.dialog_store import DialogSessionStore -from app.modules.chat.service import ChatOrchestrator -from app.modules.chat.task_store import TaskStore -from app.modules.shared.event_bus import EventBus -from app.modules.shared.idempotency_store import IdempotencyStore -from app.modules.shared.retry_executor import RetryExecutor -from app.schemas.chat import ( - ChatMessageRequest, - DialogCreateRequest, - DialogCreateResponse, - TaskQueuedResponse, - TaskResultResponse, -) -from app.schemas.common import ModuleName - -if TYPE_CHECKING: - from app.modules.chat.repository import ChatRepository - from app.modules.contracts import AgentRunner - from app.modules.rag.session_store import RagSessionStore - - -class ChatModule: - def __init__( - self, - agent_runner: AgentRunner, - event_bus: EventBus, - retry: RetryExecutor, - rag_sessions: RagSessionStore, - repository: ChatRepository, - task_store: TaskStore | None = None, - ) -> None: - self._rag_sessions = rag_sessions - self.tasks = task_store or TaskStore() - self.dialogs = DialogSessionStore(repository) - self.idempotency = IdempotencyStore() - self.events = event_bus - self.chat = ChatOrchestrator( - task_store=self.tasks, - dialogs=self.dialogs, - idempotency=self.idempotency, - runtime=agent_runner, - events=self.events, - retry=retry, - rag_session_exists=lambda rag_session_id: rag_sessions.get(rag_session_id) is not None, - message_sink=repository.add_message, - ) - - def public_router(self) -> APIRouter: - router = APIRouter(tags=["chat"]) - - @router.post("/api/chat/dialogs", response_model=DialogCreateResponse) - async def create_dialog(request: DialogCreateRequest) -> DialogCreateResponse: - if not self._rag_sessions.get(request.rag_session_id): - raise AppError("rag_session_not_found", "RAG session not found", ModuleName.RAG) - dialog = self.dialogs.create(request.rag_session_id) - return DialogCreateResponse( - dialog_session_id=dialog.dialog_session_id, - rag_session_id=dialog.rag_session_id, - ) - - @router.post("/api/chat/messages", response_model=TaskQueuedResponse | TaskResultResponse) - async def send_message( - request: ChatMessageRequest, - idempotency_key: str | None = Header(default=None, alias="Idempotency-Key"), - ) -> TaskQueuedResponse | TaskResultResponse: - task = await self.chat.enqueue_message(request, idempotency_key) - return TaskQueuedResponse(task_id=task.task_id, status=task.status.value) - - @router.get("/api/tasks/{task_id}", response_model=TaskResultResponse) - async def get_task(task_id: str) -> TaskResultResponse: - task = self.tasks.get(task_id) - if not task: - raise AppError("not_found", f"Task not found: {task_id}", ModuleName.BACKEND) - return TaskResultResponse( - task_id=task.task_id, - status=task.status, - result_type=task.result_type, - answer=task.answer, - artifacts=task.artifacts, - changeset=task.changeset, - error=task.error, - ) - - @router.get("/api/events") - async def stream_events(task_id: str) -> StreamingResponse: - queue = await self.events.subscribe(task_id) - - async def event_stream(): - import asyncio - - heartbeat = 10 - try: - while True: - try: - event = await asyncio.wait_for(queue.get(), timeout=heartbeat) - yield EventBus.as_sse(event) - except asyncio.TimeoutError: - yield ": keepalive\\n\\n" - finally: - await self.events.unsubscribe(task_id, queue) - - return StreamingResponse(event_stream(), media_type="text/event-stream") - - return router diff --git a/src/app/modules/chat/repository.py b/src/app/modules/chat/repository.py deleted file mode 100644 index e78ff9e..0000000 --- a/src/app/modules/chat/repository.py +++ /dev/null @@ -1,93 +0,0 @@ -import json - -from sqlalchemy import text - -from app.modules.shared.db import get_engine - - -class ChatRepository: - def ensure_tables(self) -> None: - with get_engine().connect() as conn: - conn.execute( - text( - """ - CREATE TABLE IF NOT EXISTS dialog_sessions ( - dialog_session_id VARCHAR(64) PRIMARY KEY, - rag_session_id VARCHAR(64) NOT NULL, - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP - ) - """ - ) - ) - conn.execute( - text( - """ - CREATE TABLE IF NOT EXISTS chat_messages ( - id BIGSERIAL PRIMARY KEY, - dialog_session_id VARCHAR(64) NOT NULL, - task_id VARCHAR(64), - role VARCHAR(16) NOT NULL, - content TEXT NOT NULL, - payload JSONB, - created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP - ) - """ - ) - ) - conn.execute(text("ALTER TABLE chat_messages ADD COLUMN IF NOT EXISTS task_id VARCHAR(64)")) - conn.execute(text("ALTER TABLE chat_messages ADD COLUMN IF NOT EXISTS payload JSONB")) - conn.commit() - - def create_dialog(self, dialog_session_id: str, rag_session_id: str) -> None: - with get_engine().connect() as conn: - conn.execute( - text( - """ - INSERT INTO dialog_sessions (dialog_session_id, rag_session_id) - VALUES (:did, :sid) - """ - ), - {"did": dialog_session_id, "sid": rag_session_id}, - ) - conn.commit() - - def get_dialog(self, dialog_session_id: str) -> dict | None: - with get_engine().connect() as conn: - row = conn.execute( - text( - """ - SELECT dialog_session_id, rag_session_id - FROM dialog_sessions - WHERE dialog_session_id = :did - """ - ), - {"did": dialog_session_id}, - ).mappings().fetchone() - return dict(row) if row else None - - def add_message( - self, - dialog_session_id: str, - role: str, - content: str, - task_id: str | None = None, - payload: dict | None = None, - ) -> None: - payload_json = json.dumps(payload, ensure_ascii=False) if payload is not None else None - with get_engine().connect() as conn: - conn.execute( - text( - """ - INSERT INTO chat_messages (dialog_session_id, task_id, role, content, payload) - VALUES (:did, :task_id, :role, :content, CAST(:payload AS JSONB)) - """ - ), - { - "did": dialog_session_id, - "task_id": task_id, - "role": role, - "content": content, - "payload": payload_json, - }, - ) - conn.commit() diff --git a/src/app/modules/chat/service.py b/src/app/modules/chat/service.py deleted file mode 100644 index 6dd87bf..0000000 --- a/src/app/modules/chat/service.py +++ /dev/null @@ -1,288 +0,0 @@ -import asyncio -import logging - -from app.core.exceptions import AppError -from app.modules.contracts import AgentRunner -from app.schemas.chat import ChatMessageRequest, TaskResultType, TaskStatus -from app.schemas.common import ErrorPayload, ModuleName -from app.modules.chat.dialog_store import DialogSessionStore -from app.modules.chat.session_resolver import ChatSessionResolver -from app.modules.chat.task_store import TaskState, TaskStore -from app.modules.shared.event_bus import EventBus -from app.modules.shared.idempotency_store import IdempotencyStore -from app.modules.shared.retry_executor import RetryExecutor - -LOGGER = logging.getLogger(__name__) - - -def _truncate_for_log(text: str, max_chars: int = 1200) -> str: - value = (text or "").replace("\n", "\\n").strip() - if len(value) <= max_chars: - return value - return value[:max_chars].rstrip() + "...[truncated]" - - -class ChatOrchestrator: - def __init__( - self, - task_store: TaskStore, - dialogs: DialogSessionStore, - idempotency: IdempotencyStore, - runtime: AgentRunner, - events: EventBus, - retry: RetryExecutor, - rag_session_exists, - message_sink, - ) -> None: - self._task_store = task_store - self._dialogs = dialogs - self._idempotency = idempotency - self._runtime = runtime - self._events = events - self._retry = retry - self._rag_session_exists = rag_session_exists - self._message_sink = message_sink - self._session_resolver = ChatSessionResolver(dialogs, rag_session_exists) - - async def enqueue_message( - self, - request: ChatMessageRequest, - idempotency_key: str | None, - ) -> TaskState: - if idempotency_key: - existing = self._idempotency.get_task_id(idempotency_key) - if existing: - task = self._task_store.get(existing) - if task: - LOGGER.info( - "enqueue_message reused task by idempotency key: task_id=%s mode=%s", - task.task_id, - request.mode.value, - ) - return task - - task = self._task_store.create() - if idempotency_key: - self._idempotency.put(idempotency_key, task.task_id) - asyncio.create_task(self._process_task(task.task_id, request)) - LOGGER.info( - "enqueue_message created task: task_id=%s mode=%s", - task.task_id, - request.mode.value, - ) - return task - - async def _process_task(self, task_id: str, request: ChatMessageRequest) -> None: - task = self._task_store.get(task_id) - if not task: - return - task.status = TaskStatus.RUNNING - self._task_store.save(task) - await self._events.publish(task_id, "task_status", {"task_id": task_id, "status": task.status.value}) - await self._publish_progress(task_id, "task.start", "Запрос принят, начинаю обработку.", progress=5) - - heartbeat_stop = asyncio.Event() - heartbeat_task = asyncio.create_task(self._run_heartbeat(task_id, heartbeat_stop)) - - try: - await self._publish_progress(task_id, "task.sessions", "Проверяю сессии диалога и проекта.", progress=10) - dialog_session_id, rag_session_id = self._resolve_sessions(request) - LOGGER.warning( - "incoming chat request: task_id=%s dialog_session_id=%s rag_session_id=%s mode=%s attachments=%s files=%s message=%s", - task_id, - dialog_session_id, - rag_session_id, - request.mode.value, - len(request.attachments), - len(request.files), - _truncate_for_log(request.message), - ) - await self._publish_progress(task_id, "task.sessions.done", "Сессии проверены, запускаю агента.", progress=15) - loop = asyncio.get_running_loop() - - def progress_cb(stage: str, message: str, kind: str = "task_progress", meta: dict | None = None): - asyncio.run_coroutine_threadsafe( - self._events.publish( - task_id, - kind, - { - "task_id": task_id, - "stage": stage, - "message": message, - "meta": meta or {}, - }, - ), - loop, - ) - - async def op(): - self._message_sink(dialog_session_id, "user", request.message, task_id=task_id) - await self._publish_progress(task_id, "task.agent.run", "Агент анализирует запрос и готовит ответ.", progress=20) - return await self._runtime.run( - task_id=task_id, - dialog_session_id=dialog_session_id, - rag_session_id=rag_session_id, - mode=request.mode.value, - message=request.message, - attachments=[a.model_dump(mode="json") for a in request.attachments], - files=[f.model_dump(mode="json") for f in request.files], - progress_cb=progress_cb, - ) - - result = await self._retry.run(op) - await self._publish_progress(task_id, "task.finalize", "Сохраняю финальный результат.", progress=95) - task.status = TaskStatus.DONE - task.result_type = TaskResultType(result.result_type) - task.answer = result.answer - task.artifacts = list(getattr(result, "artifacts", []) or []) - task.changeset = result.changeset - if task.result_type != TaskResultType.CHANGESET and (task.answer or task.artifacts): - payload = { - "result_type": task.result_type.value, - "artifacts": [item.model_dump(mode="json") for item in task.artifacts], - } - self._message_sink(dialog_session_id, "assistant", task.answer or "", task_id=task_id, payload=payload) - LOGGER.warning( - "outgoing chat response: task_id=%s dialog_session_id=%s result_type=%s answer=%s", - task_id, - dialog_session_id, - task.result_type.value, - _truncate_for_log(task.answer or ""), - ) - elif task.result_type == TaskResultType.CHANGESET: - self._message_sink( - dialog_session_id, - "assistant", - f"changeset:{len(task.changeset)}", - task_id=task_id, - payload={ - "result_type": TaskResultType.CHANGESET.value, - "changeset": [item.model_dump(mode="json") for item in task.changeset], - }, - ) - LOGGER.warning( - "outgoing chat response: task_id=%s dialog_session_id=%s result_type=%s changeset_items=%s answer=%s", - task_id, - dialog_session_id, - task.result_type.value, - len(task.changeset), - _truncate_for_log(task.answer or ""), - ) - self._task_store.save(task) - await self._events.publish( - task_id, - "task_result", - { - "task_id": task_id, - "status": task.status.value, - "result_type": task.result_type.value, - "answer": task.answer, - "artifacts": [item.model_dump(mode="json") for item in task.artifacts], - "changeset": [item.model_dump(mode="json") for item in task.changeset], - "meta": getattr(result, "meta", {}) or {}, - }, - ) - await self._publish_progress(task_id, "task.done", "Обработка завершена.", progress=100) - LOGGER.info( - "_process_task completed: task_id=%s status=%s result_type=%s changeset_items=%s", - task_id, - task.status.value, - task.result_type.value if task.result_type else "", - len(task.changeset), - ) - except (AppError, TimeoutError, ConnectionError, OSError) as exc: - task.status = TaskStatus.ERROR - if isinstance(exc, AppError): - payload = ErrorPayload(code=exc.code, desc=exc.desc, module=exc.module) - else: - payload = ErrorPayload( - code="retry_exhausted", - desc="Temporary failure after retries. Please retry request.", - module=ModuleName.BACKEND, - ) - task.error = payload - self._task_store.save(task) - await self._publish_progress(task_id, "task.error", "Не удалось завершить обработку запроса.", kind="task_thinking") - await self._events.publish(task_id, "task_error", payload.model_dump(mode="json")) - LOGGER.warning( - "_process_task handled error: task_id=%s code=%s module=%s desc=%s", - task_id, - payload.code, - payload.module.value, - payload.desc, - ) - except Exception: - task.status = TaskStatus.ERROR - payload = ErrorPayload( - code="agent_runtime_error", - desc="Agent execution failed unexpectedly. Please retry request.", - module=ModuleName.AGENT, - ) - task.error = payload - self._task_store.save(task) - await self._publish_progress( - task_id, - "task.error", - "Во время выполнения возникла внутренняя ошибка.", - kind="task_thinking", - ) - await self._events.publish(task_id, "task_error", payload.model_dump(mode="json")) - LOGGER.exception( - "_process_task unexpected error: task_id=%s code=%s", - task_id, - payload.code, - ) - finally: - heartbeat_stop.set() - await heartbeat_task - - async def _publish_progress( - self, - task_id: str, - stage: str, - message: str, - *, - progress: int | None = None, - kind: str = "task_progress", - meta: dict | None = None, - ) -> None: - payload = { - "task_id": task_id, - "stage": stage, - "message": message, - "meta": meta or {}, - } - if progress is not None: - payload["progress"] = max(0, min(100, int(progress))) - await self._events.publish(task_id, kind, payload) - LOGGER.debug( - "_publish_progress emitted: task_id=%s kind=%s stage=%s progress=%s", - task_id, - kind, - stage, - payload.get("progress"), - ) - - async def _run_heartbeat(self, task_id: str, stop_event: asyncio.Event) -> None: - messages = ( - "Собираю данные по проекту.", - "Анализирую контекст и формирую структуру ответа.", - "Проверяю согласованность промежуточного результата.", - ) - index = 0 - while not stop_event.is_set(): - try: - await asyncio.wait_for(stop_event.wait(), timeout=5.0) - except asyncio.TimeoutError: - await self._publish_progress( - task_id, - "task.heartbeat", - messages[index % len(messages)], - kind="task_thinking", - meta={"heartbeat": True}, - ) - index += 1 - LOGGER.debug("_run_heartbeat stopped: task_id=%s ticks=%s", task_id, index) - - def _resolve_sessions(self, request: ChatMessageRequest) -> tuple[str, str]: - return self._session_resolver.resolve(request) diff --git a/src/app/modules/chat/session_resolver.py b/src/app/modules/chat/session_resolver.py deleted file mode 100644 index 653523b..0000000 --- a/src/app/modules/chat/session_resolver.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from app.core.exceptions import AppError -from app.schemas.chat import ChatMessageRequest -from app.schemas.common import ModuleName - -if TYPE_CHECKING: - from app.modules.chat.dialog_store import DialogSessionStore - - -class ChatSessionResolver: - def __init__(self, dialogs: DialogSessionStore, rag_session_exists) -> None: - self._dialogs = dialogs - self._rag_session_exists = rag_session_exists - - def resolve(self, request: ChatMessageRequest) -> tuple[str, str]: - if request.dialog_session_id and request.rag_session_id: - dialog = self._dialogs.get(request.dialog_session_id) - if not dialog: - raise AppError("dialog_not_found", "Dialog session not found", ModuleName.BACKEND) - if dialog.rag_session_id != request.rag_session_id: - raise AppError("dialog_rag_mismatch", "Dialog session does not belong to rag session", ModuleName.BACKEND) - return request.dialog_session_id, request.rag_session_id - - if request.session_id and request.project_id: - if not self._rag_session_exists(request.project_id): - raise AppError("rag_session_not_found", "RAG session not found", ModuleName.RAG) - return request.session_id, request.project_id - - raise AppError( - "missing_sessions", - "dialog_session_id and rag_session_id are required", - ModuleName.BACKEND, - ) diff --git a/src/app/modules/chat/task_store.py b/src/app/modules/chat/task_store.py deleted file mode 100644 index 4b80247..0000000 --- a/src/app/modules/chat/task_store.py +++ /dev/null @@ -1,38 +0,0 @@ -from dataclasses import dataclass, field -from threading import Lock -from uuid import uuid4 - -from app.schemas.changeset import ChangeItem -from app.schemas.chat import TaskArtifact, TaskResultType, TaskStatus -from app.schemas.common import ErrorPayload - - -@dataclass -class TaskState: - task_id: str - status: TaskStatus = TaskStatus.QUEUED - result_type: TaskResultType | None = None - answer: str | None = None - artifacts: list[TaskArtifact] = field(default_factory=list) - changeset: list[ChangeItem] = field(default_factory=list) - error: ErrorPayload | None = None - - -class TaskStore: - def __init__(self) -> None: - self._items: dict[str, TaskState] = {} - self._lock = Lock() - - def create(self) -> TaskState: - task = TaskState(task_id=str(uuid4())) - with self._lock: - self._items[task.task_id] = task - return task - - def get(self, task_id: str) -> TaskState | None: - with self._lock: - return self._items.get(task_id) - - def save(self, task: TaskState) -> None: - with self._lock: - self._items[task.task_id] = task diff --git a/src/app/modules/orchestration/__init__.py b/src/app/modules/orchestration/__init__.py deleted file mode 100644 index 1c5f050..0000000 --- a/src/app/modules/orchestration/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from app.modules.orchestration.facade import OrchestrationFacade - -__all__ = ["OrchestrationFacade"] diff --git a/src/app/modules/orchestration/adapters/intent_router_adapter.py b/src/app/modules/orchestration/adapters/intent_router_adapter.py deleted file mode 100644 index c20ae83..0000000 --- a/src/app/modules/orchestration/adapters/intent_router_adapter.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import annotations - -from app.modules.agent.intent_router_v2 import IntentRouterV2 - - -class IntentRouterAdapter: - def __init__(self, router: IntentRouterV2) -> None: - self._router = router - - def route(self, user_query: str, conversation_state, repo_context): - return self._router.route(user_query, conversation_state, repo_context) diff --git a/src/app/modules/orchestration/adapters/llm_chat_adapter.py b/src/app/modules/orchestration/adapters/llm_chat_adapter.py deleted file mode 100644 index f80ddc9..0000000 --- a/src/app/modules/orchestration/adapters/llm_chat_adapter.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -import asyncio - -from app.modules.agent.llm.service import AgentLlmService - - -class LlmChatAdapter: - def __init__(self, llm: AgentLlmService, prompt_name: str = "agent_api_v1") -> None: - self._llm = llm - self._prompt_name = prompt_name - - async def generate(self, message: str, request_id: str) -> str: - return await asyncio.to_thread( - self._llm.generate, - self._prompt_name, - message, - log_context=f"agent_api:{request_id}", - ) diff --git a/src/app/modules/orchestration/context/execution_context.py b/src/app/modules/orchestration/context/execution_context.py deleted file mode 100644 index 91ea2e7..0000000 --- a/src/app/modules/orchestration/context/execution_context.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any - -from app.modules.agent_api.domain.models.agent_request import AgentRequest -from app.modules.agent_api.domain.models.agent_session import AgentSession -from app.modules.agent_api.infrastructure.logging.request_trace_logger import RequestTraceLogger -from app.modules.orchestration.messaging.client_message_publisher import ClientMessagePublisher - - -@dataclass(slots=True) -class ExecutionContext: - request: AgentRequest - session: AgentSession - publisher: ClientMessagePublisher - trace_logger: RequestTraceLogger - task_context: Any = None - route_result: Any = None - workflow_result: Any = None diff --git a/src/app/schemas/agent_api.py b/src/app/schemas/agent_api.py index dc460af..a125282 100644 --- a/src/app/schemas/agent_api.py +++ b/src/app/schemas/agent_api.py @@ -33,7 +33,7 @@ class ResetAgentSessionResponse(BaseModel): class AgentRequestCreateRequest(BaseModel): session_id: str = Field(min_length=1) message: str = Field(min_length=1) - process_version: str = Field(default="v2", min_length=1) + process_version: str = Field(default="v1", min_length=1) class AgentRequestQueuedResponse(BaseModel): diff --git a/tests/unit_tests/chat/test_chat_api_simple_code_explain.py b/tests/unit_tests/chat/test_chat_api_simple_code_explain.py deleted file mode 100644 index bb7afad..0000000 --- a/tests/unit_tests/chat/test_chat_api_simple_code_explain.py +++ /dev/null @@ -1,70 +0,0 @@ -import asyncio - -from app.modules.chat.module import ChatModule -from app.modules.chat.task_store import TaskStore -from app.schemas.chat import ChatMessageRequest -from app.schemas.chat import TaskQueuedResponse -from app.modules.shared.event_bus import EventBus -from app.modules.shared.retry_executor import RetryExecutor - - -class _FakeRuntime: - async def run(self, **kwargs): - raise AssertionError("legacy runtime must not be called") - - -class _FakeDirectChat: - def __init__(self) -> None: - self.calls = 0 - - async def handle_message(self, request): - self.calls += 1 - return TaskQueuedResponse( - task_id="task-1", - status="done", - ) - - -class _FakeRagSessions: - def get(self, rag_session_id: str): - return {"rag_session_id": rag_session_id} - - -class _FakeRepository: - def create_dialog(self, dialog_session_id: str, rag_session_id: str) -> None: - return None - - def get_dialog(self, dialog_session_id: str): - return None - - def add_message(self, dialog_session_id: str, role: str, content: str, task_id: str | None = None, payload: dict | None = None) -> None: - return None - - -def test_chat_messages_endpoint_uses_direct_service(monkeypatch) -> None: - monkeypatch.setenv("SIMPLE_CODE_EXPLAIN_ONLY", "true") - direct_chat = _FakeDirectChat() - module = ChatModule( - agent_runner=_FakeRuntime(), - event_bus=EventBus(), - retry=RetryExecutor(), - rag_sessions=_FakeRagSessions(), - repository=_FakeRepository(), - direct_chat=direct_chat, - task_store=TaskStore(), - ) - router = module.public_router() - endpoint = next(route.endpoint for route in router.routes if getattr(route, "path", "") == "/api/chat/messages") - response = asyncio.run( - endpoint( - ChatMessageRequest( - session_id="dialog-1", - project_id="rag-1", - message="Explain get_user", - ), - None, - ) - ) - - assert response.task_id == "task-1" - assert direct_chat.calls == 1 diff --git a/tests/unit_tests/chat/test_direct_service.py b/tests/unit_tests/chat/test_direct_service.py deleted file mode 100644 index f7b9141..0000000 --- a/tests/unit_tests/chat/test_direct_service.py +++ /dev/null @@ -1,61 +0,0 @@ -import asyncio - -from app.modules.chat.direct_service import CodeExplainChatService -from app.modules.chat.session_resolver import ChatSessionResolver -from app.modules.chat.task_store import TaskStore -from app.modules.agent.runtime.steps.explain.models import ExplainIntent, ExplainPack -from app.schemas.chat import ChatFileContext, ChatMessageRequest - - -class _FakeRetriever: - def build_pack(self, rag_session_id: str, user_query: str, *, file_candidates: list[dict] | None = None) -> ExplainPack: - return ExplainPack( - intent=ExplainIntent(raw_query=user_query, normalized_query=user_query), - missing=["code_excerpts"], - ) - - -class _FakeLlm: - def __init__(self) -> None: - self.calls = 0 - - def generate(self, prompt_name: str, user_input: str, *, log_context: str | None = None) -> str: - self.calls += 1 - return "should not be called" - - -class _FakeDialogs: - def get(self, dialog_session_id: str): - return None - - -def test_direct_service_skips_llm_when_evidence_is_insufficient() -> None: - messages: list[tuple[str, str, str, str | None]] = [] - llm = _FakeLlm() - task_store = TaskStore() - service = CodeExplainChatService( - retriever=_FakeRetriever(), - llm=llm, - session_resolver=ChatSessionResolver(_FakeDialogs(), lambda rag_session_id: rag_session_id == "rag-1"), - task_store=task_store, - message_sink=lambda dialog_session_id, role, content, task_id=None: messages.append((dialog_session_id, role, content, task_id)), - ) - - result = asyncio.run( - service.handle_message( - ChatMessageRequest( - session_id="dialog-1", - project_id="rag-1", - message="Explain get_user", - files=[ChatFileContext(path="app/api/users.py", content="", content_hash="x")], - ) - ) - ) - - task = task_store.get(result.task_id) - assert task is not None - assert task.answer is not None - assert "Недостаточно опоры в коде" in task.answer - assert result.status == "done" - assert llm.calls == 0 - assert [item[1] for item in messages] == ["user", "assistant"]