En la era actual, la transformación digital ha revolucionado la manera en que las empresas interactúan con sus clientes. La Inteligencia Artificial (IA) ha sido un pilar fundamental en este cambio, y la combinación de IA generativa con la infraestructura potente de Amazon Web Services (AWS) ha impulsado una evolución significativa en los call centers. La capacidad de AWS para brindar una base sólida y escalable se ha fusionado con la innovación disruptiva de la IA generativa, llevando la personalización y la eficiencia a niveles sin precedentes en la atención al cliente.

En la actualidad, en los contact center las diferentes empresas cuentan con grabaciones de las interacciones entre sus operadores y clientes en el centro de llamadas. Sin embargo, estas grabaciones no se utilizan de manera efectiva: los operadores no pueden acceder fácilmente a información previa sobre conversaciones con un cliente específico, ya sea realizada por ellos mismos o por otros operadores. Además, resulta complicado realizar un análisis detallado y obtener métricas significativas de las conversaciones del centro de llamadas. Esta limitación dificulta la extracción de indicadores clave de rendimiento (KPIs) que podrían utilizarse para mejorar el servicio prestado y aumentar la retención de clientes.

El objetivo es crear y ejecutar una demostración que utilice los servicios de inteligencia artificial generativa proporcionados por AWS. Esta iniciativa tiene como finalidad aprovechar las tecnologías de IA para elevar la calidad del servicio, incrementar la retención de clientes y optimizar las operaciones del centro de llamadas.

¿Qué es la IA generativa y cómo se integra con AWS?

La inteligencia artificial generativa (IA generativa) crea contenido nuevo, como conversaciones, imágenes o música. Esta tecnología representa un paso adelante en la IA, permitiendo que aprenda diversos temas y resuelva problemas utilizando datos de entrenamiento previos.

Su importancia radica en su potencial para reinventar experiencias de clientes, generar aplicaciones innovadoras y aumentar la productividad. Se prevé que la IA generativa impulse un aumento del 7% en el PIB mundial y aumente la productividad en 1,5 puntos porcentuales en 10 años.

La IA generativa se apoya en los Modelos de Lenguaje Extensos (LLMs), que tienen la capacidad de generar texto similar al humano sobre una amplia gama de temas, y en los modelos fundacionales, que son fundamentales para entrenar y optimizar estos LLMs. Estos modelos son cruciales para su aprendizaje y capacidad para resolver problemas, aprovechando datos previos y adaptándose a nuevas situaciones.

Los beneficios clave incluyen:

AWS ofrece nuevos servicios para poder construir soluciones de Inteligencia Artificial generativa, entre las más importantes podemos destacar las siguientes:

IA generativa en la empresa

La adaptación de modelos como ChatGPT a las necesidades empresariales, especialmente en la Inteligencia Artificial Generativa (IAG), ha generado una revolución que va más allá del mero hype. Esta rama de la IA se centra en crear nuevo contenido, marcando una etapa distinta a la estadística tradicional que se enfoca en análisis descriptivos y diagnósticos.

Pero, ¿cómo implementar estos modelos en empresas? Aquí es donde entra el fine tuning (afinado) y el retrieval-augmented generation (RAG). Mientras que el fine-tuning se centra en adaptar y mejorar un modelo preentrenado mediante datos específicos para una tarea particular, el RAG, se centra en crear una base de datos con información relevante para el cliente. Por tanto, cuando usamos el método RAG en el momento de la consulta, el modelo busca fragmentos relevantes en estos documentos, generando una respuesta y proporcionando detalles sobre qué documentos y partes específicas se utilizaron para crearla. Esta transparencia en la generación de respuestas permite una trazabilidad y verificación de la información, fundamental en entornos donde se requiere control de acceso a datos.

