Ersetzen Sie herkömmliche NLP-Ansätze durch Immediate Engineering und Massive Language Fashions (LLMS) für die Textklassifizierung von Jira-Tickets. Eine exemplarische Vorgehensweise für ein Codebeispiel
Erinnern Sie sich an die Zeiten, als die Klassifizierung von Texten bedeutete, sich auf eine Reise durch maschinelles Lernen zu begeben? Wenn Sie schon lange genug im ML-Bereich tätig sind, haben Sie wahrscheinlich miterlebt, wie mindestens ein Crew bei der Entwicklung des „perfekten“ Textklassifizierungssystems verschwunden ist. Die Geschichte geht normalerweise ungefähr so:
- Monat 1: „Wir trainieren einfach schnell ein NLP-Modell!“
- Monat 2: „Wir brauchen mehr Trainingsdaten…“
- Monat 3: „Das ist intestine genug“
Seit Jahren gehört die Textklassifizierung zum Bereich des klassischen ML. Ich erinnere mich, dass ich zu Beginn meiner Karriere eine Help-Vektor-Maschine (SVM) für die E-Mail-Klassifizierung trainiert habe. Viel Vorverarbeitung, Iteration, Datenerfassung und Beschriftung.
Aber hier ist die Wendung: Wir schreiben das Jahr 2024 und generative KI-Modelle können es schaffen „allgemein“ Klassifizieren Sie Texte über den Tellerrand hinaus! Sie können ein robustes Ticketklassifizierungssystem erstellen, ohne Tausende von gekennzeichneten Trainingsbeispielen zu sammeln, ML-Trainingspipelines zu verwalten oder benutzerdefinierte Modelle zu pflegen.
In diesem Beitrag erfahren Sie, wie Sie ein Jira-Ticketklassifizierungssystem mithilfe großer Sprachmodelle auf Amazon Bedrock und anderen AWS-Diensten einrichten.
HAFTUNGSAUSSCHLUSS: Ich bin GenAI-Architekt bei AWS und meine Meinung ist meine eigene.
Warum Jira-Tickets klassifizieren?
Eine häufige Frage von Unternehmen ist, zu verstehen, wie Groups ihre Zeit verbringen. Jira verfügt über Tagging-Funktionen, die jedoch manchmal aufgrund menschlicher Fehler oder mangelnder Granularität unzureichend sind. Durch diese Übung erhalten Unternehmen bessere Einblicke in die Aktivitäten ihres Groups und können so datengesteuerte Entscheidungen über Ressourcenzuweisung, Projektinvestitionen und Abschreibungen treffen.
Warum nicht andere NLP-Ansätze nutzen?
Herkömmliche ML-Modelle und kleinere Transformatoren wie BERT benötigen Hunderte (oder Tausende) beschrifteter Beispiele, während LLMs Textual content sofort klassifizieren können. In unseren Jira-Ticketklassifizierungstests erreichte oder übertraf ein Immediate-Engineering-Ansatz herkömmliche ML-Modelle und verarbeitete mehr als 10.000 Jahrestickets für etwa 10 US-Greenback professional Jahr mit Claude Haiku (ohne andere AWS-Servicekosten). Außerdem lassen sich Eingabeaufforderungen einfacher aktualisieren als Modelle neu trainieren.
Das Github-Repo enthält eine Beispielanwendung, die eine Verbindung zu Jira Cloud herstellt, Tickets klassifiziert und sie in einem Format ausgibt, das von Ihrem bevorzugten Dashboarding-Software (Tableu, Quicksight oder einem anderen Software, das CSVs unterstützt) verwendet werden kann.
Wichtiger Hinweis: Dieses Projekt stellt Ressourcen in Ihrer AWS-Umgebung mithilfe von Terraform bereit. Es fallen Kosten für die genutzten AWS-Ressourcen an. Bitte beachten Sie die Preise für Dienste wie Lambda, Bedrock, Glue und S3 in Ihrer AWS-Area.
Voraussetzungen
In der Umgebung, in der Sie diesen Code bereitstellen möchten, müssen Terraform und AWS CLI installiert sein
Die Architektur ist ziemlich geradlinig. Particulars finden Sie weiter unten.
Schritt 1: Eine AWS Lambda-Funktion wird auf einem Cron-Job ausgelöst, um Jira-Tickets basierend auf einem Zeitfenster abzurufen. Diese Tickets werden dann formatiert und in einen S3-Bucket darunter verschoben /unbearbeitet Präfix.
Schritt 2: Ein Klebeauftrag wird ausgelöst /unbearbeitet Objekt setzt. Dadurch wird eine PySpark-Deduplizierungsaufgabe ausgeführt, um sicherzustellen, dass keine doppelten Tickets in das Dashboard gelangen. Die deduplizierten Tickets werden dann an den gesendet /inszeniert Präfix. Dies ist nützlich, wenn Sie Tickets manuell hochladen und sich auf den automatischen Abruf verlassen. Wenn Sie sicherstellen können, dass keine Duplikate vorhanden sind, können Sie diesen Schritt entfernen.
Schritt 3: Für die neuen Tickets wird eine Klassifizierungsaufgabe gestartet, indem Amazon Bedrock aufgerufen wird, die Tickets basierend auf einer Eingabeaufforderung für ein großes Sprachmodell (LLM) zu klassifizieren. Nach der Klassifizierung werden die fertigen Ergebnisse an die weitergeleitet /verarbeitet Präfix. Von hier aus können Sie die verarbeitete CSV-Datei mit einem beliebigen Dashboarding-Software abrufen, das eine CSV-Datei verarbeiten kann.
Klonen Sie zunächst das Github-Repo oben und verschieben Sie es in das Verzeichnis /terraform
$ git clone https://github.com/aws-samples/jira-ticket-classification.git$ cd jira-ticket-classification/terraform
Führen Sie Terraform Init aus, planen Sie es und wenden Sie es an. Stellen Sie sicher, dass Terraform auf Ihrem Laptop installiert und die AWS CLI konfiguriert ist.
$ terraform init$ terraform plan
$ terraform apply
Sobald die Infrastruktur in Ihrem Konto bereitgestellt ist, können Sie zum AWS Secrets and techniques Supervisor navigieren und das Geheimnis mit Ihren Jira Cloud-Anmeldeinformationen aktualisieren. Sie benötigen einen API-Schlüssel, eine Foundation-URL und eine E-Mail-Adresse, um den automatischen Pull zu aktivieren
Und das ist es!
Sie können (1) warten, bis der Cron einen automatischen Abruf startet, (2) die Tickets in CSV exportieren und in das S3-Bucket-Präfix /unprocessed hochladen oder (3) die Lambda-Funktion mithilfe eines Exams manuell auslösen.
Jira-Abruf:
Jira fetch verwendet eine Lambda-Funktion mit einem Cloudwatch-Cron-Ereignis, um es auszulösen. Der Lambda ruft das AWS Secret ab und verwendet eine Get-Anfrage in einer Whereas-Schleife, um paginierte Ergebnisse abzurufen, bis die JQL-Abfrage abgeschlossen ist:
def fetch_jira_issues(base_url, project_id, e-mail, api_key):
url = f"{base_url}/relaxation/api/3/search"# Calculate the date 8 days in the past
eight_days_ago = (datetime.now() - timedelta(days=8)).strftime("%Y-%m-%d")
# Create JQL
jql = f"mission = {project_id} AND created >= '{eight_days_ago}' ORDER BY created DESC"
# Move into params of request.
params = {
"jql": jql,
"startAt": 0
}
all_issues = ()
auth = HTTPBasicAuth(e-mail, api_key)
headers = {"Settle for": "utility/json"}
whereas True:
response = requests.get(url, headers=headers, params=params, auth=auth)
if response.status_code != 200:
increase Exception(f"Didn't fetch points for mission {project_id}: {response.textual content}")
information = json.hundreds(response.textual content)
points = information('points')
all_issues.lengthen(points)
if len(all_issues) >= information('complete'):
break
params('startAt') = len(all_issues)
return all_issues
Anschließend wird eine Zeichenfolgendarstellung einer CSV erstellt und in S3 hochgeladen:
def upload_to_s3(csv_string, bucket, key):
attempt:
s3_client.put_object(
Bucket=bucket,
Key=key,
Physique=csv_string,
ContentType='textual content/csv'
)
besides Exception as e:
increase Exception(f"Didn't add CSV to S3: {str(e)}")
Klebeauftrag
Ein S3-Ereignis im Präfix /unprocessed löst ein zweites Lambda aus, das einen AWS Glue-Auftrag startet. Dies ist nützlich, wenn es mehrere Einstiegspunkte gibt, über die Jira-Tickets in das System gelangen können. Zum Beispiel, wenn Sie eine Auffüllung durchführen möchten.
import boto3 # Initialize Boto3 Glue shopper
glue_client = boto3.shopper('glue')
def handler(occasion, context):
# Print occasion for debugging
print(f"Acquired occasion: {json.dumps(occasion)}")
# Get bucket identify and object key (file identify) from the S3 occasion
attempt:
s3_event = occasion('Data')(0)('s3')
s3_bucket = s3_event('bucket')('identify')
s3_key = s3_event('object')('key')
besides KeyError as e:
print(f"Error parsing S3 occasion: {str(e)}")
increase
response = glue_client.start_job_run(
JobName=glue_job_name,
Arguments={
'--S3_BUCKET': s3_bucket,
'--NEW_CSV_FILE': s3_key
}
)
Der Glue-Job selbst ist in PySpark geschrieben und im Code-Repo zu finden Hier. Die wichtige Erkenntnis ist, dass es Folgendes tut: linksanti Treten Sie bei, indem Sie die Drawback-IDs der Elemente in der neuen CSV-Datei mit allen IDs in den /staged-CSVs vergleichen.
Die Ergebnisse werden dann an die weitergeleitet /inszeniert Präfix.
Jira-Tickets klassifizieren:
Hier wird es interessant. Wie sich herausstellt, kann der Einsatz von Immediate Engineering eine gleichwertige, wenn nicht sogar bessere Leistung erbringen als ein Textklassifizierungsmodell, das einige Techniken verwendet.
- Sie können die Klassifizierungen und ihre Beschreibungen in einer Eingabeaufforderung definieren.
- Bitten Sie das Modell, Schritt für Schritt zu denken (Gedankenkette).
- Und dann die Klassifizierung ausgeben, ohne ein einziges Modell trainieren zu müssen. Sehen Sie sich die Eingabeaufforderung unten an:
Notiz: Es ist wichtig, Ihre Eingabeaufforderung mithilfe einer von Menschen kuratierten Teilmenge klassifizierter/beschrifteter Tickets zu validieren. Sie sollten diese Eingabeaufforderung über den Validierungsdatensatz ausführen, um sicherzustellen, dass sie mit der erwarteten Klassifizierung der Tickets übereinstimmt
SYSTEM_PROMPT = '''
You're a help ticket assistant. You're given fields of a Jira ticket and your activity is to categorise the ticket primarily based on these fieldsUnder is the record of potential classifications together with descriptions of these classifications.
<classifications>
ACCESS_PERMISSIONS_REQUEST: Used when somebody does not have the write permissions or cannot log in to one thing or they cannot get the right IAM credentials to make a service work.
BUG_FIXING: Used when one thing is failing or a bug is discovered. Usually instances the descriptions embody logs or technical data.
CREATING_UPDATING_OR_DEPRECATING_DOCUMENTATION: Used when documentation is old-fashioned. Often references documentation within the textual content.
MINOR_REQUEST: That is not often used. Often a bug repair however it's very minor. If it appears even remotely difficult use BUG_FIXING.
SUPPORT_TROUBLESHOOTING: Used when asking for help for some engineering occasion. May appear to be an automatic ticket.
NEW_FEATURE_WORK: Often describes a brand new characteristic ask or one thing that is not operational.
</classifications>
The fields accessible and their descriptions are beneath.
<fields>
Summmary: It is a abstract or title of the ticket
Description: The outline of the difficulty in pure language. Nearly all of context wanted to categorise the textual content will come from this discipline
</fields>
<guidelines>
* It's attainable that some fields could also be empty through which case ignore them when classifying the ticket
* Assume by your reasoning earlier than making the classification and place your thought course of in <pondering></pondering> tags. That is your house to suppose and purpose in regards to the ticket classificaiton.
* Upon getting completed pondering, classify the ticket utilizing ONLY the classifications listed above and place it in <reply></reply> tags.
</guidelines>'''
USER_PROMPT = '''
Utilizing solely the ticket fields beneath:
<summary_field>
{abstract}
</summary_field>
<description_field>
{description}
</description_field>
Classify the ticket utilizing ONLY 1 of the classifications listed within the system immediate. Keep in mind to suppose step-by-step earlier than classifying the ticket and place your ideas in <pondering></pondering> tags.
If you find yourself completed pondering, classify the ticket and place your reply in <reply></reply> tags. ONLY place the classifaction within the reply tags. Nothing else.
'''
Wir haben eine Hilfsklasse hinzugefügt, die die Aufrufe an Bedrock weiterleitet, um die Sache zu beschleunigen:
import boto3
from concurrent.futures import ThreadPoolExecutor, as_completed
import re
from typing import Record, 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'<pondering>(.*?)</pondering>'
CORRECTNESS_PATTERN = r'<reply>(.*?)</reply>'
def __init__(self):
self.bedrock = boto3.shopper('bedrock-runtime')
def classify_tickets(self, tickets: Record(Dict(str, str))) -> Record(Dict(str, str)):
prompts = (self._create_chat_payload