new feature: Multi-Intent
This commit is contained in:
75
IMPLEMENTACAO_MULTI_INTENT.md
Normal file
75
IMPLEMENTACAO_MULTI_INTENT.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Implementação Multi-Intent
|
||||||
|
|
||||||
|
## Entregas
|
||||||
|
|
||||||
|
- Planejador determinístico no framework, limitado às intents, agents e tools
|
||||||
|
cadastrados no `routing.yaml`.
|
||||||
|
- Priorização automática pela natureza das tools em `tool_policies.yaml`, sem
|
||||||
|
política por combinação.
|
||||||
|
- Persistência padrão em `pending_topics` e histórico em `handled_topics`.
|
||||||
|
- Tratamento de confirmação composta (`sim e ...`) sem provocar troca de intent
|
||||||
|
durante uma transação aberta.
|
||||||
|
- Composição das mensagens controladas das operações secundárias antes dos
|
||||||
|
guardrails de saída.
|
||||||
|
- Tratamento do cancelamento de Tamboro combinado com segunda via ou assunto
|
||||||
|
realmente fora do escopo.
|
||||||
|
- Desambiguação de cobranças repetidas por seleção relativa, incluindo
|
||||||
|
`as duas mais caras`, alimentando o batch já suportado pelo workflow de
|
||||||
|
cancelamento.
|
||||||
|
- Guias em português e inglês em `agent_framework_oci/docs/features`.
|
||||||
|
- Integração propagada às dez variantes de backend existentes em
|
||||||
|
`agent_framework_oci/Tuning-Performance`, sem criar um exemplo artificial.
|
||||||
|
- Script idempotente `agent_framework_oci/scripts/sync_pending_topics_to_tuning.py`
|
||||||
|
para manter as cópias sincronizadas.
|
||||||
|
|
||||||
|
## Testes adicionados
|
||||||
|
|
||||||
|
- `agent_framework_oci/tests/unit/test_multi_intent_planner.py`
|
||||||
|
- `tests/migration/test_multi_intent_contas.py`
|
||||||
|
- `agent_framework_oci/tests/unit/test_tuning_pending_topics_sync.py`
|
||||||
|
|
||||||
|
## Evidências recuperadas do Contas antigo
|
||||||
|
|
||||||
|
O documento antigo `docs/PENDING_TOPICS_CAMADA.md` descrevia explicitamente:
|
||||||
|
|
||||||
|
- cancelamento simples;
|
||||||
|
- cancelamento + pergunta conceitual sobre outro serviço;
|
||||||
|
- cancelamento + solicitação fora do contexto, como alteração de CNPJ;
|
||||||
|
- drenagem de tópicos pendentes antes da confirmação ou finalização;
|
||||||
|
- manutenção do tópico para retry quando não fosse produzida resposta útil.
|
||||||
|
|
||||||
|
Esses comportamentos foram usados como referência. A segunda via não é tratada
|
||||||
|
como fora de escopo quando a versão atual do Contas possui uma intent/tool para
|
||||||
|
ela; nesse caso ela é executada como consulta secundária. Somente uma solicitação
|
||||||
|
sem capability registrada vira `off_context`.
|
||||||
|
|
||||||
|
## Resultado da validação
|
||||||
|
|
||||||
|
O recorte de 23 testes do planejador e das integrações transacionais afetadas
|
||||||
|
passa integralmente. A execução ampla de `agent_framework_oci/tests/unit` e
|
||||||
|
`tests/migration` resultou em 946 testes aprovados e 33 falhas já presentes em
|
||||||
|
áreas fora desta alteração (principalmente checkpoints, runtime legado de
|
||||||
|
workflows, guardrails e paridade histórica).
|
||||||
|
|
||||||
|
## Limite de execução
|
||||||
|
|
||||||
|
Consultas secundárias conhecidas são executadas e consolidadas. Assuntos sem
|
||||||
|
capability são tratados como `off_context`. Uma segunda ação mutável permanece
|
||||||
|
pendente e requer confirmação própria ou um workflow transacional coordenador.
|
||||||
|
|
||||||
|
## Correção de isolamento da operação secundária
|
||||||
|
|
||||||
|
O teste real `cancelar Tamboro + segunda via` revelou que o agent secundário
|
||||||
|
herdava o snapshot operacional da transação primária. Isso fazia o
|
||||||
|
`FaturasAgent` reutilizar `selected_tool_call=cancelar_vas_avulso` e repetir a
|
||||||
|
confirmação do cancelamento. O estado filho agora limpa integralmente transação,
|
||||||
|
tool selecionada/pendente, workflow, parâmetros ausentes, resultados MCP,
|
||||||
|
`next_state`, `workflow_id` e usa a cláusula secundária também como
|
||||||
|
`sanitized_input`. O teste `test_pending_topics_isolation.py` protege essa regra.
|
||||||
|
|
||||||
|
Uma segunda execução real mostrou outro campo de roteamento que precisava ser
|
||||||
|
isolado: `mcp_tools` permanecia com a allowlist da operação primária. O runtime
|
||||||
|
usa esse campo de primeiro nível como fonte de verdade, portanto o
|
||||||
|
`FaturasAgent` ainda selecionava o cancelamento. O estado filho agora sobrescreve
|
||||||
|
`domain`, `mcp_tools` e `available_mcp_tools` exclusivamente com a capability do
|
||||||
|
tópico secundário.
|
||||||
@@ -185,6 +185,7 @@ O framework deve concentrar os motores reutilizáveis:
|
|||||||
- Telemetria Langfuse/OpenTelemetry.
|
- Telemetria Langfuse/OpenTelemetry.
|
||||||
- Analytics IC/NOC/GRL.
|
- Analytics IC/NOC/GRL.
|
||||||
- MCP Tool Router.
|
- MCP Tool Router.
|
||||||
|
- Planejamento multi-intent e fila persistente de `pending_topics`.
|
||||||
- Cache.
|
- Cache.
|
||||||
- RAG genérico.
|
- RAG genérico.
|
||||||
|
|
||||||
@@ -230,6 +231,8 @@ Input Guardrails
|
|||||||
↓
|
↓
|
||||||
Enterprise Router ou Supervisor
|
Enterprise Router ou Supervisor
|
||||||
↓
|
↓
|
||||||
|
Planejamento multi-intent / pending_topics
|
||||||
|
↓
|
||||||
Agente especializado
|
Agente especializado
|
||||||
↓
|
↓
|
||||||
MCP Tool Router / RAG / Cache / LLM
|
MCP Tool Router / RAG / Cache / LLM
|
||||||
@@ -11486,6 +11489,8 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo
|
|||||||
| Um exemplo antigo não compila | documentação histórica versus API atual | [Validação README x Código](docs/developer/pt/VALIDATION_README_ALIGNMENT.md) |
|
| Um exemplo antigo não compila | documentação histórica versus API atual | [Validação README x Código](docs/developer/pt/VALIDATION_README_ALIGNMENT.md) |
|
||||||
| Preciso criar um agente novo do zero | fluxo completo | [`README.md`](README.md) |
|
| Preciso criar um agente novo do zero | fluxo completo | [`README.md`](README.md) |
|
||||||
| Preciso saber onde colocar uma nova feature | arquitetura e boundaries | [Arquitetura e Conceitos](docs/developer/pt/01_architecture_and_concepts.md) |
|
| Preciso saber onde colocar uma nova feature | arquitetura e boundaries | [Arquitetura e Conceitos](docs/developer/pt/01_architecture_and_concepts.md) |
|
||||||
|
| Duas solicitações na mesma mensagem acionam apenas uma intent | decomposição multi-intent, `pending_topics`, allowlist de tools | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) |
|
||||||
|
| O agent secundário repete a transação ou confirmação primária | isolamento de `active_transaction`, `mcp_tools` e estado operacional | [Planejamento Multi-Intent](docs/features/pt-BR/16_multi_intent_planning.md) |
|
||||||
|
|
||||||
### 34. Funcionalidades Avançadas
|
### 34. Funcionalidades Avançadas
|
||||||
|
|
||||||
@@ -11561,9 +11566,31 @@ O conteúdo desta pasta deve ser tratado como uma extensão adicional do framewo
|
|||||||
|
|
||||||
**Use quando:** um `COER`/guardrail de entrada gera mensagem genérica, `route=blocked` carrega resultados antigos ou há dúvida sobre a precedência entre input guardrails, routing e tools.
|
**Use quando:** um `COER`/guardrail de entrada gera mensagem genérica, `route=blocked` carrega resultados antigos ou há dúvida sobre a precedência entre input guardrails, routing e tools.
|
||||||
|
|
||||||
|
### Guias de features implementadas
|
||||||
|
|
||||||
|
Os manuais `01` a `12` acima organizam os assuntos por área de desenvolvimento. Os guias abaixo documentam cada feature executável individualmente:
|
||||||
|
|
||||||
|
1. [Autenticação](docs/features/pt-BR/01_authentication.md)
|
||||||
|
2. [Workflow Transacional Determinístico](docs/features/pt-BR/02_deterministic_transactional_workflow.md)
|
||||||
|
3. [Composição por LLM Solicitada pelo Domínio](docs/features/pt-BR/03_domain_requested_llm_composition.md)
|
||||||
|
4. [RAG Solicitado pelo Domínio](docs/features/pt-BR/04_domain_requested_rag.md)
|
||||||
|
5. [Memória de Longo Prazo](docs/features/pt-BR/05_long_term_memory.md)
|
||||||
|
6. [Regressão Offline de Workflow](docs/features/pt-BR/06_offline_workflow_regression.md)
|
||||||
|
7. [Pause e Resume de Workflow](docs/features/pt-BR/07_pause_resume_workflow.md)
|
||||||
|
8. [Route Stickiness](docs/features/pt-BR/08_route_stickiness.md)
|
||||||
|
9. [Replay em Interrupções de Voz](docs/features/pt-BR/09_voice_interruption_replay.md)
|
||||||
|
10. [Recuperação de Erro em Workflow](docs/features/pt-BR/10_workflow_error_recovery.md)
|
||||||
|
11. [Clarificação](docs/features/pt-BR/11_clarification.md)
|
||||||
|
12. [Idempotência Durável](docs/features/pt-BR/12_durable_idempotency.md)
|
||||||
|
13. [Estados Transacionais Dinâmicos](docs/features/pt-BR/13_dynamic_transaction_states.md)
|
||||||
|
14. [Replay Após Finalização](docs/features/pt-BR/14_post_finalization_replay.md)
|
||||||
|
15. [Guardrails de Retrieval e Tools](docs/features/pt-BR/15_retrieval_tool_guardrails.md)
|
||||||
|
16. [Planejamento Multi-Intent e Pending Topics](docs/features/pt-BR/16_multi_intent_planning.md)
|
||||||
|
|
||||||
|
O [índice de features em português](docs/features/pt-BR/README.md) deve ser atualizado sempre que um novo guia for adicionado.
|
||||||
|
|
||||||
### Tutorial principal
|
### Tutorial principal
|
||||||
|
|
||||||
[`README.md`](README.md) continua sendo a referência para o passo a passo completo:
|
[`README.md`](README.md) continua sendo a referência para o passo a passo completo:
|
||||||
|
|
||||||
`arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → execução → testes → gateways → memória → RAG`.
|
`arquitetura → configuração → criação do agente → registro → estado → routing → tools → MCP → identidade → execução → testes → gateways → memória → RAG`.
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ Details the evaluation and certification framework, including evaluation archite
|
|||||||
|
|
||||||
Defines the platform operational readiness and SRE model, including managed components, health checks, readiness, SLOs, metrics, dashboards, alerts, runbooks, incident management, capacity planning, and production checklists.
|
Defines the platform operational readiness and SRE model, including managed components, health checks, readiness, SLOs, metrics, dashboards, alerts, runbooks, incident management, capacity planning, and production checklists.
|
||||||
|
|
||||||
### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer%20Auth%20and%20Security_EN.md)
|
### [IMPORTANT: Disclaimer Auth and Security](specs/Disclaimer_Auth_and_Security_EN.md)
|
||||||
|
|
||||||
Oracle Cloud Infrastructure Security Best Practice Recommendations.
|
Oracle Cloud Infrastructure Security Best Practice Recommendations.
|
||||||
|
|
||||||
@@ -186,6 +186,7 @@ The framework should concentrate the reusable engines:
|
|||||||
- Langfuse/OpenTelemetry Telemetry.
|
- Langfuse/OpenTelemetry Telemetry.
|
||||||
- Analytics IC/NOC/GRL.
|
- Analytics IC/NOC/GRL.
|
||||||
- MCP Tool Router.
|
- MCP Tool Router.
|
||||||
|
- Multi-intent planning and the persistent `pending_topics` queue.
|
||||||
- Cache.
|
- Cache.
|
||||||
- Generic RAG.
|
- Generic RAG.
|
||||||
|
|
||||||
@@ -231,6 +232,8 @@ Input Guardrails
|
|||||||
↓
|
↓
|
||||||
Enterprise Router or Supervisor
|
Enterprise Router or Supervisor
|
||||||
↓
|
↓
|
||||||
|
Multi-intent planning / pending_topics
|
||||||
|
↓
|
||||||
Specialized agent
|
Specialized agent
|
||||||
↓
|
↓
|
||||||
MCP Tool Router / RAG / Cache / LLM
|
MCP Tool Router / RAG / Cache / LLM
|
||||||
@@ -11388,6 +11391,8 @@ The content of this folder should be treated as an additional framework extensio
|
|||||||
| Logs/traces do not correlate the same agent | labels, IDs, observability mapping | [Observability](docs/developer/en/11_observability_persistence_and_operational_readiness.md) |
|
| Logs/traces do not correlate the same agent | labels, IDs, observability mapping | [Observability](docs/developer/en/11_observability_persistence_and_operational_readiness.md) |
|
||||||
| Historical example no longer compiles | stale docs vs current API | [README Alignment Validation](docs/developer/en/VALIDATION_README_ALIGNMENT.md) |
|
| Historical example no longer compiles | stale docs vs current API | [README Alignment Validation](docs/developer/en/VALIDATION_README_ALIGNMENT.md) |
|
||||||
| Need to create a new agent from scratch | complete flow | [`README_en.md`](README_en.md) |
|
| Need to create a new agent from scratch | complete flow | [`README_en.md`](README_en.md) |
|
||||||
|
| Two requests in one message trigger only one intent | multi-intent decomposition, `pending_topics`, tool allowlist | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) |
|
||||||
|
| A secondary agent repeats the primary transaction or confirmation | isolation of `active_transaction`, `mcp_tools`, and operational state | [Multi-Intent Planning](docs/features/en/16_multi_intent_planning.md) |
|
||||||
|
|
||||||
### 34. Advanced Features
|
### 34. Advanced Features
|
||||||
|
|
||||||
@@ -11463,6 +11468,29 @@ The content of this folder should be treated as an additional framework extensio
|
|||||||
|
|
||||||
**Use it when:** `COER`/input guardrails generate generic messages, `route=blocked` carries stale results, or you need to reason about precedence between input guardrails, routing, and tools.
|
**Use it when:** `COER`/input guardrails generate generic messages, `route=blocked` carries stale results, or you need to reason about precedence between input guardrails, routing, and tools.
|
||||||
|
|
||||||
|
### Implemented feature guides
|
||||||
|
|
||||||
|
The `01` through `12` manuals above organize documentation by development area. The guides below document each executable feature individually:
|
||||||
|
|
||||||
|
1. [Authentication](docs/features/en/01_authentication.md)
|
||||||
|
2. [Deterministic Transactional Workflow](docs/features/en/02_deterministic_transactional_workflow.md)
|
||||||
|
3. [Domain-Requested LLM Composition](docs/features/en/03_domain_requested_llm_composition.md)
|
||||||
|
4. [Domain-Requested RAG](docs/features/en/04_domain_requested_rag.md)
|
||||||
|
5. [Long-Term Memory](docs/features/en/05_long_term_memory.md)
|
||||||
|
6. [Offline Workflow Regression](docs/features/en/06_offline_workflow_regression.md)
|
||||||
|
7. [Pause and Resume Workflow](docs/features/en/07_pause_resume_workflow.md)
|
||||||
|
8. [Route Stickiness](docs/features/en/08_route_stickiness.md)
|
||||||
|
9. [Voice Interruption Replay](docs/features/en/09_voice_interruption_replay.md)
|
||||||
|
10. [Workflow Error Recovery](docs/features/en/10_workflow_error_recovery.md)
|
||||||
|
11. [Clarification](docs/features/en/11_clarification.md)
|
||||||
|
12. [Durable Idempotency](docs/features/en/12_durable_idempotency.md)
|
||||||
|
13. [Dynamic Transaction States](docs/features/en/13_dynamic_transaction_states.md)
|
||||||
|
14. [Post-Finalization Replay](docs/features/en/14_post_finalization_replay.md)
|
||||||
|
15. [Retrieval and Tool Guardrails](docs/features/en/15_retrieval_tool_guardrails.md)
|
||||||
|
16. [Multi-Intent Planning and Pending Topics](docs/features/en/16_multi_intent_planning.md)
|
||||||
|
|
||||||
|
The [English feature index](docs/features/en/README.md) must be updated whenever a new feature guide is added.
|
||||||
|
|
||||||
### Main tutorial
|
### Main tutorial
|
||||||
|
|
||||||
[`README_en.md`](README_en.md) remains the complete step-by-step guide.
|
[`README_en.md`](README_en.md) remains the complete step-by-step guide.
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -409,6 +410,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -547,14 +552,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -623,6 +642,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -409,6 +410,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -547,14 +552,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -623,6 +642,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -407,6 +408,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -545,14 +550,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -621,6 +640,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -409,6 +410,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -547,14 +552,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -623,6 +642,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -407,6 +408,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -545,14 +550,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -621,6 +640,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa
|
|||||||
|
|
||||||
A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP.
|
A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP.
|
||||||
|
|
||||||
Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação.
|
Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação.
|
||||||
|
|
||||||
Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`.
|
Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`.
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -407,6 +408,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -545,14 +550,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -621,6 +640,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Este template suporta confirmação transacional em duas camadas: primeiro um pa
|
|||||||
|
|
||||||
A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP.
|
A configuração `router.transaction_confirmation.semantic_fallback` usa três classes: `SIM`, `NAO` e `CONTINUAR`. O prompt pode usar `{{ pending_prompt }}`, `{{ relevant_conversation_context }}`, `{{ user_input }}` e `{{ allowed_values }}`. O histórico injetado é apenas contexto de interpretação; não substitui validação de negócio ou evidência MCP.
|
||||||
|
|
||||||
Exemplo: após `Você confirma o cancelamento do serviço Tamboro Mensal?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação.
|
Exemplo: após `Você confirma o cancelamento do serviço adicional selecionado?`, a frase `isso mesmo, pode confirmar` pode ser classificada como `SIM`. Já `mas qual é o valor?` deve ser `CONTINUAR`, portanto não executa a ação por confirmação.
|
||||||
|
|
||||||
Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`.
|
Entradas explícitas já suportadas continuam no caminho determinístico e não geram custo adicional de LLM. Em observabilidade, o fallback usa `transaction.confirmation.semantic_classifier` e o `route_decision.metadata` informa `transaction_confirmation_source: semantic`.
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -407,6 +408,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -545,14 +550,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -621,6 +640,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -407,6 +408,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -545,14 +550,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -621,6 +640,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -409,6 +410,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -547,14 +552,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -623,6 +642,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
missing_parameters: list[str]
|
missing_parameters: list[str]
|
||||||
supervisor_plan: dict[str, Any]
|
supervisor_plan: dict[str, Any]
|
||||||
supervisor_results: list[dict[str, Any]]
|
supervisor_results: list[dict[str, Any]]
|
||||||
|
pending_topics: list[dict[str, Any]]
|
||||||
|
handled_topics: list[dict[str, Any]]
|
||||||
active_agent: str
|
active_agent: str
|
||||||
route_bypassed: bool
|
route_bypassed: bool
|
||||||
continuity_signal: dict[str, Any]
|
continuity_signal: dict[str, Any]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from agent_framework.guardrails.rail_action import RailAction
|
|||||||
from agent_framework.guardrails.rail_result import RailResult
|
from agent_framework.guardrails.rail_result import RailResult
|
||||||
from agent_framework.judges.judge import JudgePipeline
|
from agent_framework.judges.judge import JudgePipeline
|
||||||
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
from agent_framework.routing.enterprise_router import EnterpriseRouter
|
||||||
|
from agent_framework.routing.pending_topics import drain_pending_topics
|
||||||
from agent_framework.supervisor.supervisor import Supervisor
|
from agent_framework.supervisor.supervisor import Supervisor
|
||||||
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
from agent_framework.observability.workflow_events import WorkflowTelemetry
|
||||||
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
from agent_framework.observability.guardrail_events import GuardrailTelemetry
|
||||||
@@ -409,6 +410,10 @@ class AgentWorkflow:
|
|||||||
"reason": decision.reason if decision.method == "continuity" else None,
|
"reason": decision.reason if decision.method == "continuity" else None,
|
||||||
"profile": (decision.metadata or {}).get("continuity_profile"),
|
"profile": (decision.metadata or {}).get("continuity_profile"),
|
||||||
} if decision.method == "continuity" else {},
|
} if decision.method == "continuity" else {},
|
||||||
|
"pending_topics": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan", {}).get("operations", [])[1:]
|
||||||
|
if (decision.metadata or {}).get("multi_intent_plan") else []
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -547,14 +552,28 @@ class AgentWorkflow:
|
|||||||
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
agente gerar `answer` e antes dos judges/persistência, produzindo campos
|
||||||
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
supervisor_* no state e eventos GRL.001..GRL.009 via AgentObserver.
|
||||||
"""
|
"""
|
||||||
|
drained = await drain_pending_topics(
|
||||||
|
state,
|
||||||
|
str(state.get("answer") or ""),
|
||||||
|
{
|
||||||
|
"billing_agent": self.billing.run,
|
||||||
|
"product_agent": self.product.run,
|
||||||
|
"orders_agent": self.orders.run,
|
||||||
|
"support_agent": self.support.run,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
candidate = drained.answer
|
||||||
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
if not bool(getattr(self.settings, "ENABLE_OUTPUT_SUPERVISOR", True)):
|
||||||
return {
|
return {
|
||||||
|
"answer": candidate,
|
||||||
|
"final_answer": candidate,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
"supervisor_action": "disabled",
|
"supervisor_action": "disabled",
|
||||||
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
"supervisor_attempt": int(state.get("supervisor_attempt", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = state.get("answer") or ""
|
|
||||||
context = {
|
context = {
|
||||||
**(state.get("context") or {}),
|
**(state.get("context") or {}),
|
||||||
"tenant_id": state.get("tenant_id"),
|
"tenant_id": state.get("tenant_id"),
|
||||||
@@ -623,6 +642,8 @@ class AgentWorkflow:
|
|||||||
for r in decision.results
|
for r in decision.results
|
||||||
],
|
],
|
||||||
"output_guardrails_already_applied": True,
|
"output_guardrails_already_applied": True,
|
||||||
|
"pending_topics": drained.pending_topics,
|
||||||
|
"handled_topics": drained.handled_topics,
|
||||||
"guardrail_decisions": state.get("guardrail_decisions", [])
|
"guardrail_decisions": state.get("guardrail_decisions", [])
|
||||||
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
+ [item for r in decision.results for item in (r.metadata or {}).get("legacy_decisions", [])],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Multi-Intent Planning
|
||||||
|
|
||||||
|
`MultiIntentPlanner` recognizes independent requests in one utterance and
|
||||||
|
builds a plan constrained to the intents, agents, and tools declared in
|
||||||
|
`config/routing.yaml`. It does not execute operations or let the LLM invent a
|
||||||
|
sequence: the router selects the primary operation and the domain workflow
|
||||||
|
continues to own the transaction.
|
||||||
|
|
||||||
|
## Configuration and execution
|
||||||
|
|
||||||
|
No additional configuration is required. The planner uses the intents already
|
||||||
|
declared in `routing.yaml`, selects the single transactional operation from
|
||||||
|
`tool_policies.yaml`, and persists the remaining requests as `pending_topics`.
|
||||||
|
|
||||||
|
Known read-only topics execute through their own agent. A second mutation stays
|
||||||
|
pending for its own confirmation, while an unknown clause becomes off-context.
|
||||||
|
|
||||||
|
The router resolves clauses from registered intent keywords and routes the
|
||||||
|
transactional operation first. The graph drains read-only pending topics before
|
||||||
|
output supervision. During an open transaction,
|
||||||
|
`yes, and send me the invoice` confirms the transaction and records the
|
||||||
|
secondary request instead of causing an intent shift.
|
||||||
|
|
||||||
|
For multiple side-effecting actions, implement a transactional coordinator
|
||||||
|
that persists the plan, confirms it, executes idempotently, and records partial
|
||||||
|
failure or compensation. See `tests/unit/test_multi_intent_planner.py`. No
|
||||||
|
special Tuning-Performance template is needed because the behavior is native.
|
||||||
@@ -17,3 +17,4 @@ Documentation for the main `agent_framework_oci` features.
|
|||||||
- [Dynamic Transaction States](13_dynamic_transaction_states.md)
|
- [Dynamic Transaction States](13_dynamic_transaction_states.md)
|
||||||
- [Post Finalization Replay](14_post_finalization_replay.md)
|
- [Post Finalization Replay](14_post_finalization_replay.md)
|
||||||
- [Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md)
|
- [Retrieval / Tool Guardrails](15_retrieval_tool_guardrails.md)
|
||||||
|
- [Multi-Intent Planning](16_multi_intent_planning.md)
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Planejamento Multi-Intent
|
||||||
|
|
||||||
|
O `MultiIntentPlanner` reconhece solicitações independentes na mesma mensagem e
|
||||||
|
cria um plano limitado às intents, agents e tools declarados em
|
||||||
|
`config/routing.yaml`. O planejador não executa operações nem permite que o LLM
|
||||||
|
invente uma sequência: o roteador escolhe a operação primária e o workflow do
|
||||||
|
domínio continua responsável pela transação.
|
||||||
|
|
||||||
|
## Configuração
|
||||||
|
|
||||||
|
Não existe configuração adicional. O planejador usa as intents do `routing.yaml`
|
||||||
|
e lê `tool_policies.yaml` para priorizar automaticamente a única operação
|
||||||
|
transacional. Pedidos secundários são persistidos em `pending_topics`.
|
||||||
|
|
||||||
|
- intent conhecida e read-only: executada pelo agent correspondente;
|
||||||
|
- intent conhecida e transacional: mantida pendente para confirmação própria;
|
||||||
|
- trecho sem capability correspondente: respondido como `off_context`.
|
||||||
|
|
||||||
|
Consultas secundárias com `execute` podem ser executadas pelo agent correspondente
|
||||||
|
e consolidadas. Uma segunda mutação é marcada como `defer`, pois exige um
|
||||||
|
workflow transacional coordenador.
|
||||||
|
|
||||||
|
## Fluxo de execução
|
||||||
|
|
||||||
|
1. O `EnterpriseRouter` separa a entrada em cláusulas e resolve cada uma pelas
|
||||||
|
keywords das intents cadastradas.
|
||||||
|
2. `tool_policies.yaml` identifica a operação transacional e a torna primária.
|
||||||
|
3. A primeira operação é roteada normalmente para seu agent/workflow.
|
||||||
|
4. O `agent_graph` compõe as mensagens públicas antes do supervisor de saída.
|
||||||
|
5. Durante uma confirmação, uma frase como `sim e me manda a segunda via`
|
||||||
|
confirma a transação aberta e registra a solicitação secundária, sem causar
|
||||||
|
troca indevida de intent.
|
||||||
|
|
||||||
|
## Operações mutáveis
|
||||||
|
|
||||||
|
Para executar mais de uma ação com efeito colateral, implemente um workflow
|
||||||
|
transacional coordenador. Ele deve persistir o plano, pedir confirmação quando
|
||||||
|
necessário, executar cada passo com idempotência e registrar sucesso parcial,
|
||||||
|
falha e compensação. O planejador fornece o contrato; a política de negócio e a
|
||||||
|
orquestração permanecem no domínio.
|
||||||
|
|
||||||
|
## Referência executável
|
||||||
|
|
||||||
|
Veja os testes genéricos em `tests/unit/test_multi_intent_planner.py`. Como a
|
||||||
|
feature é padrão do runtime, não existe uma cópia especial do
|
||||||
|
`agent_template_backend` em `Tuning-Performance`.
|
||||||
@@ -17,3 +17,4 @@ Documentação das principais features do `agent_framework_oci`.
|
|||||||
- [Estados Transacionais Dinâmicos](13_dynamic_transaction_states.md)
|
- [Estados Transacionais Dinâmicos](13_dynamic_transaction_states.md)
|
||||||
- [Replay Após Finalização](14_post_finalization_replay.md)
|
- [Replay Após Finalização](14_post_finalization_replay.md)
|
||||||
- [Guardrails de Retrieval e Tools](15_retrieval_tool_guardrails.md)
|
- [Guardrails de Retrieval e Tools](15_retrieval_tool_guardrails.md)
|
||||||
|
- [Planejamento Multi-Intent](16_multi_intent_planning.md)
|
||||||
|
|||||||
@@ -201,6 +201,8 @@ src/agent_framework/routing/config_loader.py
|
|||||||
src/agent_framework/routing/continuity.py
|
src/agent_framework/routing/continuity.py
|
||||||
src/agent_framework/routing/enterprise_router.py
|
src/agent_framework/routing/enterprise_router.py
|
||||||
src/agent_framework/routing/models.py
|
src/agent_framework/routing/models.py
|
||||||
|
src/agent_framework/routing/multi_intent.py
|
||||||
|
src/agent_framework/routing/pending_topics.py
|
||||||
src/agent_framework/runtime/__init__.py
|
src/agent_framework/runtime/__init__.py
|
||||||
src/agent_framework/runtime/agent_runtime.py
|
src/agent_framework/runtime/agent_runtime.py
|
||||||
src/agent_framework/runtime/transaction_input.py
|
src/agent_framework/runtime/transaction_input.py
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user