En entornos empresariales, nuestra recomendación es un enfoque más centrado en un buen 'modelado' de los textos que en el ajuste específico del modelo mediante fine-tuning. La importancia de un buen modelado de datos resulta fundamental para alcanzar un rendimiento óptimo en diversas tareas. La elección entre fine-tuning y RAG depende de varios factores, como el volumen de datos, la privacidad requerida, la versatilidad del modelo y la frecuencia de actualización de datos. Por ejemplo, para tareas más amplias que implican un entendimiento más profundo, como aprender un nuevo lenguaje o adquirir un vocabulario completo, el fine-tuning puede resultar más apropiado debido a su capacidad para ajustarse específicamente a esos contextos específicos y complejos. En cambio, en un caso de uso en el que queremos generar respuestas contextualizadas con información verificable de nuestro dominio de negocio, usar RAG es la mejor opción en este tipo de ambientes empresariales, ofreciendo mayor seguridad y control en el acceso a la información crítica, lo cual es crucial para la toma de decisiones estratégicas y la gestión eficiente de datos sensibles.

Implementando un caso de uso real de GENAI en AWS

En la actualidad, en los contact center las diferentes empresas cuentan con grabaciones de las interacciones entre sus operadores y clientes en el centro de llamadas. Sin embargo, estas grabaciones no se utilizan de manera efectiva o hay una persona dedicando muchas horas para etiquetar o extraer esa información de manera manual: los operadores no pueden acceder fácilmente a información previa sobre conversaciones con un cliente específico, ya sea realizada por ellos mismos o por otros operadores y en muchas ocasiones tienen que escuchar de nuevo las grabaciones para poder realizar acciones. Además, resulta complicado realizar un análisis detallado y obtener métricas significativas de las conversaciones del centro de llamadas. Esta limitación dificulta la extracción de indicadores clave de rendimiento (KPIs) que podrían utilizarse para mejorar el servicio prestado y aumentar la retención de clientes.

El objetivo es crear y ejecutar una demostración que utilice los servicios de IA generativa proporcionados por AWS. Esta iniciativa tiene como finalidad aprovechar las tecnologías de IA para elevar la calidad del servicio, incrementar la retención de clientes y optimizar las operaciones del centro de llamadas.

Mediante esta demostración, podremos convertir las grabaciones de las llamadas en texto, lo que nos permitirá analizarlas a través de paneles de control y chats interactivos. Estos recursos nos habilitarán para hacer preguntas en lenguaje natural sobre las llamadas archivadas, brindándonos la capacidad de explotar esta información valiosa de manera intuitiva y eficiente.

Demostración del proceso.

Arquitectura de la solución

Arquitectura de la solución

A continuación, vamos a ir haciendo zoom por las diferentes piezas de la arquitectura, viendo la configuración de los servicios y el código más relevante de cada uno de ellos.

Paso 1 - Ingesta de datos

Una vez que los archivos de audio (mp3, mp4, wav, etc.) se coloquen en el Bucket de S3 de Recepción, se activará automáticamente el proceso. El control completo del flujo hasta que estén listos para ser consultados se llevará a cabo mediante “Step Functions”, lo que garantiza una trazabilidad total de la coordinación de los distintos procesos.

Para automatizar esta ejecución, se establecerá un Evento en Amazon EventBridge que pondrá en marcha Step Functions cada vez que se agregue un archivo de audio al Bucket de S3.

A continuación, se detalla la configuración del flujo de orquestación de “Step Functions”.

