Github Copilot Chat 初體驗

最近因爲陽了的緣故一直躺在牀上。今天終於從牀上爬起來了。不是因爲好透了,而是因爲我收到了申請Copilot Chat preview 權限通過的郵件。實在忍不住,於是起牀開電腦在咳嗽聲中進行了一番體驗。

如何申請

https://github.com/github-copilot/chat_waitlist_signup/join

安裝插件

申請通過後,郵件上就有說明。插件有2個,一個是基於 VSCode 的,一個是基於 Visual Studio 的。今天使用的是 Visual Studio 版本。
在 Marketplace 搜索 Visual Studio Extension for GitHub Copilot Preview 下載安裝重啓 VS 即可。

使用

在安裝完插件後,隨便打開一個項目,隨便選擇一個類,右鍵就有"詢問 Copilot" 菜單:

點擊菜單後,下面就會出現一個對話框:

用戶可以在對話框進行提問

同時在側邊欄也會多出一個 Github Copilot chat 的 tab。

對話/問答

Copilot 的基本能力,比如寫單元測試等,其實在以前的文章已經演示過了,這次不在重複。既然新版叫 Copilot Chat 那麼肯定是跟 ChatGPT 一樣是問答模式了。下面就來跟 Copilot 嘮嘮嗑。

minjie:
Copilot Chat 是什麼?
Copilot:

Copilot Chat是 Microsoft的GitHub Copilot AI的一種特性,允許開發人員使用文本輸入與AI交互,並實時獲得代碼建議和補全。Copilot Chat是一種旨在通過提供基於檢測到的代碼模式的建議來增強開發人員的生產力的工具。Copilot Chat使用機器學習算法識別模式併爲各種編程活動建議代碼片段。它還具有學習上一個用戶交互的能力,隨着時間的推移將變得更加準確。如果您需要使用Copilot Chat獲取幫助進行開發工作,請在有具體問題的情況下描述清晰問題的場景與具體需求,以便我更好地幫助您。

minjie:
請問 c# int 的最大值是多少
Copilot:

The maximum value of an int in C# is 2,147,483,647 (or 0x7FFFFFFF in hexadecimal). This is because an int in C# is a signed 32-bit integer, meaning it can hold values between -2,147,483,648 and 2,147,483,647 inclusive.
Here's an example of assigning int.MaxValue to a variable in C#:
int maxVal = int.MaxValue;
This creates a new variable called maxVal of type int and assigns it the value of the maximum possible integer.

minjie:
在 dotnet 中一般怎麼建立 websocket 連接,給個示例
Copilot:

using System.Net.WebSockets;
using System.Threading.Tasks;

public async Task ConnectWebSocketAsync(string url) {
    using (ClientWebSocket ws = new ClientWebSocket()) {
        await ws.ConnectAsync(new System.Uri(url), CancellationToken.None);
        await Task.WhenAll(Receive(ws), Send(ws));
    }    
}

private static async Task Receive(ClientWebSocket ws) {
    byte[] buffer = new byte[1024 * 4];
    while (ws.State == WebSocketState.Open) {
        var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        if (result.MessageType == WebSocketMessageType.Close) {
            await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
        } else {
            // Process received data.
        }
    }
}

private static async Task Send(ClientWebSocket ws) {
    while (ws.State == WebSocketState.Open) {
        // Get data to send.
        byte[] buffer = new byte[1024 * 4];
        var result = await ws.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
        await Task.Delay(2000);
    }
}

問答可以說問啥答啥,基本上毫無破綻。

對代碼進行解釋

Copilot Chat 的另外一個能力就是對一段代碼進行解釋
比如讓它對 AgileConfigClient 的核心類 ConfigClient 進行解釋。首先打開 AgileConfigClient 項目,然後選中 ConfigClient 類文件,然後就可以問了。

minjie:
請問 ConfigClient 類是用來幹嘛的?
Copilot:

ConfigClient is a class that allows the management and retrieval of configuration data. The class has methods that load configurations from a server and save them to a local storage. The configuration is typically represented in key/value pairs. The Get method is used to retrieve a particular key, and the GetGroup method is used to retrieve all keys under a common group name.
Here's an example usage of the ConfigClient class:
var config = new ConfigClient();
var result = config.Get("keyName");
This creates a new instance of the ConfigClient object and retrieves the value associated with the specified "keyName" string.

Copilot 準確的做出瞭解釋,只是使用了英文,我讓它使用中文,它不理我。

