用go让离线的linux server用上deepseek
专栏:web开发笔记 May 9, 2025, 6:56 p.m. 8 阅读
linux通过windows中转来访问deepseek

一、windows server端

package main

import (
	"net"
	"bufio"
	"fmt"
	"net/http"
	"encoding/json"
	"strings"
	"io"
)

type ChatResponse struct {
	Choices []struct {
		Delta struct {
			Content string `json:"content"`
		} `json:"delta"`
	} `json:"choices"`
}

func process(conn net.Conn) {
	defer conn.Close() // 关闭连接
	remoteAddr := conn.RemoteAddr().String()
	
	for {
		//----------------------------------
		//receive question from client
		reader := bufio.NewReader(conn)
		var buf [128]byte
		n, err := reader.Read(buf[:]) // 读取数据
		if err != nil {
			if err == io.EOF {
				//正常退出
				fmt.Println("client " + remoteAddr + " leaved! bye!")
			}else{
				//异常退出
				fmt.Println("read from client failed, err:", err)
			}
			break
		}
		recvStr := string(buf[:n])
		question := recvStr
		fmt.Println("问题:", question)

		//----------------------------------
		//call deepseek api
		myapikey := "<your-deepseek-api-key>"
		url := "https://api.deepseek.com/chat/completions"
		mymodel := "deepseek-chat"
		systemrole := "<系统角色描述>"
		
		payload := fmt.Sprintf(`{
                    "model": "%s",
                    "messages": [
                        {
                            "role": "system",
                            "content": "%s"
                        },
                        {
                            "role": "user",
                            "content": "%s"
                        }
                    ],
                    "stream": true,
                    "temperature": 0.3
                }`, mymodel, systemrole, question)

		//fmt.Print(payload)
		
		req, _ := http.NewRequest("POST", url, strings.NewReader(payload))
		req.Header.Add("Content-Type", "application/json")
		req.Header.Add("Authorization", "Bearer " + myapikey)

		res, _ := http.DefaultClient.Do(req)
		defer res.Body.Close()

		scanner := bufio.NewReader(res.Body)
		for {
			line, err := scanner.ReadString('\n')
			if err != nil {
				break
			}

			line = strings.TrimSpace(line)
			if line == "" || line == "data: [DONE]" {
				continue
			}
			if strings.HasPrefix(line, "data: ") {
				line = strings.TrimPrefix(line, "data: ")
			}
			
			var response ChatResponse
			if err := json.Unmarshal([]byte(line), &response); err != nil {
				continue
			}
			if len(response.Choices) > 0 {
				content := response.Choices[0].Delta.Content
				if content != "" {
					conn.Write([]byte(content))
				}
			}
		}

		//end of answer
		conn.Write([]byte("ENDENDEND\n"))
	}
}

func main() {
	listen, err := net.Listen("tcp", "0.0.0.0:8001")
	if err != nil {
		fmt.Println("listen failed, err:", err)
		return
	}
	fmt.Println("AI Server is up!")
	
	for {
		conn, err := listen.Accept() // 建立连接
		if err != nil {
			fmt.Println("accept failed, err:", err)
			continue
		}
		
		remoteAddr := conn.RemoteAddr().String()
		fmt.Println("Client " + remoteAddr + " is connected!")
		
		go process(conn) // 启动一个goroutine处理连接
	}
}

二、linux client端

package main

import (
	"os"
	"net"
	"strings"
	"regexp"
	"fmt"
)

func main() {
    conn, err := net.Dial("tcp", "<server-ip:port>")
    if err != nil {
        fmt.Println("err :", err)
        return
    }
    defer conn.Close()

    //get question
    question := os.Args[1]
    strings.Trim(question, "\"")
	
    //ask server
    _, err = conn.Write([]byte(question))
    if err != nil {
        return
    }
	    
    //collect answer from server
    for {
        buf := [512]byte{}
	n, err := conn.Read(buf[:])
	if err != nil {
	    fmt.Println("recv failed, err:", err)
	    return
	}

	if n > 0 {
	    //fmt.Println(string(buf[:n]))
	    s := string(buf[:n])
	    //fmt.Print(s)

	    //end of answer
	    r, _ := regexp.Compile(`ENDENDEND`)
            if r.MatchString(s) {
		fmt.Print(r.ReplaceAllString(s, ""))
		break
	    }else{
		fmt.Print(s)
	    }
	}
    }
}

三、命令行调用

aiserversingle "<here is my question>"

四、emacs配置

(defun askai ()
  "ask ai"
  (interactive)
  (progn
    (setq q (read-string "Question? " nil nil ""))
    (setq mycmd (concat "aiserversingle \"" q "\""))
    (switch-to-buffer "*scratch*")
    (goto-char (point-max))
    (call-process-shell-command mycmd nil t t)))

(global-set-key (kbd "s-q") 'askai)

 

感谢阅读,更多文章点击这里:【专栏:web开发笔记】
最新20篇 开设专栏