{
  "Comment": "Post-Call Analytics Workflow with Transcribe and Comprehend",
  "StartAt": "TranscribeAudio",
  "States": {
    "TranscribeAudio": {
      "Comment": "Sends the file in S3 for Transcription",
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:589805231100:function:GenerateCallTranscriptLambda",
      "Next": "WaitForTranscribe"
    },
    "WaitForTranscribe": {
      "Type": "Wait",
      "Seconds": 60,
      "Next": "CheckTranscribe"
    },
    "CheckTranscribe": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:589805231100:function:TranscribeCheckStatusLambda",
      "Next": "TranscribeComplete?"
    },
    "TranscribeComplete?": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.transcriptJobStatus",
          "StringEquals": "FAILED",
          "Next": "Failed"
        },
        {
          "Variable": "$.transcriptJobStatus",
          "StringEquals": "COMPLETED",
          "Next": "Parallel"
        }
      ],
      "Default": "WaitForTranscribe"
    },
    "Parallel": {
      "Type": "Parallel",
      "Next": "PrepareOutputAnalytics",
      "Branches": [
        {
          "StartAt": "SummarizeCallBedrock",
          "States": {
            "SummarizeCallBedrock": {
              "Comment": "Performs summarization using Bedrock Claude V2 Model",
              "Type": "Task",
              "Resource": "arn:aws:lambda:us-east-1:589805231100:function:GenerateCallSummaryLambdaContainer",
              "End": true
            }
          }
        },
        {
          "StartAt": "IndexTranscriptionKendra",
          "States": {
            "IndexTranscriptionKendra": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:us-east-1:589805231100:function:IndexTransctiptionInKendraLambda",
              "End": true
            }
          }
        }
      ]
    },
    "PrepareOutputAnalytics": {
      "Comment": "Prepare Data from analytics dashboard",
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:589805231100:function:PrepareOutputAnalyticsLambda",
      "Next": "Success"
    },
    "Failed": {
      "Type": "Fail",
      "Cause": "Error launching or processing Transcribe job."
    },
    "Success": {
      "Type": "Pass",
      "End": true
    }
  }
}

Paso 2 - Speech To Text

Los archivos de audio se transformarán en texto mediante el servicio de AWS, Amazon Transcribe. Esta conversión nos permitirá tener disponible nuestra grabación de audio en un formato textual, lo que nos brinda la oportunidad de aprovechar al máximo su contenido para su análisis y uso en diversas aplicaciones. Este texto generado a partir del audio nos ofrece flexibilidad para aprovecharlo de manera óptima y aplicar diversas estrategias de extracción de información.

A continuación podemos ver el código de la lambda que invocará a este servicio:

import boto3
import os
import time

S3_BUCKET_CALLS = os.environ.get('S3_BUCKET_CALLS')
S3_BUCKET_TRANSCRIPT_CALLS = os.environ.get('S3_BUCKET_TRANSCRIPT_CALLS')

def lambda_handler(event, context):

    # Transcribe meeting recording to text
    transcribe_client = boto3.client('transcribe')
    recording_name = event['detail']['object']['key']
    job_tokens = recording_name.split('-')

    job_name = '{}_{}'.format(job_tokens[0], int(time.time()))
    media_format = job_tokens[1]
    print(media_format)
    media_uri = 's3://{}/{}'.format(S3_BUCKET_CALLS, recording_name)
    output_key = '{}.txt'.format(job_name)

    try:
        job_args = {
            'TranscriptionJobName': job_name,
            'Media': {'MediaFileUri': media_uri},

            'IdentifyLanguage': True,
            'OutputBucketName': S3_BUCKET_TRANSCRIPT_CALLS,
            'OutputKey': output_key
        }
        transcribe_client.start_transcription_job(**job_args)

        print("Started transcription job {}.".format(job_name))
    except Exception:
        print("Couldn't start transcription job %s.".format(job_name))
        raise

    event["jobName"] = job_name
    event["OutputBucketName"] = S3_BUCKET_TRANSCRIPT_CALLS
    event["OutputTranscriptionKey"] = output_key
    event["MediaURI"] = media_uri

    return event

Paso 3 - Retrieval-augmented generation (RAG)

Las transcripciones textuales de las llamadas se guardarán en la base de datos vectorial de Amazon Kendra. Al utilizar Amazon Kendra, tendremos la capacidad de localizar fácilmente el contenido más relevante en nuestros documentos de texto, lo que potenciará la calidad de las respuestas proporcionadas por los LLMs, adaptándolas a nuestras fuentes de datos personalizadas.

Para llevar a cabo este proceso, es necesario haber creado previamente un índice en el servicio de Kendra (valor para la variable INDEX_ID en el siguiente código) y haber configurado un Datasource para ese índice (valor para la variable DS_ID en el siguiente código) que apunte al Bucket de S3 donde se almacenarán los textos de las transcripciones de las llamadas.

Al integrar las transcripciones textuales de llamadas en la base de datos vectorial de Amazon Kendra, se logra una gran optimización. Este enfoque permite no solo almacenar las transcripciones, sino también extraer lo más relevante para maximizar la calidad de las respuestas de los LLMs. Esto significa que las respuestas generadas se adaptan de manera personalizada, aprovechando al máximo nuestras fuentes de datos.

import json
import logging
import os
import urllib
import urllib.request
import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def put_document(dsId, indexId, s3url, text):
    logger.info(f"put_document(dsId={dsId}, indexId={indexId}, s3url={s3url}, text='{text[0:100]}...')")
    document = get_document(dsId, indexId, s3url, text)
    documents = [document]
    logger.info("KENDRA.batch_put_document: " + json.dumps(documents, default=str)[0:1000] + "...")
    kendra_client = boto3.client('kendra')
    result = kendra_client.batch_put_document(
        IndexId=indexId,
        Documents=documents
    )
    if 'FailedDocuments' in result and len(result['FailedDocuments']) > 0:
        logger.error("Failed to index document: " + result['FailedDocuments'][0]['ErrorMessage'])
    logger.info("result: " + json.dumps(result))
    return result

def parse_s3url(s3url):
    r = urllib.parse.urlparse(s3url, allow_fragments=False)
    bucket = r.netloc
    key = r.path.lstrip("/")
    file_name = key.split("/")[-1]
    return [bucket, key, file_name]

def get_bucket_region(bucket):
    # get bucket location.. buckets in us-east-1 return None, otherwise region is identified in LocationConstraint
    try:
        region = S3.get_bucket_location(Bucket=bucket)["LocationConstraint"] or 'us-east-1'
    except Exception as e:
        logger.info(
            f"Unable to retrieve bucket region (bucket owned by another account?).. defaulting to us-east-1. Bucket:"
            f" {bucket} - Message: " + str(
                e))
        region = 'us-east-1'
    return region

def get_document(ds_id, index_id, s3url, text):
    bucket, key, file_name = parse_s3url(s3url)
    region = get_bucket_region(bucket)
    document = {
        "Id": s3url,
        "Title": file_name,
        "Attributes": [
            {
                "Key": "_data_source_id",
                "Value": {
                    "StringValue": ds_id
                }
            },
            {
                "Key": "_source_uri",
                "Value": {
                    "StringValue": f"https://s3.{region}.amazonaws.com/{bucket}/{key}"
                }
            }
        ],
        "Blob": text
    }

    return document


def lambda_handler(event, context):
    logger.info("Received event: %s" % json.dumps(event))

    job_name = event['jobName']
    logger.info(f"Transcription job name: {job_name}")

    index_id = os.environ['INDEX_ID']
    ds_id = os.environ['DS_ID']

    transcribe = boto3.client('transcribe')
    transcription_job = transcribe.get_transcription_job(TranscriptionJobName=job_name)

    media_s3url = transcription_job['TranscriptionJob']['Media']['MediaFileUri']

    logger.info("Process transcription and prepare for indexing")

    bucket = event["OutputBucketName"]
    file_to_read = event["OutputTranscriptionKey"]
    client_s3 = boto3.client('s3')

    # Download de object from s3 and save temporary in tmp as lambda required
    local_file_path = '/tmp/transcription.txt'
    client_s3.download_file(bucket, file_to_read, local_file_path)

    with open(local_file_path, 'r') as file:
        content = file.read()
        jtext = json.loads(content)
        call_transcription = (jtext['results']['transcripts'][0]['transcript'])

    logger.info("Index transcription document in Kendra")
    result = put_document(dsId=ds_id, indexId=index_id, s3url=media_s3url, text=call_transcription)

    event["kendra_result"] = result
    return event

