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 00000000..c77aeb7e Binary files /dev/null and b/deploy/oke/load-test/scripts/__pycache__/prepare_env.cpython-313.pyc differ 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 4a44168d..4beeadf8 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc index 47e05974..df240730 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc differ 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 019dc27a..a526262e 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc differ 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 01101db9..2aa5c9ba 100644 Binary files a/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc differ 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 171af7a9..6f06afb7 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc differ 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 e7aa9016..57af840d 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc differ 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 a06866c9..d28453d9 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc differ 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 1e73aaa8..09eb4d94 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc differ 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 8fcf15f5..65d1d490 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc index a4a4b6fc..1350a170 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_payload_mapper.cpython-313.pyc differ 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 a18f97b5..50374fbb 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc index 31392dd2..bb1b7d8e 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/__init__.cpython-313.pyc differ 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 b5e221b2..e2bfdc6c 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc differ 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 1387eaaa..d7b6bafd 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc differ 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 585cc284..1dfea145 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc differ 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 cfeae8e0..a47ad326 100644 Binary files a/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc differ 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 beff5c83..89af7e7d 100644 Binary files a/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc differ 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 386d5fbc..b7c26689 100644 Binary files a/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc index 0bb6f40c..999a0421 100644 Binary files a/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc b/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc index 4b39fce9..bb93988a 100644 Binary files a/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc index faa15aa3..26bec0f6 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc index 8eace6d7..dfc8266e 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/adapters.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc index 71705ae5..0f8134e4 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/base.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc index 85a563e8..b7058f90 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/gateway.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc index f0b0e553..c4eeee9d 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/interruption.cpython-313.pyc differ 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 17f6fd04..9ea9e415 100644 Binary files a/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc differ 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 a89c93af..1d39403a 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc differ 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 1b4826bf..e874174f 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc differ 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 34b52689..1c996b7a 100644 Binary files a/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc differ 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 07780aa3..37e51598 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index 4d737a18..e3620c74 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc differ 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 774134a7..1876f380 100644 Binary files a/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc differ 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 0087fb9a..b7933581 100644 Binary files a/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc differ 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 1a6e4c93..daa4d787 100644 Binary files a/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/gateways/__pycache__/mcp_gateway_client.cpython-313.pyc differ 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 e6748692..b59b1a3f 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ 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 2b0d27b2..dc2c8dde 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ 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 fef46f5f..f1aaec9a 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ 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 f232e066..271e5d60 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ 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 00000000..d8a868cc Binary files /dev/null and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc differ 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 3163328a..48666933 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ 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 8d8f919a..efdc67db 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ 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 548ec054..d945f0b1 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ 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 e3641b6a..7c280a45 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ 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 1e5a9b96..9408e0b7 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ 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 d80bc2d3..c43b8e56 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ 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 ac1ae631..3484fe0b 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ 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 32fb0ab1..69d84557 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ 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 c5b5b845..3c8b0a05 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc index 1bfc7a6a..365d13c0 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ 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 3916d89d..43a26825 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ 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 e818798a..658adb8b 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ 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 03726731..8c22899e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ 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 b21b72b2..d13ac46c 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ 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 b15f86c9..52950889 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ 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 9c8fbc94..38f1aea0 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ 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 78bb173b..9b2f632d 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc index 3d364730..78b3a8c5 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc index 324b0fb5..bb720a1c 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 92693d1d..81bdd670 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ 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 24514e29..6bc38dea 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ 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 c8eb5e00..e80e85dc 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ 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 acdb4fbd..b2fb4d10 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ 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 2eea70b1..d4050835 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ 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 2b40e1b0..2d6adf34 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ 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 37cae7bb..2c3791e9 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ 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 684c4167..3b9b522d 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ 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 1122b7b6..702a1c14 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ 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 0fca1941..e1602bd0 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc index 6bfe4951..bf3d5635 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc index 9f2d9a96..d9615b9f 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc index 831d03ac..4b93a350 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc index 998cae13..2a99c447 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ 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 aa9d5999..4ac41e0a 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ 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 1013767c..fede3088 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc index f4a65628..8e47205e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ 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 27d36a48..9feeeb4e 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ 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 a5045828..3947485c 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ 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 4b1e1f9d..f46198f4 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ 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 d721fe49..86d3ca74 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ 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 7973b2a7..d365db44 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ 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 a7e0e360..57ba0f7a 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ 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 526b5cf1..f48f21d4 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ 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 0c9b4c37..738af6c8 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc index 2d7ea502..1efddfba 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc index 4e2b23b0..13ed30bb 100644 Binary files a/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/guardrails/dlex_sanitizer.py b/libs/agent_framework/src/agent_framework/guardrails/dlex_sanitizer.py new file mode 100644 index 00000000..613167e8 --- /dev/null +++ b/libs/agent_framework/src/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/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 cea3ca9c..43e7ad7a 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc differ 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 7ed3b800..c2ca0e75 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc differ 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 0f857e28..257c4153 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc differ 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 385eeb5c..03c55fa7 100644 Binary files a/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc differ 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 2c9423c8..8f8ba56a 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc differ 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 5b6c9bea..04d62397 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc differ 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 a8f8bd44..bf49c597 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc differ 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 da332e3a..08fc703b 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc differ 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 1e1a85bd..be306050 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ 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 0569e929..2ac1c475 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc differ 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 ee50b32c..cf04890a 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc differ 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 7487f111..090fb2e2 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ 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 cdc86752..ff5a0c2a 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc differ 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 e915a970..3cb62fd0 100644 Binary files a/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc differ 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 d86c644c..e760abd2 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc differ 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 3f8b7f64..f80dc48b 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc differ 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 edc92cf4..1a7095ac 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc differ 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 b551d192..1fe1d943 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc differ 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 d598db12..52218b3d 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc differ 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 e00606f1..c48d691b 100644 Binary files a/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc differ 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 99e6e261..83879cb7 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc index d54d85d9..0fcf8a97 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc index a7dd605f..20a34108 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/models.cpython-313.pyc differ 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 4932ae5d..33367480 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/registry.cpython-313.pyc differ 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 a7955344..679ede05 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc index 445d2bfe..38925995 100644 Binary files a/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_router.cpython-313.pyc differ 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 23d72b7f..6a7f80bc 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc index 13860610..d94ef3d5 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc differ 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 6aa97e0f..9f8133b7 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc differ 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 5f9e9dab..3037dbf9 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc differ 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 36f94e18..439d80f7 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc differ 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 a71ccf2d..b8449b8f 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index 94a7de6f..ccafa0cb 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc differ 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 caee3df3..2562f26c 100644 Binary files a/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc differ 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 87f406dd..23837c6f 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index c273cb37..d3d2aada 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc index 3e0b7ae4..f441e5e7 100644 Binary files a/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/models/__pycache__/session.cpython-313.pyc differ 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 3095e2ed..bf56e96e 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc index ff505671..670030b6 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/code_mapper.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc index 0a8326bf..1b693ec7 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/context.cpython-313.pyc differ 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 4dd047fb..88fdd0d5 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc differ 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 f8b4db19..54c02645 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc differ 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 1737199e..6d0d5f59 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc differ 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 0adfa30d..7aaf3326 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc index 594ea750..e13ae831 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/langgraph_telemetry.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc index fe74b6de..6a7b77e2 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/llm_advisors.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc index fb73ce38..d9233aff 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_contract.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc index 7e18ce6c..648d7ef4 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/noc_otel.cpython-313.pyc differ 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 8b8e1e10..9b8ac37b 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc differ 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 ef635b56..ef4d76fe 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc differ 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 a877ac2c..209e5189 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc differ 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 6d765162..2518acb9 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc differ 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 24460255..374c66c0 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc differ 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 f503e3a1..73fd59ea 100644 Binary files a/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc index acd781af..222e5a11 100644 Binary files a/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/oci/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc b/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc index 2af4386c..d3887592 100644 Binary files a/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/oci/__pycache__/auth.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc index 696b94fa..5fea2f23 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc index e56a6369..da54d260 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/oracle_store.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc index 6a3c582d..47ffce89 100644 Binary files a/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/persistence/__pycache__/sqlite_store.cpython-313.pyc differ 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 6f0c89e4..7849c05a 100644 Binary files a/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc differ 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 1968a29d..b6c3eb30 100644 Binary files a/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc differ 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 c9d635ac..83b55a51 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc differ 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 bdf965d5..1817712b 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc index 9b882352..57c3b081 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/graph_store.cpython-313.pyc differ 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 835fcdfa..bdfb5969 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc differ 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 eaaa2b58..72b82210 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc index 937bb6e0..29b006dc 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/rag_service.cpython-313.pyc differ 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 0472c07d..2903d0c8 100644 Binary files a/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc differ 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 f3260451..11ca557e 100644 Binary files a/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc differ 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 ae555f55..c97830a4 100644 Binary files a/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc differ 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 7ba8b55e..96c6acb1 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc index 7eac0414..164b46f7 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc index 0e187ef9..ab56bdb0 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc differ 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 a0950935..f8c401b7 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc index 28bb7697..c6302b5f 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/models.cpython-313.pyc differ 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 fea4a01f..4e496180 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc b/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc index 4b78cac2..4054cb73 100644 Binary files a/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/routing/__pycache__/pending_topics.cpython-313.pyc differ 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 4986b941..ac1069dc 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc differ 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 d43e7477..61dadcc3 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc differ 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 00000000..ab1dd270 Binary files /dev/null and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_context.cpython-313.pyc differ 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 506ac4d1..75b492e0 100644 Binary files a/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc differ 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 26baa626..4758e940 100644 Binary files a/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc differ 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 c1716a17..61119b56 100644 Binary files a/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc differ 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 568462bf..73d0402c 100644 Binary files a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc index 101f6020..5877fa05 100644 Binary files a/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/supervisor/__pycache__/supervisor.cpython-313.pyc differ 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 2653f6d6..e036addd 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc differ 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 8cd61175..c75cf677 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/graph.cpython-313.pyc differ 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 74036e8a..c3276735 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/input_contract.cpython-313.pyc differ 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 4416b6f5..ab8b87d3 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc differ 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 ff83a894..0dbba318 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc differ 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 2dfadbd1..af17ba4b 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc index 6803758e..efb677e8 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc differ diff --git a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc index 6a118c90..d68fa005 100644 Binary files a/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc and b/libs/agent_framework/src/agent_framework/workflows/__pycache__/tool_executor.cpython-313.pyc differ 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 8d2b42e6..4af29071 100644 Binary files a/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ 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 5d522d5b..56cc0fc1 100644 Binary files a/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc b/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc index cfa0343c..833d957e 100644 Binary files a/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc differ 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 61bf31ba..d1efb229 100644 Binary files a/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_state_regression_matrix.cpython-313-pytest-9.0.2.pyc differ 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 6cb76039..12437a20 100644 Binary files a/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc and b/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc differ 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