私有化大模型配置说明及接口约定
小程序配置说明如下图所示:
需要进入到 乐天派App,设备- AI语音引擎 – 第三方,进入配置私有化大模型信息,将 “设置为语音引擎” 勾选上,语音跟机器人对话时,除了机器人基础控制语句,其他语音将会默认进入第三方私有化大模型接口。
注:图中BaseUrl(私有化大模型接口地址)需要支持公网可访问
下面说明下私有化大模型接口的调用约定:
注:接口采用SSE(Server-Sent Events) 的流式请求
1. 请求方法
POST
2. 请求content-Type
application/json
3. 请求body为json格式,参数如下
参数名 | 类型 | 描述 |
app_id | string | App中配置的app_id |
api_key | string | App中配置的api_key |
api_secret | string | App中配置的api_secret |
content | string | 请求的问题内容 |
sn | string | 当前设备的唯一标记sn |
请求body示例:
{
"app_id":"123",
"api_key":"236",
"api_secret":"234",
"content":"如何成为一个产品经理",
"sn":"20010136U0100"
}
4. 返回数据格式约定:
event 为固定的 message
data 为json格式字符串
data中的json格式如下:
字段名 | 类型 | 描述 |
code | int | 返回code码,0代表正常,其他非0值均代表失败 |
msg | string | 请求返回信息,返回失败时将报错信息放到msg里面,机器人会将此错误播报出来 |
data | object | 实体内容, 见下方表格 |
json中的data 实体内容的结构:
字段名 | 类型 | 描述 |
content | string | 私有大模型返回的回答文本,可能是一部分 |
is_end | bool | false 代表未结束,true 代表结束 |
返回示例:
event: message
data: {"code":0,"msg":"success","data":{"content":"我是","is_end":false}}
event: message
data: {"code":0,"msg":"success","data":{"content":"乐天派公司","is_end":false}}
event: message
data: {"code":0,"msg":"success","data":{"content":"研发的","is_end":false}}
event: message
data: {"code":0,"msg":"success","data":{"content":"私有化","is_end":false}}
event: message
data: {"code":0,"msg":"success","data":{"content":"大模型","is_end":true}}
5. demo sdk如下:
目前只提供了golang的版本,其他版本稍后提供
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
)
type Req struct {
AppId string `json:"app_id"` // 配置的appid
ApiKey string `json:"api_key"` // 配置的apikey
ApiSecret string `json:"api_secret"` // 配置的apisecret
Content string `json:"content"` // 请求大模型的文字
Sn string `json:"sn"` // 机器唯一码
}
type Data struct {
Content string `json:"content"` // 返回文字内容
IsEnd bool `json:"is_end"` // 是否结束
}
type RespData struct {
Code int `json:"code"` // 状态码
Msg string `json:"msg"` // 返回提示信息
Data *Data `json:"data"` // 返回的结构数据
}
func getSendMsg(code int, msg, content string, isEnd bool) (string, error) {
d := &Data{
Content: content,
IsEnd: isEnd,
}
respD := &RespData{
Code: code,
Msg: msg,
Data: d,
}
respB, err := json.Marshal(respD)
return string(respB), err
}
var answerListAll = [][]string{
[]string{
"我是",
"乐天派公司",
"研发的",
"私有化",
"大模型",
},
[]string{
"我不知道",
"你说的",
"是什么,",
"我只是一个",
"私有化大模型",
},
[]string{
"沁园春·雪",
"【作者】毛泽东 ",
"北国风光,千里冰封,万里雪飘。",
"望长城内外,惟余莽莽;大河上下,顿失滔滔。",
"山舞银蛇,原驰蜡象,欲与天公试比高。",
"须晴日,看红装素裹,分外妖娆。",
"江山如此多娇,引无数英雄竞折腰。",
"惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。",
"一代天骄,成吉思汗,只识弯弓射大雕。",
"俱往矣,数风流人物,还看今朝。",
},
[]string{
"要成为一个优秀的产品经理,必须在心里要有一个“大我”和一个“小我”,可能你要经历这两个过程的磨练,才能找到原因背后的原因。",
"什么是“大我”?我的意思是,一个产品经理要把自己当成CEO。在《兄弟连》里面的连长的绰号就是CEO。",
"产品经理要对一个产品负责,虽然挂的是经理的头衔,但行驶的是总经理的职责。",
"因为要对产品负责,产品经理还要经常去协调很多部门,要去推动很多不归他管的人和事,比如要跟设计打交道,要跟技术打交道,要跟测试打交道,还要去了解用户的想法,还要去跟市场谈支持。",
"产品经理要操的心,一点也不比一个总经理少。所以,我说一个优秀的产品经理首先要把自己当成一个CEO,既要负责执行、推动(Executive),也要负责用户体验(Experience)。可能别人不拿",
"你当回事,但你自己心里的有一个“大我”。你对一个产品负责,你的title不重要,但是你一定要把这个责任担负起来。",
"所以,优秀的产品经理不必在意今天管了多少人,不必在意自己的头衔是高是低,关键最是你能不能利用公司里的资源,不管采用什么手段,最后做出来一个好产品。",
"上亿的用户选择使用你做出来的产品,就是对产品经理最大的认可。",
"在其他相同的条件下,你做的产品有上亿用户用,他做的产品只有几百万人用,那么你就比他要优秀。",
"可能你会说,我是想做一个优秀的产品经理,但我太年轻,没什么资历。我觉得,产品经理要非常自信,你要有这种气势。",
"一个优秀的产品经理心中有“大我”,但同时还得不断地“小我”,甚至要“忘我”。这就是说,",
"产品经理要忘掉自己。根据我的经验,产品经理最容易犯的几个错误,包括今天我都还在犯,就是产品做着做着,就不是给用户做产品,而是给自己做产品,给同事做产品,给领导做产品了。",
"所以,产品经理心中要做到“小我”,甚至“忘我”,就是产品经理必须身临其境,把自己当成一个典型用户,让自己精神分裂,这样才能体会用户心中真正的想法和需求。",
},
}
func handleSSE(w http.ResponseWriter, r *http.Request) {
// 设置响应头,指定SSE的Content-Type
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// 读取body
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError)
return
}
// 处理body
fmt.Printf("收到请求的body数据:%s\n", body)
var reqB Req
err = json.Unmarshal(body, &reqB)
fmt.Println("reqB:", reqB)
answerList := answerListAll[0]
content := reqB.Content
fmt.Println("content:", content)
// 模拟大模型匹配答案
if strings.Contains(content, "是谁") || strings.Contains(content, "大模型") {
answerList = answerListAll[0]
} else if strings.Contains(content, "园春") {
answerList = answerListAll[2]
} else if strings.Contains(content, "产品经理") {
answerList = answerListAll[3]
} else {
answerList = answerListAll[1]
}
// 模拟实时数据推送
for idx, partAnswer := range answerList {
isEnd := false
if idx == len(answerList)-1 {
isEnd = true
}
respStr, err := getSendMsg(0, "success", partAnswer, isEnd)
if err != nil {
respStr, _ = getSendMsg(1, "获取返回信息出错", "", true)
}
SendEventMessage(w, "message", respStr)
// 模拟加的延时,真实调用要去掉
time.Sleep(500 * time.Millisecond)
}
}
// 发送事件消息
func SendEventMessage(w http.ResponseWriter, eventType, eventData string) {
// 构造 Event Source 格式的消息
message := "event: " + eventType + "\n"
message += "data: " + eventData + "\n\n"
fmt.Println(message)
// 将消息写入响应主体
_, err := io.WriteString(w, message)
if err != nil {
// 发生错误,停止发送消息
return
}
// 刷新响应主体,确保数据被发送
flusher, ok := w.(http.Flusher)
if ok {
flusher.Flush()
}
}
func main() {
http.HandleFunc("/sse", handleSSE)
// 启动Web服务,监听在 localhost:8012
fmt.Println("服务正在监听: http://127.0.0.1:8012")
if err := http.ListenAndServe(":8012", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}