O ecossistema de modelos de linguagem de grande escala (LLMs) enfrenta um desafio crítico: a fragmentação de APIs. Cada provedor impõe sua própria estrutura de requisição, formatos de resposta e mecanismos de autenticação. O framework Eino aborda essa complexidade ao introduzir uma camada de abstração unificada, permitindo que desenvolvedores alternem entre diferentes modelos sem a necessidade de refatorar a lógica de negócio principal.
Arquitetura da Interface de Modelo
A filosofia de design do Eino baseia-se na criação de contratos genéricos que encapsulam o comportamento de modelos de chat, processamento de ferramentas e fluxos de dados via streaming. Isso é alcançado através de interfaces Go bem definidas que padronizam a comunicação.
Interfaces de Núcleo
ChatModel: A Base de Comunicação
A interface ChatModel é o ponto de entrada principal para interações textuais. Ela abstrai o processo de envio de prompts e recebimento de respostas.
// ChatModel define o contrato essencial para interação com LLMs
type ChatModel interface {
// Execute processa uma lista de mensagens e retorna uma resposta completa
Execute(ctx context.Context, input []*schema.Message, config ...Option) (*schema.Message, error)
// StreamProcess gerencia a geração de conteúdo em partes (tokens)
StreamProcess(ctx context.Context, input []*schema.Message, config ...Option) (
*schema.StreamReader[*schema.Message], error)
}
Suporte a Chamadas de Feramentas (Tool Calling)
Para modelos que suportam a execução de funções externas, o Eino oferece uma extensão da interface base, garantindo segurança de tipos e isolamento de estado.
// AdvancedToolModel estende a funcionalidade para lidar com plugins e ferramentas
type AdvancedToolModel interface {
ChatModel
// BindTools associa definições de ferramentas a uma instância específica do modelo
BindTools(definitions []*schema.ToolDefinition) (AdvancedToolModel, error)
}
Estrutura de Mensagem Unificada
Para evitar a tradução constante de formatos JSON específicos de fornecedores, o Eino utiliza a estrutura schema.Message. Esta entidade é capaz de representar desde textos simples até conteúdos multimodais complexos.
type Message struct {
Role RoleType `json:"role"`
Content string `json:"content"`
// Suporte para múltiplos tipos de mídia (imagens, áudio)
MediaParts []MediaPart `json:"media_parts,omitempty"`
// Metadados para execução de funções
Invocations []ToolInvocation `json:"invocations,omitempty"`
ReferenceID string `json:"reference_id,omitempty"`
// Informações adicionais do provedor
Metadata map[string]any `json:"metadata,omitempty"`
}
Papéis de Mensagem (Roles)
| Papel | Propósito |
|---|---|
System |
Define o comportamento e as restrições do assistente. |
User |
Entrada direta do usuário final ou do sistema. |
Assistant |
Resposta gerada pela inteligência artificial. |
Tool |
Retorno de dados após a execução de uma função externa. |
Exemplos Práticos de Implementação
Geração de Resposta Simples
// Inicialização do cliente (exemplo abstrato)
aiClient, _ := InitializeClient(ctx, settings)
// Construção do histórico de mensagens
history := []*schema.Message{
schema.SystemMessage("Você é um especialista em arquitetura de software."),
schema.UserMessage("Quais as vantagens de usar interfaces em Go?"),
}
// Chamada síncrona
aiResult, err := aiClient.Execute(ctx, history)
if err != nil {
panic(err)
}
fmt.Println("IA:", aiResult.Content)
Processamento de Stream em Tempo Real
// Início do fluxo de streaming
tokenStream, err := aiClient.StreamProcess(ctx, history)
if err != nil {
handleError(err)
}
defer tokenStream.Close()
for {
chunk, err := tokenStream.Receive()
if err == io.EOF {
break
}
// Processamento de cada fragmento da mensagem
fmt.Print(chunk.Content)
}
Configuração e Parametrizagem
O framework utiliza o padrão Functional Options para configurar o comportamento do modelo de forma flexível e extensível.
| Opção | Tipo | Função |
|---|---|---|
WithTemperature |
float32 | Ajusta a criatividade/aleatoriedade da saída. |
WithMaxTokens |
int | Limita o comprimento máximo da resposta. |
WithStopSequences |
[]string | Define gatilhos para encerrar a geração. |
WithTopP |
float32 | Aplica amostragem de núcleo para diversidade léxica. |
Tratamento de Dados Multimodais
O Eino permite o processamento de entradas que não se limitam a texto, seguindo o padrão de MediaPart para inclusão de arquivos e URIs.
imageMsg := &schema.Message{
Role: schema.User,
MediaParts: []schema.MediaPart{
{
Kind: schema.PartKindText,
Text: "Analise os erros representados nesta captura de tela:",
},
{
Kind: schema.PartKindImage,
Image: &schema.ImageSource{
SourceURL: "https://storage.exemplo.com/log-error.png",
Quality: "high",
},
},
},
}
Gerenciamento de Erros e Robustez
A padronização também se estende ao tratamento de falhas. O framework categoriza erros comuns para facilitar a implementação de políticas de retry ou fallback.
output, err := aiClient.Execute(ctx, history)
if err != nil {
if errors.Is(err, schema.ErrRateLimitExceeded) {
// Lógica de espera exponencial
} else if errors.Is(err, schema.ErrContextLengthExceeded) {
// Lógica de truncamento de histórico
}
}
Otimização de Performance e Recursos
Para aplicações em escala de produção, a gestão eficiente de conexões e latência é fundamental. Recomenda-se:
- Pool de Conexões: Reutilizar instâncias de clientes para evitar o overhead de handshake TLS frequente.
- Timeouts Granulares: Definir prazos curtos para a recepção do primeiro token e prazos mais longos para o processamento total.
- Monitoramento de Tokens: Acompanhar o consumo de tokens via metadados da resposta para controle de custos e performance.