Pour tous les projets sur lesquels j'ai travaillé, j'ai utilisé une sorte de système de gestion de projet dans lequel la portée du projet était définie comme une liste de tâches (tickets) et les progrès étaient signalés en changeant le statut des tâches.
Bien que de tels systèmes de gestion de projet proposent divers tableaux de bord et rapports, interpréter une longue liste de tâches aux titres énigmatiques créés par les ingénieurs n'est pas anodin. Pour assurer la transparence pour les sponsors du projet et les clients, j'ai dû créer manuellement un rapport de projet, puis répondre aux questions associées.
Et si nous demandions plutôt de l'aide à LLM ? L'idée est simple : récupérer toutes les tâches du projet et les transmettre à LLM en demandant le rapport. Ensuite, démarrez une discussion permettant de poser d'autres questions.
Nous utiliserons Jira pour cette expérience car il s'agit d'un outil populaire doté d'une API REST facile à utiliser. L'exemple de projet, pour lequel nous allons créer le rapport, est très technique : il s'agit de créer un script de build capable de détecter ce que le code utilise et de générer les instructions requises pour le système de build. Un tel projet comportera sûrement des tâches techniques aux titres énigmatiques.
Commençons par récupérer les tâches. Le projet dans l'exemple de configuration est représenté comme un ticket parent unique (une épopée) avec une liste de tickets enfants (tâches). Pour chaque tâche, nous récupérerons l'historique complet pour voir comment le statut du ticket évolue au fil du temps. En utilisant le client Jira de Python, la mise en œuvre est simple. Notez que dans la nomenclature Jira, le terme problème est utilisé à la place de ticket qui se reflète dans le code.
jira = JIRA(server=os.environ["JIRA_SERVER"], basic_auth=(os.environ["JIRA_USER"], os.environ["JIRA_TOKEN"])) def fetch_issues(epic_key): issues = [] print("Loading epic data...", end="", flush=True) issues.append(jira.issue(epic_key)) print("done") print("Loading tasks...", end="", flush=True) child_issues = jira.search_issues(f"parent = {epic_key}") for issue in child_issues: issues.append(jira.issue(issue.key, expand="changelog")) print("done") return issues
Comme la récupération de tous les tickets avec historique prend un certain temps, il est pratique de stocker ces données localement pour d'autres expériences. En jouant avec l'implémentation, j'ai utilisé les fonctions ci-dessous pour enregistrer des tâches et les charger à partir d'un fichier :
def save_issues(filename, issues): with open(filename, "x") as file: file.write("[") file.write(",".join( json.dumps(issue.raw) for issue in issues)) file.write("]") def load_issues(filename): with open(filename, "r") as file: data = json.load(file) return [Issue(jira._options, jira._session, raw=raw_issue) for raw_issue in data]
L'étape suivante consiste à préparer les données pour le LLM. Les données brutes Jira au format JSON sont assez détaillées, nous n'avons pas besoin de tous ces champs supplémentaires. Extrayons les informations de base : sujet, description, type, statut et date de création. De l'historique, nous extrairons uniquement les changements de statut des tickets ainsi que leur date et leur auteur, en ignorant les changements apportés aux autres champs.
Toutes ces informations seront stockées sous forme de texte brut. J'ai vu des gens utiliser JSON ou XML comme entrée LLM, mais mon observation est que les LLM sont très bons pour interpréter les données en texte brut. De plus, avec cette approche, je n'ai pas à me soucier du formatage des champs de texte pour qu'ils soient compatibles JSON ou XML. Le seul traitement que j'effectue est de supprimer les lignes vides de la description, et la raison principale est de me permettre de consulter plus facilement les résultats.
def strip_empty_lines(s): return "".join(line for line in (s or "").splitlines() if line.strip()) def issue_to_str(issue): return f""" {issue.fields.issuetype}: {issue.key} Summary: {issue.fields.summary} Description: {strip_empty_lines(issue.fields.description)} Type: {issue.fields.issuetype} Status: {issue.fields.status} Created: {issue.fields.created} Priority: {issue.fields.priority} """ def changelog_to_str(changelog, changeitem): return f""" Author: {changelog.author.displayName} Date: {changelog.created} Status change from: {changeitem.fromString} to: {changeitem.toString} """ def history_to_str(issue): if issue.changelog is None or issue.changelog.total == 0: return "" history_description = "" for changelog in issue.changelog.histories: try: statuschange = next(filter(lambda i: i.field == "status", changelog.items)) history_description += changelog_to_str(changelog, statuschange) except StopIteration: pass return history_description #this function assumes the first issue is an epic followed by tasks. def describe_issues(issues): description = "Project details:" description += issue_to_str(issues[0]) description += "\nProject tasks:" for issue in issues[1:]: description += "\n" + issue_to_str(issue) description += f"History of changes for task {issue.key}:" description += history_to_str(issue) return description
L'épopée que j'utilise pour cette expérience comporte 30 tâches qui ont entre 1 et 15 changements de statut dans leur historique. Je ne citerai pas le résultat complet de la fonctionscribe_issues, mais pour vous donner une idée de son apparence, voici un court extrait :
Project details: Epic: TKT-642 Summary: Create universal build script Description: Type: Epic Status: In Development Created: 2024-05-24T10:48:33.050+0200 Priority: P4 - Low Project tasks: Task: TKT-805 Summary: add test reporting for e2e tests Description: Type: Task Status: In Progress Created: 2024-09-06T09:56:33.919+0200 Priority: P4 - Low History of changes for task TKT-805: Author: Jane Doe Date: 2024-09-06T10:04:15.325+0200 Status change from: To Do to: In Progress Task: TKT-801 Summary: Sonar detection Description: * add sonar config file detection * Type: Task Status: In Progress Created: 2024-08-30T13:57:44.364+0200 Priority: P4 - Low History of changes for task TKT-801: Author: Jane Doe Date: 2024-08-30T13:57:58.450+0200 Status change from: To Do to: In Progress Task: TKT-799 Summary: Add check_tests step Description: Type: Task Status: Review Created: 2024-08-29T18:33:52.268+0200 Priority: P4 - Low History of changes for task TKT-799: Author: Jane Doe Date: 2024-08-29T18:40:35.305+0200 Status change from: In Progress to: Review Author: Jane Doe Date: 2024-08-29T18:33:57.095+0200 Status change from: To Do to: In Progress
L'invite que nous utiliserons se compose de deux parties. Tout d'abord, il donne des instructions pour créer un rapport, détaillant les informations spécifiques que nous souhaitons contenir dans le rapport. Ensuite, nous insérons les informations sur le ticket préparées dans les paragraphes précédents. Les LLM ont tendance à donner des réponses verbeuses, nous demandons donc spécifiquement de ne pas ajouter d'explications supplémentaires. Comme l'expérience est réalisée dans le terminal, nous demanderons également de rendre la réponse conviviale pour le terminal.
def create_prompt(isses_description): return f""" Using below information from jira generate executive report that focuses on what was done and what is remaining. Provide project progress information. Point out problematic tasks and what may be wrong. Use status change history to point out tasks that were worked on for too long or have been reopened many times. Check if you can detect other problems from tasks or their history. Provide only text content that can be displayed on terminal, without any additional explanation or markdown. {isses_description} """
Le LLM que j'utilise pour cette expérience est le Sonnet Claude 3.5 d'Anthropic. En plus d'envoyer le message d'invite, nous spécifierons également l'identité. L'identité pourrait être simplement ajoutée sous la forme d'une autre ligne au début de l'invite, mais comme le plan est de donner suite aux questions (créer une discussion interactive), la documentation de Claude recommande de définir l'identité comme paramètre système distinct dans un tel scénario.
def send_messages(messages): client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_KEY"]) identity = "You are a seasoned IT program manager. You have very good communication skills, your answers are concise and focus on key insights." message = client.messages.create( system=identity, max_tokens=2048, messages=messages, model="claude-3-5-sonnet-20240620" ) return message.content[0].text def generate_report(prompt): messages=[ { "role": "user", "content": prompt }] return send_messages(messages)
Mêlons cela ensemble et exécutons :
if __name__=="__main__": issues = fetch_issues('TKT-642') descrption = describe_issues(issues) prompt = create_prompt(description) report = generate_report(prompt) print(report)
Vous trouverez ci-dessous le rapport complet que j'ai reçu de Claude :
Rapport exécutif : Projet Universal Build Script (TKT-642)
Avancée du projet :
- 11 tâches sur 24 sont réalisées (45,8%)
- 5 tâches sont en cours ou en révision (20,8%)
- 8 tâches restent à démarrer (33,3%)
Réalisations clés :
- Création d'un modèle de prototype jinja pour le constructeur
- Extension de configuration et validation implémentées
- Ajout de l'étape d'exécution des tests unitaires
- Prise en charge intégrée du balayage sonar
- Image créée pour la création de scripts de configuration et techniques
En cours :
- Ajout de rapports de tests pour les tests e2e
- Mise en œuvre d'une logique de filtrage pour les environnements et la détection Sonar
- Développement de la documentation développeur
Domaines problématiques :
- Task TKT-734 (Add config extension) went through multiple status changes, indicating potential issues or complexity.
- Task TKT-697 (Add support for sonar scanning) has been reopened multiple times, suggesting implementation challenges.
- Task TKT-715 (Create developer documentation) has been in progress for an extended period without completion.
Upcoming Work:
- Integration of the build system into various services (authentication, database-api, shopping-cart, reporting, localization)
- Adding support for monorepos and custom jobs
- Creating user documentation
Recommendations:
- Review the implementation of config extension and sonar scanning to address recurring issues.
- Prioritize the completion of developer documentation to support team adoption.
- Begin planning for the integration of the build system into remaining services.
- Consider reassessing the priority of tasks that have remained in the "To Do" state for an extended period.
Overall, the project is making steady progress, with nearly half of the tasks completed. However, attention should be given to resolving recurring issues and completing in-progress tasks to maintain momentum.
The result is quite good. It did miscount tickets - in various runs I did it gave me a different number, but never a correct number of 30. Well, LLMs are known for not being able to count r's in the word strawberry. But the summary is correct, recommendations are also reasonable.
Now let's create an interactive session and ask some questions. We'll start with a pre-defined question which is the original prompt we used to generate the report. Then we will read the user's input and attach it to messages. Each response, along with the report is also recorded in the messages chain.
def interactive_session(prompt): messages = [] user_input = prompt while user_input != "exit": messages.append({ "role" : "user", "content" : user_input }) response = send_messages(messages) print(response) messages.append({ "role" : "assistant", "content" : response }) user_input = input("QUESTION: ") return messages
To run this, just replace generate_report in the main section of the script with interactive_session. Below is a recording of an example session I did. I omitted from the quote the first response which just prints out the report.
QUESTION: how many engineers the team counts?
Based on the information provided in the task histories, I can identify at least three distinct engineers working on this project:
- John Smith
- Jane Doe
- Mike Carpenter
These three names appear frequently in the status change histories for various tasks. However, it's important to note that this may not represent the full team size. There could be other team members who haven't made changes recorded in these specific task histories, or who are involved in the project in other capacities not reflected in this data.
To get an accurate count of the team size, you would need additional information beyond what's provided in these task histories.
QUESTION: is unit test execution task done yet?
Yes, the unit test execution task is completed. Specifically, task TKT-700 with the summary "Add unit tests execution step" is marked as Done. Here's the relevant information:
Task: TKT-700
Summary: Add unit tests execution step
Status: DoneThe history of changes for this task shows that it was moved to "Done" status on 2024-08-02, after going through the typical workflow stages (To Do -> In Progress -> Review -> PREPROD VERIFICATION -> READY FOR PRODUCTION -> Done).
QUESTION: does the scope include running some sort of static code analysis tool?
Yes, the project scope includes running static code analysis. Specifically, there's a task for implementing Sonar scanning, which is a popular static code analysis tool. Here are the details:
Task: TKT-697
Summary: Add support for sonar scanning
Status: DoneThis task was completed and moved to "Done" status on 2024-09-02. The implementation of Sonar scanning indicates that static code analysis is part of the universal build script project.
Additionally, there's another related task that's currently in progress:
Task: TKT-801
Summary: Sonar detection
Status: In ProgressThis task mentions "sonar detection" in its summary, which further confirms that Sonar (a static analysis tool) is being integrated into the build process.
These tasks demonstrate that static code analysis, particularly using Sonar, is included in the project scope and has been largely implemented.
QUESTION: what are the 3 biggest risks of the project?
Based on the provided information, the three biggest risks for this project appear to be:
Retards d'intégration :
Plusieurs tâches d'intégration du système de build dans divers services (localisation, reporting, panier d'achat, authentification, API de base de données) sont toujours au statut "To Do". Cela pose un risque de retards dans l’adoption généralisée du script de build universel dans différents services. L’incapacité à intégrer tous les services prévus pourrait limiter l’impact et la valeur globale du projet.Décalage de la documentation :
La tâche de création de la documentation du développeur (TKT-715) n'a été déplacée que récemment vers « En cours » après avoir été en « À faire » pendant une période prolongée. De plus, la tâche de documentation utilisateur (TKT-714) est toujours à l'état « À faire ». Ce retard dans la création de la documentation risque d'entraver l'adoption, l'utilisation appropriée et la maintenance du nouveau système de build. Cela pourrait conduire à une mauvaise utilisation, à des inefficacités ou à une résistance à l’adoption en raison du manque de directives claires.Complexité de mise en œuvre :
Plusieurs tâches, notamment celles liées au balayage sonar (TKT-697) et à l'extension de configuration (TKT-734), ont montré plusieurs changements de statut entre « En cours » et « Révision ». Cette tendance suggère des défis techniques potentiels ou des incertitudes dans la mise en œuvre. Il existe un risque que ces complexités entraînent des bugs, des problèmes de performances ou des difficultés de maintenance à l'avenir si elles ne sont pas correctement résolues.Ces risques pourraient potentiellement avoir un impact sur le calendrier du projet, le taux d'adoption et le succès global s'ils ne sont pas correctement gérés et atténués.
C'était étonnamment bon. J'aime le fait que le LLM utilise des connaissances génériques en plus des données d'invite, ce qui lui permet de répondre aux questions sur « l'outil d'analyse de code statique » même si aucune donnée de ticket ne mentionne ces termes. Les réponses sont un peu trop verbeuses, d'autres expériences avec l'invite sont nécessaires - probablement ajouter des instructions à la requête de chaque utilisateur aiderait à façonner de meilleures réponses.
Les problèmes tels que les erreurs de comptage devraient être faciles à résoudre, nous pouvons calculer des statistiques de base et les inclure dans l'invite elle-même.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!