package prompt import ( "context" "errors" "fmt" "prompts-core/consts/public" "strings" "prompts-core/common/util" "prompts-core/dao" "prompts-core/model/dto" "prompts-core/model/entity" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/util/gconv" ) // UserPromptPayload 用户提示词请求体 type UserPromptPayload struct { Model string `json:"model"` PromptInfo string `json:"promptInfo"` Form any `json:"form"` UserForm any `json:"userForm"` Consult []dto.ConsultItem `json:"consult"` UserFilesText map[string]string `json:"userFilesText"` Skills string `json:"skills"` BuildType int `json:"buildType"` } // buildInferenceRequest 构建推理请求 func buildInferenceRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, history []map[string]any) (map[string]any, error) { //1) 处理表单分批 processedReq, totalBatches, err := ProcessUserFormBatches(ctx, req, aiModel) if err != nil { return nil, fmt.Errorf("处理用户表单分批失败: %w", err) } ir := NewPromptIR() switch req.BuildType { case public.BuildTypePrompt: return buildPromptTypeRequest(ctx, processedReq, aiModel, chatModel, history, ir, totalBatches) case public.BuildTypeNode: return buildNodeTypeRequest(ctx, req, chatModel, ir) default: return nil, errors.New("不支持的构建类型") } } // buildPromptTypeRequest 构建提示词类型请求(BuildType=1) func buildPromptTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, aiModel *entity.AsynchModel, chatModel *entity.AsynchModel, history []map[string]any, ir *PromptIR, totalBatches int) (map[string]any, error) { //1) 构建系统提示词 systemPrompt := promptBuildWithRounds(ctx, req, chatModel, aiModel, totalBatches) ir.AddSystem(systemPrompt) //2) 构建历史对话 for _, msg := range history { role := gconv.String(msg["role"]) if role != "user" && role != "assistant" { continue } ir.AddHistory(role, gconv.String(msg["content"])) } userPrompt := buildUserPrompt(ctx, req, util.GetModelPrompt(ctx, aiModel.ModelType)) ir.AddUser(userPrompt) if !checkOverallContent(ir, aiModel) { availableWindow := util.GetAvailableWindow(aiModel.TokenConfig) return nil, fmt.Errorf("整体内容超出模型窗口大小限制(可用窗口=%d tokens),请精简后重试", availableWindow) } return compileToProviderRequest(ctx, ir, chatModel) } // buildNodeTypeRequest 构建节点类型请求(BuildType=2) func buildNodeTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, ir *PromptIR) (map[string]any, error) { ir.AddUser(NodeBuild(ctx, req)) return compileToProviderRequest(ctx, ir, chatModel) } // compileToProviderRequest 编译为 Provider 请求 func compileToProviderRequest(ctx context.Context, ir *PromptIR, chatModel *entity.AsynchModel) (map[string]any, error) { protocol, err := GetProtocolByProvider(ctx, chatModel.OperatorName) if err != nil { return nil, fmt.Errorf("获取协议配置失败: %w", err) } if protocol == nil { return nil, errors.New("协议配置不存在") } providerReq, err := Compile(ir, protocol, chatModel) if err != nil { return nil, fmt.Errorf("编译请求失败: %w", err) } return map[string]any{ "modelName": chatModel.ModelName, "bizName": util.GetServerName(ctx), "callbackUrl": util.GetCallbackURL(ctx, "/prompt/callback"), "requestPayload": providerReq, }, nil } // promptBuildWithRounds 构建系统提示词 func promptBuildWithRounds(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, batches int) string { providerProtocol, err := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{ ProviderName: chatModel.OperatorName, Status: 1, }) if err != nil || providerProtocol == nil { return "" } outputJSON := util.JSONPretty(util.ReverseMap(aiModel.RequestMapping, map[string]any{})) maxWindowSize := util.GetMaxWindowSize(chatModel.TokenConfig) availableWindow := util.GetAvailableWindow(chatModel.TokenConfig) formContent := buildUserFormContent(req.Form) userFormContent := buildUserFormContent(req.UserForm) formInfo := fmt.Sprintf(` 【系统表单(系统提示词/参数)】 %s 【用户表单全文(必须完整阅读,全部作为用户提示词来源)】 %s `, formContent, userFormContent) inputInfo := fmt.Sprintf(` 目标模型: %s %s 技能名称: %s 用户文件: %v `, req.ModelName, formInfo, req.SkillName, req.Consult) return fmt.Sprintf(providerProtocol.SystemPromptTemplate, req.ModelName, // %s 目标模型名称 maxWindowSize, // %d 最大窗口 availableWindow, // %d 可用窗口 outputJSON, // %s 输出结构 inputInfo, // %s 完整输入信息 ) } // buildUserFormContent 构建用户表单内容字符串 func buildUserFormContent(userForm []map[string]any) string { var builder strings.Builder for _, item := range userForm { builder.WriteString(fmt.Sprintf("%v\n", item)) } return builder.String() } // checkOverallContent 检查整体内容是否超出窗口 func checkOverallContent(ir *PromptIR, model *entity.AsynchModel) bool { fullContent := ir.String() return util.CountToken(fullContent, model.TokenConfig) } // buildUserPrompt 构建用户提示词 func buildUserPrompt(ctx context.Context, req *dto.ComposeMessagesReq, prompt string) string { payload := UserPromptPayload{ Model: req.ModelName, PromptInfo: prompt, Form: prepareUserFormPayload(req.Form), UserForm: prepareUserFormPayload(req.UserForm), Consult: req.Consult, UserFilesText: ExtractFileTexts(ctx, req.Consult), Skills: SkillMdContent(ctx, req.SkillName), BuildType: req.BuildType, } return gjson.New(payload).String() } // prepareUserFormPayload 准备用户表单载荷 func prepareUserFormPayload(userForm []map[string]any) any { if len(userForm) == 0 { return nil } if _, ok := userForm[0]["batch_index"]; ok { return userForm } return mergeUserFormTexts(userForm) } // mergeUserFormTexts 合并 UserForm 中的所有文本内容 func mergeUserFormTexts(userForm []map[string]any) string { var builder strings.Builder for i, item := range userForm { text := getItemText(item) if i > 0 { builder.WriteString("\n\n") } builder.WriteString(text) } return builder.String() } // NodeBuild 节点构建 func NodeBuild(ctx context.Context, req *dto.ComposeMessagesReq) string { promptTpl := util.GetBuildPrompt(ctx) if promptTpl == "" { return "" } formStr := util.FormToJSON(req.Form) userFormStr := util.UserFormToJSON(req.UserForm) return fmt.Sprintf(promptTpl, formStr, userFormStr) }