首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

谁说 AI 编程工具缺乏记忆和联想能力,简单琐碎的需求完全可以交给它

今年算是 AI 正式破圈的一年,无数的工具,产品横空出世。无论在面向企业的大语言模型,还是帮助个人的 AI 工具,数不胜数。其中关于 AI 编程助手领域,近年来也涌现了很多不错的产品,例如 Copilot, Cursor, 还是我们今天要体验的 CodeWhisperer。已经在潜移默化中改变了程序员们的生产和解决问题的方式,传统解决问题往往依靠的是谷歌等搜索引擎,找到对应的官网和知名的论坛查找问题。而如今,我们仅仅依靠 AI 编程助手就能解决很多问题。

回到 CodeWhisperer 上来,它的出生还是带着许多光环的。首先来自著名的大厂 Amazon, 他们在 AI 领域有足够多的积累,在面向开发者方面有足够多的经验和产品用户体验来反馈用户感受,不断迭代相关产品,而且还有一个相当强大的优势,借助亚马逊云的力量,能够将 AI 和云打通,这在当前云原生时代是必不可少的能力。

目标及前期准备

先给大家讲讲今天我们希望实现的目标,基于 Spring Boot 框架,简单实现用户登陆,。我们使用的是 IntelliJ 开发工具,选用 Maven 进行管理依赖管理,用到的依赖如下。

  • Web
  • JPA
  • H2

我们首先尝试安装 CodeWhisperer 插件,在 Plugins 中搜索 AWS Toolkit 下载即可。

下载完成后绑定自己的亚马逊账号即可开始使用,默认开启自动建议。

项目结构如图所示

pom.xml 文件如下

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>demo</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

开始

  • 健康检查

我们先实现一个最简单的 Controller,请求 /ping 返回 pong 即可。

代码语言:javascript
复制
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class PingController {

    @RequestMapping("/ping")
    public @ResponseBody String greeting() {
        return "pong";
    }

}

测试用例是检验代码正确性必不可少的一环,我们来写个简单的测试用例。这时 CodeWhisperer 已经开始展示它的实力了,只是写了一行 @Test 注解,它将我们想要做的测试代码完整提示出来。

下面是完整的测试代码。

代码语言:javascript
复制
package com.example.demo;

import com.example.demo.controller.PingController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@AutoConfigureMockMvc
@WebMvcTest(PingController.class)
public class TestWebApplication {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/ping")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string("pong"));
    }
}

运行一下测试用例,很顺利的通过🎉。

  • 用户类设计

我们来定一个 User 模型,发现它在 Table To Class 的实现中具备一定的表设计能力,以及字段关联联想,约束设计能力。

能推测我想要的表字段,索引约束建议。这对于新手来说是莫大的帮助,想象有一位功力深厚的同伴在旁指点你设计表结构,那么表结构的设计就能相对合理一些。

代码语言:javascript
复制
package com.example.demo.model;


import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.stereotype.Indexed;

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "game_users")
public class User {
    @Id
    private Long id;
    @Column(unique = true, nullable = false)
    private String username;
    @Column(nullable = false, length = 64)
    private String password;
    @Column(unique = true, nullable = false)
    private String email;
}

  • DAO 层实现

这时我灵光一现,根据官网的 GIF 图展示,可以通过注释进行代码推断,那好,DAO 层的实现就交给它啦。

哎哟,不错哦,根据我上面想要根据邮箱查询用户的注视,它已经给出了相应的提示,让我们再考考它,注释中进行多个方法的描述。

挺聪明呀,也很顺利的实现了。

代码语言:javascript
复制
package com.example.demo.dao;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserDao extends JpaRepository<User, Long> {
    // function to implement find user by email
    Optional<User> findByEmail(String email);

    Optional<User> findByUsername(String username);

    // two function to implement find user by id or email
    Optional<User> findById(Long id);
    Optional<User> findByEmailIgnoreCase(String email);

    // function to implement check user is existed
    Boolean existsByEmail(String email);

}

