Soluzione Bussola

Crea un'app di pianificazione dei viaggi basata su agenti con l'IA generativa

Il seguente diagramma mostra una panoramica generale di come puoi combinare i migliori servizi per sviluppatori Google per creare applicazioni basate sull'IA.

Crea splendide app basate sull'IA per dispositivi mobili e web

Puoi usare Flutter e Firebase Genkit per creare app multipiattaforma che si integrano perfettamente con l'IA.
Puoi utilizzare Genkit per consentire alla tua app di utilizzare in sicurezza i dati degli LLM definendo uno schema in grado di convalidare l'output dell'LLM. In Flutter, puoi utilizzare Dart per serializzare la richiesta e deserializzare la risposta in modo che corrisponda allo schema Genkit utilizzando richieste HTTP standard.
Con IDX, puoi creare un'app Flutter senza dover installare alcun software. In questo modo è possibile sviluppare e testare la tua app per Android e la tua app web nel browser.
Firebase Data Connect è un servizio di database relazionale per app web e mobile che consente di creare e scalare utilizzando un database PostgreSQL completamente gestito basato su Cloud SQL. Fornisce una gestione sicura di schema, query e mutazioni tramite GraphQL che si integra bene con Firebase Authentication. Data Connect include il supporto dell'SDK per Kotlin per Android e il web.

Crea un agente con Firebase Genkit

L'agente utilizza un framework di orchestrazione IA per ricevere input dell'utente e generare una risposta.
---
model: googleai/gemini-1.5-flash-latest
config:
  temperature: 1.0
  safetySettings:
    - category: HARM_CATEGORY_HATE_SPEECH
      threshold: BLOCK_LOW_AND_ABOVE
    - category: HARM_CATEGORY_DANGEROUS_CONTENT
      threshold: BLOCK_ONLY_HIGH
    - category: HARM_CATEGORY_HARASSMENT
      threshold: BLOCK_LOW_AND_ABOVE
    - category: HARM_CATEGORY_SEXUALLY_EXPLICIT
      threshold: BLOCK_LOW_AND_ABOVE
input:
  schema:
    request: string, The users request for where they want to travel to.
    place: string, The place that closely represents the users request.
    placeDescription: string, A description of that place.
    activities(array, a stringify list of activities that can be found at the specified place): string
    restaurants?(array, a stringify list of all the restaurants found at that location): string
output:
  schema:
    place: string, The place the user is traveling to.
    itineraryName: string, a catchy itinerary name that encapsulates the spirit of the trip and includes the place name
    startDate: string, the start date of the trip
    endDate: string, the end date of the trip
    tags(array, relevant tags for the trip): string
    itinerary(array):
      day: number
      date: string
      planForDay(array):
        activityRef: string, the reference value for the activity - this comes from the available activities JSON. If no value is present use a ref value of restaurant.
        activityTitle: string, a catchy title for the activity
        activityDesc: string, a six word description of the activity
        photoUri?: string, set the photo uri value for restaurants only.
        googleMapsUri?: string, if this is a restaurant include the googleMapsUri
---

Generate an itinerary for a tourist planning on traveling to the location specified based in their request.
If there is something that does not exist within the list of activities, do not include it in your answer.
Feel free to relate the activitiy to the request in a meaningful way.
In the plan for day array, put activities as a travel brouchure might do.
Come up with a catchy name for the itinerary.

Pick three activities per day, minimum of three day trip unless otherwise specified in the request.

Output schema should not include the properties type or object.

Pick a date after 2024-05-14 but before 2024-12-31.

The output date must be in the format year-month-day.

Give each activity a unique title and description.

Limit activity descriptions to 6 words.

If no restaurants are supplied, do not recommend any restaurants to eat at.

