使用 OpenAI 和 MLflow 构建代码助手

概述

欢迎来到这个全面的教程,在这里你将开始一段有趣的旅程,探索如何将OpenAI强大的语言模型与MLflow集成,我们将构建一个真正有用的工具,只需在我们声明的任何函数中添加一个装饰器,就可以在交互式环境中立即获得正在开发的代码的反馈。

Download this Notebook

学习目标

在本教程结束时,您将:

  1. 掌握 OpenAI 的 GPT-4 用于代码辅助:了解如何利用 OpenAI 的 GPT-4 模型提供实时编码辅助。学习如何利用其能力生成代码建议、解释并提高整体编码效率。

  2. 利用 MLflow 进行增强的模型跟踪:深入了解 MLflow 强大的跟踪系统,以管理机器学习实验。学习如何从 MLflow 内部调整 pyfunc 模型,以控制从交互式编码环境中显示的 LLM 输出。

  3. 无缝结合 OpenAI 和 MLflow:探索将 OpenAI 的 AI 能力与 MLflow 的跟踪和管理系统集成的实际步骤。这种集成展示了如何结合这些工具可以简化智能应用程序的开发和部署。

  4. 开发和部署一个自定义的Python代码助手:通过使用OpenAI的模型,获得创建基于Python的代码助手的实践经验。然后,在Jupyter Notebook环境中实际看到它的应用,为开发过程提供有用的帮助。

  5. 通过AI驱动的洞察力提升代码质量:应用AI驱动的分析来审查和增强您的代码。了解AI助手如何提供关于代码质量的实时反馈,提出改进建议,并帮助保持高编码标准。

  6. 探索用于稳健开发的Python高级特性: 理解Python的高级特性,如装饰器和函数式编程。这些对于构建高效、可扩展和可维护的软件解决方案至关重要,尤其是在集成AI能力时。

涵盖的关键概念

  1. MLflow 的模型管理:探索 MLflow 用于跟踪实验、将代码打包为可重复运行的功能,以及管理和部署模型的功能。

  2. 自定义 Python 模型: 学习如何使用 MLflow 的内置定制功能来定义一个通用的 Python 函数,该函数将允许您在接口 OpenAI 的同时,制定自己的处理逻辑,以执行对 LLM 输出的替代处理。

  3. Python 装饰器与函数式编程: 学习关于 Python 的高级概念,如装饰器和函数式编程,以实现高效的代码评估和增强。

为什么要为此使用 MLflow?

MLflow 在本教程中成为一个关键元素,使我们的用例不仅可行,而且非常高效。它提供了一个安全且无缝的接口,与 OpenAI 的高级语言模型配合使用。在本教程中,我们将探讨 MLflow 如何极大地简化了存储 OpenAI 特定教学提示的过程,并通过为返回的文本添加可读格式来增强用户体验。

MLflow 的灵活性和可扩展性使其成为与各种工具集成的强大选择,特别是在 Jupyter Notebooks 等交互式编码环境中。我们将亲眼见证 MLflow 如何促进快速实验和迭代,使我们能够以最小的努力创建一个功能性工具。这个工具不仅有助于开发,还将提升整体的编码和模型管理体验。通过利用 MLflow 的全面功能,我们将无缝地完成从设置复杂模型到高效执行复杂任务的端到端工作流程。

GPT-4 使用的重要成本考虑

GPT-4 的高成本

需要注意的是,使用 GPT-4 而不是 GPT-4o-mini 可能会产生更高的成本。GPT-4 的高级功能和增强的性能伴随着价格溢价,使其比 GPT-3.5 等早期模型更昂贵。

为什么在本教程中选择GPT-4

  • 增强能力:在本教程中,我们选择 GPT-4 主要是因为其卓越的能力,特别是在代码重构和检测代码实现中的问题等方面。

  • 演示目的: 这里使用GPT-4作为演示,展示了语言模型技术的尖端进展及其在复杂任务中的应用。

考虑成本效益的替代方案

对于成本是一个重要考虑因素的项目,或者在不需要 GPT-4 高级功能的情况下,可以考虑使用 GPT-4o-mini 或其他更具成本效益的替代方案。这些模型仍然为广泛的应用提供了强大的性能,但成本较低。

GPT-4 的预算

如果你选择继续使用 GPT-4,建议: - 密切监控使用情况:跟踪你的 API 使用情况以有效管理成本。 - 相应预算:分配足够的资源以覆盖与 GPT-4 相关的高成本。

