Architecture and Agent Orchestration

To move beyond simple AI demos, this approach separates the frontend (Flutter) from a robust backend (Go) using the Agent Development Kit (ADK). The system relies on a single binary deployed to Cloud Run, which manages multiple agents that communicate via HTTP.

Key architectural components include:

  • Session Management: The backend tracks conversation history using session IDs, allowing agents to maintain context across multiple turns—essential for iterative tasks like styling recommendations.
  • Artifact Storage: To keep API responses lightweight, the system does not send raw image data in JSON. Instead, agents save generated images to Google Cloud Storage and return a reference name, which the frontend uses to fetch the asset separately.
  • Agent Loop: Each agent follows a cycle of receiving a message, deciding on a tool call or delegation, and returning a response. This loop allows for complex workflows, such as a 'Styling Agent' delegating to a 'Catalog Agent' to retrieve product data.

Implementing Agents with ADK

ADK provides the structure necessary to turn LLMs into functional agents.

  • Tool Calling: Agents are equipped with specific Go functions (e.g., list_products, get_product_image) that allow them to interact with databases or external APIs.
  • Instruction Management: Prompts are stored in instruction.md files and embedded into the Go binary, keeping the codebase clean and separating logic from instructions.
  • Development UI: The ADK includes a built-in web UI that allows developers to chat with agents, inspect events, and view artifacts in real-time, significantly simplifying the debugging process.
  • Callbacks: Hooks like BeforeModelCallback allow developers to intercept agent execution—for example, to automatically save user-uploaded images to artifact storage for easier tracking.

Frontend Integration with Flutter

The Flutter application follows the Model-View-ViewModel (MVVM) pattern to ensure a clean separation of concerns.

  • State Management: The app uses Providers to encapsulate data (e.g., loading states, user images, outfit lists). When a provider updates, the UI automatically rebuilds, ensuring the interface stays in sync with the backend state.
  • Declarative UI: By using AnimatedSwitcher and pattern matching, the app transitions smoothly between UI states (e.g., uploading, loading, displaying results).
  • Cross-Platform Consistency: Because the backend is a standard REST API, the same Flutter codebase provides a consistent experience across web, iOS, and Android without requiring platform-specific logic for the AI features.