使用 Copilot Chat 一步步生成一個 To-do List 的 webapi 項目

下面的內容演示瞭如何使用 Copilot chat 一步步通過對話方式生成一個可以運行的項目。在最後我甚至想讓它直接寫個文章,Copilot 也寫出來了,不過反正見過了 ChatGPT 的能力了也不奇怪哈。

minjie:
生成一個ToDo實體類
Copilot:

public class ToDo
{
    public int Id { get; set; }
    public string Task { get; set; }
}

minjie:
添加一個字段 Completed
Copilot:

public class ToDo
{
    public int Id { get; set; }
    public string Task { get; set; }
    public bool Completed { get; set; }
}

minjie:
對這個實體類生產數據庫訪問類,使用 efcore
Copilot:

using Microsoft.EntityFrameworkCore;
using System;

public class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options)
    { }

    public DbSet<MyEntity> MyEntities { get; set; }
}

minjie:
改成 TodoContext
Copilot:

using Microsoft.EntityFrameworkCore;
using System;

public class TodoContext : DbContext
{
    public TodoContext(DbContextOptions<TodoContext> options) : base(options)
    { }

    public DbSet<ToDo> ToDos { get; set; }
}

minjie:
支持 sqlite
Copilot:

using Microsoft.EntityFrameworkCore;
using System;

public class TodoContext : DbContext
{
    public TodoContext(DbContextOptions<TodoContext> options) : base(options)
    { }

    public DbSet<ToDo> ToDos { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite("Data Source=todos.db");
}

minjie:
刪除構造函數
Copilot:

using Microsoft.EntityFrameworkCore;
using System;

public class TodoContext : DbContext
{
    public DbSet<ToDo> ToDos { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite("Data Source=todos.db");
}

minjie:
使用 ToDoContext 生成一個 ToDoService 類,這個類包裝了該刪查方法
Copilot:

public class ToDoService
{
    private readonly TodoContext _context;

    public ToDoService(TodoContext context)
    {
        _context = context;
    }

    public List<ToDo> GetToDos()
    {
        return _context.ToDos.ToList();
    }

    public ToDo GetToDoById(int id)
    {
        return _context.ToDos.Find(id);
    }

    public void AddToDo(ToDo todo)
    {
        _context.ToDos.Add(todo);
        _context.SaveChanges();
    }

    public void UpdateToDoById(int id, ToDo todo)
    {
        var existingTodo = _context.ToDos.Find(id);

        if (existingTodo != null)
        {
            existingTodo.Task = todo.Task;
            existingTodo.Completed = todo.Completed;

            _context.SaveChanges();
        }
    }

    public void RemoveToDoById(int id)
    {
        var existingTodo = _context.ToDos.Find(id);

        if (existingTodo != null)
        {
            _context.ToDos.Remove(existingTodo);
            _context.SaveChanges();
        }
    }
}

minjie:
把所有方法改成異步
Copilot:

using CopilotChatPreview;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

public class ToDoService
{
    private readonly TodoContext _context;

    public ToDoService(TodoContext context)
    {
        _context = context;
    }

    public async Task<List<ToDo>> GetToDos()
    {
        return await _context.ToDos.ToListAsync();
    }

    public async Task<ToDo> GetToDoById(int id)
    {
        return await _context.ToDos.FindAsync(id);
    }