通过注意这些成本考虑因素,您可以做出明智的决定,选择最适合您项目需求和预算的 OpenAI 模型。

[1]:
import warnings

# Disable a few less-than-useful UserWarnings from setuptools and pydantic
warnings.filterwarnings("ignore", category=UserWarning)
[2]:
import functools
import inspect
import os
import textwrap

import openai

import mlflow
from mlflow.models.signature import ModelSignature
from mlflow.pyfunc import PythonModel
from mlflow.types.schema import ColSpec, ParamSchema, ParamSpec, Schema

# Run a quick validation that we have an entry for the OPEN_API_KEY within environment variables
assert "OPENAI_API_KEY" in os.environ, "OPENAI_API_KEY environment variable must be set"

初始化 MLflow 客户端

根据您运行此笔记本的位置,您初始化 MLflow 客户端的配置可能会有所不同。如果您不确定如何配置和使用 MLflow 跟踪服务器或有哪些选项可用(最简单的是使用 Databricks Community Edition 内的免费托管服务),您可以查看 此处运行笔记本的指南 以获取更多关于设置跟踪服务器 URI 和配置访问托管或自托管 MLflow 跟踪服务器的信息。

设置 MLflow 实验

在本教程的这一部分中,我们使用 MLflow 的 set_experiment 函数来定义一个名为“代码助手”的实验。这一步骤在 MLflow 的工作流程中至关重要,原因如下:

  1. 唯一标识:一个独特且明确的实验名称,如“代码助手”,对于轻松识别和区分与该项目相关的运行至关重要,尤其是在同时处理多个项目或实验时。

  2. 简化跟踪:为实验命名可以轻松跟踪与其相关的所有运行和模型,保持模型开发、参数、指标和结果的清晰历史记录。

  3. MLflow UI 中的易访问性: 一个独特的实验名称确保了在 MLflow UI 中快速定位和访问我们的实验运行和模型,便于分析、比较不同的运行,以及分享发现。

  4. 促进更好的组织:随着项目的复杂性增加,拥有一个命名良好的实验有助于更好地组织和管理机器学习生命周期,使其更容易在实验的不同阶段之间导航。

使用像“Code Helper”这样的唯一实验名称,为高效的模型管理和跟踪奠定了基础,这是任何机器学习工作流程中的关键方面,特别是在动态和协作环境中。

[3]:
mlflow.set_experiment("Code Helper")
[3]:
<Experiment: artifact_location='file:///Users/benjamin.wilson/repos/mlflow-fork/mlflow/docs/source/llms/openai/notebooks/mlruns/703316263508654123', creation_time=1701891935339, experiment_id='703316263508654123', last_update_time=1701891935339, lifecycle_stage='active', name='Code Helper', tags={}>

定义AI模型的指令集

在本教程的这一部分,我们定义了一组特定的指令来指导我们AI模型的行为。这是通过 instruction 数组实现的,该数组概述了系统(AI模型)和用户之间的角色和预期交互。以下是其组成部分的分解:

  1. 系统角色:数组的第一个元素定义了AI模型作为‘系统’的角色。它将模型描述为一个‘有帮助的专家软件工程师’,其目的是协助代码分析并提供教育支持。AI模型应做到:

    • 提供代码意图的清晰解释。

    • 评估代码的正确性和可读性。

    • 在注重简洁性、可维护性和遵循最佳编码实践的同时,提出改进建议。

  2. 用户角色:第二个元素代表‘用户’角色。这一部分是用户(在这种情况下,是学习教程的人)通过提交代码以供审查与AI模型进行交互的地方。用户应做到:

    • 提供用于评估的代码片段。

    • 从AI模型寻求代码改进的反馈和建议。

这套指令集对于创建互动学习体验至关重要。它指导AI模型提供有针对性的、建设性的反馈,使其成为理解编码实践和提升编码技能的宝贵工具。

[4]:
instruction = [
    {
        "role": "system",
        "content": (
            "As an AI specializing in code review, your task is to analyze and critique the submitted code. For each code snippet, provide a detailed review that includes: "
            "1. Identification of any errors or bugs. "
            "2. Suggestions for optimizing code efficiency and structure. "
            "3. Recommendations for enhancing code readability and maintainability. "
            "4. Best practice advice relevant to the code’s language and functionality. "
            "Your feedback should help the user improve their coding skills and understand best practices in software development."
        ),
    },
    {"role": "user", "content": "Review my code and suggest improvements: {code}"},
]

在MLflow中定义和使用模型签名

