Solución de Compass

Compila una app de planificación de viajes impulsada por un agente con IA generativa

En el siguiente diagrama, se muestra una descripción general de alto nivel de cómo puedes combinar los mejores servicios para desarrolladores de Google para compilar aplicaciones basadas en IA.

Compila apps atractivas potenciadas por IA para dispositivos móviles y la Web

Puedes usar Flutter y Firebase Genkit para compilar apps multiplataforma que se puedan integrar sin problemas con la IA.
Puedes usar Genkit para permitir que tu app consuma datos de los LLM con confianza. Para ello, define un esquema que pueda validar la salida del LLM. En Flutter, puedes usar Dart para serializar la solicitud y deserializar la respuesta para que coincida con el esquema de Genkit a través de solicitudes HTTP estándar.
Con IDX, puedes crear una app de Flutter sin necesidad de instalar ningún software. Esto permite desarrollar y probar tu app para Android y tu app web en el navegador.
Firebase Data Connect es un servicio de base de datos relacional para apps web y para dispositivos móviles que te permite compilar y escalar mediante una base de datos PostgreSQL completamente administrada con tecnología de Cloud SQL. Proporciona un esquema seguro, además de administración de consultas y mutaciones con GraphQL, que se integra bien en Firebase Authentication. Data Connect incluye compatibilidad con SDK para Kotlin para Android y la Web.

Compila un agente con Firebase Genkit

El agente usa un framework de organización de IA para recibir entradas del usuario y generar una respuesta.
---
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}}
Cuando se trabaja con IA generativa, es importante crear instrucciones eficaces para que el modelo devuelva respuestas de alta calidad. Firebase Genkit proporciona el complemento Dotprompt y formato de texto para ayudarte a escribir y organizar tus instrucciones de IA generativa. El formato encapsula la instrucción, el esquema de entrada y salida, el modelo y la configuración, todo en un solo archivo.

En el siguiente ejemplo de código, se muestra un archivo de dos puntos que se usa en la app de viajes. El esquema se basa en la información que proporciona el usuario cuando describe su viaje de ensueño.
Dotprompt está diseñado con la premisa de que los mensajes son código. Escribes y mantienes tus instrucciones en archivos con formato especial llamados archivos dotprompt, haces un seguimiento de los cambios en ellas con el mismo sistema de control de versión que usas para tu código y las implementas junto con el código que llama a tus modelos de IA generativa.
Los flujos son funciones de tipo sólida, que se pueden transmitir, que admiten llamadas de forma local y remota, y que son totalmente observables. Firebase Genkit proporciona una interfaz de línea de comandos y una IU para desarrolladores para trabajar con flujos, como los de ejecución o depuración.
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;
}
Puedes usar las llamadas a función en Genkit para extender la funcionalidad del agente, de modo que este pueda definir aún mejor las respuestas y completar tareas adicionales. La app de viajes define una herramienta que puede mostrar información sobre restaurantes desde la API de Places según el viaje que desea el usuario. El código usa Zod para definir el esquema de entrada y salida, de modo que se puedan validar los resultados de la consulta.
...
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();
});
Para brindarles a los usuarios una experiencia de búsqueda más personalizada, después de que el usuario describe el viaje de sus sueños, Gemini determina si se necesita más información en función de las instrucciones que proporciona la app de viajes y de las señales que le envía si cree que necesita más información. Luego, la app solicita al usuario esa información y la adjunta a la solicitud en el 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());
  }
La app de viajes le pide al usuario que defina el viaje de sus sueños usando la entrada de texto o presionando el botón del micrófono para activar la función de voz a texto. El usuario también puede subir imágenes de forma opcional.

La app aprovecha un paquete Dart de pub.dev para integrarse con las capacidades nativas de voz a texto de cada plataforma y usa la API de Gemini dentro de Firebase Genkit para controlar las entradas multimodales, como imágenes o videos. La API de Gemini usa la generación de recuperación aumentada (RAG) para mostrar un conjunto de viajes sugeridos con Firebase Data Connect, además de incorporaciones para realizar una búsqueda de vecinos más cercanos.

Escala tu app para producción

Firebase Hosting se integra en frameworks web modernos y populares, incluido Flutter. Si usas Firebase Hosting y Cloud Functions para Firebase con estos frameworks, puedes desarrollar apps y microservicios en tu entorno de framework preferido y, luego, implementarlos en un entorno de servidor seguro y administrado. Antes de pasar a producción, comprende la seguridad y el rendimiento de todos los servicios de tu app. Para obtener más información, consulta la lista de tareas para el lanzamiento de Firebase.
La app de viajes usa la IA de Google para iterar rápidamente los datos de prueba y es una buena opción para apps con casos de uso mínimos de IA que no necesitan escalar. Vertex AI tiene una cuota más alta para ayudar a escalar las aplicaciones de producción y tiene políticas de privacidad más sólidas para proteger los datos del usuario. Genkit tiene una funcionalidad integrada para cambiar fácilmente de modelo, de modo que no tengas que volver a escribir tus instrucciones o llamadas a la API.