Cómo crear un ChatBot con Gemini, Kendo UI y Angular 17

Al ver tantos chats conectados con IA, un amigo me dijo una vez que esto era difícil, pero la verdad es que no lo es. Hoy quiero mostrarte lo fácil que resulta construir tu propio chat conectado con IA y con una interfaz muy cuidada usando solo unas pocas líneas de código.

Vamos a aprender cómo crear una aplicación en Angular usando la librería de Gemini e integrarla con Kendo Conversational UI para lograr una interfaz de usuario impecable.
¿Cuánto tiempo tomará? Sorprendentemente, se puede hacer muy rápido. ¡Empecemos!
Configuración del proyecto
Primero, creamos la aplicación de Angular con Angular CLI ejecutando el comando ng new conversional-with-gemini. Esto genera un proyecto de Angular nuevo y listo para trabajar.
ng new conversional-with-geminiEntramos al directorio y generamos un servicio para el cliente de Gemini usando el generador del CLI:
ng g s services/gemini
CREATE src/app/services/gemini.service.spec.ts (404 bytes)
CREATE src/app/services/gemini.service.ts (150 bytes)En Angular 17, como ya no se incluye el archivo de entornos por defecto, lo creamos con el comando ng generate environments.
ng generate environmentsUsando Gemini
Para usar Gemini, necesitamos su SDK junto con una clave de API. Primero, instalamos el SDK de Gemini en el proyecto ejecutando el siguiente comando:
npm i @google/generative-aiLuego, obtén tu clave de API en https://ai.google.dev/, cópiala y guarda el valor en el archivo environment.ts, de esta forma:
export const environment = {
geminy_key: "YourAmazingKey"
};Ya tenemos la clave y el SDK instalado. Ahora es momento de usarlos en nuestro servicio de Gemini. ¡Vamos a ello!
Usando el SDK de Gemini
Vamos a usar el SDK de Gemini dentro del servicio gemini-client. Creamos una instancia de Google AI con la clave y obtenemos el modelo (en nuestro caso 'gemini-pro'), lo que nos devuelve una instancia lista para trabajar.
En nuestro caso, queremos consultar el modelo a partir de un prompt. Para lo que necesito en particular, quiero que actúe como un desarrollador de Angular con experiencia en Kendo UI para Angular.
Así que mi prompt será el siguiente:
You are an expert in the Kendo UI library for Angular, provide a real-world scenario and how this product, helps to solve, with angular code examples and links for resources
El código queda de la siguiente manera:
import { Injectable } from '@angular/core';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class GeminiService {
#generativeAI = new GoogleGenerativeAI(environment.geminy_key);
#prompt =
'You are an expert in the Kendo UI library for Angular, provide a real-world scenario and how this product ' +
'helps to solve, with angular code examples and links for resources';
#model = this.#generativeAI.getGenerativeModel({
model: 'gemini-pro',
});
}Ahora toca generar el contenido con el modelo. Primero, creamos un método llamado generate que recibe el parámetro textInput. Como el método generateContent es asíncrono, la firma de generate también debe ser async.
Primero definimos parts para preparar el contenido que vamos a generar, combinando el prompt base y el textInput que envía el usuario. Esto le da contexto al modelo. Luego obtenemos modelResult, que devuelve varias propiedades. En nuestro caso, la más importante es response. Este objeto incluye la función text() para extraer el texto plano de la respuesta. Para probar que todo funciona, podemos imprimirlo en la consola usando console.log().
Usamos try/catch para manejar cualquier error durante la petición a generateContent.
async generate(textInput: string) {
try {
if (text) {
const parts = [
{
text: this.#prompt,
},
{ text: textInput }
];
const modelResult = await this.#model.generateContent({
contents: [{ role: 'user', parts }],
});
const response = modelResult.response;
const text = response.text();
console.log(text)
}
} catch (e: any) {
console.log(e);
}
}Para usar el servicio, seguimos estos pasos: inyectamos GeminiService en AppComponent.ts, agregamos un método llamado getAnswer y llamamos al método generate del servicio.
export class AppComponent {
geminiService = inject(GeminiService)
async getAnswer(text: any) {
await this.geminiService.generate(text);
}
}En la plantilla HTML, agregamos un input con una referencia de plantilla #userInput. En el button, llamamos al método getAnswer.
<input #userInput><button (click)="getAnswer(userInput.value)">Generate</button>Guardamos los cambios y ¡aquí tenemos nuestra "IA de Kendo con Gemini"! 🎉😎

