فراخوانی LangChain از Go (قسمت 1)

انگیزه
به دنبال آزمایشهای «تعطیلات» (پستهای قبلی…) در مورد استفاده از Golang و LLM، به دنبال راهی آسان برای پیادهسازی تماس LangChain در Go و ترجیحاً استفاده از آن بودم. watsonx.ai.
خوشبختانه من مخزن Github زیر را پیدا کردم: https://github.com/tmc/langchaingo (به تراویس کلین https://github.com/tmc).
در مخزن او، این پوشه خاص وجود دارد: https://github.com/tmc/langchaingo/blob/main/examples/watsonx-llm-example/watsonx_example.go که توجه من را به خود جلب کرد!
بنابراین طبق معمول یک پروژه ساختم و سعی کردم آن را پیاده کنم و همچنین ایده های خودم را قرار دادم (آما سس 😄).
پیاده سازی
طبق معمول طبق نیاز به متغیرهای محیطی، من یک را راه اندازی کردم env فایلی که بعداً در برنامه استفاده می شود.
export WATSONX_API_KEY="your-watsonx-api-key"
export WATSONX_PROJECT_ID="your-watsonx-projectid"
# I used the US-SOUTH, could be any other region of IBM Cloud
export SERVICE_URL="https://us-south.ml.cloud.ibm.com"
در پست قبلی به تلاش برای شمارش تعداد توکن های ارسال شده و دریافت شده از یک LLM اشاره کردم. آن کار هنوز WIP است، بنابراین من مستقیماً از ” استفاده کردمtiktoken-go” کتابخانه داخل برنامه من با ایده ایجاد برخی تغییرات در آن (در آینده ای نزدیک؟). به هر حال، در مورد وضعیت فعلی پیشرفت من واقعاً کار نمی کند، اما وجود دارد.
برای برنامه به تنهایی، من از کد تراویس از مخزن او تقریباً همانطور که هست استفاده کردم و آن را با ویژگی های زیر اضافه و پیچیده کردم.
- استفاده از یک کادر محاوره ای برای ورودی سریع (🙄 من عاشق جعبه های گفتگو هستم 😂)
- “تلاشبرای شمارش تعداد «توکنهای» ارسال شده و دریافتی از LLM. کد به خودی خود به شرح زیر است؛
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"runtime"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"github.com/joho/godotenv"
"github.com/pkoukk/tiktoken-go"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/watsonx"
)
const (
_tokenApproximation = 4
)
const (
_gpt35TurboContextSize = 4096
_gpt432KContextSize = 32768
_gpt4ContextSize = 8192
_textDavinci3ContextSize = 4097
_textBabbage1ContextSize = 2048
_textAda1ContextSize = 2048
_textCurie1ContextSize = 2048
_codeDavinci2ContextSize = 8000
_codeCushman1ContextSize = 2048
_textBisonContextSize = 2048
_chatBisonContextSize = 2048
_defaultContextSize = 2048
)
// nolint:gochecknoglobals
var modelToContextSize = map[string]int{
"gpt-3.5-turbo": _gpt35TurboContextSize,
"gpt-4-32k": _gpt432KContextSize,
"gpt-4": _gpt4ContextSize,
"text-davinci-003": _textDavinci3ContextSize,
"text-curie-001": _textCurie1ContextSize,
"text-babbage-001": _textBabbage1ContextSize,
"text-ada-001": _textAda1ContextSize,
"code-davinci-002": _codeDavinci2ContextSize,
"code-cushman-001": _codeCushman1ContextSize,
}
var tokens int
func runCmd(name string, arg ...string) {
cmd := exec.Command(name, arg...)
cmd.Stdout = os.Stdout
cmd.Run()
}
func ClearTerminal() {
switch runtime.GOOS {
case "darwin":
runCmd("clear")
case "linux":
runCmd("clear")
case "windows":
runCmd("cmd", "/c", "cls")
default:
runCmd("clear")
}
}
func promptEntryDialog() string {
var promptEntry string
// Create a new Fyne application
myApp := app.New()
myWindow := myApp.NewWindow("Prompt Entry Dialog")
// Variable to store user input
var userInput string
// Button to show the dialog
button := widget.NewButton("Click to Enter your prompt's text", func() {
entry := widget.NewEntry()
dialog.ShowCustomConfirm("Input Dialog", "OK", "Cancel", entry, func(confirm bool) {
if confirm {
userInput = entry.Text
promptEntry = userInput
fmt.Println("User Input:", userInput) // Print to the console
myWindow.Close()
}
}, myWindow)
})
// Add the button to the window
myWindow.SetContent(container.NewVBox(
widget.NewLabel("Click the button below to enter text:"),
button,
))
// Set the window size and run the application
myWindow.Resize(fyne.NewSize(400, 200))
myWindow.ShowAndRun()
return promptEntry
}
func CountTokens(model, text string, inorout string) int {
var txtLen int
e, err := tiktoken.EncodingForModel(model)
if err != nil {
e, err = tiktoken.GetEncoding("gpt2")
if err != nil {
log.Printf("[WARN] Failed to calculate number of tokens for model, falling back to approximate count")
txtLen = len([]rune(text))
fmt.Println("Guessed tokens for the "+inorout+" text:", txtLen/_tokenApproximation)
return txtLen
}
}
return len(e.Encode(text, nil, nil))
}
func GetModelContextSize(model string) int {
contextSize, ok := modelToContextSize[model]
if !ok {
return _defaultContextSize
}
return contextSize
}
func CalculateMaxTokens(model, text string) int {
return GetModelContextSize(model) - CountTokens(model, text, text)
}
func main() {
var prompt, model string
// read the '.env' file
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
ApiKey := os.Getenv("WATSONX_API_KEY")
if ApiKey == "" {
log.Fatal("WATSONX_API_KEY environment variable is not set")
}
ServiceURL := os.Getenv("SERVICE_URL")
if ServiceURL == "" {
log.Fatal("SERVICE_URL environment variable is not set")
}
ProjectID := os.Getenv("WATSONX_PROJECT_ID")
if ProjectID == "" {
log.Fatal("WATSONX_PROJECT_ID environment variable is not set")
}
// LLM from watsonx.ai
model = "ibm/granite-13b-instruct-v2"
// model = "meta-llama/llama-3-70b-instruct"
llm, err := watsonx.New(
model,
//// Optional parameters: to be implemented if needed - Not used at this stage but all ready
// wx.WithWatsonxAPIKey(ApiKey),
// wx.WithWatsonxProjectID("YOUR WATSONX PROJECT ID"),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
prompt = promptEntryDialog()
// for the output visibility on the consol - getting rid of system messages
ClearTerminal()
// Use the entry variable here
fmt.Println("Calling the llm with the user's prompt:", prompt)
tokens = CountTokens(model, prompt, "input")
completion, err := llms.GenerateFromSinglePrompt(
ctx,
llm,
prompt,
llms.WithTopK(10),
llms.WithTopP(0.95),
llms.WithSeed(25),
)
// Check for errors
if err != nil {
log.Fatal(err)
}
fmt.Println(completion)
tokens = CountTokens(model, completion, "output")
}
که به خوبی کار می کند همانطور که خروجی در زیر نشان داده شده است.
Calling the llm with the user's prompt: What is the distance in Kilmometers from Earth to Moon?
2024/12/31 11:08:04 [WARN] Failed to calculate number of tokens for model, falling back to approximate count
Guessed tokens for the input text: 13
The distance from Earth to the Moon is about 384,400 kilometers.
2024/12/31 11:08:04 [WARN] Failed to calculate number of tokens for model, falling back to approximate count
Guessed tokens for the output text: 16
#####
Calling the llm with the user's prompt: What is the name of the capital city of France?
2024/12/31 11:39:28 [WARN] Failed to calculate number of tokens for model, falling back to approximate count
Guessed tokens for the input text: 11
Paris
2024/12/31 11:39:28 [WARN] Failed to calculate number of tokens for model, falling back to approximate count
Guessed tokens for the output text: 1
Voilà!
مراحل بعدی
من ویژگی های زیر را برای نسخه 0.2 پیاده سازی می کنم.
- پیشنهاد مدلی که کاربر می خواهد استفاده کند،
- یک راه دقیق تر برای تعیین # توکن،
- برخی از پیاده سازی واقعی LangChain.
نتیجه گیری
این بازتابی بسیار ساده از کار من در مورد فراخوانی LangChain از یک برنامه Go است.
منتظر مطالب بیشتر باشید💡