+1

Xây dựng CLI hỗ trợ AI với Golang và Google Gemini

Tình cờ lướt lướt qua các diễn đàn IT trên thế giới, tôi có thấy một bài viết về chủ đề xây dựng AI có vẻ khá thú vị và hữu ích, liên quan đến Golang và Google Gemini, nay share để các bạn cùng đọc.

Nếu các bạn chưa biết Golang là gì, có thể tham khảo trong bài viết này nhé: Golang là gì? Vì sao nên sử dụng ngôn ngữ Golang

Bài viết này của tác giả Pradumna Saraf, tôi xin phép được trích dẫn nguyên văn:

Gần đây tôi đã xây dựng một CLI hỗ trợ AI với Golang có tên là GenCLI, từ đó bạn có thể đặt câu hỏi ở định dạng văn bản hoặc cung cấp cho nó một hình ảnh và yêu cầu thông tin chi tiết về nó. Nếu đó là điều gì đó nghe có vẻ thú vị với bạn, thì bài viết này là dành cho bạn. Trong bài viết này, tôi đã xây dựng một CLI hoàn toàn từ đầu và cung cấp cho nó sức mạnh AI bằng cách sử dụng API Gemini của Google.

Trong trường hợp bạn muốn xem GenCLI của tôi, đây là liên kết tại github, nó là mã nguồn mở nên các bạn có thể tải xuống sử dụng.

Điều kiện tiên quyết để xây dựng CLI

  • Đã làm quen với Golang
  • Đã thử gói Cobra

Chúng ta cùng bắt đầu nào

Đầu tiên, bạn hãy tạo một thư mục và mở nó trong IDE/Editor yêu thích của bạn. Tôi đang sử dụng VS Code và đặt tên cho thư mục là go-ai. Bây giờ hãy khởi tạo dự án bằng cách chạy lệnh command go mod init

. Đường dẫn ở đây là đường dẫn mô-đun. Nếu chúng ta xuất bản một mô-đun (dự án), thì đây phải là đường dẫn mà mô-đun của chúng ta có thể được tải xuống bằng công cụ Go. Đây sẽ là liên kết kho GitHub của chúng ta. Chúng ta có thể tạo một kho GitHub sau nhưng chúng ta có thể cung cấp liên kết ngay bây giờ. Đối với tôi, nó sẽ trông như thế này:

go mod init github.com/Pradumnasaraf/go-ai 

Khi bạn đã xong, go.mod sẽ được khởi tạo image.png

Mặc dù chúng ta có thể tạo và thực hiện mọi thứ theo cách thủ công để xây dựng CLI. Nhưng điều tuyệt vời về Cobra Package là nó có CLI tạo cấu trúc, tạo tệp và cài đặt các gói cho CLI. Điều này sẽ giúp chúng ta tăng tốc quá trình và ít lỗi hơn. Để cài đặt công cụ Cobra CLI, hãy sử dụng lệnh bên dưới:

go install github.com/spf13/cobra-cli@latest

Sau khi thực hiện xong, bạn có thể kiểm tra xem công cụ đã được cài đặt chưa bằng cách nhập cobra-cliterminal và bạn sẽ nhận được danh sách các công cụ có sẵn. Bây giờ hãy chạy cobra-cli init để thiết lập dự án. Sau khi chạy, nó sẽ tự động tạo một thư mục cmd, go.sum, và main.go. Để kiểm tra xem nó có hoạt động hay không, hãy chạy go run main.go. Bạn sẽ thấy đầu ra trong terminal về CLI (giống như ảnh chụp màn hình bên dưới) image.png

Để giao tiếp và sử dụng API Gemini của Google, trước tiên chúng ta cần cài đặt gói Gemini Golang SKD, để thực hiện điều đó, hãy thực hiện lệnh bên dưới.

go get github.com/google/generative-ai-go

Bây giờ giống như các API khác, chúng ta cần một API Key. Để có được nó, hãy truy cập vào đây https://aistudio.google.com/app/apikey và lấy nó. Nó MIỄN PHÍ và bạn có được API key chỉ trong 30 giây. Sau khi bạn có được API key, hãy thiết lập một biến môi trường bằng cách thực hiện lệnh sau:

export GEMINI_API_KEY=<YOUR_API_KEY>

Vấn đề với phương pháp này đó là biến môi trường sẽ chỉ tồn tại cho phiên hiện tại cho đến khi bạn đóng terminal thì nó sẽ biến mất. Để tránh vấn đề này, hãy thêm lệnh export vào tệp cấu hình shell, chẳng hạn như .bashrc, .bashprofile hoặc .zshrc (tùy thuộc vào shell của bạn). Theo cách này, bạn có thể truy cập CLI từ bất kỳ đâu trong hệ thống.

Bây giờ, đã đến lúc tạo một lệnh phụ cho CLI thay vì viết hàm logic trực tiếp vào root.go. Lý do để làm như vậy là nếu trong tương lai chúng ta muốn đưa thêm nhiều chức năng và nhiều lệnh phụ hơn, chúng ta có thể chỉ cần thêm bằng cách thêm nhiều lệnh phụ hơn và không chặn lệnh gốc. Nếu bạn không hiểu điều này, đừng lo lắng, hãy theo dõi tiếp dưới đây, mọi thứ sẽ rõ ràng.

Để tạo một lệnh phụ, Cobra CLI cung cấp một lệnh add để tạo lệnh đó. Để thực hiện, hãy thực hiện lệnh bên dưới. Ở đây search sẽ trở thành một lệnh phụ. Bạn có thể chọn bất kỳ lệnh nào bạn thích.

cobra-cli add search

Sau khi bạn thực thi nó, một tệp mới sẽ được tạo trong thư mục cmd với tất cả các mã được điền sẵn này. Trong mã, chúng ta đang khởi tạo một biến thành searchCmd một con trỏ tới một cobra.Commandstruct và cung cấp giá trị cho các trường như tên lệnh phụ, sử dụng, v.v. Hàm trong Run: sẽ được kích hoạt bất cứ khi nào chúng ta thực thi lệnh phụ. Ngoài ra, nếu bạn thấy chúng ta đang thêm một lệnh (lệnh phụ) cho lệnh gốc trong hàm init. Đây là những gì mà đoạn mã hoàn chỉnh sẽ trông giống như thế này.

package cmd

import (
    "fmt"

    "github.com/spf13/cobra"
)

// searchCmd represents the search command
var searchCmd = &cobra.Command{
    Use:   "search",
    Short: "A brief description of your command",
    Long: `A longer description that spans multiple lines and likely contains examples`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("search called")
    },
}

func init() {
    rootCmd.AddCommand(searchCmd)
}

Để kiểm tra xem lệnh phụ "search" có hoạt động như mong đợi hay không, hãy chạy CLI với lệnh "search" và bạn sẽ thấy "search called" được in trong terminal của mình.

go run main.go search 

Bây giờ, chúng ta hãy làm việc về phía API. Hãy nhập các gói cho Google Gemini API cũng như các gói khác cần thiết cho các tác vụ ghi nhật ký và cấp hệ điều hành. Sau đây là danh sách đầy đủ.

import (
    "context"
    "log"
    "os"

    "github.com/google/generative-ai-go/genai"
    "github.com/spf13/cobra"
    "google.golang.org/api/option"
)

Sau đó, hãy thêm một hàm có tên là getResponse. Hàm này sẽ giúp chúng ta giao tiếp với API Gemini, nhận phản hồi và in ra. Ngoài ra, nếu bạn thấy tôi đã mã hóa cứng văn bản Prompt - "Write a story about a AI and magic", đừng lo, chúng tôi sẽ thay đổi điều đó nhưng trước tiên hãy làm cho nó hoạt động. Đây là mã hàm hoàn chỉnh, hãy thêm nó bên dưới hàm init của bạn. Bạn sẽ tìm thấy cùng một mã đã bắt đầu trên trang web Gemini.

func getResponse() {

    ctx := context.Background()
    client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    model := client.GenerativeModel("gemini-1.5-flash")
    resp, err := model.GenerateContent(ctx, genai.Text("Write a story about a AI and magic"))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(resp.Candidates[0].Content.Parts[0])
}

Bây giờ chúng ta hãy thêm hàm getResponse vào hàm trong trường Run:. Để khi chúng ta chạy lệnh sun, nó sẽ gọi hàm getResponse. Bây giờ mã sẽ trông như thế này.

package cmd

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/google/generative-ai-go/genai"
    "github.com/spf13/cobra"
    "google.golang.org/api/option"
)

// searchCmd represents the search command
var searchCmd = &cobra.Command{
    Use:   "search",
    Short: "A brief description of your command",
// Added the getResponse() function
    Run: func(cmd *cobra.Command, args []string) {
        getResponse()
    },
}

func init() {
    rootCmd.AddCommand(searchCmd)
}

func getResponse() {

    ctx := context.Background()
    client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    model := client.GenerativeModel("gemini-1.5-flash")
    resp, err := model.GenerateContent(ctx, genai.Text("Write a story about a AI and magic"))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(resp.Candidates[0].Content.Parts[0])
}