Bien, funciona, pero visualmente no se ve muy bien. Vamos a darle un diseño profesional y atractivo usando Kendo Conversational UI.
Usando Conversational UI
Para comenzar, instalamos Kendo UI Conversational UI para Angular usando schematics:
ng add @progress/kendo-angular-conversational-uiImportamos ChatModule en la sección de imports de app.component.ts, ya que este módulo provee el componente kendo-chat.
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, ChatModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
export class AppComponent {Ahora viene la parte interesante en gemini.service.ts. Aquí vamos a usar Signals para sincronizar la respuesta de Gemini con el componente kendo-chat, el usuario y los mensajes.
Necesitamos declarar dos usuarios para el chat: uno será kendoIA y el otro será el user (en mi caso, yo). Cada usuario debe tener un ID único. Para mostrar avatares en el chat, podemos usar imágenes locales desde la carpeta assets.
Estoy usando crypto.randomUUID() para generar un ID único.
readonly #kendoIA: User = {
id: crypto.randomUUID(),
name: 'Kendo UI',
avatarUrl: './assets/kendo.png',
};
public readonly user: User = {
id: crypto.randomUUID(),
name: 'Dany',
avatarUrl: './assets/dany.jpg',
};Declaramos una señal $messages con un arreglo de tipo Message y la inicializamos con un mensaje de bienvenida:
$messages = signal<Message[]>([{
author: this.#kendoIA,
timestamp: new Date(),
text: 'Hi! 👋 how I can help you with Kendo ?'
}]);El componente kendo-chat emite un evento SendMessageEvent, el cual contiene toda la información del mensaje desde la interfaz: el usuario, el texto y otros datos. Actualizamos el método generate para recibir este tipo SendMessageEvent y ajustamos la lógica inicial para leer textInput.message.text.
async generate(textInput: SendMessageEvent) {
try {
if (textInput.message.text && this.#model) {Después, actualizamos la señal $messages usando el método update, agregando el nuevo mensaje enviado y configurando el contenido para el modelo a partir de textInput.message.text.
this.$messages.update((p) => [...p, textInput.message]);
const parts = [
{
text: this.#prompt,
},
{ text: textInput.message.text },
];Creamos un nuevo mensaje con la respuesta que devuelve el modelo, le asignamos el autor author: this.#kendoIA y actualizamos nuevamente la lista de mensajes en la señal.
const message = {
author: this.#kendoIA,
timestamp: new Date(),
text,
};
this.$messages.update((p) => [...p, message]);El código completo de este método queda así:
async generate(textInput: SendMessageEvent) {
try {
if (textInput.message.text && this.#model) {
this.$messages.update((p) => [...p, textInput.message]);
const parts = [
{
text: this.#prompt,
},
{ text: textInput.message.text },
];
const result = await this.#model.generateContent({
contents: [{ role: 'user', parts }],
});
const response = result.response;
const text = response.text();
const message = {
author: this.#kendoIA,
timestamp: new Date(),
text,
};
this.$messages.update((p) => [...p, message]);
}
} catch (e: any) {
console.log(e);
}
}Conectando Kendo Chat con Signals
¡Esta es la parte divertida de conectar todo con Signals! Vamos a tomar $messages y user directamente del servicio de Gemini. Primero declaramos las propiedades en app.component.ts.
export class AppComponent {
geminiService = inject(GeminiService)
user = this.geminiService.user;
messages = this.geminiService.$messages
async generate(event: SendMessageEvent) {
await this.geminiService.generate(event);
}
}En la plantilla HTML, agregamos <kendo-chat> y enlazamos la propiedad user. Luego leemos la señal messages() (con paréntesis al ser una señal) y vinculamos el evento sendMessage al método generate que definimos antes.
Como queremos mostrar tanto el avatar como el texto, personalizamos la plantilla por defecto usando la directiva kendoChatMessageTemplate para tener acceso a la variable del mensaje.
El marcado final queda de la siguiente forma:
<kendo-chat [user]="user" [messages]="messages()" (sendMessage)="generate($event)" width="450px">
<ng-template kendoChatMessageTemplate let-message>
<div>
{{ message.text }}
{{message.avatar}}
</div>
</ng-template>Guardamos los cambios y ¡listo! Ya tenemos a Gemini funcionando con una interfaz de chat moderna y fluida.

Todo funciona, pero luego de mostrarle a @Jörgen de Groot mi demo del primer chat usando Gemini and the Conversational UI, me preguntó cómo mantener el historial y el contexto sin necesidad de enviar el prompt en cada interacción.
Contexto y consumo de tokens
El primer enfoque de enviar el prompt una y otra vez genera un gasto innecesario de tokens, además de que no mantiene el contexto inicial ni conserva el historial de la conversación.
Gemini ofrece una funcionalidad de chat que almacena las preguntas y respuestas, lo que permite generar respuestas interactivas e incrementales dentro de un mismo contexto. Esto es justo lo que necesitamos para nuestro chatbot con Kendo, así que vamos a implementarlo.
La sesión de chat (ChatSession)
En nuestro primer intento usamos directamente el model de Gemini para generar contenido. Esta vez vamos a recurrir al método startChat del modelo para obtener un objeto ChatSession, el cual conserva el historial y el contexto inicial junto con el prompt.
El modelo de Gemini permite iniciar una sesión con startChat, donde definimos nuestro prompt inicial y el punto de partida de la conversación. El objeto ChatSession cuenta con el método sendMessage, lo que nos permite enviar únicamente el mensaje nuevo del usuario en cada turno.
Primero declaramos la propiedad #chatSession con el historial inicial, donde incluimos el prompt base y una primera respuesta simulada del modelo:
#chatSession = this.#model.startChat({
history: [
{
role: 'user',
parts: this.#prompt,
},
{
role: 'model',
parts: "Yes, I'm a Angular expert with Kendo UI",
},
],
generationConfig: {
maxOutputTokens: 100,
},
});El siguiente paso es usar chatSession en lugar de enviar manualmente las partes y el rol user al modelo cada vez:
const result = await this.#model.generateContent({
contents: [{ role: 'user', parts }],
});Reemplazamos la llamada al modelo por chatSession y usamos su método sendMessage:
const result = await this.#chatSession.sendMessage(
textInput.message.text,
);¡Listo! 🎉 Ahora nuestro chatbot cuenta con soporte para historial y conversación continua sin tener que reenviar el prompt completo en cada mensaje, lo que nos ahorra una buena cantidad de tokens 😊😁
Mira la demo en acción: 👇

