前端:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Sample</title>
</head>
<body>
  <label for="textInput">Prompt:</label>
  <input type="textarea" id="textInput" placeholder="您有什么问题">
  <button onclick="run_prompt()">执行prompt</button>
  <p><textarea id="answer" rows="10" cols="50" readonly></textarea></p>
<script>
  current_text = document.getElementById('answer');
  text = "";
  char_index = 0
  function run_prompt() {
    var inputValue = document.getElementById('textInput').value;
    document.getElementById('answer').value = "";
    // 调用服务端的流式接口, 修改为自己的服务器地址和端口号
    fetch('http://<server address>:8000/eb_stream', {
      method: 'post',
      headers: {'Content-Type': 'text/plain'},
      body: JSON.stringify({'prompt': inputValue})
    })
    .then(response => {
      return response.body;
    })
    .then(body => {
      const reader = body.getReader();
      const decoder = new TextDecoder();
      function read() {
        return reader.read().then(({ done, value }) => {
          if (done) { // 读取完成
            return;
          }
          data = decoder.decode(value, { stream: true });
          text += JSON.parse(data).result;
          type();  // 打字机效果输出
          return read();
        });
      }
      return read();
    })
    .catch(error => {
      console.error('发生错误:', error);
    });
  }

  function type() {
    let enableCursor = true;  // 启用光标效果
    if (char_index < text.length) {
      let txt = document.getElementById('answer').value;
      let cursor = enableCursor ? "|" : "";
      if (enableCursor && txt.endsWith("|")) {
        txt = txt.slice(0, -1);
      }
      document.getElementById('answer').value = txt + text.charAt(char_index) + cursor;
      char_index++;
      setTimeout(type, 1000/5);  // 打字机速度控制, 每秒5个字
    }
  }
</script>
</body>
</html>
// go run main.go

package main

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

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

const (
    // 运行端口号
    SERVER_PORT = ":8000"

    // 千帆应用 AK/SK
    API_KEY    = "JMLF5n6eWqxxxx9ECh"
    SECRET_KEY = "s1ii7dGJBK5Jxxxx8iIrAET"

    // 鉴权接口URL
    TOKEN_URL = "https://aip.baidubce.com/oauth/2.0/token"
    // 大模型接口URL
    EB_URL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant"
)

// GetAccessToken 使用 AK,SK 生成鉴权签名(Access Token)
func GetAccessToken() (string, error) {
    // 拼接请求参数
    postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", API_KEY, SECRET_KEY)
    client := &http.Client{}
    req, err := http.NewRequest("POST", TOKEN_URL, strings.NewReader(postData))
    if err != nil {
        return "", fmt.Errorf("创建HTTP Request报错: %s", err.Error())
    }

    // 设置请求头
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Accept", "application/json")

    // 发起请求并获取响应
    ret, err := client.Do(req)
    if err != nil {
        return "", fmt.Errorf("执行HTTP请求报错: %s", err.Error())
    }
    defer ret.Body.Close()

    // 解析响应内容
    body, err := io.ReadAll(ret.Body)
    if err != nil {
        return "", fmt.Errorf("获取Token报错: %s", err.Error())
    }

    // 解析并返回 access_token
    accessTokenObj := map[string]string{}
    _ = json.Unmarshal([]byte(body), &accessTokenObj)
    return accessTokenObj["access_token"], nil
}

// main 主入口
func main() {
    // 初始化Gin实例
    r := gin.Default()

    // 跨域配置
    r.Use(cors.Default())

    // 注册路由
    r.POST("/eb_stream", func(c *gin.Context) {
        // 设置响应头
        c.Header("Connection", "keep-alive")
        c.Header("Cache-Control", "no-cache")
        c.Header("Transfer-Encoding", "chunked")
        c.Header("Content-Type", "text/event-stream")

        // 获取入参
        var prompt struct {
            Prompt string `json:"prompt"`
        }
        if err := c.ShouldBindJSON(&prompt); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        // 获取 Access Token
        token, err := GetAccessToken()
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        // 组装请求参数并请求文心
        source := "&sourceVer=0.0.1&source=app_center&appName=streamDemo"
        url := EB_URL + "?access_token=" + token + source
        payload := strings.NewReader(fmt.Sprintf(`{"messages":[{"role":"user","content":"%s"}],"stream":true}`, prompt.Prompt))
        resp, err := http.Post(url, "application/json", payload)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        defer resp.Body.Close()

        // 处理返回结果并写入响应流
        c.Stream(func(w io.Writer) bool {
            reader := bufio.NewReader(resp.Body)
            for {
                // 逐行读取
                line, err := reader.ReadBytes('\n')
                if err != nil {
                    log.Println("读取大模型响应数据失败: ", err.Error())
                    break
                }

                // 对读到的这行数据进行处理
                lineStr := string(line)
                if strings.HasPrefix(lineStr, "data:") {
                    lineStr = strings.TrimPrefix(lineStr, "data:")
                    lineStr = strings.TrimSuffix(lineStr, "\n")
                    log.Println("收到大模型的响应: ", lineStr)

                    // 写入响应流
                    _, err = w.Write([]byte(lineStr))
                    if err != nil {
                        log.Println("写入响应流失败: ", err.Error())
                        return false
                    }

                    // 判断是否为结束
                    if strings.Contains(lineStr, `"is_end":true`) {
                        log.Println("收到大模型反馈的结束标志")
                        break
                    }
                }

                log.Println("等待大模型下一次响应")
                return true
            }

            log.Println("流式响应结束")
            return false
        })
    })

    // 启动服务
    r.Run(SERVER_PORT)
}

ERNIE-Bot 4.0

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)
const API_KEY = "JMLF5n6eWq2xxxECh"
const SECRET_KEY = "s1ii7dGJBxxxzT8iIrAET"

func main() {

    url := "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=" + GetAccessToken()
    payload := strings.NewReader(`{"disable_search":false,"enable_citation":false}`)
    client := &http.Client {}
    req, err := http.NewRequest("POST", url, payload)

    if err != nil {
        fmt.Println(err)
        return
    }
    req.Header.Add("Content-Type", "application/json")

    res, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer res.Body.Close()

    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(body))
}

/**
 * 使用 AK,SK 生成鉴权签名(Access Token)
 * @return string 鉴权签名信息(Access Token)
 */
func GetAccessToken() string {
    url := "https://aip.baidubce.com/oauth/2.0/token"
    postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", API_KEY, SECRET_KEY)
    resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(postData))
    if err != nil {
        fmt.Println(err)
        return ""
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return ""
    }
    accessTokenObj := map[string]string{}
    json.Unmarshal([]byte(body), &accessTokenObj)
    return accessTokenObj["access_token"]
}  
03-06 07:50