Nếu bạn thấy một dòng ngoằn ngoèo màu đỏ bên dưới tên gói đã nhập, hãy chạy go mod tidy. Nó sẽ cài đặt gói bị thiếu và thực hiện dọn dẹp. Bây giờ, hãy chạy lại go run main.go search. Lần này, bạn sẽ nhận được phản hồi từ API cho Lời nhắc mà chúng tôi đã mã hóa cứng, tức là "Write a story about a AI and magic"

Trong trường hợp bạn gặp lỗi bên dưới, hãy kiểm tra xem biến môi trường của bạn đã được đặt đúng tên chưa. Bạn có thể kiểm tra bằng cách thực hiện lệnh printenv trong terminal và xem nó có ở đó hay không. image.png

Khi mọi thứ đã hoạt động, hãy biến lời nhắc thành động để chúng ta không phải mã hóa cứng lời nhắc trực tiếp vào mã và cung cấp lời nhắc thông qua thiết bị đầu cuối.

Để làm điều đó, hãy thêm một trường ARG: vào searchCmdstruct để người dùng ít nhất cần nhập một đối số sau lệnh phụ. Ngoài ra, chúng ta sẽ sửa đổi hàm getResponse để chấp nhận một lát dữ liệu vì args sẽ ở định dạng lát và chúng ta sẽ sử dụng gói strings để chuyển đổi nó thành một câu.

Cuối cùng, thay thế văn bản được mã hóa cứng genai.Text() bằng biến userArgs chúng ta đã tạo để chuyển đổi lát cắt thành một câu. Đây là cách mã hoàn chỉnh sẽ trông như thế nào, hãy cùng xem nhé.

package cmd

import (
    "context"
    "fmt"
    "log"
    "os"
    "strings" // import strings package

    "github.com/google/generative-ai-go/genai"
    "github.com/spf13/cobra"
    "google.golang.org/api/option"
)

var searchCmd = &cobra.Command{
    Use:   "search",
    Short: "A brief description of your command",
    Args:  cobra.MinimumNArgs(1), // Minimum 1 arg required
    Run: func(cmd *cobra.Command, args []string) {
        getResponse(args)
    },
}

func init() {
    rootCmd.AddCommand(searchCmd)
}

// Function can now accept slice parameter 
func getResponse(args []string) {
        // Creating a sentence out of a slice
    userArgs := strings.Join(args[0:], " ") 

    ctx := context.Background()
    client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    model := client.GenerativeModel("gemini-1.5-flash")
        // change the hardcoded text to userArgs variable
    resp, err := model.GenerateContent(ctx, genai.Text(userArgs))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(resp.Candidates[0].Content.Parts[0])
}

Nếu bạn thực thi lệnh go run main searchnow, nó sẽ đưa ra thông báo lỗi trong terminal cho biết ít nhất một đối số là bắt buộc. Điều này có nghĩa là mã của chúng ta đang hoạt động hoàn hảo. image.png

Bây giờ chúng ta hãy thực hiện lệnh theo đúng cách bằng cách đưa ra đối số cho nó - một lời nhắc/câu hỏi. image.png Như bạn thấy, nó cung cấp cho chúng ta câu trả lời. Chúng ta truyền lời nhắc trong dấu ngoặc kép để có thể thêm các ký tự đặc biệt như "?", ".", v.v. Vậy là đây, một CLI hỗ trợ AI đầy đủ chức năng.

Bây giờ, nếu bạn muốn xuất bản gói để CLI của bạn có thể thực hiện trực tiếp các lệnh và được sử dụng từ bất kỳ đâu trong hệ thống, thì việc đó rất đơn giản. Trước tiên, hãy đẩy các thay đổi của bạn lên GitHub và sau đó chuyển đến URL https://pkg.go.dev/github.com/<repo-url>. Trong trường hợp của tôi, đó sẽ là https://pkg.go.dev/github.com/Pradumnasaraf/go-ai. Khi bạn truy cập, bạn sẽ thấy một nút yêu cầu; hãy nhấp vào đó để yêu cầu thêm gói vào pkg.go.dev. Khi bạn hoàn tất, sau một vài giờ, nó sẽ có trên trang web.

Khi mọi thứ đã hoạt động, bạn có thể tải xuống CLI bằng cách sử dụng lệnh go install

o install <repo-url>
go install github.com/Pradumnasaraf/go-ai@latest

Và sử dụng trực tiếp CLI với các lệnh như go-ai, go-ai search, v.v. Nếu bạn gặp lỗi command not found: go-ai sau khi chạy, bạn cần thêm $GOPATH/bin vào biến $PATH trong môi trường của mình.

Vậy là kết thúc bài viết này ở đây, cảm ơn các bạn đã theo dõi.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí