🏗️ Guia de Arquitetura para Desenvolvedores¶
Documentação completa da arquitetura do Create Agents AI, baseada em Clean Architecture e princípios SOLID.
📐 Estrutura de Camadas¶
┌─────────────────────────────────────┐
│ PRESENTATION │ CLI, UI (ChatCLIApplication)
│ (Interface do Usuário) │ Command Handlers, Terminal UI
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ APPLICATION │ Facade (CreateAgent)
│ (Lógica da Aplicação) │ Use Cases, DTOs, Services
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ DOMAIN │ Entities, Rules, Interfaces
│ (Regras de Negócio) │ Agent, ToolExecutor, LoggerInterface
└──────────────▲──────────────────────┘
│
┌──────────────┴──────────────────────┐
│ INFRASTRUCTURE │ Adapters, Handlers, Config
│ (Detalhes Técnicos) │ OpenAI, Ollama, Tools, Metrics
└─────────────────────────────────────┘
🎯 Camadas¶
1. Presentation (Apresentação)¶
Localização: src/createagents/presentation/
Responsabilidade: Interface de usuário e interação externa.
Componentes:
- CLI Application:
ChatCLIApplication— aplicação CLI interativa para chat com agentes - Command Handlers: Sistema baseado no Command Pattern
ChatCommandHandler— processamento de mensagens de chatHelpCommandHandler— exibe ajuda e comandos disponíveisMetricsCommandHandler— mostra métricas de performanceConfigsCommandHandler— exibe configurações do agenteToolsCommandHandler— lista ferramentas disponíveisClearCommandHandler— limpa histórico de conversação- UI Components:
TerminalRenderer,TerminalFormatter,ColorScheme— renderização colorida no terminal - I/O:
InputReader— leitura de entrada do usuário - Registry:
CommandRegistry— registro e resolução de comandos
2. Application (Aplicação)¶
Localização: src/createagents/application/
Responsabilidade: Orquestrar casos de uso do sistema.
Componentes:
- Facade / Controller:
CreateAgent— fachada simples que cria agentes e expõe métodos comochat(async),get_configs,get_all_available_tools,clear_history,export_metrics_*. - Services:
AgentService— serviço que encapsulaAgentcom logging integrado - Use Cases (application/use_cases):
CreateAgentUseCase— criação e validação de agentes (invocado porAgentComposer).ChatWithAgentUseCase— orquestra mensagens assíncronas entreAgenteChatRepository(adapters).GetAgentConfigUseCase— retorna as configurações do agente.GetAllAvailableToolsUseCase/GetSystemAvailableToolsUseCase— listagem de tools disponíveis.- DTOs (application/dtos): Objetos de transferência:
CreateAgentInputDTO,ChatInputDTO,AgentConfigOutputDTO— comunicação entre controller/use-casesStreamingResponseDTO— wrapper para AsyncGenerator que permite iteração e await de respostas em streaming- Interfaces (application/interfaces):
ChatRepository— contrato que os adapters implementam com suporte assíncrono
3. Domain (Domínio)¶
Localização: src/createagents/domain/
Responsabilidade: Regras de negócio puras, independentes de tecnologia.
Componentes:
- Entities:
Agent(entidade principal) - Value Objects:
Message,MessageRole,History,SupportedConfigs,SupportedProviders,BaseTool(ferramentas),ChatResponse - Domain Services:
ToolExecutor(execução assíncrona de ferramentas),ToolExecutionResult - Interfaces (domain/interfaces):
LoggerInterface— abstração de logging no domínio (DIP - Dependency Inversion Principle) - Exceptions:
domain.exceptions(ex.:AgentException,InvalidAgentConfigException,UnsupportedConfigException)
4. Infrastructure (Infraestrutura)¶
Localização: src/createagents/infra/
Responsabilidade: Implementar detalhes técnicos e integrações externas.
Componentes:
- Adapters (Chat):
OpenAIChatAdapter— integração com OpenAIOllamaChatAdapter— integração com Ollama- Handlers (Async Streaming):
OpenAIHandler/OpenAIStreamHandler— processamento de chamadas não-streaming e streaming OpenAIOllamaHandler/OllamaStreamHandler— processamento de chamadas não-streaming e streaming Ollama- Clients:
OpenAIClient— cliente HTTP para OpenAIOllamaClient— cliente HTTP para Ollama- Common Adapters:
MetricsRecorder— gravação centralizada de métricas (OpenAI e Ollama)- Tools:
CurrentDateTool— ferramenta de data/horaReadLocalFileTool— leitura de arquivos (PDF, Excel, CSV, Parquet, JSON, YAML, TXT)- Factory:
ChatAdapterFactory— criação de adapters baseada em provider - Config:
EnvironmentConfig,LoggingConfig,StandardLogger(implementação deLoggerInterface),ChatMetrics
🎨 Princípios SOLID¶
Single Responsibility (SRP)¶
Cada classe tem uma única responsabilidade:
Agent # Representa um agente
History # Gerencia histórico
ChatWithAgentUseCase # Orquestra conversa
Open/Closed (OCP)¶
Aberto para extensão, fechado para modificação:
# Adicionar novo provider sem modificar código existente
class ClaudeAdapter(ChatRepository):
def chat(self, ...): pass
Liskov Substitution (LSP)¶
Adapters são intercambiáveis:
# Qualquer adapter pode substituir outro
adapter: ChatRepository = OpenAIChatAdapter()
# ou
adapter: ChatRepository = OllamaChatAdapter()
Interface Segregation (ISP)¶
Interfaces específicas e focadas:
class ChatRepository(ABC):
@abstractmethod
def chat(self, ...) -> str:
pass
Dependency Inversion (DIP)¶
Depende de abstrações, não de implementações:
class ChatWithAgentUseCase:
def __init__(self, chat_repository: ChatRepository): # Interface
self.__chat_repository = chat_repository
# Exemplo com LoggerInterface (DIP no domínio)
class ToolExecutor:
def __init__(self, logger: LoggerInterface): # Abstração
self._logger = logger # Não depende de StandardLogger diretamente
🔧 Padrões de Design¶
Repository Pattern¶
class ChatRepository(ABC):
@abstractmethod
def chat(self, ...) -> str:
pass
class OpenAIChatAdapter(ChatRepository):
def chat(self, ...): # Implementação
Factory Pattern¶
class ChatAdapterFactory:
@classmethod
def create(
cls,
provider: str,
model: str,
) -> ChatRepository:
provider_lower = provider.lower()
adapter: ChatRepository
if provider_lower == "openai":
adapter = OpenAIChatAdapter()
elif provider_lower == "ollama":
adapter = OllamaChatAdapter()
else:
raise ValueError(f"Invalid provider: {provider}.")
return adapter
Facade Pattern¶
# CreateAgent é uma fachada simplificada
class CreateAgent:
def __init__(self, provider, model, ...):
# Esconde complexidade da criação
self.__agent = AgentComposer.create_agent(...)
self.__chat_use_case = AgentComposer.create_chat_use_case(...)
Value Object Pattern¶
@dataclass(frozen=True) # Imutável
class Message:
role: MessageRole
content: str
🔄 Fluxo de Dados¶
Fluxo Síncrono (await response)¶
User → CreateAgent.chat()
→ ChatWithAgentUseCase.execute() [async]
→ ChatRepository.chat() [async]
→ OpenAIHandler / OllamaHandler
→ StreamHandler (processa streaming)
→ API Externa (OpenAI / Ollama)
← Tokens em streaming
← Response completo
← AsyncGenerator
← StreamingResponseDTO
← await response (string completa)
Fluxo Assíncrono (async for)¶
User → CreateAgent.chat()
→ ChatWithAgentUseCase.execute() [async]
→ ChatRepository.chat() [async]
→ StreamHandler.handle_streaming()
→ async for token in api_stream:
→ yield token # Streaming em tempo real
← AsyncGenerator[str]
← StreamingResponseDTO
→ async for token in response:
→ print(token, end='') # Exibe token por token
Fluxo CLI¶
Terminal → ChatCLIApplication.run()
→ CommandRegistry.find_handler(user_input)
→ CommandHandler.execute()
→ CreateAgent.chat() [se ChatCommandHandler]
→ async for token in response:
→ TerminalRenderer.render_token()
💡 Benefícios da Arquitetura¶
🧪 Testabilidade¶
# Mock fácil de dependências
mock_repo = Mock(spec=ChatRepository)
use_case = ChatWithAgentUseCase(mock_repo)
🔄 Flexibilidade¶
# Trocar provider sem mudar código
agent = CreateAgent(provider="ollama", model="llama2")
📈 Escalabilidade¶
- Adicionar novos providers facilmente
- Extensível via interfaces
- Preparado para crescimento
🛡️ Manutenibilidade¶
- Código organizado em camadas
- Responsabilidades claras
- Fácil localizar e corrigir bugs
🔄 Padrões Assíncronos¶
Streaming com AsyncGenerator¶
# Handler retorna AsyncGenerator
async def handle_streaming(self, ...) -> AsyncGenerator[str, None]:
async for chunk in api_response:
yield chunk # Stream em tempo real
# DTO encapsula AsyncGenerator
class StreamingResponseDTO:
def __init__(self, generator: AsyncGenerator[str, None]):
self._generator = generator
async def __anext__(self): # Permite async for
return await self._generator.__anext__()
def __await__(self): # Permite await
async def _consume():
return ''.join([token async for token in self])
return _consume().__await__()
Versão: 0.1.3 | Atualização: 01/12/2025