From a03c6fd325d005d920dcba53ff68e862d663450a Mon Sep 17 00:00:00 2001 From: "cristiano.hoshikawa" Date: Fri, 18 Sep 2026 11:47:29 -0300 Subject: [PATCH] new feature: transaction interruption for same intent replacement --- .../en/agent_framework_oci_usage_spec_en.md | 1145 ++++++++++++++ ...agent_template_backend_specification_en.md | 1363 ++++++++++++++++ .../pt/agent_framework_oci_usage_spec_pt.md | 1134 ++++++++++++++ ...agent_template_backend_especificacao_pt.md | 1364 +++++++++++++++++ .../__pycache__/prepare_env.cpython-313.pyc | Bin 0 -> 9393 bytes .../guardrails/dlex_sanitizer.py | 201 +++ .../lib/agent_framework/guardrails/rails.py | 91 +- .../routing/enterprise_router.py | 66 +- .../agent_framework/runtime/agent_runtime.py | 30 +- .../src/agent_framework.egg-info/SOURCES.txt | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 404 -> 413 bytes .../__pycache__/extensions.cpython-313.pyc | Bin 2141 -> 2141 bytes .../__pycache__/idempotency.cpython-313.pyc | Bin 8905 -> 8914 bytes .../__pycache__/observer.cpython-313.pyc | Bin 17557 -> 17557 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 549 -> 549 bytes .../composite_publisher.cpython-313.pyc | Bin 2504 -> 2504 bytes .../__pycache__/event_builder.cpython-313.pyc | Bin 1262 -> 1262 bytes .../__pycache__/factory.cpython-313.pyc | Bin 3998 -> 3998 bytes .../__pycache__/publisher.cpython-313.pyc | Bin 1874 -> 1874 bytes .../tim_payload_mapper.cpython-313.pyc | Bin 9288 -> 9547 bytes .../__pycache__/tim_sequence.cpython-313.pyc | Bin 17563 -> 17563 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 516 -> 516 bytes .../__pycache__/kafka.cpython-313.pyc | Bin 1814 -> 1814 bytes .../__pycache__/langfuse.cpython-313.pyc | Bin 23174 -> 23174 bytes .../__pycache__/oci_streaming.cpython-313.pyc | Bin 1823 -> 1823 bytes .../__pycache__/pubsub.cpython-313.pyc | Bin 7442 -> 7442 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 405 -> 405 bytes .../usage_repository.cpython-313.pyc | Bin 15688 -> 15688 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 206 -> 215 bytes .../cache/__pycache__/cache.cpython-313.pyc | Bin 16516 -> 16525 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 218 -> 218 bytes .../__pycache__/adapters.cpython-313.pyc | Bin 4174 -> 4174 bytes .../channels/__pycache__/base.cpython-313.pyc | Bin 1798 -> 1798 bytes .../__pycache__/gateway.cpython-313.pyc | Bin 5773 -> 5773 bytes .../__pycache__/interruption.cpython-313.pyc | Bin 6458 -> 6458 bytes .../__pycache__/transcription.cpython-313.pyc | Bin 1431 -> 1431 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 925 -> 925 bytes .../checkpoint_repository.cpython-313.pyc | Bin 27165 -> 27165 bytes .../langgraph_saver.cpython-313.pyc | Bin 33267 -> 33267 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 207 -> 216 bytes .../agent_registry.cpython-313.pyc | Bin 6949 -> 6949 bytes .../__pycache__/settings.cpython-313.pyc | Bin 13941 -> 13950 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 299 -> 308 bytes .../mcp_gateway_client.cpython-313.pyc | Bin 3325 -> 3334 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1503 -> 1503 bytes .../__pycache__/base.cpython-313.pyc | Bin 1377 -> 1377 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 9162 -> 9162 bytes .../__pycache__/custom_rails.cpython-313.pyc | Bin 4406 -> 4406 bytes .../dlex_sanitizer.cpython-313.pyc | Bin 0 -> 8489 bytes .../framework_llm_client.cpython-313.pyc | Bin 19675 -> 19675 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 5925 -> 5925 bytes .../output_supervisor.cpython-313.pyc | Bin 24308 -> 24308 bytes .../parallel_executor.cpython-313.pyc | Bin 21864 -> 21864 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 10891 -> 10891 bytes .../__pycache__/rail_action.cpython-313.pyc | Bin 656 -> 656 bytes .../__pycache__/rail_decision.cpython-313.pyc | Bin 1607 -> 1607 bytes .../__pycache__/rail_result.cpython-313.pyc | Bin 1006 -> 1006 bytes .../__pycache__/rails.cpython-313.pyc | Bin 43326 -> 45179 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 2996 -> 2996 bytes .../__pycache__/_compat.cpython-313.pyc | Bin 2447 -> 2447 bytes .../contestation_validation.cpython-313.pyc | Bin 2179 -> 2179 bytes .../__pycache__/contracts.cpython-313.pyc | Bin 6903 -> 6903 bytes .../__pycache__/input_size.cpython-313.pyc | Bin 3651 -> 3651 bytes .../__pycache__/llm_adapter.cpython-313.pyc | Bin 3801 -> 3801 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 7456 -> 7456 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 8896 -> 8896 bytes .../output_sanitization.cpython-313.pyc | Bin 13912 -> 13912 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 12663 -> 12663 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 475 -> 475 bytes .../__pycache__/_context.cpython-313.pyc | Bin 5873 -> 5873 bytes .../ausencia_oferta_proativa.cpython-313.pyc | Bin 12207 -> 12207 bytes .../__pycache__/coerencia.cpython-313.pyc | Bin 4159 -> 4159 bytes .../__pycache__/dlex_in.cpython-313.pyc | Bin 1306 -> 1306 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 1817 -> 1817 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 17654 -> 17654 bytes .../__pycache__/fraseologia.cpython-313.pyc | Bin 7635 -> 7635 bytes .../__pycache__/out_of_scope.cpython-313.pyc | Bin 17495 -> 17495 bytes .../prompts/__pycache__/pinj.cpython-313.pyc | Bin 8426 -> 8426 bytes .../__pycache__/ragsec.cpython-313.pyc | Bin 1122 -> 1122 bytes .../__pycache__/revprec.cpython-313.pyc | Bin 3152 -> 3152 bytes .../prompts/__pycache__/tox.cpython-313.pyc | Bin 646 -> 646 bytes .../toxicidade_output.cpython-313.pyc | Bin 930 -> 930 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1665 -> 1665 bytes .../rails/__pycache__/alcada.cpython-313.pyc | Bin 5001 -> 5001 bytes .../rails/__pycache__/anatel.cpython-313.pyc | Bin 9150 -> 9150 bytes .../__pycache__/confirmation.cpython-313.pyc | Bin 11044 -> 11044 bytes .../rails/__pycache__/dlex_in.cpython-313.pyc | Bin 3281 -> 3281 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 3333 -> 3333 bytes .../rails/__pycache__/ragsec.cpython-313.pyc | Bin 5464 -> 5464 bytes .../rails/__pycache__/revprec.cpython-313.pyc | Bin 5472 -> 5472 bytes .../rails/__pycache__/tox.cpython-313.pyc | Bin 8295 -> 8295 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 535 -> 535 bytes .../rules/__pycache__/alcada.cpython-313.pyc | Bin 1883 -> 1883 bytes .../__pycache__/pinj_patterns.cpython-313.pyc | Bin 3732 -> 3732 bytes .../__pycache__/tox_blocklist.cpython-313.pyc | Bin 1508 -> 1508 bytes .../guardrails/dlex_sanitizer.py | 201 +++ .../src/agent_framework/guardrails/rails.py | 56 +- .../__pycache__/__init__.cpython-313.pyc | Bin 401 -> 410 bytes .../__pycache__/mcp_mapper.cpython-313.pyc | Bin 4685 -> 4694 bytes .../__pycache__/models.cpython-313.pyc | Bin 3137 -> 3146 bytes .../__pycache__/resolver.cpython-313.pyc | Bin 4706 -> 4715 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 620 -> 620 bytes .../judges/__pycache__/judge.cpython-313.pyc | Bin 43986 -> 43986 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 227 -> 227 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 5406 -> 5406 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 235 -> 235 bytes .../prompts/__pycache__/aluc.cpython-313.pyc | Bin 6531 -> 6531 bytes .../prompts/__pycache__/csi.cpython-313.pyc | Bin 1334 -> 1334 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 8037 -> 8037 bytes .../prompts/__pycache__/rqlt.cpython-313.pyc | Bin 1167 -> 1167 bytes .../prompts/__pycache__/vctn.cpython-313.pyc | Bin 628 -> 628 bytes .../llm/__pycache__/__init__.cpython-313.pyc | Bin 469 -> 478 bytes .../llm/__pycache__/base.cpython-313.pyc | Bin 1616 -> 1625 bytes .../profile_resolver.cpython-313.pyc | Bin 9324 -> 9324 bytes .../llm/__pycache__/providers.cpython-313.pyc | Bin 46980 -> 46980 bytes .../structured_output.cpython-313.pyc | Bin 7452 -> 7461 bytes .../llm/__pycache__/types.cpython-313.pyc | Bin 1267 -> 1276 bytes .../mcp/__pycache__/__init__.cpython-313.pyc | Bin 466 -> 475 bytes .../mcp/__pycache__/client.cpython-313.pyc | Bin 18419 -> 18428 bytes .../mcp/__pycache__/models.cpython-313.pyc | Bin 2129 -> 2138 bytes .../mcp/__pycache__/registry.cpython-313.pyc | Bin 4733 -> 4742 bytes .../__pycache__/tool_policy.cpython-313.pyc | Bin 5678 -> 5687 bytes .../__pycache__/tool_router.cpython-313.pyc | Bin 14182 -> 14191 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1128 -> 1137 bytes .../long_term_extractor.cpython-313.pyc | Bin 2515 -> 2515 bytes .../long_term_memory.cpython-313.pyc | Bin 6841 -> 6841 bytes .../long_term_models.cpython-313.pyc | Bin 1750 -> 1750 bytes .../long_term_store.cpython-313.pyc | Bin 29188 -> 29188 bytes .../message_history.cpython-313.pyc | Bin 8352 -> 8361 bytes .../summary_memory.cpython-313.pyc | Bin 12503 -> 12512 bytes .../__pycache__/summary_store.cpython-313.pyc | Bin 9513 -> 9522 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 207 -> 216 bytes .../__pycache__/identity.cpython-313.pyc | Bin 3467 -> 3467 bytes .../__pycache__/session.cpython-313.pyc | Bin 2701 -> 2710 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1396 -> 1396 bytes .../__pycache__/code_mapper.cpython-313.pyc | Bin 21449 -> 21449 bytes .../__pycache__/context.cpython-313.pyc | Bin 7388 -> 7388 bytes .../__pycache__/event_bus.cpython-313.pyc | Bin 3634 -> 3634 bytes .../guardrail_events.cpython-313.pyc | Bin 2052 -> 2052 bytes .../__pycache__/ic_events.cpython-313.pyc | Bin 627 -> 627 bytes .../__pycache__/judge_events.cpython-313.pyc | Bin 1412 -> 1412 bytes .../langgraph_telemetry.cpython-313.pyc | Bin 4965 -> 4965 bytes .../__pycache__/llm_advisors.cpython-313.pyc | Bin 2939 -> 2939 bytes .../__pycache__/noc_contract.cpython-313.pyc | Bin 5076 -> 5076 bytes .../__pycache__/noc_otel.cpython-313.pyc | Bin 6323 -> 6323 bytes .../__pycache__/observer.cpython-313.pyc | Bin 5106 -> 5106 bytes .../__pycache__/otel.cpython-313.pyc | Bin 3413 -> 3413 bytes .../streaming_events.cpython-313.pyc | Bin 1836 -> 1836 bytes .../__pycache__/telemetry.cpython-313.pyc | Bin 52429 -> 52429 bytes .../__pycache__/token_cost.cpython-313.pyc | Bin 8692 -> 8692 bytes .../workflow_events.cpython-313.pyc | Bin 3331 -> 3331 bytes .../oci/__pycache__/__init__.cpython-313.pyc | Bin 213 -> 213 bytes .../oci/__pycache__/auth.cpython-313.pyc | Bin 3245 -> 3245 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 212 -> 221 bytes .../__pycache__/oracle_store.cpython-313.pyc | Bin 45933 -> 45933 bytes .../__pycache__/sqlite_store.cpython-313.pyc | Bin 15011 -> 15020 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 456 -> 456 bytes .../__pycache__/renderers.cpython-313.pyc | Bin 4319 -> 4319 bytes .../rag/__pycache__/__init__.cpython-313.pyc | Bin 711 -> 711 bytes .../embedding_provider.cpython-313.pyc | Bin 6518 -> 6518 bytes .../__pycache__/graph_store.cpython-313.pyc | Bin 5529 -> 5529 bytes .../rag/__pycache__/ingest.cpython-313.pyc | Bin 12582 -> 12582 bytes .../__pycache__/kbdb_service.cpython-313.pyc | Bin 10013 -> 15501 bytes .../__pycache__/rag_service.cpython-313.pyc | Bin 10899 -> 10899 bytes .../__pycache__/vector_store.cpython-313.pyc | Bin 18657 -> 18657 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 222 -> 222 bytes .../session_repository.cpython-313.pyc | Bin 8591 -> 8591 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 640 -> 649 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 2682 -> 2691 bytes .../__pycache__/continuity.cpython-313.pyc | Bin 15926 -> 15935 bytes .../enterprise_router.cpython-313.pyc | Bin 80708 -> 82180 bytes .../__pycache__/models.cpython-313.pyc | Bin 2439 -> 2448 bytes .../__pycache__/multi_intent.cpython-313.pyc | Bin 20259 -> 20268 bytes .../pending_topics.cpython-313.pyc | Bin 16125 -> 16134 bytes .../routing/enterprise_router.py | 66 +- .../__pycache__/__init__.cpython-313.pyc | Bin 341 -> 350 bytes .../__pycache__/agent_runtime.cpython-313.pyc | Bin 206053 -> 206215 bytes .../transaction_context.cpython-313.pyc | Bin 0 -> 8445 bytes .../transaction_parameters.cpython-313.pyc | Bin 19576 -> 19585 bytes .../agent_framework/runtime/agent_runtime.py | 30 +- .../sse/__pycache__/__init__.cpython-313.pyc | Bin 213 -> 213 bytes .../sse/__pycache__/events.cpython-313.pyc | Bin 9589 -> 9589 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 220 -> 220 bytes .../__pycache__/supervisor.cpython-313.pyc | Bin 4945 -> 5405 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 871 -> 880 bytes .../__pycache__/graph.cpython-313.pyc | Bin 1408 -> 1417 bytes .../input_contract.cpython-313.pyc | Bin 8783 -> 8792 bytes .../__pycache__/models.cpython-313.pyc | Bin 7662 -> 7671 bytes .../__pycache__/registry.cpython-313.pyc | Bin 2354 -> 2363 bytes .../__pycache__/repository.cpython-313.pyc | Bin 2707 -> 2716 bytes .../__pycache__/runtime.cpython-313.pyc | Bin 36145 -> 36154 bytes .../__pycache__/tool_executor.cpython-313.pyc | Bin 1959 -> 1968 bytes .../conftest.cpython-313-pytest-9.0.2.pyc | Bin 800 -> 801 bytes ...ral_candidate.cpython-313-pytest-9.0.2.pyc | Bin 64262 -> 64263 bytes ...lm_precedence.cpython-313-pytest-9.0.2.pyc | Bin 58414 -> 58415 bytes ...ession_matrix.cpython-313-pytest-9.0.2.pyc | Bin 52053 -> 52059 bytes ..._interruption.cpython-313-pytest-9.0.2.pyc | Bin 28283 -> 28284 bytes ...est_same_intent_transaction_replacement.py | 164 ++ 198 files changed, 5876 insertions(+), 36 deletions(-) create mode 100644 deploy/docs/en/agent_framework_oci_usage_spec_en.md create mode 100644 deploy/docs/en/agent_template_backend_specification_en.md create mode 100644 deploy/docs/pt/agent_framework_oci_usage_spec_pt.md create mode 100644 deploy/docs/pt/agent_template_backend_especificacao_pt.md create mode 100644 deploy/oke/load-test/scripts/__pycache__/prepare_env.cpython-313.pyc create mode 100644 libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py create mode 100644 libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc create mode 100644 libs/agent_framework/src/agent_framework/guardrails/dlex_sanitizer.py create mode 100644 libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_context.cpython-313.pyc create mode 100644 tests/test_same_intent_transaction_replacement.py diff --git a/deploy/docs/en/agent_framework_oci_usage_spec_en.md b/deploy/docs/en/agent_framework_oci_usage_spec_en.md new file mode 100644 index 00000000..caf82051 --- /dev/null +++ b/deploy/docs/en/agent_framework_oci_usage_spec_en.md @@ -0,0 +1,1145 @@ +# `agent_framework_oci` Usage Specification + +## 1. Purpose + +This specification defines the standard for developing, packaging, and deploying agent projects based on `agent_framework_oci`. + +The primary goal is to establish a clear separation between: + +- the **shared enterprise framework**, responsible for generic platform capabilities; +- the **agent projects**, responsible for domain-specific rules, configuration, workflows, and implementations. + +The `agent_framework` must not be embedded or duplicated inside each agent project. + +The library must be made available separately and consumed by agent projects through standard Python imports. + +--- + +## 2. Framework source + +In the official `agent_framework_oci` repository, the shared framework implementation is located at: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +This directory is the distributable Python package for the framework. + +Its structure includes, among other components: + +```text +agent_framework/ +├── pyproject.toml +├── src/ +│ └── agent_framework/ +│ ├── analytics/ +│ ├── cache/ +│ ├── channels/ +│ ├── guardrails/ +│ ├── memory/ +│ ├── observability/ +│ ├── routing/ +│ ├── supervisor/ +│ ├── workflows/ +│ └── ... +``` + +The `pyproject.toml` defines the package: + +```toml +[project] +name = "agent-framework" +version = "0.1.0" +``` + +and uses: + +```toml +[tool.setuptools.packages.find] +where = ["src"] +``` + +Therefore: + +```text +agent_framework_oci/libs/agent_framework +``` + +must be treated as an independent, reusable Python library. + +--- + +## 3. Required directory structure for agent projects + +When using the framework outside the main repository, the directory: + +```text +agent_framework_oci/libs/agent_framework +``` + +must be made available together with the agent project. + +The agent project and the framework must reside **at the same directory level**. + +Example: + +```text +workspace/ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +└── backoffice/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +Another example: + +```text +workspace/ +├── agent_framework/ +│ +└── agent_contas/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +The architectural rule is: + +```text +/ +├── agent_framework/ +└── / +``` + +and not: + +```text +/ +└── agent_framework/ +``` + +The agent project must not contain a private copy of the framework. + +--- + +## 4. Responsibilities + +### 4.1 `agent_framework` + +The `agent_framework` contains generic and reusable platform capabilities such as: + +- routing; +- supervisor; +- multi-agent; +- workflows; +- conversational states; +- parameter collection; +- pause/resume; +- pending actions; +- multi-intent; +- intent shift; +- route stickiness; +- deterministic transactions; +- guardrails; +- judges; +- output supervisor; +- RAG; +- memory; +- cache; +- observability; +- telemetry; +- MCP integration; +- persistence; +- analytics; +- OCI provider integration; +- resilience mechanisms; +- idempotency; +- error handling. + +These components belong to the framework and must not be copied into consumer projects. + +### 4.2 Agent project + +Each agent project contains only domain-specific implementation and configuration. + +Example: + +```text +backoffice/ +├── app/ +│ ├── agents/ +│ ├── workflows/ +│ ├── observability/ +│ ├── workflow_actions/ +│ ├── presentation/ +│ ├── state.py +│ └── main.py +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── prompt_policy.yaml +│ ├── identity.yaml +│ └── mcp_parameter_mapping.yaml +│ +├── data/ +├── requirements.txt +├── Dockerfile +└── .env +``` + +The project may use any public framework capability through standard imports. + +Example: + +```python +from agent_framework.routing import ... +``` + +or: + +```python +from agent_framework.supervisor import ... +``` + +or: + +```python +from agent_framework.observability import ... +``` + +The project code must not depend on internal repository paths from `agent_framework_oci`. + +--- + +## 5. Local development installation + +Given: + +```text +workspace/ +├── agent_framework/ +└── backoffice/ +``` + +the recommended setup is: + +```bash +cd workspace + +python -m venv .venv + +source .venv/bin/activate +``` + +On Windows: + +```powershell +.venv\Scripts\activate +``` + +Install the framework: + +```bash +pip install -e ./agent_framework +``` + +Install project dependencies: + +```bash +pip install -r ./backoffice/requirements.txt +``` + +Editable mode: + +```bash +pip install -e +``` + +is recommended for development because the Python environment references the framework source directly. + +Framework changes then become immediately available to the agent project without a complete package reinstall. + +--- + +## 6. Mandatory validation + +After installing the framework, the following command must execute successfully: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +Imports used by the application should also be validated: + +```bash +python -c "from agent_framework import *" +``` + +or with specific imports required by the agent implementation. + +An error such as: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indicates that the framework was not made available or installed correctly. + +--- + +## 7. CI/CD model + +A CI/CD pipeline should create a workspace containing both components. + +Example: + +```text +build/ +├── agent_framework/ +└── backoffice/ +``` + +The framework may be obtained directly from: + +```text +agent_framework_oci/libs/agent_framework +``` + +and copied to: + +```text +build/agent_framework +``` + +The agent project should be copied to: + +```text +build/backoffice +``` + +The Docker build must use: + +```text +build/ +``` + +as the build context. + +Example: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +This is important. + +The build context must be: + +```text +. +``` + +representing the directory that contains both: + +```text +agent_framework/ +backoffice/ +``` + +--- + +## 8. Standard Dockerfile + +An agent project should use a Dockerfile similar to the following: + +```dockerfile +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_NO_CACHE_DIR=1 + +WORKDIR /workspace + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +# Shared framework +COPY agent_framework /workspace/agent_framework + +# Agent-specific project +COPY backoffice /workspace/backoffice + +# Install enterprise framework first +RUN pip install --upgrade pip \ + && pip install /workspace/agent_framework + +# Install agent-specific dependencies +RUN pip install -r /workspace/backoffice/requirements.txt + +WORKDIR /workspace/backoffice + +EXPOSE 8000 + +CMD [ + "uvicorn", + "app.main:app", + "--host", + "0.0.0.0", + "--port", + "8000" +] +``` + +For development, the following may optionally be used: + +```dockerfile +RUN pip install -e /workspace/agent_framework +``` + +For immutable production images, the recommended form is: + +```dockerfile +RUN pip install /workspace/agent_framework +``` + +This freezes the framework version used by the image at build time. + +--- + +## 9. Container directory structure + +The container should conceptually preserve the same separation used during development: + +```text +/workspace/ +├── agent_framework/ +└── backoffice/ +``` + +The application runs from: + +```text +/workspace/backoffice +``` + +The framework is installed in the Python environment and may be imported normally: + +```python +import agent_framework +``` + +There should be no need to use: + +```python +sys.path.append(...) +``` + +or dynamically modify `PYTHONPATH` inside the application code. + +--- + +## 10. Framework versioning + +The framework version used by the agent must be determined at build time. + +At minimum, the following should be recorded: + +```text +AGENT_FRAMEWORK_VERSION +AGENT_APPLICATION_VERSION +BUILD_ID +GIT_COMMIT +``` + +Example: + +```yaml +env: + - name: AGENT_FRAMEWORK_VERSION + value: "1.5.0" + + - name: AGENT_APPLICATION_VERSION + value: "2.1.4" +``` + +This makes it possible to identify the exact: + +```text +Framework + Agent +``` + +combination running in each environment. + +--- + +## 11. Image publication + +After the build: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +the image should be published to OCI Container Registry — OCIR. + +Example: + +```bash +docker tag \ + backoffice:1.0.0 \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +Then: + +```bash +docker push \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +--- + +## 12. Deployment on OCI OKE + +Each agent project should have its own Kubernetes `Deployment`. + +The framework does not need to run as an independent Pod. + +`agent_framework` is a library and is included in the agent project image during the build process. + +Architecture: + +```text + OCI OKE + | + v + Kubernetes Service + | + +------------+------------+ + | | | + v v v + Agent Pod Agent Pod Agent Pod + | | | + +------------+------------+ + | + agent_framework + installed in image +``` + +The framework code is shared by source and version, but is installed into each agent image. + +--- + +## 13. Base OKE YAML manifest + +The following manifest can be used as a base for deploying a project such as `backoffice`. + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: agent-platform + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: backoffice-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENT_FRAMEWORK_VERSION: "1.0.0" + + AGENT_ID: "backoffice" + AGENT_APPLICATION_VERSION: "1.0.0" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + ENABLE_LANGFUSE: "true" + ENABLE_OTEL: "true" + ENABLE_ANALYTICS: "true" + +--- +apiVersion: v1 +kind: Secret +metadata: + name: backoffice-secrets + namespace: agent-platform +type: Opaque +stringData: + OCI_COMPARTMENT_ID: "" + OCI_GENAI_API_KEY: "" + LANGFUSE_PUBLIC_KEY: "" + LANGFUSE_SECRET_KEY: "" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backoffice + namespace: agent-platform + labels: + app: backoffice + +spec: + replicas: 3 + + selector: + matchLabels: + app: backoffice + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: backoffice + + spec: + containers: + - name: backoffice + image: gru.ocir.io//agents/backoffice:1.0.0 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8000 + protocol: TCP + + envFrom: + - configMapRef: + name: backoffice-config + - secretRef: + name: backoffice-secrets + + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + + securityContext: + allowPrivilegeEscalation: false + +--- +apiVersion: v1 +kind: Service +metadata: + name: backoffice + namespace: agent-platform + +spec: + type: ClusterIP + + selector: + app: backoffice + + ports: + - name: http + protocol: TCP + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: backoffice + namespace: agent-platform + +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: backoffice + + minReplicas: 3 + maxReplicas: 10 + + behavior: + scaleUp: + stabilizationWindowSeconds: 0 + + policies: + - type: Percent + value: 100 + periodSeconds: 60 + + - type: Pods + value: 4 + periodSeconds: 60 + + selectPolicy: Max + + scaleDown: + stabilizationWindowSeconds: 300 + + policies: + - type: Percent + value: 25 + periodSeconds: 60 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 75 + +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: backoffice + namespace: agent-platform + +spec: + minAvailable: 2 + + selector: + matchLabels: + app: backoffice +``` + +--- + +## 14. Applying the manifest + +The manifest may be stored as: + +```text +deploy/oke/backoffice.yaml +``` + +and applied with: + +```bash +kubectl apply -f deploy/oke/backoffice.yaml +``` + +Validation: + +```bash +kubectl -n agent-platform get pods +kubectl -n agent-platform get deployment +kubectl -n agent-platform get svc +kubectl -n agent-platform get hpa +``` + +To track rollout status: + +```bash +kubectl -n agent-platform rollout status deployment/backoffice +``` + +--- + +## 15. Health check + +Every agent project deployed to OKE should provide: + +```http +GET /health +``` + +returning: + +```text +200 OK +``` + +while the application is ready to receive requests. + +This endpoint is used by: + +```text +startupProbe +readinessProbe +livenessProbe +``` + +--- + +## 16. Persistence and multiple replicas + +OKE applications must assume that multiple Pods may run simultaneously. + +Conversational and transactional data that must survive across requests must therefore not depend exclusively on the container filesystem. + +Local SQLite is not recommended as the definitive persistence layer for multi-replica OKE environments. + +Production environments should use external providers for: + +- sessions; +- checkpoints; +- memory; +- cache; +- idempotency; +- locks; +- analytics; +- transactional state. + +Depending on the framework configuration, examples include: + +```text +Oracle Autonomous Database +MongoDB +Redis +OCI Streaming +OCI Object Storage +``` + +--- + +## 17. Framework upgrades + +An agent project must not receive framework changes by copying individual framework source files into the project. + +Upgrades should be performed by replacing the available version of: + +```text +agent_framework/ +``` + +and rebuilding the image. + +Process: + +```text +New agent_framework version + | + v +CI/CD workspace update + | + v +Agent image build + | + v +Tests + | + v +OCIR push + | + v +OKE Rolling Update +``` + +This maintains a clear separation between: + +```text +product/framework +``` + +and: + +```text +agent implementation +``` + +--- + +## 18. Compatibility rule + +Each project release should explicitly declare the framework version against which it was tested. + +Example: + +```text +Backoffice 3.2.0 +Agent Framework 1.8.0 +``` + +A new framework version should not be promoted directly to production without executing regression tests for the consuming agent project. + +Recommended pipeline: + +```text +Framework + | + v +Build Agent + | + v +Unit Tests + | + v +Integration Tests + | + v +Workflow Regression + | + v +Smoke Test + | + v +Staging + | + v +Production +``` + +--- + +## 19. Multiple projects using the same framework + +The structure may support several projects simultaneously: + +```text +workspace/ +├── agent_framework/ +├── backoffice/ +├── contas/ +├── ofertas/ +├── atendimento/ +└── suporte/ +``` + +All of them use: + +```text +agent_framework/ +``` + +as the common platform source. + +Each application, however, generates its own image: + +```text +backoffice:3.0.0 +contas:5.2.0 +ofertas:1.4.0 +atendimento:2.6.0 +``` + +All images may have been built with: + +```text +agent-framework:1.8.0 +``` + +This allows projects to evolve independently without forking the framework. + +--- + +## 20. Architectural principle + +The relationship between the components should remain: + +```text + agent_framework + | + +---------------+---------------+ + | | | + v v v + Backoffice Contas Ofertas + | | | + v v v + OCI image OCI image OCI image + | | | + +---------------+---------------+ + | + v + OKE +``` + +`agent_framework` is the common technology platform. + +Agent projects are consumers of that platform. + +Therefore: + +**the agent project depends on the framework; the framework must not depend on the agent project.** + +--- + +## 21. Mandatory requirements + +To be considered compatible with `agent_framework_oci`, a project must satisfy the following requirements: + +1. `agent_framework` must be obtained from: + +```text +agent_framework_oci/libs/agent_framework +``` + +2. The framework must be made available separately from the agent project. + +3. The distribution structure must be: + +```text +/ +├── agent_framework/ +└── / +``` + +4. The framework must not be duplicated inside the agent project. + +5. The library must be installed using the standard Python packaging mechanism: + +```bash +pip install ./agent_framework +``` + +or, for development: + +```bash +pip install -e ./agent_framework +``` + +6. Imports must use: + +```python +import agent_framework +``` + +and never physical repository paths. + +7. The Docker build context must have access to both: + +```text +agent_framework/ +``` + +and: + +```text +/ +``` + +8. Each image must contain a known framework version. + +9. OKE deployments must expose health probes. + +10. Scalable applications must externalize shared state. + +11. Secrets must not be stored in source code or committed deployment manifests. + +12. Each project must have its own Deployment and Service. + +13. Horizontal scaling must occur at the agent application layer, not through a centralized executable `agent_framework` instance. + +--- + +## 22. Recommended final structure + +The reference structure for development and CI/CD is: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +├── backoffice/ +│ ├── app/ +│ ├── config/ +│ ├── data/ +│ ├── requirements.txt +│ ├── Dockerfile +│ └── .env.example +│ +└── deploy/ + └── oke/ + └── backoffice.yaml +``` + +For new projects, only the project-specific directory changes: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ +├── new_agent/ +│ +└── deploy/ + └── oke/ + └── new_agent.yaml +``` + +This should be the standard distribution and deployment model for projects built on `agent_framework_oci`. diff --git a/deploy/docs/en/agent_template_backend_specification_en.md b/deploy/docs/en/agent_template_backend_specification_en.md new file mode 100644 index 00000000..23390d1f --- /dev/null +++ b/deploy/docs/en/agent_template_backend_specification_en.md @@ -0,0 +1,1363 @@ +# `agent_template_backend` Usage Specification + +## Complementary document to the general `agent_framework_oci` architecture + +> **Scope of this document** +> +> This document is specific to the `agent_template_backend` distributed with `agent_framework_oci`. +> +> It **does not replace** the general architectural specification for using `agent_framework_oci` in production projects. The general specification remains the normative reference for production project structure, packaging, versioning, CI/CD, state externalization, security, scalability, and OKE deployment. +> +> This document explains how the existing `agent_template_backend` should be used as a **reference template**, development environment, starting point for new agents, and source for derived production projects. + +--- + +## 1. Purpose + +`agent_template_backend` is the agent backend template provided by the `agent_framework_oci` repository. + +Its purpose is to provide a functional starting implementation showing how an agent project consumes `agent_framework`, organizes configuration, exposes APIs, uses workflows, integrates MCP, applies guardrails and judges, and consumes the other enterprise capabilities of the framework. + +The template should be understood as: + +- an implementation reference; +- a starting point for new projects; +- a development and validation environment; +- an integration example for `agent_framework`; +- a deployment example; +- a source for independent derived projects. + +The template **must not be interpreted as an exception** to the architectural rules defined by the general production specification. + +--- + +## 2. Relationship with the general architectural specification + +There are two documentation levels. + +### 2.1. General architectural specification + +The general specification defines the standard for production projects derived from the framework: + +```text +/ +├── agent_framework/ +└── / +``` + +It defines, among other items: + +- separation between framework and project; +- `agent_framework` installation; +- versioning; +- build context; +- Docker; +- CI/CD; +- OKE deployment; +- scalability; +- state externalization; +- secret management; +- compatibility between framework and agent versions. + +### 2.2. This specification + +This document describes how the template delivered in the repository fits into that model. + +Whenever a demonstration setting in the template differs from a rule in the general architectural specification, **the general architectural specification takes precedence for production environments**. + +--- + +## 3. Template location in the repository + +In the source repository, the template is located at: + +```text +agent_framework_oci/ +└── templates/ + └── agent_template_backend/ +``` + +The shared framework is located at: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Therefore, inside the development repository, the structure is: + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +├── mcp/ +├── deploy/ +└── ... +``` + +This is the **source structure of the `agent_framework_oci` product**. + +It does not change the production architecture rule. + +--- + +## 4. Template distribution structure + +When `agent_template_backend` is extracted from the repository for independent use, isolated build, or creation of a derived project, the workspace should materialize: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +The content of: + +```text +agent_framework_oci/libs/agent_framework/ +``` + +should be made available as: + +```text +workspace/agent_framework/ +``` + +and the content of: + +```text +agent_framework_oci/templates/agent_template_backend/ +``` + +as: + +```text +workspace/agent_template_backend/ +``` + +This structure is compatible with the Dockerfile currently delivered with the template. + +--- + +## 5. Current `agent_template_backend` structure + +The main template structure is: + +```text +agent_template_backend/ +├── app/ +│ ├── main.py +│ ├── state.py +│ ├── mcp_gateway_client_factory.py +│ └── ... +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── identity.yaml +│ ├── prompt_policy.yaml +│ ├── mcp_parameter_mapping.yaml +│ ├── mcp_servers.yaml +│ └── mcp_servers.docker.yaml +│ +├── workflows/ +│ ├── devolucao_pedido.active.yaml +│ └── devolucao_pedido.v1.yaml +│ +├── data/ +├── docs/ +├── scripts/ +├── llm_profiles.yaml +├── requirements.txt +├── Dockerfile +├── .env.example +├── README.md +└── README_ENTERPRISE_TEMPLATE.md +``` + +The template contains enough configuration to demonstrate enterprise framework capabilities without copying those capabilities into the project. + +--- + +## 6. Dependency on `agent_framework` + +`agent_template_backend` is a framework consumer. + +The dependency must remain one-way: + +```text +agent_template_backend + | + v +agent_framework +``` + +The inverse must not occur. + +`agent_framework` must not depend on: + +```text +templates/agent_template_backend +``` + +or on template-specific files. + +--- + +## 7. Local installation from the source repository + +For development directly inside the repository: + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +the framework may be installed in editable mode. + +Example, from `templates/agent_template_backend`: + +```bash +pip install -e ../../libs/agent_framework +pip install -r requirements.txt +``` + +Or, from the repository root: + +```bash +pip install -e ./libs/agent_framework +pip install -r ./templates/agent_template_backend/requirements.txt +``` + +Editable mode is appropriate for product development because changes made under `libs/agent_framework` are immediately consumed by the template. + +--- + +## 8. Installation in an independent workspace + +When the template is distributed according to the general architecture: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +use: + +```bash +pip install -e ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +For a production build: + +```bash +pip install ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +--- + +## 9. Installation validation + +The installation must allow: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +and, from the project: + +```bash +python -c "import app.main" +``` + +An error such as: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indicates that the framework was not made available or installed correctly. + +--- + +## 10. Backend execution + +The template backend exposes its FastAPI application on: + +```text +8000 +``` + +Local execution: + +```bash +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` + +Health check: + +```bash +curl http://localhost:8000/health +``` + +Other useful checks: + +```bash +curl http://localhost:8000/agents +curl http://localhost:8000/debug/mcp/tools +``` + +Port `8000` belongs to the agent backend and must not be confused with MCP Server or gateway ports. + +--- + +## 11. Agent configuration + +Template-specific behavior is primarily defined by files external to the framework. + +### `config/agents.yaml` + +Defines available agents and their profiles. + +### `config/routing.yaml` + +Defines intents, routes, and routing behavior. + +### `config/tools.yaml` + +Defines project tools and their MCP bindings. + +### `config/tool_policies.yaml` + +Defines execution, confirmation, control, and transactional requirements for tools. + +### `config/guardrails.yaml` + +Configures project guardrails. + +### `config/judges.yaml` + +Configures judges. + +### `config/identity.yaml` + +Defines identity and business keys. + +### `config/mcp_parameter_mapping.yaml` + +Defines parameter mapping and extraction for MCP tools. + +### `config/mcp_servers.yaml` + +Defines MCP Servers used in local execution. + +### `config/mcp_servers.docker.yaml` + +Defines endpoints suitable for Docker/container environments. + +### `llm_profiles.yaml` + +Defines LLM profiles and providers used by the project. + +This model preserves the separation between: + +```text +generic engine = agent_framework +``` + +and: + +```text +domain rules = agent_template_backend/config + app +``` + +--- + +## 12. Example MCP Servers + +The repository includes development MCP Servers under: + +```text +agent_framework_oci/mcp/servers/ +├── telecom_mcp_server/ +├── retail_mcp_server/ +└── mock_telecom_mcp/ +``` + +The main examples use: + +```text +Telecom MCP : 8100 +Retail MCP : 8200 +``` + +The local template configuration references: + +```yaml +servers: + telecom: + endpoint: http://localhost:8100/mcp + + retail: + endpoint: http://localhost:8200/mcp +``` + +These MCP Servers are separate components from the agent backend. + +--- + +## 13. Starting MCP Servers in development + +The repository provides: + +```text +scripts/run_mcp_servers.sh +``` + +which starts the example MCP Servers. + +Local architecture: + +```text + +------------------+ + | Telecom MCP | + | :8100 | + +---------+--------+ + | + | ++----------------------+ | +| agent_template |---------+ +| backend :8000 | ++----------------------+---------+ + | + | + +---------v--------+ + | Retail MCP | + | :8200 | + +------------------+ +``` + +This script is a development convenience. + +In production, MCP Servers should be deployed according to the target architecture and should not depend on shell processes started inside the backend Pod. + +--- + +## 14. MCP Gateway + +`agent_framework_oci` also contains: + +```text +apps/mcp_gateway/ +``` + +and OKE configuration for the MCP Gateway. + +The default repository example uses: + +```text +8300 +``` + +The template supports: + +```env +MCP_GATEWAY_ENABLED=true +MCP_GATEWAY_URL=http://localhost:8300 +``` + +or, in OKE: + +```text +http://mcp-gateway.agent-platform.svc.cluster.local:8300 +``` + +The MCP Gateway is a platform component and is not physically part of the `agent_template_backend` directory. + +--- + +## 15. Agent Gateway, Channel Gateway, and Frontend + +The repository also contains: + +```text +apps/ +├── agent_gateway/ +├── channel_gateway/ +├── mcp_gateway/ +└── agent_frontend/ +``` + +These should be treated as platform or channel components. + +`agent_template_backend` can operate directly on port `8000` without all of them being required for a simple local test. + +In a complete enterprise topology, the flow may be: + +```text +Client / Frontend + | + v +Channel Gateway + | + v +Agent Gateway + | + v +agent_template_backend + | + v +MCP Gateway + | + v +MCP Servers +``` + +The actual composition must follow the requirements of the target environment. + +--- + +## 16. Development frontend + +The reference frontend is located at: + +```text +agent_framework_oci/apps/agent_frontend/ +``` + +and points by default to: + +```text +http://localhost:8000 +``` + +The frontend should not be moved into `agent_template_backend` merely to satisfy a project structure. + +It is a separate component. + +A production project may: + +- use this frontend; +- use another frontend; +- integrate Web/WhatsApp/Voice; +- expose APIs only; +- use Agent Gateway/Channel Gateway. + +--- + +## 17. Template Dockerfile + +The Dockerfile at: + +```text +templates/agent_template_backend/Dockerfile +``` + +expects a build context containing both: + +```text +agent_framework/ +agent_template_backend/ +``` + +Structure: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +Build: + +```bash +docker build \ + -t agent-template-backend:local \ + -f agent_template_backend/Dockerfile \ + . +``` + +This is aligned with the general architectural specification. + +--- + +## 18. Existing OKE Dockerfile in the repository + +The repository also provides: + +```text +deploy/oke/dockerfiles/Dockerfile.agent-template-backend +``` + +This Dockerfile runs with the `agent_framework_oci` repository root as context and copies: + +```text +libs/agent_framework -> /opt/agent_framework +templates/agent_template_backend -> /app +``` + +There are therefore two valid build contexts for different purposes. + +### Independent development/distribution + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +### Build directly from the monorepo + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +Both materialize the same logical dependency: + +```text +agent_template_backend -> agent_framework +``` + +--- + +## 19. Deriving a production project + +`agent_template_backend` should be used as the source for a new project. + +Example: + +```text +agent_template_backend + | + | derive + v +agent_backoffice +``` + +After derivation, the recommended production structure is: + +```text +workspace/ +├── agent_framework/ +└── agent_backoffice/ +``` + +There is no requirement to preserve the name: + +```text +agent_template_backend +``` + +in production. + +The derived project then owns: + +- its own version lifecycle; +- its own Docker image; +- its own configuration; +- its own agents; +- its own workflows; +- its own policies; +- its own pipeline. + +The `agent_framework` library remains shared. + +--- + +## 20. What should and should not be copied + +### Should be derived into the project + +As required by the domain: + +```text +app/ +config/ +workflows/ +data/ +scripts/ +requirements.txt +Dockerfile +.env.example +``` + +### Should not be copied into the project + +```text +libs/agent_framework +``` + +The framework must remain separately distributed and installed. + +It is also not necessary to automatically copy: + +```text +apps/agent_gateway +apps/channel_gateway +apps/mcp_gateway +apps/agent_frontend +mcp/servers +``` + +Those components should only be included when required by the target topology. + +--- + +## 21. OKE: relationship with the general architecture + +The repository contains a reference OKE implementation under: + +```text +deploy/oke/ +``` + +including: + +```text +deploy/oke/k8s/base/ +├── 00-namespace.yaml +├── 01-configmap.yaml +├── 02-secret-template.yaml +├── 03-agent-template-backend.yaml +├── 04-agent-gateway.yaml +├── 05-channel-gateway.yaml +├── 06-mcp-gateway.yaml +├── 07-frontend.yaml +└── kustomization.yaml +``` + +This implementation demonstrates how the components can be materialized in Kubernetes. + +Production projects must continue to follow the rules defined by the general architectural specification. + +--- + +## 22. Important note about demonstration OKE settings + +The reference configuration currently present in the repository uses values such as: + +```text +SESSION_REPOSITORY_PROVIDER=sqlite +MEMORY_REPOSITORY_PROVIDER=sqlite +CHECKPOINT_REPOSITORY_PROVIDER=sqlite +USAGE_REPOSITORY_PROVIDER=sqlite +VECTOR_STORE_PROVIDER=sqlite +GRAPH_STORE_PROVIDER=sqlite +``` + +These values are useful for demonstrations and controlled environments. + +**They must not be interpreted as the recommended architecture for multi-replica production environments.** + +In production, the general architectural specification takes precedence: shared state that must survive across Pods should use appropriate external providers. + +--- + +## 23. Base OKE deployment for `agent_template_backend` + +The following example represents the template backend specifically. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + replicas: 3 + + selector: + matchLabels: + app: agent-template-backend + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: agent-template-backend + + spec: + containers: + - name: agent-template-backend + image: .ocir.io//agents/agent-template-backend: + + ports: + - name: http + containerPort: 8000 + + envFrom: + - configMapRef: + name: agent-template-backend-config + - secretRef: + name: agent-template-backend-secrets + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + failureThreshold: 3 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + +--- +apiVersion: v1 +kind: Service +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + type: ClusterIP + + selector: + app: agent-template-backend + + ports: + - name: http + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: agent-template-backend + + minReplicas: 3 + maxReplicas: 10 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +Sizing must be adjusted using load test results for the actual project. + +--- + +## 24. Template-specific base ConfigMap + +Example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: agent-template-backend-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_PARALLEL_GUARDRAILS: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + FRAMEWORK_CHANNEL_INPUT_MODE: "embedded" +``` + +Persistence, LLM, memory, cache, observability, and RAG providers should be configured according to the production environment and the general architectural specification. + +--- + +## 25. Complete reference OKE topology + +A complete installation may use: + +```text + +----------------------+ + | Agent Frontend | + +----------+-----------+ + | + v + +----------------------+ + | Channel Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Backend | + | :8000 | + +----------+-----------+ + | + v + +----------------------+ + | MCP Gateway | + | :8300 | + +----------+-----------+ + | + +-------------+-------------+ + | | + v v + +------------------+ +------------------+ + | Telecom MCP | | Retail MCP | + | :8100 | | :8200 | + +------------------+ +------------------+ +``` + +This is a reference topology, not a mandatory topology for every project. + +--- + +## 26. MCP Servers on OKE + +MCP Servers should be deployed as independent components when used in production. + +Conceptual example: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + replicas: 2 + selector: + matchLabels: + app: telecom-mcp + template: + metadata: + labels: + app: telecom-mcp + spec: + containers: + - name: telecom-mcp + image: .ocir.io//mcp/telecom: + ports: + - containerPort: 8100 + +--- +apiVersion: v1 +kind: Service +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + selector: + app: telecom-mcp + ports: + - port: 8100 + targetPort: 8100 +``` + +The same approach may be used for other MCP Servers. + +--- + +## 27. Health, readiness, and startup + +The backend should maintain: + +```text +GET /health +``` + +as the minimum probe endpoint. + +Recommended: + +- `startupProbe` for initialization; +- `readinessProbe` for traffic admission; +- `livenessProbe` to detect an unhealthy process. + +An application should not rely only on `livenessProbe`. + +--- + +## 28. State and scalability + +When using: + +```yaml +replicas: 3 +``` + +there is no guarantee that consecutive requests will be handled by the same Pod. + +Therefore, in production: + +```text +session +checkpoint +memory +idempotency +transaction state +shared cache +``` + +must be assessed for externalization. + +A SQLite database stored under: + +```text +/data +``` + +inside an `emptyDir` volume is local to a Pod and is not shared storage. + +--- + +## 29. Local versus container MCP configuration + +The template separates: + +```text +config/mcp_servers.yaml +``` + +from: + +```text +config/mcp_servers.docker.yaml +``` + +This is intentional. + +Local: + +```yaml +endpoint: http://localhost:8100/mcp +``` + +Docker/Kubernetes: + +```yaml +endpoint: http://telecom-mcp:8100/mcp +``` + +The endpoint must reflect the DNS visible from the backend process. + +--- + +## 30. `.env.example` + +The file: + +```text +templates/agent_template_backend/.env.example +``` + +is a configuration reference. + +It must not be used as a place to store real credentials in source control. + +In production: + +- non-sensitive configuration may come from ConfigMaps; +- secrets should come from Kubernetes Secrets, OCI Vault, or an equivalent enterprise solution; +- credentials must not be committed. + +--- + +## 31. Using the template for new projects + +Recommended flow: + +```text +agent_framework_oci + | + +--> libs/agent_framework + | + +--> templates/agent_template_backend + | + v + new agent project +``` + +Steps: + +1. select the `agent_framework` version; +2. derive `agent_template_backend`; +3. rename the project; +4. adjust `agents.yaml`; +5. adjust `routing.yaml`; +6. adjust tools and tool policies; +7. create or modify workflows; +8. configure identity; +9. configure MCP; +10. configure guardrails and judges; +11. configure persistence and memory; +12. test locally; +13. run regression tests; +14. build the image; +15. deploy to staging; +16. promote to production according to the enterprise pipeline. + +--- + +## 32. The template must not create a framework fork + +Generic and reusable changes should be evaluated for implementation under: + +```text +libs/agent_framework +``` + +Domain-specific changes should remain in the derived project. + +Example: + +```text +"new billing-specific rule" + -> agent project + +"new generic pause/resume mechanism" + -> agent_framework +``` + +This principle reduces divergence between projects. + +--- + +## 33. Version compatibility + +A derived project should record: + +```text +AGENT_APPLICATION_VERSION +AGENT_FRAMEWORK_VERSION +GIT_COMMIT +BUILD_ID +``` + +Example: + +```text +agent-backoffice: 3.2.0 +agent-framework: 1.8.0 +``` + +A framework upgrade should trigger the project's regression suite. + +--- + +## 34. Role of `Tuning-Performance` + +The repository contains scenarios and variants under: + +```text +Tuning-Performance/ +``` + +These materials are references for features, tuning scenarios, regression, and implementation examples. + +They do not change the basic architectural relationship: + +```text +agent project -> agent_framework +``` + +A production project should incorporate only the configurations or implementations required and validated for its use case. + +--- + +## 35. Role of the template's internal documentation + +The directory: + +```text +templates/agent_template_backend/docs/ +``` + +contains detailed documentation for template-specific functionality, including topics such as: + +- memory; +- observability; +- guardrails; +- IC/NOC/GRL; +- transactions; +- routing; +- channel input; +- output supervisor. + +This document does not replace those functional manuals. + +It defines the **usage, distribution, and deployment model for the template**. + +--- + +## 36. Mandatory requirements for correct usage + +1. The template must consume `agent_framework` through Python imports. + +2. The framework must remain under: + +```text +agent_framework_oci/libs/agent_framework +``` + +as its source location in the monorepo. + +3. The template must remain under: + +```text +agent_framework_oci/templates/agent_template_backend +``` + +as its source location in the monorepo. + +4. In an independent distribution, both should be materialized at the same level: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +5. A derived production project must follow the general architectural specification. + +6. The framework must not be duplicated inside the project. + +7. MCP Servers must not be confused with the backend. + +8. The MCP Gateway is not part of the template directory. + +9. The frontend is not part of the template directory. + +10. SQLite settings present in examples must not be treated as the production default for multi-Pod environments. + +11. The backend must expose a health check. + +12. Real secrets must not be stored in `.env.example` or committed manifests. + +13. The framework version used by the project must be traceable. + +14. Generic changes should preferably be implemented in the framework, avoiding project-specific framework forks. + +--- + +## 37. Final reference structure + +### Inside the `agent_framework_oci` product + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +│ ├── agent_gateway/ +│ ├── channel_gateway/ +│ ├── mcp_gateway/ +│ └── agent_frontend/ +│ +├── mcp/ +│ └── servers/ +│ +└── deploy/ + └── oke/ +``` + +### Derived production project + +```text +workspace/ +├── agent_framework/ +│ +└── agent_backoffice/ + ├── app/ + ├── config/ + ├── workflows/ + ├── data/ + ├── scripts/ + ├── requirements.txt + ├── Dockerfile + └── .env.example +``` + +--- + +## 38. Final rule + +`agent_template_backend` is a **reference implementation of the standard**, not the framework itself. + +The correct relationship is: + +```text +agent_framework + ^ + | +agent_template_backend + | + v +derived projects +``` + +For development inside the monorepo, the original `libs/` and `templates/` paths may be used. + +For distribution and production, derived projects must follow the **General `agent_framework_oci` Usage Specification**, keeping the agent project and `agent_framework` as separate, independently versioned components. diff --git a/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md b/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md new file mode 100644 index 00000000..303b146e --- /dev/null +++ b/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md @@ -0,0 +1,1134 @@ +# Especificação de Utilização do `agent_framework_oci` + +## 1. Objetivo + +Esta especificação define o padrão para desenvolvimento, empacotamento e deployment de projetos de agentes baseados no `agent_framework_oci`. + +O objetivo principal é estabelecer uma separação clara entre: + +- o **framework corporativo compartilhado**, responsável pelas capacidades genéricas da plataforma; +- os **projetos de agentes**, responsáveis pelas regras, configurações, workflows e implementações específicas de cada domínio. + +O `agent_framework` não deve ser incorporado ou duplicado dentro de cada projeto de agente. + +A biblioteca deve ser disponibilizada separadamente e utilizada pelos projetos por meio de imports Python. + +--- + +## 2. Origem do framework + +No repositório oficial `agent_framework_oci`, a implementação compartilhada encontra-se em: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Essa pasta corresponde ao pacote Python distribuível do framework. + +Sua estrutura contém, entre outros componentes: + +```text +agent_framework/ +├── pyproject.toml +├── src/ +│ └── agent_framework/ +│ ├── analytics/ +│ ├── cache/ +│ ├── channels/ +│ ├── guardrails/ +│ ├── memory/ +│ ├── observability/ +│ ├── routing/ +│ ├── supervisor/ +│ ├── workflows/ +│ └── ... +``` + +O `pyproject.toml` define o pacote: + +```toml +[project] +name = "agent-framework" +version = "0.1.0" +``` + +e utiliza: + +```toml +[tool.setuptools.packages.find] +where = ["src"] +``` + +Portanto, a pasta: + +```text +agent_framework_oci/libs/agent_framework +``` + +deve ser tratada como uma biblioteca Python independente e reutilizável. + +--- + +## 3. Estrutura obrigatória para projetos de agentes + +Para utilização do framework fora do repositório principal, a pasta: + +```text +agent_framework_oci/libs/agent_framework +``` + +deve ser disponibilizada juntamente com o projeto de agente. + +O projeto de agente e o framework devem ficar **no mesmo nível de diretório**. + +Exemplo: + +```text +workspace/ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +└── backoffice/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +Para outro agente: + +```text +workspace/ +├── agent_framework/ +│ +└── agent_contas/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +A regra arquitetural é: + +```text +/ +├── agent_framework/ +└── / +``` + +e não: + +```text +/ +└── agent_framework/ +``` + +O projeto do agente não deve possuir uma cópia privada do framework. + +--- + +## 4. Responsabilidades + +### 4.1 `agent_framework` + +O `agent_framework` contém capacidades genéricas e reutilizáveis da plataforma, como: + +- routing; +- supervisor; +- multi-agent; +- workflows; +- estados conversacionais; +- coleta de parâmetros; +- pause/resume; +- pending actions; +- multi-intent; +- intent shift; +- route stickiness; +- transações determinísticas; +- guardrails; +- judges; +- output supervisor; +- RAG; +- memória; +- cache; +- observabilidade; +- telemetria; +- integração MCP; +- persistência; +- analytics; +- integração com provedores OCI; +- mecanismos de resiliência; +- idempotência; +- tratamento de erros. + +Esses componentes pertencem ao framework e não devem ser copiados para os projetos consumidores. + +### 4.2 Projeto do agente + +Cada projeto de agente contém somente a implementação e configuração específicas do domínio. + +Exemplo: + +```text +backoffice/ +├── app/ +│ ├── agents/ +│ ├── workflows/ +│ ├── observability/ +│ ├── workflow_actions/ +│ ├── presentation/ +│ ├── state.py +│ └── main.py +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── prompt_policy.yaml +│ ├── identity.yaml +│ └── mcp_parameter_mapping.yaml +│ +├── data/ +├── requirements.txt +├── Dockerfile +└── .env +``` + +O projeto pode utilizar qualquer funcionalidade pública do framework por meio de imports. + +Exemplo: + +```python +from agent_framework.routing import ... +``` + +ou: + +```python +from agent_framework.supervisor import ... +``` + +ou: + +```python +from agent_framework.observability import ... +``` + +O código do projeto não deve depender de caminhos internos do repositório `agent_framework_oci`. + +--- + +## 5. Instalação local para desenvolvimento + +Considerando: + +```text +workspace/ +├── agent_framework/ +└── backoffice/ +``` + +a instalação recomendada é: + +```bash +cd workspace + +python -m venv .venv + +source .venv/bin/activate +``` + +Em Windows: + +```powershell +.venv\Scripts\activate +``` + +Instalar o framework: + +```bash +pip install -e ./agent_framework +``` + +Instalar as dependências do projeto: + +```bash +pip install -r ./backoffice/requirements.txt +``` + +O modo: + +```bash +pip install -e +``` + +é recomendado para desenvolvimento porque mantém o código do framework referenciado diretamente pelo ambiente Python. + +Alterações no framework tornam-se imediatamente disponíveis para o projeto sem reinstalação completa do pacote. + +--- + +## 6. Validação obrigatória + +Após instalar o framework, o seguinte comando deve executar sem erro: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +Também deve ser possível testar imports utilizados pelo projeto: + +```bash +python -c "from agent_framework import *" +``` + +ou imports específicos conforme a implementação do agente. + +Uma falha como: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indica que o framework não foi disponibilizado ou instalado corretamente. + +--- + +## 7. Modelo para CI/CD + +Em um pipeline de CI/CD, deve ser criado um workspace contendo os dois componentes. + +Exemplo: + +```text +build/ +├── agent_framework/ +└── backoffice/ +``` + +O framework pode ser obtido diretamente do repositório `agent_framework_oci`: + +```text +agent_framework_oci/libs/agent_framework +``` + +e copiado para: + +```text +build/agent_framework +``` + +O projeto específico do agente deve ser copiado para: + +```text +build/backoffice +``` + +O build da imagem Docker deve utilizar: + +```text +build/ +``` + +como contexto. + +Exemplo: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +Esse detalhe é importante. + +O contexto deve ser: + +```text +. +``` + +representando o diretório que contém simultaneamente: + +```text +agent_framework/ +backoffice/ +``` + +--- + +## 8. Dockerfile padrão + +Um projeto de agente deve adotar como base um Dockerfile semelhante ao seguinte: + +```dockerfile +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_NO_CACHE_DIR=1 + +WORKDIR /workspace + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +# Framework compartilhado +COPY agent_framework /workspace/agent_framework + +# Projeto específico do agente +COPY backoffice /workspace/backoffice + +# Instala primeiro o framework corporativo +RUN pip install --upgrade pip \ + && pip install /workspace/agent_framework + +# Instala as dependências específicas do agente +RUN pip install -r /workspace/backoffice/requirements.txt + +WORKDIR /workspace/backoffice + +EXPOSE 8000 + +CMD [ + "uvicorn", + "app.main:app", + "--host", + "0.0.0.0", + "--port", + "8000" +] +``` + +Para ambiente de desenvolvimento, opcionalmente pode ser utilizado: + +```dockerfile +RUN pip install -e /workspace/agent_framework +``` + +Para imagens imutáveis de produção, recomenda-se: + +```dockerfile +RUN pip install /workspace/agent_framework +``` + +Dessa forma a versão do framework utilizada pela imagem fica congelada no momento do build. + +--- + +## 9. Estrutura dentro do container + +O container deve manter conceitualmente a mesma separação utilizada no desenvolvimento: + +```text +/workspace/ +├── agent_framework/ +└── backoffice/ +``` + +A aplicação será executada a partir de: + +```text +/workspace/backoffice +``` + +O framework estará instalado no ambiente Python e poderá ser importado normalmente: + +```python +import agent_framework +``` + +Não deve ser necessário utilizar: + +```python +sys.path.append(...) +``` + +nem alterar dinamicamente o `PYTHONPATH` no código da aplicação. + +--- + +## 10. Versionamento do framework + +A versão do framework utilizada pelo agente deve ser determinada durante o build. + +É recomendado registrar, no mínimo: + +```text +AGENT_FRAMEWORK_VERSION +AGENT_APPLICATION_VERSION +BUILD_ID +GIT_COMMIT +``` + +Exemplo: + +```yaml +env: + - name: AGENT_FRAMEWORK_VERSION + value: "1.5.0" + + - name: AGENT_APPLICATION_VERSION + value: "2.1.4" +``` + +Isso permite identificar exatamente qual combinação: + +```text +Framework + Agente +``` + +está executando em cada ambiente. + +--- + +## 11. Publicação da imagem + +Após o build: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +a imagem deve ser publicada no OCI Container Registry — OCIR. + +Exemplo: + +```bash +docker tag \ + backoffice:1.0.0 \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +Depois: + +```bash +docker push \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +--- + +## 12. Deployment no OCI OKE + +Cada projeto de agente deve possuir seu próprio Kubernetes `Deployment`. + +O framework não precisa ser executado como Pod independente. + +O `agent_framework` é uma biblioteca e estará incorporado à imagem do projeto do agente durante o build. + +A arquitetura é: + +```text + OCI OKE + | + v + Kubernetes Service + | + +------------+------------+ + | | | + v v v + Agent Pod Agent Pod Agent Pod + | | | + +------------+------------+ + | + agent_framework + instalado em cada imagem +``` + +O código do framework é compartilhado em termos de origem e versionamento, mas estará instalado dentro de cada imagem de agente. + +--- + +## 13. Manifesto YAML base para OKE + +O manifesto abaixo pode ser utilizado como base para deployment de um projeto como `backoffice`. + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: agent-platform + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: backoffice-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENT_FRAMEWORK_VERSION: "1.0.0" + + AGENT_ID: "backoffice" + AGENT_APPLICATION_VERSION: "1.0.0" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + ENABLE_LANGFUSE: "true" + ENABLE_OTEL: "true" + ENABLE_ANALYTICS: "true" + +--- +apiVersion: v1 +kind: Secret +metadata: + name: backoffice-secrets + namespace: agent-platform +type: Opaque +stringData: + OCI_COMPARTMENT_ID: "" + OCI_GENAI_API_KEY: "" + LANGFUSE_PUBLIC_KEY: "" + LANGFUSE_SECRET_KEY: "" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backoffice + namespace: agent-platform + labels: + app: backoffice +spec: + replicas: 3 + + selector: + matchLabels: + app: backoffice + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: backoffice + + spec: + containers: + - name: backoffice + image: gru.ocir.io//agents/backoffice:1.0.0 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8000 + protocol: TCP + + envFrom: + - configMapRef: + name: backoffice-config + - secretRef: + name: backoffice-secrets + + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + + securityContext: + allowPrivilegeEscalation: false + +--- +apiVersion: v1 +kind: Service +metadata: + name: backoffice + namespace: agent-platform +spec: + type: ClusterIP + selector: + app: backoffice + ports: + - name: http + protocol: TCP + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: backoffice + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: backoffice + + minReplicas: 3 + maxReplicas: 10 + + behavior: + scaleUp: + stabilizationWindowSeconds: 0 + policies: + - type: Percent + value: 100 + periodSeconds: 60 + - type: Pods + value: 4 + periodSeconds: 60 + selectPolicy: Max + + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 25 + periodSeconds: 60 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 75 + +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: backoffice + namespace: agent-platform +spec: + minAvailable: 2 + selector: + matchLabels: + app: backoffice +``` + +--- + +## 14. Aplicação do manifesto + +O manifesto pode ser armazenado como: + +```text +deploy/oke/backoffice.yaml +``` + +e aplicado com: + +```bash +kubectl apply -f deploy/oke/backoffice.yaml +``` + +Validação: + +```bash +kubectl -n agent-platform get pods +kubectl -n agent-platform get deployment +kubectl -n agent-platform get svc +kubectl -n agent-platform get hpa +``` + +Para acompanhar o rollout: + +```bash +kubectl -n agent-platform rollout status deployment/backoffice +``` + +--- + +## 15. Health check + +Todo projeto de agente destinado ao OKE deve disponibilizar: + +```http +GET /health +``` + +com retorno: + +```text +200 OK +``` + +enquanto estiver apto a receber novas requisições. + +Esse endpoint é utilizado pelas probes: + +```text +startupProbe +readinessProbe +livenessProbe +``` + +--- + +## 16. Persistência e múltiplas réplicas + +Aplicações em OKE devem considerar que múltiplos Pods podem executar simultaneamente. + +Portanto, dados conversacionais e transacionais que precisem sobreviver entre requisições não devem depender exclusivamente do filesystem do container. + +Não é recomendado utilizar SQLite local como repositório definitivo para ambientes OKE com múltiplas réplicas. + +Para produção devem ser utilizados providers externos adequados para: + +- sessions; +- checkpoints; +- memória; +- cache; +- idempotência; +- locks; +- analytics; +- estado transacional. + +Dependendo da configuração do framework, podem ser utilizados, por exemplo: + +```text +Oracle Autonomous Database +MongoDB +Redis +OCI Streaming +OCI Object Storage +``` + +--- + +## 17. Atualização do framework + +Um projeto de agente não deve receber alterações no framework copiando arquivos individualmente para seu código. + +A atualização deve acontecer substituindo a versão disponibilizada de: + +```text +agent_framework/ +``` + +e reconstruindo a imagem. + +Processo: + +```text +Nova versão agent_framework + | + v +Atualização do workspace CI/CD + | + v +Build da imagem do agente + | + v +Testes + | + v +Push OCIR + | + v +Rolling Update OKE +``` + +Esse modelo mantém clara a separação entre: + +```text +produto/framework +``` + +e: + +```text +implementação do agente +``` + +--- + +## 18. Regra de compatibilidade + +Cada release de projeto deverá declarar explicitamente a versão do framework contra a qual foi testada. + +Exemplo: + +```text +Backoffice 3.2.0 +Agent Framework 1.8.0 +``` + +Uma nova versão do framework não deve ser promovida diretamente para produção sem que sejam executados os testes de regressão do projeto consumidor. + +Pipeline recomendado: + +```text +Framework + | + v +Build Agent + | + v +Unit Tests + | + v +Integration Tests + | + v +Workflow Regression + | + v +Smoke Test + | + v +Homologação + | + v +Produção +``` + +--- + +## 19. Vários projetos utilizando o mesmo framework + +A estrutura pode atender simultaneamente vários projetos: + +```text +workspace/ +├── agent_framework/ +├── backoffice/ +├── contas/ +├── ofertas/ +├── atendimento/ +└── suporte/ +``` + +Todos utilizam: + +```text +agent_framework/ +``` + +como origem comum da plataforma. + +Cada aplicação, entretanto, gera sua própria imagem: + +```text +backoffice:3.0.0 +contas:5.2.0 +ofertas:1.4.0 +atendimento:2.6.0 +``` + +Todas podem ter sido construídas com: + +```text +agent-framework:1.8.0 +``` + +Isso permite evoluir projetos de forma independente sem realizar forks do framework. + +--- + +## 20. Princípio arquitetural + +A relação entre os componentes deve permanecer: + +```text + agent_framework + | + +---------------+---------------+ + | | | + v v v + Backoffice Contas Ofertas + | | | + v v v + imagem OCI imagem OCI imagem OCI + | | | + +---------------+---------------+ + | + v + OKE +``` + +O `agent_framework` é a plataforma tecnológica comum. + +Os projetos de agentes são consumidores dessa plataforma. + +Consequentemente: + +**o projeto do agente depende do framework; o framework não deve depender do projeto do agente.** + +--- + +## 21. Requisitos obrigatórios + +Para que um projeto seja considerado compatível com o `agent_framework_oci`, devem ser atendidos os seguintes requisitos: + +1. `agent_framework` deve ser obtido de: + +```text +agent_framework_oci/libs/agent_framework +``` + +2. O framework deve ser disponibilizado separadamente do projeto do agente. + +3. A estrutura de distribuição deve ser: + +```text +/ +├── agent_framework/ +└── / +``` + +4. O framework não deve ser duplicado dentro do projeto. + +5. A biblioteca deve ser instalada pelo mecanismo padrão do Python: + +```bash +pip install ./agent_framework +``` + +ou, em desenvolvimento: + +```bash +pip install -e ./agent_framework +``` + +6. Imports devem utilizar: + +```python +import agent_framework +``` + +e nunca caminhos físicos do repositório. + +7. O contexto Docker deve permitir acesso simultâneo a: + +```text +agent_framework/ +``` + +e: + +```text +/ +``` + +8. Cada imagem deve conter uma versão conhecida do framework. + +9. O deployment OKE deve possuir probes de saúde. + +10. Aplicações escaláveis devem externalizar estado compartilhado. + +11. Secrets não devem ser mantidos no código nem no manifesto versionado. + +12. Cada projeto deve possuir Deployment e Service próprios. + +13. A escala horizontal deve acontecer no projeto do agente, e não através de uma instância central executável do `agent_framework`. + +--- + +## 22. Estrutura recomendada final + +A estrutura de referência para desenvolvimento e CI/CD é: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +├── backoffice/ +│ ├── app/ +│ ├── config/ +│ ├── data/ +│ ├── requirements.txt +│ ├── Dockerfile +│ └── .env.example +│ +└── deploy/ + └── oke/ + └── backoffice.yaml +``` + +Para novos projetos, somente a pasta específica muda: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ +├── novo_agente/ +│ +└── deploy/ + └── oke/ + └── novo_agente.yaml +``` + +Esse deve ser o padrão de distribuição e deployment dos projetos construídos sobre o `agent_framework_oci`. diff --git a/deploy/docs/pt/agent_template_backend_especificacao_pt.md b/deploy/docs/pt/agent_template_backend_especificacao_pt.md new file mode 100644 index 00000000..652dec68 --- /dev/null +++ b/deploy/docs/pt/agent_template_backend_especificacao_pt.md @@ -0,0 +1,1364 @@ +# Especificação de Uso do `agent_template_backend` + +## Documento complementar à arquitetura geral do `agent_framework_oci` + +> **Escopo deste documento** +> +> Este documento é específico para o `agent_template_backend` distribuído com o `agent_framework_oci`. +> +> Ele **não substitui** a especificação arquitetural geral de utilização do `agent_framework_oci` para projetos de produção. A especificação geral continua sendo a referência normativa para estrutura de projetos produtivos, empacotamento, versionamento, CI/CD, externalização de estado, segurança, escalabilidade e deployment em OKE. +> +> Este documento explica como o `agent_template_backend` existente no repositório deve ser utilizado como **template de referência**, ambiente de desenvolvimento, base para novos agentes e origem para projetos produtivos derivados. + +--- + +## 1. Objetivo + +O `agent_template_backend` é o template de backend de agentes fornecido pelo repositório `agent_framework_oci`. + +Sua finalidade é oferecer uma implementação inicial funcional que demonstre como um projeto de agente deve consumir o `agent_framework`, organizar configurações, expor APIs, utilizar workflows, integrar MCP, aplicar guardrails e judges e utilizar os demais serviços corporativos do framework. + +O template deve ser entendido como: + +- referência de implementação; +- ponto de partida para novos projetos; +- ambiente de desenvolvimento e validação; +- exemplo de integração com o `agent_framework`; +- exemplo de deployment; +- base para geração de projetos independentes. + +O template **não deve ser interpretado como uma exceção** às regras arquiteturais definidas na especificação geral de produção. + +--- + +## 2. Relação com a especificação arquitetural geral + +Existem dois níveis de documentação. + +### 2.1. Especificação arquitetural geral + +A especificação geral define o padrão para projetos produtivos derivados do framework: + +```text +/ +├── agent_framework/ +└── / +``` + +Ela define, entre outros pontos: + +- separação entre framework e projeto; +- instalação do `agent_framework`; +- versionamento; +- contexto de build; +- Docker; +- CI/CD; +- deployment em OKE; +- escalabilidade; +- externalização de estado; +- gerenciamento de secrets; +- compatibilidade entre versão do framework e versão do agente. + +### 2.2. Esta especificação + +Este documento descreve como o template entregue no repositório se encaixa nesse modelo. + +Quando existir diferença entre uma configuração demonstrativa do template e uma regra da especificação arquitetural geral, **a especificação arquitetural geral prevalece para ambientes de produção**. + +--- + +## 3. Localização do template no repositório + +No repositório fonte, o template está localizado em: + +```text +agent_framework_oci/ +└── templates/ + └── agent_template_backend/ +``` + +O framework compartilhado está localizado em: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Portanto, dentro do repositório de desenvolvimento, a estrutura é: + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +├── mcp/ +├── deploy/ +└── ... +``` + +Essa organização é a **estrutura fonte do produto `agent_framework_oci`**. + +Ela não altera a regra da especificação arquitetural de produção. + +--- + +## 4. Estrutura de distribuição do template + +Quando o `agent_template_backend` for extraído do repositório para uso independente, build isolado ou criação de um projeto derivado, o workspace deve materializar: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +O conteúdo de: + +```text +agent_framework_oci/libs/agent_framework/ +``` + +deve ser disponibilizado como: + +```text +workspace/agent_framework/ +``` + +e o conteúdo de: + +```text +agent_framework_oci/templates/agent_template_backend/ +``` + +como: + +```text +workspace/agent_template_backend/ +``` + +Essa estrutura é compatível com o Dockerfile existente no template. + +--- + +## 5. Estrutura atual do `agent_template_backend` + +A estrutura principal do template é: + +```text +agent_template_backend/ +├── app/ +│ ├── main.py +│ ├── state.py +│ ├── mcp_gateway_client_factory.py +│ └── ... +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── identity.yaml +│ ├── prompt_policy.yaml +│ ├── mcp_parameter_mapping.yaml +│ ├── mcp_servers.yaml +│ └── mcp_servers.docker.yaml +│ +├── workflows/ +│ ├── devolucao_pedido.active.yaml +│ └── devolucao_pedido.v1.yaml +│ +├── data/ +├── docs/ +├── scripts/ +├── llm_profiles.yaml +├── requirements.txt +├── Dockerfile +├── .env.example +├── README.md +└── README_ENTERPRISE_TEMPLATE.md +``` + +O template contém configuração suficiente para demonstrar as capacidades corporativas do framework sem que essas capacidades precisem ser copiadas para o projeto. + +--- + +## 6. Dependência do `agent_framework` + +O `agent_template_backend` é um consumidor do framework. + +A dependência deve permanecer unidirecional: + +```text +agent_template_backend + | + v +agent_framework +``` + +O inverso não deve ocorrer. + +O `agent_framework` não deve depender de: + +```text +templates/agent_template_backend +``` + +nem de arquivos específicos do template. + +--- + +## 7. Instalação local no repositório fonte + +Para desenvolvimento diretamente dentro do repositório: + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +o framework pode ser instalado em modo editável. + +Exemplo, a partir de `templates/agent_template_backend`: + +```bash +pip install -e ../../libs/agent_framework +pip install -r requirements.txt +``` + +Ou, a partir do root: + +```bash +pip install -e ./libs/agent_framework +pip install -r ./templates/agent_template_backend/requirements.txt +``` + +O modo editável é adequado para desenvolvimento do produto, pois alterações realizadas em `libs/agent_framework` são imediatamente utilizadas pelo template. + +--- + +## 8. Instalação em workspace independente + +Quando o template for disponibilizado segundo a estrutura arquitetural geral: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +utilize: + +```bash +pip install -e ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +Para um build de produção: + +```bash +pip install ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +--- + +## 9. Validação de instalação + +A instalação deve permitir: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +e, a partir do projeto: + +```bash +python -c "import app.main" +``` + +Uma falha: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indica erro na disponibilização ou instalação da biblioteca. + +--- + +## 10. Execução do backend + +O backend do template expõe a aplicação FastAPI em: + +```text +8000 +``` + +Execução local: + +```bash +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` + +Health check: + +```bash +curl http://localhost:8000/health +``` + +Outras verificações úteis: + +```bash +curl http://localhost:8000/agents +curl http://localhost:8000/debug/mcp/tools +``` + +A porta `8000` pertence ao backend do agente e não deve ser confundida com as portas dos MCP Servers ou gateways. + +--- + +## 11. Configuração do agente + +Os comportamentos específicos do template são definidos principalmente por arquivos externos ao framework. + +### `config/agents.yaml` + +Define os agentes disponíveis e seus perfis. + +### `config/routing.yaml` + +Define intents, rotas e comportamento de roteamento. + +### `config/tools.yaml` + +Define as tools conhecidas pelo projeto e seus vínculos com MCP. + +### `config/tool_policies.yaml` + +Define políticas de execução, confirmação, controle e requisitos transacionais das tools. + +### `config/guardrails.yaml` + +Configura guardrails aplicados pelo projeto. + +### `config/judges.yaml` + +Configura judges. + +### `config/identity.yaml` + +Define identidade e business keys. + +### `config/mcp_parameter_mapping.yaml` + +Define mapeamento e extração de parâmetros destinados às tools MCP. + +### `config/mcp_servers.yaml` + +Define MCP Servers utilizados em execução local. + +### `config/mcp_servers.docker.yaml` + +Define endpoints adequados ao ambiente Docker. + +### `llm_profiles.yaml` + +Define perfis e providers LLM utilizados pelo projeto. + +Esse modelo preserva a separação entre: + +```text +motor genérico = agent_framework +``` + +e: + +```text +regras do domínio = agent_template_backend/config + app +``` + +--- + +## 12. MCP Servers de exemplo + +O repositório inclui MCP Servers de desenvolvimento em: + +```text +agent_framework_oci/mcp/servers/ +├── telecom_mcp_server/ +├── retail_mcp_server/ +└── mock_telecom_mcp/ +``` + +Os exemplos principais utilizam: + +```text +Telecom MCP : 8100 +Retail MCP : 8200 +``` + +A configuração local do template referencia: + +```yaml +servers: + telecom: + endpoint: http://localhost:8100/mcp + + retail: + endpoint: http://localhost:8200/mcp +``` + +Esses MCP Servers são componentes separados do backend do agente. + +--- + +## 13. Inicialização dos MCP Servers no ambiente de desenvolvimento + +O repositório fornece: + +```text +scripts/run_mcp_servers.sh +``` + +que inicia os MCP Servers de exemplo. + +Arquitetura local: + +```text + +------------------+ + | Telecom MCP | + | :8100 | + +---------+--------+ + | + | ++----------------------+ | +| agent_template |---------+ +| backend :8000 | ++----------------------+---------+ + | + | + +---------v--------+ + | Retail MCP | + | :8200 | + +------------------+ +``` + +Esse script é uma conveniência de desenvolvimento. + +Em produção, a implantação dos MCP Servers deve respeitar a arquitetura produtiva do projeto e não depender de processos iniciados por shell dentro do Pod do backend. + +--- + +## 14. MCP Gateway + +O `agent_framework_oci` também contém: + +```text +apps/mcp_gateway/ +``` + +e configuração OKE para o MCP Gateway. + +A porta padrão apresentada no repositório é: + +```text +8300 +``` + +O template possui suporte a: + +```env +MCP_GATEWAY_ENABLED=true +MCP_GATEWAY_URL=http://localhost:8300 +``` + +ou, em OKE: + +```text +http://mcp-gateway.agent-platform.svc.cluster.local:8300 +``` + +O MCP Gateway é um componente da plataforma e não faz parte fisicamente da pasta `agent_template_backend`. + +--- + +## 15. Agent Gateway, Channel Gateway e Frontend + +O repositório também contém: + +```text +apps/ +├── agent_gateway/ +├── channel_gateway/ +├── mcp_gateway/ +└── agent_frontend/ +``` + +Esses componentes devem ser entendidos como componentes de plataforma ou canais adicionais. + +O `agent_template_backend` pode operar diretamente na porta `8000` sem que todos esses componentes sejam obrigatórios para um teste local simples. + +Em uma topologia corporativa completa, o fluxo pode ser: + +```text +Client / Frontend + | + v +Channel Gateway + | + v +Agent Gateway + | + v +agent_template_backend + | + v +MCP Gateway + | + v +MCP Servers +``` + +A composição concreta deve seguir os requisitos do ambiente de destino. + +--- + +## 16. Frontend de desenvolvimento + +O frontend de referência está em: + +```text +agent_framework_oci/apps/agent_frontend/ +``` + +e aponta, por padrão, para: + +```text +http://localhost:8000 +``` + +O frontend não deve ser movido para dentro do `agent_template_backend` apenas para satisfazer uma estrutura de projeto. + +Ele é um componente separado. + +Um projeto produtivo poderá: + +- utilizar esse frontend; +- utilizar outro frontend; +- integrar Web/WhatsApp/Voice; +- utilizar apenas APIs; +- utilizar Agent Gateway/Channel Gateway. + +--- + +## 17. Dockerfile do template + +O Dockerfile existente em: + +```text +templates/agent_template_backend/Dockerfile +``` + +pressupõe um contexto que contenha simultaneamente: + +```text +agent_framework/ +agent_template_backend/ +``` + +Estrutura: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +Build: + +```bash +docker build \ + -t agent-template-backend:local \ + -f agent_template_backend/Dockerfile \ + . +``` + +Essa forma está alinhada à especificação arquitetural geral. + +--- + +## 18. Dockerfile OKE existente no repositório + +O repositório também fornece: + +```text +deploy/oke/dockerfiles/Dockerfile.agent-template-backend +``` + +Esse Dockerfile é executado com o root de `agent_framework_oci` como contexto e copia: + +```text +libs/agent_framework -> /opt/agent_framework +templates/agent_template_backend -> /app +``` + +Portanto existem dois contextos válidos, para objetivos diferentes. + +### Desenvolvimento/distribuição independente + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +### Build a partir do monorepo + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +Ambos materializam a mesma dependência lógica: + +```text +agent_template_backend -> agent_framework +``` + +--- + +## 19. Derivação de um projeto produtivo + +O `agent_template_backend` deve servir como origem para um novo projeto. + +Exemplo: + +```text +agent_template_backend + | + | derivação + v +agent_backoffice +``` + +Após a derivação, a estrutura produtiva recomendada é: + +```text +workspace/ +├── agent_framework/ +└── agent_backoffice/ +``` + +Não é necessário manter o nome: + +```text +agent_template_backend +``` + +em produção. + +O projeto derivado passa a ter: + +- ciclo de versão próprio; +- Docker image própria; +- configuração própria; +- agentes próprios; +- workflows próprios; +- políticas próprias; +- pipeline próprio. + +A biblioteca `agent_framework` permanece compartilhada. + +--- + +## 20. O que deve e o que não deve ser copiado + +### Deve ser derivado para o projeto + +Conforme a necessidade do domínio: + +```text +app/ +config/ +workflows/ +data/ +scripts/ +requirements.txt +Dockerfile +.env.example +``` + +### Não deve ser copiado para dentro do projeto + +```text +libs/agent_framework +``` + +O framework deve continuar sendo distribuído e instalado separadamente. + +Também não é necessário copiar automaticamente: + +```text +apps/agent_gateway +apps/channel_gateway +apps/mcp_gateway +apps/agent_frontend +mcp/servers +``` + +Esses componentes devem ser incluídos na solução apenas quando fizerem parte da topologia necessária. + +--- + +## 21. OKE: relação com a arquitetura geral + +O repositório contém uma implementação OKE de referência em: + +```text +deploy/oke/ +``` + +Incluindo: + +```text +deploy/oke/k8s/base/ +├── 00-namespace.yaml +├── 01-configmap.yaml +├── 02-secret-template.yaml +├── 03-agent-template-backend.yaml +├── 04-agent-gateway.yaml +├── 05-channel-gateway.yaml +├── 06-mcp-gateway.yaml +├── 07-frontend.yaml +└── kustomization.yaml +``` + +Essa implementação demonstra como os componentes podem ser materializados no Kubernetes. + +Para projetos produtivos, devem ser mantidas as regras da especificação arquitetural geral. + +--- + +## 22. Atenção às configurações demonstrativas do OKE + +A configuração de referência presente no repositório utiliza, entre outros valores: + +```text +SESSION_REPOSITORY_PROVIDER=sqlite +MEMORY_REPOSITORY_PROVIDER=sqlite +CHECKPOINT_REPOSITORY_PROVIDER=sqlite +USAGE_REPOSITORY_PROVIDER=sqlite +VECTOR_STORE_PROVIDER=sqlite +GRAPH_STORE_PROVIDER=sqlite +``` + +Esses valores são úteis para demonstração e ambientes controlados. + +**Eles não devem ser interpretados como recomendação arquitetural para produção com múltiplas réplicas.** + +Em produção, a especificação arquitetural geral prevalece: estados compartilhados que precisem sobreviver entre Pods devem utilizar providers externos apropriados. + +--- + +## 23. Deployment base do `agent_template_backend` no OKE + +O exemplo abaixo representa especificamente o backend do template. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + replicas: 3 + + selector: + matchLabels: + app: agent-template-backend + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: agent-template-backend + + spec: + containers: + - name: agent-template-backend + image: .ocir.io//agents/agent-template-backend: + + ports: + - name: http + containerPort: 8000 + + envFrom: + - configMapRef: + name: agent-template-backend-config + - secretRef: + name: agent-template-backend-secrets + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + failureThreshold: 3 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + +--- +apiVersion: v1 +kind: Service +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + type: ClusterIP + + selector: + app: agent-template-backend + + ports: + - name: http + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: agent-template-backend + + minReplicas: 3 + maxReplicas: 10 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +O dimensionamento deve ser ajustado com base nos testes de carga do projeto real. + +--- + +## 24. ConfigMap base específico do template + +Exemplo: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: agent-template-backend-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_PARALLEL_GUARDRAILS: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + FRAMEWORK_CHANNEL_INPUT_MODE: "embedded" +``` + +Providers de persistência, LLM, memória, cache, observabilidade e RAG devem ser configurados de acordo com o ambiente de produção e com a especificação arquitetural geral. + +--- + +## 25. Topologia OKE completa de referência + +Uma instalação completa pode utilizar: + +```text + +----------------------+ + | Agent Frontend | + +----------+-----------+ + | + v + +----------------------+ + | Channel Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Backend | + | :8000 | + +----------+-----------+ + | + v + +----------------------+ + | MCP Gateway | + | :8300 | + +----------+-----------+ + | + +-------------+-------------+ + | | + v v + +------------------+ +------------------+ + | Telecom MCP | | Retail MCP | + | :8100 | | :8200 | + +------------------+ +------------------+ +``` + +Essa é uma topologia de referência, não uma obrigação para todos os projetos. + +--- + +## 26. MCP Servers em OKE + +Os MCP Servers devem ser implantados como componentes próprios quando utilizados em produção. + +Exemplo conceitual: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + replicas: 2 + selector: + matchLabels: + app: telecom-mcp + template: + metadata: + labels: + app: telecom-mcp + spec: + containers: + - name: telecom-mcp + image: .ocir.io//mcp/telecom: + ports: + - containerPort: 8100 + +--- +apiVersion: v1 +kind: Service +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + selector: + app: telecom-mcp + ports: + - port: 8100 + targetPort: 8100 +``` + +A mesma abordagem pode ser utilizada para outros MCP Servers. + +--- + +## 27. Health, readiness e startup + +O backend deve manter: + +```text +GET /health +``` + +como endpoint mínimo para probes. + +Recomenda-se utilizar: + +- `startupProbe` para inicialização; +- `readinessProbe` para admissão de tráfego; +- `livenessProbe` para detectar processo não saudável. + +Uma aplicação não deve depender apenas de `livenessProbe`. + +--- + +## 28. Estado e escalabilidade + +Ao utilizar: + +```yaml +replicas: 3 +``` + +não existe garantia de que requisições consecutivas sejam atendidas pelo mesmo Pod. + +Portanto, em produção: + +```text +session +checkpoint +memory +idempotency +transaction state +cache compartilhado +``` + +devem ser analisados quanto à necessidade de externalização. + +Um banco SQLite armazenado em: + +```text +/data +``` + +dentro de `emptyDir` é local ao Pod e não representa armazenamento compartilhado. + +--- + +## 29. Configuração de MCP local versus container + +O template separa: + +```text +config/mcp_servers.yaml +``` + +e: + +```text +config/mcp_servers.docker.yaml +``` + +Isso é intencional. + +Local: + +```yaml +endpoint: http://localhost:8100/mcp +``` + +Docker/Kubernetes: + +```yaml +endpoint: http://telecom-mcp:8100/mcp +``` + +O endpoint deve refletir o DNS visível a partir do processo do backend. + +--- + +## 30. `.env.example` + +O arquivo: + +```text +templates/agent_template_backend/.env.example +``` + +é uma referência de configuração. + +Ele não deve ser utilizado como mecanismo para armazenar credenciais reais no repositório. + +Em produção: + +- configurações não sensíveis podem vir de ConfigMap; +- secrets devem vir de Kubernetes Secret, OCI Vault ou solução corporativa equivalente; +- credenciais não devem ser commitadas. + +--- + +## 31. Uso do template em novos projetos + +Fluxo recomendado: + +```text +agent_framework_oci + | + +--> libs/agent_framework + | + +--> templates/agent_template_backend + | + v + novo projeto de agente +``` + +Passos: + +1. selecionar a versão do `agent_framework`; +2. derivar o `agent_template_backend`; +3. renomear o projeto; +4. ajustar `agents.yaml`; +5. ajustar `routing.yaml`; +6. ajustar tools e tool policies; +7. criar/ajustar workflows; +8. configurar identidade; +9. configurar MCP; +10. configurar guardrails e judges; +11. configurar persistência e memória; +12. testar localmente; +13. executar regressão; +14. gerar imagem; +15. implantar em ambiente de homologação; +16. promover para produção conforme pipeline corporativo. + +--- + +## 32. O template não deve criar fork do framework + +Alterações genéricas e reutilizáveis devem ser avaliadas para implementação em: + +```text +libs/agent_framework +``` + +Alterações específicas de domínio devem permanecer no projeto derivado. + +Exemplo: + +```text +"nova regra específica de cobrança" + -> projeto do agente + +"novo mecanismo genérico de pause/resume" + -> agent_framework +``` + +Esse princípio reduz divergência entre projetos. + +--- + +## 33. Compatibilidade de versões + +Um projeto derivado deve registrar: + +```text +AGENT_APPLICATION_VERSION +AGENT_FRAMEWORK_VERSION +GIT_COMMIT +BUILD_ID +``` + +Exemplo: + +```text +agent-backoffice: 3.2.0 +agent-framework: 1.8.0 +``` + +A atualização do framework deve acionar os testes de regressão do projeto. + +--- + +## 34. Papel da pasta `Tuning-Performance` + +O repositório possui cenários e variantes em: + +```text +Tuning-Performance/ +``` + +Esses materiais servem como referência para features, cenários de tuning, regressão e exemplos de implementação. + +Eles não alteram a estrutura arquitetural básica: + +```text +agent project -> agent_framework +``` + +Um projeto produtivo deve incorporar apenas as configurações ou implementações necessárias e validadas para seu caso. + +--- + +## 35. Papel da documentação interna do template + +A pasta: + +```text +templates/agent_template_backend/docs/ +``` + +contém documentação detalhada sobre funcionalidades específicas do template, incluindo temas como: + +- memória; +- observabilidade; +- guardrails; +- IC/NOC/GRL; +- transações; +- routing; +- channel input; +- output supervisor. + +Este documento não substitui esses manuais funcionais. + +Ele define o **modelo de utilização, distribuição e deployment do template**. + +--- + +## 36. Requisitos obrigatórios para uso correto + +1. O template deve consumir `agent_framework` por import Python. + +2. O framework deve continuar em: + +```text +agent_framework_oci/libs/agent_framework +``` + +como origem no monorepo. + +3. O template deve continuar em: + +```text +agent_framework_oci/templates/agent_template_backend +``` + +como origem no monorepo. + +4. Em uma distribuição independente, ambos devem ser materializados no mesmo nível: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +5. Um projeto produtivo derivado deve respeitar a especificação arquitetural geral. + +6. O framework não deve ser duplicado dentro do projeto. + +7. MCP Servers não devem ser confundidos com o backend. + +8. O MCP Gateway não faz parte da pasta do template. + +9. O frontend não faz parte da pasta do template. + +10. Configurações SQLite existentes nos exemplos não devem ser consideradas padrão produtivo para ambientes multi-Pod. + +11. O backend deve expor health check. + +12. Secrets reais não devem ser armazenados em `.env.example` ou manifestos versionados. + +13. A versão do framework utilizada pelo projeto deve ser rastreável. + +14. Mudanças genéricas devem preferencialmente ser implementadas no framework, evitando forks por projeto. + +--- + +## 37. Estrutura de referência final + +### Dentro do produto `agent_framework_oci` + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +│ ├── agent_gateway/ +│ ├── channel_gateway/ +│ ├── mcp_gateway/ +│ └── agent_frontend/ +│ +├── mcp/ +│ └── servers/ +│ +└── deploy/ + └── oke/ +``` + +### Projeto derivado para produção + +```text +workspace/ +├── agent_framework/ +│ +└── agent_backoffice/ + ├── app/ + ├── config/ + ├── workflows/ + ├── data/ + ├── scripts/ + ├── requirements.txt + ├── Dockerfile + └── .env.example +``` + +--- + +## 38. Regra final + +O `agent_template_backend` é uma **implementação de referência do padrão**, não o próprio framework. + +A relação correta é: + +```text +agent_framework + ^ + | +agent_template_backend + | + v +projetos derivados +``` + +Para desenvolvimento dentro do monorepo, os caminhos originais em `libs/` e `templates/` podem ser utilizados. + +Para distribuição e produção, os projetos derivados devem seguir a **Especificação Geral de Utilização do `agent_framework_oci`**, mantendo o projeto de agente e o `agent_framework` como componentes separados e versionáveis. + diff --git a/deploy/oke/load-test/scripts/__pycache__/prepare_env.cpython-313.pyc b/deploy/oke/load-test/scripts/__pycache__/prepare_env.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c77aeb7e5f6afce40e1927be562514b4a41c32ab GIT binary patch literal 9393 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 literal 0 HcmV?d00001 diff --git a/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py b/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py new file mode 100644 index 00000000..613167e8 --- /dev/null +++ b/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py @@ -0,0 +1,201 @@ +"""Deterministic, framework-owned sanitization for DLEX_OUT. + +The DLEX classifier decides whether a candidate response contains information +that must not be exposed. When the finding is recoverable, this module masks +only the sensitive values so the response can be revalidated and preserved. + +Rules are intentionally generic and live in the framework, never in domain +agents. Deployments may extend the built-ins without changing agent code via: + +* ``GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON`` (JSON list of rule objects), or +* the DLEX_OUT rail policy key ``sanitize_patterns`` in guardrails.yaml. + +Each extension rule accepts ``name``, ``pattern`` and ``replacement``. Patterns +are regular expressions. Raw matched values are never returned in findings. +""" +from __future__ import annotations + +from dataclasses import dataclass +import json +import os +import re +from typing import Any, Iterable + + +@dataclass(frozen=True, slots=True) +class DlexMaskRule: + name: str + pattern: str + replacement: str + flags: int = re.IGNORECASE + + +# Conservative defaults: values that are broadly unsafe to echo verbatim and +# can be redacted without changing the semantic meaning of an answer. +_BUILTIN_RULES: tuple[DlexMaskRule, ...] = ( + DlexMaskRule( + "payment_barcode", + # Brazilian boleto/payment representations normally contain 44, 47 or + # 48 digits and may be formatted with dots/spaces. Keep the match on the + # numeric token only so surrounding prose is preserved. + r"(? DlexMaskRule | None: + if not isinstance(raw, dict): + return None + name = str(raw.get("name") or "custom").strip() + pattern = str(raw.get("pattern") or "").strip() + replacement = str(raw.get("replacement") or "[DADO_PROTEGIDO]") + if not pattern: + return None + try: + re.compile(pattern) + except re.error: + return None + return DlexMaskRule(name=name, pattern=pattern, replacement=replacement, flags=0) + + +def _extra_rules_from_env() -> list[DlexMaskRule]: + raw = os.getenv("GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON", "").strip() + if not raw: + return [] + try: + data = json.loads(raw) + except Exception: + return [] + if isinstance(data, dict): + data = [data] + return [rule for item in (data or []) if (rule := _parse_rule(item)) is not None] + + +def _extra_rules_from_policy(policy: dict[str, Any] | None) -> list[DlexMaskRule]: + data = dict(policy or {}).get("sanitize_patterns") or [] + if isinstance(data, dict): + data = [data] + return [rule for item in data if (rule := _parse_rule(item)) is not None] + + +def _mask_payment_sequences(text: str) -> tuple[str, int]: + """Mask 44/47/48-digit payment tokens without consuming surrounding prose. + + Regex alone is awkward because formatted boletos have variable punctuation. + We scan numeric/punctuation runs, count digits, and only redact the run when + it has a canonical payment digit count. + """ + token_re = re.compile(r"(? str: + nonlocal count + token = match.group(0) + digits = re.sub(r"\D", "", token) + if len(digits) in {44, 47, 48}: + count += 1 + return "[CODIGO_DE_PAGAMENTO_OCULTADO]" + return token + + return token_re.sub(repl, text), count + + +def sanitize_dlex_output( + text: str, + *, + policy: dict[str, Any] | None = None, + exclude_values: Iterable[str] | None = None, +) -> tuple[str, list[dict[str, Any]]]: + """Return ``(sanitized_text, findings)`` without exposing raw matches. + + ``exclude_values`` contains explicitly authorized values (for example a + workflow-generated protocol). They are temporarily protected so generic + masking never alters them. + """ + original = text or "" + sanitized = original + findings: list[dict[str, Any]] = [] + + protected: dict[str, str] = {} + for index, value in enumerate(exclude_values or []): + value = str(value or "").strip() + if not value or value not in sanitized: + continue + marker = f"__DLEX_AUTHORIZED_{index}__" + sanitized = sanitized.replace(value, marker) + protected[marker] = value + + # Payment sequences use digit-count validation rather than a plain rule. + sanitized, payment_count = _mask_payment_sequences(sanitized) + if payment_count: + findings.append({"type": "payment_barcode", "action": "redact", "count": payment_count}) + + rules = [r for r in _BUILTIN_RULES if r.name != "payment_barcode"] + rules.extend(_extra_rules_from_env()) + rules.extend(_extra_rules_from_policy(policy)) + + for rule in rules: + try: + regex = re.compile(rule.pattern, rule.flags) + except re.error: + continue + sanitized, count = regex.subn(rule.replacement, sanitized) + if count: + findings.append({"type": rule.name, "action": "redact", "count": count}) + + for marker, value in protected.items(): + sanitized = sanitized.replace(marker, value) + + return sanitized, findings + + +__all__ = ["DlexMaskRule", "sanitize_dlex_output"] diff --git a/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py b/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py index 18b3fe9c..8ae0a4ec 100644 --- a/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py +++ b/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py @@ -25,6 +25,7 @@ from .calibrated.output_sanitization import mascarar_pii_output, sanitizar_toxic from .calibrated.rules.pinj_patterns import _PINJ_PATTERNS, is_obvious_injection from .calibrated.rules.tox_blocklist import _EXPLICIT_TERMS, _THREAT_PATTERNS, is_obvious_toxic from .framework_llm_client import classify_with_framework_llm +from .dlex_sanitizer import sanitize_dlex_output from agent_framework.workflows.input_contract import has_meaningful_unmatched_policy, has_semantic_classifier @@ -323,10 +324,28 @@ class CoherenceRail(Guardrail): "data": out, }, ) - out = await classify_with_framework_llm( - _llm(ctx), "COER", {"text": text or "", "context": ctx}, - profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", - ) + try: + out = await classify_with_framework_llm( + _llm(ctx), "COER", {"text": text or "", "context": ctx}, + profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", + ) + except Exception as exc: + # COER is a conversational-coherence rail, not a security boundary. + # If its semantic classifier/provider is unavailable, the graph must + # continue and let the normal router/workflow clarification logic own + # the turn instead of surfacing an infrastructure exception. + return RailDecision( + code=self.code, + allowed=True, + reason="Classificador de coerência indisponível; continuidade delegada ao runtime", + sanitized_text=text, + metadata={ + "mechanism": "infrastructure_fail_open", + "calibrated": True, + "error_type": type(exc).__name__, + "error": str(exc)[:500], + }, + ) return RailDecision( code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "COER avaliado"), @@ -344,6 +363,9 @@ class LoopRail(Guardrail): "COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION", "EXECUTING", "PAUSED", "WAITING_INPUT", } + # A queued multi-intent transaction may be promoted immediately after + # the previous one completed. Prefer the newest live MCP evidence over + # a stale top-level COMPLETED status left by checkpoint merge order. for result in reversed(list(ctx.get("mcp_results") or [])): if isinstance(result, dict): result_status = str(result.get("transaction_status") or "").strip().upper() @@ -359,6 +381,11 @@ class LoopRail(Guardrail): async def evaluate(self, text: str, context: dict[str, Any]) -> RailDecision: ctx = _ctx(context) transaction_status = self._transaction_status(ctx) + + # Parameter values and short confirmations may legitimately repeat in + # the same session. VLOOP must not consume input owned by the active + # transaction contract; extraction/confirmation remains responsible + # for deciding whether the value is valid. if transaction_status in {"COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION"}: return RailDecision( code=self.code, @@ -373,6 +400,7 @@ class LoopRail(Guardrail): "calibrated": True, }, ) + normalized = _lower(text).strip() history = [_lower(h).strip() for h in ctx.get("history_texts", [])[-6:]] repeated = history.count(normalized) >= 2 if normalized else False @@ -805,11 +833,64 @@ class DataLeakageOutputRail(Guardrail): } protocol_authorization_verified = True + # DLEX_OUT is permissive for recoverable leakage: when classification + # rejects the candidate, mask only generic sensitive values and re-run + # the same classifier. The agent/domain remains unaware of this detail. + # If sanitization cannot change the candidate, or the sanitized version + # is still unsafe, the original blocking behavior is preserved. + dlex_sanitized_text = None + dlex_findings: list[dict[str, Any]] = [] + sanitization_recheck = None + original_dlex_result = dict(out) + if not bool(out.get("allowed", True)): + policy = dict(getattr(self, "_guardrail_policy", {}) or {}) + candidate_sanitized, dlex_findings = sanitize_dlex_output( + original_text, + policy=policy, + exclude_values=matched_expected_protocols, + ) + if candidate_sanitized != original_text: + sanitized_classifier_text = candidate_sanitized + sanitized_classifier_ctx: dict[str, Any] = ctx + if matched_expected_protocols: + sanitized_classifier_text = _mask_authorized_protocol_values( + sanitized_classifier_text, matched_expected_protocols + ) + sanitized_classifier_ctx = _mask_authorized_protocol_values( + ctx, matched_expected_protocols + ) + sanitization_recheck = await classify_with_framework_llm( + _llm(ctx), + "DLEX_OUT", + {"text": sanitized_classifier_text, "context": sanitized_classifier_ctx}, + profile_name="grl", + component_name="guardrail.dlex_out.sanitization_recheck", + generation_name="guardrail.dlex_out.sanitization_recheck", + ) + if bool(sanitization_recheck.get("allowed", True)): + dlex_sanitized_text = candidate_sanitized + out = { + "allowed": True, + "label": "SANITIZED", + "reason": "DLEX_OUT sanitizado e revalidado com segurança", + "original_reason": original_dlex_result.get("reason"), + } + metadata = { "mechanism": "llm_rail", "data": out, "calibrated": True, } + if dlex_findings: + metadata.update( + { + "action": "sanitize" if dlex_sanitized_text is not None else "block", + "sanitized": dlex_sanitized_text is not None, + "findings": dlex_findings, + "original_decision": original_dlex_result, + "sanitization_recheck": sanitization_recheck, + } + ) if matched_expected_protocols: metadata.update( { @@ -823,7 +904,7 @@ class DataLeakageOutputRail(Guardrail): code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "DLEX_OUT avaliado"), - sanitized_text=text, + sanitized_text=dlex_sanitized_text, metadata=metadata, ) diff --git a/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py b/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py index 836282a5..f6ec8a73 100644 --- a/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py @@ -1218,7 +1218,17 @@ class EnterpriseRouter: or (started_intent and configured_candidate.intent != started_intent) or (previous_intent and not previous_intent.startswith("state:") and configured_candidate.intent != previous_intent) ) - if not different: + + # A configured candidate that resolves to the same intent/agent is not + # sufficient to conclude that the active transaction continues. The + # user may be starting a *new instance of the same intent* with a + # different transactional target (for example cancelling TIM Fashion + # while Tamboro is awaiting confirmation). In LLM mode keep the + # candidate only as a semantic hint and let the transaction classifier + # decide CONTINUE/REPLACE/ABANDON. Without semantic classification we + # retain the legacy state lock because guessing replacement from a + # keyword alone would be unsafe. + if not different and not (self.enable_llm_router and self.llm is not None): return None # During parameter collection, a configured keyword may be present in @@ -1259,6 +1269,11 @@ class EnterpriseRouter: "current_intent": started_intent or previous_intent, "transaction_status": state.get("transaction_status"), "tool_name": active_tx.get("tool_name"), + # Structured arguments are part of the identity of the active + # transaction. They let the semantic classifier distinguish a new + # instance of the same intent from a continuation without relying on + # domain keywords or hard-coded product names. + "active_arguments": dict(active_tx.get("arguments") or {}), "missing_parameters": list(state.get("missing_parameters") or []), "configured_candidate": ( { @@ -1285,11 +1300,15 @@ class EnterpriseRouter: ) else: system = ( - "Você decide apenas se o turno atual continua a transação ativa ou muda de intenção. " - "Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " + "Você decide apenas se o turno atual continua a transação ativa, substitui a instância transacional ativa, " + "ou muda de intenção. Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " - "Se o usuário passou claramente a perseguir outro objetivo sem desistir explicitamente da ação atual, " + "Se o usuário continua com a MESMA intent, mas claramente inicia outra instância da operação com alvo/argumentos diferentes, " + "retorne REPLACE e informe a mesma intent e agent. REPLACE significa substituir a transação ativa; não significa estado novo. " + "Não use REPLACE quando o usuário apenas complementa/adiciona itens à mesma operação (por exemplo 'também', 'além desses'), " + "nem quando apenas reformula ou confirma o mesmo alvo: nesses casos retorne CONTINUE. " + "Se o usuário passou claramente a perseguir outro objetivo/intent sem desistir explicitamente da ação atual, " "retorne SHIFT e a nova intent permitida. " "Se o usuário desistiu explicitamente da ação/transação atual, retorne ABANDON. " "Quando ABANDON vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " @@ -1323,7 +1342,7 @@ class EnterpriseRouter: if abandon_only: if decision_kind != "ABANDON": return None - elif decision_kind not in {"SHIFT", "ABANDON"}: + elif decision_kind not in {"SHIFT", "REPLACE", "ABANDON"}: return None confidence = float(data.get("confidence") or 0.0) if confidence < self.intent_shift_threshold: @@ -1374,7 +1393,42 @@ class EnterpriseRouter: mcp_tools=mcp_tools, ) - if not intent_name or intent_name == (started_intent or previous_intent): + current_intent = started_intent or previous_intent + if not intent_name: + return None + + # Same-intent replacement is a transaction lifecycle event, not a new + # conversation state. It cancels only the active transaction instance + # and re-enters normal routing for the same configured intent. + if decision_kind == "REPLACE": + if not current_intent or intent_name != current_intent: + return None + agent = agent or str(self._agent_for_intent(intent_name) or "").strip() + if not agent: + return None + return RouteDecision( + route=agent, + agent=agent, + intent=intent_name, + confidence=confidence, + reason=str(data.get("reason") or "Nova instância transacional da mesma intenção."), + method="llm", + metadata={ + "transaction_interruption": "same_intent_replacement", + "interrupted_state": state_decision.next_state, + "interrupted_agent": state_decision.agent, + "interrupted_intent": current_intent, + "interruption_source": "semantic_classifier", + "configured_routing_hint": ( + configured_candidate.intent if configured_candidate is not None else None + ), + "raw_llm_answer": answer[:1000], + }, + domain=self._domain_for_intent(intent_name), + mcp_tools=self._tools_for_intent(intent_name), + ) + + if intent_name == current_intent: return None agent = agent or str(self._agent_for_intent(intent_name) or "").strip() if not agent: diff --git a/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py b/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py index 3fa365af..dbae2498 100644 --- a/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py +++ b/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py @@ -3113,21 +3113,33 @@ class AgentRuntimeMixin: self._normalize_transaction_lifecycle(state) # Uma transação em coleta/confirmação não pode aprisionar a sessão. O - # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT ou - # ABANDON. Não existe interpretação lexical de desistência no runtime; o - # runtime apenas aplica a decisão do router e limpa os latches da ação. + # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT, + # ABANDON ou replacement da instância transacional na mesma intent. Não + # existe interpretação lexical de desistência no runtime; o runtime apenas + # aplica a decisão do router e limpa os latches da ação. active_before_interruption = self._active_transaction(state) interruption = str(route_meta.get("transaction_interruption") or "").strip().lower() - if active_before_interruption and interruption in {"intent_shift", "explicit_abandonment"}: + if active_before_interruption and interruption in { + "intent_shift", + "explicit_abandonment", + "same_intent_replacement", + }: interrupted_tool = active_before_interruption.get("tool_name") self._finish_active_transaction(state, "CANCELLED") state["transaction_pre_validation"] = None + # Generic framework boundary: consumers do not need to know why the + # previous transactional context was invalidated. This deliberately + # avoids leaking same_intent_replacement (or any other interruption + # subtype) into domain agents/workflows. + state["operational_context_reset"] = True + if interruption == "explicit_abandonment": + action = "cancelled_by_explicit_abandonment" + elif interruption == "same_intent_replacement": + action = "cancelled_by_same_intent_replacement" + else: + action = "cancelled_by_intent_shift" state["tool_policy_result"] = { - "action": ( - "cancelled_by_explicit_abandonment" - if interruption == "explicit_abandonment" - else "cancelled_by_intent_shift" - ), + "action": action, "tool_name": interrupted_tool, } diff --git a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt index a54b9a88..f524a570 100644 --- a/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt +++ b/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt @@ -53,6 +53,7 @@ src/agent_framework/guardrails/__init__.py src/agent_framework/guardrails/base.py src/agent_framework/guardrails/config_loader.py src/agent_framework/guardrails/custom_rails.py +src/agent_framework/guardrails/dlex_sanitizer.py src/agent_framework/guardrails/executor.py src/agent_framework/guardrails/framework_llm_client.py src/agent_framework/guardrails/langgraph_adapters.py diff --git a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index 4a44168d543f33e5c335efa7d3fefe0420c96f8c..4beeadf8bb927e731c26c3ba132adb123a6e6c40 100644 GIT binary patch delta 64 zcmbQjJeQgKGcPX}0}!a1uARvJUZX%iH?KrLMc+5rGd`dwKPxr4q*y;YDJ4liF+DY} SBtAJmuOzWJ-e|HUV>JMF7!>vZ delta 55 zcmbQsJcXJ2GcPX}0}$wGt)9sJUOrtvH?KrLC9xz?KQSpWFC{-OzBn~EF|Q;uIX*43 JVzLrrH2}{B5RU diff --git a/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc index 019dc27a3b50b06489aad46eb0fbcc7c50f6bb13..a526262e0cc97fd03683e032cad60442143b03a6 100644 GIT binary patch delta 66 zcmX@=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_fH_hg0JUWm*8l(j delta 57 zcmccQdeW8qGcPX}0}$wGt=`D(#w?$upPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+Tj2IZ_?~2j&wZ diff --git a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index 01101db924a5b707e898df279b520ff566e9565a..2aa5c9bab3a8af051437b7862dfd8888dd8a8569 100644 GIT binary patch delta 22 ccmbQ*$vCx>k^3_*FBbz4sG6?b$lc`v07sey@&Et; delta 22 ccmbQ*$vCx>k^3_*FBbz4+{{_Lk-N(U08O3-(*OVf diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index 171af7a9e44fbd759ae24d118163e19b636a5eb1..6f06afb7404b9c9ad19e6f549edb8b1200c2ec39 100644 GIT binary patch delta 20 acmZ3=vXq7UGcPX}0}!a2uHDEj#RLE{u>@8C delta 20 acmZ3=vXq7UGcPX}0}$NIS-g>3iU|NXrv)_t diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index e7aa90165a7fb514c4296d49c3706cfd65ef69cc..57af840d345729682e55e0713aa27ff9f411c64f 100644 GIT binary patch delta 20 acmX>hd_tJ}GcPX}0}!a2uHDGJmlFUxOa){B delta 20 acmX>hd_tJ}GcPX}0}$NIS-g>ZFDC#&LIy(s diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc index a06866c9d49d92743e08c19099ea2c6bb4aaf57a..d28453d97bfa21a375c12916a4de113258818740 100644 GIT binary patch delta 20 acmaFI`HqwOGcPX}0}!a2uHDG}m<0eo&jq~z delta 20 acmaFI`HqwOGcPX}0}$NIS-g?^F$(}i#Ri-J diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc index 1e73aaa889a99e2812e11fc4dbcb095e3e4830f9..09eb4d943ba4963954cc7d049ff9db26f25f83a9 100644 GIT binary patch delta 20 acmbOyKTn?fGcPX}0}!a2uHDEznI8Z)as>nc delta 20 acmbOyKTn?fGcPX}0}$NIS-g>ZGCu%3Xa(Z{ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index 8fcf15f5d02813d2c88cebd40810a82546dfd486..65d1d490a760d81612cb45e7d2901c300a61ee52 100644 GIT binary patch delta 20 acmcb_cZrYtGcPX}0}!a2uHDG(&JF-OCIt2X delta 20 acmcb_cZrYtGcPX}0}$NIS-g?kogDx{90k@B%LqdD>i)y$`7 ziiwH>UVIC)s%{x6Y^vU}M0%M-2Lj@Q+OiPufp+U}D~qL|)oQclzYJ3pfB%C3Ps(EnR83 z-EQN1E^uy2&8umaNGobZ-Ecy2(|B0crwzG5y(?3_De-6{1f%=Xr`w_g` z7wB0mmMMv4u-Lhfx$vDrmDQ>`6+XUO<+9?c5&B|56|JetTEbqUV;HM9T8`nd2|BsU zS_(O*jumPtsqk_U6&8&)N8tcbMDVS~omi%sAmBA%MMp|dm}-P4Mz^dm8ZT!S;AJ69 znSNNOjwF+@=sd$m6Y+gvHWH1qVJZ@3W8r0riE%6wUQ;GHcA1UyR5VV_&ax}e4SABx zS}ehYJu|bj6vMHUG>2P(!Ay!zEJ3|_CKg+xmJ?JY#iuw{$?!ZEolEiTGBwY{sYonw zh2q;5xJ2sW0yUnRYp3B87(3QJc6#6>btTF#BvL%J#3Yj}N6jxV@r&&8L%lhosL^l+ zxJ=H03~J&_MJgF)cvkq({;lCF2z0?`IjbOu8^mjyaMU5Jzg82jx{UYp#T)(?a@9wH z_XB|N%S?*xO5^R|XVafdr{5LLEziwe*{h%&5kNtZRU4h@`V5&pBpN$Gg@jfTWX)!E zMxIIJ4)`CtMC&vlB|=G%hK+`_DZQGtid7vzC8279v}`KUye4x#=Nk}f27w0R!kE)+ zgDLd8DMVxQb7M!A1qMW4clNJCx|$$O8u$9IkCZ zh13(Ic4H+S&J2i_=4|*U%l@1EBRSY3#WfP7ZL>2|pKI*Obq7TI*q==j-b|48&B2T} z*VLWs9Ty#^K#PR8;`k}iF#+~Tc-wBJJDqCJPv2i$?|Y)T7j#Il7tcB>+5^A`alzm+ zw!(rB!h&zD+zzJ)M4bn8NfC#zof{IZ!=M`ljJu?r=-VZ?uIKz`MC%mjNA$YOYK3~o zWyK<-tr(CY_JPX^?bH065jdw>>eA>|pJ+J>PDpe9bvaNA1CZTm`Btr1efY02#45lz z#HBB!utBHt_ym@5RWu(2bRItqaovXEHlLmp_4Oc_M<*d#Y_`wWWGIu?+jglR8 zTZZ5&;BA5(3(-0RqItDNNMJj0DUZgAnYWx`bw5~=k?i*TSU# zxUW{46tCgQEw2z(Ju`-9N@Gx^c;+R82F1@ZMbIGp;pv(G8eI(V^b<`At{(Y!k=e8B zA^xg49G&Mw%RJXg9f_~aP}itHBF;8dav3zwiOiSCNU<%@dnunwBx0N-epmRQ?x+J@ zHQx{%`cuYf(2UBSr9?OtWBa&!!tZt7cOo)b{|mhllp$wZWd-HP0hNT_1W3tM5(?Ab JD&$Hs{{iU0R4o7i delta 2395 zcmZuxTTB~A6x}6e&4QnpwOPMyOn8R(BRmq)G+-VD8U_g95=sLkAp}x`qJ}(`uSSa0 zq>EI4pw?1Jg_Iwv^ds#@AAeEvV|5~><)g|+RjGf|rixUls(NP}f*?L$fACbkC2F?|I!+jA4SmLPQ7)32 zfv3-mh>S6wGom~sHyifYd>xyQ3OJ=HX4L#9k7(4|SsXW2L4MOT7R^iR1!08EmU&D6 zhJ~0ZgN#@*S@ySCOy?tZ?U}(X!H5oMbl6NAO__ad{J6zP$D%0aa=h#eQlxB?pi#pe7|aRfUB#~ zdDL*T)Ph5`Rn@Ju4%Kto2A#G6HF8`NsIOUl(0|kF(Rs9?cCM~N=h1;WIqnd65G!4S z$wp(jjr#T20y@7Q6y-F%I=^1j$8r7O_pwdCq5<>Z9PRG1*&PUk4M z_nEW%Dxop$Tf6GZBDrW3jdA*M(0^es1?8??zatZ9lB=82%`k;x9QPJ@)D-GhFpW-f z6*D^Z40@a6WpmS(Gqs?3fo%aGdo#A!(8FUscW-hOe zIfs^BrD#Xz(ekSZw1O_|;Ywz-iPtk9DP4|3spYpkp5f4Hjod9R8^?0WUkYa{;s1_t z#H9s`8nk~LA(3W_IqP3(9~6E0;Ds_T*-UXe6vw?Z$Plzu?|u6gByI4w`k^EUH-tZ( zTCGnTB!;LMke=l^?u>mjd3Q2#87oy!mA375l1m9933A~^OCpk#x7)F!g%}x{C&~>hJ0XFK1SC1uLDz#YDwf^wPs>tVD^*aQB{BdRz~65(7#F zIQa!x*;q)7COfgSjs#eJP>>xPJ&C5I8y8iR5UuVC?7t4x5kazT422#po<$~%_e^u#TXwn|URq|sLGx023NpV2E0_gScr;t`tPqoH&! zqx`xuclOj;Y;`SO9ei#1(sb}jaA;+Dz9KLF39a+dkfecWt03DoytA^h6ldo5wD%)N y#Vze|#4X;}evLHM&xm5zk2VL{N0Vm-D#@h@$rEfvL}qEeAQinRplRF+cK-rkiV98u diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc index a18f97b5d01b9cc8958509185ddb1c51701fa80b..50374fbbd90ea2561a5316f5ac9dc54ea2f57088 100644 GIT binary patch delta 22 ccmbQ;$vC@{k^3_*FBbz4sGF|c$UVUY07$9^1^@s6 delta 22 ccmbQ;$vC@{k^3_*FBbz4+{{_Lk$Zv*08XF=&j|B4o delta 20 acmZo+X<_01%*)Hg00cL47H{PK$p`>7eg)A0 diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index b5e221b2a45c0a9235e1c84e871489dbf9b41c73..e2bfdc6c855850d79dbe4ae0ce3be7e750c280ed 100644 GIT binary patch delta 20 acmbQnH;s?`GcPX}0}yDKuHDGZ%?+XL delta 20 acmbQnH;s?`GcPX}0}$NIS-g>(n;if*yagcu diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc index 1387eaaa92436c263a48b9ee63efbb77d9f43ff9..d7b6bafd78d865dae5c5b4cc560bc8ff6dbb4e55 100644 GIT binary patch delta 22 ccmZqM%GkD*k^3_*FBbz4Xqc|u$Xy);088`+HUIzs delta 22 ccmZqM%GkD*k^3_*FBbz4+{{_Lk-Ity08!%y6aWAK diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc index 585cc2841c4ad97f56b90c0430977154b1d3800d..1dfea145bd965b92c4b247ece660185137eecb2c 100644 GIT binary patch delta 20 acmbQwH=mFDGcPX}0}yDKuHDEj!VUm4H3VJ& delta 20 acmbQwH=mFDGcPX}0}$NIS-g>3gdG4mBn3PG diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc index cfeae8e067794ecc40c113e8ea258e8abf616e15..a47ad3262c0216461c66ff66d79d69f3d58d4a0a 100644 GIT binary patch delta 20 acmbPaHOY$mGcPX}0}yDKuHDGZE(-uRa|ERT delta 20 acmbPaHOY$mGcPX}0}$NIS-g>(T^0a6Vg-W$ diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc index beff5c8389cb8b1869a4db7b456bdda0dfddb5e1..89af7e7df19745860731fd18c5bd7783c2b94074 100644 GIT binary patch delta 20 acmbQrJe8UIGcPX}0}yDKuHDGp#Rvc~69l9H delta 20 acmbQrJe8UIGcPX}0}$NGS-g?EixB`f2?c`y diff --git a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index 386d5fbc73df7de8644101120ddc3a0fd2ee1cf1..b7c26689a9847cd4327e4e2093a0157d34c8b85b 100644 GIT binary patch delta 20 acmX?6b)t&t`pWB3UlIU9eg)?M diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc index 17f6fd043738c906b863eb69aaeeeac64962535c..9ea9e415fb1b91d5937e8dac5b58e3f0f2bdaac9 100644 GIT binary patch delta 20 acmbQvJ)N8TGcPX}0}yDKuHDGp!wLX0hXlR= delta 20 acmbQvJ)N8TGcPX}0}$NIS-g?EhZO)gb_JXO diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index a89c93af39a9db8b271ef08fec274c2e85ac09dc..1d39403a903179f7a23edba73f72bad97712ef34 100644 GIT binary patch delta 20 acmbQsK9`;QGcPX}0}yDMuHDEzi5UPg>;%UE delta 20 acmbQsK9`;QGcPX}0}$NGS-g>Z5;Fie+XbZn diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc index 1b4826bfbc63729b3ebc55a8bc0dbee810bce08f..e874174f70f7924874a98ac3cabf68d361ab7078 100644 GIT binary patch delta 22 ccmbPxg>mi`M()qNyj%=GplP~xBezf%08^m`N&o-= delta 22 ccmbPxg>mi`M()qNyj%=Ga4To=MsA@j09lX+C;$Ke diff --git a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 34b526898166801e1e2dba39f10ee5076fe78e54..1c996b7a1af61218396acd86fb3b561e0c6252c7 100644 GIT binary patch delta 22 ccmey|%=EdLiTg7zFBbz4Xqv9w$o--b09N=1LjV8( delta 22 ccmey|%=EdLiTg7zFBbz4+{#(Jk^4m>09@w?ApigX diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index 07780aa3ca0da997147d9f69551878681fdf6250..37e51598390c6512a200f58fa511643fde356e95 100644 GIT binary patch delta 63 zcmX@lc!QDqGcPX}0}!a1uARtjtWmC?n^&TrqVF5*86QxTpOu3N*Vw^&jqsp diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 774134a7c466f570fae7d549d4995aa1608e5f3f..1876f380006442218df13b77df388cfaca6fa4dc 100644 GIT binary patch delta 66 zcmeyG^Dl?{GcPX}0}!a1uHDFeLrbGvKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoS8lXfvT00C(h`Tzg` delta 57 zcmeyD^EHS2GcPX}0}$wGt=`CeLrXqSKR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Lvtl#1b}=^qM_?31 diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index 0087fb9a1feb485a61f84da7346bbc75b94f738a..b7933581bca688eb729e0ee31860acfa1f8cffed 100644 GIT binary patch delta 63 zcmZ3@w1tWLGcPX}0}yDKuARu;q*1A#n^&TrqVF5*86QxTpOuz}>0J*Uep8x;= diff --git a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc index 1a6e4c9363493d9b0be221b201c3976414f142a9..daa4d787ff6ab344eb8d679c4045c930b0b21e2f 100644 GIT binary patch delta 66 zcmew>*(SyPnU|M~0SGiq*KXuK!Kl%!pPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;Jw6C*b_0JhH-82|tP delta 57 zcmZpZ`YXx(nU|M~0SFAVR&V4!!6;v)pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+V&yBR4ky2I3QA diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index e67486929d7acc87bcea7dd167e89642b5912002..b59b1a3f393a7e2316ee9eea9112d9545d6ace62 100644 GIT binary patch delta 20 acmcc5eV?2AGcPX}0}!a0uHDFeg%tojxdoH} delta 20 acmcc5eV?2AGcPX}0}$NGS-g?^3M&9Zy#{pv diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 2b0d27b2d00eb2c56273e7c304475988ed2ca6b6..dc2c8ddee836ab76e4513e332ce0a1b119fff4c7 100644 GIT binary patch delta 20 acmaFJ^^lAEGcPX}0}!a0uHDET#tHyDSv=>_=! diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index fef46f5f8ec2c894d69228ab50d9016e1d2da8c5..f1aaec9a44b7fefb91e4db01c597bd9cded42e8b 100644 GIT binary patch delta 20 acmX@*e#)KuGcPX}0}!a0uHDGJUl{;G{{`y+ delta 20 acmX@*e#)KuGcPX}0}$NGS-g>ZzcK(x0|v|h diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index f232e066e4e3ebf1319c5c739691e764a3c5729d..271e5d608e427c98d452e3ee972c1761260b9850 100644 GIT binary patch delta 20 acmdm{v`vZoGcPX}0}!a0uHDG3EeHTPnFP}S delta 20 acmdm{v`vZoGcPX}0}$NGS-g>3TMz(0odvW2 diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8a868ccdcae27a426ac5bab3a2708a722dbef85 GIT binary patch literal 8489 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 K_GS$|j{gUY0tLkY literal 0 HcmV?d00001 diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index 3163328a157240a8b688d085b8449d56cd5b2212..48666933633e4c535edfda83a1bd60fa2b6bf332 100644 GIT binary patch delta 22 ccmcaTlkxUUM()qNyj%=Gpk}&uBliU#09D%t?EnA( delta 22 ccmcaTlkxUUM()qNyj%=Ga4To=M(ztf09)S&&Hw-a diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 8d8f919a3dcafa9df89d3f96e46e523b0bb75a8f..efdc67dbe678fbb689961c902c4e7907b021e74a 100644 GIT binary patch delta 20 acmZ3gw^Wb&GcPX}0}!a0uHDEjB@O^Mrv$zL delta 20 acmZ3gw^Wb&GcPX}0}$NGS-g>3N*n+_s|B9` diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index 548ec0546a4323185c107c93e354229581f1706a..d945f0b1cb6a1294d997365d2112f746ed44630c 100644 GIT binary patch delta 22 ccmeyem+{M9M()qNyj%=Gpk}&uBlpWV0AHL3k^lez delta 22 ccmeyem+{M9M()qNyj%=Ga4To=M(&q!0A-*Ea{vGU diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index e3641b6ad80f9d9cd01c3a877fc0639cee612d04..7c280a45c0b19ffe13f54791fe0e13d85c527397 100644 GIT binary patch delta 22 ccmaE{it)uNM()qNyj%=Gpk}&uBX?{l09mRAzW@LL delta 22 ccmaE{it)uNM()qNyj%=Ga4To=M()^90AH>Lpa1{> diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 1e5a9b9635eaf0a55be427989df3d21bd5e73b1f..9408e0b7f18f840e08f53d754e144cc9f75c0ada 100644 GIT binary patch delta 20 acmeAU?GEMs%*)Hg00e5LYd3N?XaN8_CIwLd delta 20 acmeAU?GEMs%*)Hg00g&k7H{Nk&;kHKDh4tD diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index d80bc2d3f21f2dd934c75c02f562a34776a2a6b3..c43b8e564671a2848064f87185804a4f376aa26c 100644 GIT binary patch delta 20 acmbQhI)Rn@GcPX}0}!a0uHDGp$^-x~h6I=Z delta 20 acmbQhI)Rn@GcPX}0}$NGS-g?El?ebgiUoN9 diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index ac1ae631c5170120413e5fccaf54a2c6de3b1a72..3484fe0bf3b48951660d62338c4090e790376a94 100644 GIT binary patch delta 20 acmX@kbDW3!GcPX}0}!a0uHDFO!v+939t6(- delta 20 acmX@kbDW3!GcPX}0}$NGS-g?kh7ABeA_cGj diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index 32fb0ab1872155d6cecf02e2dd503cfe11116602..69d84557413224f3f7c1dcc630aef7f8675c2a83 100644 GIT binary patch delta 20 acmaFI{*ImdGcPX}0}!a0uHDG}m>B>+e+9Py delta 20 acmaFI{*ImdGcPX}0}$NGS-g?^F*5)~g9exY diff --git a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index c5b5b845d737ad32664c90229d022682e1266c2b..3c8b0a05da7a4ef2f925e6853a7c19e5ffe33601 100644 GIT binary patch delta 10888 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 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 3916d89d14c8cd6dc1a1a6c8befd1a03d5c15d4c..43a26825cc1cf5148a160e5128ae06a0bc3b5395 100644 GIT binary patch delta 20 acmeAd?ic3%%*)Hg00e5LYd3PYZ~_1_T?DuQ delta 20 acmeAd?ic3%%*)Hg00g&k7H{Nk;RFCUVFj50 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index e818798a4488801df846c0dfe0dce323eecd1cb4..658adb8b245d74cb8fd74c8a19ad353295f25d1a 100644 GIT binary patch delta 20 acmZn`Y!>AH%*)Hg00e5LYd3ONZ~y=>Gz5AH%*)Hg00g&k7H{OP-~a$NI0bM3 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 037267311a8ef5266b355a4a03517c0f53d281d2..8c22899e1d3452a7e2d36f2d05269b45e5411eaa 100644 GIT binary patch delta 20 acmexv`rVZKGcPX}0}!a0uHDG}MhXB&b_PWN delta 20 acmexv`rVZKGcPX}0}$NGS-g?^jT8V)dIu%| diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc index b21b72b29b8169ad077af32b15313c7f07bded69..d13ac46c7fe94c90bc0503b5fbb1d03222f0bac9 100644 GIT binary patch delta 20 acmX>sb6AG^GcPX}0}!a0uHDFO!3O|3#supC delta 20 acmX>sb6AG^GcPX}0}$NGS-g?kf)4;d$_2~- diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index b15f86c964479352421bd87898443705b294ee16..5295088950a8d94c2fea80020b1f3bbbcf6b4f9c 100644 GIT binary patch delta 20 acmca9dsCMCGcPX}0}!a0uHDFejt>AoS_Q8F delta 20 acmca9dsCMCGcPX}0}$NGS-g?^93KEhUIvf= diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index 9c8fbc947c5d8c566150434f08e5cdb2165500a0..38f1aea0af2cb9d0c0823c73788c8acf2a502a84 100644 GIT binary patch delta 20 acmZ2rwZMw|GcPX}0}!a0uHDEjDhmKPl?2TI delta 20 acmZ2rwZMw|GcPX}0}$NGS-g>3R2Bd~nFX!@ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 78bb173bcacfea29a29e8641039bb6aed61a1019..9b2f632da982b4a35ce1c0948dc76ae7cc118730 100644 GIT binary patch delta 20 acmX@$dcc+YGcPX}0}!a0uHDGJRS5t=CI!d< delta 20 acmX@$dcc+YGcPX}0}$NGS-g>Zs}cZ5Dh8Z^#ybQ delta 20 acmcc3e4Cm3GcPX}0}$NGS-g?^0wVxH`36-0 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 24514e29c5219b838c316fe37de10dcb4f898b00..6bc38dea756c9989838583ecca74b0775397fed0 100644 GIT binary patch delta 20 acmeyU`%#zsGcPX}0}!a0uHDG}Obh@;Vg?@o delta 20 acmeyU`%#zsGcPX}0}$NGS-g?^nHT^|W(NQO diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index c8eb5e0062312b89a3ac0758ba04c002edd013c9..e80e85dc224f55966a2c7560b48ff76c1df8f6ef 100644 GIT binary patch delta 20 acmZ1~Y`GX>TF delta 20 acmZ1Zi9P^GHwL!= diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index acdb4fbd693863122f03afa4f795a4cab44e3237..b2fb4d1089a824243a22f6fec5a900c68eb26a99 100644 GIT binary patch delta 20 acmdn5uwQ}uGcPX}0}!a0uHDFOA^-q7xCH3{ delta 20 acmdn5uwQ}uGcPX}0}$NGS-g?kL;wIlyambt diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index 2eea70b1e96c54eea71be9c4e91ba23a9936016b..d4050835f379bd07cf562e567843f55f7aa7f8b4 100644 GIT binary patch delta 20 acmbQmHH(Y;GcPX}0}!a0uHDGZ&k6uB%>+XL delta 20 acmbQmHH(Y;GcPX}0}$NGS-g>(pA`T%(FG&` diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index 2b40e1b065dd7751bdcc235ccb39fcd504335b96..2d6adf34193140fd2bf35b50409ea963d93dbfc0 100644 GIT binary patch delta 20 acmbQqH(j~xIwI|VBM diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 37cae7bbe7d2803af7c56dff82c4f92c71dc4c42..2c3791e9e4c2922bba01ca3559367d6b70cc9416 100644 GIT binary patch delta 22 ccmey?$@s04k^3_*FBbz4sF|+a$o<*{09Wh>761SM delta 22 ccmey?$@s04k^3_*FBbz4+{#(Jk^8j^0A270_W%F@ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc index 684c41674b4643a4a51832d14d71e151c8e0c4fc..3b9b522d5be407e9cce1e5593a5a2263680d30c6 100644 GIT binary patch delta 20 acmca?ec77(GcPX}0}!a0uHDFeLKXl*u?6S= delta 20 acmca?ec77(GcPX}0}$NGS-g?^ge(9^wFb!m diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index 1122b7b6e5c4c48794c46a19d61bfb2fbd6927cc..702a1c147f5165a8469b7c42941e9c2f5ece4cf8 100644 GIT binary patch delta 22 ccmccK!Fauck^3_*FBbz4sF|+a$nE0-08x+zHvj+t delta 22 ccmccK!Fauck^3_*FBbz4+{#(Jk=w@w09TX;7ytkO diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index 0fca1941bc13401b9f02347f41d3640c9e6491a5..e1602bd0bead516489b03edd3aa209458ba1434d 100644 GIT binary patch delta 20 acmaFm_{x#{GcPX}0}!a0uHDFeUjYC|BnCVH delta 20 acmaFm_{x#{GcPX}0}$NGS-g?^z5)PECZIx_${qXo18 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index aa9d59997ce4ca41417f6443c95bf4911d40fd4a..4ac41e0ad986a47c71b57cc63e3dde54fbfa79b8 100644 GIT binary patch delta 20 acmZqVZRF+t%*)Hg00io$Yd3P2u>k-tSpy+Rs~=H diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 1013767cdcd1c369531be02eff94e8076f95b691..fede3088669a5c093f782db4e7af9822ef02fcd3 100644 GIT binary patch delta 20 acmeBF?^Ngh%*)Hg00io$Yd3P&2?GE$P6XKi delta 20 acmeBF?^Ngh%*)Hg00g&k7H{OP69xb}O9iZvoZijBL=7d diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index 27d36a486d6d185f06f84c0029d494792b9e72b3..9feeeb4e04c70a10c2a3a47a0321a09a370087a3 100644 GIT binary patch delta 20 acmZ1ywj_-EGcPX}0}!a2uHDEjsSN-=It3sA delta 20 acmZ1ywj_-EGcPX}0}$NGS-g>3QX2q7HwFLz diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index a5045828c44a8c80794144927c0b234f8f05ec9e..3947485cef5ebc839a48ef4791c51299cd5e0b24 100644 GIT binary patch delta 20 acmca8c~O%4GcPX}0}!a2uHDFej0XTcj|GkZ delta 20 acmca8c~O%4GcPX}0}$NGS-g?^7!LqLj0SE1 diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index 4b1e1f9dbe79bbeda4dd7ce2c92aba10c0006155..f46198f478a099fa00b01754cd964961baf838c7 100644 GIT binary patch delta 20 acmZpbYL(*t%*)Hg00io$Yd3QL;sF3M(*-O5 delta 20 acmZpbYL(*t%*)Hg00g&k7H{PK#RC91&;|?u diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index d721fe49229127363d68206955ed824830ce6825..86d3ca74f19ed57713000867feb8ffba69faaa88 100644 GIT binary patch delta 20 acmcbibwi8$GcPX}0}!a2uHDG(D+&NW6a`KI delta 20 acmcbibwi8$GcPX}0}$NGS-g?kR}=t75e6;* diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index 7973b2a7da6bffe6cd72c98debc6922994a1f921..d365db449802ab986f74c54bbd5a6cc7654c0452 100644 GIT binary patch delta 20 acmaE$^+1dJGcPX}0}!a2uHDETDhdEWR0U=L delta 20 acmaE$^+1dJGcPX}0}$NGS-g=uR1^S5Q3gf; diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index a7e0e360abf8b03c15642407bb3cea9f4c2da0af..57ba0f7a708c4f1807b6b5957583a86a71608b59 100644 GIT binary patch delta 20 acmaFv@Z5p>GcPX}0}!a2uHDETqW}O!j|H*- delta 20 acmaFv@Z5p>GcPX}0}$NGS-g=uMgagzj0Tbb diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 526b5cf15a5d944671b22807b46dfa6ea0261e78..f48f21d49c43955fa2b9f5a97e70ad0f1b9239de 100644 GIT binary patch delta 20 acmbQvGM$C{GcPX}0}!a0uHDGZ!vp{@dIT!~ delta 20 acmbQvGM$C{GcPX}0}$NGS-g>(hY0{SegzBw diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index 0c9b4c37a66435c37302b65fb8f139947a229ede..738af6c8e05297296b25bb99d1c91c97a55f3bf3 100644 GIT binary patch delta 20 acmcc3cbkv DlexMaskRule | None: + if not isinstance(raw, dict): + return None + name = str(raw.get("name") or "custom").strip() + pattern = str(raw.get("pattern") or "").strip() + replacement = str(raw.get("replacement") or "[DADO_PROTEGIDO]") + if not pattern: + return None + try: + re.compile(pattern) + except re.error: + return None + return DlexMaskRule(name=name, pattern=pattern, replacement=replacement, flags=0) + + +def _extra_rules_from_env() -> list[DlexMaskRule]: + raw = os.getenv("GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON", "").strip() + if not raw: + return [] + try: + data = json.loads(raw) + except Exception: + return [] + if isinstance(data, dict): + data = [data] + return [rule for item in (data or []) if (rule := _parse_rule(item)) is not None] + + +def _extra_rules_from_policy(policy: dict[str, Any] | None) -> list[DlexMaskRule]: + data = dict(policy or {}).get("sanitize_patterns") or [] + if isinstance(data, dict): + data = [data] + return [rule for item in data if (rule := _parse_rule(item)) is not None] + + +def _mask_payment_sequences(text: str) -> tuple[str, int]: + """Mask 44/47/48-digit payment tokens without consuming surrounding prose. + + Regex alone is awkward because formatted boletos have variable punctuation. + We scan numeric/punctuation runs, count digits, and only redact the run when + it has a canonical payment digit count. + """ + token_re = re.compile(r"(? str: + nonlocal count + token = match.group(0) + digits = re.sub(r"\D", "", token) + if len(digits) in {44, 47, 48}: + count += 1 + return "[CODIGO_DE_PAGAMENTO_OCULTADO]" + return token + + return token_re.sub(repl, text), count + + +def sanitize_dlex_output( + text: str, + *, + policy: dict[str, Any] | None = None, + exclude_values: Iterable[str] | None = None, +) -> tuple[str, list[dict[str, Any]]]: + """Return ``(sanitized_text, findings)`` without exposing raw matches. + + ``exclude_values`` contains explicitly authorized values (for example a + workflow-generated protocol). They are temporarily protected so generic + masking never alters them. + """ + original = text or "" + sanitized = original + findings: list[dict[str, Any]] = [] + + protected: dict[str, str] = {} + for index, value in enumerate(exclude_values or []): + value = str(value or "").strip() + if not value or value not in sanitized: + continue + marker = f"__DLEX_AUTHORIZED_{index}__" + sanitized = sanitized.replace(value, marker) + protected[marker] = value + + # Payment sequences use digit-count validation rather than a plain rule. + sanitized, payment_count = _mask_payment_sequences(sanitized) + if payment_count: + findings.append({"type": "payment_barcode", "action": "redact", "count": payment_count}) + + rules = [r for r in _BUILTIN_RULES if r.name != "payment_barcode"] + rules.extend(_extra_rules_from_env()) + rules.extend(_extra_rules_from_policy(policy)) + + for rule in rules: + try: + regex = re.compile(rule.pattern, rule.flags) + except re.error: + continue + sanitized, count = regex.subn(rule.replacement, sanitized) + if count: + findings.append({"type": rule.name, "action": "redact", "count": count}) + + for marker, value in protected.items(): + sanitized = sanitized.replace(marker, value) + + return sanitized, findings + + +__all__ = ["DlexMaskRule", "sanitize_dlex_output"] diff --git a/libs/agent_framework/src/agent_framework/guardrails/rails.py b/libs/agent_framework/src/agent_framework/guardrails/rails.py index 676683b5..8ae0a4ec 100644 --- a/libs/agent_framework/src/agent_framework/guardrails/rails.py +++ b/libs/agent_framework/src/agent_framework/guardrails/rails.py @@ -25,6 +25,7 @@ from .calibrated.output_sanitization import mascarar_pii_output, sanitizar_toxic from .calibrated.rules.pinj_patterns import _PINJ_PATTERNS, is_obvious_injection from .calibrated.rules.tox_blocklist import _EXPLICIT_TERMS, _THREAT_PATTERNS, is_obvious_toxic from .framework_llm_client import classify_with_framework_llm +from .dlex_sanitizer import sanitize_dlex_output from agent_framework.workflows.input_contract import has_meaningful_unmatched_policy, has_semantic_classifier @@ -832,11 +833,64 @@ class DataLeakageOutputRail(Guardrail): } protocol_authorization_verified = True + # DLEX_OUT is permissive for recoverable leakage: when classification + # rejects the candidate, mask only generic sensitive values and re-run + # the same classifier. The agent/domain remains unaware of this detail. + # If sanitization cannot change the candidate, or the sanitized version + # is still unsafe, the original blocking behavior is preserved. + dlex_sanitized_text = None + dlex_findings: list[dict[str, Any]] = [] + sanitization_recheck = None + original_dlex_result = dict(out) + if not bool(out.get("allowed", True)): + policy = dict(getattr(self, "_guardrail_policy", {}) or {}) + candidate_sanitized, dlex_findings = sanitize_dlex_output( + original_text, + policy=policy, + exclude_values=matched_expected_protocols, + ) + if candidate_sanitized != original_text: + sanitized_classifier_text = candidate_sanitized + sanitized_classifier_ctx: dict[str, Any] = ctx + if matched_expected_protocols: + sanitized_classifier_text = _mask_authorized_protocol_values( + sanitized_classifier_text, matched_expected_protocols + ) + sanitized_classifier_ctx = _mask_authorized_protocol_values( + ctx, matched_expected_protocols + ) + sanitization_recheck = await classify_with_framework_llm( + _llm(ctx), + "DLEX_OUT", + {"text": sanitized_classifier_text, "context": sanitized_classifier_ctx}, + profile_name="grl", + component_name="guardrail.dlex_out.sanitization_recheck", + generation_name="guardrail.dlex_out.sanitization_recheck", + ) + if bool(sanitization_recheck.get("allowed", True)): + dlex_sanitized_text = candidate_sanitized + out = { + "allowed": True, + "label": "SANITIZED", + "reason": "DLEX_OUT sanitizado e revalidado com segurança", + "original_reason": original_dlex_result.get("reason"), + } + metadata = { "mechanism": "llm_rail", "data": out, "calibrated": True, } + if dlex_findings: + metadata.update( + { + "action": "sanitize" if dlex_sanitized_text is not None else "block", + "sanitized": dlex_sanitized_text is not None, + "findings": dlex_findings, + "original_decision": original_dlex_result, + "sanitization_recheck": sanitization_recheck, + } + ) if matched_expected_protocols: metadata.update( { @@ -850,7 +904,7 @@ class DataLeakageOutputRail(Guardrail): code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "DLEX_OUT avaliado"), - sanitized_text=text, + sanitized_text=dlex_sanitized_text, metadata=metadata, ) diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index cea3ca9c89e27e5ba5ee1b8281201ba7b42a4b29..43e7ad7ae927c1fb13bd45685094fb58b045cbd6 100644 GIT binary patch delta 64 zcmbQpJd2t8GcPX}0}yDMuARvJRHITqH?KrLMc+5rGd`dwKPxr4q*y;YDJ4liF+DY} SBtAJmuOzWJ-e@vEV-o;%6BO_O delta 55 zcmbQmJdv6EGcPX}0}vQ!t)9sJRK7qzH?KrLC9xz?KQSpWFC{-OzBn~EF|Q;uIX*43 JVzLBd69CP~5;*_> diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index 7ed3b8000c410a9c00afc0c5831b07d5c1ef74c2..c2ca0e7533534d8399fe64f3a78e1dc76e67c8d4 100644 GIT binary patch delta 66 zcmX@Ba!rN%GcPX}0}yDMuHDG(#H>-RpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;Hahxs8N0I66O7ytkO delta 57 zcmcbna#n@=GcPX}0}vQ!t=`D(#4KN=pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+Tj4`5_+w{WKF{ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc index 0f857e2848227f76ac901690ee710fd94c2d5795..257c4153cd81847c0c2d7763dac1f797054a57c4 100644 GIT binary patch delta 66 zcmX>oaY};wGcPX}0}yDMuHDF8&a6?cpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IFA@c$b0H(+m2LJ#7 delta 57 zcmX>laZrN$GcPX}0}vQ!t=`C8&Mcp&pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+RK)^8yY4`kNC_ diff --git a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index 385eeb5ccdf375be02163f37fbe5d80caa5f3370..03c55fa72c5ae596d477d4311edce450ee0c1c27 100644 GIT binary patch delta 66 zcmaE)@>+%aGcPX}0}yDMuHDET%&bwVpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_miZ|k0JkI-W&i*H delta 57 zcmaE@@<@gIGcPX}0}vQ!t=`BT%q(A^pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+Ti|`6(X&2@Vsl diff --git a/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc index 2c9423c896ff7eda6f517aa24fc4dda292215725..8f8ba56a4d9ddbea53fcacc7ae0d195c560c8eef 100644 GIT binary patch delta 20 acmaFE@`i=`GcPX}0}!a1uHDF;$OHgAy9Fr# delta 20 acmaFE@`i=`GcPX}0}$NGS-g=ukqH1p#s&)j diff --git a/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc index 5b6c9bea97c6f1d3443090e67e02bb68ac6f9cd8..04d6239795dc9c3606e20d6a3136cbd263f17f86 100644 GIT binary patch delta 22 ccmca~o$1nbChpI?yj%=GplZ5yBlq#u0A5E2t^fc4 delta 22 ccmca~o$1nbChpI?yj%=Ga4To=M(*RQ0c27LkN^Mx diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index a8f8bd445da0b4df9230c8f82739f1ff76b5dbbf..bf49c5979a47dce055acd252abcb936e65b8e152 100644 GIT binary patch delta 19 ZcmaFN_?VIVGcPX}0}!a1uARtz0{}Mv1!e#M delta 19 ZcmaFN_?VIVGcPX}0}$NGSv-;Z1^_%L21oz^ diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc index da332e3a3dafa1075d3483a5b045fbf871d4f3d5..08fc703bd848abcc4fe8682ac273a72072f71ef0 100644 GIT binary patch delta 20 acmbQIHBXECGcPX}0}!a1uHDEjED8WN_5_{) delta 20 acmbQIHBXECGcPX}0}$NGS-g>3SQG#~0R?~n diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 1e1a85bdf29402da666678e6963524da678698b0..be306050e0f4620d050608474792ecebfc772069 100644 GIT binary patch delta 19 ZcmaFO_?nUXGcPX}0}!a1uARvJ001~A1%3bk delta 19 ZcmaFO_?nUXGcPX}0}$NGSv-;Z0RTNv24DaH diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc index 0569e929e37bbff41785643ab650b593faa09fcd..2ac1c47549192a6d0647617bedabb2edf2aff1d6 100644 GIT binary patch delta 20 acmZoRZZ_ur%*)Hg00gS0Yd3ONNCE&g1_a{( delta 20 acmZoRZZ_ur%*)Hg00g&k7H{OPkOTlb5e3Bn diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index ee50b32c8475b42ab90941e0988cf89cba8c40fb..cf04890af0b6cf42657b642129641ef45df251a9 100644 GIT binary patch delta 20 acmdnSwT+AWGcPX}0}!a1uHDG3%?bcECIprM delta 20 acmdnSwT+AWGcPX}0}$NGS-g>3n-u^&F$H)4 diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 7487f111cdb60a86e419fd29b9db322657f4da4b..090fb2e2626d6425726232d01b0c74ba3c8d31ba 100644 GIT binary patch delta 20 acmaEA_tcL2GcPX}0}!a1uHDETB@X~Z=mn(! delta 20 acmaEA_tcL2GcPX}0}$NGS-g=uN*(}8^9F|i diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index cdc86752ac40eabbf44f752e55db8f41eea34a22..ff5a0c2a87be34fdbba1878e972e70aa535735ac 100644 GIT binary patch delta 20 acmeC@?C0eE%*)Hg00gS0Yd3PYumAus=>(nt delta 20 acmeC@?C0eE%*)Hg00g&k7H{NkVF3U(^aX$b diff --git a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index e915a97034ede7a3a852c7562c6a69c8b5b54851..3cb62fd0831d3fb04035e75a29a1e9a33d945891 100644 GIT binary patch delta 20 acmeyu@`Z)_GcPX}0}!a1uHDF;$pipC`vpM& delta 20 acmeyu@`Z)_GcPX}0}$NGS-g=ulL-Jt1_mPl diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc index d86c644c398374b02da958b1710a32ec10fb0a46..e760abd24f8ebecf19ad70331dd49449522c39a9 100644 GIT binary patch delta 65 zcmcc0e2=_?Wl%JKFTvDu`os^QKpO~JS TR}!C`pI4Gt9B(u^n6U`}e-RZK delta 56 zcmcb|e3hB|GcPX}0}$wIt=`D3$ta(#pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% KSur_=u?Ya#2@+%g diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 3f8b7f64ffde4ba57377abda795fecedb91244ae..f80dc48ba6b7478a5e143e45f98fa05543e9017b 100644 GIT binary patch delta 66 zcmcb>bCZYrGcPX}0}!a0uHDFehEbzHKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUH^wLy0Jhu~UjP6A delta 57 zcmcb~bAgBZGcPX}0}$wIt=`CehEYCUKR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Lvtsjq#wZp52u~BL diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index edc92cf457a1fb8c763f9a6c0bc101d342c873e7..1a7095ac30c7a0560bb1b747b35de676cb2d8ec8 100644 GIT binary patch delta 20 acmaFk@y3JuGcPX}0}!a0uHDF;r~&{-bOqD^ delta 20 acmaFk@y3JuGcPX}0}$NGS-g=uQ3U`^cm}lq diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc index b551d192f147dafec9c2c97ec7c9aed0c7cbace7..1fe1d943f18f1f59c3369d55aed3432a69d22472 100644 GIT binary patch delta 22 ccmZp9&(!jsiTg7zFBbz4sF|+a$X&S|095D)0RR91 delta 22 ccmZp9&(!jsiTg7zFBbz4+{#(Jk-Ks`09xz^;s5{u diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc index d598db127c1e1f98957c8f586292e4a1f93cf102..52218b3d024463491e8bace30a5edb82894312a7 100644 GIT binary patch delta 66 zcmbPZwbY9HGcPX}0}!a0uHDGJhgqXmKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUGiEbM0J5$YH2?qr delta 57 zcmZ2#HOGqkGcPX}0}$wIt=`DJhgrTvKR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj LvtsjmW;00u1eOzk diff --git a/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc b/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc index e00606f177d0dfd6d2ae307cd300c6b825612b25..c48d691bcb17a20c5b69d812f936702828273ade 100644 GIT binary patch delta 66 zcmey&`G=GHGcPX}0}!a0uHDF8!K_iJpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF5py6T0Je@5@Bjb+ delta 57 zcmeyv`I(dZGcPX}0}$wIt=`C8!7QJlpPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+RLFb08xC2$~Z+ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index 99e6e2618558ad08abf14c0c87b4e4ff52f95809..83879cb7224802f1369da89a4e47656b560b23e8 100644 GIT binary patch delta 65 zcmcb_e4Cm3GcPX}0}yDKuHDG3#i&uDpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS TR}!C`pI4Gt9B(u^gs~0)ekTb>KSU)={B}qRq WJvFZ+J~==_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_i0M8n0HBW*?EnA( delta 57 zcmca5a8ZE!GcPX}0}$wGt=`D(!6culpPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+TjA={_p}^^6lW diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc index 4932ae5d9e15d4df9be758021802a4bb9ebdf228..3336748010d6170ea58ceb31811fedae1833512c 100644 GIT binary patch delta 66 zcmeyX(x%G&nU|M~0SGiq*KXt%VbUnk&&?~*Pto@c_KXiG%FjwoE-BW}PD)AAPfSnE UD~V6e&nrnRjyKwD$7C-60F}EG^8f$< delta 57 zcmZou{j0+LnU|M~0SNT8R&V4MVUo|*&&?~*Pf09E)K5%G%uC77i!V;iP0TCFOpZ^> Ltk~?%WG?^!>vIx2 diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc index a7955344ad56cccb65a4d6e284388085b3fb1d97..679ede05199d3c22660234ce2f050ad893435e1b 100644 GIT binary patch delta 66 zcmZ3dvt5V#GcPX}0}yDKuHDF8#H3NKpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF4$~xI0HvlC`2YX_ delta 57 zcmdn4vrdQmGcPX}0}$wGt=`C8#3Y}mpPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+RL3(J1HeeKQTQu UuOvP>Kd&UQINoUUORicS0Q5N*n*aa+ delta 57 zcmaE#_biY5GcPX}0}$wGt=`DJpG!VZKR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Lvtsinu38-cJc|_L diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index 23d72b7f165feceef367c262cd88133b1e0c8772..6a7f80bcd768d0cdcc03da35a8b51e818c5ad44b 100644 GIT binary patch delta 65 zcmaFC@sWf3GcPX}0}yDMuHDEzmsz7+KR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu TuOvP>Kd&UQINoUTapr{pqfQoD delta 56 zcmey!@q&ZV diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc index 6aa97e0f7d88b0725af5ca09e21fc380909d4316..9f8133b7581babf65520f27f2a99d4cff66c6392 100644 GIT binary patch delta 20 acmdmKy3>^VGcPX}0}yDMuHDGJP6_}$+XapQ delta 20 acmdmKy3>^VGcPX}0}$NKS-g>ZofH5=(FSb* diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index 5f9e9dabfa56f9be696d4b70ab95780e9ca966eb..3037dbf93d39614602812c9e0346cd887a80ee99 100644 GIT binary patch delta 20 acmcb{dySX-GcPX}0}yDMuHDFenhgLvuLXes delta 20 acmcb{dySX-GcPX}0}$NKS-g?^G#dayr3PRC diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index 36f94e18d7657228d70ff731d0dc5ac07e738425..439d80f766c9d965d5f4bd2a5b06a0d7726753c7 100644 GIT binary patch delta 22 ccmZp9!r1bJk^3_*FBbz4Xqv9w$o;br08`}$CIA2c delta 22 ccmZp9!r1bJk^3_*FBbz4+|F6Nk^5&M09o4z1poj5 diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index a71ccf2d4412fe06a3255a7a2b85fa42a09be860..b8449b8f52e57f5f71bb86bffd4be7c7d3c20a7a 100644 GIT binary patch delta 66 zcmZ4BxYCjPGcPX}0}yDMuHDG}k5QveKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoTp22+I`0K$?M7XSbN delta 57 zcmZ4KxWJM7GcPX}0}$wIt=`D}k5RrJ1HeeKQTQu UuOvP>Kd&UQINoUUd*&B90O$@E(*OVf delta 57 zcmaEmcs-H(GcPX}0}$wIt=`Celv%z+KR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj LvtsiP<`+5uG9ncl diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc index caee3df39f97b416d4b749c36aadc871a95169db..2562f26cb36c73d4bb13cd48da95faa1087b490b 100644 GIT binary patch delta 66 zcmZ4KwaJV7GcPX}0}yDMuHDEj%cN1GpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;JQo#~Mh0HU20<^TWy delta 57 zcmdnwwbG0GGcPX}0}$wIt=`Bj%Oqc{pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+O~Q>5&ou_jVI5 diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc index 87f406dd6ace10e1e1eeef5c608389f74cf74d1a..23837c6fc10ac234ad61be590d2e4fc723556ae2 100644 GIT binary patch delta 63 zcmX@lc!QDqGcPX}0}yDKuARtjtWmC?n^&TrqVF5*86QxTpOu Ltk`VEoWKqM&O{Nn diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc index 3095e2ed2ae70eaa8dbf7dbad2e8cb5df9cebaa2..bf56e96e121d63e7e2824fc1ff4a6d9e61295a63 100644 GIT binary patch delta 20 acmeyu^@WT3GcPX}0}!a2uHDF;$qE2J9bGcPX}0}!a2uHDFeQ3e1**9HFo delta 20 acmca(dB>9bGcPX}0}$NIS-g?^q6`2@%?928 diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc index 4dd047fb91b2d6efa7c79b50c948ce9b7589155a..88fdd0d5ebc5728da6d8a59d89aaa93a6d45fd15 100644 GIT binary patch delta 20 acmdlavq^^gGcPX}0}!a2uHDG3&IbTEB?P$u delta 20 acmdlavq^^gGcPX}0}$NIS-g>3oeuy$8wHpE diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index f8b4db195dd31bac9b6eefce0875c8b713dcbc72..54c0264593e63bd8060aafd44492ba66eed9e7a7 100644 GIT binary patch delta 20 acmZn>Xc6H4%*)Hg00io$Yd3QLWCs8-J_Qf} delta 20 acmZn>Xc6H4%*)Hg00cL47H{PK$qoQFGzISf diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 1737199e4b5233b676c5d1f31b100a1a2eb74c8f..6d0d5f59287bb6c8b3c61c51081ea5e53c152348 100644 GIT binary patch delta 20 acmey&@|lJEGcPX}0}!a2uHDF;!2|$4;RQed delta 20 acmey&@|lJEGcPX}0}$NIS-g=ug9!jc*9IQ| diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc index 0adfa30d5d1e1d2de246e65447c1d107768a06e3..7aaf3326e5348a14706faccf92ec94ceb93e8bdc 100644 GIT binary patch delta 20 acmZqSZsF$s%*)Hg00io$Yd3ONvH}1vhy;WH delta 20 acmZqSZsF$s%*)Hg00cL47H{OPWCZ{~VGcPX}0}!a2uHDGJLIMChO9gNM delta 20 acmdmNxY>~VGcPX}0}$NIS-g>Zg#-XXK?Y9% diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc index 8b8e1e10fb2d3575334785f6caa0d3bd4ec73275..9b8ac37bfc661ac2bbac1f24fbc0d594aef27e3c 100644 GIT binary patch delta 20 acmeyQ{z;wtGcPX}0}!a2uHDG}To?dEwgwac delta 20 acmeyQ{z;wtGcPX}0}$NIS-g?^xiA1qtOoM{ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc index ef635b56ecc909f82a7e9ec76afaa0d4d64b5a3b..ef4d76febe2e05d6342db1b8a293f3cd924e63c9 100644 GIT binary patch delta 20 acmcaAbybS{GcPX}0}!a2uHDG(#R~vER0SOX delta 20 acmcaAbybS{GcPX}0}$NIS-g?kix&VxN(KA? diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc index a877ac2ca0893d39169c0acfc4f2dc7745a3f309..209e518930f35da9d2c0ce8ea47e51716bb25c55 100644 GIT binary patch delta 20 acmZ3(w}y}VGcPX}0}!a2uHDG3$PNHCHUxwK delta 20 acmZ3(w}y}VGcPX}0}$NIS-g>3ksSa#ECpi# diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index 6d765162bf91ab87aadf217533eb9669df2477f5..2518acb9a21b55c8f312b4c3f99c7fe69fa171de 100644 GIT binary patch delta 22 ccmX>*llkmSX710tyj%=Gpl-T$Bln>*08~;3NB{r; delta 22 ccmX>*llkmSX710tyj%=Ga5HD|M(#sr09r^0CjbBd diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index 2446025531d61ad41fb114c373262163e5497d3b..374c66c0fafa53b8121bfb40c23a25c58b8f1b19 100644 GIT binary patch delta 20 acmez3{Kc93GcPX}0}!a2uHDG}QV{@21O{LL delta 20 acmez3{Kc93GcPX}0}$NIS-g?^r6K@M`UgJ% diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index f503e3a187b4041fb9b46c426f795c2fe4559c33..73fd59ea7f916c902fa03aa7bcc42a6a0384720f 100644 GIT binary patch delta 20 acmZpcYL?>u%*)Hg00io$Yd3QL-~j+Kg9Ro4 delta 20 acmZpcYL?>u%*)Hg00cL47H{PK!2@~ delta 22 ccmaF+jOpz&ChpI?yj%=Ga4To=M((7|0BS4=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_k5SYT0K$A03IG5A delta 57 zcmZ2ey1114GcPX}0}vQ!t=`C;!YE&^pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+RLKqo^eS5?m8g diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc index 6f0c89e4d316b87231c641d658fbe6c99f1267b9..7849c05a935654cf954eaf26dc3da056219260fa 100644 GIT binary patch delta 20 acmX@Xe1e(#GcPX}0}!a2uHDGJmk|It1O+?* delta 20 acmX@Xe1e(#GcPX}0}$NGS-g>ZFCzdy0R|iZ diff --git a/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc index 1968a29db873f5970f1124e908c34c17429c245b..b6c3eb30aad6ac6035a129160b7455493203195e 100644 GIT binary patch delta 20 acmcbwcwdqGGcPX}0}!a2uHDFeMF0Rm;|0tB delta 20 acmcbwcwdqGGcPX}0}$NGS-g?^iU0sc;0CM! diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc index c9d635ac0b00452d49841cad12e6ca3c124e6e88..83b55a515bfd33a128b65dd93f48fa56a7a84e63 100644 GIT binary patch delta 20 acmX@kdYqN}GcPX}0}yDKuHDGJhY0{VECoLR delta 20 acmX@kdYqN}GcPX}0}$NGS-g>Z4-)`CA_g7+ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index bdf965d5ea2e7e233093c29a6562ab0a5bcbf657..1817712bb8c881209220775ce4b8a495c608d3d1 100644 GIT binary patch delta 20 acmexn^v#I-GcPX}0}yDKuHDF;EeQZc?*+X8 delta 20 acmexn^v#I-GcPX}0}$NGS-g=uTM__ET?O+1 diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc index 835fcdfa4cbfdcfde21b0100f28f6e51a169d579..bdfb59699bef8ad559da69c4fec801cea320093a 100644 GIT binary patch delta 20 acmZ3Mv@D7HGcPX}0}yDKuHDEjZ3qBBM+HRy delta 20 acmZ3Mv@D7HGcPX}0}$NGS-g>3+7JLnJq9EI diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc index eaaa2b5808805ad35294bf3867308d9f36f546bc..72b822106729185a4460e4ec597b84217ec72e2a 100644 GIT binary patch delta 5695 zcmb6-TW}lKb-Q>G@7Dq(NC4!L1RtRI5GB!iSd;}45J?jRC@hu|OFP>Yu%uv<3wi-a zwrUmrNLwn6BPwghV4IF8m#LwS98pnHyPk|A*V9g$X~)27 zfp9Vc<>Z8nQxGQpS4ziso>LQMLe+%4pcsN;pm7?2vk-N=a9YAjs3C^a5gI}cep*7; zuV_Z#kH82$q`e^H^n{@a;S7Y4GcL%zwz~$ER@;JB6-y8rCr1$UVF6&@L`Wop@`H_- z2_4$z!XZx1Uu|+ehmvUbwrE_2AOna8AyGo61UNztAuA(M44ILqWCKV_h9To`1Du=$ z=0PecJ9ZX9pyZShLFOPzYJjd#^2<#&{y*w8ZbEkKjcu-dfUE8?E}6vTP}WR)vI;H# zZk$Z zUK|>-n55&o6yf-(NO%edc2PB5soe}`#siT6zD&nnq!$=z9()+@nw}iXQj;ST5s1u8 zgvPpd%iy>#>Gq0<#F@_U~35 zj`#x+d}J06WoLrjwKetPUHiMdy?BawDZ{WaW|s^HLJ0Xn0RRWXgM&f&1;mN6iO?4y znh~!;R6q}k>Y4{dRn>!{X2*kGrC7(j%)~Njz&FpNv$6BQZK6EEra2qbrpzoaGaS$* zk7YPf!6h%IMB{vdOQorrouV9Kp($`PIRg~4QOjI1#>7D&e7y%^`{cR!xd^>LFx-Vi zjOqU-Q#PFf=L1RoCjhS@6&0eLnhapeyP?*5YdP|7GQN6%fNshPmieAxv|aI$x)H*T1>e))KL+=&)?kj1veWNC2V^IZEnyC! zdqHN{&QBn(fE+Xgw;!}xexjWK5As$O`#2;~9(CAw)~X%{=}Sh)p9a)#C{+Bv6n>iN05*5!9;gG2Jk5JLs=>P>sC!`owj0VFaqB11??a<*U z@%t;$VC zrWFe45F7Re=r$n(Wmo`SLq6BpawgkTea`zsYcozL2<6ar2-W!a@TdqCK zp?kQ#;l%8doJchF%K|8?Ay8I3fUr6OVfADkhK!-(I4Cm=gp)K7E|@W-6Wt>v08m2g zZD(VE&kgx%+r%14U8L_ZK5)#WMjC6|fFq=l)lY#_L~7>-YOJKXMnCpTkVIN{g;+B* zZpO4z3xTUD`Fo-TP7I0+%}$YFG9m|ewwpnLaV;vy6d8nti!5>w)?P|8X^sp@IRvlG zSqp6%aFb@Dl|U2-YbG@jc-Hqc(tP%N(vr9HBx@nHL|Y%YuUKu(9Ynj7Tj45>*2GE6 zoU7KarbPZPFx}I7Fr#H>exQ?R3!`9TEMf1CHjlIo_+D-N_pwGA9L7EJ9YDSlShag2 zwx=d7wKd~lyQkW#z9aRq-G{oX#sv9I27|$S`!RqsGo!b}je> zFmV{Q@V~~~I^T|vg@1tcYqrZl(>&i|^PpbdZ}S|;oC8dukN-M}&nFYGWJ`VgE-ZE8 z3@8@QaS0BmVHKE=YC{^JasGQMPZnpEa9=P;`66TFR3H>3#QMn*ki94TU@&oNmhy*3 zB|=m*O9Ubsjs&RFWFRstV!K(R;b|(w`GFmxZo4dlw~DB&a%-hODjGN&71f|mL?^=8 zkf?-WlOj4P8be`!fC`+Q_JxQEa4p#bA>T+aK)r}V*R$V-T3S&(F%ok_^9#lG$9<Cp@s(J&Q=`ux7APr4vQb!{kG zzTqaX46OYz7a4arB=3mwFd5~>fQvYI3udy<0O0uV8qW!@s)i zD_K;-&ICXm5EW@I!-&dxdNIXh4g!(A7D;o$nA&9)4%UKSNK^xjKX2H-j11SjSho>n*?YcvtN+%7xx@wQqiEoW#giJkzqw;D$OO9xgaEH!PACIr!FSa z=SBGflNPlJ5WoNlYH<#{-Vjj@uovjX3{$O@74bwY?X~io_V%n)afq6o=a6WQ(QJ}U z#OOsT!7gXgDNzrr^LTBfldxbEXI>VS@S&6v<)Hi!)wMT_sDM~ekw`O3qKQp%OY|Zr zg5uKqg#+mbk?KiUgyZ}_;u!zm_MW=S5d8}U-&McV<8YN6gGI;SX1m~cYGvXx%(YeD zR*|9hAo@@)Z?IMnxy4%1A{IOU^ZEs`Z!Povv6Q$%IoNn!b0bC#R*9+ z#DcHe-f`b>ms)+rR$r-g;$F1k2Bd7I8PXi3*1>$V(gK-r=$G-5t2eKxv_eE1G&Opw zYt!>y-<`gKZ?-T;2|a%(bkLP{K!4;xaL>kA-hX@Q=2YS7seeujo~Y15Znbr6EL7c6 z7>*R8q|iAdw4Hg-1!Pu&l^(?DTA9q2TUu8FWo!M)=$5NvRb9p$x$*V0MOVLo^_QJ3 z*N4}J^Vs|9d(PsKDZv?9^_OjpZw0Rf*KPN0o#m$H)zRPBTxG0zqrK4il+boqzz%OU zb(S5ozZp6qIG_31Y(BbP|Ks4D*`o8ALet5L9Bmwdf#|FUfoQMKf-q>`mw&3*cA(UD zq}X;uXggMFJ6UWyDYT8On#x#P3F|9jeff9^J6OaHmOFCJP6&ZDd0vcpqybQc}n z`If@5XN$+q2#&L>%Cgy+d#Px~S9N8~nVT$P-T83=JM_z@dx3vFR606cJUabZYv-CT z@4FT#+yB6wd$DNmDfG_>_L=*0Okw`T`%5pC=D6Y<_gUw@wNsl7tAVoF{+9llK1Y_! z?O%EkcPDV38N2dF;9wQn&kFUk1@mlKZ@L=15-c=6bI*QXKelCS=7$@)vZM?#y01`= zW@L!XkyBpRty=}m^V>63^j8l!6mgwIf15%9+w0W-q&mS#{zx6_B(3sK`i-Q)_{nj9 z4;b9vj{*3pK?$K%OX786U7VSxGmGg{;N7l)StbQObuiqUFlT8OIC}X!#oql@gP=0s z0x0fX2z~^?Px-f-Gu{o@h0NR?2z~~^T?l>-!7m{AI|$z6Pquhm@563Y^&*`HA1Yp< zmQve7{A(@tA@C`yDkYk>=VlTM=g%d%6!!rn!I{1FqK=~%s8sdK0GH=~-O``E0m-Wn zT!Vmz0DhI=egMIDA^1K7Y6xIz=l%qOKZT$l08s;Vf^RP1@_HSNy%=`@(j<$%Ugb|9 z?r$K-0|4&;koUR2M?g6&qABhsL@MIRSV~l6*hHH9TgVEX240~I6X$*k#~<)F@zZGG z?*HQdAV!CEH>mm7asw!yLF^dT}57%Ja{AWzsDyt;nmY8YQhx za=o;iPk`o2#@%vW?PILcB6fr>X~B0J{8c=ZWEt)!650W6lhy{k;x+M9N4H1RgC(XD zF>t+~PsXbkCcFzd$>IM3d;iAs9i8Z_{0ANFgOwwQ%KA{&q|`o?gI)#pwsA`3@HP(g z9vM%g%8tssjQ_8Wk#1-^r-GB3MJ+|mXW&;sisIm7lDh{1d}44P@}bTo`tSUQogvE^ PIeJp|0FnNrr|oqP1!oNRa)X^5W?`h#LXNMCmcJO zI*^KZm>6QJ)+@w#n1;j~LX)7+fEW+F4()+*g#`ZqXkwL+c;cQER&42W?>XOle%$Np zYa2Is5Vu@o^4cHLS|0HC^CS{V5J)H30sGphR}2C0QO3u+YCxh>gtV9}I~*zm3fE zyhM%!V`5n-4U^U3lxTU%649pOyg>d)j_hnEP5w~XGbK;E_7kMJs`6j1vZwSEc{h?G zcMto*-s;m2{oZPx1oMgQ%sB5Q=QD=_)guQQDQ5EIN@kQ7$>*6dVvc9|B)L9bd6Ei! z9cbXCg0@y!J*6$GD@7VvDll6;HCLKHGjLPEv>f7}=x1>!)Y~wezTWB?R5oqXvK`&3 z84yEBteZ7Q@sVF9Mq^Z{^?(YUo&y1<***sHUiO6NRkSKRxsz?DhvFY+uV-)Meo9UK zn3}pb^vwJ7yMgf5^3~MR_8nKY2U((@WZ1~bH zpQX1AyQ{MeoZb0)@>eNhuqaqZyn<+uFP`lw=Wu!!(L}t7Xd&7N3(-Mz$>OoG&9`uh ztEt@59mA?^Xl?r-hz2j|V3}4!t95#oqqw1r0hon`8=!YDYL;O(E?($>4K_;Hi84(6 zpf@zzfWFDv)p5D#m~8{_5>Q`hZb4gHS=E-v@5kbsuc1$1UkX$JnlRv5d$wP#;z_`iA9Dp=DS~6po@c==b1E-awy*7&OHy=tsc;zzn<% z7jP8ootjO>o@F{v!=-q{RJ0AF4h^(za(nt^-udeI9f7||zCZC?Q`)bCUGJYgc1R^K zoE})0LwMDyY%`PTIcwvbBL8poptXl_mt}T+)9<8<9m{}0uL(>!Xc)Ju1jx0Mxd7mi z!4<>@xY6eOG diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 0472c07d04ebc84bd833839d1ca33901a2beb67b..2903d0c825702d72ebbba6c8d86717b92ef54178 100644 GIT binary patch delta 22 ccmaDjk@4X~M()qNyj%=GpkcapBlk5A09CIB?EnA( delta 22 ccmaDjk@4X~M()qNyj%=Ga4To=M(%4K09&O8%m4rY diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc index f326045174332481c69cffdab0c7d746162e1bd1..11ca557ed2c87504a1b1575d4be72b31d1cd4aab 100644 GIT binary patch delta 19 Zcmcb|c#o0$GcPX}0}yDKuARtz82~m|1zG?A delta 19 Zcmcb|c#o0$GcPX}0}$NISv-;ZG5|Yi1~>o! diff --git a/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc index ae555f559a2fe60a0c521705d876a042da6dafd2..c97830a49e3fcb0375ae1d8701732bc8df8aecc4 100644 GIT binary patch delta 20 acmeBo?sw+?%*)Hg00bJQYd3PYC;|XELj^Jb delta 20 acmeBo?sw+?%*)Hg00cL47H{NkQ3L=#G6oO; diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index 7ba8b55e6485323bd2c594edbb0983357ffb7f17..96c6acb132e92eca605f994a7ddc2722e1ad9f9c 100644 GIT binary patch delta 65 zcmZo*?PTTt%*)Hg00gS0Yd3PAV$`V6&&?~*Pto@c_KXiG%FjwoE-BW}PD)AAPfSnE TD~V6e&nrnRjyIb8g|P KteE_lu?GOp=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;JwFynR(0I1~_O8@`> delta 57 zcmZn`{UyTvnU|M~0SFAVR&V5<$tYiGcPX}0}!a1uHDF8!K6{8pPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF5z`cF0Mh~%-v9sr delta 57 zcmdmAv#o~vGcPX}0}vQ!t=`C8!6aX(pPN^rpORRTsGpdWn3s~D7hjy3o0wOUnH-;% LS+RLF(-dm}AzKtI diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index a09509359fd5f9d5bcf1fae12f7102d9fd6378b5..f8c401b745b7a5c3f0ce9f3f31cb8c5c6d470733 100644 GIT binary patch delta 3134 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 UD~V6e&nrnRjyKvIz--P20CbBKcK`qY delta 57 zcmbOr+%C-hnU|M~0SFAVR&V51W0ud=&&?~*Pf09E)K5%G%uC77i!V;iP0TCFOpZ^> Ltk@jMY|aJ%(1H=f diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc index fea4a01f68927e99e9f7117be2391dc9468b15a7..4e496180f12cd23961b9b46a30bb32934a5a674f 100644 GIT binary patch delta 68 zcmZ2Hk8#aBM()qNyj%=GplZ5yBX=^hMvZ=MUWtB+zHhK+d_YltR%&udv3_<^N|JtJ WdTL%td~$wXNn&xl(dJ&}Y2E Ltk`VMeBK%W9%U2V diff --git a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index 836282a5..f6ec8a73 100644 --- a/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -1218,7 +1218,17 @@ class EnterpriseRouter: or (started_intent and configured_candidate.intent != started_intent) or (previous_intent and not previous_intent.startswith("state:") and configured_candidate.intent != previous_intent) ) - if not different: + + # A configured candidate that resolves to the same intent/agent is not + # sufficient to conclude that the active transaction continues. The + # user may be starting a *new instance of the same intent* with a + # different transactional target (for example cancelling TIM Fashion + # while Tamboro is awaiting confirmation). In LLM mode keep the + # candidate only as a semantic hint and let the transaction classifier + # decide CONTINUE/REPLACE/ABANDON. Without semantic classification we + # retain the legacy state lock because guessing replacement from a + # keyword alone would be unsafe. + if not different and not (self.enable_llm_router and self.llm is not None): return None # During parameter collection, a configured keyword may be present in @@ -1259,6 +1269,11 @@ class EnterpriseRouter: "current_intent": started_intent or previous_intent, "transaction_status": state.get("transaction_status"), "tool_name": active_tx.get("tool_name"), + # Structured arguments are part of the identity of the active + # transaction. They let the semantic classifier distinguish a new + # instance of the same intent from a continuation without relying on + # domain keywords or hard-coded product names. + "active_arguments": dict(active_tx.get("arguments") or {}), "missing_parameters": list(state.get("missing_parameters") or []), "configured_candidate": ( { @@ -1285,11 +1300,15 @@ class EnterpriseRouter: ) else: system = ( - "Você decide apenas se o turno atual continua a transação ativa ou muda de intenção. " - "Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " + "Você decide apenas se o turno atual continua a transação ativa, substitui a instância transacional ativa, " + "ou muda de intenção. Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " - "Se o usuário passou claramente a perseguir outro objetivo sem desistir explicitamente da ação atual, " + "Se o usuário continua com a MESMA intent, mas claramente inicia outra instância da operação com alvo/argumentos diferentes, " + "retorne REPLACE e informe a mesma intent e agent. REPLACE significa substituir a transação ativa; não significa estado novo. " + "Não use REPLACE quando o usuário apenas complementa/adiciona itens à mesma operação (por exemplo 'também', 'além desses'), " + "nem quando apenas reformula ou confirma o mesmo alvo: nesses casos retorne CONTINUE. " + "Se o usuário passou claramente a perseguir outro objetivo/intent sem desistir explicitamente da ação atual, " "retorne SHIFT e a nova intent permitida. " "Se o usuário desistiu explicitamente da ação/transação atual, retorne ABANDON. " "Quando ABANDON vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " @@ -1323,7 +1342,7 @@ class EnterpriseRouter: if abandon_only: if decision_kind != "ABANDON": return None - elif decision_kind not in {"SHIFT", "ABANDON"}: + elif decision_kind not in {"SHIFT", "REPLACE", "ABANDON"}: return None confidence = float(data.get("confidence") or 0.0) if confidence < self.intent_shift_threshold: @@ -1374,7 +1393,42 @@ class EnterpriseRouter: mcp_tools=mcp_tools, ) - if not intent_name or intent_name == (started_intent or previous_intent): + current_intent = started_intent or previous_intent + if not intent_name: + return None + + # Same-intent replacement is a transaction lifecycle event, not a new + # conversation state. It cancels only the active transaction instance + # and re-enters normal routing for the same configured intent. + if decision_kind == "REPLACE": + if not current_intent or intent_name != current_intent: + return None + agent = agent or str(self._agent_for_intent(intent_name) or "").strip() + if not agent: + return None + return RouteDecision( + route=agent, + agent=agent, + intent=intent_name, + confidence=confidence, + reason=str(data.get("reason") or "Nova instância transacional da mesma intenção."), + method="llm", + metadata={ + "transaction_interruption": "same_intent_replacement", + "interrupted_state": state_decision.next_state, + "interrupted_agent": state_decision.agent, + "interrupted_intent": current_intent, + "interruption_source": "semantic_classifier", + "configured_routing_hint": ( + configured_candidate.intent if configured_candidate is not None else None + ), + "raw_llm_answer": answer[:1000], + }, + domain=self._domain_for_intent(intent_name), + mcp_tools=self._tools_for_intent(intent_name), + ) + + if intent_name == current_intent: return None agent = agent or str(self._agent_for_intent(intent_name) or "").strip() if not agent: diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index 4986b94191156131b9eea41fb595291c682847df..ac1069dc81a17bf1c4e4f568f648cb806babb0ed 100644 GIT binary patch delta 63 zcmcc0bdQPqGcPX}0}yDKuARueR--~cH?KrLMc+5rGd`dwKPxr4q*y;YDJ4liF+DY} RBtAJmuOzWJ-e}_WIskZU75xAJ delta 54 zcmcb|bd`zwGcPX}0}vQ!t)9reRz6=pH?KrLC9xz?KQSpWFC{-OzBn~EF|Q;uIX*43 IV&a230MS$vM*si- diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index d43e74772c51ef7e62cd087e8412fd2a17c2e4c6..61dadcc3d9dbf487bede12c43b15d7b51a6f8cff 100644 GIT binary patch delta 6222 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 K@EC6-_xv~choMFQ delta 6022 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 diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_context.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab1dd270b0cd5e9e73afa2c26b3434cb707393e8 GIT binary patch literal 8445 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 literal 0 HcmV?d00001 diff --git a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc index 506ac4d157842ac5edde238a50daab675f5886d5..75b492e0773bbe75e51173445a83331cee0ee67b 100644 GIT binary patch delta 68 zcmew{gRyZYBll-sUM>b8&@f%Qk=un?qfI|IuS7pZ-#6GZKAb>KSU)={B}qRq WJvFZ+J~=b8Fwk1Pk=un?zE(dsuS7p3u_RGHF)1-GB|k5|I5jshuOu@$ NJ}t9ib2&4YCjcr(6O{k} diff --git a/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py b/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py index 3fa365af..dbae2498 100644 --- a/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py +++ b/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py @@ -3113,21 +3113,33 @@ class AgentRuntimeMixin: self._normalize_transaction_lifecycle(state) # Uma transação em coleta/confirmação não pode aprisionar a sessão. O - # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT ou - # ABANDON. Não existe interpretação lexical de desistência no runtime; o - # runtime apenas aplica a decisão do router e limpa os latches da ação. + # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT, + # ABANDON ou replacement da instância transacional na mesma intent. Não + # existe interpretação lexical de desistência no runtime; o runtime apenas + # aplica a decisão do router e limpa os latches da ação. active_before_interruption = self._active_transaction(state) interruption = str(route_meta.get("transaction_interruption") or "").strip().lower() - if active_before_interruption and interruption in {"intent_shift", "explicit_abandonment"}: + if active_before_interruption and interruption in { + "intent_shift", + "explicit_abandonment", + "same_intent_replacement", + }: interrupted_tool = active_before_interruption.get("tool_name") self._finish_active_transaction(state, "CANCELLED") state["transaction_pre_validation"] = None + # Generic framework boundary: consumers do not need to know why the + # previous transactional context was invalidated. This deliberately + # avoids leaking same_intent_replacement (or any other interruption + # subtype) into domain agents/workflows. + state["operational_context_reset"] = True + if interruption == "explicit_abandonment": + action = "cancelled_by_explicit_abandonment" + elif interruption == "same_intent_replacement": + action = "cancelled_by_same_intent_replacement" + else: + action = "cancelled_by_intent_shift" state["tool_policy_result"] = { - "action": ( - "cancelled_by_explicit_abandonment" - if interruption == "explicit_abandonment" - else "cancelled_by_intent_shift" - ), + "action": action, "tool_name": interrupted_tool, } diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index 26baa6268843892ba0f4e94b6f142d4d6df77c0d..4758e940d7689f4c44d92180adf3f58c424bae66 100644 GIT binary patch delta 19 Zcmcc0c$Ja+GcPX}0}yDKuARtz3IH_=1wQ}) delta 19 Zcmcc0c$Ja+GcPX}0}$NKSv-;Z6aYFX1|I+b diff --git a/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc index c1716a172603309056589f5fea3bce23581dd11e..61119b566612ee11762b72e2fc85071c7dbd6751 100644 GIT binary patch delta 20 acmezB_0@~}GcPX}0}yDKuHDF;r3wH^GX?el delta 20 acmezB_0@~}GcPX}0}$NKS-g=uOBDc5Fb38D diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index 568462bf6ba90c3d66770d4fe20db1309f16203f..73d0402c7d099c66766f3b669dc2d51c6a652833 100644 GIT binary patch delta 19 Zcmcb^c!!buGcPX}0}yDKuARtz5dbz01yle4 delta 19 Zcmcb^c!!buGcPX}0}$NKSv-;ZA^VgqR+|;)yQeFbS9aYP5NPcHz7V9eKHAnv12Ss#YrNHG9d|ZVe#&cC=r3^oWry zP=JeJ02X+}9R-*_;dc}Nsendco#dD}>8h7l zj+J4s30e`5pMpDAX5QX^pWW{n*~bsIix0mQjh&*ghpR8qzL5N$${YyiNDnhKKLi9Q zkpbQW+4>WkWa*dS2pnnt3QnRpuSvQ%WI6DqHg9)MK-f=TNjD|e=C|yUt&?H;L%IO3 z(Z5n(*i9bejyxTb`(TdF$w`=RR^_k0Q`ecUcC=dtN;PXuC#>mobO>s6H9DSDeQuxj zQShCq0hh1$)3t!Ip*&Y+>owiYSr^%vo(N8W=yrqD9K^%SGtB$5i)^p`Pxx_V$@a{1 zUQ9*g2Inyjt|~Cb+%sNAd*}DOIE}^Pu#=`^eG!#wmpSA(%+f+EeP@Bu7vRtr@(Zm5 z7`wVTvmGDZL*p$0Ou<{QB^LLg@jGI5Qb1?GVVku6|NxQ{Tl3Gzx zq&k_6(|}QRvpJ_di<}D3h9Z6tp$a5!@uel^qieH}5WU>{%3O7h!ks*lCpPb3B!l*X6k6(&WYx7F}Q%s`fKygjx zBCuYz#JuA2)FPn$9YWfSA(OWVS=1MafD|zLX|fe*fQ;Y<5g-$Pai?S^mnhUI_~qxN z-eN0CEh#O^150bN6xo7Q34sW25WxW?fCjjO*l+`iykPQG0wtvdIjPY}`T03I3dJQw zv6|dPq99qZ$p?gGxwSw{H4x!3`K_>;lqQhb&hU|mla=Fx2!pw0gYN}l(xY%&=BM}{5dq$a03_zkt7AOV)TvTjU diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index 2653f6d63037e76036e50585bc69bb8deb6d5058..e036addd226b811fc346a1efb857db379ff97cc4 100644 GIT binary patch delta 65 zcmaFP_JNK2GcPX}0}yDKuHDEzpGl)iKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu TuOvP>Kd&UQINoUTNv3%KqEi-C delta 56 zcmeys_MDCTGcPX}0}vQ!t=`BzpGm$@KR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Kvtsfkrg;GNFB6{t diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc index 8cd6117518cf4050cec0ee9d8e24b06da36531ac..c75cf6779bede1076d443fe059e02c238eb5b796 100644 GIT binary patch delta 66 zcmZqR?&RkF%*)Hg00bJQYd3PcF>93T=jN5@r|A0zd&UP80Bk0CV>gasU7T delta 57 zcmeC=Zs6wr%*)Hg00ahFt2c7HG0W%a=jN5@rzDmn>L(^8=B4E4#TTdMCgzo7Cda2` LR&1_h7GMGZ&omLd diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc index 74036e8af080422c057836f7e9aac4450dcb29f0..c3276735f770b2116ad5b29505804ac46d556a09 100644 GIT binary patch delta 66 zcmX@_a>IrDGcPX}0}yDKuHDGJj#;BYKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUU4d!w=0MIlR-T(jq delta 57 zcmccNa^8jeGcPX}0}vQ!t=`DJj#<83KR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Lvtsi@=5jdz9rqL{ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index 4416b6f593322491147dac63489f31e383be139d..ab8b87d38ba5deaeb13bde96c5ab3019e133bd17 100644 GIT binary patch delta 66 zcmaE7{oR`TGcPX}0}yDKuHDG}iAkeEKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoTp4D%%^0OVa3-~a#s delta 57 zcmexv{mz>EGcPX}0}vQ$t=`D}iAg?RKR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj LvtqL<^Cc+&F0B+R diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc index ff83a894971abd68ab8810df7d79e87ba8c84eee..0dbba318bb15f6fc0377e97e77b56b0ed1d6fcac 100644 GIT binary patch delta 66 zcmdlav|EV#GcPX}0}yDKuHDFeiczCVKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUU7e+gF0IV+-9smFU delta 57 zcmdljv`L8jGcPX}0}vQ$t=`Ceic!8$KR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj LvtsjKMmu%@{^=8G diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index 2dfadbd17039d1aa1d2928013a5a955593e947e5..af17ba4ba7e36da03ef11cd1a4af1d82b6861fd7 100644 GIT binary patch delta 66 zcmbO%I!BcIGcPX}0}yDKuHDFelTo8aKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoS8Gt&eP0GJ1HeeKQTQu UuOvP>Kd&UQINoUUQN~BC0G+fJO8@`> delta 57 zcmdnMznq`@GcPX}0}vQ!t=`Bzn^C?@KR2&LKP9mwQ9m&$F)t-QFTOZ6H!-gyGdVsj Lvtsi(#z(9G^0*U~ diff --git a/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 8d2b42e60c66b8a4bc5fb377539cd51553811715..4af29071b0aac0e7bdd0affc4e1446fa20f6a08e 100644 GIT binary patch delta 56 zcmZ3$wvdhMGcPX}0}!}A*vRF^q~NNbn^&Trl30?cpO~JSR}!C`pI4Gt9G{d}oSKuF KmpVD0X(IsFR1&`c delta 55 zcmZ3;wt$W6GcPX}0}z;KZRB!el6TS1%`4GQNi0d!PfSY8OUciRFHX%(%qz)Ej!(<1 Jm|VcL5dfo}5sLr- diff --git a/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc index 5d522d5be30e9aafd88f8986dfe3c43f65b95d45..56cc0fc17549814175b4ddbc31ba283eb4ec9e0d 100644 GIT binary patch delta 59 zcmZqs#@zmmnd>tzFBbz4xIWm(mCUP9t)H7$qMwpjlBl1Uo|;z@pPZjpl2{y{lvtda NlbM&gxrevvEdX0x70mzu delta 58 zcmZqw#@zOend>tzFBbz4oV>M>E16fmNs(x->iGE6ANuqvYQes|8eqMZWYHng)NoI0< MT4u%OCEVRN05}d6ApigX diff --git a/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc index 61bf31ba1285688b761b09bbfd24c63693493f1b..d1efb229311e18372a712aaa9426cb4fcae12680 100644 GIT binary patch delta 60 zcmcaQjrsO8X710tyj%=G;QClKABOypqJ?_@u<* P)SS$`)XmvEul4}|a3B^) delta 54 zcmcaTjrr;{X710tyj%=G5O8+dMs7DAsd)X|yb}GC#F9k)qSWO4yyVQB)cCZ_yu_T% Jg*>nJ0RZpR6RH3J diff --git a/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc index 6cb76039a82004a7b4b14812ed4d509700921701..12437a20652a01271d31d70df679c1550e8e84df 100644 GIT binary patch delta 59 zcmex;hw;xHMy}7iyj%=G;Pzl6mn*YEx_)k6iGE6ANuqvYdTL%td~$wXNn&w)Qetsx NPG(-}<~(NJYyfz)6)gY& delta 58 zcmex!hw=9vMy}7iyj%=GV5+r|%avI^O+PoUL_a05BvC&xDKRf4KQF#GH8(M@Br`ca MEwf^CJ~MAN06|0(`2YX_ diff --git a/tests/test_same_intent_transaction_replacement.py b/tests/test_same_intent_transaction_replacement.py new file mode 100644 index 00000000..6244ff36 --- /dev/null +++ b/tests/test_same_intent_transaction_replacement.py @@ -0,0 +1,164 @@ +import json +from types import SimpleNamespace + +import pytest + +from agent_framework.routing.enterprise_router import EnterpriseRouter +from agent_framework.runtime.agent_runtime import AgentRuntimeMixin + + +class _ReplacementLLM: + async def ainvoke(self, messages, **kwargs): + system = str(messages[0].get("content") if isinstance(messages[0], dict) else messages[0]).lower() + user = str(messages[-1].get("content") if isinstance(messages[-1], dict) else messages[-1]).lower() + + # First probe is abandonment-only and must not steal a normal same-intent replacement. + if "somente se o usuário desistiu explicitamente" in system: + if "esquece" in user: + return json.dumps({ + "decision": "ABANDON", + "intent": "contas_vas_cancel", + "agent": "contestacao_agent", + "confidence": 0.99, + "reason": "abandona a instância anterior e inicia novo cancelamento", + }) + return json.dumps({ + "decision": "CONTINUE", + "intent": None, + "agent": None, + "confidence": 0.99, + "reason": "não houve abandono explícito", + }) + + if "também tim fashion" in user or "tambem tim fashion" in user: + return json.dumps({ + "decision": "CONTINUE", + "intent": "contas_vas_cancel", + "agent": "contestacao_agent", + "confidence": 0.99, + "reason": "adição de item à mesma operação, não substituição", + }) + + return json.dumps({ + "decision": "REPLACE", + "intent": "contas_vas_cancel", + "agent": "contestacao_agent", + "confidence": 0.99, + "reason": "mesma intenção, novo alvo transacional", + }) + + +def _router(tmp_path): + routing = tmp_path / "routing.yaml" + routing.write_text( + """ +router: + fallback_agent: contestacao_agent + confidence_threshold: 0.70 +state_policies: + - state: WAITING_CONTESTACAO_CONFIRMATION + agent: contestacao_agent +intents: + - name: contas_vas_cancel + domain: telecom_contas + agent: contestacao_agent + priority: 20 + keywords: [cancelar] + mcp_tools: [consultar_vas, cancelar_vas_avulso] +""", + encoding="utf-8", + ) + return EnterpriseRouter( + SimpleNamespace( + ROUTING_CONFIG_PATH=str(routing), + ENABLE_LLM_ROUTER=True, + ENABLE_ROUTE_STICKINESS=False, + ), + llm=_ReplacementLLM(), + ) + + +def _state(message): + return { + "user_text": message, + "sanitized_input": message, + "next_state": "WAITING_CONTESTACAO_CONFIRMATION", + "transaction_status": "AWAITING_CONFIRMATION", + "intent": "state:WAITING_CONTESTACAO_CONFIRMATION", + "active_agent": "contestacao_agent", + "active_transaction": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": ["Tamboro Mensal", "Paramount+"]}, + "status": "AWAITING_CONFIRMATION", + "started_from_intent": "contas_vas_cancel", + }, + "pending_tool_call": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": ["Tamboro Mensal", "Paramount+"]}, + }, + } + + +@pytest.mark.asyncio +async def test_same_intent_new_target_emits_replacement_without_new_state(tmp_path): + decision = await _router(tmp_path).route(_state("quero cancelar TIM Fashion")) + + assert decision.intent == "contas_vas_cancel" + assert decision.agent == "contestacao_agent" + assert decision.next_state is None + assert decision.metadata["transaction_interruption"] == "same_intent_replacement" + assert decision.metadata["interrupted_intent"] == "contas_vas_cancel" + + +@pytest.mark.asyncio +async def test_same_intent_additive_request_keeps_current_transaction(tmp_path): + decision = await _router(tmp_path).route(_state("quero cancelar também TIM Fashion")) + + assert decision.method == "state" + assert decision.intent == "state:WAITING_CONTESTACAO_CONFIRMATION" + assert "transaction_interruption" not in (decision.metadata or {}) + + +@pytest.mark.asyncio +async def test_explicit_abandonment_with_same_intent_new_target_can_preempt_state_lock(tmp_path): + decision = await _router(tmp_path).route(_state("esquece, quero cancelar TIM Fashion")) + + assert decision.intent == "contas_vas_cancel" + assert decision.agent == "contestacao_agent" + assert decision.metadata["transaction_interruption"] == "explicit_abandonment" + + +@pytest.mark.asyncio +async def test_runtime_cancels_old_instance_for_same_intent_replacement(): + runtime = AgentRuntimeMixin() + state = { + "sanitized_input": "quero cancelar TIM Fashion", + "user_text": "quero cancelar TIM Fashion", + "route_decision": { + "metadata": {"transaction_interruption": "same_intent_replacement"} + }, + "active_transaction": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": ["Tamboro Mensal", "Paramount+"]}, + "status": "AWAITING_CONFIRMATION", + "started_from_intent": "contas_vas_cancel", + }, + "pending_tool_call": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": ["Tamboro Mensal", "Paramount+"]}, + }, + "selected_tool_call": { + "tool_name": "cancelar_vas_avulso", + "arguments": {"subject": ["Tamboro Mensal", "Paramount+"]}, + }, + "transaction_status": "AWAITING_CONFIRMATION", + "mcp_tools": [], + } + + await runtime.execute_tools_for_intent(state, tools=[]) + + assert state.get("active_transaction") is None + assert state.get("pending_tool_call") in ({}, None) + assert state.get("selected_tool_call") in ({}, None) + assert state["tool_policy_result"]["action"] == "cancelled_by_same_intent_replacement" + assert state["operational_context_reset"] is True