Resumen
En este artículo vimos cómo combinar Gemini con Angular e integrarlo con Angular Conversational UI para crear una interfaz de usuario atractiva y fácil de usar.
También agregamos soporte de historial a nuestro chat, lo que mejora la experiencia del usuario y optimiza el consumo de tokens. Mantener el historial y el contexto en Gemini evita tener que reenviar las instrucciones iniciales una y otra vez.
Aprovechar la función de chat mediante ChatSession nos permite gestionar respuestas incrementales con el contexto adecuado, ofreciendo una experiencia mucho más natural y ahorrando tokens en el proceso. 💰🎉
-
Repositorio del proyecto: https://github.com/danywalls/conversational-with-gemini
Modern frontend development — Angular, React, TypeScript, accessibility, and performance.
Artículos Relacionados
Dockerizar Next.js: Los desafíos ocultos
Vercel oculta la complejidad de las variables de entorno. Cuando pasas a Docker, estos desafíos ocultos salen a la luz. Aprende la arquitectura para escalar tu aplicación correctamente.
Cómo crear un chatbot de IA local con Gemini Nano, Chrome Canary, Angular y Kendo AI Prompt
La carrera de la IA continúa. Alternativas como Ollama nos ayudan a interactuar con modelos de IA en nuestras máquinas, pero los equipos de Chrome y Google van un paso más allá permitiendo que Gemini Nano corra directamente en el navegador.
Lo que aprendí: de demos de chat con IA a productos reales de IA
En 2025 creé varias demos combinando Gemini y Kendo UI para prototipar experiencias de chat rápidas. Aquí comparto lo que aprendí al pasar de simples demos a productos reales con IA.
Cómo usar Resolvers en Angular
Cuando vamos a una fiesta, me encanta tener todas las cervezas listas para tomar una, pero tener que esperar a que las traigan de la nevera a la mesa no es una buena experiencia. Lo mismo pasa con nuestras aplicaciones en Angular: aprende a usar resolvers para precargar datos antes de activar un componente.
Compartir este artículo
Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.
Software real. Lecciones reales.
Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.
Únete a 13,800+ desarrolladores y lectores.
Cero spam. Cancela tu suscripción en cualquier momento.