在本教程的这一部分,我们为我们的 OpenAI 模型定义了一个 ModelSignature,这是在保存基础模型和后续在我们的自定义 Python 模型实现中的关键步骤。以下是该过程的概述:

  1. 模型签名定义:

    • 我们创建一个 ModelSignature 对象,该对象指定了我们模型的输入、输出和参数。

    • inputsoutputs 被定义为具有单个字符串列的模式,表明我们的模型将处理字符串类型的数据。

    • params 模式包含两个参数:max_tokenstemperature,每个参数都有默认值和定义的数据类型。

注意 这里我们明确地定义了模型签名,以供演示之用。如果你没有指定签名,模式将根据记录或保存模型时定义的 task 自动推断并设置。

  1. 记录基础OpenAI模型:

    • 使用 mlflow.openai.log_model ,我们记录了基础的 OpenAI 模型(gpt-4)以及我们之前定义的 instruction 集。

    • 我们定义的 signature 也在此步骤中传递,确保模型以正确的输入、输出和参数规格保存。

这种双重用途的签名至关重要,因为它确保了模型在基础形式和后来被封装在自定义Python模型中时处理数据的一致性。这种方法简化了工作流程,并在模型实现和部署的不同阶段保持了统一性。

[5]:
# Define the model signature that will be used for both the base model and the eventual custom pyfunc implementation later.
signature = ModelSignature(
    inputs=Schema([ColSpec(type="string", name=None)]),
    outputs=Schema([ColSpec(type="string", name=None)]),
    params=ParamSchema(
        [
            ParamSpec(name="max_tokens", default=500, dtype="long"),
            ParamSpec(name="temperature", default=0, dtype="float"),
        ]
    ),
)

# Log the base OpenAI model with the included instruction set (prompt)
with mlflow.start_run():
    model_info = mlflow.openai.log_model(
        model="gpt-4",
        task=openai.chat.completions,
        artifact_path="base_model",
        messages=instruction,
        signature=signature,
    )

我们在 MLflow UI 中记录的模型

在记录模型后,您可以打开 MLflow UI 并查看已记录的组件。请注意,我们的模型配置,包括模型类型(gpt-4)、端点 API 类型(任务)(chat.completions)和提示,都已被记录。

openai-ui

通过自定义 Pyfunc 实现增强用户体验