Paso 4 - GenAI para sumarizar y extraer conocimiento de las transcripciones

Utilizaremos GenAI, una solución poderosa para resumir y extraer información clave de las transcripciones. Para este fin, nos apoyaremos en el servicio serverless de IA Generativa de Amazon, Bedrock, que utiliza el LLM Claude V2, una solución gestionada y sin servidor proporcionados por AWS.

Además, sacaremos provecho de Langchain, una librería especializada en trabajar con modelos fundamentales, maximizando todas sus ventajas. Esta herramienta nos brinda la capacidad de realizar ingeniería de prompts, gestionar eficientemente los modelos de lenguaje y manejar textos extensos presentes en las transcripciones. Implementaremos técnicas de map-reduce para procesar estos textos extensos, asegurando que nunca sobrepasemos los límites del servicio Bedrock, independientemente del tamaño del archivo de audio original.

Con la gestión de prompts en Langchain, tendremos la capacidad de instruir a la IA generativa sobre cómo deseamos obtener el resultado deseado, permitiéndonos sacar el máximo provecho en procesos posteriores. Esto nos permite guiar la estructura del resultado final y especificar los valores de listas de categorías, por ejemplo, para etiquetar nuestros textos de manera personalizada.

Esta capacidad nos permite enriquecer la IA generativa con nuestras necesidades y conocimientos específicos del caso de uso o del negocio en particular. De esta forma, podemos aprovechar al máximo las capacidades de la IA generativa, adaptándola a nuestras necesidades y optimizando su rendimiento en escenarios concretos.

A continuación, presentamos un ejemplo de código en Python diseñado para extraer información relevante de nuestras llamadas. El prompt utilizado es un ejemplo básico; en cada escenario particular, será necesario ajustar este prompt para proporcionar ejemplos específicos adaptados a nuestro caso de uso o necesidad empresarial. Esto incluye definir los valores de categorización de las llamadas según nuestras necesidades particulares.

Además, aprovecharemos el potencial de Langchain para modificar diversos templates, personalizando al máximo el resultado del LLM.

import json
import os
import boto3
from langchain.llms.bedrock import Bedrock

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import PromptTemplate
from langchain.chains.summarize import load_summarize_chain

import wave
import contextlib

