提高您的AI助手的生产力。
最近,我开始尝试使用不同的AI助手来发现更多利用这些工具的可能性,而且我很惊喜地了解到了一些我以前不知道的东西。
GitHub Copilot被认为能够了解您项目的上下文。这在某种程度上是正确的。它能理解您项目的堆栈和所使用的库。但是,上下文并不完整;它不能详细理解它。根据GitHub上的这一讨论,我们现在了解了嵌入在背后生成提示的基本逻辑(从VS Code插件到LLM)。
Copilot从代码编辑器中获取上下文信息。它不需要访问您的存储库。发送给这个模型的代码上下文通常被称为“提示”。我们不断尝试新的方法来创建提示,以便从模型中获取更高质量的建议。
这就是为什么AI助手并不能始终提供一成不变的答案。今天它可能会提出精彩的建议,但明天可能会产生一些奇怪的建议。
举个例子,我们今天使用的一种技术是在相邻的文件(即在代码编辑器中打开的选项卡)中寻找相关的代码。想象一下,你打开了两个文件 —— 一个定义了一个类,另一个是该类的单元测试。通过同时查看这两个文件,我们可以更多地了解相关代码的上下文,这将导致更高质量的建议。
是的,没错,但正如我所说,上下文还不完整。让我们尝试用一个简单的例子来重现它。假设我们有一个经典的NestJS应用程序,其中有一个名为“Tasks”的模块。当然,我们有一个负责数据存储的TasksService,并且设计用于TasksController使用。
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './task.entity';
import { CreateTaskDto } from './dto/create-task.dto';
@Injectable()
export class TasksService {
constructor(
@InjectRepository(Task)
private taskRepository: Repository<Task>,
) {}
async create(createTaskDto: CreateTaskDto): Promise<Task> {
const task = this.taskRepository.create(createTaskDto);
await this.taskRepository.save(task);
return task;
}
async findAll(): Promise<Task[]> {
return this.taskRepository.find();
}
async findOne(id: number): Promise<Task> {
return this.taskRepository.findOne(id);
}
async update(id: number, updateTaskDto: CreateTaskDto): Promise<Task> {
const task = await this.taskRepository.findOne(id);
if (!task) {
return null;
}
Object.assign(task, updateTaskDto);
await this.taskRepository.save(task);
return task;
}
async remove(id: number): Promise<void> {
const task = await this.taskRepository.findOne(id);
if (!task) {
return;
}
await this.taskRepository.remove(task);
}
}
现在,我想要创建一个控制器,以下是我的聊天提示:
基于TasksService,实现TasksController和CRUD方法。
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { Task } from './task.entity';
@Controller('tasks')
export class TasksController {
constructor(private tasksService: TasksService) {}
@Get()
async findAll(): Promise<Task[]> {
return this.tasksService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string): Promise<Task> {
return this.tasksService.findOne(id);
}
@Post()
async create(@Body() task: Task): Promise<Task> {
return this.tasksService.create(task);
}
@Put(':id')
async update(@Param('id') id: string, @Body() task: Task): Promise<Task> {
return this.tasksService.update(id, task);
}
@Delete(':id')
async delete(@Param('id') id: string): Promise<void> {
return this.tasksService.delete(id);
}
}
好的,很酷。乍一看,一切似乎都很好。除了一些问题,这段代码是不兼容的,也没有计算TasksService类的方法。
❌ TasksService没有“delete”方法,但有“remove”方法。❌ id参数是字符串,但TasksService需要数字类型。❌ 它没有计算CreateTaskDto,这在项目中也存在。
当然,我可以手动修复它;看起来似乎没有问题。但是如果我付费使用这项服务,我希望能从中获得最大的价值。
在这里,我之前不知道的“工作区”功能派上了用场。在提示符中添加“@workspace”命令,你会得到魔法:
➡️ @workspace 基于TasksService实现TasksController的CRUD方法
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';
@Controller('tasks')
export class TasksController {
constructor(private readonly tasksService: TasksService) {}
@Post()
async create(@Body() createTaskDto: CreateTaskDto) {
return this.tasksService.create(createTaskDto);
}
@Get()
async findAll() {
return this.tasksService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: number) {
return this.tasksService.findOne(id);
}
@Put(':id')
async update(@Param('id') id: number, @Body() updateTaskDto: CreateTaskDto) {
return this.tasksService.update(id, updateTaskDto);
}
@Delete(':id')
async remove(@Param('id') id: number) {
return this.tasksService.remove(id);
}
}
最终,这就是我想要的 - 使用我的项目的背景和现有的类,并在保存文件后立即运行的兼容代码。
✅ TasksService调用“remove”方法。 ✅ id参数是数字类型。 ✅ CreateTaskDto的使用。
顺便说一下,该插件会指示有哪些文件被用于上下文。LLM拥有越多的上下文,结果将会越好。
我不明白为什么默认情况下不使用“@workspace”前缀的功能。我更倾向于反向使用它:在非项目上下文的问题中使用“@everywhere”,并在日常编码例程中不使用前缀。
但是答案可能相当琐碎 —— 它更便宜。对于每个“提示”,使用额外的上下文可以极大地增加服务器负载,并使得微软付出巨大代价。并且在使用此功能时要注意 —— 对于与您的项目无关的问题,使用“@workspace”没有意义 —— 响应会更快且更广泛,因为有时候超越项目的范围去思考会更好 ;)