mismatch.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package compapi
  2. import (
  3. "errors"
  4. "fmt"
  5. "wechat-api/ent"
  6. "wechat-api/internal/types"
  7. "github.com/openai/openai-go"
  8. )
  9. type MismatchClient struct {
  10. StdClient
  11. ResformatConfig ResponseFormatConfig
  12. }
  13. func (me *MismatchClient) ResponseFormatSetting(req *types.CompApiReq) ResponseFormatConfig {
  14. //Message重构的配置
  15. me.ResformatConfig.SysmesArgs = []any{req.Variables["missed"]}
  16. me.ResformatConfig.SysmesTmpl = ` # 任务介绍
  17. 公司在用程序巡检电话通话记录时,有些用户的回复内容没有被关键词库识别到,请根据以下通话记录,给出一些类似的回复,以及对关键词库的具体建议,非常感谢!
  18. # 输出要求
  19. 1. 用户意图:结合上下文,首先考虑可能的语音识别错误并纠错,然后分析未识别内容的根本意图,用户说这句话的背后真实目的是什么,结合上下文挖掘用户最深的意图,请不要停留在表面的意思。
  20. 2. 类似回复:首先应包含未识别的内容原文:%s。其次生成尽可能多的类似意图的回复。
  21. 2. 关键词库:从类似回复中抽取特征词,要求有两点:一是特征词应尽可能的准确,即要覆盖住此类问题,又要尽可能避免和其他语境相冲突。二是需要注重通用性,一些专有名词、人名、地名、产品名、公司名等需要排除在外。
  22. 3. 正则表达式:对关键词词进行解耦,用正则表达式进行表示,专注于主要内容,排除次要内容
  23. # 注意事项
  24. 1. 应贴近真实的外呼场景,用户的素质参差不齐,请避免使用任何浮夸的用词,避免使用高级词汇,避免使用任何的礼貌用语或敬语,适当的低素质些,请至少给出20条结果
  25. 2. 通话记录为录音转译,因此可能有错别字或音同字不同的情况(例如:借和接),请根据上下文分析后就成可能的错字错误
  26. 3. 正则关键词库和正则表达式中也应该考虑到音同字不同的情况,且避免使用匹配次数相关的语法如{0,2}`
  27. me.ResformatConfig.UsermesArgs = []any{req.Variables["chat_history"], req.Variables["missed"]}
  28. me.ResformatConfig.UsermesTmpl = `
  29. # 通话记录%s
  30. # 可能识别有误的内容:%s`
  31. //ResponseFormat设置的配置
  32. me.ResformatConfig.ResformatDesc = "从通话记录中提取表单"
  33. me.ResformatConfig.ResformatTxt = `{
  34. "user_intent": str, #用户意图
  35. "similar_reply": list[str], #类似回复
  36. "keywords": list[str], #关键词库
  37. "regular": list[str], #正则表达式
  38. }`
  39. me.ResformatConfig.ResformatStruct = struct {
  40. UserIntent string `json:"user_intent" jsonschema_description:"用户意图"`
  41. SimilarReply []string `json:"similar_reply" jsonschema_description:"类似回复"`
  42. Keywords []string `json:"keywords" jsonschema_description:"关键词库"`
  43. Regular []string `json:"regular" jsonschema_description:"正则表达式"`
  44. }{}
  45. me.ResformatConfig.HaveSet = true
  46. return me.ResformatConfig
  47. }
  48. // 向Client.getClientActFace工厂方法注册
  49. func init() {
  50. builder := func(c *Client) (clientActionFace, error) {
  51. return &MismatchClient{StdClient: StdClient{Client: c}}, nil
  52. }
  53. err := RegisterClient("mismatch", builder)
  54. if err != nil {
  55. //panic(fmt.Sprintf("Failed to register client type 'mismatch': %v", err))
  56. }
  57. }
  58. type ResponseFormatConfig struct {
  59. SysmesTmpl string `json:"sysmes_tmpl" jsonschema_description:"系统消息文本模板"`
  60. SysmesArgs []any `json:"sysmes_args" jsonschema_description:"系统消息文本参数列表"`
  61. UsermesTmpl string `json:"usermes_tmpl" jsonschema_description:"用户消息文本模板"`
  62. UsermesArgs []any `json:"usermes_args" jsonschema_description:"用户消息文本参数列表"`
  63. ResformatDesc string `json:"resformat_desc" jsonschema_description:"response_format的介绍描述"`
  64. ResformatTxt string `json:"resformat_txt" jsonschema_description:"response_format文本版本"`
  65. ResformatStruct any `json:"resformat_struct" jsonschema_description:"response_format结构体版本"`
  66. HaveSet bool
  67. }
  68. type StringGen func() string
  69. func CreateStringGen(format string, args ...interface{}) StringGen {
  70. return func() string {
  71. return fmt.Sprintf(format, args...)
  72. }
  73. }
  74. /*
  75. {"response_format":
  76. {"type":"json_schema",
  77. "json_schema":
  78. {"description":"从通话记录中提取表单",
  79. "name":"keyword_schema",
  80. "schema":
  81. {"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false,
  82. "properties":
  83. {"keywords":
  84. {"description":"关键词库","items":{"type":"string"},"type":"array"},
  85. "regular":
  86. {"description":"正则表达式","items":{"type":"string"},"type":"array"},
  87. "similar_reply":
  88. {"description":"类似回复","items":{"type":"string"},"type":"array"},
  89. "user_intent":{"description":"用户意图","type":"string"}
  90. },
  91. "required":
  92. ["user_intent","similar_reply","keywords","regular"],
  93. "type":"object"
  94. },
  95. "strict":true
  96. }
  97. }
  98. }
  99. {"response_format":
  100. {"type":"json_object"}
  101. }
  102. */
  103. func (me *MismatchClient) RebuildMessage(openaiModel bool) []types.StdCompMessage {
  104. newMessSlice := make([]types.StdCompMessage, 2)
  105. newMessSlice[0] = types.StdCompMessage{Role: "system",
  106. Content: CreateStringGen(me.ResformatConfig.SysmesTmpl, me.ResformatConfig.SysmesArgs...)()}
  107. resFormatStr := ""
  108. if !openaiModel {
  109. resFormatStr = CreateStringGen(me.ResformatConfig.ResformatTxt)()
  110. }
  111. newMessSlice[1] = types.StdCompMessage{Role: "user",
  112. Content: CreateStringGen(me.ResformatConfig.UsermesTmpl, me.ResformatConfig.UsermesArgs...)() + resFormatStr}
  113. return newMessSlice
  114. }
  115. func (me *MismatchClient) BuildJsonSchemaParam() (*openai.ResponseFormatJSONSchemaJSONSchemaParam, error) {
  116. schema, err := GenerateSchemaFromValue(me.ResformatConfig.ResformatStruct)
  117. if err != nil {
  118. return nil, err
  119. }
  120. schemaParam := openai.ResponseFormatJSONSchemaJSONSchemaParam{
  121. Name: "keyword_schema",
  122. Description: openai.String(me.ResformatConfig.ResformatDesc),
  123. Schema: schema,
  124. Strict: openai.Bool(true),
  125. }
  126. return &schemaParam, nil
  127. }
  128. func (me *MismatchClient) GetResFormat(openaiModel bool) (any, error) {
  129. if openaiModel { //针对openai兼容api 设置JsonSchema 定制化输出结构
  130. schemaParam, err := me.BuildJsonSchemaParam()
  131. if err != nil {
  132. return nil, err
  133. }
  134. return &openai.ResponseFormatJSONSchemaParam{JSONSchema: *schemaParam}, nil
  135. } else { //针对不兼容比如Deepseek系设置JsonObject
  136. return &openai.ResponseFormatJSONObjectParam{Type: "json_object"}, nil
  137. }
  138. }
  139. func (me *MismatchClient) BuildRequest(req *types.CompApiReq) error {
  140. //设置相关个性化ResponseFormat配置信息
  141. if !me.ResformatConfig.HaveSet {
  142. me.ResponseFormatSetting(req)
  143. }
  144. openaiModel := IsOpenaiModel(req.Model)
  145. //重构req.Message
  146. req.Messages = me.RebuildMessage(openaiModel)
  147. //设置req.ResponseFormat
  148. var err error
  149. req.ResponseFormat, err = me.GetResFormat(openaiModel)
  150. return err
  151. }
  152. func (me *MismatchClient) CallbackPrepare(params any) ([]byte, error) {
  153. taskData, ok := params.(*ent.CompapiAsynctask)
  154. if !ok {
  155. return nil, errors.New("invalid callback taskdata")
  156. }
  157. type OutResult struct {
  158. InternalID string `json:"internal_id"`
  159. ExternalID string `json:"external_id"`
  160. ChatID string `json:"chat_id"`
  161. EventType string `json:"event_type"`
  162. Content string `json:"content"`
  163. }
  164. res := OutResult{}
  165. res.InternalID = fmt.Sprintf("%d", taskData.ID)
  166. res.ExternalID = taskData.ResponseChatItemID
  167. res.EventType = taskData.EventType
  168. res.ChatID = taskData.ChatID
  169. var err error
  170. res.Content, err = NewChatResult(taskData.ResponseRaw).GetContentJsonStr()
  171. if err != nil {
  172. return nil, err
  173. }
  174. return WrapJSON(res, "", false)
  175. }