看来以后 CRUDDAO 层实现可以交给它来完成啦。我们希望能够预先插入一些数据便于测试,琐碎的日志测试对它来说轻轻松松。

代码语言:javascript
复制
package com.example.demo;

import com.example.demo.dao.UserDao;
import com.example.demo.model.User;
import org.slf4j.Logger;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class LoadDatabase {
    public static final Logger log = org.slf4j.LoggerFactory.getLogger(LoadDatabase.class);

    // this is Bean is loaded once in the application context
    // it is used to load the data into the database
    @Bean
    public CommandLineRunner initDatabase(UserDao dao) {
        return args -> {
            log.info("Preloading " + dao.save(new User(1L, "test1", "111111", "abc@gmail.com")));
            log.info("Preloading " + dao.save(new User(2L, "test2", "222222", "123@gmail.com")));
        };
    }
}

  • Service 层实现

轮到 Service 层了,让我们看看它的表现,在这里我们简单的根据用户名查询用户,返回对应的数据即可。当我方法签名写一半时,它给我的建议让我停下继续敲击的手指,因为基本符合我的预期,而且具备一定的记忆联想能力,在 DAO 层定义的 Optional<User>,这里也能找到对应的方法进行处理。

代码语言:javascript
复制
package com.example.demo.service;

import com.example.demo.dao.UserDao;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserDetailServiceImpl {
    private final UserDao userdao;

    @Autowired
    public UserDetailServiceImpl(UserDao userdao) {
        this.userdao = userdao;
    }

    public User getUserByUsername(String username) throws Exception {
        Optional<User> user = userdao.findByUsername(username);
        if (user.isPresent()) {
            return user.get();
        } else {
            throw new Exception("User not found");
        }
    }
}

  • Controller 层实现

最后我们来实现最外层入口,简单的进行相关业务校验,用户名是否为空,密码是否正确,在这里用于演示。

用户不存在相关处理,密码正确性验证,基本符合我们的要求。

代码语言:javascript
复制
package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserDetailServiceImpl;
import org.apache.coyote.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class UserController {
    private final UserDetailServiceImpl userDetailService;

    @Autowired
    public UserController(UserDetailServiceImpl userDetailService) {
        this.userDetailService = userDetailService;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user) {
        try {
            if (user.getUsername().isEmpty()) {
                return ResponseEntity.badRequest().body("user name is empty");
            }

            User res;
            res = userDetailService.getUserByUsername(user.getUsername());
            if (res == null) {
                return ResponseEntity.badRequest().body("user not  found");
            }

            if (res.getPassword().equals(user.getPassword())) {
                return ResponseEntity.ok(res);
            }
            return new ResponseEntity<>("user password invalid", HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            return ResponseEntity.notFound().build();
        }
    }
}

最后我们来测试一下,格式错误和用户密码错误的情况。

与预期一致,撒花。

总结

CodeWhisperer 就我今天的使用而言,还是有些出乎我的意料,之前的一些 AI 编程工具并不具备记忆和联想能力,今天 CodeWhisperer 展示的记忆联想效果不错,并且具备一定的表结构设计能力,一些简单的测试用例完成度也不错,我想未来一些简单琐碎的需求,测试用例也可以交给它了。但是今天在体验的过程中还是发现了一些不足,插件 UI 会出现挡住建议的情况,这样我需要再次触发建议才行,目前阶段可以使用它来投入生产,在一些复杂的场景还是需要谨慎,会出现胡言乱语的情况,跟上下文关联性不强的建议。

当然,这些问题相信随着模型的数据量级和质量不断优化能够慢慢解决🎉。

版权声明: 本文为 InfoQ 作者【天黑黑】的原创文章。

原文链接:【https://xie.infoq.cn/article/179127e04fff483aac667444d】。文章转载请联系作者。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/yiLUmJIFFwVKkBmguIpu
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券