」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 與 Jira 和 LLM 的互動專案報告

與 Jira 和 LLM 的互動專案報告

發佈於2024-11-08
瀏覽:443

Interactive project report with Jira and LLM

For all projects I worked on, I used some sort of project management system where project scope was defined as a list of tasks (tickets), and progress was reported by changing task statuses.

While such project management systems offer various dashboards and reports, interpreting a long list of tasks with cryptic titles created by engineers is not trivial. To provide transparency for project sponsors and customers, I had to manually create a project report and then answer related questions.

What if instead we asked LLM for help? The idea is simple: fetch all project tasks and feed them to LLM asking for the report. Then start a chat allowing for further questions.

Collecting data

We will use Jira for this experiment as it's a popular tool with easy-to-use REST API. The example project, for which we'll create the report, is very technical - it's about creating a build script that can detect what the code uses and generate the required instructions for the build system. Such a project will surely have technical tasks with cryptic titles.

Let's start with fetching tasks. The project in the example setup is represented as a single parent ticket (an epic) with a list of child tickets (tasks). For each task, we will fetch the full history to see how the ticket status changes over time. Using Python's Jira client the implementation is simple. Note in Jira nomenclature the term issue is used instead of ticket which is reflected in the 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

Since fetching all tickets with history takes a while, it is handy to store this data locally for further experiments. While playing with the implementation I used the below functions to save tasks to, and load them from a file:

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]

Preparing data

The next step is to prepare data for LLM. Raw Jira data in JSON format is quite verbose, we don't need all these extra fields. Let's extract basic information: subject, description, type, status, and creation date. From history, we will only extract ticket status changes along with their date and author, ignoring changes to other fields.

All this information will be stored as plain text. I have seen people using JSON or XML as LLM input, but my observation is LLMs are very good at interpreting plain text data. Plus with this approach, I don't need to worry about formatting text fields to be JSON or XML compatible. The only processing I do is to strip empty lines from the description, and the primary reason is to make it easier for me to look at results.

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

The epic I use for this experiment has 30 tasks that have between 1 and 15 status changes in their history. I will not quote the full output of the describe_issues function, but to give you an idea of how it looks here is a short excerpt:

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

Prompting

The prompt we'll use consists of two parts. First, it gives an instruction to create a report, detailing what specific information we want in the report. Then we insert ticket information prepared in previous paragraphs. LLMs tend to give verbose responses, so we ask specifically to not add any additional explanation. As the experiment is performed in the terminal, we'll also ask to make the response terminal-freindly.

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}
    """

The LLM I use for this experiment is Anthropic's Claude 3.5 Sonnet. In addition to sending the prompt message, we'll also specify identity. Identity could be just added as another line at the beginning of the prompt, but since the plan is to follow up with questions (create an interactive chat), Claude's documentation recommends setting identity as a separate system parameter in such a scenario.

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)

Let's put this together and run:

if __name__=="__main__":
    issues = fetch_issues('TKT-642')
    descrption = describe_issues(issues)
    prompt = create_prompt(description)
    report = generate_report(prompt)
    print(report)

Below is the full report I received from Claude:

Executive Report: Universal Build Script Project (TKT-642)

Project Progress:

  • 11 out of 24 tasks are completed (45.8%)
  • 5 tasks are in progress or review (20.8%)
  • 8 tasks remain to be started (33.3%)

Key Accomplishments:

  1. Created prototype jinja template for builder
  2. Implemented config extension and validation
  3. Added unit tests execution step
  4. Integrated support for sonar scanning
  5. Created image for building config and tech scripts

In Progress:

  • Adding test reporting for e2e tests
  • Implementing filtering logic for environments and Sonar detection
  • Developing developer documentation

Problematic Areas:

  1. Task TKT-734 (Add config extension) went through multiple status changes, indicating potential issues or complexity.
  2. Task TKT-697 (Add support for sonar scanning) has been reopened multiple times, suggesting implementation challenges.
  3. 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:

  1. Review the implementation of config extension and sonar scanning to address recurring issues.
  2. Prioritize the completion of developer documentation to support team adoption.
  3. Begin planning for the integration of the build system into remaining services.
  4. 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.

Asking questions

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:

  1. John Smith
  2. Jane Doe
  3. 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: Done

The 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: Done

This 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 Progress

This 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:

  1. Integration Delays:
    Several tasks for integrating the build system into various services (localization, reporting, shopping cart, authentication, database API) are still in "To Do" status. This poses a risk of delays in widespread adoption of the universal build script across different services. Failure to integrate across all intended services could limit the project's overall impact and value.

  2. Documentation Lag:
    The task for creating developer documentation (TKT-715) has only recently moved to "In Progress" after being in "To Do" for an extended period. Additionally, the user documentation task (TKT-714) is still in "To Do" status. This lag in documentation creation risks hindering the adoption, proper use, and maintenance of the new build system. It could lead to misuse, inefficiencies, or resistance to adoption due to lack of clear guidance.

  3. Complexity in Implementation:
    Several tasks, particularly those related to Sonar scanning (TKT-697) and config extension (TKT-734), have shown multiple status changes between "In Progress" and "Review". This pattern suggests potential technical challenges or uncertainties in implementation. There's a risk that these complexities could lead to bugs, performance issues, or maintenance difficulties in the future if not properly resolved.

These risks could potentially impact the project's timeline, adoption rate, and overall success if not properly managed and mitigated.

That was surprisingly good. I do like the fact the LLM is using generic knowledge in addition to prompt data, so it was able to answer questions about the "static code analysis tool" even though no ticket data mentions those terms. The answers are a bit too verbose, more experiments with the prompt are required - probably appending instructions to each user's query would help shape better answers.
Issues such as miscounting tickets should be easy to solve, we can calculate base statistics and include them in the prompt itself.

版本聲明 本文轉載於:https://dev.to/michal1024/interactive-project-report-with-jira-and-llm-jfa?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何在 Java 中將 Long 值轉換為位元組數組並返回?
    如何在 Java 中將 Long 值轉換為位元組數組並返回?
    在Java 中將Long 轉換為位元組數組並傳回在Java 中,將long 基本資料型別轉換為位元組數組(byte[] ),反之亦然是各種操作的常見任務,例如透過TCP 連線發送資料。以下是實現此轉換的全面解決方案:Long 到 Byte Arraypublic byte[] longToBytes...
    程式設計 發佈於2024-11-08
  • 如何使用 JavaScript 從 iFrame 重新導向父視窗?
    如何使用 JavaScript 從 iFrame 重新導向父視窗?
    從iFrame 重定向父視窗如果父視窗中嵌入了iFrame,則可能需要重定向父視窗視窗的位置更改為新的URL。為了實現這一點,JavaScript 提供了一個簡單的解決方案。 使用JavaScript 重新導向父視窗在iFrame 的JavaScript 程式碼中,您可以使用以下方法: 重定向最頂層...
    程式設計 發佈於2024-11-08
  • 如何在 Python 中以空格分割字串,同時保留引用的子字串?
    如何在 Python 中以空格分割字串,同時保留引用的子字串?
    在Python 中按空格分割字串同時保留帶引號的子字串處理同時包含空格和帶引號的子字符當字串的字串時,可能會遇到困難準確地分割字串,同時保持引用部分的完整性。在 Python 中,shlex 模組為此特定場景提供了解決方案。 使用shlex.split() 保留引號shlex.split() 函數可...
    程式設計 發佈於2024-11-08
  • 如何使用 Selenium 在 Google Chrome 模擬 Microsoft Edge Mobile?
    如何使用 Selenium 在 Google Chrome 模擬 Microsoft Edge Mobile?
    使用Selenium 更改Google Chrome 中的用戶代理在Selenium 自動化腳本中,為瀏覽器視窗設定特定的用戶代理對於模擬設備行為和確保網站渲染至關重要正如預期的那樣。在這種情況下,我們的目標是將 Google Chrome 中的使用者代理程式修改為 Microsoft Edge M...
    程式設計 發佈於2024-11-08
  • NPM 對等依賴關係深入:全面介紹
    NPM 對等依賴關係深入:全面介紹
    作為 Javascript 開發者,我們都知道專案中存在兩種不同的依賴關係,dependency 和 devDependency,但是peerDependency 又如何呢? 在本系列中,我們將研究 Javascript 中這種較不常見的依賴關係。我們將研究它們是什麼,作為圖書館使用者我需要了解什...
    程式設計 發佈於2024-11-08
  • 哪種 MySQL 取得函數適合您的 PHP 應用程式:`mysql_fetch_array`、`mysql_fetch_assoc` 和 `mysql_fetch_object` 的比較
    哪種 MySQL 取得函數適合您的 PHP 應用程式:`mysql_fetch_array`、`mysql_fetch_assoc` 和 `mysql_fetch_object` 的比較
    比較mysql_fetch_array、mysql_fetch_assoc 和mysql_fetch_object:綜合分析mysql 函數系列在從MySQL 函式中擷取結果起至關重要的查詢作用在PHP 中。在這些函數中,mysql_fetch_array、mysql_fetch_assoc 和 m...
    程式設計 發佈於2024-11-08
  • Lerna – Monorepo 管理的關鍵
    Lerna – Monorepo 管理的關鍵
    歡迎回到莫諾雷波城堡! 現在城堡已經建成,每個房間(項目)都已就位。但如果沒有正確的管理,事情可能會變得混亂。誰來幫助城堡順利運作?這時勒納登場了──一位強大的巫師,擁有神奇的命令,可以讓一切保持秩序。 Lerna 是您在 monorepo 土地上的嚮導,確保所有房間(項目)同步,所有包都鏈接,...
    程式設計 發佈於2024-11-08
  • 如何在 PHP 中循環嵌套數組並顯示特定值?
    如何在 PHP 中循環嵌套數組並顯示特定值?
    PHP foreach 與嵌套數組:綜合指南在 PHP 中,瀏覽嵌套數組可能是常見的挑戰。本討論重點討論特定場景,您的目標是顯示嵌套數組的子集,特別是第二個嵌套數組中的值。 將foreach 與嵌套數組結合使用要使用foreach 處理嵌套數組,您可以使用以下命令方法:]範例:$tmpArray =...
    程式設計 發佈於2024-11-08
  • 提升 Web 效能:前端開發人員指南
    提升 Web 效能:前端開發人員指南
    大家好!自从我上次写博客以来已经有一段时间了,我承认,这让我有点难过。现实是,有太多东西需要学习,有时感觉永远没有足够的时间来深入了解所有内容。我在跟谁开玩笑呢?事实是,我最近拖延得很厉害。 但最近,我一直在探索网络性能——这对于任何前端开发人员来说都是一个至关重要的话题——我很高兴分享我所学到的东...
    程式設計 發佈於2024-11-08
  • 如何利用先進的加密技術增強資料保護?
    如何利用先進的加密技術增強資料保護?
    對稱金鑰加密:FernetPython 擁有強大的加密庫,提供Fernet,這是一種安全、最佳實踐的加密方案。 Fernet 採用 AES CBC 加密、HMAC 簽章以及版本和時間戳記資訊來保護資料。建議使用 Fernet.generate_key() 產生金鑰。 from cryptograph...
    程式設計 發佈於2024-11-08
  • 什麼是本地主機?本地主機作為開發人員的用途
    什麼是本地主機?本地主機作為開發人員的用途
    您有沒有想過當開發人員在將網站上線之前測試網站時會發生什麼?或者網路管理員如何檢查他們的系統是否正常運作?答案在於一個強大但經常被誤解的概念,稱為 localhost。讓我們深入了解 localhost 是什麼、為何它很重要以及它如何變得非常有用。 什麼是本地主機? 用最簡單的術語...
    程式設計 發佈於2024-11-08
  • 為什麼 Debian 和 Ubuntu Docker 容器之間的標準輸出緩衝不同?
    為什麼 Debian 和 Ubuntu Docker 容器之間的標準輸出緩衝不同?
    Docker 容器中的標準輸出緩衝:Debian 與Ubuntu 的案例在Docker 容器中執行程式碼時,標準輸出緩衝可能會發生在某些情況下,但在其他情況下則不然。在使用 io.MultiWriter 將 stdout 定向到控制台和日誌檔案的情況下會出現此問題。 根本原因:平台差異根本原因造成此...
    程式設計 發佈於2024-11-08
  • 語意HTML
    語意HTML
    語意 HTML 是 HTML 的一部分,可協助您以維護和 SEO 友善的方式組織您的網站。 SEO 代表:搜尋引擎優化。 當您在建立網站時遵循 HTML 語意時,網站往往會更容易被搜尋引擎排名更高,當然也能讓螢幕閱讀器更輕鬆地瀏覽您的網站。 以下是一些語意 HTML 標籤: 1- 「標題」標籤...
    程式設計 發佈於2024-11-08
  • 如何在單一 FastAPI 端點中處理表單和 JSON 資料?
    如何在單一 FastAPI 端點中處理表單和 JSON 資料?
    如何建立可以接受表單或 JSON 正文的 FastAPI 端點? 在 FastAPI 中,您可以建立可以接受表單或 JSON 正文的端點使用不同的方法。以下是幾個選項:選項1:使用依賴函數此選項涉及建立一個依賴函數,該函數檢查Content-Type 請求標頭的值並使用Starlette 的方法解析...
    程式設計 發佈於2024-11-08
  • 如何在 Golang 中合併多個映射,同時避免重複值?
    如何在 Golang 中合併多個映射,同時避免重複值?
    在Golang 中合併映射問題:合併多個映射,保留與跨映射中的相同鍵關聯的值。 初始方法:提供的程式碼嘗試透過迭代每個映射來合併映射,根據匹配鍵將值新增至結果映射。但是,此方法不處理結果映射中的重複值。 簡單合併:可以透過將值附加到切片來實現修改後的合併函數來處理重複項與結果映射中的鍵關聯。 fu...
    程式設計 發佈於2024-11-08

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3