人工智能学习

打造自定义AI智维助手:基于Jira的自动化任务处理系统

本文主要是介绍打造自定义AI智维助手:基于Jira的自动化任务处理系统,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
我是如何通过使用Google Mesop、Django、LangChain Agents,CO-STAR和链式思维(CoT)提示,结合Jira API来更好地自动化Jira的

照片由 Google DeepMind 拍摄,来自 Unsplash

这个项目的灵感来自于在我的一个为内部用户群体开发的Web应用上集成一个Jira工单创建工具。我还实现了系统出错时自动创建Jira工单的功能。

用户和系统错误常常会提交类似的工单,所以我想要看看大型语言模型的推理能力是否可以用来自动分类和处理工单,将相关的问题关联起来,生成用户故事、验收标准,并确定优先级,以便自动分类工单,关联相关问题,生成用户故事、验收标准,并确定优先级。

此外,让用户和产品/管理方面的相关方能够更轻松地用自然语言直接与Jira交流,而不需要任何技术知识,这听起来是一个有趣的想法。

Jira 在软件开发领域已经变得非常普遍,并且现在已经成为项目管理领域的佼佼者。

具体地说,在大型语言模型(LLM)和代理研究方面的进展表明在这一领域有可能取得显著的生产效率提升。

与Jira相关的任务非常适合被自动化处理,因为这些任务都是文本形式的,高度重复性,风险和复杂性相对较低。

在这篇文章中,我将介绍我的开源项目——AI Jira助理:一个通过AI代理与Jira交互的聊天界面,并有一个自定义的AI代理工具,帮助处理新创建的Jira工单。

所有代码均可在文章末尾的GitHub仓库中访问。

该项目利用通过Google的Mesop服务提供的LangChain代理,并且服务可以在Docker中本地运行。

提示策略包括一个CO-STAR系统提示语,以及少量样本的链式思维(CoT)推理。

本文将包括以下几个部分。

  1. 定义
  2. Mesop 接口:Streamlit 还是 Mesop?
  3. Django REST 框架
  4. 自定义 LangChain 代理工具和提示语
  5. Jira API 示例
  6. 下一步行动
1. 定义:

首先,我想讲解一下这个项目中一些重要的定义,这些定义对项目来说至关重要。

AI Jira小助手

这里的开源项目在本地运行起来看起来如下。

包括一个让用户输入提示信息的聊天界面,一些示例提示用于预先填充界面,一个显示模型回复的框和一个清除回复的按钮。

项目中涉及的主要技术难题及其关键代码片段进行了详细的讨论。

Jira的AI助手

Google Mesop是什么?

Mesop 是一个相对较新的 Python Web 框架,用于快速开发 Google 的 AI 应用(2023)。

“Mesop 提供了 30 种多样化的组件,从低级构建块到高级 AI 组件应有尽有。这种灵活性让您可以快速为 ML 应用程序创建原型或构建自定义 UI,所有这些都可以在一个灵活的框架内完成,该框架适应您的项目需求。” — Mesop 官网

什么是AI代理人?

代理软件范式的起源可以追溯到英文单词“Agency”,指的是能够观察环境并采取行动的软件程序。

“人工智能(AI)代理是一种软件程序,可以与环境互动,收集数据,并利用这些数据执行任务,以实现预定目标。

人类设定目标,而AI代理独立选择最合适的行动来达成这些目标。

AI代理是理性的代理。它们基于感知和数据做出理性决策,以达到最佳表现和结果。

AI代理通过物理或软件界面来感知环境。” — AWS 网站

你知道什么是CO-STAR提示功能吗?

这是关于提示格式的指南,以下为推荐的标题:上下文、目标、风格、语调、受众和响应。这被认为可以提高LLM(大型语言模型)的输出质量。

“CO-STAR框架是一个由新加坡政府科技局的数据科学与人工智能团队创造的工具,是一个实用模板,用于结构化提示。

它考虑了影响大规模语言模型响应的有效性和相关性的所有关键因素,从而产生更优的回应。” — Sheila Teo的Medium文章 (https://medium.com/how-i-won-singapores-gpt-4-prompt-engineering-competition-34c195a93d41)

什么是链式思维(CoT)引导?

最初由 Google 在一篇论文中提出;Wei 等人 (2022)。Chain-of-Thought (CoT) 提示法是指提供少量的中间推理步骤示例。这种方法已被证明能提升模型的常识推理能力。

Django是什么?

Django 是一个广泛使用的 Python 框架,它功能更加强大。

“Django 是一个高级的 Python 网络框架,它鼓励快速开发和干净、实用的设计。它是免费且开源的。” ——Django 官方网站

什么是 LangChain?

LangChain 是一个比较受欢迎的开源库,支持大型语言模型应用程序,比如代理和与此项目相关的提示。

“LangChain 灵活的抽象概念和以 AI 为中心的工具包使其成为开发人员在构建使用 GenAI 应用时的首选平台。
加入 100 多万开发者,使用 LangChain 的 Python 和 JavaScript 框架来标准化他们的大型语言模型应用开发。” — LangChain 官网

2. Mesop接口:Streamlit或者Mesop?

照片由 Konsepta Studio 拍摄,来自 Unsplash

我经常使用Streamlit来部署生成式人工智能应用,下面是一个我的工作的例子:请点击这里。

总体来说,Streamlit 是一个开源的Python网页框架,它与其他框架类似。

有关Streamlit的更多内容,请查看我另一篇详细长篇讨论Streamlit的Medium文章。

AI 资助申请写手 — AutoGen, PostgreSQL RAG, LangChain, FastAPI 和 Streamlit写资助申请可能既耗时又繁琐,但生成式 AI 看起来像是一个自然的解决方案,不过有点天真……medium.com

这是第一次真正把Mesop用起来的机会,于是我认为比较一下会很有帮助。

Mesop 设计用于对组件的 CSS 样式提供更精细的控制,并且原生集成了 JS 网页注释功能。Mesop 还提供了本地运行时的一些有用调试工具。从经验来看,它的多页应用功能也更容易使用。

然而,但这确实意味着对于那些不太熟悉CSS样式的机器学习者(比如我自己)来说,进入的门槛更高。Streamlit也拥有更大的支持社区。

从这段代码中,我们可以设置不同的页面路由。这个项目只有两个页面。一个为主页面,另一个是出现错误时显示的页面。

    导入 mesop 作为 me

    # 局部导入
    try:
        from .utils import ui_components
    except Exception:
        from utils import ui_components

    @me.page(path="/")
    def page(security_policy=me.SecurityPolicy(dangerously_disable_trusted_types=True)):
        with me.box(
            style=me.Style(
                背景="#fff",
                最小高度="calc(100% - 48px)",
                内边距=me.Padding(底部=16),
            )
        ):
            with me.box(
                style=me.Style(
                    宽度="min(800px, 100%)",
                    外边距=me.Margin.对称(水平="auto"),
                    内边距=me.Padding.对称(
                        水平=16,
                    ),
                )
            ):
                ui_components.header_text()
                ui_components.example_row()
                ui_components.chat_input()
                ui_components.output()
                ui_components.clear_output()
        ui_components.footer()

    @me.page(path="/error")
    def error(security_policy=me.SecurityPolicy(dangerously_disable_trusted_types=True)):
        with me.box(
            style=me.Style(
                背景="#fff",
                最小高度="calc(100% - 48px)",
                内边距=me.Padding(底部=16),
            )
        ):
            with me.box(
                style=me.Style(
                    宽度="min(720px, 100%)",
                    外边距=me.Margin.对称(水平="auto"),
                    内边距=me.Padding.对称(
                        水平=16,
                    ),
                )
            ):
                ui_components.header_text()
                ui_components.render_error_page()
        ui_components.footer()

错误页面有一个按钮,可以返回首页。

这是作者提供的AI Jira助手错误页面的图像

这里包含用来启动跳转到主页的代码。

    def navigate_home(event: me.ClickEvent):  
        me.navigate("/")  

    def render_error_page():  
        is_mobile = me.viewport_size().width < 640  
        with me.box(  
            style=me.Style(  
                position="sticky",  
                width="100%",  
                display="block",  
                height="100%",  
                font_size=50,  
                text_align="center",  
                flex_direction="column" if is_mobile else "row",  
                gap=10,  
                margin=me.Margin(bottom=30),  
            )  
        ):  
            me.text(  
                "出错了",  
                style=me.Style(  
                    text_align="center",  
                    font_size=30,  
                    font_weight=700,  
                    padding=me.Padding.all(8),  
                    background="white",  
                    justify_content="center",  
                    display="flex",  
                    width="100%",  
                ),  
            )  
            me.button(  
                "导航回首页",   
                type="flat",  
                on_click=navigate_home  
            )

我们还需要创建 State 类,这样数据就能在事件循环中保持持久。

    import mesop as me

    @me.stateclass
    # 下面的代码定义了一个状态类
    class State:
        输入: str  # 一个字符串输入
        输出: str  # 一个字符串输出
        处理中: bool  # 操作是否正在进行中

为了清除界面里的模型输出结果,可以将输出变量设为空字符串。还有不同的按钮类型,目前包括默认、凸起样式、扁平样式和描边样式。

    def clear_output():  
        with me.box(style=me.Style(margin=me.Margin.all(15))):  
            with me.box(style=me.Style(display="flex", flex_direction="row", gap=12)):  
                me.button("清空输出", type="flat", on_click=delete_state_helper)  

    def delete_state_helper(ClickEvent):  # 清除状态助手,将输出设置为空字符串
        config.State.output = ""

为了自动填充聊天界面,我们会利用按钮的 onclick 事件来更新状态,从而自动填充聊天界面。

    def example_row():
        # 判断是否为移动端
        is_mobile = me.viewport_size().width < 640
        # 创建一个 me.box
        with me.box(
            style=me.Style(
                display="flex",
                flex_direction="column" if is_mobile else "row",  # 根据是否为移动端设置 flex 方向
                gap=10,
                margin=me.Margin(bottom=40),
            )
        ):
            # 遍历配置中的示例提示
            for example in config.EXAMPLE_PROMPTS:
                prompt_box(example, is_mobile)

    def prompt_box(example: str, is_mobile: bool):
        # 创建一个 me.box
        with me.box(
            style=me.Style(
                width="100%" if is_mobile else 200,  # 根据是否为移动端设置宽度
                height=250,
                text_align="center",
                background="#F0F4F9",
                内边距=me.Padding.all(16),  # 设置内边距
                font_weight=500,
                line_height="1.5",
                border_radius=16,
                cursor="pointer",
            ),
            key=example,
            on_click=click_prompt_box,
        ):
            me.text(example)

    def click_prompt_box(e: me.ClickEvent):
        # 设置配置状态输入为点击事件的键值
        config.State.input = e.key

同样,为了将请求发送到 Django 服务,我们使用了如下代码片段。我们使用了 海象运算符 (:=) 来检查请求是否返回了有效的响应(状态码为200),并将结果添加到状态中,以便在UI中呈现。如果未成功,我们将用户重定向到错误页面,如前所述。

    def chat_input():  
        with me.box(  
            style=me.Style(  
                padding=me.Padding.all(8),  
                background="white",  
                display="flex",  
                width="100%",  
                border=me.Border.all(me.BorderSide(width=0, style="solid", color="black")),  
                border_radius=12,  
                box_shadow="0 10px 20px #0000000a, 0 2px 6px #0000000a, 0 0 1px #0000000a",  
            )  
        ):  
            with me.box(  
                style=me.Style(  
                    flex_grow=1,  
                )  
            ):  
                me.native_textarea(  
                    value=config.State.input,  
                    autosize=True,  
                    min_rows=4,  
                    placeholder="输入您的提示语",  
                    style=me.Style(  
                        padding=me.Padding(top=16, left=16),  
                        background="white",  
                        outline="none",  
                        width="100%",  
                        overflow_y="auto",  
                        border=me.Border.all(  
                            me.BorderSide(style="none"),  
                        ),  
                    ),  
                    on_blur=textarea_on_blur,  
                )  
            with me.content_button(type="icon", on_click=click_send):  
                me.icon("send")  

    def click_send(e: me.ClickEvent):  
        if not config.State.input:  
            return  
        config.State.in_progress = True  
        input = config.State.input  
        config.State.input = ""  
        yield  

        if result := api_utils.call_jira_agent(input):  
            config.State.output += result  
        else:  
            me.navigate("/error")  

        config.State.in_progress = False  
        yield  

    def textarea_on_blur(e: me.InputBlurEvent):  
        config.State.input = e.value

为了确保完整性,我提供了请求代码片段以调用Django端点来运行AI Jira代理程序。

    import requests  

    ## 本地导入  
    from . import config  

    def call_jira_agent(request):  
        try:  
            data = {"request": request}  
            if (response := requests.post(f"{config.DJANGO_URL}api/jira-agent/", data=data)) and \  
            (response.status_code == 200) and \  
            (output := response.json().get("output")):  
                return f"请求:{request}<br>输出:{output}<br><br>"  
        except Exception as e:  
            print(f"调用call_jira_agent时出错: {e}")

为了本地运行的需要,我已经包含了相关的Docker和Docker-compose文件,这样可以在本地运行。

这是从Mesop项目主页获取的运行Mesop的Docker文件。

Docker Compose 文件包括三个服务,一个是 Django 后端应用,一个是 Mesop 前端应用,以及一个配合 Django 应用程序的 PostgreSQL 数据库。

我想引用传递给Mesop Docker容器的环境变量,设置PYTHONUNBUFFERED=1可以确保Python的输出、stdout和stderr流被直接发送到终端。使用了推荐的Mesop应用的Docker镜像后,我花了些时间才找到没有看到任何应用输出的原因。

环境变量 DOCKER_RUNNING=true 用来简单判断应用程序是否在 Docker 或其他环境中运行。

值得注意的是,环境变量将通过“config.ini”配置文件来设置,该文件位于由env_file元素引用的配置子目录中的“config.ini”文件里。

要运行这个项目,你需要用你的 Open AI 和 Jira 账户信息填入此配置文件。

3. Django REST框架

照片由Faisal在Unsplash上分享。

Django 是一个 Python Web 框架,自带许多有用的功能。

它与框架类似,比如Flask或FastAPI,不过需要一些额外的设置,而且学习曲线会比较陡峭,开始使用时会有些挑战。

想了解更多关于Flask的内容,请看我下面的文章。

利用Catboost机器学习进行运河船只定价策略,来自YouTube推荐引擎的一个有趣建议 medium.com

在这篇文章中,我将介绍应用、模型、序列化工具、视图函数以及 PostgreSQL 数据库的整合。

一个应用就是一个逻辑上分离的网络应用,有特定的用途。

在这个例子中,我们将应用命名为“api”,并通过运行以下命令来创建。

    django-admin startapp api

下面的命令用于创建一个新的名为api的应用程序: django-admin startapp api

在 views.py 文件里,我们定义了 API 的端点。

“视图函数,简称视图,是一个Python函数,它接收一个Web请求并返回一个Web响应。响应可以是HTML内容、重定向、404错误、XML文档、图像……或者其他任何内容。视图本身包含了返回该响应所需的各种逻辑。” —— Django官网

在应用的urls.py文件中定义了端点路由到Django视图。该urls.py文件在应用初始化时会被创建。在这个项目中,我们有三个端点;一个健康检查端点,一个用于返回数据库中所有记录的端点,以及一个用于处理调用AI代理的端点。

这些视图被声明为类,这是Django中的标准做法。请完整查看该文件。

虽然大部分代码都很直观,但这个代码片段特别重要,因为它会把模型的数据存到数据库里。

    modelRequest = models.ModelRequest(request=request, response=response)  # 创建一个模型请求对象,并保存
    modelRequest.save()

下面的代码片段会从 ModelRequest 模型返回数据库中 ModelRequest 模型的所有记录,接下来我会讲到模型。

    class GetRecords(APIView):  
        def get(self, request):  
            """获取请求记录"""  
            data = models.ModelRequest.objects.all().values()  
            return Response({'records': data})

“模型是关于你数据的唯一且确定的信息来源。它包含了你在存储的数据中的关键字段和行为。每个模型通常对应一个数据库表。” — Django 官方网站

我们的这个项目的模型很简单,我们只需存储用户的请求和模型的最终输出,两者均为文本形式。

__str__ 方法是 Python 中常用的约定,例如,print 函数会默认调用它。该方法的目的是返回对象的易读字符串表示。

序列化器将模型中的字段映射到验证输入和输出的有效性,并同时将更复杂的数据类型转换为 Python 中的数据类型。如先前的 views.py 所示,可以从中看出这一点。

“ModelSerializer 通常指的是 Django REST 框架 (DRF) 中的一个组件。Django REST 框架是一个流行的工具包,用于在 Django 应用程序中构建 Web API 接口。它提供了一组工具和库来简化构建 API 的过程,包括序列化器。

ModelSerializer 类提供了一种快捷方式,可以根据模型字段自动生成具有对应字段的序列化器类。

ModelSerializer 类继承自普通的 Serializer 类,除了以下几点:

它将自动生成一组与模型对应的字段。

它会自动生成序列化器的相关验证器,例如 unique_together 验证器。

它包含了 .create() 和 .update() 的简单默认实现方法。” — Geeks for geeks

项目的 serializers.py 文件内容如下:

在集成 PostgreSQL 数据库时,settings.py 文件中的配置信息需要与 database.ini 文件中的设置保持一致。

为了与 Django 默认使用的数据库不同,您需要将默认数据库设置改为指向 PostgreSQL 数据库。

DATABASES = {  
    'default': {  
        'ENGINE': 'django.db.backends.postgresql_psycopg2',  # 数据库引擎类型
        'NAME': 'vectordb',  # 数据库名称
        'USER': 'testuser',  # 数据库用户名
        'PASSWORD': 'testpwd',  # 数据库用户密码
        'HOST': 'db' if DOCKER_RUNNING else '127.0.0.1',  # 如果DOCKER_RUNNING为真,则使用'db'作为主机地址,否则使用本地回环地址127.0.0.1
        'PORT': '5432',  # 数据库端口号
    }  
}

database.ini 文件定义了 PostgreSQL 数据库在初始化过程中的配置。

为了确保在运行 Docker 容器之后数据库迁移只被执行一次,我们可以使用一个 bash 脚本来执行迁移,然后启动服务器。自动执行迁移可确保每次 Django 源码定义发生变化时,数据库都会自动更新。这从长远来看能节省不少时间。

将Dockerfile的入口改为指向使用CMD指令的bash脚本,如下所示。

4. 自定义 LangChain 代理工具和提示语

这张照片由 Vlad Tchompalov 在 Unsplash 上拍的

我正在利用现有的LangChain代理功能结合来Jira工具包,该工具包基于Atlassian的Python API封装而成。

默认库非常实用,不过有时需要在提示上多做一些尝试和错误的摸索,但我相信随着研究进展,这种情况应该会逐渐改善。

然而,对于这个项目来说,我想为这个代理程序添加一些自定义工具。下面带有@tool装饰器的“函数”‘triage’就是这样的一个例子。

工具的函数类型提示和注释说明对于向助手传达在调用时的期望是必要的。函数返回的字符串会被助手看到,在这种情形下,我们简单返回“任务完成”,这样助手就会停止执行下一步操作。

自定义分诊工具进行以下步骤。

  • 获取项目中所有未解决的 Jira 问题
  • 获取代理正在处理的 Jira 问题的描述和摘要
  • 异步地使用 LLM 进行文本对比,标记与当前问题相关的所有未解决的问题,然后使用 Jira API 链接相关问题
  • 使用 LLM 生成用户故事、验收标准并确定优先级,并将生成的结果作为评论添加到主要问题中
from langchain.agents import AgentType, initialize_agent  
from langchain_community.agent_toolkits.jira.toolkit import JiraToolkit  
from langchain_community.utilities.jira import JiraAPIWrapper  
from langchain_openai import OpenAI  
from langchain.tools import tool  
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate  

llm = OpenAI(temperature=0)  

@tool  
def triage(ticket_number: str) -> None:  
    """对给定的工单进行分类,并链接相关的工单"""  
    ticket_number = str(ticket_number)  
    all_tickets = jira_utils.get_all_tickets()  
    primary_issue_key, primary_issue_data = jira_utils.get_ticket_data(ticket_number)  
    find_related_tickets(primary_issue_key, primary_issue_data, all_tickets)  
    user_stories_acceptance_criteria_priority(primary_issue_key, primary_issue_data)  
    return "任务完成"  

jira = JiraAPIWrapper()  
toolkit = JiraToolkit.from_jira_api_wrapper(jira)  
# 初始化agent,使用JiraToolkit和自定义的triage函数
agent = initialize_agent(  
    toolkit.get_tools() + [triage],   
    llm,   
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,   
    verbose=True,   
    max_iterations=5,  
    return_intermediate_steps=True  
)

这两个 LLM 任务都使用了 CO-STAR 系统提示词和少量样本的链式思考策略。因此,我将这些任务抽象成一个 LLMTask 类型。

它们在下面的代码片段中实现。我们也可以尝试为每个任务使用不同的大模型,但考虑到时间有限,我还没有进行这方面的实验——如果您拉取了代码库并有什么经验想要分享,请在下面留言。

    class LLMTask:  
        def __init__(self, system_prompt, examples, llm):  
            self.system_prompt = system_prompt  
            self.examples = examples  
            self.llm = llm  

        定义构建提示(self):  
            example_prompt = ChatPromptTemplate.from_messages(  
                [  
                    ("人类", "{input}"),  
                    ("AI", "{output}"),  
                ]  
            )         
            few_shot_prompt = FewShotChatMessagePromptTemplate(  
                example_prompt=example_prompt,  
                examples=self.examples,  
            )         
            返回 ChatPromptTemplate.from_messages(  
                [  
                    ("system", self.system_prompt),  
                    few_shot_prompt,  
                    ("人类", "{input}"),  
                ]  
            )  

        定义运行LLM(self, input):  
            chain = self.construct_prompt() | self.llm   
            返回 chain.invoke({"input": input})  

    产品模型 = LLMTask(系统提示.get("system_prompt_product"), 示例.get("examples_product"), llm)  
    链接模型 = LLMTask(系统提示.get("system_prompt_linking"), 示例.get("examples_linking"), llm)

针对链接任务,CO-STAR系统的提示内容如下。上下文、目标、风格、语气、受众及回应是CO-STAR方法的标准标题。我们定义了上下文及输出,包括对每个模型结果元素的标记。

明确受众、风格和语气的要求有助于使模型生成的内容更适合商务场合。

    # 上下文 #
    对新创建的Jira工单进行分类,通过将它们与以前的工单进行比较。

    # 目标 #
    判断两个工单是否相关,依据是它们描述的任务是否相似,并在<related>标签中返回True。请在<thought>标签中解释你的想法。

    # 风格 #
    保持推理简明扼要且逻辑清晰。

    # 语气 #
    语气应为信息性。

    # 目标受众 #
    目标受众包括业务负责人、产品负责人和软件工程师。

    # 回应 #
    如果你认为两个工单相关,请在<related>标签中返回True,并在<thought>标签中解释为什么你认为这两个工单是相关的。

为了执行产品风格的评估(包括用户故事、验收标准和优先级),系统将提供以下提示。我们明确将优先级分为低、中、高三个等级。

我们还要求模型应该体现出产品负责人/经理的风格,这种任务以往通常是他们来做的。

    # 上下文 #
    你是一位在大型软件公司工作的产品负责人,需要根据用户提交的工单中的描述(<description>标签内)处理新的工单。

    # 目标 #
    根据<description>标签中的描述,你需要写出以下几个部分:用户故事(<user_stories>标签内),验收标准(<acceptance_criteria>标签内)以及优先级(<priority>标签内)。优先级必须是LOW,MEDIUM或HIGH,根据你对工单描述的判断来确定最合适的优先级。同时,请在<thought>标签内包括你对优先级的思考。

    # 风格 #
    应该以产品负责人的风格来写。

    # 语气 #
    使用专业且商务化的语气。

    # 受众 #
    受众是业务相关方、产品相关方和软件工程师。

    # 响应格式 #
    用户故事(在<user_stories>标签内)。
    验收标准(在<acceptance_criteria>标签内)。
    优先级(在<priority>标签内)。

我现在将提供一个Chain-of-thought少量样本提示,用于链接Jira问题。我们将问题摘要和描述分别用<issue1>和<issue2>标签包含起来。模型的思考过程将在输出的<thought>标签中被捕获,这构成了Chain-of-Thought部分。

少量样本训练名称的由来在于将少量训练样本输入模型。

The <related> 标签包含对提供的两个工单是否相关的判定,如果模型认为这两个是相关的,那么返回值为 True

我们后来用正则表达式解析模型输出,并有一个辅助功能通过Jira API把这些相关的工单链接起来,这个项目中所有与Jira API相关的帮助功能文章后面会提到。

        "examples_linking": [  
            {  
                "input": "<issue1>在网站前端增加Jira集成票务创建小部件<issue1><issue2>为前端添加一个Jira小部件 为前端添加一个集成,允许用户手动创建Jira票务<issue2>",  
                "output": "<related>True<related><thought>两个票务都涉及Jira创建小部件,因此它们是重复票务。<thought>"  
            },  
            {  
                "input": "<issue1>前端拼写错误 首页的拼写错误,应该改为'Welcome to the homepage' 而不是 'Wellcome to the homepage'<issue1><issue2>延迟问题 存在一个延迟问题,对Open AI的调用应该异步处理<issue2>",  
                "output": "<related>False<related><thought>第一个票务有关拼写错误,第二个票务有关延迟问题,因此它们不相关。<thought>"  
            },  
            {  
                "input": "<issue1>更新架构,包括添加用于模型请求和响应的两个新字段<issue1><issue2>更新架构,包括添加用于模型请求和响应的两个新字段<issue2>",  
                "output": "<related>True<related><thought>两个票务都涉及架构更新,并添加用于模型请求和响应的两个新字段,因此它们相关。<thought>"  
            }  
        ]

同样,对于任务评估,用户故事用<user_stories>标签包裹,验收准则用<acceptance_criteria>标签包裹,优先级用<priority>标签包裹。<thought>标签也用于捕获模型给出优先级的具体原因。

所有的例子都是我亲自手动注释的。

        "examples_product": [  
            {  
                "input": "<description>添加 Jira 集成以创建工单 在网站前端添加 Jira 创建工单的小部件<description>",  
                "output": "<user_stories>作为用户,我希望从网站上直接创建 Jira 工单。\n作为产品负责人,我希望简化我们团队创建 Jira 工单的流程。<user_stories>\n<acceptance_criteria>该 Jira 创建小部件应该在网站前端易于查找并使用。\n该小部件应允许用户输入所有必要的工单信息,如标题、描述和指派人。\n一旦提交,小部件应在指定的项目中创建一个新的 Jira 工单。\n该小部件应具有适当的错误处理机制,以防创建工单时出现问题。<acceptance_criteria>\n<priority>MEDIUM<priority>\n<thought>通过在网站上添加 Jira 集成和创建工单的小部件,我们可以提高我们团队创建 Jira 工单的效率和准确性。这将最终节省业务和开发团队的时间和资源。目前暂时没有迫切的需求,但我认为此工单应该被优先定为中等优先级。<thought>"  
            },  
            {  
                "input": "<description>紧急数据库模式更新 我们需要添加一个列来记录模型请求和响应以符合合规性要求<description>",  
                "output": "<user_stories>作为开发人员,我希望能够查看和跟踪模型请求和响应以符合合规性要求。\n作为产品负责人,我希望确保我们的系统符合法规和标准。<user_stories>\n<acceptance_criteria>在数据库模式中添加一个新列来记录模型请求和响应。\n该列应该准确记录并存储所有相关的合规性信息。\n该列应该对授权用户易于访问和查看。\n更新不应干扰现有系统的任何功能。<acceptance_criteria>\n<priority>HIGH<priority>\n<thought>此工单被标记为紧急,表明它对业务来说是高优先级。合规性是我们业务的关键方面,及时解决这个问题很重要。通过添加一个新列来记录模型请求和响应,我们可以确保我们的系统符合要求并避免任何潜在的法律或监管问题。因此,我认为此工单应该被优先定为高优先级。<thought>"  
            },  
            {  
                "input": "<description>主页 CSS 错误 主页上的 CSS 错误影响了一个行动号召按钮并降低了转化率<description>",  
                "output": "<user_stories>作为用户,我希望能够修复主页上的 CSS 错误,以便我可以轻松访问行动号召按钮。\n作为产品负责人,我希望通过修复任何 CSS 错误来提高我们网站的转化率。<user_stories>\n<acceptance_criteria>应该识别并修复主页上的 CSS 错误。\n行动号召按钮应该对用户来说是可见且易于访问的。\n修复不应影响网站的其他功能。<acceptance_criteria>\n<priority>HIGH<priority>\n<thought>此 CSS 错误直接影响了我们网站的转化率,这是业务的关键指标。尽快解决此问题以改善用户体验并提高转化率非常重要。<thought>"  
            }  
        ],

这段代码使用了多线程方式来并发链接Jira问题。这将显著减少对项目中所有开放工单进行成对比较所需的时间,以确定它们是否相关。

    def check_issue_and_link_helper(args):  
        # 检查问题并辅助链接问题
        key, data, primary_issue_key, primary_issue_data = args  
        # 如果key不等于primary_issue_key,并且llm_check_ticket_match函数返回True,则进行链接操作
        if key != primary_issue_key and llm_check_ticket_match(primary_issue_data, data):  
            jira_utils.link_jira_issue(primary_issue_key, key)   

    def find_related_tickets(primary_issue_key, primary_issue_data, issues):  
        # 查找相关的问题
        args = [(key, data, primary_issue_key, primary_issue_data) for key, data in issues.items()]  
        # 使用线程池执行器来并行处理问题链接任务
        with concurrent.futures.ThreadPoolExecutor(os.cpu_count()) as executor:  
            # 将问题处理函数应用到每个参数集
            executor.map(check_issue_and_link_helper, args)  

    def llm_check_ticket_match(ticket1, ticket2):  
        # 检查两个ticket是否匹配
        llm_result = linking_model.run_llm(f"<ticket1>{ticket1}<ticket1><ticket2>{ticket2}<ticket2>")  
        # 解析llm结果并检查是否为True
        if ((result := jira_utils.extract_tag_helper(llm_result))) and (result == 'True'):  
            return True 

这里展示了一个工具的工作流程示例,包括创建问题单并进行优先级排序。

工单的创建和分类(图片来源:作者)

这些行动的结果记录在Jira工单中。相关工单已自动关联,用户故事、验收标准和优先级,以及思考过程,都已作为Jira评论记录为。

关联的工单、用户案例、验收条件和优先顺序(图片由作者提供)

我们可以在Docker容器的日志信息中看到代理的中间过程。

分诊步骤的代理人输出(图片由作者提供)

5. Jira API 示例代码

AI Jira助理发现正在进行中的工单(图源作者)

在这个项目中,我明确地使用了Jira REST API的所有例子均已列在下方,以便更加清晰。

正则表达式提取帮助函数,用于解析模型结果,也被包含。虽然有一个 Jira 的 Python SDK,但我选择了 requests 库,使得更容易转换成其他编程语言。

6. 下一步要做什么

自然而然的下一步是通过与源代码控制系统集成来包含代码生成功能,这样可以实现接近全自动化的软件开发生命周期。在加入人类干预的情况下,这样的方案就变得可行了。

我们已经可以看到这种趋势正在显现,AI驱动的代码生成正在对企业产生影响——如果常规任务可以部分自动化,那么软件开发者和产品经理就可以专注于更有趣和有意义的任务。

如果这篇文章很受欢迎,也许我可以考虑把它作为接下来的项目来做。

AI 现在编写 Google 超过 25% 的代码谷歌 CEO 桑达尔·皮查伊表示,该公司正在使用 AI 编写代码,这些代码随后会由工程师进行审查。uk.pcmag.com

我希望如我所承诺的,这篇文章对你有启发作用。你可以在这里找到所有代码:在Github上here,也欢迎你通过LinkedIn与我联系。

参考文献
  • https://google.github.io/mesop/getting-started/quickstart/#starter-kit (Google Mesop 项目快速入门指南)
  • https://www.django-rest-framework.org/#example (Django REST 框架示例页面)
  • https://blog.logrocket.com/dockerizing-django-app/ (使用 Docker 包装 Django 应用教程)

*文中所有图片,除非另有注明,均为作者拍摄。

这篇关于打造自定义AI智维助手:基于Jira的自动化任务处理系统的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!