{{#if restaurants}}
Find a restaurant to eat at each day.

Include a restaurant to visit in the itinerary for each day from the available restaurants.
The restaurant should be the only activity with a photoUri.
The photoUri for the restaurant should be from the photoUri property from the restaurant array.
If there are no restaurants to pick from, do not include it in the list.

The photoUri from the restaurantFinder should be in the format of places/${placeId}/photos/${photoId}

Each restaurant should be unique to the overall itinerary.
Each restaurant must contain a photoUri in their output JSON schema.
Each restaurant must also include  an activitiyRef, activityTitle, and activityDesc in their output
{{/if}}
Output must be in JSON format.

REQUEST : {{request}}
PLACE : {{place}}
PLACE DESCRIPTION : {{placeDescription}}
AVAILABLE ACTIVITIES : {{activities}}
RESTAURANTS : {{restaurants}}
Quando si lavora con l'IA generativa, è importante creare prompt efficaci in modo che il modello restituisca risposte di alta qualità. Firebase Genkit fornisce il plug-in Dotprompt e il formato di testo per aiutarti a scrivere e organizzare i tuoi prompt di IA generativa. Il formato incapsula il prompt, lo schema di input e output, il modello e la configurazione in un unico file.

Il seguente esempio di codice mostra un file Dotprompt utilizzato nell'app di viaggio. Lo schema si basa sulle informazioni fornite dall'utente quando descrive il viaggio dei propri sogni.
Dotprompt è progettato sulla base del presupposto che i prompt sono codice. Scrivi e gestisci i tuoi prompt in file appositamente formattati denominati file dotprompt, tieni traccia delle modifiche apportate utilizzando lo stesso sistema di controllo della versione che utilizzi per il codice e ne esegui il deployment insieme al codice che chiama i tuoi modelli di IA generativa.
I flussi sono funzioni altamente digitate, riproducibili in streaming, richiamabili localmente e da remoto e completamente osservabili. Firebase Genkit fornisce un'interfaccia a riga di comando e un'interfaccia utente per sviluppatori per lavorare con i flussi, ad esempio quelli in esecuzione o di debug.
import {defineTool} from '@genkit-ai/ai/tool';
...
{
  name: 'restaurantFinder',
  description: `Used when needing to find a restaurant based on a users location.
  The location should be used to find nearby restaurants to a place. You can also
  selectively find restaurants based on the users preferences, but you should default
  to 'Local' if there are no indications of restaurant types in the users request.
  `,
  inputSchema: z.object({
    place: z.string(),
    typeOfRestaurant: z.string().optional() }),
    outputSchema: z.unknown(),
},
...
async (input) => {
  if (input.typeOfRestaurant == undefined) {
    input.typeOfRestaurant = "Local";
  }
  const geocodeEndpoint = "https://places.googleapis.com/v1/places:searchText";
  const textQuery = {textQuery: `${input.typeOfRestaurant} restaurants in ${input.place}`};

  const  response = await axios.post(
    geocodeEndpoint,
    JSON.stringify(textQuery),
    {
      headers: {
        "Content-Type": "application/json",
        "X-Goog-Api-Key": MAPS_API_KEY,
        "X-Goog-FieldMask": "places.displayName,places.formattedAddress,places.priceLevel,places.photos.name,places.editorialSummary,places.googleMapsUri"
      }
    }
  );
  console.log(response.data);
  let data = (response.data as PlaceResponse);
  for(let i = 0; i < data.places.length; i++) {
    if (data.places[i].photos) {
      data.places[i].photos = [data.places[i].photos[0]];
    }
  }
  return data as PlaceResponse;
}
Puoi utilizzare le chiamate di funzione in Genkit per estendere la funzionalità dell'agente in modo che possa perfezionare ulteriormente le risposte e completare attività aggiuntive. L'app di viaggi definisce uno strumento che può restituire informazioni sui ristoranti dall'API Places in base alla corsa desiderata dall'utente. Il codice utilizza Zod per definire lo schema di input e output in modo che i risultati della query possano essere convalidati.
...
export const textRefinement = defineFlow(
{
  name: 'textRefinement',
  inputSchema: z.string(),
  outputSchema: z.unknown(),
},
async (userRequest) => {
  const refinementPrompt = await prompt('textRefinement')
  const result = await refinementPrompt.generate({
      input: {
          request: userRequest
      },
  });
  return result.output();
});
Per aiutare a offrire agli utenti un'esperienza di ricerca più personalizzata, dopo che l'utente descrive il viaggio dei propri sogni, Gemini determina se sono necessarie ulteriori informazioni in base ai prompt forniti dall'app di viaggi e segnala all'app se pensa che siano necessarie maggiori informazioni. L'app richiede quindi queste informazioni all'utente e le aggiunge alla richiesta nel backend.
import 'package:http:http.dart' as http;
...
Future<List<Trip>> generateTrips(String description, List<Image> images) async {
  final uri = Uri.parse('.../generateTrips');
  final request = http.MultipartRequest('POST', uri);
  request.fields['description'] = description;
  request.files.add(http.MultipartFile.fromData(
      images.name, images.bytes,
      contentType: MediaType('image', 'png'),
  ));
  var response = await request.send();
  if (response.statusCode == 200) {
      final body = await response.body.text();
      final items = jsonDecode(body) as List<dynamic>;
      return items.map(Trip.fromJson).toList();
  }
  ...
  import { imagen2, geminiProVision } from '@genkit-ai/vertexai';
  import { generate } from '@genkit-ai/ai';

  const imageResult = await generate({
    model: imagen2,
    prompt: 'Generate an image of a very specific historical time and place.',
  });
  const generatedImage = imageResult.media();

  const descriptionResult = await generate({
    model: geminiProVision,
    prompt: [
      {
        text: 'What is the historical time and place represented in this picture?',
      },
      { media: generatedImage },
    ],
  });
  console.log(descriptionResult.text());
  }
L'app Viaggi chiede all'utente di definire il viaggio dei propri sogni utilizzando l'input di testo o toccando il pulsante del microfono per attivare la conversione della voce in testo. L'utente può anche caricare immagini.

L'app utilizza un pacchetto Dart di pub.dev per l'integrazione con funzionalità di conversione della voce in testo native per ogni piattaforma e utilizza l'API Gemini all'interno di Firebase Genkit per gestire input multimodali come immagini o video. L'API Gemini utilizza la RAG (Retrieval-augmented Generation) per restituire un insieme di corse suggerite utilizzando Firebase Data Connect e gli incorporamenti per eseguire una ricerca del vicino più vicino.

Scala la tua app per la produzione

Firebase Hosting si integra con i più diffusi framework web moderni, tra cui Flutter. Utilizzando Firebase Hosting e Cloud Functions for Firebase con questi framework, puoi sviluppare app e microservizi nel tuo ambiente framework preferito e quindi eseguirne il deployment in un ambiente server gestito e sicuro. Prima di passare alla produzione, determina la sicurezza e le prestazioni di tutti i servizi della tua app. Per ulteriori informazioni, consulta l'elenco di controllo per il lancio di Firebase.
L'app di viaggio utilizza l'IA di Google per eseguire rapidamente l'iterazione dei dati di test ed è una buona scelta per le app con casi d'uso minimi dell'IA che non hanno bisogno di scalabilità. Vertex AI ha una quota più alta per contribuire alla scalabilità delle applicazioni di produzione e norme sulla privacy più efficaci per proteggere i dati degli utenti. Genkit dispone di funzionalità integrate per cambiare facilmente modello, in modo da non dover riscrivere i prompt o le chiamate API.