ویرگول
ورودثبت نام
مسعود درویشیان
مسعود درویشیانمهندس نرم‌افزار
مسعود درویشیان
مسعود درویشیان
خواندن ۶ دقیقه·۹ ساعت پیش

ساخت یک چت‌بات هوشمند با Go و Ollama

همه‌ی ما توی زندگی روزمره از AI استفاده می‌کنیم، ولی تا حالا به این فکر کردین که چطور می‌تونیم توی اپلیکیشن خودمون از AI استفاده کنیم؟ مثلاً مثل یه دستیار هوشمند که به کاربر کمک کنه تجربه‌ی بهتری موقع استفاده از برنامه داشته باشه.

برای اینکه این موضوع رو ساده‌تر ببینیم، می‌تونیم یه دفترچه تلفن ساده با داده‌های ساختگی درست کنیم و بعد با کمک یه مدل AI محلی، اون رو هوشمندتر کنیم.

ما می‌خوایم همه‌چیز تا جای ممکن رایگان باشه. برای شروع، می‌تونین برین به سایت Ollama و اون رو دانلود کنین:

https://ollama.com/

بعد از نصب Ollama، می‌تونین یه مدل AI روش نصب کنین. مثلاً اگه یه مدل خیلی هوشمند می‌خواین که همه‌چیز رو آفلاین اجرا کنه و داده‌هاتون هم خصوصی بمونه، می‌تونین از qwen3.6 استفاده کنین. البته این مدل یه مقدار سنگینه، حدود ۲۴ گیگابایت حجم داره، و برای اجراش هم به یه سیستم نسبتاً قوی نیاز دارین.

یه گزینه‌ی دیگه هم llama3.1:8b هست. این مدل هم معمولاً خیلی خوبه، ولی شاید به اندازه‌ی qwen3.6 باهوش نباشه.

اگه سیستم قوی ندارین و نمی‌تونین یه مدل سنگین رو روی کامپیوتر خودتون اجرا کنین، می‌تونین از مدل‌های ابری هم استفاده کنین. یه مدل به اسم nemotron-3-ultra:cloud وجود داره که هم خیلی هوشمنده و هم سریع کار می‌کنه.

بعد از اینکه Ollama رو نصب کردین، این دستور رو توی ترمینال اجرا کنین تا مطمئن بشین همه‌چیز درست کار می‌کنه:

ollama run nemotron-3-ultra:cloud

اگه ترمینال وارد حالت چت شد، یعنی همه‌چیز درست راه افتاده.

احتمالاً می‌دونین که خود Ollama بیشتر با زبان Go نوشته شده. برای همین ما هم قراره برنامه‌مون رو با Go بسازیم.

به‌عنوان قدم اول، خوبه یه نگاهی به صفحه‌ی GitHub مربوط به Ollama بندازیم:

https://github.com/ollama/ollama

از اونجایی که Ollama با Go نوشته شده، برای کار کردن با خودش لازم نیست سراغ کتابخونه‌ی عجیب‌وغریب یا اضافه‌ای بریم. خیلی ساده می‌تونیم پکیج Go مربوط به Ollama رو توی برنامه‌مون import کنیم:

github.com/ollama/ollama/api

اگه به مثال‌های API توی لینک زیر نگاه کنیم، تقریباً کل ایده‌ی کار کردن با Ollama API دستمون میاد:

https://github.com/ollama/ollama/tree/main/api/examples

بیاین یه نگاه به مثال چت بندازیم:

package main import ( "context" "fmt" "log" "github.com/ollama/ollama/api" ) func main() { client, err := api.ClientFromEnvironment() if err != nil { log.Fatal(err) } messages := []api.Message{ { Role: "system", Content: "Provide very brief, concise responses", }, { Role: "user", Content: "Name some unusual animals", }, { Role: "assistant", Content: "Monotreme, platypus, echidna", }, { Role: "user", Content: "which of these is the most dangerous?", }, } ctx := context.Background() req := &api.ChatRequest{ Model: "llama3.2", Messages: messages, } respFunc := func(resp api.ChatResponse) error { fmt.Print(resp.Message.Content) return nil } err = client.Chat(ctx, req, respFunc) if err != nil { log.Fatal(err) } }

توی این مثال، برنامه سعی می‌کنه از مدل llama3.2 استفاده کنه. ولی من پیشنهاد می‌کنم اون رو به nemotron-3-ultra:cloud تغییر بدین، چون این‌طوری راحت‌تر اجرا می‌شه و لازم نیست چندین گیگابایت مدل دانلود کنین.

همون‌طور که می‌بینین، بخش اصلی مثال در نهایت به این خط می‌رسه:

err = client.Chat(ctx, req, respFunc)

در واقع فقط باید بهش یه context بدین، یه ChatRequest بدین که داخلش لیست پیام‌ها قرار داره، و یه function بدین که جواب مدل رو پردازش کنه.

اگه این برنامه رو اجرا کنین، یه خروجی شبیه این می‌گیرین:

The platypus (specifically males) is the most dangerous due to venomous spurs on their hind legs capable of causing excruciating pain. Echidnas only have sharp spines for defense.

جالبه، نه؟ حالا تصور کنین با همین ایده چقدر کارهای مختلف می‌شه کرد و چطور می‌تونیم AI رو روی داده‌های خودمون سوار کنیم.

حالا بیاین یه سری داده‌ی ساختگی برای دفترچه تلفن آماده کنیم و اون رو به agent خودمون بدیم. این کد رو با همون مثال اصلی API ترکیب می‌کنیم:

package main import ( "bufio" "context" "encoding/json" "fmt" "log" "os" "strings" "github.com/ollama/ollama/api" ) type Person struct { Name string Family string Address string Number string } type Phonebook struct { Contacts []Person } const ( model = "nemotron-3-ultra:cloud" ) func main() { phonebook := Phonebook{} phonebook.Contacts = append(phonebook.Contacts, Person{ Name: "Florian", Family: "Schumacher", Address: "Berlin, Germany", Number: "+49173123456", }) phonebook.Contacts = append(phonebook.Contacts, Person{ Name: "Sabrina", Family: "Herkenhof", Address: "Hamburg, Germany", Number: "+4917654321", }) phonebook.Contacts = append(phonebook.Contacts, Person{ Name: "Jane", Family: "Peterson", Address: "Texas, USA", Number: "+15550123456", }) phonebook.Contacts = append(phonebook.Contacts, Person{ Name: "Peter", Family: "Jackson", Address: "Florida, USA", Number: "+1212555-0147", }) phonebook.Contacts = append(phonebook.Contacts, Person{ Name: "Peter", Family: "Parker", Address: "California, USA", Number: "+1310555-0189", }) client, err := api.ClientFromEnvironment() if err != nil { log.Fatal(err) } messages := []api.Message{ { Role: "system", Content: ` Provide a friendly, brief, and concise responses about data being provided in the as list of contacts. In case user was greeting you, response politely and tell user that how you can help him his/her phonebook? In case of manipulating data, tell user you cannot do that, and he/she should do it themselves. If user asks anything else, tell the user (politely and professionally) that I don't know the answer and you should contact administrator. Answer in the language that user is asking the question. Always show the requested data in a consistant human readable format like a table with borders (left to right), and not json or markdown. `, }, { Role: "user", Content: "Hi", }, } req := &api.ChatRequest{ Model: model, Messages: messages, } ctx := context.Background() respFunc := func(resp api.ChatResponse) error { fmt.Print(resp.Message.Content) return nil } // fire up the conversation err = client.Chat(ctx, req, respFunc) if err != nil { log.Fatal(err) } contactsBytes, err := json.Marshal(phonebook.Contacts) if err != nil { log.Fatal("failed to make json data out of contacts!", err) } req.Messages = append(req.Messages, api.Message{ Role: "assistant", Content: fmt.Sprintf("contact list data is %s\n", string(contactsBytes)), }) fmt.Println() for { fmt.Print("-> ") reader := bufio.NewReader(os.Stdin) prmpt, err := reader.ReadString('\n') if err != nil { fmt.Println("an error happened!", err) continue } prmpt = strings.TrimSpace(prmpt) if strings.Compare(prmpt, "exit") == 0 { break } req.Messages = append(req.Messages, api.Message{ Role: "user", Content: prmpt, }) err = client.Chat(ctx, req, respFunc) if err != nil { log.Fatal(err) } fmt.Println() } fmt.Println("Bye! 👋") }

اگه با دقت به کد نگاه کنین، می‌بینین که این تقریباً همون مثال اصلیه، فقط یه کم تغییرش دادیم. من messageها رو عوض کردم و یه system prompt آماده کردم. بعد هم یه پیام Hi از طرف user اضافه کردم تا وقتی برنامه اجرا می‌شه، AI agent شروع به کار کنه.

این بخش، قسمت اصلی کاره؛ جایی که ما داده‌هامون رو به AI می‌دیم:

contactsBytes, err := json.Marshal(phonebook.Contacts) if err != nil { log.Fatal("failed to make json data out of contacts!", err) } req.Messages = append(req.Messages, api.Message{ Role: "assistant", Content: fmt.Sprintf("contact list data is %s\n", string(contactsBytes)), })

اینجا لیست مخاطب‌ها رو تبدیل به JSON می‌کنیم و بعد اون رو به‌عنوان یه پیام assistant به لیست پیام‌ها اضافه می‌کنیم. این‌طوری مدل از این به بعد می‌دونه چه داده‌ای در اختیار داره.

بعدش داخل یه حلقه‌ی بی‌نهایت، می‌تونین از AI سؤال بپرسین و اون هم بر اساس داده‌هایی که قبلاً بهش دادین جواب می‌ده.

مثلاً می‌تونین این سؤال‌ها یا دستورها رو بپرسین:

Show my contact list. Which contacts are from Germany? Which contacts are from the USA? Who is close to Brandenburg? Show contact details of [name] Do I have duplicate numbers?

و چیزهای شبیه به این.

همین! شما الان یه برنامه‌ی چت ساده با AI ساختین که context-aware هست، داده‌های شما رو می‌شناسه، و فقط درباره‌ی همون داده‌ها جواب می‌ده.

امیدوارم کلی باهاش خوش بگذرونین :)

goبرنامه نویسیآموزشهوش مصنوعی
۰
۰
مسعود درویشیان
مسعود درویشیان
مهندس نرم‌افزار
شاید از این پست‌ها خوشتان بیاید