在本节中,我们介绍了一个自定义的Python模型,CodeHelper,它在Jupyter Notebook等交互式开发环境中与OpenAI模型交互时显著提升了用户体验。``CodeHelper``类旨在格式化OpenAI模型的输出,使其更具可读性和视觉吸引力,类似于聊天界面。以下是它的工作原理:

  1. 初始化和模型加载:

    • CodeHelper 类继承自 PythonModel

    • load_context 方法用于加载 OpenAI 模型,该模型保存为 self.model。此模型从 context.artifacts 加载,确保使用适当的模型进行预测。

  2. 响应格式化:

    • _format_response 方法对于增强输出格式至关重要。

    • 它处理响应中的每个项目,以不同的方式处理文本和代码块。

    • 代码块外的文本行会被自动换行至80个字符的宽度,以提高可读性。

    • 代码块内的行(由 ``` 标记)不会换行,保持代码结构不变。

    • 这种格式创建了一个类似于聊天界面的输出,使交互更加直观和用户友好。

  3. 进行预测

    • predict 方法是模型进行预测的地方。

    • 它调用加载的 OpenAI 模型以获取给定输入的原始响应。

    • 原始响应随后被传递给 _format_response 方法进行格式化。

    • 格式化的响应被返回,提供了一个清晰且易于阅读的输出。

通过实现这个自定义的 pyfunc,我们增强了用户与AI代码助手之间的交互。这不仅使输出更易于理解,而且还以类似于消息的熟悉格式呈现,这在交互式编码环境中尤其有益。

[6]:
# Custom pyfunc implementation that applies text and code formatting to the output results from the OpenAI model
class CodeHelper(PythonModel):
    def __init__(self):
        self.model = None

    def load_context(self, context):
        self.model = mlflow.pyfunc.load_model(context.artifacts["model_path"])

    @staticmethod
    def _format_response(response):
        formatted_output = ""
        in_code_block = False

        for item in response:
            lines = item.split("\n")
            for line in lines:
                # Check for the start/end of a code block
                if line.strip().startswith("```"):
                    in_code_block = not in_code_block
                    formatted_output += line + "\n"
                    continue

                if in_code_block:
                    # Don't wrap lines inside code blocks
                    formatted_output += line + "\n"
                else:
                    # Wrap lines outside of code blocks
                    wrapped_lines = textwrap.fill(line, width=80)
                    formatted_output += wrapped_lines + "\n"

        return formatted_output

    def predict(self, context, model_input, params):
        # Call the loaded OpenAI model instance to get the raw response
        raw_response = self.model.predict(model_input, params=params)

        # Return the formatted response so that it is easier to read
        return self._format_response(raw_response)

使用 MLflow 保存自定义 Python 模型

本教程的这一部分演示了如何使用 MLflow 保存自定义 Python 模型 CodeHelper。该过程涉及指定模型的位置和附加信息,以确保其正确存储并可在将来检索使用。以下是概述:

  1. 定义工件

    • 创建一个 artifacts 字典,其中键 "model_path" 指向基础 OpenAI 模型的位置。这一步很重要,它将我们的自定义模型与必要的基础模型文件链接起来。我们通过访问 log_model() 函数返回的 model_uri 属性,获取之前记录的 openai 模型的位置。

  2. 保存模型

    • mlflow.pyfunc.save_model 函数用于保存 CodeHelper 模型。

    • path: 指定模型将被保存的位置 (final_model_path)。

    • python_model: 提供了一个 CodeHelper 类的实例,指示要保存的模型。

    • input_example: 提供了一个示例输入 (["x = 1"]),这对于理解模型预期的输入格式非常有用。

    • signature: 传递了之前定义的 ModelSignature,确保模型处理数据的一致性。

    • artifacts: artifacts 字典用于将基础 OpenAI 模型与我们自定义的模型关联起来。

这一步对于将我们 CodeHelper 模型的全部功能封装成 MLflow 可以管理和跟踪的格式至关重要。它允许模型轻松部署和版本控制,便于在各种应用和环境中使用。

[7]:
# Define the location of the base model that we'll be using within our custom pyfunc implementation
artifacts = {"model_path": model_info.model_uri}

with mlflow.start_run():
    helper_model = mlflow.pyfunc.log_model(
        artifact_path="code_helper",
        python_model=CodeHelper(),
        input_example=["x = 1"],
        signature=signature,
        artifacts=artifacts,
    )

加载我们保存的自定义Python模型

在下一节中,我们加载刚刚保存的模型,以便我们可以使用它!

[8]:
loaded_helper = mlflow.pyfunc.load_model(helper_model.model_uri)

比较两种使用 MLflow 模型的代码审查方法

在本教程中,我们将探讨两种不同的方法来利用 MLflow 模型进行代码审查和提供反馈。这些方法提供了不同程度的复杂性和集成度,以适应不同的用例和偏好。

方法1:简单的 review 函数

我们的第一种方法是直接的 review 函数。这种方法侵入性较小,不会改变原始函数的行为。它非常适合在你想要手动触发对函数代码的审查,并且不需要查看函数的输出结果来理解LLM分析的上下文的场景。

  • 工作原理review 函数接受一个函数和一个 MLflow 模型作为参数。然后,它使用该模型来评估给定函数的源代码。

  • 手动调用: 你需要显式调用 review(my_func) 来审查 my_func。这种方法是手动的,不会自动与函数调用集成。

  • 简单性: 这种方法更简单和直接,适合一次性评估或不需要自动审查的使用场景。

方法2:高级 code_inspector 装饰器

第二种方法是高级装饰器 code_inspector,它通过自动审查函数并允许函数的评估执行来更深入地集成。这对于更复杂的函数非常有帮助,其中输出结果与代码助手提供的评估相结合,可以更深入地理解任何观察到的逻辑缺陷。

  • 自动评估: 当作为装饰器应用时,code_inspector 会在每次调用函数时自动评估函数的代码。

  • 错误处理:在评估过程中包含强大的错误处理机制。

  • 函数修改: 此方法修改了函数的行为,整合了一个自动审查流程。

review 函数的介绍

我们将从检查 review 函数开始。这个函数将在我们的 Jupyter 笔记本的下一个单元格中定义。以下是 review 函数功能的快速概述:

  • 输入: 它接受一个函数和一个 MLflow 模型作为输入。

  • 功能: 提取输入函数的源代码,并使用 MLflow 模型对其提供反馈。

  • 错误处理:增强了错误处理功能,以优雅地管理异常。

在下面的 Jupyter notebook 单元格中,你将看到 review 函数的实现,展示了其在评估代码时的简单性和有效性。


在探索了 review 功能之后,我们将深入研究更复杂的 code_inspector 装饰器,以理解其自动评估过程和错误处理机制。

[9]:
def review(func, model):
    """
    Function to review the source code of a given function using a specified MLflow model.

    Args:
    func (function): The function to review.
    model (MLflow pyfunc model): The MLflow pyfunc model used for evaluation.

    Returns:
    The model's prediction or an error message.
    """
    try:
        # Extracting the source code of the function
        source_code = inspect.getsource(func)

        # Using the model to predict/evaluate the source code
        prediction = model.predict([source_code])
        print(prediction)
    except Exception as e:
        # Handling any exceptions that occur and returning an error message
        return f"Error during model prediction or source code inspection: {e}"

process_data 函数的解释与回顾

功能概述

process_data 函数旨在通过识别唯一元素并计算重复项来处理列表。然而,其实现存在几个效率和可读性问题。

建议的修订代码

GPT-4 的分析输出提供了清晰简洁的反馈,正如提示所指示的那样。通过此应用程序的 MLflow 集成,使用该工具的简便性显而易见,使我们能够在开发过程中通过一个简单的函数调用获得高质量的指导。

[10]:
def process_data(lst):
    s = 0
    q = []
    for i in range(len(lst)):
        a = lst[i]
        for j in range(i + 1, len(lst)):
            b = lst[j]
            if a == b:
                s += 1
            else:
                q.append(b)
    rslt = [x for x in lst if x not in q]
    k = []
    for i in rslt:
        if i not in k:
            k.append(i)
    final_data = sorted(k, reverse=True)
    return final_data, s


review(process_data, loaded_helper)
Your code seems to be trying to find the count of duplicate elements in a list
and return a sorted list of unique elements in descending order along with the
count of duplicates. Here are some suggestions to improve your code:

1. **Errors or Bugs**: There are no syntax errors in your code, but the logic is
flawed. The variable `s` is supposed to count the number of duplicate elements,
but it only counts the number of times an element is equal to another element in
the list, which is not the same thing. Also, the way you're trying to get unique
elements is inefficient and can lead to incorrect results.

2. **Optimizing Code Efficiency and Structure**: You can use Python's built-in
`set` and `list` data structures to simplify your code and make it more
efficient. A `set` in Python is an unordered collection of unique elements. You
can convert your list to a set to remove duplicates, and then convert it back to
a list. The length of the original list minus the length of the list with
duplicates removed will give you the number of duplicate elements.

3. **Enhancing Code Readability and Maintainability**: Use meaningful variable
names to make your code easier to understand. Also, add comments to explain what
each part of your code does.

4. **Best Practice Advice**: It's a good practice to write a docstring at the
beginning of your function to explain what it does.

Here's a revised version of your code incorporating these suggestions:

```python
def process_data(lst):
    """
    This function takes a list as input, removes duplicate elements, sorts the remaining elements in descending order,
    and counts the number of duplicate elements in the original list.
    It returns a tuple containing the sorted list of unique elements and the count of duplicate elements.
    """
    # Convert the list to a set to remove duplicates, then convert it back to a list
    unique_elements = list(set(lst))

    # Sort the list of unique elements in descending order
    sorted_unique_elements = sorted(unique_elements, reverse=True)

    # Count the number of duplicate elements
    duplicate_count = len(lst) - len(unique_elements)

    return sorted_unique_elements, duplicate_count
```
This version of the code is simpler, more efficient, and easier to understand.
It also correctly counts the number of duplicate elements in the list.

code_inspector 装饰器函数

code_inspector 函数是一个 Python 装饰器,旨在使用 MLflow pyfunc 模型为函数增加自动代码审查功能。这个装饰器增强了函数的功能,允许它们在使用 MLflow pyfunc 模型进行代码质量和正确性自动审查时,从而丰富开发和学习体验。与上述 review() 函数的实现相比,这种方法将在调用函数时执行,增强与自动代码审查配对时的上下文信息。

[11]:
import functools
import inspect


def code_inspector(model):
    """
    Decorator for automatic code review using an MLflow pyfunc model.

    Args:
        model: The MLflow pyfunc model for code evaluation.
    """

    def decorator_check_my_function(func):
        # Decorator that wraps around the given function
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                # Extracting the source code of the decorated function
                parsed_func = inspect.getsource(func)

                # Using the MLflow model to evaluate the extracted source code
                response = model.predict([parsed_func])

                # Printing the response for code review feedback
                print(response)

            except Exception as e:
                # Handling exceptions during model prediction or source code extraction
                print("Error during model prediction or formatting:", e)

            # Executing and returning the original function's output
            return func(*args, **kwargs)

        return wrapper

    return decorator_check_my_function

首次使用试验:summing_functioncode_inspector

我们将 code_inspector 装饰器应用于一个名为 summing_function 的函数。该函数旨在计算给定范围内的和的和。以下是其功能以及 code_inspector 带来的增强的见解:

  1. 功能概述:

    • summing_function 计算从1到``n``的累积和。它通过迭代一个范围并在每一步中累加中间和来实现这一点。

    • 一个字典 intermediate_sums 用于存储这些和,然后将其汇总以找到最终的和。

  2. 使用 ``code_inspector``:

    • 该函数用 code_inspector(loaded_helper) 装饰。这意味着每次调用 summing_function 时,作为 loaded_helper 加载的 MLflow 模型都会分析其代码。

    • 装饰器提供代码的实时反馈,评估诸如质量、效率和最佳实践等方面。

  3. 教育效益:

    • 这种设置非常适合学习,允许用户在代码上获得即时、可操作的反馈。

    • 它提供了一种实用的方式来理解函数背后的逻辑,并学习代码优化和改进。

通过将 code_inspectorsumming_function 集成,本教程展示了一种增强编程技能的交互式方法,即时反馈有助于理解和改进。

在继续查看GPT-4的响应之前,你能识别出这段代码中的所有问题吗(不止一个)?

[12]:
@code_inspector(loaded_helper)
def summing_function(n):
    sum_result = 0

    intermediate_sums = {}

    for i in range(1, n + 1):
        intermediate_sums[str(i)] = sum(x for x in range(1, i + 1))
        for key in intermediate_sums:
            if key == str(i):
                sum_result = intermediate_sums[key]  # noqa: F841

    final_sum = sum([intermediate_sums[key] for key in intermediate_sums if int(key) == n])

    return int(str(final_sum))

执行和分析 summing_function(1000)

当我们执行 summing_function(1000) 时,会进行几个关键过程,通过 code_inspector 装饰器利用我们的自定义 MLflow 模型。以下是发生的情况:

  1. 装饰器激活:

    • 在调用 summing_function(1000) 时,code_inspector 装饰器首先被激活。这个装饰器旨在使用 loaded_helper 模型来分析被装饰的函数。

  2. 模型分析函数代码

    • code_inspector 使用 inspect 模块获取 summing_function 的源代码。

    • 然后,此源代码被传递给 loaded_helper 模型,该模型根据其训练和提供的指令执行分析。模型预测代码质量、效率和最佳实践的反馈。

  3. 反馈展示:

    • 模型生成的反馈会被打印出来。这些反馈可能包括代码优化的建议、潜在错误的识别,或关于编码实践的一般建议。

    • 这一步在函数执行其逻辑之前,提供了对代码质量的教育性见解。

  4. 函数执行

    • 反馈显示后,summing_function 将使用输入 1000 执行。

    • 该函数计算了数字到1000的累积和,但由于其实现效率低下,这个过程可能比必要的更慢且更消耗资源。

  5. 返回结果

    • 该函数返回最终计算的总和,这是在其内部实现的求和逻辑的结果。

此演示展示了 code_inspector 装饰器与我们的自定义 MLflow 模型相结合,如何提供一种独特的实时代码分析和反馈机制,增强了在交互式环境中的学习和开发体验。

[13]:
summing_function(1000)
Here's a detailed review of your code:

1. Errors or bugs: There are no syntax errors in your code, but there is a
logical error. The summing_function is supposed to calculate the sum of numbers
from 1 to n, but it's doing more than that. It's calculating the sum of numbers
from 1 to i for each i in the range 1 to n, storing these sums in a dictionary,
and then summing these sums again. This is unnecessary and inefficient.

2. Optimizing code efficiency and structure: The function can be simplified
significantly. The sum of numbers from 1 to n can be calculated directly using
the formula n*(n+1)/2. This eliminates the need for the loop and the dictionary,
making the function much more efficient.

3. Enhancing code readability and maintainability: The code can be made more
readable by simplifying it and removing unnecessary parts. The use of the
dictionary and the conversion of numbers to strings and back to numbers is
confusing and unnecessary.

4. Best practice advice: In Python, it's best to keep things simple and
readable. Avoid unnecessary complexity and use built-in functions and operators
where possible. Also, avoid unnecessary type conversions.

Here's a simplified version of your function:

```python
def summing_function(n):
    return n * (n + 1) // 2
```

This function does exactly the same thing as your original function, but it's
much simpler, more efficient, and more readable.

[13]:
500500

one_liner 函数的分析

one_liner 函数,使用 code_inspector 装饰,展示了一种有趣的方法,但存在几个问题:

  1. 复杂性: 该函数使用嵌套的 lambda 表达式来计算 n 的阶乘。虽然紧凑,但这种方法过于复杂且难以阅读,使得代码的可维护性和可理解性降低。

  2. 可读性: 良好的编码实践强调可读性,这里由于单行代码的方式而受到影响。这种代码在调试和理解上可能会很困难,尤其是对于不熟悉特定编码风格的人来说。

  3. 最佳实践:虽然这个示例展示了Python编写简洁代码的能力,但它偏离了常见的最佳实践,特别是在清晰性和简洁性方面。

当由 code_inspector 模型审查时,这些问题很可能会被突出显示,强调了在编写聪明代码与可读性和可维护性之间取得平衡的重要性。

[14]:
@code_inspector(loaded_helper)
def one_liner(n):
    return (
        (lambda f, n: f(f, n))(lambda f, n: n * f(f, n - 1) if n > 1 else 1, n)
        if isinstance(n, int) and n >= 0
        else "Invalid input"
    )
[15]:
one_liner(10)
The code you've provided is a one-liner function that calculates the factorial
of a given number `n`. It uses a lambda function to recursively calculate the
factorial. Here's a review of your code:

1. Errors or bugs: There are no syntax errors or bugs in your code. It correctly
checks if the input is a non-negative integer and calculates the factorial. If
the input is not a non-negative integer, it returns "Invalid input".

2. Optimizing code efficiency and structure: The code is already quite efficient
as it uses recursion to calculate the factorial. However, the structure of the
code is quite complex due to the use of a lambda function for recursion. This
can make the code difficult to understand and maintain.

3. Enhancing code readability and maintainability: The code could be made more
readable by breaking it down into multiple lines and adding comments to explain
what each part of the code does. The use of a lambda function for recursion
makes the code more difficult to understand than necessary. A more
straightforward recursive function could be used instead.

4. Best practice advice: In Python, it's generally recommended to use clear and
simple code over complex one-liners. This is because clear code is easier to
read, understand, and maintain. While one-liners can be fun and clever, they can
also be difficult to understand and debug.

Here's a revised version of your code that's easier to understand:

```python
def factorial(n):
    # Check if the input is a non-negative integer
    if not isinstance(n, int) or n < 0:
        return "Invalid input"

    # Base case: factorial of 0 is 1
    if n == 0:
        return 1

    # Recursive case: n! = n * (n-1)!
    return n * factorial(n - 1)
```

This version of the code does the same thing as your original code, but it's
much easier to understand because it uses a straightforward recursive function
instead of a lambda function.

[15]:
3628800

审查 find_phone_numbers 函数

find_phone_numbers 函数,通过 code_inspector 增强,旨在从给定文本中提取电话号码,但包含一些值得注意的问题和预期行为:

  1. 排版错误: 该函数错误地使用了 re.complie 而不是 re.compile,导致运行时异常。

  2. 模式匹配不准确: 正则表达式模式 "(\d{3})-\d{3}-\d{4}" 虽然格式化为典型电话号码,但如果字符串中没有出现电话号码,可能会导致错误。

  3. 缺乏错误处理:直接访问 phone_numbers 列表的第一个元素而不检查列表是否为空,可能会导致 IndexError

  4. 导入语句位置: import re 语句位于函数内部,这是不常见的。导入通常放在脚本的顶部以提高清晰度。

  5. 分析与异常处理

    • 由于我们在 code_inspector 中定制了我们的 MLflow 模型,函数的问题将在函数逻辑执行之前被分析并返回反馈。

    • 经过此分析,函数的执行很可能会导致异常(由于拼写错误),这表明了仔细的代码审查和测试的重要性。

code_inspector 模型的审查将突出这些编码错误,强调在Python编程中正确语法、模式准确性和错误处理的价值。

[16]:
@code_inspector(loaded_helper)
def find_phone_numbers(text):
    pattern = r"(\d{3})-\d{3}-\d{4}"

    import re

    compiled_pattern = re.complie(pattern)

    phone_numbers = compiled_pattern.findall(text)
    first_number = phone_numbers[0]

    print(f"First found phone number: {first_number}")
    return phone_numbers
[17]:
find_phone_numbers("Give us a call at 888-867-5309")
Here's a detailed review of your code:

1. Errors or Bugs:
   - There's a typo in the `re.compile` function. You've written `re.complie`
instead of `re.compile`.

2. Suggestions for Optimizing Code Efficiency and Structure:
   - The import statement `import re` is inside the function. It's a good
practice to keep all import statements at the top of the file. This makes it
easier to see what modules are being used in the script.
   - The function will throw an error if no phone numbers are found in the text
because you're trying to access the first element of `phone_numbers` without
checking if it exists. You should add a check to see if any phone numbers were
found before trying to access the first one.

3. Recommendations for Enhancing Code Readability and Maintainability:
   - The function name `find_phone_numbers` is clear and descriptive, which is
good. However, the variable `pattern` could be more descriptive. Consider
renaming it to `phone_number_pattern` or something similar.
   - You should add docstrings to your function to describe what it does, what
its parameters are, and what it returns.

4. Best Practice Advice:
   - Use exception handling to catch potential errors and make your program more
robust.
   - Avoid using print statements in functions that are meant to return a value.
If you want to debug, consider using logging instead.

Here's how you could improve your code:

```python
import re

def find_phone_numbers(text):
    """
    This function finds all phone numbers in the given text.

    Parameters:
    text (str): The text to search for phone numbers.

    Returns:
    list: A list of all found phone numbers.
    """
    phone_number_pattern = "(\d{3})-\d{3}-\d{4}"
    compiled_pattern = re.compile(phone_number_pattern)

    phone_numbers = compiled_pattern.findall(text)

    if phone_numbers:
        print(f"First found phone number: {phone_numbers[0]}")

    return phone_numbers
```

Remember, the print statement is not recommended in production code. It's there
for the sake of this example.

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/var/folders/cd/n8n0rm2x53l_s0xv_j_xklb00000gp/T/ipykernel_38633/78508464.py in <cell line: 1>()
----> 1 find_phone_numbers("Give us a call at 888-867-5309")

/var/folders/cd/n8n0rm2x53l_s0xv_j_xklb00000gp/T/ipykernel_38633/2021999358.py in wrapper(*args, **kwargs)
     18             except Exception as e:
     19                 print("Error during model prediction or formatting:", e)
---> 20             return func(*args, **kwargs)
     21
     22         return wrapper

/var/folders/cd/n8n0rm2x53l_s0xv_j_xklb00000gp/T/ipykernel_38633/773713950.py in find_phone_numbers(text)
      5     import re
      6
----> 7     compiled_pattern = re.complie(pattern)
      8
      9     phone_numbers = compiled_pattern.findall(text)

AttributeError: module 're' has no attribute 'complie'

结论:在AI辅助开发中利用MLflow的力量

随着本教程的结束,我们已经完成了将OpenAI的语言模型与MLflow的强大功能相结合的过程,创建了一个强大的AI辅助软件开发工具包。以下是我们旅程的回顾和关键要点:

  1. 将 OpenAI 与 MLflow 集成

    • 我们探讨了如何在 MLflow 框架内无缝集成 OpenAI 的高级语言模型。这种集成突显了将 AI 智能与强大的模型管理相结合的潜力。

  2. 实现自定义 Python 模型

    • 我们的旅程包括创建一个自定义的 CodeHelper 模型,该模型展示了 MLflow 在处理自定义 Python 函数方面的灵活性。这个模型通过将 AI 响应格式化为更易读的格式,显著提升了用户体验。

  3. 实时代码分析与反馈

    • 通过使用 code_inspector 装饰器,我们展示了 MLflow 在提供关于代码质量和效率的实时、有洞察力的反馈方面的实用性,促进了引导向最佳编码实践的学习环境。

  4. 处理复杂代码分析

    • 教程展示了复杂的代码示例,揭示了如何结合 MLflow 和 OpenAI 处理复杂的代码分析,提供建议并识别潜在问题。

  5. 从交互式反馈中学习

    • 通过我们的 MLflow 模型实现的交互式反馈循环,展示了一种学习和提高编码技能的实用方法,使得这套工具特别适用于教育和开发目的。

  6. MLflow 的灵活性和可扩展性

    • 在整个教程中,MLflow 的灵活性和可扩展性显而易见。无论是管理简单的 Python 函数还是集成最先进的 AI 模型,MLflow 都证明了它在简化模型管理过程中的宝贵价值。

总之,本教程不仅提供了有效编码实践的见解,还强调了MLflow在增强AI辅助软件开发方面的多功能性。它证明了机器学习工具和模型如何创新地应用于提高代码质量、效率和整体开发体验。

下一步是什么?

要继续您的学习之旅,请参阅 MLflow 的 OpenAI 风格的进阶教程