    public async Task AddToDoAsync(ToDo todo)
    {
        _context.ToDos.Add(todo);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateToDoByIdAsync(int id, ToDo todo)
    {
        var existingTodo = await _context.ToDos.FindAsync(id);

        if (existingTodo != null)
        {
            existingTodo.Task = todo.Task;
            existingTodo.Completed = todo.Completed;

            await _context.SaveChangesAsync();
        }
    }

    public async Task RemoveToDoByIdAsync(int id)
    {
        var existingTodo = await _context.ToDos.FindAsync(id);

        if (existingTodo != null)
        {
            _context.ToDos.Remove(existingTodo);
            await _context.SaveChangesAsync();
        }
    }
}

minjie:
生產一個控制器並且使用 ToDoService,使用 restful 方式暴露出去
Copilot:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class ToDoController : ControllerBase
{
    private readonly ToDoService _toDoService;

    public ToDoController(ToDoService toDoService)
    {
        _toDoService = toDoService;
    }

    [HttpGet]
    public async Task<ActionResult<List<ToDo>>> GetAllToDos()
    {
        return await _toDoService.GetToDos();
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<ToDo>> GetToDoById(int id)
    {
        var todo = await _toDoService.GetToDoById(id);

        if (todo == null)
        {
            return NotFound();
        }

        return todo;
    }

    [HttpPost]
    public async Task<IActionResult> CreateToDoAsync(ToDo todo)
    {
        await _toDoService.AddToDoAsync(todo);

        return CreatedAtAction(nameof(GetToDoById), new { id = todo.Id }, todo);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateToDoByIdAsync(int id, ToDo todo)
    {
        await _toDoService.UpdateToDoByIdAsync(id, todo);

        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteToDoByIdAsync(int id)
    {
        await _toDoService.RemoveToDoByIdAsync(id);

        return NoContent();
    }
}

minjie:
在程序啓動的時候把ToDoService註冊到容器並且使用scope生命週期
Copilot:

// In Startup.cs file, add the following code to ConfigureServices method
services.AddScoped<ToDoService>();

minjie:
可以把以上對話整理成一篇文章嗎?文章的題目叫 CopilotChat 初體驗

CopilotChat 初體驗
在這篇文章中,我們將一起學習如何使用 Microsoft 的 AI 編程助手,GitHub Copilot 來生成 C# 代碼。同時,我們將使用一個 To-Do 應用來展示 Copilot 的功能。
註冊 Copilot
要使用 GitHub Copilot,你需要註冊並安裝 GitHub Copilot 擴展。在安裝時,你需要登錄你的 GitHub 帳戶和選擇你喜歡的語言。
準備工作
我們將使用 Entity Framework Core 和 ASP.NET Core 創建 To-Do 應用程序。以下是創建應用程序的基本步驟。
步驟 1:創建數據庫上下文
我們首先需要創建數據上下文。我們創建一個名爲 TodoContext 的類,在其中我們將 ToDo 實體框架創建爲數據庫表。同時還將添加一個連接字符串以及一些模型構建代碼。
using Microsoft.EntityFrameworkCore;

namespace CopilotChatPreview
{
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options) : base(options) {}

        public DbSet<ToDo> ToDos { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ToDo>().HasData(
                new ToDo
                {
                    Id = 1,
                    Task = "Learn AI programming",
                    Completed = false
                },
                new ToDo
                {
                    Id = 2,
                    Task = "Build a chatbot",
                    Completed = false
                });
        }
    }
}


步驟 2:創建服務類
我們創建一個 ToDoService 類,用於在數據庫中執行增刪改查操作。
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

public class ToDoService
{
    private readonly TodoContext _context;

    public ToDoService(TodoContext context)
    {
        _context = context;
    }

    ...

    public async Task RemoveToDoByIdAsync(int id)
    {
        var existingTodo = await _context.ToDos.FindAsync(id);

        if (existingTodo != null)
        {
            _context.ToDos.Remove(existingTodo);
            await _context.SaveChangesAsync();
        }
    }
}

步驟 3:創建 To-Do 控制器
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class ToDoController : ControllerBase
{
    private readonly ToDoService _toDoService;

    public ToDoController(ToDoService toDoService)
    {
        _toDoService = toDoService;
    }

    ...

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteToDoByIdAsync(int id)
    {
        await _toDoService.RemoveToDoByIdAsync(id);

        return NoContent();
    }
}

步驟 4:註冊服務
在 ConfigureServices 方法中,我們需要將數據庫上下文和服務類註冊到容器中。
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddDbContextPool<TodoContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddScoped<ToDoService>();
    ...
}

總結
在這篇文章裏,我們介紹瞭如何使用 AI 編程助手 Copilot 來生成 C# 代碼。我們還介紹瞭如何使用 Entity Framework Core 創建和操縱數據庫表。通過這篇文章,你已經開始掌握瞭如何使用 AI 編程助手來讓開發 C# 應用程序變得更加輕鬆和高效。

總結

本想讓 Copilot 幫我總結一下,最後還是我自己來總結吧。本次初步體驗了 Copilot chat 的一些基本能力。比如如何問答,如何分析代碼。特別是最後一步步全程使用對話方式生成一個可以運行的項目的內容,還是挺精彩的。作爲一個 AI 助手是完全合格的。不過說實話沒有第一次使用 Copilot 的時候的那種震撼。爲啥?因爲 ChatGPT 珠玉在前,短短的一段時間,已經讓我們把對 AI 的期望拉到了前所未有的高度。Copilot chat 作爲 ChatGPT 模型在垂直領域的實踐,還需好好打磨,讓我們期待一下正式版吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章