mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-11-05 19:04:13 +00:00
adding ABANDON in multi-intent/intent-shift
This commit is contained in:
@@ -199,11 +199,44 @@ ROUTE_STICKINESS_MAX_TOKENS
|
|||||||
|
|
||||||
`CONTINUE` mantém o agente ativo; `ROUTE` volta ao EnterpriseRouter. Stickiness não autoriza transações e não substitui tool policy.
|
`CONTINUE` mantém o agente ativo; `ROUTE` volta ao EnterpriseRouter. Stickiness não autoriza transações e não substitui tool policy.
|
||||||
|
|
||||||
## P.7. Intent shift
|
## P.7. Intent shift e abandono explícito (`CONTINUE` / `SHIFT` / `ABANDON`)
|
||||||
|
|
||||||
Durante `COLLECTING_PARAMETERS` ou `AWAITING_CONFIRMATION`, o framework protege a transação atual contra mudanças acidentais. Entretanto, uma mudança explícita de assunto pode cancelar/limpar o latch transacional e devolver o turno ao routing normal.
|
Durante `COLLECTING_PARAMETERS` ou `AWAITING_CONFIRMATION`, o framework protege a transação atual contra mudanças acidentais. Antes de assumir que toda nova fala pertence à transação aberta, o EnterpriseRouter pode classificar a relação da mensagem com o objetivo transacional ativo em três decisões semânticas:
|
||||||
|
|
||||||
A fala atual também tem precedência para preencher campos pendentes. Uma resposta como `PED-1001` durante coleta de `order_id` não deve ser reinterpretada como uma nova intent.
|
```text
|
||||||
|
CONTINUE
|
||||||
|
SHIFT
|
||||||
|
ABANDON
|
||||||
|
```
|
||||||
|
|
||||||
|
Essas decisões não são novos estados transacionais persistidos. Elas orientam o que fazer com o estado atual. O estado terminal operacional continua sendo, por exemplo, `CANCELLED`.
|
||||||
|
|
||||||
|
- `CONTINUE`: a mensagem pertence à transação em andamento. A coleta, confirmação ou execução continua normalmente.
|
||||||
|
- `SHIFT`: o usuário passa temporariamente a perseguir outro objetivo. O framework libera o latch da transação ativa para rotear a nova intenção, mas **não interpreta o shift como autorização para apagar todas as `pending_topics`**. Pendências de um plano multi-intent podem continuar disponíveis para retomada posterior.
|
||||||
|
- `ABANDON`: o usuário manifesta desistência explícita da ação/transação ativa. O router sinaliza `transaction_interruption=explicit_abandonment`; o runtime encerra a transação relacionada como `CANCELLED` e limpa seus latches transacionais.
|
||||||
|
|
||||||
|
Exemplos conceituais:
|
||||||
|
|
||||||
|
```text
|
||||||
|
"PED-1001"
|
||||||
|
→ CONTINUE
|
||||||
|
→ preenche order_id da coleta atual
|
||||||
|
|
||||||
|
"antes disso, quero consultar meus serviços"
|
||||||
|
→ SHIFT
|
||||||
|
→ atende o novo objetivo
|
||||||
|
→ pending_topics anteriores não são apagadas automaticamente
|
||||||
|
|
||||||
|
"não quero mais cancelar"
|
||||||
|
→ ABANDON
|
||||||
|
→ encerra a transação de cancelamento ativa como CANCELLED
|
||||||
|
```
|
||||||
|
|
||||||
|
Quando existe `ABANDON` sem um novo objetivo para rotear, o framework pode representar o turno com uma intenção de estado como `state:TRANSACTION_ABANDONED`, sem selecionar novamente a tool que acabou de ser abandonada. Isso evita o ciclo incorreto `ABANDON → detectar novamente a palavra cancelar → reabrir cancelar_pedido`.
|
||||||
|
|
||||||
|
`ABANDON` também não deve ser confundido com uma limpeza global do plano multi-intent. Por padrão, ele encerra a ação/transação ativa correlacionada; outras operações independentes podem permanecer em `pending_topics`. Se o usuário disser explicitamente que quer abandonar também outras ações, esse escopo deve ser resolvido de forma correlacionada, e não por um `pending_topics = []` indiscriminado.
|
||||||
|
|
||||||
|
A fala atual continua tendo precedência para preencher campos pendentes quando a decisão é `CONTINUE`. Uma resposta como `PED-1001` durante coleta de `order_id` não deve ser reinterpretada como uma nova intent.
|
||||||
|
|
||||||
## P.8. Coleta de parâmetros
|
## P.8. Coleta de parâmetros
|
||||||
|
|
||||||
|
|||||||
@@ -196,11 +196,44 @@ ROUTE_STICKINESS_MAX_TOKENS
|
|||||||
|
|
||||||
`CONTINUE` keeps the active agent; `ROUTE` goes back to EnterpriseRouter. Stickiness does not authorize transactions and does not replace tool policy.
|
`CONTINUE` keeps the active agent; `ROUTE` goes back to EnterpriseRouter. Stickiness does not authorize transactions and does not replace tool policy.
|
||||||
|
|
||||||
## P.7. Intent shift
|
## P.7. Intent shift and explicit abandonment (`CONTINUE` / `SHIFT` / `ABANDON`)
|
||||||
|
|
||||||
During `COLLECTING_PARAMETERS` or `AWAITING_CONFIRMATION`, the framework protects the current transaction from accidental topic changes. An explicit change of subject may, however, cancel/clear the transactional latch and return the turn to normal routing.
|
During `COLLECTING_PARAMETERS` or `AWAITING_CONFIRMATION`, the framework protects the current transaction from accidental topic changes. Before assuming that every new utterance belongs to the open transaction, EnterpriseRouter may classify how the message relates to the active transactional goal using three semantic decisions:
|
||||||
|
|
||||||
The current utterance also has precedence when filling pending fields. A response such as `PED-1001` while collecting `order_id` must not be reinterpreted as a new intent.
|
```text
|
||||||
|
CONTINUE
|
||||||
|
SHIFT
|
||||||
|
ABANDON
|
||||||
|
```
|
||||||
|
|
||||||
|
These decisions are not new persisted transactional states. They guide how the current state is handled. The operational terminal state remains, for example, `CANCELLED`.
|
||||||
|
|
||||||
|
- `CONTINUE`: the message belongs to the transaction in progress. Parameter collection, confirmation, or execution continues normally.
|
||||||
|
- `SHIFT`: the user temporarily moves to another goal. The framework releases the active transactional latch so the new intent can be routed, but **a shift is not interpreted as permission to erase all `pending_topics`**. Pending work from a multi-intent plan may remain available for later resumption.
|
||||||
|
- `ABANDON`: the user explicitly gives up the active action/transaction. The router signals `transaction_interruption=explicit_abandonment`; the runtime terminates the related transaction as `CANCELLED` and clears its transactional latches.
|
||||||
|
|
||||||
|
Conceptual examples:
|
||||||
|
|
||||||
|
```text
|
||||||
|
"PED-1001"
|
||||||
|
→ CONTINUE
|
||||||
|
→ fills order_id for the current collection
|
||||||
|
|
||||||
|
"before that, I want to check my services"
|
||||||
|
→ SHIFT
|
||||||
|
→ handles the new goal
|
||||||
|
→ previous pending_topics are not automatically erased
|
||||||
|
|
||||||
|
"I don't want to cancel anymore"
|
||||||
|
→ ABANDON
|
||||||
|
→ terminates the active cancellation transaction as CANCELLED
|
||||||
|
```
|
||||||
|
|
||||||
|
When `ABANDON` occurs without a new goal to route, the framework may represent the turn with a state intent such as `state:TRANSACTION_ABANDONED`, without selecting again the tool that was just abandoned. This avoids the incorrect loop `ABANDON → detect the word cancel again → reopen cancelar_pedido`.
|
||||||
|
|
||||||
|
`ABANDON` must also not be confused with a global cleanup of the multi-intent plan. By default, it terminates the correlated active action/transaction; other independent operations may remain in `pending_topics`. If the user explicitly abandons additional actions, that scope should be resolved by correlation rather than by an indiscriminate `pending_topics = []`.
|
||||||
|
|
||||||
|
The current utterance still has precedence when filling pending fields when the decision is `CONTINUE`. A response such as `PED-1001` while collecting `order_id` must not be reinterpreted as a new intent.
|
||||||
|
|
||||||
## P.8. Parameter collection
|
## P.8. Parameter collection
|
||||||
|
|
||||||
|
|||||||
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.
@@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
agent_responses: list[dict[str, Any]]
|
agent_responses: list[dict[str, Any]]
|
||||||
pending_topics: list[dict[str, Any]]
|
pending_topics: list[dict[str, Any]]
|
||||||
handled_topics: list[dict[str, Any]]
|
handled_topics: list[dict[str, Any]]
|
||||||
|
operation_results: dict[str, dict[str, Any]]
|
||||||
|
multi_intent_plan: 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]
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -448,6 +448,13 @@ class AgentWorkflow:
|
|||||||
if (decision.metadata or {}).get("multi_intent_plan")
|
if (decision.metadata or {}).get("multi_intent_plan")
|
||||||
else list(state.get("pending_topics") or [])
|
else list(state.get("pending_topics") or [])
|
||||||
),
|
),
|
||||||
|
# Durable correlation for the primary operation. route_decision
|
||||||
|
# is replaced by state routing on confirmation/resume turns.
|
||||||
|
"multi_intent_plan": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan")
|
||||||
|
or state.get("multi_intent_plan")
|
||||||
|
or {}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -605,6 +612,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
@@ -700,6 +708,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
**drained.state_patch,
|
**drained.state_patch,
|
||||||
|
|||||||
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.
@@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
agent_responses: list[dict[str, Any]]
|
agent_responses: list[dict[str, Any]]
|
||||||
pending_topics: list[dict[str, Any]]
|
pending_topics: list[dict[str, Any]]
|
||||||
handled_topics: list[dict[str, Any]]
|
handled_topics: list[dict[str, Any]]
|
||||||
|
operation_results: dict[str, dict[str, Any]]
|
||||||
|
multi_intent_plan: 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]
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -448,6 +448,13 @@ class AgentWorkflow:
|
|||||||
if (decision.metadata or {}).get("multi_intent_plan")
|
if (decision.metadata or {}).get("multi_intent_plan")
|
||||||
else list(state.get("pending_topics") or [])
|
else list(state.get("pending_topics") or [])
|
||||||
),
|
),
|
||||||
|
# Durable correlation for the primary operation. route_decision
|
||||||
|
# is replaced by state routing on confirmation/resume turns.
|
||||||
|
"multi_intent_plan": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan")
|
||||||
|
or state.get("multi_intent_plan")
|
||||||
|
or {}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -605,6 +612,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
@@ -700,6 +708,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
**drained.state_patch,
|
**drained.state_patch,
|
||||||
|
|||||||
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.
@@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
agent_responses: list[dict[str, Any]]
|
agent_responses: list[dict[str, Any]]
|
||||||
pending_topics: list[dict[str, Any]]
|
pending_topics: list[dict[str, Any]]
|
||||||
handled_topics: list[dict[str, Any]]
|
handled_topics: list[dict[str, Any]]
|
||||||
|
operation_results: dict[str, dict[str, Any]]
|
||||||
|
multi_intent_plan: 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]
|
||||||
|
|||||||
Binary file not shown.
@@ -446,6 +446,13 @@ class AgentWorkflow:
|
|||||||
if (decision.metadata or {}).get("multi_intent_plan")
|
if (decision.metadata or {}).get("multi_intent_plan")
|
||||||
else list(state.get("pending_topics") or [])
|
else list(state.get("pending_topics") or [])
|
||||||
),
|
),
|
||||||
|
# Durable correlation for the primary operation. route_decision
|
||||||
|
# is replaced by state routing on confirmation/resume turns.
|
||||||
|
"multi_intent_plan": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan")
|
||||||
|
or state.get("multi_intent_plan")
|
||||||
|
or {}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -603,6 +610,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
@@ -698,6 +706,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
**drained.state_patch,
|
**drained.state_patch,
|
||||||
|
|||||||
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.
@@ -39,6 +39,8 @@ class AgentState(TypedDict, total=False):
|
|||||||
agent_responses: list[dict[str, Any]]
|
agent_responses: list[dict[str, Any]]
|
||||||
pending_topics: list[dict[str, Any]]
|
pending_topics: list[dict[str, Any]]
|
||||||
handled_topics: list[dict[str, Any]]
|
handled_topics: list[dict[str, Any]]
|
||||||
|
operation_results: dict[str, dict[str, Any]]
|
||||||
|
multi_intent_plan: 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]
|
||||||
|
|||||||
Binary file not shown.
@@ -448,6 +448,13 @@ class AgentWorkflow:
|
|||||||
if (decision.metadata or {}).get("multi_intent_plan")
|
if (decision.metadata or {}).get("multi_intent_plan")
|
||||||
else list(state.get("pending_topics") or [])
|
else list(state.get("pending_topics") or [])
|
||||||
),
|
),
|
||||||
|
# Durable correlation for the primary operation. route_decision
|
||||||
|
# is replaced by state routing on confirmation/resume turns.
|
||||||
|
"multi_intent_plan": (
|
||||||
|
(decision.metadata or {}).get("multi_intent_plan")
|
||||||
|
or state.get("multi_intent_plan")
|
||||||
|
or {}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def billing_agent(self, state):
|
async def billing_agent(self, state):
|
||||||
@@ -605,6 +612,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
"output_guardrails_already_applied": False,
|
"output_guardrails_already_applied": False,
|
||||||
@@ -700,6 +708,7 @@ class AgentWorkflow:
|
|||||||
"pending_topics": drained.pending_topics,
|
"pending_topics": drained.pending_topics,
|
||||||
"handled_topics": drained.handled_topics,
|
"handled_topics": drained.handled_topics,
|
||||||
"agent_responses": drained.agent_responses,
|
"agent_responses": drained.agent_responses,
|
||||||
|
"operation_results": drained.operation_results,
|
||||||
"mcp_results": drained.mcp_results,
|
"mcp_results": drained.mcp_results,
|
||||||
"rag_results": drained.rag_results,
|
"rag_results": drained.rag_results,
|
||||||
**drained.state_patch,
|
**drained.state_patch,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user