Reemplace los enfoques tradicionales de PNL con ingeniería rápida y modelos de lenguaje grande (LLMS) para la clasificación de texto de tickets de Jira. Un tutorial de código de muestra
¿Recuerda los días en que clasificar texto significaba embarcarse en un viaje de aprendizaje automático? Si ha estado en el espacio ML el tiempo suficiente, probablemente haya visto al menos a un equipo desaparecer en la madriguera de construir el sistema de clasificación de texto “perfecto”. La historia suele ser algo como esto:
- Mes 1: “¡Entrenaremos rápidamente un modelo de PNL!”
- Mes 2: “Necesitamos más datos de entrenamiento…”
- Mes 3: “Esto es suficientemente bueno”
Durante años, la clasificación de textos ha caído en el ámbito del aprendizaje automático clásico. Al principio de mi carrera, recuerdo haber entrenado una máquina de vectores de soporte (SVM) para la clasificación de correo electrónico. Mucho preprocesamiento, iteración, recopilación de datos y etiquetado.
Pero aquí está el giro: estamos en 2024 y los modelos de IA generativa pueden “generalmente” ¡clasifica el texto fuera de la caja! Puede crear un sistema sólido de clasificación de tickets sin necesidad de recopilar miles de ejemplos de capacitación etiquetados, administrar canales de capacitación de ML o mantener modelos personalizados.
En esta publicación, veremos cómo configurar un sistema de clasificación de tickets de Jira utilizando modelos de lenguaje grandes en Amazon Bedrock y otros servicios de AWS.
DESCARGO DE RESPONSABILIDAD: Soy arquitecto GenAI en AWS y mis opiniones son mías.
¿Por qué clasificar los tickets de Jira?
Una pregunta común de las empresas es comprender cómo emplean su tiempo los equipos. Jira tiene funciones de etiquetado, pero a veces puede fallar debido a errores humanos o falta de granularidad. Al realizar este ejercicio, las organizaciones pueden obtener mejores conocimientos sobre las actividades de su equipo, lo que permite tomar decisiones basadas en datos sobre la asignación de recursos, la inversión en proyectos y la desaprobación.
¿Por qué no utilizar otros enfoques de PNL?
Los modelos de ML tradicionales y los transformadores más pequeños como BERT necesitan cientos (o miles) de ejemplos etiquetados, mientras que los LLM pueden clasificar texto de forma inmediata. En nuestras pruebas de clasificación de tickets de Jira, un enfoque de ingeniería rápida igualó o superó a los modelos de aprendizaje automático tradicionales, procesando más de 10.000 tickets anuales por ~$10 al año utilizando Claude Haiku (sin incluir otros costos de servicios de AWS). Además, las indicaciones son más fáciles de actualizar que volver a entrenar los modelos.
Este repositorio de github contiene una aplicación de muestra que se conecta a Jira Cloud, clasifica tickets y los genera en un formato que puede ser consumido por su herramienta de panel favorita (Tableu, Quicksight o cualquier otra herramienta que admita archivos CSV).
Aviso importante: este proyecto implementa recursos en su entorno de AWS mediante Terraform. Incurrirá en costos por los recursos de AWS utilizados. Tenga en cuenta los precios de servicios como Lambda, Bedrock, Glue y S3 en su región de AWS.
Requisitos previos
Deberá tener instalado terraform y la CLI de AWS instalada en el entorno desde el que desea implementar este código.
La arquitectura es bastante sencilla. Puede encontrar detalles a continuación.
Paso 1: Se activa una función de AWS Lambda en un trabajo cron para recuperar tickets de Jira en función de una ventana de tiempo. Luego, esos tickets se formatean y se envían a un depósito de S3 bajo el /sin procesar prefijo.
Paso 2: Se activa un trabajo de pegamento /sin procesar objeto pone. Esto ejecuta una tarea de deduplicación de PySpark para garantizar que no lleguen tickets duplicados al panel. Los boletos deduplicados luego se envían al /escenificado prefijo. Esto es útil para los casos en los que carga tickets manualmente y también depende de la recuperación automática. Si puede asegurarse de que no haya duplicados, puede eliminar este paso.
Paso 3: Se inicia una tarea de clasificación en los nuevos tickets llamando a Amazon Bedrock para clasificar los tickets según un mensaje de un modelo de lenguaje grande (LLM). Después de la clasificación, los resultados finales se envían al /procesado prefijo. Desde aquí, puede recoger el CSV procesado utilizando cualquier herramienta de panel que desee y que pueda consumir un CSV.
Para comenzar, clone el repositorio de github anterior y vaya al directorio /terraform
$ git clone https://github.com/aws-samples/jira-ticket-classification.git$ cd jira-ticket-classification/terraform
Ejecute terraform init, planifique y aplique. Asegúrese de tener terraform instalado en su computadora y AWS CLI configurada.
$ terraform init$ terraform plan
$ terraform apply
Una vez que la infraestructura esté implementada en su cuenta, puede navegar hasta AWS Secrets Manager y actualizar el secreto con sus credenciales de Jira Cloud. Necesitará una clave API, una URL base y un correo electrónico para habilitar la extracción automática.
¡Y eso es todo!
Puede (1) esperar a que Cron inicie una recuperación automática, (2) exportar los tickets a CSV y cargarlos en el prefijo de depósito S3 /unprocessed, o (3) activar manualmente la función Lambda mediante una prueba.
Jira buscar:
Jira fetch usa una función Lambda con un evento cron de Cloudwatch para activarla. Lambda extrae el secreto de AWS y utiliza una solicitud de obtención en un bucle while para recuperar resultados paginados hasta que se completa la consulta JQL:
def fetch_jira_issues(base_url, project_id, email, api_key):
url = f"{base_url}/rest/api/3/search"# Calculate the date 8 days ago
eight_days_ago = (datetime.now() - timedelta(days=8)).strftime("%Y-%m-%d")
# Create JQL
jql = f"project = {project_id} AND created >= '{eight_days_ago}' ORDER BY created DESC"
# Pass into params of request.
params = {
"jql": jql,
"startAt": 0
}
all_issues = []
auth = HTTPBasicAuth(email, api_key)
headers = {"Accept": "application/json"}
while True:
response = requests.get(url, headers=headers, params=params, auth=auth)
if response.status_code != 200:
raise Exception(f"Failed to fetch issues for project {project_id}: {response.text}")
data = json.loads(response.text)
issues = data['issues']
all_issues.extend(issues)
if len(all_issues) >= data['total']:
break
params['startAt'] = len(all_issues)
return all_issues
Luego crea una representación de cadena de un CSV y la carga en S3:
def upload_to_s3(csv_string, bucket, key):
try:
s3_client.put_object(
Bucket=bucket,
Key=key,
Body=csv_string,
ContentType='text/csv'
)
except Exception as e:
raise Exception(f"Failed to upload CSV to S3: {str(e)}")
Trabajo de pegamento
Un evento S3 en el prefijo /unprocessed inicia una segunda lambda que inicia un trabajo de AWS Glue. Esto es útil cuando hay varios puntos de entrada a través de los cuales los tickets de Jira pueden ingresar al sistema. Por ejemplo, si quieres hacer un relleno.
import boto3 # Initialize Boto3 Glue client
glue_client = boto3.client('glue')
def handler(event, context):
# Print event for debugging
print(f"Received event: {json.dumps(event)}")
# Get bucket name and object key (file name) from the S3 event
try:
s3_event = event['Records'][0]['s3']
s3_bucket = s3_event['bucket']['name']
s3_key = s3_event['object']['key']
except KeyError as e:
print(f"Error parsing S3 event: {str(e)}")
raise
response = glue_client.start_job_run(
JobName=glue_job_name,
Arguments={
'--S3_BUCKET': s3_bucket,
'--NEW_CSV_FILE': s3_key
}
)
El trabajo de Glue en sí está escrito en PySpark y se puede encontrar en el repositorio de código. aquí. Lo importante es que hace un izquierdaanti Únase utilizando los ID de problema de los elementos del nuevo CSV frente a todos los ID de los CSV /staged.
Luego los resultados se envían al /escenificado prefijo.
Clasificar entradas de Jira:
Aquí es donde se pone interesante. Resulta que el uso de ingeniería rápida puede funcionar a la par, si no mejor, que un modelo de clasificación de texto que utiliza un par de técnicas.
- Puede definir las clasificaciones y sus descripciones en un mensaje,
- Pídale al modelo que piense paso a paso. (Cadena de pensamiento).
- Y luego generar la clasificación sin tener que entrenar un solo modelo. Vea el mensaje a continuación:
Nota: Es importante validar su mensaje utilizando un subconjunto de tickets clasificados/etiquetados seleccionados por humanos. Debe ejecutar este mensaje a través del conjunto de datos de validación para asegurarse de que se alinee con la forma en que espera que se clasifiquen los tickets.
SYSTEM_PROMPT = '''
You are a support ticket assistant. You are given fields of a Jira ticket and your task is to classify the ticket based on those fieldsBelow is the list of potential classifications along with descriptions of those classifications.
<classifications>
ACCESS_PERMISSIONS_REQUEST: Used when someone doesn't have the write permissions or can't log in to something or they can't get the correct IAM credentials to make a service work.
BUG_FIXING: Used when something is failing or a bug is found. Often times the descriptions include logs or technical information.
CREATING_UPDATING_OR_DEPRECATING_DOCUMENTATION: Used when documentation is out of date. Usually references documentation in the text.
MINOR_REQUEST: This is rarely used. Usually a bug fix but it's very minor. If it seems even remotely complicated use BUG_FIXING.
SUPPORT_TROUBLESHOOTING: Used when asking for support for some engineering event. Can also look like an automated ticket.
NEW_FEATURE_WORK: Usually describes a new feature ask or something that isn't operational.
</classifications>
The fields available and their descriptions are below.
<fields>
Summmary: This is a summary or title of the ticket
Description: The description of the issue in natural language. The majority of context needed to classify the text will come from this field
</fields>
<rules>
* It is possible that some fields may be empty in which case ignore them when classifying the ticket
* Think through your reasoning before making the classification and place your thought process in <thinking></thinking> tags. This is your space to think and reason about the ticket classificaiton.
* Once you have finished thinking, classify the ticket using ONLY the classifications listed above and place it in <answer></answer> tags.
</rules>'''
USER_PROMPT = '''
Using only the ticket fields below:
<summary_field>
{summary}
</summary_field>
<description_field>
{description}
</description_field>
Classify the ticket using ONLY 1 of the classifications listed in the system prompt. Remember to think step-by-step before classifying the ticket and place your thoughts in <thinking></thinking> tags.
When you are finished thinking, classify the ticket and place your answer in <answer></answer> tags. ONLY place the classifaction in the answer tags. Nothing else.
'''
Agregamos una clase de ayuda que encadena las llamadas a Bedrock para acelerar las cosas:
import boto3
from concurrent.futures import ThreadPoolExecutor, as_completed
import re
from typing import List, Dict
from prompts import USER_PROMPT, SYSTEM_PROMPT
class TicketClassifier:
SONNET_ID = "anthropic.claude-3-sonnet-20240229-v1:0"
HAIKU_ID = "anthropic.claude-3-haiku-20240307-v1:0"
HYPER_PARAMS = {"temperature": 0.35, "topP": .3}
REASONING_PATTERN = r'<thinking>(.*?)</thinking>'
CORRECTNESS_PATTERN = r'<answer>(.*?)</answer>'
def __init__(self):
self.bedrock = boto3.client('bedrock-runtime')
def classify_tickets(self, tickets: List[Dict[str, str]]) -> List[Dict[str, str]]:
prompts = [self._create_chat_payload