当前位置: 首页 > news >正文

基于SSE技术加 deepseek 实现打字机回复效果

具体代码,已上传到厂库

git clone https://gitee.com/rush_peng/sse-deepseek-demo.git

SSE 技术

go 后端实现

package mainimport ("bufio""bytes""encoding/json""fmt""log""net/http""strings""time"
)// Message 表示聊天消息结构
type Message struct {Role    string `json:"role"`Content string `json:"content"`
}// GenerateRequest 生成请求结构
type GenerateRequest struct {Model    string         `json:"model"`Prompt   string         `json:"prompt"`Stream   bool           `json:"stream"`Options  map[string]any `json:"options"`Messages []Message      `json:"messages"`
}// ResponseMessage 响应消息结构
type ResponseMessage struct {Role     string `json:"role"`Content  string `json:"content"` // chat模式下返回的内容Thinking string `json:"thinking"`
}// DeepseekResponse Deepseek API响应结构
type DeepseekResponse struct {Model      string          `json:"model"`Done       bool            `json:"done"`CreateAt   time.Time       `json:"create_at"`ResMessage ResponseMessage `json:"message"`Response   string          `json:"response"` // completion模式下返回的内容
}// cat 模式下, 需要存储历史聊天消息
var chartHistory []Messageconst (AskTypeCompletion = "completion"AskTypeChat       = "chat"
)// deepseekStream 通过Deepseek API进行流式生成响应
func deepseekStream(w http.ResponseWriter, prompt string, askType string) (err error) {var apiURL stringvar completeResponse strings.Builder // 存储模型完整回复,使用strings.Builder 高效拼接字符串var getResContest func(resp DeepseekResponse) stringreqBody := GenerateRequest{Model:  "deepseek-r1:8b",Stream: true,Options: map[string]any{"max_tokens":  1024,"temperature": 0.3,"top_p":       0.9,"raw":         true, // 这个如果false,会屏蔽 Thinking 字段},}switch askType {case AskTypeCompletion:apiURL = "http://localhost:11434/api/generate"reqBody.Prompt = promptgetResContest = func(resp DeepseekResponse) string {return resp.Response}case AskTypeChat:apiURL = "http://localhost:11434/api/chat"chartHistory = append(chartHistory, Message{Role: "user", Content: prompt})// 加入模型回复到历史记录defer func() {if err == nil {fmt.Println("模型的完整回复:", completeResponse.String())chartHistory = append(chartHistory, Message{Role: "assistant", Content: completeResponse.String()})}}()reqBody.Messages = chartHistorygetResContest = func(resp DeepseekResponse) string {return resp.ResMessage.Content}}fmt.Printf("接受到了发送到的请求....%+v\n", reqBody)data, _ := json.Marshal(reqBody)resp, err := http.Post(apiURL, "application/json", bytes.NewReader(data))if err != nil {return err}defer resp.Body.Close()reader := bufio.NewReader(resp.Body)flusher, _ := w.(http.Flusher)for {line, err := reader.ReadBytes('\n')if err != nil {break}lineStr := strings.TrimSpace(string(line))if lineStr == "" {continue}var part DeepseekResponseif err := json.Unmarshal(line, &part); err != nil {continue}fmt.Printf("原始返回:%+v\n", part)// 检查是否完成,输出完了,跳出循环if part.Done {break}if content := getResContest(part); content != "" {// 一般浏览器都兼容,直接发送字符串即可,不需要逐字符发送// for _, ch := range content {// 	fmt.Fprintf(w, "data: %c\n\n", ch) //将字节写入 io.write 中// 	flusher.Flush()// 	fmt.Println("输出后端返回结果:", content)// 	time.Sleep(1 * time.Millisecond) // 可调节打字机速度// }fmt.Fprintf(w, "data: %s\n\n", content) //将字节写入 io.write 中flusher.Flush()completeResponse.WriteString(content) // 累加模型回复}}fmt.Fprintf(w, "data: \n\n")fmt.Fprintf(w, "data: __END__\n\n") //自定义的结束符,前端代码根据这个结束符判断是否结束flusher.Flush()return nil
}// chatHandler 处理聊天请求的HTTP处理器
func chatHandler(w http.ResponseWriter, r *http.Request) {flusher, ok := w.(http.Flusher)if !ok {http.Error(w, "Streaming not supported", http.StatusInternalServerError)return}// sse 响应头设置,基本都是固定的w.Header().Set("Access-Control-Allow-Origin", "*")  // 测试过程中,允许所有域名跨域访问w.Header().Set("Content-Type", "text/event-stream") // 要实现打字机效果,事件流格式比如为 text/event-streamw.Header().Set("Cache-Control", "no-cache")         // 禁用缓存,确保实时更新w.Header().Set("Connection", "keep-alive")          // sse 保持连接userMessage := r.URL.Query().Get("msg")if userMessage == "" {userMessage = "Hello DeepSeek"}err := deepseekStream(w, userMessage, AskTypeChat)if err != nil {fmt.Fprintf(w, "data: %s\n\n", err.Error())flusher.Flush()}
}func main() {http.HandleFunc("/chat", chatHandler)fmt.Println("Server started at :8080")log.Fatal(http.ListenAndServe(":8080", nil))
}
http://www.rkmt.cn/news/62083.html

相关文章:

  • 龙哥量化:初版简单的通达信公式回测案例_macd趋势策略收益曲线,展示在通达信的主副图(展示部分代码)
  • 代码随想录算法训练营第三章 哈希表part02
  • 龙哥量化:简单的通达信公式回测案例_macd趋势策略收益曲线,展示在通达信的主副图(展示部分代码)
  • AE表达式
  • 【论文阅读】DeltaLag: Learning Dynamic Lead-Lag Patterns in Financial Markets
  • AI元人文:从价值对齐到价值共生的范式革命,及其在社会治理中的实践验证
  • INFINI Labs 产品更新 - Coco AI v0.9 与 Easysearch v2.0 全新功能上线,全面支持 GitLab 合并请求(MR)自动 AI Review
  • 【C语言】条件编译时谨慎使用枚举值
  • 手机电池突然掉电?工程师揭秘锂电池保养十大误区,延长续航200%的冷知识!
  • Proxifier代理游戏加速器
  • Ai元人文:从心所欲不逾矩
  • CSES1448-Maximum Building II
  • 快速排序板子
  • CF1774F2
  • PostgreSQL权限管理实践
  • 预编译命令
  • 本地环境自建的es重启,http和https访问es,nested数据类型及设置es别名
  • 迈向人机共育的文明语法:AI元人文理论体系深度阐释——内观照叙事模型
  • Intellij扩展列表
  • 251126好好学习 天天向上
  • 干扰素信号通路:从JAK-STAT到科研应用
  • 2025年11月室外木塑地板厂家,共挤木塑地板厂家,wpc木塑地板厂家品牌推荐:市政工程合作优选企业
  • ABC396 VP总结
  • Zelda
  • Day 28 类的定义和手段
  • SetSkeletalMesh优化问题
  • NOIP 模板大赛(没写完)
  • Day25CSS精灵
  • 11/26
  • 关于生育问题的初步看法