การดู : 109

16/06/2026 04:00น.

การเขียนโค้ด Go แปลงข้อความเป็นเวกเตอร์ด้วย OpenAI Embedding API

Golang The Series EP.152: Intro to Embeddings - เปลี่ยนข้อความให้เป็นตัวเลขด้วย Go

#Vector Embeddings

#Go OpenAI

#Go

#Text Embedding

#Go Concurrency

#Data Pipeline

#RAG Backend

ยินดีต้อนรับเข้าสู่ EP.152 ครับ! ในตอนที่แล้วเราคุยกันถึงคอนเซปต์ RAG ว่ามันคือการให้ AI เปิดหนังสือสอบ แต่คำถามสำคัญที่สาย Backend อย่างเราต้องคิดต่อคือ "เราจะค้นหาข้อมูลในหนังสือเล่มนั้นอย่างไร ให้เจอประโยคที่มีความหมายตรงกับคำถามของ User มากที่สุด?"

ถ้าเราใช้คำสั่ง SQL แบบเดิมๆ อย่าง WHERE content LIKE '%ชำระเงิน%' แล้วเจอ User สายอินดี้ถามว่า "จ่ายตังค์ยังไง?" ระบบของเราจะควานหาเอกสารไม่เจอทันที เพราะคำว่า "ชำระเงิน" กับ "จ่ายตังค์" สะกดไม่เหมือนกันเลยสักตัวเดียว

เพื่อแก้ปัญหานี้ โลกของ AI จึงสร้างสิ่งที่เรียกว่า Embeddings ขึ้นมา มันคือการแปลง "ข้อความภาษาคน" ให้กลายเป็น "ชุดตัวเลข (Vectors)" เพื่อให้คอมพิวเตอร์สามารถคำนวณและวัด "ความหมาย" ของคำได้อย่างแม่นยำ วันนี้เราจะมาเขียน Go จัดการสิ่งนี้กันครับ

Vector Embeddings คืออะไร?

Embedding คือการนำข้อความ (ไม่ว่าจะเป็นคำ ประโยค หรือทั้งบทความ) วิ่งผ่าน AI Model สำหรับถอดรหัสความหมายโดยเฉพาะ (เช่น text-embedding-3-small ของ OpenAI) เพื่อแปลงข้อความนั้นให้ออกมาเป็น Array ของตัวเลขทศนิยม ([]float32) ที่มีความยาวคงที่ เช่น 1,536 มิติ

ความเจ๋งของมันคือ:

  • คำที่ความหมายคล้ายกัน เช่น "แมว" กับ "ลูกแมว" หรือ "ชำระเงิน" กับ "จ่ายตังค์" จะได้ชุดตัวเลขที่อยู่ใกล้กัน ในพิกัดมิติเชิงความหมาย

  • เปลี่ยนภาษาคนให้เป็นคณิตศาสตร์ คอมพิวเตอร์ไม่เข้าใจภาษาเรา แต่เข้าใจตัวเลข มันจึงสามารถนำตัวเลขเหล่านี้ไปคำนวณหาค่าระยะห่าง (เช่น Cosine Similarity) เพื่อบอกได้ทันทีว่าสองประโยคนี้คุยเรื่องเดียวกันหรือไม่

เขียน Go เรียกใช้งาน Embedding API

เราจะใช้คลังไลบรารี go-openai เจ้าเก่า ในการส่งข้อความไปแปลงเป็น Vector ครับ

Go

package main

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

	"github.com/sashabaranov/go-openai"
)

func main() {
	// ดึง API Key จาก Environment Variable
	apiKey := os.Getenv("OPENAI_API_KEY")
	if apiKey == "" {
		log.Fatal("Embedding API Key is required")
	}

	client := openai.NewClient(apiKey)

	// ข้อความที่เราต้องการแปลงความหมาย
	inputText := "ฉันต้องการจ่ายเงินด้วยบัตรเครดิต"

	// 1. สร้าง Request สำหรับ Embedding
	req := openai.EmbeddingRequest{
		Input: []string{inputText},
		Model: openai.SmallEmbedding3Small, // โมเดลยอดนิยม 1,536 มิติ ประหยัดและเร็วมาก
	}

	// 2. ส่งข้อมูลไปแปลงที่ OpenAI API
	resp, err := client.CreateEmbeddings(context.Background(), req)
	if err != nil {
		log.Fatalf("Embedding failed: %v", err)
	}

	// 3. ดึงผลลัพธ์ Vector ออกมา (Array of float32)
	vector := resp.Data[0].Embedding

	fmt.Printf("ข้อความ: '%s'\n", inputText)
	fmt.Printf("ขนาดของมิติ Vector (Dimensions): %d\n", len(vector))
	fmt.Printf("ตัวอย่างเลข 5 มิติแรก: %v\n", vector[:5])
}

โครงสร้างข้อมูลเมื่อนำไปใช้งานจริง

ในระบบ RAG หลังจากที่เราได้ Vector ออกมาแล้ว เรามักจะเก็บมันไว้คู่กับข้อความต้นฉบับในรูปแบบ Struct เพื่อเตรียมเอาไปคำนวณเปรียบเทียบตอนที่ User พิมพ์ถามเข้ามา:

Go

type DocumentChunk struct {
	ID        string    `json:"id"`
	Content   string    `json:"content"`   // ข้อความดิบ เช่น "คู่มือการคืนสินค้าใน 7 วัน"
	Embedding []float32 `json:"embedding"` // ตัวเลข 1,536 มิติที่ได้จาก API
}

จุดเด่นของ Go ในงานทำ Data Embedding Pipeline

ถ้าคุณต้องทำระบบ RAG ขนาดใหญ่ขององค์กร คุณอาจต้องแปลงเอกสารนับแสนหน้าให้กลายเป็น Vector งานนี้จะกลายเป็นทั้ง I/O Bound (ยิง API) และ Compute Bound (จัดการข้อมูล) พร้อมกัน ซึ่ง Go กินขาดในเรื่องนี้:

  • Goroutine Worker Pool: เราสามารถใช้ Go แยกอ่านเอกสาร ทำการตัดแบ่งชิ้นส่วนข้อความ (Chunking) และสั่งยิงไปแปลง Embedding พร้อมกันหลายร้อยลูปได้แบบ Concurrent ช่วยย่นเวลาจากหลายชั่วโมงให้เหลือไม่กี่นาที

  • Memory Efficiency: ข้อมูลประเภท []float32 จำนวนมหาศาลถ้าจัดการไม่ดี แรมของ Server จะเต็มไวมาก แต่การจัดการหน่วยความจำที่ต่ำและเสถียรของ Go ช่วยให้คุม Resource ได้อยู่หมัด

🎯 ท้าให้ลอง (Daily Mission)

ลองสร้างข้อความขึ้นมา 3 ประโยคในโค้ด Go ของคุณ:

  1. "วิธีเปลี่ยนรหัสผ่านใหม่ทำอย่างไร"

  2. "ขั้นตอนการรีเซ็ตพาสเวิร์ด"

  3. "วันนี้ฝนตกหนักมากที่กรุงเทพ"

การบ้าน: ปรับโค้ดของคุณให้นำทั้ง 3 ประโยคนี้ไปแปลงเป็น Embedding พร้อมกัน จากนั้นลองเขียนฟังก์ชันลูปเพื่อสังเกตค่าตัวเลขดู หรือหากใครต้องการท้าทายตัวเอง ลองเขียนสูตรคำนวณหาค่าระยะห่างแบบง่ายๆ (เช่น Dot Product) ระหว่างข้อความที่ [1 กับ 2] และ [1 กับ 3] ดูครับว่า... คู่ไหนได้ตัวเลขที่ใกล้เคียงกันมากกว่ากัน?

❓ FAQ: คำถามที่พบบ่อยเกี่ยวกับ Embeddings

ตัวเลข 1,536 มิติ (Dimensions) หมายความว่าอย่างไร?

เปรียบเหมือนการมองวัตถุในระบบพิกัด ถ้ากราฟ 2 มิติมีค่า (X, Y) โมเดล AI ก็จะสร้างกราฟ 1,536 มิติขึ้นมาเพื่อสับย่อยคุณลักษณะของความหมายคำ เช่น มิติความเป็นทางการ, มิติเกี่ยวกับสัตว์, มิติเกี่ยวกับการเงิน ยิ่งมิติตัวเลขเยอะ ยิ่งเก็บความละเอียดของความหมายได้ลึกขึ้นครับ

ถ้าส่งประโยคภาษาไทย กับภาษาอังกฤษที่ความหมายเดียวกัน ตัวเลขจะใกล้กันไหม?

ใกล้กันครับ! โมเดลยุคใหม่อย่าง text-embedding-3-small เป็นแบบ Multilingual มันเข้าใจบริบทข้ามภาษา ดังนั้นคำว่า "แมว" กับ "Cat" จะถูกจัดวางให้อยู่ในพิกัดที่ใกล้กันมากๆ บนโลกของ Vector


📌 บทสรุป (Conclusion)

Vector Embeddings คือสะพานเชื่อมที่เปลี่ยนภาษาพูดของมนุษย์ให้กลายเป็นภาษาคณิตศาสตร์ที่คอมพิวเตอร์เข้าใจ ช่วยให้เราก้าวข้ามขีดจำกัดของการค้นหาด้วย Keyword แบบเดิมๆ ไปสู่การค้นหาด้วย "ความเข้าใจในบริบท" และเมื่อผสานพลังเข้ากับความเร็วและความประหยัดทรัพยากรของ ภาษา Go การทำ Data Pipeline เพื่อแปลงข้อมูลระดับล้านเรคคอร์ดก็ไม่ใช่เรื่องที่น่ากังวลอีกต่อไปครับ

ในตอนต่อไป (EP.153): เมื่อเรามี Array ของ []float32 นับล้านตัวลอยอยู่ในระบบ คำถามคือเราจะเอาตัวเลขเหล่านี้ไปเก็บไว้ที่ไหนให้ค้นหาและเปรียบเทียบได้เร็วในระดับมิลลิวินาที? ตอนหน้าเราจะก้าวเข้าสู่โลกของ "Vector Databases 101: ทำความรู้จัก Pinecone, Weaviate และ Milvus" เตรียมตัวอัปเกรด Stack ของคุณขึ้นไปอีกขั้น ห้ามพลาดครับ!

ฝากกดติดตามพวกเราได้ที่ Superdev Academy ในทุกช่องทางนะครับ!

  • 🔵 Facebook: Superdev Academy Thailand (อัปเดตข่าวสารและบทความใหม่)

  • 🎬 YouTube: Superdev Academy Channel (ติวเข้มแบบวิดีโอ)

  • 📸 Instagram: @superdevacademy (เกร็ดความรู้สั้นๆ และเบื้องหลังการทำงาน)

  • 🎬 TikTok: @superdevacademy (Tips & Tricks ฉบับย่อยง่าย)

  • 🌐 Website: superdevacademy.com (คลังบทความและคอร์สเรียนฉบับเต็ม)