2026-05-18 19:19:17 +08:00
|
|
|
|
package prompt
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
2026-05-20 11:36:39 +08:00
|
|
|
|
"prompts-core/consts/public"
|
2026-05-18 19:19:17 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
"prompts-core/common/util"
|
|
|
|
|
|
"prompts-core/dao"
|
2026-05-20 11:36:39 +08:00
|
|
|
|
"prompts-core/model/dto"
|
2026-05-18 19:19:17 +08:00
|
|
|
|
"prompts-core/model/entity"
|
|
|
|
|
|
|
2026-05-23 18:08:09 +08:00
|
|
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
2026-05-18 19:19:17 +08:00
|
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-23 18:08:09 +08:00
|
|
|
|
// UserPromptPayload 用户提示词请求体
|
|
|
|
|
|
type UserPromptPayload struct {
|
|
|
|
|
|
Model string `json:"model"`
|
|
|
|
|
|
PromptInfo string `json:"promptInfo"`
|
2026-05-29 17:54:19 +08:00
|
|
|
|
Form any `json:"form"`
|
2026-05-23 18:08:09 +08:00
|
|
|
|
UserForm any `json:"userForm"`
|
|
|
|
|
|
Consult []dto.ConsultItem `json:"consult"`
|
|
|
|
|
|
UserFilesText map[string]string `json:"userFilesText"`
|
|
|
|
|
|
Skills string `json:"skills"`
|
2026-05-27 09:36:26 +08:00
|
|
|
|
BuildType int `json:"buildType"`
|
2026-05-23 18:08:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// buildInferenceRequest 构建推理请求
|
2026-05-22 09:49:46 +08:00
|
|
|
|
func buildInferenceRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, history []map[string]any) (map[string]any, error) {
|
2026-05-29 17:54:19 +08:00
|
|
|
|
//1) 处理表单分批
|
2026-05-22 09:49:46 +08:00
|
|
|
|
processedReq, totalBatches, err := ProcessUserFormBatches(ctx, req, aiModel)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("处理用户表单分批失败: %w", err)
|
|
|
|
|
|
}
|
2026-05-18 19:19:17 +08:00
|
|
|
|
ir := NewPromptIR()
|
|
|
|
|
|
switch req.BuildType {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
case public.BuildTypePrompt:
|
2026-05-22 09:49:46 +08:00
|
|
|
|
return buildPromptTypeRequest(ctx, processedReq, aiModel, chatModel, history, ir, totalBatches)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
case public.BuildTypeNode:
|
2026-05-21 10:53:58 +08:00
|
|
|
|
return buildNodeTypeRequest(ctx, req, chatModel, ir)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
default:
|
|
|
|
|
|
return nil, errors.New("不支持的构建类型")
|
|
|
|
|
|
}
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// buildPromptTypeRequest 构建提示词类型请求(BuildType=1)
|
2026-05-22 09:49:46 +08:00
|
|
|
|
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) {
|
2026-05-29 17:54:19 +08:00
|
|
|
|
//1) 构建系统提示词
|
|
|
|
|
|
systemPrompt := promptBuildWithRounds(ctx, req, chatModel, aiModel, totalBatches)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
ir.AddSystem(systemPrompt)
|
2026-05-29 17:54:19 +08:00
|
|
|
|
//2) 构建历史对话
|
2026-05-20 11:36:39 +08:00
|
|
|
|
for _, msg := range history {
|
|
|
|
|
|
role := gconv.String(msg["role"])
|
|
|
|
|
|
if role != "user" && role != "assistant" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
ir.AddHistory(role, gconv.String(msg["content"]))
|
|
|
|
|
|
}
|
2026-05-22 09:49:46 +08:00
|
|
|
|
userPrompt := buildUserPrompt(ctx, req, util.GetModelPrompt(ctx, aiModel.ModelType))
|
2026-05-20 11:36:39 +08:00
|
|
|
|
ir.AddUser(userPrompt)
|
2026-05-22 09:49:46 +08:00
|
|
|
|
if !checkOverallContent(ir, aiModel) {
|
|
|
|
|
|
availableWindow := util.GetAvailableWindow(aiModel.TokenConfig)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
return nil, fmt.Errorf("整体内容超出模型窗口大小限制(可用窗口=%d tokens),请精简后重试", availableWindow)
|
|
|
|
|
|
}
|
2026-05-22 09:49:46 +08:00
|
|
|
|
return compileToProviderRequest(ctx, ir, chatModel)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// buildNodeTypeRequest 构建节点类型请求(BuildType=2)
|
2026-05-21 10:53:58 +08:00
|
|
|
|
func buildNodeTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, ir *PromptIR) (map[string]any, error) {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
ir.AddUser(NodeBuild(ctx, req))
|
2026-05-22 09:49:46 +08:00
|
|
|
|
return compileToProviderRequest(ctx, ir, chatModel)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
2026-05-18 19:19:17 +08:00
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// compileToProviderRequest 编译为 Provider 请求
|
2026-05-22 09:49:46 +08:00
|
|
|
|
func compileToProviderRequest(ctx context.Context, ir *PromptIR, chatModel *entity.AsynchModel) (map[string]any, error) {
|
|
|
|
|
|
protocol, err := GetProtocolByProvider(ctx, chatModel.OperatorName)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
if err != nil {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
return nil, fmt.Errorf("获取协议配置失败: %w", err)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
if protocol == nil {
|
|
|
|
|
|
return nil, errors.New("协议配置不存在")
|
|
|
|
|
|
}
|
2026-05-21 10:53:58 +08:00
|
|
|
|
providerReq, err := Compile(ir, protocol, chatModel)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
if err != nil {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
return nil, fmt.Errorf("编译请求失败: %w", err)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return map[string]any{
|
2026-05-22 09:49:46 +08:00
|
|
|
|
"modelName": chatModel.ModelName,
|
2026-05-29 17:54:19 +08:00
|
|
|
|
"bizName": util.GetServerName(ctx),
|
2026-05-21 10:53:58 +08:00
|
|
|
|
"callbackUrl": util.GetCallbackURL(ctx, "/prompt/callback"),
|
2026-05-18 19:19:17 +08:00
|
|
|
|
"requestPayload": providerReq,
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-29 17:54:19 +08:00
|
|
|
|
// promptBuildWithRounds 构建系统提示词
|
|
|
|
|
|
func promptBuildWithRounds(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, batches int) string {
|
2026-05-18 19:19:17 +08:00
|
|
|
|
providerProtocol, err := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{
|
2026-05-29 17:54:19 +08:00
|
|
|
|
ProviderName: chatModel.OperatorName,
|
2026-05-18 19:19:17 +08:00
|
|
|
|
Status: 1,
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil || providerProtocol == nil {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-29 17:54:19 +08:00
|
|
|
|
outputJSON := util.JSONPretty(util.ReverseMap(aiModel.RequestMapping, map[string]any{}))
|
|
|
|
|
|
maxWindowSize := util.GetMaxWindowSize(chatModel.TokenConfig)
|
|
|
|
|
|
availableWindow := util.GetAvailableWindow(chatModel.TokenConfig)
|
|
|
|
|
|
formContent := buildUserFormContent(req.Form)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
userFormContent := buildUserFormContent(req.UserForm)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
formInfo := fmt.Sprintf(`
|
|
|
|
|
|
【系统表单(系统提示词/参数)】
|
|
|
|
|
|
%s
|
|
|
|
|
|
【用户表单全文(必须完整阅读,全部作为用户提示词来源)】
|
|
|
|
|
|
%s
|
2026-05-29 17:54:19 +08:00
|
|
|
|
`, formContent, userFormContent)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
|
|
|
|
|
|
inputInfo := fmt.Sprintf(`
|
|
|
|
|
|
目标模型: %s
|
|
|
|
|
|
%s
|
|
|
|
|
|
技能名称: %s
|
|
|
|
|
|
用户文件: %v
|
2026-05-23 18:08:09 +08:00
|
|
|
|
`, req.ModelName, formInfo, req.SkillName, req.Consult)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf(providerProtocol.SystemPromptTemplate,
|
2026-05-22 13:03:10 +08:00
|
|
|
|
req.ModelName, // %s 目标模型名称
|
|
|
|
|
|
maxWindowSize, // %d 最大窗口
|
|
|
|
|
|
availableWindow, // %d 可用窗口
|
|
|
|
|
|
outputJSON, // %s 输出结构
|
|
|
|
|
|
inputInfo, // %s 完整输入信息
|
2026-05-20 11:36:39 +08:00
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-05-18 19:19:17 +08:00
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// 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()
|
2026-05-18 19:19:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// 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 {
|
2026-05-23 18:08:09 +08:00
|
|
|
|
payload := UserPromptPayload{
|
|
|
|
|
|
Model: req.ModelName,
|
|
|
|
|
|
PromptInfo: prompt,
|
2026-05-29 17:54:19 +08:00
|
|
|
|
Form: prepareUserFormPayload(req.Form),
|
2026-05-23 18:08:09 +08:00
|
|
|
|
UserForm: prepareUserFormPayload(req.UserForm),
|
|
|
|
|
|
Consult: req.Consult,
|
|
|
|
|
|
UserFilesText: ExtractFileTexts(ctx, req.Consult),
|
|
|
|
|
|
Skills: SkillMdContent(ctx, req.SkillName),
|
2026-05-27 09:36:26 +08:00
|
|
|
|
BuildType: req.BuildType,
|
2026-05-18 19:19:17 +08:00
|
|
|
|
}
|
2026-05-23 18:08:09 +08:00
|
|
|
|
return gjson.New(payload).String()
|
2026-05-18 19:19:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// 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()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 19:19:17 +08:00
|
|
|
|
// NodeBuild 节点构建
|
2026-05-20 11:36:39 +08:00
|
|
|
|
func NodeBuild(ctx context.Context, req *dto.ComposeMessagesReq) string {
|
|
|
|
|
|
promptTpl := util.GetBuildPrompt(ctx)
|
2026-05-18 19:19:17 +08:00
|
|
|
|
if promptTpl == "" {
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
2026-05-20 11:36:39 +08:00
|
|
|
|
|
2026-05-18 19:19:17 +08:00
|
|
|
|
formStr := util.FormToJSON(req.Form)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
userFormStr := util.UserFormToJSON(req.UserForm)
|
|
|
|
|
|
|
2026-05-18 19:19:17 +08:00
|
|
|
|
return fmt.Sprintf(promptTpl, formStr, userFormStr)
|
|
|
|
|
|
}
|