mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 18:23:46 +00:00
new feature: Integration with kbdb Autonomous
This commit is contained in:
22
.env.example
22
.env.example
@@ -212,3 +212,25 @@ LONG_TERM_MEMORY_INJECT_CONTEXT=true
|
|||||||
# Keep disabled in the generic framework; agents may enable their own YAML mapping.
|
# Keep disabled in the generic framework; agents may enable their own YAML mapping.
|
||||||
OBSERVABILITY_CODE_MAPPING_ENABLED=false
|
OBSERVABILITY_CODE_MAPPING_ENABLED=false
|
||||||
OBSERVABILITY_CODE_MAPPING_PATH=
|
OBSERVABILITY_CODE_MAPPING_PATH=
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# RAG provider selection (mutually exclusive at runtime)
|
||||||
|
###############################################################################
|
||||||
|
# standard = RAG original do agent_framework_oci (default, backward compatible)
|
||||||
|
# kbdb = KBDB enterprise via PKG_KB_SERVING.SEARCH_KNOWLEDGE_BASE
|
||||||
|
RAG_PROVIDER=standard
|
||||||
|
|
||||||
|
# Somente usados quando RAG_PROVIDER=kbdb. Se vazios, credenciais caem para ADB_*.
|
||||||
|
KBDB_DB_USER=
|
||||||
|
KBDB_DB_PASSWORD=
|
||||||
|
KBDB_DB_DSN=
|
||||||
|
KBDB_DB_WALLET_LOCATION=
|
||||||
|
KBDB_DB_WALLET_PASSWORD=
|
||||||
|
KBDB_SEARCH_TYPE=hybrid
|
||||||
|
KBDB_NODE_EXPANSION=true
|
||||||
|
KBDB_NODE_MAX_RELATED=8
|
||||||
|
KBDB_GRAPH_CROSS_REF=false
|
||||||
|
KBDB_MAX_CROSS_REF_HOPS=1
|
||||||
|
KBDB_DOCUMENT_TYPE=customer_safe
|
||||||
|
KBDB_METADATA_JSON=
|
||||||
|
KBDB_MIN_SCORE=
|
||||||
|
|||||||
@@ -11176,3 +11176,7 @@ A adoção das funcionalidades do `Tuning-Performance` pode proporcionar:
|
|||||||
* comportamento consistente entre diferentes agentes e projetos.
|
* comportamento consistente entre diferentes agentes e projetos.
|
||||||
|
|
||||||
O conteúdo desta pasta deve ser tratado como uma extensão adicional do framework. Sua utilização requer implementação, configuração, testes funcionais e validação das regras de negócio antes da implantação em produção.
|
O conteúdo desta pasta deve ser tratado como uma extensão adicional do framework. Sua utilização requer implementação, configuração, testes funcionais e validação das regras de negócio antes da implantação em produção.
|
||||||
|
|
||||||
|
### RAG provider alternativo (KBDB Enterprise)
|
||||||
|
|
||||||
|
O RAG original continua sendo o default (`RAG_PROVIDER=standard`). Para usar a arquitetura KBDB enterprise como alternativa de serving, configure `RAG_PROVIDER=kbdb`. Os agentes continuam usando o mesmo `RagService`/`_retrieve_rag_context()` e os dois backends não são executados simultaneamente. Consulte `docs/RAG_PROVIDER_KBDB.md`.
|
||||||
|
|||||||
Binary file not shown.
@@ -14,45 +14,38 @@ CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
|||||||
# LLM - OCI Generative AI como provider principal
|
# LLM - OCI Generative AI como provider principal
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
||||||
LLM_PROVIDER=oci_sdk
|
LLM_PROVIDER=oci_openai
|
||||||
LLM_TEMPERATURE=0.2
|
LLM_TEMPERATURE=0.2
|
||||||
LLM_MAX_TOKENS=2048
|
LLM_MAX_TOKENS=2048
|
||||||
LLM_TIMEOUT_SECONDS=120
|
LLM_TIMEOUT_SECONDS=120
|
||||||
|
|
||||||
# OCI OpenAI-compatible endpoint
|
# OCI OpenAI-compatible endpoint
|
||||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
|
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
|
||||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
OCI_GENAI_MODEL=openai.gpt-4.1
|
||||||
OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
OCI_GENAI_API_KEY=sk-ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6
|
||||||
OCI_GENAI_PROJECT_OCID=
|
OCI_GENAI_PROJECT_OCID=
|
||||||
|
|
||||||
#OCI_GENAI_BASE_URL=https://pegruagntaiatenddev.pe.inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com
|
|
||||||
#OCI_GENAI_MODEL=openai.gpt-4.1
|
|
||||||
#OCI_GENAI_API_KEY=
|
|
||||||
#OCI_GENAI_PROJECT_OCID=
|
|
||||||
|
|
||||||
|
|
||||||
# OCI_AUTH_MODE=config_file|instance_principal|resource_principal
|
|
||||||
OCI_AUTH_MODE=config_file
|
|
||||||
# OCI SDK / signer / profiles
|
# OCI SDK / signer / profiles
|
||||||
OCI_CONFIG_FILE=~/.oci/config
|
OCI_CONFIG_FILE=~/.oci/config
|
||||||
OCI_PROFILE=LATINOAMERICA-Chicago
|
OCI_PROFILE=DEFAULT
|
||||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
OCI_REGION=us-chicago-1
|
OCI_REGION=us-chicago-1
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Persistência
|
# Persistência
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Opções: memory, autonomous, mongodb
|
# Opções: memory, autonomous, mongodb
|
||||||
SESSION_REPOSITORY_PROVIDER=autonomous
|
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||||
MEMORY_REPOSITORY_PROVIDER=autonomous
|
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||||
|
SQLITE_DB_PATH=./data/agent_framework.db
|
||||||
|
|
||||||
# Autonomous Database
|
# Autonomous Database
|
||||||
ADB_USER=admin
|
ADB_USER=admin
|
||||||
ADB_PASSWORD=Moniquinha19721972
|
ADB_PASSWORD=fjhsdf04954hf
|
||||||
ADB_DSN=oradb23ai_high
|
ADB_DSN=oradb23aidev_high
|
||||||
ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai
|
ADB_WALLET_LOCATION=/ORACLE/DEFAULT/Wallet_ORADB23aiDev
|
||||||
ADB_WALLET_PASSWORD=Moniquinha1972
|
ADB_WALLET_PASSWORD=fjhsdf04954hf
|
||||||
ADB_TABLE_PREFIX=AGENTFW
|
ADB_TABLE_PREFIX=AGENTFW
|
||||||
|
|
||||||
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
||||||
@@ -66,10 +59,10 @@ ENABLE_REDIS_CACHE=false
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# RAG / Vector / Graph
|
# RAG / Vector / Graph
|
||||||
###############################################################################
|
###############################################################################
|
||||||
VECTOR_STORE_PROVIDER=autonomous
|
VECTOR_STORE_PROVIDER=sqlite
|
||||||
GRAPH_STORE_PROVIDER=autonomous
|
GRAPH_STORE_PROVIDER=sqlite
|
||||||
RAG_TOP_K=5
|
RAG_TOP_K=5
|
||||||
EMBEDDING_PROVIDER=oci
|
EMBEDDING_PROVIDER=mock
|
||||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
||||||
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
||||||
|
|
||||||
@@ -77,21 +70,14 @@ RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
|||||||
# Observabilidade
|
# Observabilidade
|
||||||
###############################################################################
|
###############################################################################
|
||||||
ENABLE_LANGFUSE=true
|
ENABLE_LANGFUSE=true
|
||||||
# Opcional: verbose, compact
|
LANGFUSE_TRACE_MODE=compact # Opcional: verbose, compact
|
||||||
LANGFUSE_TRACE_MODE=compact
|
LANGFUSE_PUBLIC_KEY=pk-lf-2f9da109-5b0f-4c78-b61d-9598ed787eba
|
||||||
# Nome customizado do trace pai, ex.: backoffice.checklist.workflow ou backoffice.emulador.workflow
|
LANGFUSE_SECRET_KEY=sk-lf-a4cb0cdd-f2ea-4468-9911-cebeb91ba944
|
||||||
LANGFUSE_COMPACT_VISIBLE_EVENT_PREFIXES=AGA.,NOC., IC.
|
|
||||||
LANGFUSE_COMPACT_SUPPRESSED_PREFIXES=llm.chat_completion
|
|
||||||
LANGFUSE_IGNORE_HEALTHCHECKS=true
|
|
||||||
LANGFUSE_IGNORED_PATHS=/health,/ready,/metrics
|
|
||||||
LANGFUSE_PUBLIC_KEY=pk-lf-4a1e3921-5158-4fd3-a16d-7a77549fb312
|
|
||||||
LANGFUSE_SECRET_KEY=sk-lf-efc6fd59-c5ec-4858-b6ec-4aa129734915
|
|
||||||
LANGFUSE_HOST=http://localhost:3005
|
LANGFUSE_HOST=http://localhost:3005
|
||||||
ENABLE_OTEL=false
|
ENABLE_OTEL=false
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=
|
OTEL_EXPORTER_OTLP_ENDPOINT=
|
||||||
OTEL_SERVICE_NAME=ai-agent-template
|
OTEL_SERVICE_NAME=ai-agent-template
|
||||||
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
||||||
ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Analytics / Observer corporativo
|
# Analytics / Observer corporativo
|
||||||
@@ -99,7 +85,7 @@ ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false
|
|||||||
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
||||||
ENABLE_ANALYTICS=false
|
ENABLE_ANALYTICS=false
|
||||||
# Providers aceitos: oci_streaming,pubsub,noop
|
# Providers aceitos: oci_streaming,pubsub,noop
|
||||||
ANALYTICS_PROVIDERS=oci_streaming
|
ANALYTICS_PROVIDERS=pubsub
|
||||||
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
||||||
AGENT_PUBSUB_TOPIC=
|
AGENT_PUBSUB_TOPIC=
|
||||||
GCP_PUBSUB_TOPIC_PATH=
|
GCP_PUBSUB_TOPIC_PATH=
|
||||||
@@ -162,6 +148,7 @@ ROUTE_STICKINESS_HISTORY_TURNS=2
|
|||||||
ROUTE_STICKINESS_MAX_TOKENS=80
|
ROUTE_STICKINESS_MAX_TOKENS=80
|
||||||
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
||||||
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
||||||
|
SESSION_ALREADY_ENDED_MESSAGE=Este atendimento já foi encerrado. Inicie uma nova sessão para continuar.
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# MCP / Tools
|
# MCP / Tools
|
||||||
@@ -169,13 +156,14 @@ END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
|||||||
ENABLE_MCP_TOOLS=true
|
ENABLE_MCP_TOOLS=true
|
||||||
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
||||||
TOOLS_CONFIG_PATH=./config/tools.yaml
|
TOOLS_CONFIG_PATH=./config/tools.yaml
|
||||||
|
TOOL_POLICIES_PATH=./config/tool_policies.yaml
|
||||||
MCP_TOOL_TIMEOUT_SECONDS=30
|
MCP_TOOL_TIMEOUT_SECONDS=30
|
||||||
|
|
||||||
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
||||||
ROUTING_MODE=router
|
ROUTING_MODE=router
|
||||||
|
|
||||||
# Usage/cost accounting
|
# Usage/cost accounting
|
||||||
USAGE_REPOSITORY_PROVIDER=autonomous
|
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||||
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
||||||
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
||||||
|
|
||||||
@@ -192,6 +180,18 @@ MEMORY_SUMMARY_USE_LLM=true
|
|||||||
MEMORY_INJECT_RECENT_MESSAGES=true
|
MEMORY_INJECT_RECENT_MESSAGES=true
|
||||||
MEMORY_INJECT_SUMMARY=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
|
# LONG-TERM MEMORY
|
||||||
###############################################################################
|
###############################################################################
|
||||||
94
docs/RAG_PROVIDER_KBDB.md
Normal file
94
docs/RAG_PROVIDER_KBDB.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# RAG alternativo: Standard x KBDB Enterprise
|
||||||
|
|
||||||
|
O framework passa a suportar dois backends de retrieval pelo mesmo contrato `RagService`, sem alterar os agentes nem `_retrieve_rag_context()`.
|
||||||
|
|
||||||
|
## Seleção
|
||||||
|
|
||||||
|
```env
|
||||||
|
RAG_PROVIDER=standard # default: comportamento anterior
|
||||||
|
# ou
|
||||||
|
RAG_PROVIDER=kbdb # KBDB enterprise
|
||||||
|
```
|
||||||
|
|
||||||
|
A seleção é exclusiva por processo. Os dois RAGs não executam juntos e não compartilham vector store, graph store ou ingestão.
|
||||||
|
|
||||||
|
## `standard`
|
||||||
|
|
||||||
|
Mantém integralmente o RAG já existente no `agent_framework_oci`: `VECTOR_STORE_PROVIDER`, `GRAPH_STORE_PROVIDER`, embedding, query rewrite, compression, retrieval guardrails e geração continuam válidos.
|
||||||
|
|
||||||
|
## `kbdb`
|
||||||
|
|
||||||
|
O framework integra somente a porta estável de serving do projeto KBDB:
|
||||||
|
|
||||||
|
`PKG_KB_SERVING.SEARCH_KNOWLEDGE_BASE`
|
||||||
|
|
||||||
|
O pipeline enterprise continua externo ao runtime do agente e preserva sua própria arquitetura RAW → SILVER → GOLD, HVI/hybrid search, property graph, publicação, lifecycle, auditoria e observabilidade.
|
||||||
|
|
||||||
|
O envelope KBDB é adaptado para `RagResult`/`VectorDocument`; portanto os agentes existentes continuam chamando `_retrieve_rag_context()` e os retrieval guardrails do framework continuam depois do retrieval.
|
||||||
|
|
||||||
|
### Configuração
|
||||||
|
|
||||||
|
```env
|
||||||
|
RAG_PROVIDER=kbdb
|
||||||
|
RAG_TOP_K=5
|
||||||
|
KBDB_DB_USER=KB_USER
|
||||||
|
KBDB_DB_PASSWORD=...
|
||||||
|
KBDB_DB_DSN=...
|
||||||
|
KBDB_DB_WALLET_LOCATION=...
|
||||||
|
KBDB_DB_WALLET_PASSWORD=...
|
||||||
|
KBDB_SEARCH_TYPE=hybrid
|
||||||
|
KBDB_NODE_EXPANSION=true
|
||||||
|
KBDB_NODE_MAX_RELATED=8
|
||||||
|
KBDB_GRAPH_CROSS_REF=false
|
||||||
|
KBDB_MAX_CROSS_REF_HOPS=1
|
||||||
|
KBDB_DOCUMENT_TYPE=customer_safe
|
||||||
|
KBDB_METADATA_JSON=
|
||||||
|
KBDB_MIN_SCORE=
|
||||||
|
```
|
||||||
|
|
||||||
|
Quando `RAG_PROVIDER=kbdb`, `KBDB_DB_USER`, `KBDB_DB_PASSWORD` e `KBDB_DB_DSN` são obrigatórios. O KBDB usa conexão isolada porque pode residir em outro Autonomous. `KBDB_DB_DSN` segue a mesma semântica de `ADB_DSN`: use o alias TNS existente no `tnsnames.ora` da wallet indicada por `KBDB_DB_WALLET_LOCATION`, e não uma URL `tcps://...`.
|
||||||
|
|
||||||
|
## Isolamento e compatibilidade
|
||||||
|
|
||||||
|
- `RAG_PROVIDER=standard` não importa nem conecta ao KBDB.
|
||||||
|
- `RAG_PROVIDER=kbdb` não instancia vector/graph stores do RAG padrão.
|
||||||
|
- Ingestão por `RagService.add_documents()` não é permitida no modo KBDB: deve passar pelo pipeline/publicação KBDB.
|
||||||
|
- Query rewrite e context compression continuam opcionais e são aplicados pela camada comum do framework.
|
||||||
|
- `AgentRuntimeMixin._retrieve_rag_context()` e os agentes permanecem inalterados.
|
||||||
|
- Falhas do KBDB seguem a semântica existente do framework: retrieval é evidência auxiliar e a exceção é convertida em metadata técnica sem derrubar a jornada.
|
||||||
|
|
||||||
|
|
||||||
|
## Resposta direta de tool e RAG
|
||||||
|
|
||||||
|
O framework não considera mais que um resultado MCP estruturado é, por si só, uma resposta suficiente ao usuário.
|
||||||
|
|
||||||
|
Uma política `response.renderer` define somente **como** apresentar o resultado. Ela não encerra o fluxo antes de RAG/LLM. Para uma tool deliberadamente produzir uma resposta final direta, a aplicação deve declarar explicitamente:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
response:
|
||||||
|
mode: renderer
|
||||||
|
renderer: meu.renderer
|
||||||
|
direct: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Sem `direct: true`, o resultado da tool permanece como evidência MCP e o fluxo segue para `_retrieve_rag_context()` e composição LLM. Isso permite, por exemplo, que uma consulta operacional de plano seja combinada com conhecimento documental do KBDB quando a pergunta pedir regras, políticas ou explicações.
|
||||||
|
|
||||||
|
O core do framework não possui fallback por nome de tool (`consultar_plano`, `consultar_pedido`, etc.). Regras de apresentação pertencem à aplicação/domínio.
|
||||||
|
|
||||||
|
|
||||||
|
## Suficiência MCP e grounding
|
||||||
|
|
||||||
|
Um resultado MCP bem-sucedido **não** faz o framework pular RAG automaticamente.
|
||||||
|
O domínio só pode declarar suficiência documental explicitamente no payload com
|
||||||
|
`rag_sufficient=true` ou `knowledge_sufficient=true`. Essa decisão é genérica e
|
||||||
|
não depende do nome da tool nem de palavras-chave de telecom/retail.
|
||||||
|
|
||||||
|
No provider `kbdb`, `KBDB_GROUNDED_ONLY=true` é o padrão. Quando a busca KBDB
|
||||||
|
retorna vazia, bloqueada ou com erro, a composição LLM pode usar fatos comprovados
|
||||||
|
por MCP/business context, mas não pode completar a parte documental com conhecimento
|
||||||
|
paramétrico do modelo. Deve informar que não há evidência suficiente na base.
|
||||||
|
|
||||||
|
Eventos do ProductAgent registram `IC.PRODUCT_RAG_CONTEXT_EVALUATED` em toda
|
||||||
|
tentativa/decisão e `IC.PRODUCT_RAG_CONTEXT_RETRIEVED` somente quando há contexto
|
||||||
|
recuperado. Os metadados incluem `provider`, `status`, `document_count`, `reason`,
|
||||||
|
`error`, `query`, `namespace` e `latency_ms`.
|
||||||
@@ -190,6 +190,7 @@ src/agent_framework/rag/__init__.py
|
|||||||
src/agent_framework/rag/embedding_provider.py
|
src/agent_framework/rag/embedding_provider.py
|
||||||
src/agent_framework/rag/graph_store.py
|
src/agent_framework/rag/graph_store.py
|
||||||
src/agent_framework/rag/ingest.py
|
src/agent_framework/rag/ingest.py
|
||||||
|
src/agent_framework/rag/kbdb_service.py
|
||||||
src/agent_framework/rag/rag_service.py
|
src/agent_framework/rag/rag_service.py
|
||||||
src/agent_framework/rag/vector_store.py
|
src/agent_framework/rag/vector_store.py
|
||||||
src/agent_framework/repositories/__init__.py
|
src/agent_framework/repositories/__init__.py
|
||||||
@@ -215,6 +216,7 @@ src/agent_framework/supervisor/router_supervisor.py
|
|||||||
src/agent_framework/supervisor/supervisor.py
|
src/agent_framework/supervisor/supervisor.py
|
||||||
src/agent_framework/workflows/__init__.py
|
src/agent_framework/workflows/__init__.py
|
||||||
src/agent_framework/workflows/graph.py
|
src/agent_framework/workflows/graph.py
|
||||||
|
src/agent_framework/workflows/input_contract.py
|
||||||
src/agent_framework/workflows/models.py
|
src/agent_framework/workflows/models.py
|
||||||
src/agent_framework/workflows/registry.py
|
src/agent_framework/workflows/registry.py
|
||||||
src/agent_framework/workflows/repository.py
|
src/agent_framework/workflows/repository.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.
BIN
libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-314.pyc
vendored
Normal file
BIN
libs/agent_framework/src/agent_framework/cache/__pycache__/__init__.cpython-314.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-314.pyc
vendored
Normal file
BIN
libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-314.pyc
vendored
Normal file
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.
@@ -18,6 +18,7 @@ returned using CheckpointTuple; otherwise a simple dict is returned for tests.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Any, AsyncIterator, Iterator
|
from typing import Any, AsyncIterator, Iterator
|
||||||
@@ -94,6 +95,200 @@ def _strict_json_value(value: Any, *, path: str = "$") -> Any:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_TYPED_SERDE_MARKER = "__agent_framework_langgraph_typed__"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_langgraph_interrupt(value: Any) -> bool:
|
||||||
|
"""Return True only for LangGraph's native Interrupt primitive.
|
||||||
|
|
||||||
|
Keep this deliberately narrow. The repository contract remains strict JSON
|
||||||
|
everywhere else so an unrelated future runtime object cannot silently change
|
||||||
|
the durable checkpoint format.
|
||||||
|
"""
|
||||||
|
cls = type(value)
|
||||||
|
return cls.__module__ == "langgraph.types" and cls.__qualname__ == "Interrupt"
|
||||||
|
|
||||||
|
|
||||||
|
def _typed_serde_envelope(serializer: Any, value: Any, *, path: str) -> dict[str, Any]:
|
||||||
|
dumps_typed = getattr(serializer, "dumps_typed", None)
|
||||||
|
if not callable(dumps_typed):
|
||||||
|
raise TypeError(f"Serializer LangGraph não suporta dumps_typed em {path}")
|
||||||
|
type_name, payload = dumps_typed(value)
|
||||||
|
if not isinstance(payload, (bytes, bytearray)):
|
||||||
|
raise TypeError(
|
||||||
|
f"Serializer LangGraph retornou payload inválido em {path}: "
|
||||||
|
f"{type(payload).__module__}.{type(payload).__qualname__}"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
_TYPED_SERDE_MARKER: True,
|
||||||
|
"type": str(type_name),
|
||||||
|
"data": base64.b64encode(bytes(payload)).decode("ascii"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _encode_checkpoint_value(
|
||||||
|
serializer: Any,
|
||||||
|
value: Any,
|
||||||
|
*,
|
||||||
|
path: str = "$.checkpoint",
|
||||||
|
in_channel_values: bool = False,
|
||||||
|
in_interrupt_channel: bool = False,
|
||||||
|
) -> Any:
|
||||||
|
"""Serialize the checkpoint without widening its historical JSON contract.
|
||||||
|
|
||||||
|
The only typed exception is LangGraph's native ``Interrupt`` and only while
|
||||||
|
traversing the special ``__interrupt__`` channel under ``channel_values``.
|
||||||
|
Every other checkpoint value keeps the strict-JSON behavior, so future
|
||||||
|
runtime/business objects cannot silently start using typed serde.
|
||||||
|
"""
|
||||||
|
if value is None or isinstance(value, (str, int, float, bool)):
|
||||||
|
return value
|
||||||
|
if isinstance(value, dict):
|
||||||
|
out: dict[str, Any] = {}
|
||||||
|
for key, item in value.items():
|
||||||
|
skey = str(key)
|
||||||
|
child_in_channel_values = in_channel_values or (
|
||||||
|
path == "$.checkpoint" and skey == "channel_values"
|
||||||
|
)
|
||||||
|
child_in_interrupt_channel = in_interrupt_channel or (
|
||||||
|
child_in_channel_values and skey == "__interrupt__"
|
||||||
|
)
|
||||||
|
out[skey] = _encode_checkpoint_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}.{skey}",
|
||||||
|
in_channel_values=child_in_channel_values,
|
||||||
|
in_interrupt_channel=child_in_interrupt_channel,
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
return [
|
||||||
|
_encode_checkpoint_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}[{idx}]",
|
||||||
|
in_channel_values=in_channel_values,
|
||||||
|
in_interrupt_channel=in_interrupt_channel,
|
||||||
|
)
|
||||||
|
for idx, item in enumerate(value)
|
||||||
|
]
|
||||||
|
if in_channel_values and in_interrupt_channel and _is_langgraph_interrupt(value):
|
||||||
|
return _typed_serde_envelope(serializer, value, path=path)
|
||||||
|
return _strict_json_value(value, path=path)
|
||||||
|
|
||||||
|
|
||||||
|
def _decode_checkpoint_value(
|
||||||
|
serializer: Any,
|
||||||
|
value: Any,
|
||||||
|
*,
|
||||||
|
path: str = "$.checkpoint",
|
||||||
|
in_channel_values: bool = False,
|
||||||
|
in_interrupt_channel: bool = False,
|
||||||
|
) -> Any:
|
||||||
|
"""Inverse of :func:`_encode_checkpoint_value`, with the same narrow scope."""
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if in_channel_values and in_interrupt_channel and value.get(_TYPED_SERDE_MARKER) is True:
|
||||||
|
loads_typed = getattr(serializer, "loads_typed", None)
|
||||||
|
if not callable(loads_typed):
|
||||||
|
raise TypeError("Serializer LangGraph não suporta loads_typed")
|
||||||
|
type_name = value.get("type")
|
||||||
|
raw = value.get("data")
|
||||||
|
if not isinstance(type_name, str) or not isinstance(raw, str):
|
||||||
|
raise TypeError(f"Envelope de serialização LangGraph inválido em {path}")
|
||||||
|
return loads_typed((type_name, base64.b64decode(raw.encode("ascii"))))
|
||||||
|
out: dict[str, Any] = {}
|
||||||
|
for key, item in value.items():
|
||||||
|
skey = str(key)
|
||||||
|
child_in_channel_values = in_channel_values or (
|
||||||
|
path == "$.checkpoint" and skey == "channel_values"
|
||||||
|
)
|
||||||
|
child_in_interrupt_channel = in_interrupt_channel or (
|
||||||
|
child_in_channel_values and skey == "__interrupt__"
|
||||||
|
)
|
||||||
|
out[skey] = _decode_checkpoint_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}.{skey}",
|
||||||
|
in_channel_values=child_in_channel_values,
|
||||||
|
in_interrupt_channel=child_in_interrupt_channel,
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [
|
||||||
|
_decode_checkpoint_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}[{idx}]",
|
||||||
|
in_channel_values=in_channel_values,
|
||||||
|
in_interrupt_channel=in_interrupt_channel,
|
||||||
|
)
|
||||||
|
for idx, item in enumerate(value)
|
||||||
|
]
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _encode_pending_write_value(
|
||||||
|
serializer: Any,
|
||||||
|
value: Any,
|
||||||
|
*,
|
||||||
|
path: str,
|
||||||
|
in_interrupt_channel: bool = False,
|
||||||
|
) -> Any:
|
||||||
|
"""Serialize pending writes with a narrowly-scoped Interrupt exception.
|
||||||
|
|
||||||
|
``Interrupt`` is accepted only in LangGraph's special ``__interrupt__``
|
||||||
|
branch (either the write channel itself or a nested ``__interrupt__`` key).
|
||||||
|
Other non-JSON objects still fail loudly.
|
||||||
|
"""
|
||||||
|
if value is None or isinstance(value, (str, int, float, bool)):
|
||||||
|
return value
|
||||||
|
if isinstance(value, dict):
|
||||||
|
out: dict[str, Any] = {}
|
||||||
|
for key, item in value.items():
|
||||||
|
skey = str(key)
|
||||||
|
child_interrupt = in_interrupt_channel or skey == "__interrupt__"
|
||||||
|
out[skey] = _encode_pending_write_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}.{skey}",
|
||||||
|
in_interrupt_channel=child_interrupt,
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
return [
|
||||||
|
_encode_pending_write_value(
|
||||||
|
serializer,
|
||||||
|
item,
|
||||||
|
path=f"{path}[{idx}]",
|
||||||
|
in_interrupt_channel=in_interrupt_channel,
|
||||||
|
)
|
||||||
|
for idx, item in enumerate(value)
|
||||||
|
]
|
||||||
|
if in_interrupt_channel and _is_langgraph_interrupt(value):
|
||||||
|
return _typed_serde_envelope(serializer, value, path=path)
|
||||||
|
return _strict_json_value(value, path=path)
|
||||||
|
|
||||||
|
|
||||||
|
def _serde_decode(serializer: Any, value: Any) -> Any:
|
||||||
|
"""Restore values encoded by :func:`_serde_encode` recursively."""
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if value.get(_TYPED_SERDE_MARKER) is True:
|
||||||
|
loads_typed = getattr(serializer, "loads_typed", None)
|
||||||
|
if not callable(loads_typed):
|
||||||
|
raise TypeError("Serializer LangGraph não suporta loads_typed")
|
||||||
|
type_name = value.get("type")
|
||||||
|
raw = value.get("data")
|
||||||
|
if not isinstance(type_name, str) or not isinstance(raw, str):
|
||||||
|
raise TypeError("Envelope de serialização LangGraph inválido")
|
||||||
|
return loads_typed((type_name, base64.b64decode(raw.encode("ascii"))))
|
||||||
|
return {key: _serde_decode(serializer, item) for key, item in value.items()}
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [_serde_decode(serializer, item) for item in value]
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def _normalize_checkpoint(checkpoint: Any) -> dict[str, Any]:
|
def _normalize_checkpoint(checkpoint: Any) -> dict[str, Any]:
|
||||||
checkpoint = _parse_legacy_json_container(checkpoint, dict)
|
checkpoint = _parse_legacy_json_container(checkpoint, dict)
|
||||||
if not isinstance(checkpoint, dict):
|
if not isinstance(checkpoint, dict):
|
||||||
@@ -277,6 +472,7 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
|
|||||||
|
|
||||||
def __init__(self, settings, repository=None):
|
def __init__(self, settings, repository=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.serde = getattr(self, "serde", None)
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.repository = repository or create_checkpoint_repository(settings)
|
self.repository = repository or create_checkpoint_repository(settings)
|
||||||
self._loop: asyncio.AbstractEventLoop | None = None
|
self._loop: asyncio.AbstractEventLoop | None = None
|
||||||
@@ -302,8 +498,10 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
|
|||||||
# Second-stage protection: never re-bind the full persisted RunnableConfig.
|
# Second-stage protection: never re-bind the full persisted RunnableConfig.
|
||||||
# Rebuild only the durable identifiers, as official LangGraph savers do.
|
# Rebuild only the durable identifiers, as official LangGraph savers do.
|
||||||
config = _canonical_checkpoint_config(payload, request_config)
|
config = _canonical_checkpoint_config(payload, request_config)
|
||||||
checkpoint = _strip_runtime_refs(_normalize_checkpoint(payload.get("checkpoint") or {}))
|
checkpoint_value = _decode_checkpoint_value(self.serde, payload.get("checkpoint") or {})
|
||||||
metadata = _strip_runtime_refs(_normalize_metadata(payload.get("metadata") or {}))
|
metadata_value = payload.get("metadata") or {}
|
||||||
|
checkpoint = _strip_runtime_refs(_normalize_checkpoint(checkpoint_value))
|
||||||
|
metadata = _strip_runtime_refs(_normalize_metadata(metadata_value))
|
||||||
raw_parent_config = payload.get("parent_config")
|
raw_parent_config = payload.get("parent_config")
|
||||||
if isinstance(raw_parent_config, dict):
|
if isinstance(raw_parent_config, dict):
|
||||||
parent_payload = {
|
parent_payload = {
|
||||||
@@ -317,8 +515,9 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
|
|||||||
parent_config = _canonical_checkpoint_config(parent_payload)
|
parent_config = _canonical_checkpoint_config(parent_payload)
|
||||||
else:
|
else:
|
||||||
parent_config = None
|
parent_config = None
|
||||||
|
pending_value = _serde_decode(self.serde, payload.get("pending_writes") or [])
|
||||||
pending_writes = _normalize_pending_writes(
|
pending_writes = _normalize_pending_writes(
|
||||||
_strip_runtime_refs(payload.get("pending_writes") or [])
|
_strip_runtime_refs(pending_value)
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
from langgraph.checkpoint.base import CheckpointTuple
|
from langgraph.checkpoint.base import CheckpointTuple
|
||||||
@@ -359,7 +558,7 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
|
|||||||
await self.repository.put(thread_id, {
|
await self.repository.put(thread_id, {
|
||||||
"thread_id": thread_id,
|
"thread_id": thread_id,
|
||||||
"config": _strict_json_value(next_config, path="$.config"),
|
"config": _strict_json_value(next_config, path="$.config"),
|
||||||
"checkpoint": _strict_json_value(_strip_runtime_refs(_normalize_checkpoint(checkpoint)), path="$.checkpoint"),
|
"checkpoint": _encode_checkpoint_value(self.serde, _strip_runtime_refs(_normalize_checkpoint(checkpoint)), path="$.checkpoint"),
|
||||||
"metadata": _strict_json_value(_strip_runtime_refs(_normalize_metadata(metadata or {})), path="$.metadata"),
|
"metadata": _strict_json_value(_strip_runtime_refs(_normalize_metadata(metadata or {})), path="$.metadata"),
|
||||||
"new_versions": _strict_json_value(_strip_runtime_refs(new_versions or {}), path="$.new_versions"),
|
"new_versions": _strict_json_value(_strip_runtime_refs(new_versions or {}), path="$.new_versions"),
|
||||||
"checkpoint_id": checkpoint_id,
|
"checkpoint_id": checkpoint_id,
|
||||||
@@ -410,7 +609,12 @@ class RepositoryCheckpointSaver(BaseCheckpointSaver):
|
|||||||
"task_id": task_id,
|
"task_id": task_id,
|
||||||
"task_path": task_path,
|
"task_path": task_path,
|
||||||
"channel": channel,
|
"channel": channel,
|
||||||
"value": _strict_json_value(durable_value, path=f"$.pending_writes[{task_id}].{channel}"),
|
"value": _encode_pending_write_value(
|
||||||
|
self.serde,
|
||||||
|
durable_value,
|
||||||
|
path=f"$.pending_writes[{task_id}].{channel}",
|
||||||
|
in_interrupt_channel=(str(channel) == "__interrupt__"),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
latest["pending_writes"] = pending
|
latest["pending_writes"] = pending
|
||||||
await self.repository.put(thread_id, latest)
|
await self.repository.put(thread_id, latest)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -103,12 +103,32 @@ class Settings(BaseSettings):
|
|||||||
ENABLE_REDIS_CACHE: bool = False
|
ENABLE_REDIS_CACHE: bool = False
|
||||||
CACHE_KEY_PREFIX: str = 'agentfw'
|
CACHE_KEY_PREFIX: str = 'agentfw'
|
||||||
|
|
||||||
|
# RAG backend selector. 'standard' preserves the existing framework RAG;
|
||||||
|
# 'kbdb' consumes the enterprise KBDB stable serving facade without changing agents.
|
||||||
|
RAG_PROVIDER: Literal['standard','kbdb'] = 'standard'
|
||||||
VECTOR_STORE_PROVIDER: Literal['memory','sqlite','autonomous','oracle','mongodb'] = 'memory'
|
VECTOR_STORE_PROVIDER: Literal['memory','sqlite','autonomous','oracle','mongodb'] = 'memory'
|
||||||
GRAPH_STORE_PROVIDER: Literal['memory','autonomous','oracle'] = 'memory'
|
GRAPH_STORE_PROVIDER: Literal['memory','autonomous','oracle'] = 'memory'
|
||||||
ORACLE_GRAPH_NAME: str = 'AGENTFW_GRAPH'
|
ORACLE_GRAPH_NAME: str = 'AGENTFW_GRAPH'
|
||||||
ORACLE_GRAPH_AUTO_CREATE: bool = False
|
ORACLE_GRAPH_AUTO_CREATE: bool = False
|
||||||
RAG_TOP_K: int = 5
|
RAG_TOP_K: int = 5
|
||||||
|
KBDB_DB_USER: str | None = None
|
||||||
|
KBDB_DB_PASSWORD: str | None = None
|
||||||
|
KBDB_DB_DSN: str | None = None
|
||||||
|
KBDB_DB_WALLET_LOCATION: str | None = None
|
||||||
|
KBDB_DB_WALLET_PASSWORD: str | None = None
|
||||||
|
KBDB_SEARCH_TYPE: Literal['hybrid','vector','keyword'] = 'hybrid'
|
||||||
|
KBDB_NODE_EXPANSION: bool = True
|
||||||
|
KBDB_NODE_MAX_RELATED: int = 8
|
||||||
|
KBDB_GRAPH_CROSS_REF: bool = False
|
||||||
|
KBDB_MAX_CROSS_REF_HOPS: int = 1
|
||||||
|
KBDB_DOCUMENT_TYPE: str | None = 'customer_safe'
|
||||||
|
KBDB_METADATA_JSON: str | None = None
|
||||||
|
KBDB_MIN_SCORE: float | None = None
|
||||||
|
# MCP só pula retrieval quando declarar explicitamente rag_sufficient/knowledge_sufficient.
|
||||||
SKIP_RAG_WHEN_MCP_SUFFICIENT: bool = True
|
SKIP_RAG_WHEN_MCP_SUFFICIENT: bool = True
|
||||||
|
# Preserva comportamento legado do RAG standard; KBDB enterprise é grounded-only por padrão.
|
||||||
|
RAG_GROUNDED_ONLY: bool = False
|
||||||
|
KBDB_GROUNDED_ONLY: bool = True
|
||||||
ENABLE_RAG_QUERY_REWRITE: bool = False
|
ENABLE_RAG_QUERY_REWRITE: bool = False
|
||||||
ENABLE_RAG_CONTEXT_COMPRESSION: bool = False
|
ENABLE_RAG_CONTEXT_COMPRESSION: bool = False
|
||||||
ENABLE_RAG_GENERATION: bool = False
|
ENABLE_RAG_GENERATION: bool = False
|
||||||
|
|||||||
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