Skip to content

Instantly share code, notes, and snippets.

@dvgamerr
Created July 22, 2025 03:01
Show Gist options
  • Save dvgamerr/a9ea1ed61aa0d68dff4694394d008cf9 to your computer and use it in GitHub Desktop.
Save dvgamerr/a9ea1ed61aa0d68dff4694394d008cf9 to your computer and use it in GitHub Desktop.
package main
import (
"context"
"database/sql"
"fmt"
"log"
"net/http"
"os"
"sync"
"time"
"github.com/labstack/echo/v4"
"github.com/tmc/langchaingo/agents"
"github.com/tmc/langchaingo/chains"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/memory"
"github.com/tmc/langchaingo/memory/sqlite3"
"github.com/tmc/langchaingo/tools"
_ "github.com/mattn/go-sqlite3"
)
// ------------------------------------------------------------
// Custom Tool 1: TimeTool – ส่งเวลาปัจจุบัน (RFC3339)
// Custom Tool 2: JokeTool – ส่งมุกตลกสั้น ๆ ภาษาไทย
// ------------------------------------------------------------
type TimeTool struct{}
func (t TimeTool) Name() string { return "time_now" }
func (t TimeTool) Description() string { return "Return current time in RFC3339 format." }
func (t TimeTool) Call(ctx context.Context, input string) (string, error) {
fmt.Println("call tool time")
return time.Now().Format(time.RFC3339), nil
}
type JokeTool struct{}
func (j JokeTool) Name() string { return "thai_joke" }
func (j JokeTool) Description() string { return "Tell a short Thai joke to lighten the mood." }
func (j JokeTool) Call(ctx context.Context, input string) (string, error) {
fmt.Println("call tool joke")
joke := "ทำไมโปรแกรมเมอร์ถึงสวมแว่น? เพราะเวลา debug มองโลกในแง่บวกไม่ได้!"
return joke, nil
}
// ------------------------------------------------------------
// Persona Prompt (เด็กผู้ชาย 15 ปี)
// ------------------------------------------------------------
const personaPrompt = `คุณคือเด็กผู้ชายไทยอายุ 15 ปี ชื่อ "บอทน้องโอ๊ต" พูดคุยเป็นกันเองสไตล์วัยรุ่นสุภาพ (ใช้คำว่า "นะฮ้าฟฟฟ" เมื่อจบประโยค)
- คุณมีเครื่องมือให้ใช้ 2 อย่าง:
1. time_now – บอกเวลาปัจจุบัน
2. thai_joke – เล่ามุกตลกสั้น ๆ ภาษาไทย
- ถ้าผู้ใช้ถามเวลาหรือขอมุกตลก ให้เรียกใช้ tool ที่ตรงกับคำถาม แล้วอธิบายผลเป็นภาษาธรรมชาติ
- หากคำถามไม่เกี่ยวข้องกับ tools ให้ตอบเองตามความรู้ทั่วไปของ LLM
- ตอบเป็นภาษาไทยโดยอัตโนมัติ เว้นแต่ผู้ใช้จะใช้ภาษาอื่นก่อน`
// ------------------------------------------------------------
// API Structures
// ------------------------------------------------------------
type ChatRequest struct {
SessionID string `json:"session_id"`
Message string `json:"message"`
}
type ChatResponse struct {
Reply string `json:"reply"`
}
// ------------------------------------------------------------
// Globals
// ------------------------------------------------------------
var (
llm *openai.LLM
db *sql.DB
toolsSet []tools.Tool
mu sync.Mutex
)
func main() {
// 1) Init LLM via LiteLLM proxy (or OpenAI fallback)
baseURL := os.Getenv("LITELLM_BASE_URL")
if baseURL == "" {
baseURL = "https://api.openai.com/v1"
}
apiKey := os.Getenv("LITELLM_API_KEY")
if apiKey == "" {
log.Fatal("missing LITELLM_API_KEY env var")
}
model := os.Getenv("LITELLM_MODEL")
if model == "" {
model = "gpt-3.5-turbo"
}
var err error
llm, err = openai.New(
openai.WithBaseURL(baseURL),
openai.WithToken(apiKey),
openai.WithModel(model),
)
if err != nil {
log.Fatalf("init llm: %v", err)
}
// 2) Init SQLite DB for chat history
db, err = sql.Open("sqlite3", "chat_history.db")
if err != nil {
log.Fatal(err)
}
if _, err := db.Exec("PRAGMA journal_mode=WAL;"); err != nil {
log.Println(err)
}
// 3) Register custom tools
toolsSet = []tools.Tool{TimeTool{}, JokeTool{}}
// 4) Start Echo server
e := echo.New()
e.POST("/chat", chatHandler)
addr := ":8080"
log.Printf("Server running at %s", addr)
if err := e.Start(addr); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}
func chatHandler(c echo.Context) error {
req := new(ChatRequest)
if err := c.Bind(req); err != nil {
return c.String(http.StatusBadRequest, "invalid request body")
}
if req.SessionID == "" || req.Message == "" {
return c.String(http.StatusBadRequest, "session_id and message are required")
}
// 5) Create / get chat history for session
mu.Lock() // ensure thread‑safe SQLite access
history := sqlite3.NewSqliteChatMessageHistory(
sqlite3.WithSession(req.SessionID),
sqlite3.WithDB(db),
)
mu.Unlock()
// 5.1) Add persona system message (ใส่ครั้งแรกเท่านั้น)
msgs, _ := history.Messages(context.Background())
if len(msgs) == 0 {
if err := history.AddMessage(context.Background(), llms.SystemChatMessage{Content: personaPrompt}); err != nil {
log.Println("add system msg:", err)
}
}
convoMem := memory.NewConversationBuffer(
memory.WithChatHistory(history),
memory.WithInputKey("input"),
)
agent := agents.NewConversationalAgent(llm, toolsSet)
executor := agents.NewExecutor(
agent,
agents.WithMemory(convoMem),
agents.WithMaxIterations(5),
)
reply, err := chains.Run(context.Background(), executor, req.Message)
if err != nil {
return c.String(http.StatusInternalServerError, fmt.Sprintf("agent error: %v", err))
}
return c.JSON(http.StatusOK, ChatResponse{Reply: reply})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment