# 继GitHub的Copilot收费后，亚马逊推出了 CodeWhisperer，感觉不错哟！


Copilot 是 Github 推出的一款人工智能编程助手，推出仅一年就受到大量开发者的追捧（据官方统计有 120 万用户）。然而，自 2022 年 6 月起，它改为了付费订阅模式（每月 10 美元或每年 100 美元）。

我们暂且不讨论训练代码可能涉及的版权及授权许可问题，可以肯定的是，利用机器学习训练出智能编程 AI 模型，这会是未来的大势所趋！

巧合的是，仅在 Copilot 宣布收费的几天后，Amazon 就推出了一款竞品 CodeWhisperer！相信在不久的将来，类似的产品会如雨后春笋般涌现，到那时，程序员和编程学习者们就更加有福了！

> 作者：Brian Tarbox
>
> 译者：豌豆花下猫@Python猫
>
> 英文：https://blog.symops.com/2022/08/31/amazon-codewhisperer
>
> 转载请保留作者&译者&来源信息

代码补全最早出现在 1985 年的一个[名为 Alice 的 Pascal 编辑器](https://www.templetons.com/brad/alice.html)上。它支持自动缩进、自动补全 BEGIN/END 控制结构，甚至支持语法着色。

争议也随之而来：在 Alice 的早期，人们担心代码补全[使得编写软件过于简单](https://www.reddit.com/r/learnprogramming/comments/99njwv/is_the_use_of_ides_good_or_bad_for_absolute/)。但它实际上只是一个语法助手。

代码补全可以帮你写出语法正确的、可编译的代码，但它不能帮你写出语义正确的代码，甚至不能写出任何有用的代码。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680688959788/d4cc7120-ff5b-48cc-9cb1-58c870d3588b.gif)

GitHub 的 [CoPilot](https://github.com/features/copilot) 和 Amazon 的 [CodeWhisperer](https://aws.amazon.com/codewhisperer/) 改变了这一点，它们除了提供语法辅助，还能生成语义上正确的代码。它们不仅能提供 if 语句的大纲，还能创建出完整的代码样例。

但在 2022 年，一个代码辅助工具到底能好到什么程度呢？

本文将重点介绍 CodeWhisperer，尝试回答这个问题。

## 试用：用 Python 从 S3 读取数据

亚马逊在 2022 年 6 月发布了 CodeWhisperer 预览版，现在它支持 Python、Java 和 JavaScript。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680688963499/bc0822de-4d0a-49e8-a4ff-f7f00788ee62.jpeg)

> Python猫注：截至2022年9月17日，这个服务还未全面开放。
>
> 若要试用，可在官网申请：https://pages.awscloud.com/codewhisperer-sign-up-form.html
>
> 附官方介绍：https://aws.amazon.com/cn/blogs/compute/introducing-amazon-codewhisperer-in-the-aws-lambda-console-in-preview

在 AWS 博客的[一篇文章](https://aws.amazon.com/blogs/compute/introducing-amazon-codewhisperer-in-the-aws-lambda-console-in-preview/)中，Mark Richman 解释说，CodeWhisperer 的模型是在“包括 Amazon 开源代码在内的各种数据源”上训练的。有了这个语料库（显然[确实存在](https://www.techrepublic.com/article/aws-getting-better-at-open-source/)）完善 CodeWhisperer 的模型，编写从 S3 读取文件的代码应该是一个很好的测试用例。

在使用 CodeWhisperer（CW）时，你需要写一个注释，描述你希望函数去做什么。注释的描述性和准确性越高，系统就越能更好地推断出你想要的逻辑。

```Python
Function to open an S3 file
```
注释以 Function 开头，让 CW 知道你想要创建一个函数。也就是说，你需要添加一个注释，作为给 CW 的提示。

CW 分析注释并生成一个函数的定义。此时，你可以在生成函数体之前修改函数定义。CW 还可能提供多种函数定义供你选择。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680688966760/8d19df6a-dae8-455b-b96b-6c36396b7f8b.jpeg)

IntelliJ 集成 CodeWhisperer 的截图

点击“插入代码”，你的函数就在注释的下方创建好了。注意 CodeWhisperer 不仅插入了代码，还创建了一个文档字符串。

```Python
# Function to open an S3 file
def open_s3_file(filename):
    """
    :param filename:
    :return:
    """
    s3 = boto3.resource('s3')
    return s3.Object(bucket, filename).get()['Body'].read()
```

看起来不错！这段代码实现了你的注释所期望的功能，并且是在几秒钟内就生成了。节省了查找`boto3` API 的时间，你只需要检查代码，确保语义正确。

接着看看提出更多要求时，会发生什么。

这有一个很有用的例子：写一个函数从 S3 的文件中返回前“n”行。

```Python
# Function to get first n lines from a file in S3
def get_first_n_lines_from_s3(filename, n):
    """
    Get the first n lines of a file in S3
    :param filename:
    :param n:
    :return:
    """
    lines = []
    for line in open_s3_file(filename):
        lines.append(line)
        if len(lines) >= n:
            break
    return lines
```

真厉害！CodeWhisperer 使用了之前创建的辅助方法 open_s3_file，并生成了正确的函数。

在 CW 生成函数体之前，你可以修改参数名，提高代码的可读性。例如，如果我们输入：

```python
# Function to find common elements in lists
```
CW 会提示：
```Python
def find_common_elements(list1, list2)
```

在生成函数体之前，我们可以修改函数定义，使其更具描述性：

```Python
def find_common_elements(first, second)
```

如果我们接受它的函数定义，CodeWhisperer 将使用函数签名和注释来构建函数体。得到的函数将使用改进后的参数名。

```Python
# Function to find common elements in lists
def find_common_elements(first, second):
        return list(set(first) & set(second))
```

CodeWhisperer 不仅仅可以创建整个函数；它还可以在函数中插入代码片段，并在过程中推断相关变量。

```Python
  # reverse the list order
  lines.reverse()
```

## CodeWhisperer 是一个重要的生产力助推器

使用 CodeWhisperer，我能够更快地编写代码。到目前为止，最大的价值是 S3 示例所演示的功能。

如果我自己写代码，大部分时间将会用来查找 boto API 文档，以确保写出正确的调用。使用 CodeWhisperer，则只需三秒钟。

这让我想到了一段曾花了很多时间的烦人代码：给 CloudWatch 发送数据。让我们看看 CodeWhisperer 能否有所帮助，试试提供注释：“Function to emit a CloudWatch metric”。

```Python
# Function to emit a cloudwatch metric
def put_metric_data(metric_name, metric_data):
    """
    Put a metric data into cloudwatch
    :param metric_name:
    :param metric_data:
    :return:
    """
    cloudwatch = boto3.client('cloudwatch')
    cloudwatch.put_metric_data(
        Namespace='my_namespace',
        MetricData=[
            {
                'MetricName': metric_name,
                'Dimensions': [
                    {
                        'Name': 'string',
                        'Value': 'string'
                    },
                ],
                'Unit': 'Seconds',
                'Value': metric_data
            },
        ]
    )
```

非常有用！CW 为我节省了许多打字和查阅 API 文档的时间。

我有可能需要重构这段代码，但即使我从零开始编写它，第一步也要写出类似这样的原始逻辑。如今只花三秒钟，我节省了编写大量样板代码的时间，有了一个很好的自定义或重构的起点。

## 那么 CodeWhisperer 是一个比我更好的程序员吗?

尽管有一丝标题党，但 CW 的代码是否比我的好，这并不重要。重要的是，它可以为我节省大量的时间和精力，让我能够专注于改进、重构和测试。

**它通过承担一些同质化的繁重工作，让我有机会成为一个更好的程序员。**

上面的例子是 Amazon 工具（经过 Amazon 开源代码训练）能够表现出色的例子。当然，在大多数开发人员需要花费很多时间的地方，比如编写领域相关的逻辑时，CW 也许不会非常有用。让我们看看 CW 会出什么问题。

我们可以从 Python 文档中的数据类示例开始。

```Python
@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand
```
我想知道 CodeWhisperer 是否可以向这个类添加一个方法。让我们看看如果添加注释：" Function that return this item costs more than $10"，会发生什么？
```Python
@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

    # Function that returns whether this item costs more than $10
    def expensive(self) -> bool:
        return self.unit_price > 10
```

非常酷。值得注意的是，CW 给函数起了一个直观的名字，并包含了对 self 的引用。

接着，让我们尝试用 CW 来做测试，看是否会触及它的极限。

```Python
# Function to test InventoryItem class
def test_inventory_item():
     """
    Test InventoryItem class
    :return:
    """
    item = InventoryItem("Widget", 10, 5)
    assert item.name == "Widget"
    assert item.unit_price == 10
    assert item.quantity_on_hand == 5
    assert item.total_cost() == 50
    assert not item.expensive()
```
🤯哇！在上面的代码中，我输入了注释，CW 自动完成了剩下的工作。

测试似乎是一个极好的证明 CW 可以节省时间的例子。我不需要浪费时间去想测试的值，也不用输入所有的成员变量和方法。

## CodeWhisperer 的局限性

它现在还处于初级阶段，还有很多问题。

研究人员发现，GitHub CoPilot 生成的代码[有 40% 存在安全漏洞](https://cyber.nyu.edu/2021/10/15/ccs-researchers-find-github-copilot-generates-vulnerable-code-40-of-the-time/)。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680688969507/820f82d9-4a3c-4d1c-bc4a-2c717d2ea8c5.jpeg)

CodeWhisperer 还没有这类的统计数据，但 AWS 似乎强调了对安全性的关注。

据我测试，在一些例子中，CW 生成的函数有 bug，或者结果没有符合我的意图。

下方的例子应该返回两个文件中最长的公共行，但它只返回了第一个匹配的行：

```Python
# Function to find the longest common line in two files
def find_longest_common_line(file1, file2):
    """
    Find the longest common line in two files
    :param file1:
    :param file2:
    :return:
    """
    with open(file1, 'r') as f1:
        with open(file2, 'r') as f2:
            for line in f1:
                if line in f2:
                    return line
```
CW 还出现了其它问题，原因是它没有足够的上下文来理解我的意图。经过反思，我觉得如果周围的代码结构很好的话，也是可以实现的。

如果你在设计代码时用了准确表示领域的名词，那么，在给出了良好注释的前提下，很容易想象 CW 能够创建出特定于领域的逻辑。至于 bug，将来肯定会得到改善的。

## 写在最后

如果你尝试使用 CW，它可能会让你想象：可能有一天，有人会写出历史上最后一行由人类编写的代码。

在那之前，CW 可以帮助你成为一个更好的程序员，这样即使世界上最后一个程序员是你，人类的最后一行代码也不会有 bug。



