Search by

asmpkg / guiche

alexsmonte

Core PHP para assistentes conversacionais orientados a Tools, Plugins, estado, segurança, persistência e runtime de conversação.

Package info

github.com/alexsmonte/guiche

pkg:composer/asmpkg/guiche

Statistics

Installs: 31

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0


README

Guichê é um Core PHP para construir assistentes conversacionais orientados a Tools, Plugins, estado, segurança, persistência e runtime de conversação sem acoplar o Core a um domínio específico.

O projeto evoluiu de um piloto clínico para uma biblioteca reutilizável. Domínios como clínica, atendimento, vendas ou suporte são integrações construídas sobre os mesmos contratos públicos do Core.

Versão documentada: v1.6.1 — Stable

O que o Guichê oferece

A linha v1 reúne:

  • Tools e Plugins extensíveis;
  • estado estruturado e persistência por ContextStoreInterface;
  • contexto confiável multi-tenant;
  • bindIdentity() para identidade externa autenticada;
  • Trusted Parameter Binding;
  • ToolPolicy e hard Execution Gate;
  • proteção de WRITE, idempotência e Result Projection;
  • prompts globais, dinâmicos e por tenant;
  • loaders de Tools/Plugins e DI explícita;
  • Operational State;
  • lifecycle de sessão com TTL;
  • Conversation Runtime, scheduler e follow-up experimental;
  • provider OpenAI Responses;
  • Prompt Caching otimizado na v1.6.1.

Requisitos

  • PHP ^8.4;
  • Composer;
  • uma chave de API para o provider usado em conversas reais.

O exemplo oficial usa OpenAI, mas os contratos do Core não dependem de um domínio específico.

O quality gate e os testes automatizados não dependem de OpenAI real, banco real, Clinsoft, Internet ou side effects reais.

Instalação

Instale a linha estável atual:

composer require asmpkg/guiche:^1.6

Para trabalhar diretamente com o clone:

git clone git@github.com:alexsmonte/guiche.git guiche
cd guiche
composer install

Quick Start — primeira conversa

1. Crie um projeto vazio

mkdir meu-guiche
cd meu-guiche
composer require asmpkg/guiche:^1.6

2. Inicialize o projeto

vendor/bin/guiche init

O comando cria, sem sobrescrever arquivos existentes:

.env
guiche.prompt.md                 # compatibilidade
guiche.php                       # Composition Root
prompts/
└── comportamento.md             # prompt global
src/
├── Tool/
└── Plugin/
storage/
└── conversations/

Quando existe composer.json, o init também garante o mapeamento PSR-4:

{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}

Se o prefixo tiver sido adicionado, regenere o autoload:

composer dump-autoload

Um App\\ já existente com outro caminho é preservado e reportado pelo doctor, em vez de ser sobrescrito silenciosamente.

3. Configure o provider

Edite .env:

AI_API_KEY=sua-chave
AI_MODEL=gpt-5.6-luna

AI_MODEL possui valor padrão. Por compatibilidade, OPENAI_API_KEY e OPENAI_MODEL continuam aceitos como aliases legados.

4. Personalize o prompt

O padrão atual usa:

prompts/comportamento.md

Para um tenant confiável acme, você pode adicionar:

prompts/acme.md

A composição será:

prompts/comportamento.md
        +
prompts/acme.md

A prioridade de resolução é:

$app['prompt_resolver']
        ↓
GUICHE_PROMPT_FILE
        ↓
GUICHE_PROMPT
        ↓
prompts/comportamento.md + prompts/{tenant_id}.md
        ↓
guiche.prompt.md legado

O tenant_id precisa vir do contexto confiável da sessão. Mensagem do usuário, state livre e argumentos de Tool não podem selecionar o arquivo de tenant.

5. Verifique o ambiente

vendor/bin/guiche doctor

O doctor valida PHP, extensões, .env, configuração de IA, prompt global, App\\ => src/, diretórios de loaders e storage/conversations, sem fazer chamada de rede ao provider.

6. Converse

Modo interativo:

vendor/bin/guiche chat

Mensagem única:

vendor/bin/guiche chat "Olá, tudo bem?"

Sessão explícita:

vendor/bin/guiche chat --session=cliente-123 "Olá"

No modo interativo:

  • /context mostra o contexto salvo;
  • /usage mostra o uso registrado na sessão;
  • /tools mostra as Tools disponíveis;
  • /reset reinicia a sessão;
  • /sair encerra a CLI.

Esses comandos são tratados localmente e não são enviados à IA.

Também é possível consultar usage fora do chat:

vendor/bin/guiche usage --session=cliente-123

Como o Guichê funciona

Fluxo conceitual simplificado:

Host / aplicação
      ↓
Guiche
      ↓
Prompt + contexto estruturado + ToolPolicy
      ↓
AiProvider
      ↓
Tool call
      ↓
ExecutionGate
      ↓
Tool
      ↓
State / Result Projection / continuação

A chamada de uma Tool pela IA não equivale a autorização de execução. O ExecutionGate continua sendo a autoridade final antes do side effect real.

Conceitos principais

Tool

Uma Tool implementa ToolInterface e declara:

  • nome;
  • descrição;
  • schema de argumentos;
  • execução.

Tools podem ser classificadas como READ ou WRITE. Operações de escrita possuem requisitos adicionais de segurança e idempotência.

Quando a execução depende de contexto confiável da sessão, a Tool pode implementar ContextAwareToolInterface.

Plugin

Um Plugin implementa PluginInterface e registra capacidades pelo GuicheBuilder, como:

  • Tools;
  • Presenters;
  • State Manager;
  • Policies;
  • instruction fragments;
  • completion guards;
  • result projections.

State e Operational State

O contexto estruturado da conversa pode ser persistido por uma implementação de ContextStoreInterface.

O Core não precisa conhecer detalhes do domínio para manter:

  • intenção;
  • estado operacional;
  • histórico de Tools;
  • lifecycle da sessão;
  • usage;
  • contexto confiável de execução.

O domínio continua responsável por interpretar seu próprio estado por meio de Plugins e State Managers.

Contexto confiável e multi-tenant

O fluxo recomendado é:

SESSION
  ↓
execution_context.identity
  ↓
TenantResolver
  ↓
execution_context.tenant_id
  ↓
ToolExecutionContext
  ↓
ContextAwareToolInterface::executeWithContext()

tenant_id não deve ser aceito da IA como fonte de autoridade.

Para aplicações externas, vincule a identidade autenticada antes de processar a mensagem:

$guiche->bindIdentity(
    $sessionId,
    [
        'provider' => 'custom-crm',
        'account_id' => $accountId,
        'conversation_id' => $conversationId,
    ]
);

$response = $guiche->handle(
    $sessionId,
    $message
);

bindIdentity() é write-once por sessão, idempotente para o mesmo binding e falha fechado em tentativa de troca.

A autenticação do request/webhook pertence ao host. Somente depois de estabelecer confiança o host deve chamar bindIdentity().

Veja:

  • docs/multitenancy.md;
  • docs/trusted-external-identity.md;
  • examples/chatwoot/;
  • examples/external-host/.

Prompts e Composition Root

O vendor/bin/guiche init cria guiche.php, o Composition Root do consumidor:

$factory = require __DIR__ . '/guiche.php';
$guiche = $factory();

Fontes públicas de prompt incluem:

  • StaticPromptResolver;
  • FilePromptResolver;
  • CallbackPromptResolver;
  • FallbackPromptResolver;
  • TenantPromptResolver;
  • CompositePromptResolver.

Fluxo confiável:

SESSION
  ↓
bindIdentity()
  ↓
TenantResolver
  ↓
tenant_id
  ↓
PromptContext
  ↓
PromptResolver
  ↓
prompt

Mensagem do usuário, state livre e argumentos de Tool não são fonte de autoridade para selecionar tenant ou prompt.

Veja:

  • docs/dynamic-prompts.md;
  • examples/prompt-sources/;
  • examples/framework-integration/.

