开发个人Go-ChatGPT--5 模型管理 (一)

开发个人Go-ChatGPT--5 模型管理 (一)

背景

开发一个chatGPT的网站,后端服务如何实现与大模型的对话?是整个项目中开发困难较大的点。

如何实现上图的聊天对话功能?在开发后端的时候,如何实现stream的响应呢?本文就先介绍后端的原理,逐步攻克这个课题。

环境部署

  • 启动ollamadocker run -d -p 3000:8080 -p 11434:11434 -v ollama:/root/.ollama -v open-webui:/app/backend/data --name open-webui --restart always ollama/ollama

  • ollama 下载对话模型: docker exec -it open-webui ollama run gemma:2b

    bash 复制代码
    pulling manifest 
    pulling c1864a5eb193... 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 1.7 GB                         
    pulling 097a36493f71... 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ 8.4 KB                         
    pulling 109037bec39c... 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏  136 B                         
    pulling 22a838ceb7fb... 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏   84 B                         
    pulling 887433b89a90... 100% ▕████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏  483 B                         
    verifying sha256 digest 
    writing manifest 
    removing any unused layers 
    success

Stream reponse

前端

svelte 复制代码
        ....
        const [res, controller] = await generateChatCompletion(localStorage.token, {
            model: model,
            messages: messagesBody,
            options: {
                ...($settings.options ?? {})
            },
            format: $settings.requestFormat ?? undefined,
            keep_alive: $settings.keepAlive ?? undefined,
            docs: docs.length > 0 ? docs : undefined
        });

        if (res && res.ok) {
            console.log('controller', controller);

            const reader = res.body
                .pipeThrough(new TextDecoderStream())
                .pipeThrough(splitStream('\n'))
                .getReader();
        ...

ollamaopen-webui 前端项目实现和人类一样沟通的方法,使用的是stream监听 messages事件收到的响应,保持长连接的状态,逐渐将收到的消息显示到前端,直到后端响应结束。

后端

  • gin.Stream
go 复制代码
...
    c.Stream(func(w io.Writer) bool {
        select {
        case msg, ok := <-msgChan:
            if !ok {
                // 如果msgChan被关闭,则结束流式传输
                return false
            }
            fmt.Print(msg)
            // 流式响应,发送给 messages 事件,和前端进行交互
            c.SSEvent("messages", msg)
            return true
        case <-c.Done():
            // 如果客户端连接关闭,则结束流式传输
            return false
        }
    })
...
  • ollama 响应
go 复制代码
...
    // llms.WithStreamingFunc 将ollama api 的响应内容逐渐返回,而不是一次性全部返回
    callOp := llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
        select {
        case msgChan <- string(chunk):
        case <-ctx.Done():
            return ctx.Err() // 返回上下文的错误
        }
        return nil
    })

    _, err := llaClient.Call(context.Background(), prompt, callOp)
    if err != nil {
        log.Fatalf("Call failed: %v", err) // 处理错误,而不是 panic
    }
...
  • 完整代码
go 复制代码
package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/ollama"
)

func main() {
    router := gin.Default()

    router.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "OK",
        })
    })

    router.POST("/chat", chat)

    router.Run(":8083")
}

type Prompt struct {
    Text string `json:"text"`
}

func chat(c *gin.Context) {
    var prompt Prompt
    if err := c.BindJSON(&prompt); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return


    }

    var msgChan = make(chan string)
    // 通过chan 将ollama 响应返回给前端
    go Generate(prompt.Text, msgChan)

    c.Stream(func(w io.Writer) bool {
        select {
        case msg, ok := <-msgChan:
            if !ok {
                // 如果msgChan被关闭,则结束流式传输
                return false
            }
            // fmt.Print(msg)
            c.SSEvent("messages", msg)
            return true
        case <-c.Done():
            // 如果客户端连接关闭,则结束流式传输
            return false
        }
    })
}

var llaClient *ollama.LLM

func init() {
    // Create a new Ollama instance
    // The model is set to "gemma:2b"
    // remote url is set to "http://ollama-ip:11434"
    url := ollama.WithServerURL("http://ollama-ip:11434")
    lla, err := ollama.New(ollama.WithModel("gemma:2b"), url)
    if err != nil {
        panic(err)
    }

    llaClient = lla

    fmt.Println("connect to ollama server successfully")
}

func Generate(prompt string, msgChan chan string) {
    // ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) // 设置超时
    // defer cancel()                                                          // 确保在函数结束时取消上下文

    callOp := llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
        select {
        case msgChan <- string(chunk):
        case <-ctx.Done():
            return ctx.Err() // 返回上下文的错误
        }
        return nil
    })

    _, err := llaClient.Call(context.Background(), prompt, callOp)
    if err != nil {
        log.Fatalf("Call failed: %v", err) // 处理错误,而不是 panic
    }

    // 确保在所有数据处理完毕后关闭 msgChan
    close(msgChan)
}

项目地址

jackwillsmith/openui-svelte-build (github.com)

GitHub - jackwillsmith/openui-backend-go: openui-backend-go

相关推荐
ToToBe7 小时前
L1G3000 提示工程(Prompt Engineering)
chatgpt·prompt
龙的爹23337 小时前
论文 | Legal Prompt Engineering for Multilingual Legal Judgement Prediction
人工智能·语言模型·自然语言处理·chatgpt·prompt
bytebeats8 小时前
我用 Spring AI 集成 OpenAI ChatGPT API 创建了一个 Spring Boot 小程序
spring boot·chatgpt·openai
于顾而言9 小时前
【笔记】Go Coding In Go Way
后端·go
qq_172805599 小时前
GIN 反向代理功能
后端·golang·go
&永恒的星河&12 小时前
Hunyuan-Large:推动AI技术进步的下一代语言模型
人工智能·语言模型·自然语言处理·chatgpt·moe·llms
follycat16 小时前
2024强网杯Proxy
网络·学习·网络安全·go
OT.Ter18 小时前
【力扣打卡系列】单调栈
算法·leetcode·职场和发展·go·单调栈
探索云原生19 小时前
GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU
ai·云原生·kubernetes·go·gpu
waiting不是违停19 小时前
LangChain Ollama实战文献检索助手(二)少样本提示FewShotPromptTemplate示例选择器
langchain·llm·ollama