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) }