def lambda_handler(event, context):
    os.environ['TRANSFORMERS_CACHE'] = '/tmp/cache/'

    event["OutputBucketName"]

    boto3_bedrock = boto3.client('bedrock-runtime')

    llm = Bedrock(
        model_id="anthropic.claude-v2",
        model_kwargs={'max_tokens_to_sample': 2048,
                      'temperature': 0.3,
                      'top_k': 250,
                      'top_p': 0.999,
                      'stop_sequences': ['Human:']},
        client=boto3_bedrock,
    )

    bucket = event["OutputBucketName"]
    file_to_read = event["OutputTranscriptionKey"]
    client_s3 = boto3.client('s3')

    # Descarga el objeto de S3 y almacénalo en /tmp
    local_file_path = '/tmp/transcription.txt'
    client_s3.download_file(bucket, file_to_read, local_file_path)

    with open(local_file_path, 'r') as file:
        content = file.read()
        jtext = json.loads(content)
        print(f'Contenido del archivo descargado: {jtext}')
        call_transcription = (jtext['results']['transcripts'][0]['transcript'])
        language_code_transcription = (jtext['results']['language_code'])
    llm.get_num_tokens(call_transcription)

    text_splitter = RecursiveCharacterTextSplitter(
        separators=["\n\n", "\n"], chunk_size=4000, chunk_overlap=100
    )

    docs = text_splitter.create_documents([call_transcription])
    num_docs = len(docs)
    num_tokens_first_doc = llm.get_num_tokens(docs[0].page_content)

    print(
        f"Now we have {num_docs} documents and the first one has {num_tokens_first_doc} tokens"
    )

    prompt_template = """Dado este texto {text}. \n """ \
                        """Devuelve un json compuesto por los siguientes cinco campos (Summary, Sentiment, Topic, Positive Tags, Negative Tags):\n """ \
                        """1. Texto resumido del contenido de la llamada. Elimina detalles de que es una llamada, o un agente ha llamado.\n""" \
                        """2. Sentimiento de la conversación en base a las etiquetas de (Positivo, Negativo o Neutro)\n""" \
                        """3. Tópic principal de la llamada\n""" \
                        """4. Palabras y tópicos que salen en el texto y resalten el sentimiento positivo de la conversación\n""" \
                        """5. Palabras y tópicos que salen en el texto y resalten el sentimiento negativo de la conversación\n"""

    prompt = PromptTemplate(
        template=prompt_template,
        input_variables=["text"]
    )

    overall_chain = load_summarize_chain(llm, chain_type="map_reduce", map_prompt=prompt, combine_prompt=prompt,
                                         return_intermediate_steps=True, verbose=False)

    output = overall_chain({"input_documents": docs}, return_only_outputs=True)
    event["outputIAResults"] = output["output_text"]

    return event

Paso 5 - Preparación del dato analítico para su posterior visualización en el cuadro de mando

Optimizaremos los resultados obtenidos en las fases previas al realizar los cálculos necesarios para garantizar la disponibilidad de datos en Athena. Esta acción permitirá a usuarios y modelos de Machine Learning realizar consultas mediante SQL en etapas posteriores.

La presentación de todos los resultados sigue la metodología del enfoque Lakehouse, capitalizando la eficiencia de Iceberg junto con el catálogo de Glue y Athena como motor de SQL.

Además, estos datos analíticos, provenientes de las interacciones con los clientes, tienen la capacidad de integrarse con nuestro CRM para enriquecer la información existente. Asimismo, pueden ser vinculados con otros sistemas internos o aplicativos para maximizar su utilidad y valor dentro de la organización.

Para asegurar el correcto funcionamiento de este proceso, es crucial tener las tablas en formato Apache Iceberg previamente creadas en el servicio de Athena. La ausencia de estas tablas podría generar errores en esta fase del flujo. Para la creación de estas tablas, se utilizarán los siguientes comandos SQL:

CREATE TABLE summary_table_iceberg( callid int, summary string, sentiment string, topic string, positivetags string, negativetags string, agent string, duration string, calltimestamp string) LOCATION 's3://poc-iagen-output-calls-results-589805231100/table_summary/' TBLPROPERTIES ('table_type'='ICEBERG')

CREATE TABLE positive_entities_iceberg( entity string) LOCATION 's3://poc-iagen-output-calls-results-589805231100/tags_positive//' TBLPROPERTIES ('table_type'='ICEBERG')

CREATE TABLE negative_entities_iceberg( entity string) LOCATION 's3://poc-iagen-output-calls-results-589805231100/tags_negative//' TBLPROPERTIES ('table_type'='ICEBERG')

Utilizando el siguiente código, lograremos almacenar los resultados derivados de la aplicación de la IA generativa para extraer valor de nuestras transcripciones, todo esto sobre tablas en Athena.

import json
import boto3
import time

CLIENT = boto3.client("athena")
DATABASE_NAME = '"demo-call-center"'
RESULT_OUTPUT_LOCATION = "s3://poc-iagen-call-athena-output-results"

