Created
          July 22, 2025 03:01 
        
      - 
      
- 
        Save dvgamerr/a9ea1ed61aa0d68dff4694394d008cf9 to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | 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