Tools, Plugins, loaders e DI

A API explícita permanece disponível:

$builder->registerTool(new MinhaTool());
$builder->registerPlugin(new MeuPlugin());

Também é possível usar loaders por convenção:

$builder
    ->loadTools('InformacoesEmpresa', 'ConsultarPedido')
    ->loadPlugins('Clinica');

Convenções:

InformacoesEmpresa
→ App\Tool\InformacoesEmpresaTool

Clinica
→ App\Plugin\ClinicaPlugin

FQCN também é aceito.

Não existe scanning de diretório, glob, require dinâmico ou autowiring recursivo.

Dependências precisam ser registradas explicitamente:

$builder
    ->setService(MinhaConexao::class, $conexao)
    ->loadTool('InformacoesEmpresa');

No Composition Root:

$guiche = $factory([
    'services' => [
        MinhaConexao::class => $conexao,
    ],
    'tools' => [
        'InformacoesEmpresa',
        $toolLegada,
    ],
    'plugins' => [
        'Clinica',
        $pluginLegado,
    ],
]);

GuicheBuilder::loadCapabilities() também permite carregar services, tools e plugins de forma declarativa.

Os nomes enviados a loadTool(), loadPlugin() e loaders equivalentes são configuração de bootstrap confiável. Nunca use mensagem, saída da IA ou argumentos livres do usuário para escolher classe a carregar.

Safety Layer

A execução de Tools passa por regras estruturadas de segurança.

Entre os mecanismos disponíveis:

  • classificação READ/WRITE;
  • validação de argumentos;
  • ToolPolicy;
  • estágios permitidos;
  • preconditions de estado;
  • Trusted Parameter Binding;
  • confirmação explícita;
  • hard Execution Gate;
  • proteção de idempotência para WRITE;
  • bloqueio de duplicate writes;
  • Result Projection;
  • revalidação de chamadas stale.

Fluxo resumido:

ToolRegistry
    ↓
ToolPolicy
    ↓
subconjunto permitido
    ↓
AiProvider
    ↓
Tool call
    ↓
ExecutionGate
    ↓
execução real

Mesmo que uma Tool seja forjada, fique stale entre chamadas ou seja produzida fora do estágio permitido, o ExecutionGate revalida a operação imediatamente antes da execução.

OpenAI Responses e Prompt Caching — v1.6.1

O provider OpenAiResponsesProvider usa a Responses API e, na v1.6.1, foi otimizado para Prompt Caching.

A estratégia separa:

PREFIXO ESTÁVEL
- instructions
- schemas completos das Tools
- breakpoint explícito
- TTL de 12h

CONTEXTO DINÂMICO
- runtime
- intenção
- state
- lifecycle da sessão
- resultados incrementais
- TTL de 30m

Dados mutáveis não ficam no prefixo estável. Isso evita invalidar o cache por mudanças de horário, sessão, intenção ou estado.

Schemas estáveis e allowed_tools

Para manter os schemas estáveis entre turnos, o Guichê não precisa remover fisicamente definições de Tools a cada mudança de estágio.

A ToolPolicy calcula o subconjunto permitido e o provider OpenAI o traduz para tool_choice.allowed_tools.

schemas completos e estáveis
        +
ToolPolicy
        ↓
allowed_tools
        ↓
modelo

Essa otimização não substitui a Safety Layer.

O ExecutionGate continua revalidando a Tool antes da execução real, inclusive contra estado atualizado.

Cache incremental após Tools

Nas continuations, o Guichê reaproveita o prefixo já cacheado e escreve somente os incrementos necessários da conversa.

Isso reduz reprocessamento de tokens em fluxos multi-Tool sem alterar a API pública da linha v1.

Session Lifecycle

O GuicheBuilder suporta configuração de TTL por meio de withSessionTtl().

O lifecycle expõe estados como:

new
active
expired

Mudanças de lifecycle fazem parte do contexto dinâmico da conversa e não precisam invalidar o prefixo estável do provider OpenAI.

Conversation Runtime

O runtime cobre responsabilidades como:

  • incoming events;
  • buffer/debounce;
  • turn;
  • sequence;
  • scheduler;
  • claim/lease;
  • stale-event protection;
  • follow-up;
  • recovery.

A divisão conceitual é:

ConversationRuntime = quando processar
Guiche              = como processar/raciocinar o turno

FollowUpRuntime e partes do Conversation Runtime permanecem API experimental até promoção explícita.

Exemplo básico

O exemplo oficial de Developer Experience fica em:

examples/basic/

Ele contém:

  • BasicPlugin;
  • Tool local CatalogSearch;
  • catálogo determinístico em memória;
  • operação somente READ;
  • nenhum MySQL;
  • nenhuma API privada;
  • nenhum side effect.

O exemplo deve permanecer pequeno, legível, neutro de domínio e executável como primeira experiência com o framework.

Veja Quick Start detalhado.

Exemplo clínico avançado

A biblioteca mantém o Core genérico em src/ e disponibiliza um exemplo completo de agendamento em examples/clinica-piloto/.

Para executar o exemplo pela primeira vez, use o Fake Mode, que não depende de banco, IA ou APIs externas:

cp examples/clinica-piloto/.env.example examples/clinica-piloto/.env
php examples/clinica-piloto/fake-smoke.php

Veja o guia de instalação e isolamento em examples/clinica-piloto/INSTALL.md.

O namespace do exemplo não pertence ao autoload público do pacote.

Desenvolvimento

Instale as dependências:

composer install

Execute o quality gate:

composer check

O quality gate inclui:

  • PHPUnit;
  • PHPStan;
  • cobertura mínima definida pelo projeto;
  • acceptance legado;
  • freeze da API pública.

Na v1.6.1, o gate validado permanece com 391 testes, PHPStan sem erros e coverage acima do threshold de 70%.

Arquitetura e documentação

Referências principais:

  • Quick Start: docs/quick-start.md;
  • arquitetura: docs/architecture.md;
  • Tools: docs/tools.md;
  • Plugins: docs/plugins.md;
  • State: docs/state.md;
  • contexto multi-tenant: docs/multitenancy.md;
  • identidade externa confiável: docs/trusted-external-identity.md;
  • prompts dinâmicos: docs/dynamic-prompts.md;
  • segurança: docs/security.md;
  • estabilidade de API: docs/api-stability.md;
  • suporte PHP: docs/php-support.md;
  • changelog: CHANGELOG.md;
  • release v1.6.0: docs/release-v1.6.0.md;
  • release v1.6.1: docs/release-v1.6.1.md.

Documentos de migração permanecem disponíveis em docs/migration-*.

Status e compatibilidade

A linha atual é v1.6.x Stable.

A evolução principal da linha v1 inclui:

v1.0  API pública estável
v1.1  contexto multi-tenant
v1.2  CLI e bootstrap
v1.3  identidade externa confiável
v1.4  Composition Root e prompts dinâmicos
v1.5  loaders, DI explícita e prompts por tenant
v1.6  Operational State & Runtime DX
v1.6.1 Prompt Caching otimizado no provider OpenAI

O compromisso da linha v1 inclui:

  • API public protegida por SemVer/BC;
  • suporte oficial a PHP 8.4 e PHP 8.5;
  • hard Execution Gate antes de side effects;
  • idempotência explícita para WRITE;
  • extensibilidade por Tools, Plugins, ContextStore e AiProvider;
  • instalação e uso em consumidor Composer externo;
  • Conversation Runtime experimental fora do compromisso BC estável até promoção explícita.

Pontos de extensão públicos centrais:

  • ToolInterface;
  • PluginInterface;
  • ContextStoreInterface;
  • AiProviderInterface;
  • ToolRegistration;
  • GuicheBuilder.

O principal critério de aceite permanece:

Em um projeto vazio, um desenvolvedor deve conseguir seguir somente este README e obter uma conversa funcional.

Licença

Consulte o arquivo de licença do repositório.