mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 10:13:46 +00:00
Melhorias no guardrails de FRASEOLOGY. Melhorias no Transaction Workflow
This commit is contained in:
209
.env
209
.env
@@ -1,209 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# AI AGENT PLATFORM - CONFIGURAÇÃO ÚNICA
|
|
||||||
# Este arquivo é lido por Pydantic Settings no framework e no backend template.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
APP_NAME=ai-agent-template
|
|
||||||
APP_ENV=local
|
|
||||||
LOG_LEVEL=INFO
|
|
||||||
API_HOST=0.0.0.0
|
|
||||||
API_PORT=8000
|
|
||||||
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# LLM - OCI Generative AI como provider principal
|
|
||||||
###############################################################################
|
|
||||||
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
|
||||||
LLM_PROVIDER=oci_openai
|
|
||||||
LLM_TEMPERATURE=0.2
|
|
||||||
LLM_MAX_TOKENS=2048
|
|
||||||
LLM_TIMEOUT_SECONDS=120
|
|
||||||
|
|
||||||
# OCI OpenAI-compatible endpoint
|
|
||||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
|
|
||||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
|
||||||
OCI_GENAI_API_KEY=sk-ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6
|
|
||||||
OCI_GENAI_PROJECT_OCID=
|
|
||||||
|
|
||||||
# OCI_AUTH_MODE=config_file|instance_principal|resource_principal
|
|
||||||
OCI_AUTH_MODE=config_file
|
|
||||||
# OCI SDK / signer / profiles
|
|
||||||
OCI_CONFIG_FILE=~/.oci/config
|
|
||||||
OCI_PROFILE=DEFAULT
|
|
||||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
OCI_REGION=us-chicago-1
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Persistência
|
|
||||||
###############################################################################
|
|
||||||
# Opções: memory, autonomous, mongodb
|
|
||||||
SESSION_REPOSITORY_PROVIDER=autonomous
|
|
||||||
MEMORY_REPOSITORY_PROVIDER=autonomous
|
|
||||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
|
||||||
|
|
||||||
# Autonomous Database
|
|
||||||
ADB_USER=admin
|
|
||||||
ADB_PASSWORD=fjhsdf04954hf
|
|
||||||
ADB_DSN=oradb23aidev_high
|
|
||||||
ADB_WALLET_LOCATION=/ORACLE/DEFAULT/Wallet_ORADB23aiDev
|
|
||||||
ADB_WALLET_PASSWORD=fjhsdf04954hf
|
|
||||||
ADB_TABLE_PREFIX=AGENTFW
|
|
||||||
|
|
||||||
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
|
||||||
MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017
|
|
||||||
MONGODB_DATABASE=agent_platform
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_URL=redis://localhost:6379/0
|
|
||||||
ENABLE_REDIS_CACHE=false
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# RAG / Vector / Graph
|
|
||||||
###############################################################################
|
|
||||||
VECTOR_STORE_PROVIDER=memory
|
|
||||||
GRAPH_STORE_PROVIDER=memory
|
|
||||||
RAG_TOP_K=5
|
|
||||||
EMBEDDING_PROVIDER=mock
|
|
||||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
|
||||||
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Observabilidade
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_LANGFUSE=true
|
|
||||||
LANGFUSE_TRACE_MODE=verbose # Opcional: verbose, compact
|
|
||||||
LANGFUSE_ROOT_SPAN_NAME=agent.gateway_message
|
|
||||||
LANGFUSE_LEGACY_IO_FALLBACK=true
|
|
||||||
LANGFUSE_PUBLIC_KEY=pk-lf-bd9b0c7e-2b8b-4e5b-a382-284a9b4413b3
|
|
||||||
LANGFUSE_SECRET_KEY=sk-lf-5f5cc18d-0bb5-424e-b5d0-cb3664d58c20
|
|
||||||
LANGFUSE_HOST=http://localhost:3005
|
|
||||||
ENABLE_OTEL=false
|
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=
|
|
||||||
OTEL_SERVICE_NAME=ai-agent-template
|
|
||||||
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Analytics / Observer corporativo
|
|
||||||
###############################################################################
|
|
||||||
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
|
||||||
ENABLE_ANALYTICS=false
|
|
||||||
# Providers aceitos: oci_streaming,pubsub,noop
|
|
||||||
ANALYTICS_PROVIDERS=pubsub
|
|
||||||
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
|
||||||
AGENT_PUBSUB_TOPIC=
|
|
||||||
GCP_PUBSUB_TOPIC_PATH=
|
|
||||||
GCP_PROJECT_ID=
|
|
||||||
GCP_PUBSUB_TOPIC=
|
|
||||||
GCP_PUBSUB_TIMEOUT_SECONDS=30
|
|
||||||
# Credencial GCP segue padrão Google:
|
|
||||||
# GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OCI Streaming
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_OCI_STREAMING=false
|
|
||||||
OCI_STREAM_ENDPOINT=
|
|
||||||
OCI_STREAM_OCID=
|
|
||||||
OCI_STREAM_PARTITION_KEY=agent-events
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Guardrails, Judges, Supervisor
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_INPUT_GUARDRAILS=true
|
|
||||||
ENABLE_OUTPUT_GUARDRAILS=true
|
|
||||||
ENABLE_JUDGES=true
|
|
||||||
ENABLE_SUPERVISOR=true
|
|
||||||
ENABLE_OUTPUT_SUPERVISOR=true
|
|
||||||
ENABLE_PARALLEL_GUARDRAILS=true
|
|
||||||
GUARDRAILS_FAIL_FAST=true
|
|
||||||
OUTPUT_SUPERVISOR_MAX_RETRIES=3
|
|
||||||
GUARDRAILS_CONFIG_PATH=./config/guardrails.yaml
|
|
||||||
JUDGES_CONFIG_PATH=./config/judges.yaml
|
|
||||||
PROMPT_POLICY_PATH=./config/prompt_policy.yaml
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Gateway de canais
|
|
||||||
###############################################################################
|
|
||||||
DEFAULT_CHANNEL=web
|
|
||||||
# embedded = backend may parse simple/native channel payloads.
|
|
||||||
# external = backend only accepts GatewayRequest normalized by an external Channel Gateway.
|
|
||||||
FRAMEWORK_CHANNEL_INPUT_MODE=embedded
|
|
||||||
ENABLE_VOICE_ADAPTER=true
|
|
||||||
ENABLE_WHATSAPP_ADAPTER=true
|
|
||||||
ENABLE_TEXT_ADAPTER=true
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
# ENTERPRISE ROUTING
|
|
||||||
#################################################
|
|
||||||
# Arquivo YAML com intents, keywords, políticas de estado e fallback.
|
|
||||||
ROUTING_CONFIG_PATH=./config/routing.yaml
|
|
||||||
# true = usa LLM para classificar quando keywords/estado não resolverem.
|
|
||||||
# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência.
|
|
||||||
ENABLE_LLM_ROUTER=true
|
|
||||||
|
|
||||||
# Semantic route stickiness (optional).
|
|
||||||
# Uses a lightweight LLM profile to decide only CONTINUE vs ROUTE.
|
|
||||||
# There are no regexes or deterministic language rules.
|
|
||||||
ENABLE_ROUTE_STICKINESS=false
|
|
||||||
ROUTE_STICKINESS_LLM_PROFILE=route_continuity
|
|
||||||
ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
|
|
||||||
ROUTE_STICKINESS_HISTORY_TURNS=2
|
|
||||||
ROUTE_STICKINESS_MAX_TOKENS=80
|
|
||||||
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
|
||||||
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# MCP / Tools
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_MCP_TOOLS=true
|
|
||||||
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
|
||||||
TOOLS_CONFIG_PATH=./config/tools.yaml
|
|
||||||
TOOL_POLICIES_PATH=./config/tool_policies.yaml
|
|
||||||
MCP_TOOL_TIMEOUT_SECONDS=30
|
|
||||||
|
|
||||||
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
|
||||||
ROUTING_MODE=router
|
|
||||||
|
|
||||||
# Usage/cost accounting
|
|
||||||
USAGE_REPOSITORY_PROVIDER=autonomous
|
|
||||||
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
|
||||||
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# ConversationSummaryMemory / compressão de contexto conversacional
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
ENABLE_CONVERSATION_SUMMARY_MEMORY=true
|
|
||||||
MEMORY_CONTEXT_STRATEGY=summary
|
|
||||||
MEMORY_HISTORY_LIMIT=80
|
|
||||||
MEMORY_RECENT_MESSAGES_LIMIT=8
|
|
||||||
MEMORY_SUMMARY_TRIGGER_MESSAGES=20
|
|
||||||
MEMORY_MAX_SUMMARY_CHARS=6000
|
|
||||||
MEMORY_SUMMARY_USE_LLM=true
|
|
||||||
MEMORY_INJECT_RECENT_MESSAGES=true
|
|
||||||
MEMORY_INJECT_SUMMARY=true
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# MCP Gateway
|
|
||||||
###############################################################################
|
|
||||||
# true = framework routes tool calls to the dedicated MCP Gateway.
|
|
||||||
# false = framework calls MCP servers directly from mcp_servers.yaml.
|
|
||||||
MCP_GATEWAY_ENABLED=true
|
|
||||||
MCP_GATEWAY_URL=http://localhost:8300
|
|
||||||
MCP_GATEWAY_TIMEOUT_SECONDS=60
|
|
||||||
# MCP_GATEWAY_TOKEN=
|
|
||||||
MCP_GATEWAY_AGENT_ID=telecom_contas
|
|
||||||
MCP_GATEWAY_TENANT_ID=default
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# LONG-TERM MEMORY
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_LONG_TERM_MEMORY=true
|
|
||||||
LONG_TERM_MEMORY_PROVIDER=sqlite
|
|
||||||
LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db
|
|
||||||
LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory
|
|
||||||
# For Autonomous/Oracle, defaults to ${ADB_TABLE_PREFIX}_LONG_TERM_MEMORY
|
|
||||||
# LONG_TERM_MEMORY_ORACLE_TABLE=AGENTFW_LONG_TERM_MEMORY
|
|
||||||
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
|
|
||||||
LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
|
|
||||||
LONG_TERM_MEMORY_AUTO_EXTRACT=true
|
|
||||||
LONG_TERM_MEMORY_INJECT_CONTEXT=true
|
|
||||||
@@ -328,6 +328,47 @@ class ProactiveOfferRail(Guardrail):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_low_risk_phraseology(text: str, reason: str) -> str | None:
|
||||||
|
"""Remove apenas fechamentos/redirecionamentos de baixo risco.
|
||||||
|
|
||||||
|
FRASEOLOGIA continua fail-closed para conteúdo material. Para B4 e ofertas
|
||||||
|
genéricas de continuação, porém, bloquear toda uma resposta grounded piora a
|
||||||
|
UX; nesses casos removemos somente a sentença ofensora.
|
||||||
|
"""
|
||||||
|
normalized_reason = (reason or "").casefold()
|
||||||
|
low_risk = any(token in normalized_reason for token in (
|
||||||
|
"viola b4", "outro canal", "atendimento especializado",
|
||||||
|
"realizar alguma ação", "oferta de ação", "orienta o cliente",
|
||||||
|
))
|
||||||
|
if not low_risk:
|
||||||
|
return None
|
||||||
|
|
||||||
|
forbidden = (
|
||||||
|
"entre em contato", "fale com um atendente", "procure uma loja",
|
||||||
|
"acesse o app", "acesse o site", "atendimento especializado",
|
||||||
|
"área de planos", "area de planos", "é só me avisar",
|
||||||
|
"e so me avisar", "realizar alguma ação", "realizar alguma acao",
|
||||||
|
"gerenciar esses serviços", "gerenciar esses servicos",
|
||||||
|
)
|
||||||
|
sentences = re.split(r"(?<=[.!?])\s+", (text or "").strip())
|
||||||
|
kept: list[str] = []
|
||||||
|
removed = False
|
||||||
|
for sentence in sentences:
|
||||||
|
normalized = sentence.casefold()
|
||||||
|
proactive = (
|
||||||
|
("se quiser" in normalized or "caso queira" in normalized or "se desejar" in normalized)
|
||||||
|
and any(token in normalized for token in ("realizar", "gerenciar", "cancelar", "contratar", "alterar", "ação", "acao"))
|
||||||
|
)
|
||||||
|
if proactive or any(token in normalized for token in forbidden):
|
||||||
|
removed = True
|
||||||
|
continue
|
||||||
|
kept.append(sentence.strip())
|
||||||
|
sanitized = " ".join(x for x in kept if x).strip()
|
||||||
|
if removed and sanitized:
|
||||||
|
return sanitized
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PhraseologyRail(Guardrail):
|
class PhraseologyRail(Guardrail):
|
||||||
"""FRASEOLOGIA calibrado: bloqueia fraseados proibidos do agente."""
|
"""FRASEOLOGIA calibrado: bloqueia fraseados proibidos do agente."""
|
||||||
code = "FRASEOLOGIA"
|
code = "FRASEOLOGIA"
|
||||||
@@ -339,9 +380,26 @@ class PhraseologyRail(Guardrail):
|
|||||||
_llm(ctx), "FRASEOLOGIA", {"text": text or "", "context": ctx},
|
_llm(ctx), "FRASEOLOGIA", {"text": text or "", "context": ctx},
|
||||||
profile_name="grl", component_name="guardrail.fraseologia", generation_name="guardrail.fraseologia",
|
profile_name="grl", component_name="guardrail.fraseologia", generation_name="guardrail.fraseologia",
|
||||||
)
|
)
|
||||||
|
allowed = bool(out.get("allowed", True))
|
||||||
|
reason = str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado")
|
||||||
|
if not allowed:
|
||||||
|
sanitized = _sanitize_low_risk_phraseology(text or "", reason)
|
||||||
|
if sanitized:
|
||||||
return RailDecision(
|
return RailDecision(
|
||||||
code=self.code, allowed=bool(out.get("allowed", True)),
|
code=self.code,
|
||||||
reason=str(out.get("reason") or out.get("label") or "FRASEOLOGIA avaliado"),
|
allowed=True,
|
||||||
|
reason=f"FRASEOLOGIA sanitizada: {reason}",
|
||||||
|
sanitized_text=sanitized,
|
||||||
|
metadata={
|
||||||
|
"mechanism": "llm_rail+deterministic_sanitize",
|
||||||
|
"data": out,
|
||||||
|
"calibrated": True,
|
||||||
|
"original_allowed": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return RailDecision(
|
||||||
|
code=self.code, allowed=allowed,
|
||||||
|
reason=reason,
|
||||||
sanitized_text=text, metadata={"mechanism": "llm_rail", "data": out, "calibrated": True},
|
sanitized_text=text, metadata={"mechanism": "llm_rail", "data": out, "calibrated": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1167,7 +1167,25 @@ class AgentRuntimeMixin:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _select_transactional_tool(self, tools: list[str], text: str) -> str | None:
|
def _select_transactional_tool(self, tools: list[str], text: str) -> str | None:
|
||||||
return self._transactional_action_match(text, tools)
|
"""Seleciona a ação transacional da intent atual.
|
||||||
|
|
||||||
|
O match por ``selection_keywords`` continua tendo precedência. Porém, depois
|
||||||
|
que o EnterpriseRouter já restringiu ``tools`` às capabilities da intent,
|
||||||
|
uma única tool transacional é uma escolha determinística e segura. Isso
|
||||||
|
evita perder frases naturais como ``quero cancelar TIM Fashion Mensal`` ou
|
||||||
|
``não contratei esse serviço`` só porque elas não repetem literalmente uma
|
||||||
|
keyword de ``tools.yaml``.
|
||||||
|
"""
|
||||||
|
matched = self._transactional_action_match(text, tools)
|
||||||
|
if matched:
|
||||||
|
return matched
|
||||||
|
|
||||||
|
transactional = [
|
||||||
|
tool
|
||||||
|
for tool in tools
|
||||||
|
if self._resolve_tool_execution_policy(tool).get("operation_type") == "transactional"
|
||||||
|
]
|
||||||
|
return transactional[0] if len(transactional) == 1 else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _agent_state_prefix(agent_name: str | None) -> str:
|
def _agent_state_prefix(agent_name: str | None) -> str:
|
||||||
@@ -1521,6 +1539,11 @@ class AgentRuntimeMixin:
|
|||||||
**previous_args,
|
**previous_args,
|
||||||
**{k: v for k, v in new_args.items() if v not in (None, "", [], {})},
|
**{k: v for k, v in new_args.items() if v not in (None, "", [], {})},
|
||||||
}
|
}
|
||||||
|
# Execute parameter extraction before deciding whether the workflow
|
||||||
|
# must enter COLLECTING_PARAMETERS. Otherwise parameters declared
|
||||||
|
# with strategy=llm in mcp_parameter_mapping.yaml are invisible to
|
||||||
|
# the deterministic transaction state machine.
|
||||||
|
arguments = await self._extract_mcp_parameters(tool_name, arguments, state)
|
||||||
policy = self._resolve_tool_execution_policy(tool_name, arguments)
|
policy = self._resolve_tool_execution_policy(tool_name, arguments)
|
||||||
missing = self._missing_required_arguments(policy, arguments)
|
missing = self._missing_required_arguments(policy, arguments)
|
||||||
if missing:
|
if missing:
|
||||||
@@ -1653,6 +1676,9 @@ class AgentRuntimeMixin:
|
|||||||
aliases=aliases,
|
aliases=aliases,
|
||||||
extra_args=self._extract_action_arguments(text),
|
extra_args=self._extract_action_arguments(text),
|
||||||
)
|
)
|
||||||
|
# Extract parameters (including LLM-declared extraction rules) before
|
||||||
|
# validating required fields and before persisting the pending call.
|
||||||
|
action_args = await self._extract_mcp_parameters(selected_action, action_args, state)
|
||||||
policy = self._resolve_tool_execution_policy(selected_action, action_args)
|
policy = self._resolve_tool_execution_policy(selected_action, action_args)
|
||||||
selected = {"tool_name": selected_action, "arguments": action_args}
|
selected = {"tool_name": selected_action, "arguments": action_args}
|
||||||
state["selected_tool_call"] = selected
|
state["selected_tool_call"] = selected
|
||||||
|
|||||||
Reference in New Issue
Block a user