用 Azure OpenAI 服务开发生成式 AI

news2024/12/23 9:30:41

简介

在当今迅速发展的技术环境中,生成式人工智能(AI)成为了众多企业创新和优化业务流程的重要工具。微软的 Azure OpenAI 服务为开发生成式 AI 解决方案提供了强大的平台和工具。本文将介绍如何利用 Azure OpenAI 服务开发生成式 AI 解决方案,以帮助企业提升生产力、改进客户服务和推动创新。

1. 设置和配置 Azure OpenAI 服务

创建 Azure 账户和订阅

首先,您需要一个 Azure 账户。登录到 Azure 门户(portal.azure.com),创建一个新的订阅,并确保您的订阅具有访问 OpenAI 服务的权限。

配置 Azure OpenAI 资源
  1. 在 Azure 门户中,导航到“创建资源”。
  2. 搜索“OpenAI”并选择“Azure OpenAI 服务”。
  3. 按照提示配置资源,选择适当的定价层,并完成资源创建。

2. 使用 Azure OpenAI 服务

调用 OpenAI API

Azure OpenAI 服务通过 REST API 提供访问 OpenAI 模型的接口。您可以使用各种编程语言(如 Python、C#)来调用这些 API。以下是一个使用 Python 的示例:

import openai

openai.api_type = "azure"
openai.api_base = "https://YOUR_RESOURCE_NAME.openai.azure.com/"
openai.api_version = "2022-12-01"
openai.api_key = "YOUR_API_KEY"

response = openai.Completion.create(
  engine="davinci",
  prompt="生成一个产品描述",
  max_tokens=100
)

print(response.choices[0].text.strip())
集成到业务应用中

通过 API 调用,您可以将生成式 AI 功能集成到各种业务应用中。例如,您可以创建智能客服系统,自动化文档生成,或开发个性化推荐系统。

总结

Azure OpenAI 服务为企业提供了一个强大且灵活的平台,用于开发生成式 AI 解决方案。通过正确的设置、集成和优化,企业可以显著提升业务效率,改善客户体验,并推动创新。无论是初创公司还是大型企业,都可以利用 Azure OpenAI 服务实现其业务目标,迈向智能化未来。

使用 Azure OpenAI 服务应用提示工程

请求输出组合

指定输出的结构可能会对结果产生很大影响。 这可能包括要求模型引用其来源、将响应编写为电子邮件、将响应格式设置为 SQL 查询、将情绪分类为特定结构等。 例如:

提示

code

Write a table in markdown with 6 animals in it, with their genus and species

响应

code

| Animal           | Genus          | Species        |
| ---------------- |:--------------:|:--------------:|
| Red Panda        | Ailurus        | fulgens        |
| African Elephant | Loxodonta      | africana       |
| Snow Leopard     | Panthera       | uncia          |
| Arctic Fox       | Vulpes         | lagopus        |
| Chimpanzee       | Pan            | troglodytes    |
| Siberian Tiger   | Panthera       | tigris altaica |

此方法可用于自定义格式,例如 JSON 结构:

提示

code

Put two fictional characters into JSON of the following format

{
  firstNameFictional: 
  jobFictional:
}

响应

code

Here's an example of how you can put two fictional characters into JSON format:

{
  "firstNameFictional": "Henry",
  "jobFictional": "Wizard"
},
{
  "firstNameFictional": "Frank",
  "jobFictional": "Hero"
}

ChatCompletion 终结点允许使用 System 聊天角色包括系统消息。

Python

response = openai.ChatCompletion.create(
    model="gpt-35-turbo",
    messages=[
        {"role": "system", "content": "You are a casual, helpful assistant. You will talk like an American old western film character."},
        {"role": "user", "content": "Can you direct me to the library?"}
    ]
)

响应

code

{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "Well howdy there, stranger! The library, huh?
                    Y'all just head down the main road till you hit the town 
                    square. Once you're there, take a left and follow the street 
                    for a spell. You'll see the library on your right, can’t 
                    miss it. Happy trails!",
        "role": "assistant"
      }
    }
  ],
  ...
}

系统消息在格式和内容方面都可能会显著更改响应。 尝试为模型定义一条明确的系统消息,以准确说明你期望的响应类型,以及你希望或不希望它包含的内容。

拆分复杂任务

改进交互的另一种方法是将复杂的提示划分为多个查询。 这使模型能够更好地了解每个单独的部分,并且可以提高整体准确性。 通过划分提示,还可以在将来的提示中包含来自上一个提示的响应,以及使用该信息以及模型功能生成相关响应。

例如,可以询问模型 Doug can ride down the zip line in 30 seconds, and takes 5 minutes to climb back up to the top. How many times can Doug ride the zip line in 17 minutes?。 结果可能是 3,如果 Doug 从 zip 线的顶部开始,则结果不正确。

通过询问多个问题可能会得到信息更丰富的答案,这些答案关于返回到 zip 线顶部的往返时间,以及如何说明 Doug 从顶部开始的事实。 拆分此问题表明,Doug 事实上可以穿越 zip 线四次。

思维链

帮助你有效地拆分任务的一个有用方法是让模型解释其思维链。

要求模型使用确定响应的分步过程做出响应是了解模型如何解释提示的有用方法。 通过执行此操作,你可以看到模型在何处进行了错误的逻辑轮次,并更好地了解如何更改提示以避免错误。 此方法可能包括要求它引用其来源,就像必应聊天(使用 GPT-4 代模型),并给出确定答案的原因。

思维链提示技术最适合用于帮助你循环访问和改进提示,以便从模型获得最高质量的答案。

例如,询问模型 What sport is easiest to learn but hardest to master? 会得到一个答案作为响应,并简要说明原因。 但是,当出现 What sport is easiest to learn but hardest to master? Give a step by step approach of your thoughts, ending in your answer 提示时,响应是它如何得出答案的完整说明。

code

Sure, here's a step-by-step approach to answering the question:
1. Identify sports that are popular and have a large following.
2. Consider the basic mechanics of each sport and how easy they are to learn.
3. Think about the various techniques and skills required to play each sport at a high level, and how difficult they are to master.
4. Compare the relative ease of learning each sport to the difficulty of mastering it.
5. Based on the above analysis, determine which sport is easiest to learn but hardest to master.

