2026-05-18 19:19:17 +08:00
|
|
|
|
package prompt
|
2026-05-12 13:59:15 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
2026-06-08 18:01:54 +08:00
|
|
|
|
"prompts-core/service/session"
|
2026-05-20 11:36:39 +08:00
|
|
|
|
|
|
|
|
|
|
"prompts-core/common/util"
|
|
|
|
|
|
"prompts-core/consts/public"
|
|
|
|
|
|
"prompts-core/dao"
|
|
|
|
|
|
"prompts-core/model/dto"
|
|
|
|
|
|
"prompts-core/model/entity"
|
|
|
|
|
|
"prompts-core/service/gateway"
|
2026-06-03 18:37:18 +08:00
|
|
|
|
|
2026-06-10 16:32:42 +08:00
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/beans"
|
|
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/utils"
|
2026-06-03 18:37:18 +08:00
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
2026-06-05 11:00:05 +08:00
|
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
2026-05-12 13:59:15 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-18 19:19:17 +08:00
|
|
|
|
// ComposeMessages 核心拼接提示词主流程
|
2026-05-20 11:36:39 +08:00
|
|
|
|
func ComposeMessages(ctx context.Context, req *dto.ComposeMessagesReq) (*dto.ComposeMessagesRes, error) {
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 1) 获取模型信息
|
2026-05-18 19:19:17 +08:00
|
|
|
|
chatModel, aiModel, err := GetModelMessage(ctx, req)
|
2026-05-12 13:59:15 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 2) 校验用户表单
|
2026-05-22 09:49:46 +08:00
|
|
|
|
if err = validateUserForm(req, aiModel); err != nil {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return handleBuild(ctx, req, chatModel, aiModel)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
2026-05-12 13:59:15 +08:00
|
|
|
|
|
2026-05-22 09:49:46 +08:00
|
|
|
|
// GetModelMessage 获取模型信息
|
2026-06-03 13:30:39 +08:00
|
|
|
|
func GetModelMessage(ctx context.Context, req *dto.ComposeMessagesReq) (*gateway.AsynchModel, *gateway.AsynchModel, error) {
|
2026-05-22 09:49:46 +08:00
|
|
|
|
userInfo, err := utils.GetUserInfo(ctx)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, nil, fmt.Errorf("获取用户信息失败: %w", err)
|
|
|
|
|
|
}
|
2026-06-03 13:30:39 +08:00
|
|
|
|
chatModel, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
2026-05-27 09:36:26 +08:00
|
|
|
|
SQLBaseDO: beans.SQLBaseDO{Creator: userInfo.UserName},
|
2026-06-03 13:30:39 +08:00
|
|
|
|
IsChatModel: 1,
|
2026-05-27 09:36:26 +08:00
|
|
|
|
})
|
2026-06-03 18:37:18 +08:00
|
|
|
|
if err != nil || chatModel == nil {
|
2026-05-27 09:36:26 +08:00
|
|
|
|
return nil, nil, errors.New("当前没有对话模型,请添加")
|
|
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
|
|
|
|
|
|
aiModel, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
2026-06-03 13:30:39 +08:00
|
|
|
|
SQLBaseDO: beans.SQLBaseDO{TenantId: userInfo.TenantId, Creator: userInfo.UserName},
|
2026-05-27 09:36:26 +08:00
|
|
|
|
ModelName: req.ModelName,
|
|
|
|
|
|
})
|
2026-06-03 18:37:18 +08:00
|
|
|
|
if err != nil || aiModel == nil {
|
2026-05-27 09:36:26 +08:00
|
|
|
|
return nil, nil, errors.New("需要构建的模型不存在")
|
|
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
|
|
|
|
|
|
return chatModel, aiModel, nil
|
2026-05-22 09:49:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
// validateUserForm 校验用户表单
|
2026-06-03 13:30:39 +08:00
|
|
|
|
func validateUserForm(req *dto.ComposeMessagesReq, model *gateway.AsynchModel) error {
|
2026-05-20 11:36:39 +08:00
|
|
|
|
if len(req.UserForm) == 0 {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
isValid, exceedTokens, err := util.CheckUserFormWithinWindow(req.UserForm, model.TokenConfig)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("校验用户表单失败: %w", err)
|
|
|
|
|
|
}
|
2026-05-12 13:59:15 +08:00
|
|
|
|
|
2026-05-20 11:36:39 +08:00
|
|
|
|
if !isValid {
|
|
|
|
|
|
availableWindow := util.GetAvailableWindow(model.TokenConfig)
|
|
|
|
|
|
return fmt.Errorf("UserForm 内容超出窗口大小: 超出 %d tokens,可用窗口 %d tokens,请精简后重试",
|
|
|
|
|
|
exceedTokens, availableWindow)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// handleBuild 通用构建处理
|
|
|
|
|
|
func handleBuild(ctx context.Context, req *dto.ComposeMessagesReq, chatModel, aiModel *gateway.AsynchModel) (*dto.ComposeMessagesRes, error) {
|
|
|
|
|
|
// 1) 处理表单分批
|
2026-06-10 14:51:25 +08:00
|
|
|
|
processedReq, _, err := ProcessUserFormBatches(ctx, req, aiModel)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
if err != nil {
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return nil, fmt.Errorf("处理用户表单分批失败: %w", err)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
2026-05-27 09:36:26 +08:00
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 2) 构建推理请求
|
|
|
|
|
|
ir := NewPromptIR()
|
|
|
|
|
|
var taskReq map[string]any
|
|
|
|
|
|
switch req.BuildType {
|
|
|
|
|
|
case public.BuildTypePrompt:
|
2026-06-10 14:51:25 +08:00
|
|
|
|
taskReq, err = buildPromptTypeRequest(ctx, processedReq, aiModel, chatModel, ir)
|
2026-06-03 18:37:18 +08:00
|
|
|
|
case public.BuildTypeNode:
|
|
|
|
|
|
taskReq, err = buildNodeTypeRequest(ctx, req, chatModel, ir)
|
|
|
|
|
|
case public.BuildTypeStruct:
|
|
|
|
|
|
taskReq, err = buildStructTypeRequest(ctx, req, chatModel, ir)
|
|
|
|
|
|
default:
|
|
|
|
|
|
return nil, errors.New("不支持的构建类型")
|
|
|
|
|
|
}
|
2026-05-27 09:36:26 +08:00
|
|
|
|
if err != nil {
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return nil, fmt.Errorf("构建推理请求失败: %w", err)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 3) 调用网关创建任务
|
|
|
|
|
|
taskID, err := gateway.CreateGatewayTask(ctx, taskReq)
|
2026-05-20 11:36:39 +08:00
|
|
|
|
if err != nil {
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return nil, fmt.Errorf("创建网关任务失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if taskID == "" {
|
|
|
|
|
|
return nil, errors.New("网关未返回taskId")
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 4) 保存任务记录
|
|
|
|
|
|
if _, err = dao.ComposeTask.Insert(ctx, &entity.ComposeTask{
|
2026-05-20 11:36:39 +08:00
|
|
|
|
TaskId: taskID,
|
|
|
|
|
|
ModelName: req.ModelName,
|
|
|
|
|
|
SkillName: req.SkillName,
|
2026-05-22 09:49:46 +08:00
|
|
|
|
BuildType: req.BuildType,
|
|
|
|
|
|
CallbackUrl: req.CallbackUrl,
|
2026-06-10 14:51:25 +08:00
|
|
|
|
RequestPayload: gconv.Map(req),
|
2026-05-20 11:36:39 +08:00
|
|
|
|
Status: public.ComposeStatusPending,
|
2026-06-03 18:37:18 +08:00
|
|
|
|
}); err != nil {
|
|
|
|
|
|
return nil, err
|
2026-05-15 09:45:51 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return &dto.ComposeMessagesRes{TaskId: taskID}, nil
|
2026-05-20 11:36:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// Callback 回调处理
|
|
|
|
|
|
func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
|
|
|
|
|
g.Log().Infof(ctx, "[开始回调处理] taskId=%s state=%d", req.TaskId, req.State)
|
|
|
|
|
|
// 1) 查询任务
|
|
|
|
|
|
composeTask, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{TaskId: req.TaskId})
|
2026-05-15 09:45:51 +08:00
|
|
|
|
if err != nil {
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return fmt.Errorf("查询任务失败: %w", err)
|
2026-05-12 13:59:15 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 2) 处理失败
|
|
|
|
|
|
if req.State == 3 {
|
|
|
|
|
|
return handleCallbackFailed(ctx, req, composeTask)
|
2026-05-12 13:59:15 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// 3) 处理成功
|
|
|
|
|
|
if req.State == 2 {
|
|
|
|
|
|
return handleCallbackSuccess(ctx, req, composeTask)
|
2026-05-12 13:59:15 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return nil
|
2026-05-22 09:49:46 +08:00
|
|
|
|
}
|
2026-05-15 09:45:51 +08:00
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
// handleCallbackFailed 处理回调失败
|
|
|
|
|
|
func handleCallbackFailed(ctx context.Context, req *dto.CallbackReq, composeTask *entity.ComposeTask) error {
|
|
|
|
|
|
_, err := dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
|
|
|
|
|
TaskId: req.TaskId,
|
|
|
|
|
|
Status: public.ComposeStatusFailed,
|
|
|
|
|
|
ErrorMessage: req.ErrorMsg,
|
|
|
|
|
|
GatewayState: req.State,
|
|
|
|
|
|
OssFile: req.OssFile,
|
|
|
|
|
|
FileType: req.FileType,
|
2026-06-09 15:46:09 +08:00
|
|
|
|
ResultJson: req.Messages,
|
2026-05-22 09:49:46 +08:00
|
|
|
|
})
|
2026-06-03 18:37:18 +08:00
|
|
|
|
if composeTask.CallbackUrl != "" {
|
|
|
|
|
|
composeTask.Status = public.ComposeStatusFailed
|
|
|
|
|
|
composeTask.ErrorMessage = req.ErrorMsg
|
2026-06-05 11:00:05 +08:00
|
|
|
|
_ = gateway.SendCallback(ctx, composeTask, 0)
|
2026-05-22 09:49:46 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 14:51:25 +08:00
|
|
|
|
// handleCallbackSuccess 处理回调成功
|
2026-06-03 18:37:18 +08:00
|
|
|
|
func handleCallbackSuccess(ctx context.Context, req *dto.CallbackReq, composeTask *entity.ComposeTask) error {
|
2026-06-05 11:00:05 +08:00
|
|
|
|
// 1) 获取模型配置
|
2026-06-03 13:30:39 +08:00
|
|
|
|
model, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
2026-05-23 18:08:09 +08:00
|
|
|
|
SQLBaseDO: beans.SQLBaseDO{Creator: composeTask.Creator},
|
|
|
|
|
|
ModelName: composeTask.ModelName,
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("查询模型失败: %w", err)
|
2026-05-22 09:49:46 +08:00
|
|
|
|
}
|
2026-06-03 18:37:18 +08:00
|
|
|
|
|
2026-06-10 10:16:58 +08:00
|
|
|
|
// 2) 获取协议配置
|
|
|
|
|
|
protocol, _ := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{
|
|
|
|
|
|
ProviderName: model.OperatorName,
|
|
|
|
|
|
Status: 1,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-06-10 14:51:25 +08:00
|
|
|
|
// 3) 获取历史消息 + 保存当前轮
|
2026-06-10 10:16:58 +08:00
|
|
|
|
payload := composeTask.RequestPayload
|
|
|
|
|
|
sessionId := gconv.String(payload["sessionId"])
|
|
|
|
|
|
nodeId := gconv.String(payload["nodeId"])
|
|
|
|
|
|
var history []dto.FlatMessage
|
2026-06-10 14:51:25 +08:00
|
|
|
|
var epicycleId int64
|
|
|
|
|
|
|
|
|
|
|
|
if sessionId != "" && nodeId != "" && model.ModelType == public.ModelTypeInference {
|
|
|
|
|
|
// 3.1 获取历史
|
2026-06-10 10:16:58 +08:00
|
|
|
|
h, _ := session.GetHistoryMessages(ctx, &dto.GetHistoryMessagesReq{
|
|
|
|
|
|
SessionId: sessionId,
|
|
|
|
|
|
NodeId: nodeId,
|
|
|
|
|
|
})
|
|
|
|
|
|
if h != nil {
|
|
|
|
|
|
history = h.Messages
|
|
|
|
|
|
}
|
2026-06-10 14:51:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 3.2 保存当前轮(先存,下次查询就能拿到)
|
|
|
|
|
|
if userMsg := util.ExtractUserText(req.Messages); userMsg != nil {
|
|
|
|
|
|
epicycleId, _ = dao.ComposeSession.Insert(ctx, &entity.ComposeSession{
|
|
|
|
|
|
NodeId: nodeId,
|
|
|
|
|
|
SessionId: sessionId,
|
|
|
|
|
|
RequestContent: userMsg,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2026-06-10 10:16:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4) 合并附加结构
|
2026-06-08 18:01:54 +08:00
|
|
|
|
messages := util.MergeConsult(composeTask.RequestPayload, req.Messages, model.ExtendMapping)
|
2026-06-10 14:51:25 +08:00
|
|
|
|
// 5) 注入历史
|
|
|
|
|
|
if len(history) > 0 {
|
2026-06-10 10:16:58 +08:00
|
|
|
|
messages = InjectHistory(messages, history, protocol)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6) 更新数据库
|
2026-06-03 18:37:18 +08:00
|
|
|
|
_, err = dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
|
|
|
|
|
TaskId: req.TaskId,
|
|
|
|
|
|
Status: public.ComposeStatusSuccess,
|
|
|
|
|
|
GatewayState: req.State,
|
|
|
|
|
|
OssFile: req.OssFile,
|
|
|
|
|
|
FileType: req.FileType,
|
2026-06-09 15:46:09 +08:00
|
|
|
|
ResultJson: messages,
|
2026-06-03 18:37:18 +08:00
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
2026-05-22 09:49:46 +08:00
|
|
|
|
return err
|
2026-05-12 13:59:15 +08:00
|
|
|
|
}
|
2026-06-10 10:16:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 8) 回调业务方
|
2026-06-03 18:37:18 +08:00
|
|
|
|
if composeTask.CallbackUrl != "" {
|
|
|
|
|
|
composeTask.Status = public.ComposeStatusSuccess
|
2026-06-09 15:46:09 +08:00
|
|
|
|
composeTask.ResultJson = messages
|
2026-06-05 11:00:05 +08:00
|
|
|
|
_ = gateway.SendCallback(ctx, composeTask, epicycleId)
|
2026-05-12 13:59:15 +08:00
|
|
|
|
}
|
2026-06-10 10:16:58 +08:00
|
|
|
|
|
2026-06-03 18:37:18 +08:00
|
|
|
|
return nil
|
2026-05-22 09:49:46 +08:00
|
|
|
|
}
|
2026-05-21 14:23:34 +08:00
|
|
|
|
|
2026-06-10 14:51:25 +08:00
|
|
|
|
// InjectHistory 插入历史会话
|
2026-06-10 10:16:58 +08:00
|
|
|
|
func InjectHistory(roundsData map[string]any, history []dto.FlatMessage, protocol *entity.ProviderProtocol) map[string]any {
|
|
|
|
|
|
if protocol == nil || len(history) == 0 {
|
|
|
|
|
|
return roundsData
|
|
|
|
|
|
}
|
|
|
|
|
|
// 1) 提取第一轮的 messages
|
|
|
|
|
|
rounds := roundsData["rounds"].([]any)
|
|
|
|
|
|
firstRound := rounds[0].(map[string]any)
|
|
|
|
|
|
original := firstRound["messages"].([]any)
|
|
|
|
|
|
|
|
|
|
|
|
// 2) 按 merge_order 拼接
|
|
|
|
|
|
result := make([]any, 0, len(original)+len(history))
|
|
|
|
|
|
|
|
|
|
|
|
for _, part := range protocol.MergeOrder {
|
|
|
|
|
|
switch part {
|
|
|
|
|
|
case "system":
|
|
|
|
|
|
for _, m := range original {
|
|
|
|
|
|
msg := m.(map[string]any)
|
|
|
|
|
|
if gconv.String(msg["role"]) == "system" {
|
|
|
|
|
|
result = append(result, msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
case "history":
|
|
|
|
|
|
if gconv.Bool(protocol.Capabilities["support_history"]) {
|
|
|
|
|
|
for _, msg := range history {
|
|
|
|
|
|
result = append(result, map[string]any{
|
|
|
|
|
|
"role": msg.Role,
|
|
|
|
|
|
"content": msg.Content, // 纯字符串,不转换
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
case "user":
|
|
|
|
|
|
for _, m := range original {
|
|
|
|
|
|
msg := m.(map[string]any)
|
|
|
|
|
|
if gconv.String(msg["role"]) == "user" {
|
|
|
|
|
|
result = append(result, msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3) 角色映射
|
|
|
|
|
|
if len(protocol.RoleMapping) > 0 {
|
|
|
|
|
|
for _, m := range result {
|
|
|
|
|
|
msg := m.(map[string]any)
|
|
|
|
|
|
role := gconv.String(msg["role"])
|
|
|
|
|
|
if mapped, ok := protocol.RoleMapping[role]; ok {
|
|
|
|
|
|
msg["role"] = mapped
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4) 直接修改原对象
|
|
|
|
|
|
firstRound["messages"] = result
|
|
|
|
|
|
return roundsData
|
|
|
|
|
|
}
|
2026-06-10 14:51:25 +08:00
|
|
|
|
|
|
|
|
|
|
// GetComposeTask 查询任务结果
|
|
|
|
|
|
func GetComposeTask(ctx context.Context, taskID string) (*dto.GetComposeTaskRes, error) {
|
|
|
|
|
|
record, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{
|
|
|
|
|
|
TaskId: taskID,
|
|
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("查询任务失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return &dto.GetComposeTaskRes{
|
|
|
|
|
|
TaskId: record.TaskId,
|
|
|
|
|
|
Status: record.Status,
|
|
|
|
|
|
ErrorMessage: record.ErrorMessage,
|
|
|
|
|
|
Messages: record.ResultJson,
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
}
|