def has_query_succeeded(execution_id):
    state = "RUNNING"
    max_execution = 5

    while max_execution > 0 and state in ["RUNNING", "QUEUED"]:
        max_execution -= 1
        response = CLIENT.get_query_execution(QueryExecutionId=execution_id)
        if (
                "QueryExecution" in response
                and "Status" in response["QueryExecution"]
                and "State" in response["QueryExecution"]["Status"]
        ):
            state = response["QueryExecution"]["Status"]["State"]
            if state == "SUCCEEDED":
                return True

        time.sleep(5)

    return False

def insert_rows(query):
    print(query)
    response = CLIENT.start_query_execution(
        QueryString=query,
        ResultConfiguration={"OutputLocation": RESULT_OUTPUT_LOCATION},
    )

    return response["QueryExecutionId"]

def lambda_handler(event, context):
    # Transcribe meeting recording to text
    output_data = event[0]['outputIAResults']
    ObjectName = event[0]['detail']['object']['key'].split("-")

    caller_id = ObjectName[0].replace("_", " ")
    call_agent = ObjectName[1].replace("_", " ")
    duration = ObjectName[2].replace("_", " ")
    timestamp = ObjectName[3] + "-" + ObjectName[4] + "-" + ObjectName[5] + " " + ObjectName[6] + ":" + ObjectName[
        7] + ":" + ObjectName[8]

    # Formateamos la salida en formato JSON del paso anterior con GENAI
    inicio_json = output_data.find('{')
    fin_json = output_data.rfind('}') + 1
    json_string = output_data[inicio_json:fin_json]
    datos_json = json.loads(json_string)

    summary = datos_json['Summary']
    sentiment = datos_json['Sentiment']
    topic = datos_json['Topic']
    positive_tags = ','.join([str(elem) for elem in datos_json['Positive Tags']])
    negative_tags = ','.join([str(elem) for elem in datos_json['Negative Tags']])

    summary_table = '"summary_table_iceberg"'
    summary_query = f"INSERT INTO {DATABASE_NAME}.{summary_table} (callid, agent, calltimestamp, topic, " \
            f"summary, sentiment, duration, positivetags, negativetags) VALUES ({caller_id}, " \
            f"'{call_agent}', '{timestamp}', '{topic}','{summary}', '{sentiment}', '{duration}'," \
            f" '{positive_tags}', '{negative_tags}')"

    result_query = insert_rows(summary_query)
    has_query_succeeded(result_query)

    if len(datos_json['Positive Tags']) > 0:

        positive_entities_table = "positive_entities_iceberg"
        positive_entities_query = f"INSERT INTO {DATABASE_NAME}.{positive_entities_table} (entity) VALUES"

        for elem in datos_json['Positive Tags']:
            positive_entities_query = positive_entities_query + "('" + elem + "')" + ","

        positive_entities_query = positive_entities_query[:len(positive_entities_query) - 1] + ";"

        result_query = insert_rows(positive_entities_query)
        has_query_succeeded(result_query)

    if len(datos_json['Negative Tags']) > 0:

        negative_entities_table = "negative_entities_iceberg"
        negative_entities_query = f"INSERT INTO {DATABASE_NAME}.{negative_entities_table} (entity) VALUES"

        for elem in datos_json['Negative Tags']:
            negative_entities_query = negative_entities_query + "('" + elem + "')" + ","

        negative_entities_query = negative_entities_query[:len(negative_entities_query) - 1] + ";"

        result_query = insert_rows(negative_entities_query)
        has_query_succeeded(result_query)

    return event

Paso 6 - Explotación del dato desde cuadro de mando con el estado del Call Center

Podemos aprovechar al máximo nuestros datos de las llamadas mediante informes en Quicksight. Estos informes nos proporcionarán mediciones precisas, como la eficacia de nuestro call center y la satisfacción de nuestros clientes. Además, tendremos acceso a información detallada, incluyendo resúmenes, sentimientos y temas discutidos en cada llamada.

Esta valiosa información también estará disponible para consultas mediante SQL en Athena. Esto nos permitirá enriquecer nuestros modelos de Machine Learning y realizar predicciones basadas en estas características extraídas de las llamadas.

Cuadro de mando

Paso 7 - GenAI con aplicación de Q&A en tiempo real para los agentes del Call Center

Una vez que hemos registrado todas las conversaciones transcritas en nuestra base de datos de vectores, aprovechamos la potencia de Claude V2, un LLM ofrecido por Bedrock y potenciado con nuestros datos. Esta capacidad permite a los gestores del centro de llamadas hacer preguntas en tiempo real sobre las necesidades del interlocutor, momentos específicos de llamadas pasadas y cualquier información relevante al atender una llamada.

Con el propósito de simplificar este proceso, creamos una aplicación fácil de usar con "Streamlit", que se ha dockerizado y desplegado en AWS utilizando el servicio de contenedores gestionados Amazon Fargate. Esta herramienta posibilitará a los gestores del call center realizar preguntas en lenguaje natural a nuestro LLM mejorado. Esta implementación acelera significativamente la obtención de información clave durante las interacciones telefónicas.

Demo Call Center

A continuación, podemos ver el código para crear nuestra aplicación:

from langchain.retrievers import AmazonKendraRetriever
from langchain.llms.bedrock import Bedrock
from langchain.chains import RetrievalQA
import streamlit as st
import os
import boto3
from PIL import Image


kendra_index = "a80e6368-a96e-462b-b1c8-affdd8700be4"
bedrock_region = "us-east-1"
kendra_region = "us-east-1"

def get_kendra_doc_retriever():
    
    kendra_client = boto3.client("kendra", kendra_region)
    retriever = AmazonKendraRetriever(index_id=kendra_index, top_k=3, client=kendra_client, attribute_filter={
        'EqualsTo': {
            'Key': '_language_code',
            'Value': {'StringValue': 'es'}
        }
    }) 
    return retriever


image = Image.open('LogoWeb1.jpg')

st.image(image)

col1, mid, col2 = st.columns([1,1,20])
with col1:
    st.image('LogoParadigma1.jpg', width=60)
with col2:
    st.title('Paradigma Demo Call Center Q&A app')

query = st.text_input("Qué quieres saber sobre las llamadas atendidas en el call center?")

llm_model = "Anthropic Claude V2"

if st.button("Search"):
    with st.spinner("Building response..."):
        retriever = get_kendra_doc_retriever()
        bedrock_client = boto3.client("bedrock-runtime", bedrock_region)

        llm = Bedrock(
            model_id="anthropic.claude-v2",
            model_kwargs={'max_tokens_to_sample': 2048,
                          'temperature': 0.3,
                          'top_k': 250,
                          'top_p': 0.999,
                          'stop_sequences': ['Human:']},
            client=bedrock_client,
        )

        qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)
        query_prompt = query
        print(query_prompt)
        response = qa(query)
        st.markdown("### Answer:")
        st.write(response['result'])

Conclusiones

En resumen, la combinación entre la Inteligencia Artificial Generativa y la infraestructura de AWS está cambiando de forma notable los Call Centers, logrando niveles de personalización y eficiencia en el servicio al cliente sin precedentes. Este enfoque innovador aprovecha la potencia de la infraestructura de AWS y la innovación disruptiva que trae la Inteligencia Artificial Generativa para mejorar la calidad del servicio, retener a más clientes y optimizar las operaciones de los operadores en los Contact Centers.

A través de los pasos detallados, se ha mostrado un caso real de cómo aplicar la Inteligencia Artificial Generativa en AWS para este tipo de caso de uso, demostrando su potencial para transformar las interacciones con los clientes y ofrecer valiosos conocimientos para tomar decisiones comerciales y automatizar muchas tareas realizadas de manera manual o artesanal por los operadores. Esta poderosa combinación está lista para revolucionar la industria de los centros de atención telefónica y mejorar la experiencia de los clientes.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.