快速开始:首个 LLM 调用

安装

mkdir llm-demo && cd llm-demo
go mod init example.com/llm-demo
go get github.com/tmc/langchaingo

langchaingo 采用模块化设计,核心包之外按需引入具体 provider(如 llms/openai)。

配置模型

langchaingo 的 openai 包兼容一切 OpenAI 协议的服务(OpenAI 官方、Azure、DeepSeek、通义千问、Ollama 等):

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

func main() {
    llm, err := openai.New(
        openai.WithToken("你的 API Key"),           // 建议改读环境变量
        openai.WithBaseURL("https://api.openai.com/v1"), // 可替换为兼容服务地址
        openai.WithModel("gpt-4o-mini"),
    )
    if err != nil {
        log.Fatal(err)
    }
    _ = llm
}
Tip

密钥管理:openai.New() 无参调用时默认读取 OPENAI_API_KEY 环境变量。生产服务应通过环境变量或密钥管理系统注入,切勿将 Key 写入代码或提交到仓库。

首个补全

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

func main() {
    ctx := context.Background()

    llm, err := openai.New()
    if err != nil {
        log.Fatal(err)
    }

    prompt := "用一句话解释 Go 语言的 goroutine。"
    completion, err := llms.GenerateFromSinglePrompt(ctx, llm, prompt)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(completion)
}
OPENAI_API_KEY=sk-xxx go run main.go

多轮消息对话

GenerateContent 接收结构化消息列表,支持 system / human / AI 角色:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

func main() {
    ctx := context.Background()
    llm, err := openai.New()
    if err != nil {
        log.Fatal(err)
    }

    resp, err := llm.GenerateContent(ctx, []llms.MessageContent{
        llms.TextParts(llms.ChatMessageTypeSystem, "你是一名严谨的 Go 技术顾问,回答保持简洁。"),
        llms.TextParts(llms.ChatMessageTypeHuman, "context 包的作用是什么?"),
        llms.TextParts(llms.ChatMessageTypeAI, "context 用于在请求链路中传递取消信号与超时控制。"),
        llms.TextParts(llms.ChatMessageTypeHuman, "给一个最典型的使用场景。"),
    })
    if err != nil {
        log.Fatal(err)
    }

    for _, choice := range resp.Choices {
        fmt.Println(choice.Content)
    }
}

流式输出

流式(streaming)让响应逐段到达,是交互式应用的标准体验。通过 WithStreamingFunc 接收增量内容:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

func main() {
    ctx := context.Background()
    llm, err := openai.New(
        openai.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
            fmt.Print(string(chunk)) // 逐段打印增量
            return nil
        }),
    )
    if err != nil {
        log.Fatal(err)
    }

    _, err = llms.GenerateFromSinglePrompt(ctx, llm,
        "写一首关于 Go 并发编程的四行短诗。")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println()
}

与 Go 服务集成

将 LLM 调用封装为 HTTP 接口,并施加超时控制

package main

import (
    "context"
    "encoding/json"
    "log"
    "net/http"
    "time"

    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

type req struct {
    Question string `json:"question"`
}

func main() {
    llm, err := openai.New()
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("POST /api/ask", func(w http.ResponseWriter, r *http.Request) {
        var in req
        if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
            http.Error(w, "请求体不合法", http.StatusBadRequest)
            return
        }

        ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
        defer cancel()

        answer, err := llms.GenerateFromSinglePrompt(ctx, llm, in.Question)
        if err != nil {
            http.Error(w, "模型调用失败", http.StatusBadGateway)
            return
        }
        _ = json.NewEncoder(w).Encode(map[string]string{"answer": answer})
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}
Warning

LLM 响应延迟通常在秒级,远高于普通 RPC。务必为每次调用设置 context 超时;面向终端用户的接口建议使用流式转发,避免网关或客户端超时中断。

小结

  • openai.New() 统一接入 OpenAI 兼容服务;GenerateFromSinglePromptGenerateContent 分别对应单轮补全与多轮对话。
  • WithStreamingFunc 实现流式输出。
  • 模型调用必须包裹超时 context,并结合 goroutine 做并发编排。