package utils_langchain import ( "ai_scheduler/internal/config" "math/rand" "net" "net/http" "os" "sync" "time" "github.com/gofiber/fiber/v2/log" "github.com/tmc/langchaingo/llms/ollama" ) type UtilLangChain struct { LlmClientPool *sync.Pool poolSize int // 记录池大小,用于调试 model string serverURL string c *config.Config } type LlmObj struct { Number string Llm *ollama.LLM } func NewUtilLangChain(c *config.Config, logger log.AllLogger) *UtilLangChain { poolSize := c.Sys.LlmPoolLen if poolSize <= 0 { poolSize = 10 // 默认值 logger.Warnf("LlmPoolLen not set, using default: %d", poolSize) } // 初始化 Pool pool := &sync.Pool{ New: func() interface{} { llm, err := ollama.New( ollama.WithModel(c.Ollama.Model), ollama.WithHTTPClient(http.DefaultClient), ollama.WithServerURL(getUrl(c)), ollama.WithKeepAlive("-1s"), ) if err != nil { logger.Fatalf("Failed to create Ollama client: %v", err) panic(err) // 或者返回 nil + 错误处理 } number := randStr(5) log.Info(number) return &LlmObj{ Number: number, Llm: llm, } }, } // 预填充 Pool for i := 0; i < poolSize; i++ { pool.Put(pool.New()) } return &UtilLangChain{ LlmClientPool: pool, poolSize: poolSize, model: c.Ollama.Model, serverURL: getUrl(c), } } func (o *UtilLangChain) NewClient() *ollama.LLM { llm, _ := ollama.New( ollama.WithModel(o.c.Ollama.Model), ollama.WithHTTPClient(&http.Client{ Transport: &http.Transport{ MaxIdleConns: 100, // 最大空闲连接数(默认 2,太小) MaxIdleConnsPerHost: 100, // 每个 Host 的最大空闲连接数(默认 2) IdleConnTimeout: 90 * time.Second, // 空闲连接超时时间 DialContext: (&net.Dialer{ Timeout: 30 * time.Second, // 连接超时 KeepAlive: 30 * time.Second, // TCP Keep-Alive }).DialContext, }, Timeout: 60 * time.Second, // 整体请求超时(避免无限等待) }), ollama.WithServerURL(getUrl(o.c)), ollama.WithKeepAlive("-1s"), ) return llm } // Get 返回一个可用的 LLM 客户端 func (o *UtilLangChain) Get() *LlmObj { client := o.LlmClientPool.Get().(*LlmObj) return client } // Put 归还客户端(可选:检查是否仍可用) func (o *UtilLangChain) Put(llm *LlmObj) { if llm == nil { return } o.LlmClientPool.Put(llm) } // Stats 返回池的统计信息(用于监控) func (o *UtilLangChain) Stats() (current, max int) { return o.poolSize, o.poolSize } func getUrl(c *config.Config) string { baseURL := c.Ollama.BaseURL envURL := os.Getenv("OLLAMA_BASE_URL") if envURL != "" { baseURL = envURL } return baseURL } var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") func randStr(n int) string { b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) }