Browse Source

优化串聊模式,使对话可一直进行 (#247)

suyunkai 1 year ago
parent
commit
ddb184f253
6 changed files with 64 additions and 7 deletions
  1. 7 0
      README.md
  2. 7 1
      config.example.yml
  3. 30 0
      config/config.go
  4. 4 1
      docker-compose.yml
  5. 3 3
      pkg/chatgpt/chatgpt.go
  6. 13 2
      pkg/chatgpt/context.go

+ 7 - 0
README.md

@@ -182,6 +182,7 @@ $ docker run -itd --name chatgpt -p 8090:8090 \
   -v ./data:/app/data --add-host="host.docker.internal:host-gateway" \
   -e LOG_LEVEL="info" -e APIKEY=换成你的key -e BASE_URL="" \
   -e MODEL="gpt-3.5-turbo" -e SESSION_TIMEOUT=600 \
+  -e MAX_QUESTION_LENL=4096 -e MAX_ANSWER_LEN=4096 -e MAX_TEXT=4096 \
   -e HTTP_PROXY="http://host.docker.internal:15732" \
   -e DEFAULT_MODE="单聊" -e MAX_REQUEST=0 -e PORT=8090 \
   -e SERVICE_URL="你当前服务外网可访问的URL" -e CHAT_TYPE="0" \
@@ -398,6 +399,12 @@ base_url: ""
 model: "gpt-3.5-turbo"
 # 会话超时时间,默认600秒,在会话时间内所有发送给机器人的信息会作为上下文
 session_timeout: 600
+# 最大问题长度
+max_question_len: 4096
+# 最大回答长度
+max_answer_len: 4096
+# 最大上下文文本长度,通常该参数可设置为与模型Token限制相同
+max_text: 4096
 # 指定请求时使用的代理,如果为空,则不使用代理,注意需要带上 http 协议 或 socks5 协议,如果你是用的是azure,则该配置项可以留空或者直接忽略
 http_proxy: ""
 # 指定默认的对话模式,可根据实际需求进行自定义,如果不设置,默认为单聊,即无上下文关联的对话模式

+ 7 - 1
config.example.yml

@@ -10,6 +10,12 @@ base_url: ""
 model: "gpt-3.5-turbo"
 # 会话超时时间,默认600秒,在会话时间内所有发送给机器人的信息会作为上下文
 session_timeout: 600
+# 最大问题长度
+max_question_len: 4096
+# 最大回答长度
+max_answer_len: 4096
+# 最大上下文文本长度,通常该参数可设置为与模型Token限制相同
+max_text: 4096
 # 指定请求时使用的代理,如果为空,则不使用代理,注意需要带上 http 协议 或 socks5 协议,如果你是用的是azure,则该配置项可以留空或者直接忽略
 http_proxy: ""
 # 指定默认的对话模式,可根据实际需求进行自定义,如果不设置,默认为单聊,即无上下文关联的对话模式
@@ -63,4 +69,4 @@ azure_openai_token: "xxxxxxx"
 credentials:
   -
     client_id: "put-your-client-id-here"
-    client_secret: "put-your-client-secret-here"
+    client_secret: "put-your-client-secret-here"

+ 30 - 0
config/config.go

@@ -33,6 +33,12 @@ type Configuration struct {
 	Model string `yaml:"model"`
 	// 会话超时时间
 	SessionTimeout time.Duration `yaml:"session_timeout"`
+	// 最大问题长度
+  	MaxQuestionLen int `yaml:"max_question_len"`
+	// 最大答案长度
+	MaxAnswerLen int `yaml:"max_answer_len"`
+	// 最大文本 = 问题 + 回答, 接口限制
+	MaxText int `yaml:"max_text"`
 	// 默认对话模式
 	DefaultMode string `yaml:"default_mode"`
 	// 代理地址
@@ -122,6 +128,21 @@ func LoadConfig() *Configuration {
 		} else {
 			config.SessionTimeout = time.Duration(config.SessionTimeout) * time.Second
 		}
+		maxQuestionLen := os.Getenv("MAX_QUESTION_LEN")
+		if maxQuestionLen != "" {
+			newLen, _ := strconv.Atoi(maxQuestionLen)
+			config.MaxQuestionLen = newLen
+		}
+		maxAnswerLen := os.Getenv("MAX_ANSWER_LEN")
+		if maxAnswerLen != "" {
+			newLen, _ := strconv.Atoi(maxAnswerLen)
+			config.MaxAnswerLen = newLen
+		}
+		maxText := os.Getenv("MAX_TEXT")
+		if maxText != "" {
+			newLen, _ := strconv.Atoi(maxText)
+			config.MaxText = newLen
+		}
 		defaultMode := os.Getenv("DEFAULT_MODE")
 		if defaultMode != "" {
 			config.DefaultMode = defaultMode
@@ -243,5 +264,14 @@ func LoadConfig() *Configuration {
 	if config.ServiceURL == "" {
 		logger.Fatal("config err: service url required")
 	}
+	if config.MaxQuestionLen == 0 {
+		config.MaxQuestionLen = 4096
+	}
+	if config.MaxAnswerLen == 0 {
+		config.MaxAnswerLen = 4096
+	}
+	if config.MaxText == 0 {
+		config.MaxText = 4096
+	}
 	return config
 }

+ 4 - 1
docker-compose.yml

@@ -12,6 +12,9 @@ services:
       BASE_URL: ""  # 如果你使用官方的接口地址 https://api.openai.com,则留空即可,如果你想指定请求url的地址,可通过这个参数进行配置,注意需要带上 http 协议
       MODEL: "gpt-3.5-turbo" # 指定模型,默认为 gpt-3.5-turbo , 可选参数有: "gpt-4-0314", "gpt-4", "gpt-3.5-turbo-0301", "gpt-3.5-turbo",如果使用gpt-4,请确认自己是否有接口调用白名单
       SESSION_TIMEOUT: 600 # 会话超时时间,默认600秒,在会话时间内所有发送给机器人的信息会作为上下文
+      MAX_QUESTION_LEN: 4096 # 最大问题长度,默认4096 token,正常情况默认值即可,如果使用gpt4-8k或gpt4-32k,可根据模型token上限修改。
+      MAX_ANSWER_LEN: 4096 # 最大回答长度,默认4096 token,正常情况默认值即可,如果使用gpt4-8k或gpt4-32k,可根据模型token上限修改。
+      MAX_TEXT: 4096 # 最大文本 = 问题 + 回答, 接口限制,默认4096 token,正常情况默认值即可,如果使用gpt4-8k或gpt4-32k,可根据模型token上限修改。
       HTTP_PROXY: http://host.docker.internal:15777 # 指定请求时使用的代理,如果为空,则不使用代理,注意需要带上 http 协议 或 socks5 协议
       DEFAULT_MODE: "单聊" # 指定默认的对话模式,可根据实际需求进行自定义,如果不设置,默认为单聊,即无上下文关联的对话模式
       MAX_REQUEST: 0 # 单人单日请求次数上限,默认为0,即不限制
@@ -45,4 +48,4 @@ services:
     ports:
       - "8090:8090"
     extra_hosts:
-      - host.docker.internal:host-gateway
+      - host.docker.internal:host-gateway

+ 3 - 3
pkg/chatgpt/chatgpt.go

@@ -60,9 +60,9 @@ func New(userId string) *ChatGPT {
 		client:         openai.NewClientWithConfig(config),
 		ctx:            ctx,
 		userId:         userId,
-		maxQuestionLen: 2048, // 最大问题长度
-		maxAnswerLen:   2048, // 最大答案长度
-		maxText:        4096, // 最大文本 = 问题 + 回答, 接口限制
+		maxQuestionLen: public.Config.MaxQuestionLen, // 最大问题长度
+		maxAnswerLen:   public.Config.MaxAnswerLen, // 最大答案长度
+		maxText:        public.Config.MaxText, // 最大文本 = 问题 + 回答, 接口限制
 		timeOut:        public.Config.SessionTimeout,
 		doneChan:       timeOutChan,
 		cancel: func() {

+ 13 - 2
pkg/chatgpt/context.go

@@ -161,9 +161,20 @@ func (c *ChatGPT) ChatWithContext(question string) (answer string, err error) {
 	promptTable = append(promptTable, "\n"+c.ChatContext.restartSeq+question)
 	prompt := strings.Join(promptTable, "\n")
 	prompt += c.ChatContext.startSeq
-	if tokenizer.MustCalToken(prompt) > c.maxText-c.maxAnswerLen {
-		return "", OverMaxTextLength
+	// 删除对话,直到prompt的长度满足条件
+	for tokenizer.MustCalToken(prompt) > c.maxText {
+		if len(c.ChatContext.old) > 1 { // 至少保留一条记录
+			c.ChatContext.PollConversation() // 删除最旧的一条对话
+			// 重新构建 prompt,计算长度
+			promptTable = promptTable[1:]    // 删除promptTable中对应的对话
+			prompt = strings.Join(promptTable, "\n") + c.ChatContext.startSeq
+		} else {
+			break // 如果已经只剩一条记录,那么跳出循环
+			}
 	}
+//	if tokenizer.MustCalToken(prompt) > c.maxText-c.maxAnswerLen {
+//		return "", OverMaxTextLength
+//	}
 	model := public.Config.Model
 	userId := c.userId
 	if public.Config.AzureOn {