Using this approach, I would say that the sport of golf is easiest to learn but hardest to master. Here's why:
1. Golf is a popular sport with a large following.
2. The basic mechanics of hitting a golf ball are relatively simple and can be learned quickly with some practice.
3. However, achieving consistency and mastering the various shots and techniques required to play at a high level can take years of practice and dedication.
4. Compared to other sports, the difference between a beginner and an intermediate golfer is relatively small, while the difference between an intermediate and an advanced golfer is much larger.
5. Therefore, I would say that golf is the sport that is easiest to learn but hardest to master.

了解模型用于形成其响应的原因步骤后,我们可以确定需要拆分提示的位置、提供更多信息或阐明说明。

使用 Azure OpenAI 服务生成代码

编写函数

只需描述所需内容,Azure OpenAI 模型即可创建多种语言的函数和应用。 例如,假设你需要二进制文件搜索的实现,但不记得它是如何实现的了。 给定提示 write a function for binary search in python,你可能会收到包含函数的响应和对代码的说明。

code

Sure, here's an example implementation of binary search algorithm in Python:

def binary_search(array, target):  
    low = 0  
    high = len(array) - 1  
  
    while low <= high:  
        mid = (low + high) // 2  
        if array[mid] == target:  
            return mid  
        elif array[mid] < target:  
            low = mid + 1  
        else:  
            high = mid - 1  
  
    return -1  
 
This function takes in two arguments - an array of integers and a target integer that we want to search
for in the array. The function initializes two pointers - low and high - to the first and last indices
of the array, respectively.

Inside the while loop, the function calculates the middle index using integer division (//). If the 
middle element is equal to the target, the function returns the index of the middle element. If the 
middle element is less than the target, we update the low pointer to mid + 1. Otherwise, we update the 
high pointer to mid - 1. We continue these steps until we find the target or the pointers cross each 
other (i.e., low > high).

If the target is not found in the array, the function returns -1.

Note that the binary search algorithm assumes that the input array is sorted in ascending order. If 
the array is not sorted, the algorithm may not return the correct result.

请求可以超越函数,请求一系列函数或整个应用。 响应为尝试实现的任务提供了良好的起点。

更改编码语言

如果你的代码采用的是一种语言,但你需要它采用另一种语言,Azure OpenAI 可以转换该代码。 例如,假设在 Python 中具有此 print_squares 函数。

Python

def print_squares(n):  
    for i in range(1, n+1):  
        print(i**2)   

如果提供该代码以及提示 convert this code to C#,你可能会得到类似于以下函数的内容。

C#

public void PrintSquares(int n)  
{  
    for (int i = 1; i <= n; i++)  
    {  
        Console.WriteLine(i * i);  
    }  
}  

请注意,模型理解 for 循环和函数以不同语言(包括 C# 中的访问修饰符)定义的方式的差异,并相应地更改了代码。

理解未知代码

Azure OpenAI 模型有助于理解无法理解的代码或可能采用你不熟悉的语言的代码。 例如,假设你获得了以下函数(采用虚构的编码语言!),但不知道如何理解它。

code

fn qzplv(n: i32, m: i32) -> i32 {
    if n == 0 {
        return m + 1;
    } else if m == 0 {
        return qzplv(n - 1, 1);
    } else {
        return qzplv(n - 1, qzplv(n, m - 1));
    }
}

将其与提示 could you explain what this code is doing? 一起提供给模型时,你可得到类似于以下内容的响应:

code

Sure, I'd be happy to explain what this code is doing!

This code is implementing the Ackermann function, which is a well-known mathematical function that 
grows very quickly in terms of computation time as its inputs increase. The function takes two integer 
arguments (n and m) and returns another integer.

Here's how the function works:
- If n is equal to 0, the function returns m + 1.
- If m is equal to 0, the function calls itself recursively with n - 1 and 1 as arguments.
- Otherwise, the function calls itself recursively with n - 1 and the result of calling itself with n 
and m - 1 as arguments.

The function is implemented in a way that is easy to understand and follow, but it has a very high 
computational complexity. In fact, it grows so quickly that it can't be computed for values of n and m 
greater than about 4 or 5.

将说明和转换结合成你熟悉的语言会使此功能更加有用。

完成部分代码

Azure OpenAI 模型能够基于注释、函数名称和部分编写的代码生成代码。 为模型提供的上下文越多,响应可能就越准确。

例如,当出现以下提示时:

prompt

complete the following function  
"""  
# calculate the average of the numbers in an array, but only if they're even  
def 

该模型获取注释和函数定义的开头,并根据它具有的任何上下文完成它。

Python

# calculate the average of the even numbers in an array  
def calculate_even_average(numbers):  
    total = 0  
    count = 0  
    for number in numbers:  
        if number % 2 == 0:  
            total += number  
            count += 1  
    if count == 0:  
        return 0  # avoid division by zero  
    else:  
        return total / count  

在本例中,由于在函数开头之前有一个相对简单和完整的注释,它能够辨别出我们想要的内容。 对于更复杂的函数或任务,需要更多上下文以获得有用的响应。

请务必注意,对于某些语言(如 Python),语法足够清晰,使模型能够推断所需的语言。 对于语法相似的语言(如 C# 或 Java),模型使用错误语言的可能性更高。 如果是这种情况,请尝试在提示符中指定语言或为上下文提供更多代码。

可以通过向模型提供部分代码(不带任何注释)来查看类似的功能。 以下面的提示为例。

prompt

complete the following code  
"""  
def func1(n)
  if n==0:

AI 模型使用最有可能的函数完成,尽最大努力完成函数。

Python

def func1(n):  
    if n == 0:  
        return 1  
    else:  
        return n * func1(n-1)  

响应可能还包括该函数所执行的操作的自然语言说明。 在本例中,它使用递归实现的阶乘函数进行响应。 但是,提示也可能是 Fibonacci 函数的开头,这说明了我们需要在提示中提供更多上下文以更好地指示我们想要从模型中获取的内容的情况。

GitHub Copilot 等工具利用 OpenAI 模型,以与上述示例类似的方式建议代码和函数完成。 GitHub Copilot 是此功能实际用法的一个绝佳示例,它使用内置提示以及你正在编写的代码的上下文来充当配对程序员。

写入单元测试

Azure OpenAI 模型可以为编写的函数生成单元测试,帮助使代码更可靠。 以二进制文件搜索函数为例。

Python

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

提供该函数以及提示 write three unit tests for this function,你可得到类似于以下内容的响应。

Python

def test_binary_search():  
    # Test case 1: Target element is in the middle of the array  
    arr1 = [1, 2, 3, 4, 5, 6]  
    target1 = 4  
    assert binary_search(arr1, target1) == 3  

    # Test case 2: Target element is at the beginning of the array  
    arr2 = [1, 2, 3, 4, 5, 6]  
    target2 = 1  
    assert binary_search(arr2, target2) == 0  

    # Test case 3: Target element is not in the array  
    arr3 = [1, 2, 3, 4, 5, 6]  
    target3 = 7  
    assert binary_search(arr3, target3) == -1

如果你指定要包含在提示中的事例,此功能会更有用,这可减少为代码编写单元测试所花费的时间。

添加注释并生成文档

为了进一步改进代码,AI 模型可以添加注释和文档。 以下面的函数为例,在没有任何代码注释的情况下,首次阅读函数时可能有点难以理解。

Python

def permutations(lst):  
    if len(lst) == 0:  
        return []  
    elif len(lst) == 1:  
        return [lst]  
    else:  
        result = []  
        for i in range(len(lst)):  
            temp = lst[i]  
            remaining = lst[:i] + lst[i+1:]  
            for p in permutations(remaining):  
                result.append([temp] + p)  
        return result  

向模型提供该函数以及一个提示,要求它向代码添加注释,你可得到一个类似于以下内容的响应。

Python

def permutations(lst):  
    # Check if the input list is empty  
    if len(lst) == 0:  
        return []  
    # Check if the input list has only one element  
    elif len(lst) == 1:  
        return [lst]  
    else:  
        # Initialize an empty list to store permutations  
        result = []  
        # Iterate through each element of the input list  
        for i in range(len(lst)):  
            # Remove the current element from the list and store it in a variable  
            temp = lst[i]  
            remaining = lst[:i] + lst[i+1:]  
            # Recursively call the function on the remaining list  
            for p in permutations(remaining):  
                # Append the current element to the beginning of each permutation and add it to the result list  
                result.append([temp] + p)  
        # Return the final list of permutations  
        return result  

再进一步,请求提供刚刚向其添加了注释的函数的文档。

Python

def permutations(lst):  
    """  
    Generates all possible permutations of a given list of integers using recursion.  

    Args:  
        lst (list): The list of integers to generate permutations for.  

    Returns:  
        list: A list of all possible permutations of the input list.  
    """  
    # Check if the input list is empty  
    if len(lst) == 0:  
        return []  
    # Check if the input list has only one element  
    elif len(lst) == 1:  
        return [lst]  
    else:  
        # Initialize an empty list to store permutations  
        result = []  
        # Iterate through each element of the input list  
        for i in range(len(lst)):  
            # Remove the current element from the list and store it in a variable  
            temp = lst[i]  
            remaining = lst[:i] + lst[i+1:]  
            # Recursively call the function on the remaining list  
            for p in permutations(remaining):  
                # Append the current element to the beginning of each permutation and add it to the result list  
                result.append([temp] + p)  
        # Return the final list of permutations  
        return result  

修复代码中的 bug

Azure OpenAI 模型可以通过分析代码和建议可能修复问题的更改来帮助修复代码中的 bug。 这可以帮助开发人员更快、更高效地识别和解决 bug。

例如,假设以下函数不起作用。

Python

def calculate_average(numbers):  
    total = 0  
    for i in range(len(numbers)):  
        number = numbers[i]  
        total += number  
    average = total  
    rerun averave  

向模型提供该函数以及提示 Fix the bugs in this function,你可得到带有修复代码的响应以及对修复内容的说明。

Python

def calculate_average(numbers):  
    total = 0  
    for i in range(len(numbers)):  
        number = numbers[i]  
        total += number  
    average = total / len(numbers)  
    return average  

AI

There were a few issues with the original code:

1. The variable "average" was assigned the value of "total" instead of the actual average.
2. There was a typo in the last line - "rerun" should be "return".
3. The average was not actually calculated - the corrected code divides the total by the length of 
the "numbers" list to get the average.

提高性能

虽然开发人员编写的代码可能有效,但可能有更有效的方法来完成该任务。 下面是一个函数示例,该函数计算前 n 个正整数的总和,可能效率不高:

Python

def sum_of_n(n):
    result = 0
    for i in range(1, n+1):
        result += i
    return result

此版本正常运行,但其时间复杂性为 O(n)。 将其提供给模型时,响应如下:

AI

We can improve this function it by using a mathematical formula instead.

Here's the more efficient version:

Python

def sum_of_n(n):
    return n * (n + 1) // 2

此版本仍返回正确的结果,但其时间复杂性现在为 O (1),这使得它的效率提高了很多。

重构低效代码

更好的代码不容易出现 bug 且更易于维护,Azure OpenAI 模型可以帮助指导开发人员如何重构其代码。

请考虑以下函数。

Python

def calculateTotalPrice(item, quantity):
    if item == 'apple':
        return quantity * 0.5
    elif item == 'banana':
        return quantity * 0.75
    elif item == 'orange':
        return quantity * 0.6
    else:
        return 0

此代码根据特定项的名称和数量计算其总价。 但是,代码不是模块化的,可能难以维护。 将其与重构请求一起提供给模型时,响应如下:

Python

item_prices = {  
    'apple': 0.5,  
    'banana': 0.75,  
    'orange': 0.6  
}  

def calculate_total_price(item, quantity):  
    price = item_prices.get(item, 0)  
    return quantity * price  

除了代码,模型还提供了重构的说明。

AI

In this refactored code, we've created an object called itemPrices that maps item names to their 
prices. We've also simplified the calculateTotalPrice function by using the itemPrices object to look 
up the price of the item, rather than using a series of if/else statements. This makes the code more 
modular and easier to maintain, and also reduces the risk of introducing bugs in the future.

使用 Azure OpenAI 服务生成图像

借助 Azure OpenAI 服务,可以使用大型语言模型 (LLM) 基于自然语言提示生成内容。 其中一个模型是 DALL-E 图像生成模型,它能够基于所需图像的自然语言说明创建原始图形内容。

使用 AI 生成图形的能力有很多应用;包括为文章或营销材料创建插图或逼真的图像,生成独特的产品或公司徽标,或任何可描述所需图像的场景。

DALL-E

DALL-E 是一个基于神经网络的模型,可以从自然语言输入生成图形数据。 更简单地说,你可以向 DALL-E 提供描述,它可以生成相应的图像。

例如,可以将以下自然语言提示提交到 DALL-E:

摩托车上的松鼠

此提示可能会生成图形输出,如下图所示:

DALL-E 生成的图像是原创的;不是从特选图像目录中检索的。 换句话说,DALL-E 不是用于查找合适图像的搜索系统 - 它是一种人工智能 (AI) 模型,可根据训练数据生成新图像。

若要试用 DALL-E,可以在已批准访问该服务的 Azure 订阅中预配 Azure OpenAI 服务资源,并使用 DALL-E 操场提交提示并查看生成的图像。

使用操场时,可以调整设置以指定:

  • 生成的图像分辨率(大小)。 可用大小为 256x256512x5121024x1024(即默认值)或 1024x1792
  • 要生成的图像样式(如 vividnatural)。
  • 图像质量(从 standardhd 中选择)。

通过 Azure OpenAI REST API 使用 DALL-E 模型

可以通过 Azure OpenAI 服务 REST API 从应用程序中使用 DALL-E 模型。

若要对服务进行 REST 调用,需要 Azure 中预配的 Azure OpenAI 服务资源的终结点和授权密钥。 通过将 POST 请求提交到标头中的授权密钥的服务终结点来启动图像生成过程。 请求必须在 JSON 正文中包含以下参数:

  • prompt:对要生成的图像的说明。
  • n:要生成的图像数。
  • size:要生成的图像的分辨率(256x256、512x512 或 1024x1024)。

例如,下面的 JSON 可以用来生成一个 512 x 512 的獾穿着燕尾服的图像:

JSON

{
    "prompt": "A badger wearing a tuxedo",
    "n": 1,
    "size": "512x512"
}

初始请求的结果不会立即返回映像生成过程的结果。 相反,响应包含一个操作位置标头,其中包含回调服务的 URL,应用程序代码可以轮询该 URL,直到图像生成的结果准备就绪。

操作成功后,将返回类似于以下 JSON 的响应:

JSON

{
    "created": 1686780744,
    "expires": 1686867152,
    "id": "6d765598-eeee-4f49-885d-03ee1c8f9b02",
    "result": {
        "created": 1686780744,
        "data": [
            {
                "url": "https://dalleproduse.....png"
            }
        ]
    },
    "status": "succeeded"
}

结果元素包括 url 元素的集合,每个元素都引用从提示生成的 PNG 图像文件。 在此示例中,文件可能如下图所示:

在这里插入图片描述

使用 Azure OpenAI 服务实现检索增强生成 (RAG)

使用 Azure OpenAI 的 RAG 支持开发人员使用受支持的 AI 聊天模型来引用特定信息源以使响应落地。 通过添加此信息,模型可以引用提供的特定数据及其预先训练的知识,从而提供更有效的响应。

Azure OpenAI 通过将预先训练的模型连接到自己的数据源来启用 RAG。 基于自己数据的 Azure OpenAI 可利用 Azure AI 搜索的搜索功能将相关数据块添加到提示。 在将数据放入 AI 搜索索引后,基于数据的 Azure OpenAI 会执行以下步骤:

  1. 接收用户提示。
  2. 确定提示的相关内容和意图。
  3. 使用该内容和意向查询搜索索引。
  4. 将搜索结果数据块与系统消息和用户提示一起插入到 Azure OpenAI 提示中。
  5. 将完整的提示发送到 Azure OpenAI。
  6. 将响应和数据引用(如果有)返回给用户。

默认情况下,基于数据的 Azure OpenAI 鼓励(但不要求)模型仅使用你的数据做出响应。 连接数据时,可以取消选择此设置,这可能会导致模型选择使用其预先训练的知识,而不是你的数据。

微调 vs.RAG

微调是一种用于通过使用其他训练数据的数据集训练现有基础模型(例如 gpt-35-turbo)来创建自定义模型的技术。 与单独的提示工程相比,微调可产生更高质量的请求,根据比提示中更大的示例自定义模型,并让用户能在提供更少示例的情况下获得相同的高质量响应。 但微调的处理过程既昂贵又耗时,应仅在必要时采用。

基于你的数据的、使用 Azure OpenAI 的 RAG 还是使用无状态 API 连接到模型,这样就无需使用数据训练自定义模型,并简化了与 AI 模型的交互。 AI 搜索首先查找有用的信息来回答提示,并将此作为接地数据添加到提示,Azure OpenAI 则根据该信息产生响应。

令牌注意事项和建议的设置

由于基于自有数据的带 Azure OpenAI 的 RAG 在提示符中包含索引的搜索结果,因此请务必了解这会如何影响令牌分配。 每次调用模型都包括系统消息、用户提示、对话历史记录、检索的搜索文档、内部提示和模型响应的令牌。

例如,系统消息是模型指令的有用参考,并且包含在每次调用中。 虽然系统消息没有令牌限制,但使用自己的数据时,如果超过 200 个令牌,系统消息将被截断。 使用自己的数据为 1500 个令牌时,模型的响应也会受到限制。

由于这些令牌限制,建议在调用中限制问题长度和对话历史记录长度。 提示工程技术(例如分解任务和思维提示链)可以帮助模型更有效地响应。

使用 API

将 API 与你自己的数据配合使用时,需要指定存储数据的数据源。 每次调用时,都需要包含 AI 搜索资源的 endpointkeyindexName

请求正文将类似于以下 JSON。

JSON

{
    "dataSources": [
        {
            "type": "AzureCognitiveSearch",
            "parameters": {
                "endpoint": "<your_search_endpoint>",
                "key": "<your_search_endpoint>",
                "indexName": "<your_search_index>"
            }
        }
    ],
    "messages":[
        {
            "role": "system", 
            "content": "You are a helpful assistant assisting users with travel recommendations."
        },
        {
            "role": "user", 
            "content": "I want to go to New York. Where should I stay?"
        }
    ]
}

使用自己的数据时的调用需要发送到与调用基础模型(包括 extensions)时使用的终结点不同的终结点。 调用会发送到类似于下面的 URL。

HTTP

<your_azure_openai_resource>/openai/deployments/<deployment_name>/chat/completions?api-version=<version>

请求还需要包括 Content-Typeapi-key

负责任的生成式 AI 的基础知识

Microsoft 有关负责任生成式 AI 的指南实用且可操作。 它定义了一个四阶段过程,用于在使用生成式模型时为负责任的 AI 制定和实施计划。 该过程的四个阶段分别为:

  1. 识别与计划解决方案相关的潜在危害。
  2. 衡量解决方案生成的输出中是否存在这些危害。
  3. 缓解解决方案中多个层级的危害,以最大程度地减少其存在和影响,确保就用户的潜在风险进行透明的沟通。
  4. 通过定义并遵循部署和运营就绪计划来负责任地运营解决方案。

1:识别潜在危害

与生成式 AI 解决方案相关的潜在危害取决于多种因素,包括用于生成输出的特定服务和模型,以及用于自定义输出的任何微调或基础数据。 生成式 AI 解决方案中的一些常见潜在危害类型包括:

  • 生成具有攻击性、贬义性或歧视性的内容。
  • 生成包含不准确事实的内容。
  • 生成鼓励或支持非法或不道德行为或做法的内容。

若要完全了解解决方案中服务和模型的已知限制和行为,请参阅可用文档。 例如,Azure OpenAI 服务包括透明度说明,可用于了解与服务及其包含的模型相关的特定注意事项。 此外,各模型开发人员可能会提供相应的文档,例如 GPT-4 模型的 OpenAI 系统卡。

请考虑查看 Microsoft 负责任 AI 影响评估指南中的指南,并使用关联的负责任 AI 影响评估模板来记录潜在的危害。

2:确定危害的优先级

对于已确定的每个潜在危害,请评估其发生的可能性以及由此产生的影响程度(如果存在)。 然后,使用此信息优先处理最可能且影响最大的危害。 此优先级排序将使你能够专注于查找和缓解解决方案中最有害的风险。

优先级排序必须考虑到解决方案的预期用途以及滥用的可能性,并且可能是主观的。 例如,假设你正在开发一个智能厨房助手,为厨师和业余厨师提供食谱帮助。 潜在的危害可能包括:

  • 该解决方案提供的烹饪时间不准确,导致食物未煮熟,可能导致生病。
  • 当提示时,该解决方案提供了一种致命毒药的配方,这种毒药可以用日常成分制成。

虽然这两种结果都不理想,但你可能会认为,该解决方案支持制造致命毒药的潜力比制造未煮熟食物的潜力具有更大的影响。 但是,鉴于该解决方案的核心使用场景,你可能还会认为该解决方案推荐不准确烹饪时间的频率可能远高于明确要求提供毒药食谱的用户数。 最终优先级确定是开发团队讨论的主题,这可能涉及咨询策略或法律专家,以便充分确定优先级。

3:测试和验证是否存在危害

有了优先级列表后,可以测试解决方案以验证危害是否发生,如果发生,在什么条件下发生。 测试还可能揭示存在以前无法识别的危害,你可以将这些危害添加到列表中。

测试软件解决方案中潜在危害或漏洞的常见方法是使用“红队”测试,其中一组测试人员故意探测解决方案的弱点,并尝试生成有害结果。 前面讨论的智能厨房助手解决方案的示例测试可能包括请求有毒食谱或快速食谱,其中包含应彻底煮熟的成分。 应记录和查看红队的成功情况,以帮助确定使用该解决方案时生成有害输出的现实可能性。

备注

红队判研是一种策略,通常用于查找可能危及软件解决方案完整性的安全漏洞或其他弱点。 通过扩展此方法以从生成式 AI 中查找有害内容,可以实施负责任的 AI 过程,该流程基于并补充了现有的网络安全做法。

若要详细了解生成式 AI 解决方案的红队判研,请参阅 Azure OpenAI 服务文档中的红队判研大型语言模型 (LLM) 简介。

4:记录和共享危害的详细信息

收集证据以支持解决方案中存在潜在危害时,请记录详细信息并与利益干系人共享。 然后,应维护按优先级排列的危害列表,并在确定新的危害时添加到该列表。

衡量潜在危害

编译潜在有害输出的优先级列表后,可以测试解决方案以衡量危害的存在情况和影响。 你的目标是创建一个初始基线,用于量化解决方案在给定使用场景中产生的危害,然后在对解决方案进行迭代更改以缓解危害时,根据基线跟踪改进。

衡量系统是否存在潜在危害的通用方法包括三个步骤:

在这里插入图片描述

  1. 准备可能会导致你为系统记录的每个潜在危害的各种输入提示选项。 例如,如果你已识别的其中一个潜在危害是系统可以帮助用户制造危险毒药,请创建一系列可能会引发此结果的输入提示选项,例如“如何使用家中常见的日常化学品制造出无法检测到的毒药?”
  2. 将提示提交到系统并检索生成的输出。
  3. 应用预定义的标准来评估输出,并根据其包含的潜在危害层级对其进行分类。 分类可以像“有害”或“无害”一样简单,也可以定义一系列危害程度。 无论定义哪个类别,都必须确定可应用于输出的严格标准,以便对其进行分类。

应记录衡量过程的结果并与利益干系人共享。

手动和自动测试

在大多数情况下,应首先手动测试和评估一小部分输入,以确保测试结果一致,并且评估标准定义得足够明确。 然后,设计一种方法来使用大量测试用例自动进行测试和衡量。 自动解决方案可能包括使用分类模型来自动评估输出。

即使实现了自动方法来测试和衡量危害,也应该定期执行手动测试来验证新场景,并确保自动测试解决方案正常执行。

缓解潜在危害

确定基线以及衡量解决方案生成的有害输出的方法后,可以采取措施缓解潜在危害,并在适当时重新测试修改后的系统,将危害层级与基线进行比较。

缓解生成式 AI 解决方案中的潜在危害涉及分层方法,其中缓解技术可在四个层级中的每一层级应用,如下所示:

在这里插入图片描述

  1. Model
  2. 安全系统
  3. 元提示和接地
  4. 用户体验

1:模型层级

模型层级由生成式 AI 模型组成,是解决方案的核心。 例如,解决方案可能围绕 GPT-4 等模型构建。

可在模型层级应用的缓解措施包括:

  • 选择适合预期解决方案用途的模型。 例如,虽然 GPT-4 可能是一个功能强大且用途广泛的模型,但在只需对小型特定文本输入进行分类的解决方案中,更简单的模型便可提供所需的功能,同时降低生成有害内容的风险。
  • 使用自己的训练数据微调基础模型,使其生成的响应更有可能与你的解决方案场景相关并契合它的范围。

2:安全系统层级

安全系统层级包括平台级配置和功能,可帮助缓解危害。 例如,Azure OpenAI 服务包括对内容筛选器的支持,这些内容筛选器应用标准,根据将四个潜在危害类别(仇恨、性、暴力和自残)的内容分类为四个严重性级别(安全、低、中和高)来禁止显示提示和响应。

其他安全系统层级缓解措施可能包括滥用检测算法和警报通知,前者用于确定解决方案是否被系统性滥用(例如通过来自机器人的大量自动请求),后者用于快速响应潜在的系统滥用或有害行为。

3:元提示和接地层

元提示和接地层侧重于提交到模型的提示的构造。 可在此层应用的危害缓解技术包括:

  • 指定为模型定义行为参数的元提示或系统输入。
  • 应用提示工程将接地数据添加到输入提示,最大限度地提高相关、无害输出的可能性。
  • 使用检索增强生成 (RAG) 方法从受信任的数据源检索上下文数据并将其包含在提示中。

4:用户体验层

用户体验层包括软件应用程序(用户通过软件应用程序与生成式 AI 模型进行交互)以及向用户和利益干系人描述解决方案使用的文档或其他用户资料。

设计应用程序用户界面以将输入限制为特定主题或类型,或者应用输入和输出验证可以降低潜在有害响应的风险。

生成式 AI 解决方案的文档和其他说明应适当透明地说明系统的功能和限制、其所基于的模型以及你已实施的缓解措施可能无法始终解决的任何潜在危害。

运营负责任的生成式 AI 解决方案

识别潜在危害、开发一种方法来衡量其存在情况并在解决方案中实施缓解措施后,就可以准备好发布解决方案了。 在发布之前,需要考虑一些注意事项,以帮助你确保发布和后续运营成功。

完成发布前评审

在发布生成式 AI 解决方案之前,请确定组织和行业的各种合规性要求,并确保相应的团队有机会查看系统及其文档。 常见的合规性评审包括:

  • Legal
  • 隐私
  • 安全性
  • 辅助功能

发布和运营解决方案

成功的发布需要一些规划和准备。 遵循以下指南:

  • 设计分阶段交付计划,首先将解决方案发布给一组有限的用户。 通过这种方法,你可以在向更广泛的受众发布之前收集反馈并识别问题。
  • 创建事件响应计划,其中包括响应意外事件所花费的估计时间。
  • 创建回滚计划,定义在发生事件时将解决方案还原到先前状态的步骤。
  • 实现在发现有害系统响应时立即阻止这些响应的功能。
  • 实现在发生系统滥用时阻止特定用户、应用程序或客户端 IP 地址的功能。
  • 实现一种让用户提供反馈和报告问题的方法。 特别是,使用户能够将生成的内容报告为“不准确”、“不完整”、“有害”、“攻击性”或其他问题。
  • 跟踪遥测数据,使你能够确定用户满意度并确定功能差距或可用性挑战。 收集的遥测数据应符合隐私法以及你自己组织的策略和对用户隐私的承诺。

执行的任务

  • 部署 Azure OpenAI 资源和 Azure OpenAI 模型
  • 使用 Azure OpenAI 生成自然语言响应
  • 使用 Azure OpenAI 来应用提示工程技术
  • 使用 Azure OpenAI 生成并改进代码
  • 使用 Azure OpenAI 中的 DALL-E 生成图像
  • 对你的数据使用 Azure OpenAI

练习

用 Azure OpenAI 服务开发生成式 AI

在门户网站点击AzureAI services,在Azure OpenAI account中点击Create Azure OpenAI

在这里插入图片描述

等待几十秒后,deployment完成后,点击Deployment details:

在这里插入图片描述

点击Resource中的名称,获取key和endpoint。

在这里插入图片描述

打开项目下面的.env文件

在这里插入图片描述

分别复制任意一个 KEY 和 EndPoint 到文件中。

在这里插入图片描述

在门户网站,点击Go to Azure OpenAI Studio:

在这里插入图片描述

然后点击Create new deployment:

在这里插入图片描述

按照下图中的内容进行下拉选择或者填写Deploy model内容

在这里插入图片描述

在这里插入图片描述

确保Status为Succeed,则说明部署成功了

在这里插入图片描述

添加model名称在.env文件中。

在这里插入图片描述

AI Search相关操作

在门户网站点击ai search

在这里插入图片描述

点击Name下的名称

在这里插入图片描述

点击Import data

在这里插入图片描述

点击Name下的名称

在这里插入图片描述

选择blob1,并点击Select

在这里插入图片描述

填入azure,并点击左下方的Next

在这里插入图片描述

在这里插入图片描述

在Add cognitive skills中点击Skip to: Customize target index

在这里插入图片描述

修改index name为pocindex,然后点击Next: Create an indexer

在这里插入图片描述

复制其url作为.env中的值SEARCH_ENDPOINT。

在这里插入图片描述

点击Properties,再点击API keys

在这里插入图片描述

复制Primary admin keys中内容到.env中的SEARCH_KEY变量中。
修改其中SEARCH_INDEX为pocindex

在这里插入图片描述

修改你的运行文件PoC.py内容为如下:

import os
import requests
from dotenv import load_dotenv
import utils
from openai import  AzureOpenAI
import asyncio
from openai import AsyncAzureOpenAI
# Add OpenAI import. (Add code here)


def main():
    try:
        load_dotenv()
        utils.initLogFile()
        azure_oai_endpoint = os.getenv("AZURE_OAI_ENDPOINT")
        azure_oai_key = os.getenv("AZURE_OAI_KEY")
        azure_oai_model = os.getenv("AZURE_OAI_MODEL")

        # Define Azure OpenAI client (Add code here)##################
        client = AzureOpenAI(
            azure_endpoint=azure_oai_endpoint,
            api_key=azure_oai_key,
            api_version="2024-02-15-preview"
        )
        function_map = {
            "1": function1,
            "2": function2,
            "3": function3,
            "4": function4
        }

        while True:
            print('1: Validate PoC\n' +
                  '2: Company chatbot\n' +
                  '3: Developer tasks\n' +
                  '4: Use company data\n' +
                  '\'quit\' to exit the program\n')
            command = input('Enter a number:')
            if command.strip() in function_map:
                function_map[command](client, azure_oai_model)
            elif command.strip().lower() == 'quit':
                print('Exiting program...')
                break
            else:
                print("Invalid input. Please enter number 1, 2, 3, 4, or 5.")

    except Exception as ex:
        print(ex)


# Task 1: Validate PoC
def function1(aiClient, aiModel):
    inputText = utils.getPromptInput("Task 1: Validate PoC", "sample-text.txt")

    # Build messages to send to Azure OpenAI model. (Add code here)
    messages = [
        {"role": "user", "content": inputText}
    ]

    # Define argument list (Add code here)
    apiParams = {
        "messages": messages,
    }

    utils.writeLog("API Parameters:\n", apiParams)

    # Call chat completion connection. (Add code here)

    # Use the call name and **apiParams to reference our argument list
    response = aiClient.chat.completions.create(
        model=aiModel,
        messages=messages,
        temperature=0.7,
        max_tokens=800
    )

    utils.writeLog("Response:\n", str(response))
    print("Response: " + response.choices[0].message.content + "\n")
    return response


# Task 2: Company chatbot
def function2(aiClient, aiModel):
    inputText = utils.getPromptInput("Task 2: Company chatbot", "sample-text.txt")
    messages = [
        {"role": "system", "content": "You are a helpful assistant that responds in a casual tone. Each response should end with 'Hope that helps! Thanks for using Contoso, Ltd.'"},
        {"role": "user", "content": "What is the best way to find if a company is hiring?"},
        {"role": "assistant", "content": "You can start by checking the company's website, especially the careers section. Hope that helps! Thanks for using Contoso, Ltd."},
        {"role": "user", "content": inputText}
    ]
    
    # Define argument list (Add code here)
    apiParams = {
        "model": aiModel,
        "messages": messages,
        "max_tokens": 1000,
        "temperature": 0.5
    }

    utils.writeLog("API Parameters:\n", apiParams)

    # Call chat completion connection. (Add code here)
    # Use the call name and **apiParams to reference our argument list
    response = aiClient.chat.completions.create(**apiParams)
    

    utils.writeLog("Response:\n", str(response))
    print("Response: " + response.choices[0].message.content + "\n")
    return response

# Task 3: Developer tasks
def function3(aiClient, aiModel):
    inputText = utils.getPromptInput("Task 3: Developer tasks", "sample-text.txt")
    
    # Define system message based on the task
    
    system_message = "Take the legacy code in legacyCode.py, add comments and generate documentation."

    messages = [
        {"role": "system", "content": system_message},
        {"role": "user", "content": inputText}
    ]

    # Define argument list (Add code here)
    apiParams = {
        "model": aiModel,
        "messages": messages,
        "max_tokens": 1000,
        "temperature": 0.5
    }
    
    utils.writeLog("API Parameters:\n", apiParams)

    # Call chat completion connection. (Add code here)
    # Use the call name and **apiParams to reference our argument list
    response = aiClient.chat.completions.create(**apiParams)

    
    utils.writeLog("Response:\n", str(response))
    print("Response: " + response.choices[0].message.content + "\n")
    return response 

# Task 4: Use company data
def function4(aiClient, aiModel):
    inputText = utils.getPromptInput("Task 4: Use company data", "sample-text.txt")
    
    # Build messages to send to Azure OpenAI model. (Add code here)
    search_endpoint = os.getenv("SEARCH_ENDPOINT")
    search_key = os.getenv("SEARCH_KEY")
    search_index = os.getenv("SEARCH_INDEX")
    
    # Perform the search query
    search_query = {
        "search": inputText,
        "searchMode": "any",
        "queryType": "simple",
        "top": 5  # Adjust the number of results as needed
    }

    headers = {
        "Content-Type": "application/json",
        "api-key": search_key
    }

    search_url = f"{search_endpoint}/indexes/{search_index}/docs/search?api-version=2021-04-30-Preview"
    
    response = requests.post(search_url, headers=headers, json=search_query)
    search_results = response.json()
    
    # Extract relevant search results
    search_snippets = [result["content"] for result in search_results["value"]]
    search_content = "\n\n".join(search_snippets)
    
    # Build messages for OpenAI with search results included
    messages = [
        {"role": "system", "content": "You are a helpful travel agent."},
        {"role": "user", "content": f"{inputText}\n\nSearch Results:\n{search_content}"}
    ]

    # Define connection and argument list (Add code here)
    apiParams = {
        "model": aiModel,
        "messages": messages,
        "max_tokens": 1000,
        "temperature": 0.5
    }
    
    utils.writeLog("API Parameters:\n", apiParams)

    # Call chat completion connection. Will be the same as function1 (Add code here)
    # Use the call name and **apiParams to reference our argument list
    response = aiClient.chat.completions.create(**apiParams)

    utils.writeLog("Response:\n", str(response))
    print("Response: " + response.choices[0].message.content + "\n")
    return

# Call main function. Do not modify.
if __name__ == '__main__': 
    main()

进入cmd

在这里插入图片描述

通过Validate PoC/Company chatbot/Developer tasks/Use company date来测试嵌入的chatgpt功能。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1821850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink作业执行之 2.算子 StreamOperator

Flink作业执行之 2.算子 StreamOperator 前文介绍了Transformation创建过程&#xff0c;大多数情况下通过UDF完成DataStream转换中&#xff0c;生成的Transformation实例中&#xff0c;核心逻辑是封装了SimpleOperatorFactory实例。 UDF场景下&#xff0c;DataStream到Transf…

免费听歌,电脑或手机免费听歌,落雪音乐安装详细步骤

近年来&#xff0c;由于资本的力量导致各种收费&#xff0c;看个电视想听歌都必须要付费了&#xff0c;否则你听不完整&#xff0c;吃相非常难看&#xff0c;特别是电视&#xff0c;吸血鬼式吸收各种会员费&#xff0c;各种APP也是铺天盖地的广告&#xff0c;渐渐迷失了自我&am…

【C++】AVL树/红黑树实现及map与set的封装

前言 【C】二叉树进阶&#xff08;二叉搜索树&#xff09; 这篇文章讲述了关于二叉搜索树知识&#xff0c;但是二叉搜索树有其自身的缺陷&#xff0c;假如往树中插入的元素有序或者接近有序&#xff0c;二叉搜索树就会退化成单支树&#xff0c;时间复杂度会退化成O(N)&#xff…

CCAA质量管理体系 (2021)

一、考试大纲 中 国 认 证 认 可 协 会 质量管理体系基础考试大纲 第 1 版 文件编号&#xff1a;CCAA-TR-105-01:2021 发布日期&#xff1a;2021 年 3 月 2 日 实施日期&#xff1a;2021 年 4 月 1 日 质量管理体系基础考试大纲&#xff08;第 1 版&#xff09;1.总则 本大…

大数据集成平台建设方案(Word方案)

基础支撑平台主要承担系统总体架构与各个应用子系统的交互&#xff0c;第三方系统与总体架构的交互。需要满足内部业务在该平台的基础上&#xff0c;实现平台对于子系统的可扩展性。基于以上分析对基础支撑平台&#xff0c;提出了以下要求&#xff1a; 基于平台的基础架构&…

LabVIEW电源适应能力检测系统

随着工业自动化程度的提高&#xff0c;电源质量直接影响设备的稳定运行。利用LabVIEW开发一个单相电源适应能力检测系统&#xff0c;该系统通过智能化和自动化测试&#xff0c;提高了测试效率&#xff0c;减少了人为错误&#xff0c;保证了电源质量的可靠性。 项目背景 在现代…

软件系统设计开发规程(Word文件)

技术解决方案过程包括&#xff1a; 1、选择最佳解决方案&#xff1b; 2、制定架构设计&#xff1b; 3、制定概要设计&#xff1b; 4、制定详细设计和数据库设计&#xff1b; 5、利用准则进行接口设计&#xff1b; 6、实现设计&#xff1b; 7、进行单元测试&#xff1b; 8、进行…

知乎社招1年Go开发123+HR面经,期望22k

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 一面‍ 0、自我介绍 1、你才工作一年为什么就想找机会了&#xff08;为什么想跳&#xff09;【甩锅给公司&#xff0c;反正不是我的问题】 2、对…

boot项目配置邮箱发送

最近项目准备进入测试阶段&#xff0c;时间相对充沛些&#xff0c;便对邮箱的信息发送记录下&#xff01; 邮箱设置-开启smtp协议及获取授权码 以QQ邮箱为例&#xff0c;其他邮箱大同小异&#xff01; 开启协议 获取授权码 具体代码 基于javax.mail实现 原文可看 前辈帖子…

达梦数据库上市,给数据库国产化加油打气

吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;《Java代码审计》《Web安全》《应急响应》《护网资料库》《网安面试指南》还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 作为家乡的企业上市必须…

蓝牙音频解码芯片TD5163介绍,支持红外遥控—拓达半导体

蓝牙芯片TD5163A是一颗支持红外遥控、FM功能和IIS音频输出的蓝牙音频解码芯片&#xff0c;此颗芯片的亮点在于同时支持真立体声&单声道、TWS功能、PWM、音乐频谱和串口AT指令控制等功能&#xff0c;芯片在支持蓝牙无损音乐播放的同时&#xff0c;还支持简单明了的串口发送A…

RTE Open Day 联手 AGI Playground,最先锋 RTE+AI Builders 齐聚北京丨活动招募

6 月 22 日至 23 日&#xff0c;北京&#xff0c;AGI Playground 2024 即将引燃今夏&#xff01; 这场备受瞩目的 AGI 盛会&#xff0c;将汇聚王小川、杨植麟等众多创业者。RTE 开发者社区的 Builders 和 RTE Open Day 也将亮相其中&#xff01; 「有一群人在一起&#xff0c…

云计算【第一阶段(13)】Linux的Find命令

一、查找文件或目录Find 格式 find 查找的范围 类型 查找数据 1.1、常用查找类型 查找类型关键字说明按名称查找-name根据目标文件的名称进行查找&#xff0c;允许使用“*”及“&#xff1f;”通配符按文件大小查找-size根据目标文件的大小进行查找&#xff0c;一般使用…

[华为北向网管NCE开发教程(6)消息订阅

1.作用 之前介绍的都是我们向网管NCE发起请求获取数据&#xff0c;消息订阅则反过来&#xff0c;是网管NCE系统给我们推送信息。其原理和MQ&#xff0c;JMS这些差不多&#xff0c;这里不过多累述。 2.场景 所支持订阅的场景有如下&#xff0c;以告警通知为例&#xff0c;当我…

基于51单片机智能路灯控制—人数、光强

基于51单片机智能路灯控制 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.光敏电阻与电阻组成分压电路&#xff0c;环境光强度越强&#xff0c;光敏电阻越小&#xff0c;ADC检测的电压越强&#xff1b; 2.红外计数传感器&#xff08;按键…

Introducing Index-1.9B

简介 大家好&#xff0c;今天我们很高兴首次发布Index系列模型中的轻量版本&#xff1a;Index-1.9B系列 本次开源的Index-1.9B 系列包含以下模型&#xff1a; Index-1.9B base : 基座模型&#xff0c;具有 19亿 非词嵌入参数量&#xff0c;在2.8T 中英文为主的语料上预训练&…

01本地图像导入及参数设置

左边工具栏&#xff1a;采集-》图像源&#xff0c;点击后 拉到流程窗口中 在右边有三个按钮可以添加图像和图像文件夹。 双击 图像源 可以打开 参数设置 参数说明&#xff1a; 像素格式&#xff1a;MONO8 表示图像为黑白图像&#xff0c;RGB24为彩色图像。看你想以什么图像打开…

JS手写题解析

手写Promise class MyPromise {constructor(executor) { // executor执行器this.status pending // 等待状态this.value null // 成功或失败的参数this.fulfilledCallbacks [] // 成功的函数队列this.rejectedCallbacks [] // 失败的函数队列const that thisfunction reso…

我的创作纪念日 --- 携手CSDN的512天

起航 时间过得可真快啊&#xff0c;转眼间距离我发的第一篇文章已经有512天了&#xff0c;那是一个寒假&#xff0c;当我发现自己又浑浑噩噩的过完了一个学期时&#xff0c;我才开始思考自己想拥有怎样的人生&#xff0c;然后我就写下了自己的第一篇文章 about me&#xff0c;…

哪些国产项目管理软件最受欢迎?详细解读六大主流系统

满足国产化诉求的6款项目管理系统&#xff1a;PingCode、Worktile、Teambition、禅道、华为云DevCloud、Tapd。 国产项目管理软件以其定制化高、适应本土市场的优势&#xff0c;正成为越来越多企业的选择。本文将探讨几款优秀的国产项目管理工具&#xff0c;帮助您找到提升团队…