123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- package compapi
- import (
- "errors"
- "fmt"
- "wechat-api/ent"
- "wechat-api/internal/types"
- "github.com/openai/openai-go"
- )
- type MismatchClient struct {
- StdClient
- ResformatConfig ResponseFormatConfig
- }
- func (me *MismatchClient) ResponseFormatSetting(req *types.CompApiReq) ResponseFormatConfig {
- //Message重构的配置
- me.ResformatConfig.SysmesArgs = []any{req.Variables["missed"]}
- me.ResformatConfig.SysmesTmpl = ` # 任务介绍
- 公司在用程序巡检电话通话记录时,有些用户的回复内容没有被关键词库识别到,请根据以下通话记录,给出一些类似的回复,以及对关键词库的具体建议,非常感谢!
- # 输出要求
- 1. 用户意图:结合上下文,首先考虑可能的语音识别错误并纠错,然后分析未识别内容的根本意图,用户说这句话的背后真实目的是什么,结合上下文挖掘用户最深的意图,请不要停留在表面的意思。
- 2. 类似回复:首先应包含未识别的内容原文:%s。其次生成尽可能多的类似意图的回复。
- 2. 关键词库:从类似回复中抽取特征词,要求有两点:一是特征词应尽可能的准确,即要覆盖住此类问题,又要尽可能避免和其他语境相冲突。二是需要注重通用性,一些专有名词、人名、地名、产品名、公司名等需要排除在外。
- 3. 正则表达式:对关键词词进行解耦,用正则表达式进行表示,专注于主要内容,排除次要内容
- # 注意事项
- 1. 应贴近真实的外呼场景,用户的素质参差不齐,请避免使用任何浮夸的用词,避免使用高级词汇,避免使用任何的礼貌用语或敬语,适当的低素质些,请至少给出20条结果
- 2. 通话记录为录音转译,因此可能有错别字或音同字不同的情况(例如:借和接),请根据上下文分析后就成可能的错字错误
- 3. 正则关键词库和正则表达式中也应该考虑到音同字不同的情况,且避免使用匹配次数相关的语法如{0,2}`
- me.ResformatConfig.UsermesArgs = []any{req.Variables["chat_history"], req.Variables["missed"]}
- me.ResformatConfig.UsermesTmpl = `
- # 通话记录%s
- # 可能识别有误的内容:%s`
- //ResponseFormat设置的配置
- me.ResformatConfig.ResformatDesc = "从通话记录中提取表单"
- me.ResformatConfig.ResformatTxt = `{
- "user_intent": str, #用户意图
- "similar_reply": list[str], #类似回复
- "keywords": list[str], #关键词库
- "regular": list[str], #正则表达式
- }`
- me.ResformatConfig.ResformatStruct = struct {
- UserIntent string `json:"user_intent" jsonschema_description:"用户意图"`
- SimilarReply []string `json:"similar_reply" jsonschema_description:"类似回复"`
- Keywords []string `json:"keywords" jsonschema_description:"关键词库"`
- Regular []string `json:"regular" jsonschema_description:"正则表达式"`
- }{}
- me.ResformatConfig.HaveSet = true
- return me.ResformatConfig
- }
- // 向Client.getClientActFace工厂方法注册
- func init() {
- builder := func(c *Client) (clientActionFace, error) {
- return &MismatchClient{StdClient: StdClient{Client: c}}, nil
- }
- err := RegisterClient("mismatch", builder)
- if err != nil {
- //panic(fmt.Sprintf("Failed to register client type 'mismatch': %v", err))
- }
- }
- type ResponseFormatConfig struct {
- SysmesTmpl string `json:"sysmes_tmpl" jsonschema_description:"系统消息文本模板"`
- SysmesArgs []any `json:"sysmes_args" jsonschema_description:"系统消息文本参数列表"`
- UsermesTmpl string `json:"usermes_tmpl" jsonschema_description:"用户消息文本模板"`
- UsermesArgs []any `json:"usermes_args" jsonschema_description:"用户消息文本参数列表"`
- ResformatDesc string `json:"resformat_desc" jsonschema_description:"response_format的介绍描述"`
- ResformatTxt string `json:"resformat_txt" jsonschema_description:"response_format文本版本"`
- ResformatStruct any `json:"resformat_struct" jsonschema_description:"response_format结构体版本"`
- HaveSet bool
- }
- type StringGen func() string
- func CreateStringGen(format string, args ...interface{}) StringGen {
- return func() string {
- return fmt.Sprintf(format, args...)
- }
- }
- /*
- {"response_format":
- {"type":"json_schema",
- "json_schema":
- {"description":"从通话记录中提取表单",
- "name":"keyword_schema",
- "schema":
- {"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false,
- "properties":
- {"keywords":
- {"description":"关键词库","items":{"type":"string"},"type":"array"},
- "regular":
- {"description":"正则表达式","items":{"type":"string"},"type":"array"},
- "similar_reply":
- {"description":"类似回复","items":{"type":"string"},"type":"array"},
- "user_intent":{"description":"用户意图","type":"string"}
- },
- "required":
- ["user_intent","similar_reply","keywords","regular"],
- "type":"object"
- },
- "strict":true
- }
- }
- }
- {"response_format":
- {"type":"json_object"}
- }
- */
- func (me *MismatchClient) RebuildMessage(openaiModel bool) []types.StdCompMessage {
- newMessSlice := make([]types.StdCompMessage, 2)
- newMessSlice[0] = types.StdCompMessage{Role: "system",
- Content: CreateStringGen(me.ResformatConfig.SysmesTmpl, me.ResformatConfig.SysmesArgs...)()}
- resFormatStr := ""
- if !openaiModel {
- resFormatStr = CreateStringGen(me.ResformatConfig.ResformatTxt)()
- }
- newMessSlice[1] = types.StdCompMessage{Role: "user",
- Content: CreateStringGen(me.ResformatConfig.UsermesTmpl, me.ResformatConfig.UsermesArgs...)() + resFormatStr}
- return newMessSlice
- }
- func (me *MismatchClient) BuildJsonSchemaParam() (*openai.ResponseFormatJSONSchemaJSONSchemaParam, error) {
- schema, err := GenerateSchemaFromValue(me.ResformatConfig.ResformatStruct)
- if err != nil {
- return nil, err
- }
- schemaParam := openai.ResponseFormatJSONSchemaJSONSchemaParam{
- Name: "keyword_schema",
- Description: openai.String(me.ResformatConfig.ResformatDesc),
- Schema: schema,
- Strict: openai.Bool(true),
- }
- return &schemaParam, nil
- }
- func (me *MismatchClient) GetResFormat(openaiModel bool) (any, error) {
- if openaiModel { //针对openai兼容api 设置JsonSchema 定制化输出结构
- schemaParam, err := me.BuildJsonSchemaParam()
- if err != nil {
- return nil, err
- }
- return &openai.ResponseFormatJSONSchemaParam{JSONSchema: *schemaParam}, nil
- } else { //针对不兼容比如Deepseek系设置JsonObject
- return &openai.ResponseFormatJSONObjectParam{Type: "json_object"}, nil
- }
- }
- func (me *MismatchClient) BuildRequest(req *types.CompApiReq) error {
- //设置相关个性化ResponseFormat配置信息
- if !me.ResformatConfig.HaveSet {
- me.ResponseFormatSetting(req)
- }
- openaiModel := IsOpenaiModel(req.Model)
- //重构req.Message
- req.Messages = me.RebuildMessage(openaiModel)
- //设置req.ResponseFormat
- var err error
- req.ResponseFormat, err = me.GetResFormat(openaiModel)
- return err
- }
- func (me *MismatchClient) CallbackPrepare(params any) ([]byte, error) {
- taskData, ok := params.(*ent.CompapiAsynctask)
- if !ok {
- return nil, errors.New("invalid callback taskdata")
- }
- type OutResult struct {
- InternalID string `json:"internal_id"`
- ExternalID string `json:"external_id"`
- ChatID string `json:"chat_id"`
- EventType string `json:"event_type"`
- Content string `json:"content"`
- }
- res := OutResult{}
- res.InternalID = fmt.Sprintf("%d", taskData.ID)
- res.ExternalID = taskData.ResponseChatItemID
- res.EventType = taskData.EventType
- res.ChatID = taskData.ChatID
- var err error
- res.Content, err = NewChatResult(taskData.ResponseRaw).GetContentJsonStr()
- if err != nil {
- return nil, err
- }
- return WrapJSON(res, "", false)
- }
|