feat: 新增模型扩展映射与查询配置字段
This commit is contained in:
@@ -3,10 +3,14 @@ package util
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/container/gvar"
|
"github.com/gogf/gf/v2/container/gvar"
|
||||||
"github.com/gogf/gf/v2/encoding/gjson"
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
|
||||||
|
tGjson "github.com/tidwall/gjson"
|
||||||
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseOutput 解析模型输出为 JSON 格式
|
// ParseOutput 解析模型输出为 JSON 格式
|
||||||
@@ -57,14 +61,15 @@ func UserFormToJSON(form []map[string]any) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustMarshal 将对象序列化为 JSON 字符串,失败时返回空对象
|
// MustMarshalToMap 将对象序列化为 map[string]any,失败时返回空 map
|
||||||
func MustMarshal(v any) string {
|
func MustMarshalToMap(v any) map[string]any {
|
||||||
b, err := json.Marshal(v)
|
b, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "{}"
|
return make(map[string]any)
|
||||||
}
|
}
|
||||||
|
var m map[string]any
|
||||||
return string(b)
|
json.Unmarshal(b, &m)
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONPretty 将任意类型转为格式化的 JSON 字符串
|
// JSONPretty 将任意类型转为格式化的 JSON 字符串
|
||||||
@@ -82,42 +87,6 @@ func JSONPretty(v any) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GvarToMap 将 *gvar.Var 类型转换为 map[string]any
|
|
||||||
func GvarToMap(v *gvar.Var) map[string]any {
|
|
||||||
if v == nil || v.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make(map[string]any)
|
|
||||||
|
|
||||||
// 方法1:尝试获取 map 值
|
|
||||||
if m := v.Map(); len(m) > 0 {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法2:尝试解析 JSON 字符串
|
|
||||||
str := v.String()
|
|
||||||
if str != "" && str != "<nil>" {
|
|
||||||
json.Unmarshal([]byte(str), &result)
|
|
||||||
if len(result) > 0 {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法3:尝试获取 interface 再转换
|
|
||||||
if val := v.Val(); val != nil {
|
|
||||||
switch val.(type) {
|
|
||||||
case map[string]any:
|
|
||||||
return val.(map[string]any)
|
|
||||||
default:
|
|
||||||
data, _ := json.Marshal(val)
|
|
||||||
json.Unmarshal(data, &result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseJSONFieldFromGvar 专门处理 *gvar.Var 类型的 JSON 字段解析
|
// ParseJSONFieldFromGvar 专门处理 *gvar.Var 类型的 JSON 字段解析
|
||||||
func ParseJSONFieldFromGvar(source any, target any) {
|
func ParseJSONFieldFromGvar(source any, target any) {
|
||||||
if source == nil {
|
if source == nil {
|
||||||
@@ -149,3 +118,112 @@ func ParseJSONFieldFromGvar(source any, target any) {
|
|||||||
json.Unmarshal(data, target)
|
json.Unmarshal(data, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MergeConsult 将 consult 附件合并到模型生成的 messages 结构中。
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - req: 请求参数 map,需包含 "consult" 字段,值为 []any,每个元素是 {"type":"xxx","url":"..."}
|
||||||
|
// - messages: 模型生成的返回结构(如 rounds[...].messages[...].content 数组)
|
||||||
|
// - extendMapping: 附加映射配置,格式:
|
||||||
|
// {"attachments": {"image": {"template": {...}, "target_path": "...", "field_mapping": {...}}, ...}}
|
||||||
|
//
|
||||||
|
// 返回值:合并后的完整 map。
|
||||||
|
func MergeConsult(req map[string]any, messages map[string]any, extendMapping map[string]any) map[string]any {
|
||||||
|
if len(req) == 0 || len(messages) == 0 || len(extendMapping) == 0 {
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(req)
|
||||||
|
msgJSON, _ := json.Marshal(messages)
|
||||||
|
extJSON, _ := json.Marshal(extendMapping)
|
||||||
|
|
||||||
|
reqStr := string(reqJSON)
|
||||||
|
msgStr := string(msgJSON)
|
||||||
|
extStr := string(extJSON)
|
||||||
|
|
||||||
|
// 获取 consult 数组
|
||||||
|
consultResult := tGjson.Get(reqStr, "consult")
|
||||||
|
if !consultResult.Exists() || !consultResult.IsArray() {
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 attachments 配置
|
||||||
|
attachmentsResult := tGjson.Get(extStr, "attachments")
|
||||||
|
if !attachmentsResult.Exists() || !attachmentsResult.IsObject() {
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
consultArr := consultResult.Array()
|
||||||
|
attachmentsMap := attachmentsResult.Map()
|
||||||
|
|
||||||
|
for _, consultItem := range consultArr {
|
||||||
|
if !consultItem.IsObject() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
itemType := consultItem.Get("type").String()
|
||||||
|
if itemType == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找对应类型的附件配置
|
||||||
|
attachResult, ok := attachmentsMap[itemType]
|
||||||
|
if !ok || !attachResult.IsObject() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取模板
|
||||||
|
templateResult := attachResult.Get("template")
|
||||||
|
if !templateResult.Exists() || !templateResult.IsObject() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深拷贝模板
|
||||||
|
filledTemplateStr := templateResult.Raw
|
||||||
|
|
||||||
|
// 应用字段映射
|
||||||
|
fieldMappingResult := attachResult.Get("field_mapping")
|
||||||
|
if fieldMappingResult.Exists() && fieldMappingResult.IsObject() {
|
||||||
|
fieldMapping := fieldMappingResult.Map()
|
||||||
|
for fieldPath, valueSource := range fieldMapping {
|
||||||
|
sourceKey := valueSource.String()
|
||||||
|
valueResult := consultItem.Get(sourceKey)
|
||||||
|
if valueResult.Exists() {
|
||||||
|
var err error
|
||||||
|
filledTemplateStr, err = sjson.SetRaw(filledTemplateStr, fieldPath, valueResult.Raw)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标路径
|
||||||
|
targetPath := attachResult.Get("target_path").String()
|
||||||
|
if targetPath == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标路径是否存在且为数组
|
||||||
|
targetResult := tGjson.Get(msgStr, targetPath)
|
||||||
|
if !targetResult.Exists() || !targetResult.IsArray() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加到数组末尾
|
||||||
|
arrLen := len(targetResult.Array())
|
||||||
|
appendPath := targetPath + "." + strconv.Itoa(arrLen)
|
||||||
|
var err error
|
||||||
|
msgStr, err = sjson.SetRaw(msgStr, appendPath, filledTemplateStr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转回 map[string]any
|
||||||
|
var result map[string]any
|
||||||
|
if err := json.Unmarshal([]byte(msgStr), &result); err != nil {
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,9 +2,16 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"prompts-core/common/util"
|
||||||
|
"prompts-core/dao"
|
||||||
"prompts-core/model/dto"
|
"prompts-core/model/dto"
|
||||||
|
"prompts-core/model/entity"
|
||||||
|
|
||||||
promptService "prompts-core/service/prompt"
|
promptService "prompts-core/service/prompt"
|
||||||
|
|
||||||
|
"gitea.com/red-future/common/beans"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
)
|
)
|
||||||
|
|
||||||
type prompt struct{}
|
type prompt struct{}
|
||||||
@@ -27,3 +34,31 @@ func (c *prompt) Callback(ctx context.Context, req *dto.CallbackReq) (res *dto.C
|
|||||||
func (c *prompt) GetComposeTask(ctx context.Context, req *dto.GetComposeTaskReq) (res *dto.GetComposeTaskRes, err error) {
|
func (c *prompt) GetComposeTask(ctx context.Context, req *dto.GetComposeTaskReq) (res *dto.GetComposeTaskRes, err error) {
|
||||||
return promptService.GetComposeTask(ctx, req.TaskId)
|
return promptService.GetComposeTask(ctx, req.TaskId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *prompt) Text(ctx context.Context, req *dto.TextReq) (res *dto.TextRes, err error) {
|
||||||
|
composeTask, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{
|
||||||
|
TaskId: "c58c9296-994f-4e83-8285-1daebf3c492d",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
model, err := dao.Model.Get(ctx, &entity.AsynchModel{
|
||||||
|
SQLBaseDO: beans.SQLBaseDO{Creator: composeTask.Creator},
|
||||||
|
ModelName: composeTask.ModelName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message := promptService.ParsePromptResult(composeTask.ResultText)
|
||||||
|
|
||||||
|
// 加这两行
|
||||||
|
g.Log().Infof(ctx, "[Text] RequestPayload.consult: %v", composeTask.RequestPayload["consult"])
|
||||||
|
g.Log().Infof(ctx, "[Text] ExtendMapping: %v", model.ExtendMapping)
|
||||||
|
messages := util.MergeConsult(composeTask.RequestPayload, message, model.ExtendMapping)
|
||||||
|
g.Log().Infof(ctx, "[Text] MergeConsult 结果 rounds[0].messages[0].content: %v",
|
||||||
|
gjson.New(messages).Get("rounds.0.messages.0.content"))
|
||||||
|
res = &dto.TextRes{
|
||||||
|
Messages: messages,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -7,6 +7,7 @@ require (
|
|||||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
|
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
|
||||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0
|
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0
|
||||||
github.com/gogf/gf/v2 v2.10.0
|
github.com/gogf/gf/v2 v2.10.0
|
||||||
|
github.com/tidwall/sjson v1.2.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -68,7 +69,7 @@ require (
|
|||||||
github.com/r3labs/diff/v2 v2.15.1 // indirect
|
github.com/r3labs/diff/v2 v2.15.1 // indirect
|
||||||
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/tidwall/gjson v1.19.0
|
github.com/tidwall/gjson v1.19.0 // indirect
|
||||||
github.com/tiger1103/gfast-token v1.0.10 // indirect
|
github.com/tiger1103/gfast-token v1.0.10 // indirect
|
||||||
github.com/vcaesar/cedar v0.30.0 // indirect
|
github.com/vcaesar/cedar v0.30.0 // indirect
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -288,12 +288,15 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||||
|
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||||
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
|
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
|
||||||
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
|
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ type ComposeMessagesReq struct {
|
|||||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||||
Form map[string]any `p:"form" json:"form" dc:"系统表单:form 下所有字段都作为系统提示词来源"`
|
Form map[string]any `p:"form" json:"form" dc:"系统表单:form 下所有字段都作为系统提示词来源"`
|
||||||
UserForm []map[string]any `p:"userForm" json:"userForm" dc:"用户表单:userForm 下所有字段都作为用户提示词来源;若与 form 含义接近则严格覆盖系统字段"`
|
UserForm []map[string]any `p:"userForm" json:"userForm" dc:"用户表单:userForm 下所有字段都作为用户提示词来源;若与 form 含义接近则严格覆盖系统字段"`
|
||||||
|
Consult []ConsultItem `json:"consult" dc:"附件列表(图片/视频/音频)"`
|
||||||
SkillName string `p:"skillName" json:"skillName" dc:"技能名称"`
|
SkillName string `p:"skillName" json:"skillName" dc:"技能名称"`
|
||||||
UserFiles []string `p:"userFiles" json:"userFiles" dc:"用户附件地址列表"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConsultItem 单个附件
|
||||||
|
type ConsultItem struct {
|
||||||
|
Type string `json:"type" dc:"附件类型:image/video/audio"`
|
||||||
|
Url string `json:"url" dc:"附件地址"`
|
||||||
|
}
|
||||||
type ComposeMessagesRes struct {
|
type ComposeMessagesRes struct {
|
||||||
TaskId string `json:"taskId" dc:"任务ID"`
|
TaskId string `json:"taskId" dc:"任务ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Messages *MultiRoundResult `json:"messages,omitempty" dc:"最终消息数组"`
|
|
||||||
EpicycleId int64 `json:"epicycleId" dc:"轮次ID"`
|
|
||||||
*/
|
|
||||||
|
|
||||||
// MultiRoundResult 多轮返回结果
|
// MultiRoundResult 多轮返回结果
|
||||||
type MultiRoundResult struct {
|
type MultiRoundResult struct {
|
||||||
TotalRounds int `json:"total_rounds"` // 总轮数
|
TotalRounds int `json:"total_rounds"` // 总轮数
|
||||||
@@ -58,3 +58,11 @@ type GetComposeTaskRes struct {
|
|||||||
OssFile string `json:"ossFile" dc:"结果文件地址"`
|
OssFile string `json:"ossFile" dc:"结果文件地址"`
|
||||||
FileType string `json:"fileType" dc:"结果文件类型"`
|
FileType string `json:"fileType" dc:"结果文件类型"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TextReq struct {
|
||||||
|
g.Meta `path:"/text" method:"post" tags:"提示词处理" summary:"拼接提示词" dc:"按 modelTypeId 读取 prompts_model_prompt.prompt_info 与 response_json_schema;form 作为系统表单,userForm 作为用户表单,结合 userFiles 调用 model-gateway,并直接返回最终 messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextRes struct {
|
||||||
|
Messages any `json:"messages" dc:"文本结果"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,37 +2,6 @@ package entity
|
|||||||
|
|
||||||
import "gitea.com/red-future/common/beans"
|
import "gitea.com/red-future/common/beans"
|
||||||
|
|
||||||
// AsynchModel 异步模型配置
|
|
||||||
type AsynchModel struct {
|
|
||||||
beans.SQLBaseDO `orm:",inline"`
|
|
||||||
ModelName string `orm:"model_name" json:"modelName"`
|
|
||||||
ModelType int `orm:"model_type" json:"modelType"`
|
|
||||||
BaseURL string `orm:"base_url" json:"baseUrl"`
|
|
||||||
HttpMethod string `orm:"http_method" json:"httpMethod"`
|
|
||||||
HeadMsg string `orm:"head_msg" json:"headMsg"`
|
|
||||||
Form any `orm:"form_json" json:"form"`
|
|
||||||
RequestMapping any `orm:"request_mapping" json:"requestMapping"`
|
|
||||||
ResponseMapping any `orm:"response_mapping" json:"responseMapping"`
|
|
||||||
ResponseBody any `orm:"response_body" json:"responseBody"`
|
|
||||||
ResponseTokenField string `orm:"response_token_field" json:"responseTokenField"`
|
|
||||||
Prompt string `orm:"prompt" json:"prompt"`
|
|
||||||
IsPrivate *int `orm:"is_private" json:"isPrivate"`
|
|
||||||
IsChatModel *int `orm:"is_chat_model" json:"isChatModel"`
|
|
||||||
ApiKey string `orm:"api_key" json:"apiKey"`
|
|
||||||
Enabled *int `orm:"enabled" json:"enabled"`
|
|
||||||
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
|
|
||||||
QueueLimit int `orm:"queue_limit" json:"queueLimit"`
|
|
||||||
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
|
|
||||||
ExpectedSeconds int `orm:"expected_seconds" json:"expectedSeconds"`
|
|
||||||
RetryTimes int `orm:"retry_times" json:"retryTimes"`
|
|
||||||
RetryQueueMaxSeconds int `orm:"retry_queue_max_seconds" json:"retryQueueMaxSeconds"`
|
|
||||||
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
|
|
||||||
Remark string `orm:"remark" json:"remark"`
|
|
||||||
IsOwner *int `json:"isOwner" orm:"is_owner"`
|
|
||||||
OperatorName string `orm:"operator_name" json:"operatorName"`
|
|
||||||
TokenConfig any `orm:"token_config" json:"tokenConfig"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type asynchModelCol struct {
|
type asynchModelCol struct {
|
||||||
beans.SQLBaseCol
|
beans.SQLBaseCol
|
||||||
ModelName string
|
ModelName string
|
||||||
@@ -61,6 +30,8 @@ type asynchModelCol struct {
|
|||||||
IsOwner string
|
IsOwner string
|
||||||
OperatorName string
|
OperatorName string
|
||||||
TokenConfig string
|
TokenConfig string
|
||||||
|
ExtendMapping string
|
||||||
|
QueryConfig string
|
||||||
}
|
}
|
||||||
|
|
||||||
var AsynchModelCol = asynchModelCol{
|
var AsynchModelCol = asynchModelCol{
|
||||||
@@ -91,4 +62,39 @@ var AsynchModelCol = asynchModelCol{
|
|||||||
IsOwner: "is_owner",
|
IsOwner: "is_owner",
|
||||||
OperatorName: "operator_name",
|
OperatorName: "operator_name",
|
||||||
TokenConfig: "token_config",
|
TokenConfig: "token_config",
|
||||||
|
ExtendMapping: "extend_mapping",
|
||||||
|
QueryConfig: "query_config",
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsynchModel 异步模型配置
|
||||||
|
type AsynchModel struct {
|
||||||
|
beans.SQLBaseDO `orm:",inline"`
|
||||||
|
ModelName string `orm:"model_name" json:"modelName"`
|
||||||
|
ModelType int `orm:"model_type" json:"modelType"`
|
||||||
|
BaseURL string `orm:"base_url" json:"baseUrl"`
|
||||||
|
HttpMethod string `orm:"http_method" json:"httpMethod"`
|
||||||
|
HeadMsg string `orm:"head_msg" json:"headMsg"`
|
||||||
|
Form map[string]any `orm:"form_json" json:"form"`
|
||||||
|
RequestMapping map[string]any `orm:"request_mapping" json:"requestMapping"`
|
||||||
|
ResponseMapping map[string]any `orm:"response_mapping" json:"responseMapping"`
|
||||||
|
ResponseBody map[string]any `orm:"response_body" json:"responseBody"`
|
||||||
|
ResponseTokenField string `orm:"response_token_field" json:"responseTokenField"`
|
||||||
|
Prompt string `orm:"prompt" json:"prompt"`
|
||||||
|
IsPrivate *int `orm:"is_private" json:"isPrivate"`
|
||||||
|
IsChatModel *int `orm:"is_chat_model" json:"isChatModel"`
|
||||||
|
ApiKey string `orm:"api_key" json:"apiKey"`
|
||||||
|
Enabled *int `orm:"enabled" json:"enabled"`
|
||||||
|
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
|
||||||
|
QueueLimit int `orm:"queue_limit" json:"queueLimit"`
|
||||||
|
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
|
||||||
|
ExpectedSeconds int `orm:"expected_seconds" json:"expectedSeconds"`
|
||||||
|
RetryTimes int `orm:"retry_times" json:"retryTimes"`
|
||||||
|
RetryQueueMaxSeconds int `orm:"retry_queue_max_seconds" json:"retryQueueMaxSeconds"`
|
||||||
|
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
|
||||||
|
Remark string `orm:"remark" json:"remark"`
|
||||||
|
IsOwner *int `json:"isOwner" orm:"is_owner"`
|
||||||
|
OperatorName string `orm:"operator_name" json:"operatorName"`
|
||||||
|
TokenConfig map[string]any `orm:"token_config" json:"tokenConfig"`
|
||||||
|
ExtendMapping map[string]any `orm:"extend_mapping" json:"extendMapping"`
|
||||||
|
QueryConfig map[string]any `orm:"query_config" json:"queryConfig"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ import "gitea.com/red-future/common/beans"
|
|||||||
|
|
||||||
type ComposeTask struct {
|
type ComposeTask struct {
|
||||||
beans.SQLBaseDO `orm:",inline"`
|
beans.SQLBaseDO `orm:",inline"`
|
||||||
TaskId string `orm:"task_id" json:"taskId"`
|
TaskId string `orm:"task_id" json:"taskId"`
|
||||||
ModelName string `orm:"model_name" json:"modelName"`
|
ModelName string `orm:"model_name" json:"modelName"`
|
||||||
SkillName string `orm:"skill_name" json:"skillName"`
|
SkillName string `orm:"skill_name" json:"skillName"`
|
||||||
BuildType int `orm:"build_type" json:"buildType"`
|
BuildType int `orm:"build_type" json:"buildType"`
|
||||||
CallbackUrl string `orm:"callback_url" json:"callbackUrl"`
|
CallbackUrl string `orm:"callback_url" json:"callbackUrl"`
|
||||||
GatewayState int `orm:"gateway_state" json:"gatewayState"`
|
GatewayState int `orm:"gateway_state" json:"gatewayState"`
|
||||||
RequestPayload any `orm:"request_payload" json:"requestPayload"`
|
RequestPayload map[string]any `orm:"request_payload" json:"requestPayload"`
|
||||||
ResultText string `orm:"result_text" json:"resultText"`
|
ResultText string `orm:"result_text" json:"resultText"`
|
||||||
Messages any `orm:"messages" json:"messages"`
|
Messages map[string]any `orm:"messages" json:"messages"`
|
||||||
Status string `orm:"status" json:"status"`
|
Status string `orm:"status" json:"status"`
|
||||||
ErrorMessage string `orm:"error_message" json:"errorMessage"`
|
ErrorMessage string `orm:"error_message" json:"errorMessage"`
|
||||||
OssFile string `orm:"oss_file" json:"ossFile"`
|
OssFile string `orm:"oss_file" json:"ossFile"`
|
||||||
FileType string `orm:"file_type" json:"fileType"`
|
FileType string `orm:"file_type" json:"fileType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type composeTaskCol struct {
|
type composeTaskCol struct {
|
||||||
|
|||||||
@@ -12,9 +12,21 @@ import (
|
|||||||
"prompts-core/model/dto"
|
"prompts-core/model/dto"
|
||||||
"prompts-core/model/entity"
|
"prompts-core/model/entity"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UserPromptPayload 用户提示词请求体
|
||||||
|
type UserPromptPayload struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
PromptInfo string `json:"promptInfo"`
|
||||||
|
Form map[string]any `json:"form"`
|
||||||
|
UserForm any `json:"userForm"`
|
||||||
|
Consult []dto.ConsultItem `json:"consult"`
|
||||||
|
UserFilesText map[string]string `json:"userFilesText"`
|
||||||
|
Skills string `json:"skills"`
|
||||||
|
}
|
||||||
|
|
||||||
// buildInferenceRequest 构建推理请求
|
// buildInferenceRequest 构建推理请求
|
||||||
func buildInferenceRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, history []map[string]any) (map[string]any, error) {
|
func buildInferenceRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *entity.AsynchModel, aiModel *entity.AsynchModel, history []map[string]any) (map[string]any, error) {
|
||||||
processedReq, totalBatches, err := ProcessUserFormBatches(ctx, req, aiModel)
|
processedReq, totalBatches, err := ProcessUserFormBatches(ctx, req, aiModel)
|
||||||
@@ -117,7 +129,7 @@ func promptBuildWithRounds(ctx context.Context, req *dto.ComposeMessagesReq, mod
|
|||||||
%s
|
%s
|
||||||
技能名称: %s
|
技能名称: %s
|
||||||
用户文件: %v
|
用户文件: %v
|
||||||
`, req.ModelName, formInfo, req.SkillName, req.UserFiles)
|
`, req.ModelName, formInfo, req.SkillName, req.Consult)
|
||||||
|
|
||||||
return fmt.Sprintf(providerProtocol.SystemPromptTemplate,
|
return fmt.Sprintf(providerProtocol.SystemPromptTemplate,
|
||||||
req.ModelName, // %s 目标模型名称
|
req.ModelName, // %s 目标模型名称
|
||||||
@@ -148,19 +160,16 @@ func checkOverallContent(ir *PromptIR, model *entity.AsynchModel) bool {
|
|||||||
|
|
||||||
// buildUserPrompt 构建用户提示词
|
// buildUserPrompt 构建用户提示词
|
||||||
func buildUserPrompt(ctx context.Context, req *dto.ComposeMessagesReq, prompt string) string {
|
func buildUserPrompt(ctx context.Context, req *dto.ComposeMessagesReq, prompt string) string {
|
||||||
userFormForPayload := prepareUserFormPayload(req.UserForm)
|
payload := UserPromptPayload{
|
||||||
|
Model: req.ModelName,
|
||||||
payload := map[string]any{
|
PromptInfo: prompt,
|
||||||
"model": req.ModelName,
|
Form: req.Form,
|
||||||
"promptInfo": prompt,
|
UserForm: prepareUserFormPayload(req.UserForm),
|
||||||
"form": req.Form,
|
Consult: req.Consult,
|
||||||
"userForm": userFormForPayload,
|
UserFilesText: ExtractFileTexts(ctx, req.Consult),
|
||||||
"userFiles": req.UserFiles,
|
Skills: SkillMdContent(ctx, req.SkillName),
|
||||||
"userFilesText": FetchFileTexts(ctx, req.UserFiles),
|
|
||||||
"skills": SkillMdContent(ctx, req.SkillName),
|
|
||||||
}
|
}
|
||||||
|
return gjson.New(payload).String()
|
||||||
return util.MustMarshal(payload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareUserFormPayload 准备用户表单载荷
|
// prepareUserFormPayload 准备用户表单载荷
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"gitea.com/red-future/common/beans"
|
"gitea.com/red-future/common/beans"
|
||||||
"gitea.com/red-future/common/utils"
|
"gitea.com/red-future/common/utils"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
|
||||||
"prompts-core/common/util"
|
"prompts-core/common/util"
|
||||||
@@ -122,7 +123,7 @@ func saveComposeTask(ctx context.Context, taskID string, req *dto.ComposeMessage
|
|||||||
SkillName: req.SkillName,
|
SkillName: req.SkillName,
|
||||||
BuildType: req.BuildType,
|
BuildType: req.BuildType,
|
||||||
CallbackUrl: req.CallbackUrl,
|
CallbackUrl: req.CallbackUrl,
|
||||||
RequestPayload: util.MustMarshal(req),
|
RequestPayload: util.MustMarshalToMap(req),
|
||||||
Status: public.ComposeStatusPending,
|
Status: public.ComposeStatusPending,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
@@ -182,13 +183,13 @@ func callInferenceModel(ctx context.Context, req *dto.ComposeMessagesReq, chatMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createDefaultResult 创建默认结果
|
// createDefaultResult 创建默认结果
|
||||||
func createDefaultResult(data map[string]any) *dto.MultiRoundResult {
|
func createDefaultResult(data map[string]any) map[string]any {
|
||||||
if data == nil {
|
if data == nil {
|
||||||
data = make(map[string]any)
|
data = make(map[string]any)
|
||||||
}
|
}
|
||||||
return &dto.MultiRoundResult{
|
return map[string]any{
|
||||||
TotalRounds: 1,
|
"total_rounds": 1,
|
||||||
Rounds: []map[string]any{data},
|
"rounds": []map[string]any{data},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,14 +197,19 @@ func createDefaultResult(data map[string]any) *dto.MultiRoundResult {
|
|||||||
func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
||||||
g.Log().Infof(ctx, "[Callback][RECV] taskId=%s state=%d ossFile=%s fileType=%s textLen=%d",
|
g.Log().Infof(ctx, "[Callback][RECV] taskId=%s state=%d ossFile=%s fileType=%s textLen=%d",
|
||||||
req.TaskId, req.State, req.OssFile, req.FileType, len(req.Text))
|
req.TaskId, req.State, req.OssFile, req.FileType, len(req.Text))
|
||||||
|
// 查询任务
|
||||||
composeTask, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{
|
composeTask, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{
|
||||||
TaskId: req.TaskId,
|
TaskId: req.TaskId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("查询任务失败: %w", err)
|
return fmt.Errorf("查询任务失败: %w", err)
|
||||||
}
|
}
|
||||||
if composeTask == nil {
|
model, err := dao.Model.Get(ctx, &entity.AsynchModel{
|
||||||
return fmt.Errorf("任务不存在: %s", req.TaskId)
|
SQLBaseDO: beans.SQLBaseDO{Creator: composeTask.Creator},
|
||||||
|
ModelName: composeTask.ModelName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("查询模型失败: %w", err)
|
||||||
}
|
}
|
||||||
//处理失败
|
//处理失败
|
||||||
if req.State == 3 {
|
if req.State == 3 {
|
||||||
@@ -232,16 +238,18 @@ func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
|||||||
//处理成功
|
//处理成功
|
||||||
if req.State == 2 {
|
if req.State == 2 {
|
||||||
// 1. 根据 BuildType 解析结果
|
// 1. 根据 BuildType 解析结果
|
||||||
var messages any
|
var messages map[string]any
|
||||||
switch composeTask.BuildType {
|
switch composeTask.BuildType {
|
||||||
case public.BuildTypePrompt: // 提示词构建解析
|
case public.BuildTypePrompt: // 提示词构建解析
|
||||||
messages = parsePromptResult(req.Text)
|
messages = ParsePromptResult(req.Text)
|
||||||
case public.BuildTypeNode: // 节点构建解析
|
case public.BuildTypeNode: // 节点构建解析
|
||||||
messages = parseNodeResult(req.Text)
|
messages = ParseNodeResult(req.Text)
|
||||||
default:
|
default:
|
||||||
messages = req.Text
|
messages = gjson.New(req.Text).Map()
|
||||||
}
|
}
|
||||||
// 2. 更新数据库
|
// 2. 处理附加字段
|
||||||
|
messages = util.MergeConsult(composeTask.RequestPayload, messages, model.ExtendMapping)
|
||||||
|
// 3. 更新数据库
|
||||||
_, err = dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
_, err = dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
||||||
TaskId: req.TaskId,
|
TaskId: req.TaskId,
|
||||||
Status: public.ComposeStatusSuccess,
|
Status: public.ComposeStatusSuccess,
|
||||||
@@ -269,8 +277,8 @@ func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePromptResult 解析提示词构建结果
|
// ParsePromptResult 解析提示词构建结果
|
||||||
func parsePromptResult(raw string) *dto.MultiRoundResult {
|
func ParsePromptResult(raw string) map[string]any {
|
||||||
var wrapper map[string]any
|
var wrapper map[string]any
|
||||||
if err := json.Unmarshal([]byte(raw), &wrapper); err != nil {
|
if err := json.Unmarshal([]byte(raw), &wrapper); err != nil {
|
||||||
return createDefaultResult(map[string]any{"raw": raw})
|
return createDefaultResult(map[string]any{"raw": raw})
|
||||||
@@ -283,17 +291,17 @@ func parsePromptResult(raw string) *dto.MultiRoundResult {
|
|||||||
|
|
||||||
// 先尝试解析为数组
|
// 先尝试解析为数组
|
||||||
if roundsArray := tryParseAsMapArray(contentStr); roundsArray != nil {
|
if roundsArray := tryParseAsMapArray(contentStr); roundsArray != nil {
|
||||||
return &dto.MultiRoundResult{
|
return map[string]any{
|
||||||
TotalRounds: len(roundsArray),
|
"total_rounds": len(roundsArray),
|
||||||
Rounds: roundsArray,
|
"rounds": roundsArray,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再尝试解析为单个对象
|
// 再尝试解析为单个对象
|
||||||
if singleRound := tryParseAsMap(contentStr); singleRound != nil {
|
if singleRound := tryParseAsMap(contentStr); singleRound != nil {
|
||||||
return &dto.MultiRoundResult{
|
return map[string]any{
|
||||||
TotalRounds: 1,
|
"total_rounds": 1,
|
||||||
Rounds: []map[string]any{singleRound},
|
"rounds": []map[string]any{singleRound},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,8 +330,8 @@ func tryParseAsMap(jsonStr string) map[string]any {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseNodeResult 解析节点构建结果
|
// ParseNodeResult 解析节点构建结果
|
||||||
func parseNodeResult(raw string) *dto.MultiRoundResult {
|
func ParseNodeResult(raw string) map[string]any {
|
||||||
var result map[string]any
|
var result map[string]any
|
||||||
if err := json.Unmarshal([]byte(raw), &result); err != nil {
|
if err := json.Unmarshal([]byte(raw), &result); err != nil {
|
||||||
return createDefaultResult(map[string]any{"raw": raw})
|
return createDefaultResult(map[string]any{"raw": raw})
|
||||||
@@ -335,10 +343,9 @@ func parseNodeResult(raw string) *dto.MultiRoundResult {
|
|||||||
result = inner
|
result = inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return map[string]any{
|
||||||
return &dto.MultiRoundResult{
|
"total_rounds": 1,
|
||||||
TotalRounds: 1,
|
"rounds": []map[string]any{result},
|
||||||
Rounds: []map[string]any{result},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"prompts-core/model/dto"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -21,6 +22,17 @@ const (
|
|||||||
bytesPerMB = 1024 * 1024
|
bytesPerMB = 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ExtractFileTexts 从 ConsultItem 列表中提取文件内容
|
||||||
|
func ExtractFileTexts(ctx context.Context, consult []dto.ConsultItem) map[string]string {
|
||||||
|
urls := make([]string, 0, len(consult))
|
||||||
|
for _, item := range consult {
|
||||||
|
if item.Url != "" {
|
||||||
|
urls = append(urls, item.Url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FetchFileTexts(ctx, urls)
|
||||||
|
}
|
||||||
|
|
||||||
// FetchFileTexts 从 URL 列表获取文件内容,支持 zip 内文件
|
// FetchFileTexts 从 URL 列表获取文件内容,支持 zip 内文件
|
||||||
func FetchFileTexts(ctx context.Context, urls []string) map[string]string {
|
func FetchFileTexts(ctx context.Context, urls []string) map[string]string {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
# Prompts-Core(提示词核心服务)
|
|
||||||
|
|
||||||
> 智能提示词构建与管理系统,支持多模态 AI 模型的提示词组装、会话管理和协议适配。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 项目简介
|
|
||||||
|
|
||||||
**Prompts-Core** 是一个基于 Go 语言开发的提示词核心服务,作为 AI 应用层与模型网关之间的桥梁,负责将业务需求转换为标准化的模型请求。
|
|
||||||
|
|
||||||
### 核心价值
|
|
||||||
- **统一提示词管理**:集中化管理不同模型类型的提示词模板
|
|
||||||
- **智能会话维护**:基于 Redis + PostgreSQL 的双层会话存储
|
|
||||||
- **多协议适配**:支持 OpenAI、DeepSeek、Qwen、Gemini 等多种模型协议
|
|
||||||
- **文件处理能力**:自动提取文本文件和 ZIP 压缩包内容
|
|
||||||
- **技能系统集成**:支持从外部加载 Markdown 格式的技能描述
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心功能
|
|
||||||
|
|
||||||
### 1. 提示词构建引擎
|
|
||||||
|
|
||||||
#### 多模态支持
|
|
||||||
| 类型 | 说明 | 适用场景 |
|
|
||||||
|------|------|----------|
|
|
||||||
| Type 1 | 文字处理助手 | 文章撰写、文案优化、翻译等 |
|
|
||||||
| Type 2 | 图片处理助手 | 图像生成、风格迁移等 |
|
|
||||||
| Type 3 | 音频处理助手 | 语音合成、识别、降噪等 |
|
|
||||||
| Type 4 | 向量化处理助手 | 语义检索、知识索引等 |
|
|
||||||
| Type 5 | 全模态助手 | 跨模态转换、多模态融合等 |
|
|
||||||
|
|
||||||
#### 构建模式
|
|
||||||
- **BuildType 1(提示词构建)**:完整流程,包含系统提示词、历史会话、用户输入的智能组装
|
|
||||||
- **BuildType 2(节点构建)**:工作流路由决策,根据上下文选择节点 ID
|
|
||||||
|
|
||||||
#### 分批处理
|
|
||||||
当用户表单内容超出模型窗口限制时,自动按 Token 大小分批处理。
|
|
||||||
|
|
||||||
### 2. 会话管理系统
|
|
||||||
|
|
||||||
- **双层存储**:Redis 缓存(最近 N 轮)+ PostgreSQL 持久化
|
|
||||||
- **自动管理**:最大轮数控制(默认 10 轮)、自动过期(默认 30 分钟)
|
|
||||||
|
|
||||||
### 3. 协议适配器
|
|
||||||
|
|
||||||
通过配置动态支持多种模型协议:
|
|
||||||
- 角色映射:system/user/assistant → 目标协议角色
|
|
||||||
- 内容字段映射:content → parts.text 等
|
|
||||||
- 消息顺序控制:灵活配置拼接顺序
|
|
||||||
- 请求模板渲染:支持占位符替换
|
|
||||||
|
|
||||||
### 4. 任务调度
|
|
||||||
|
|
||||||
- **异步流程**:创建网关任务 → 轮询等待 → 接收回调 → 返回结果
|
|
||||||
- **重试机制**:可配置最大重试次数(默认 3 次)
|
|
||||||
- **超时保护**:默认 300 秒超时
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 技术架构
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
|
|
||||||
| 组件 | 版本 | 用途 |
|
|
||||||
|------|------|------|
|
|
||||||
| Go | 1.26.0 | 编程语言 |
|
|
||||||
| GoFrame | v2.10.0 | Web 框架 |
|
|
||||||
| PostgreSQL | - | 关系型数据库 |
|
|
||||||
| Redis | - | 缓存与会话存储 |
|
|
||||||
| Consul | - | 服务注册与发现 |
|
|
||||||
| Jaeger | - | 分布式链路追踪 |
|
|
||||||
|
|
||||||
### 架构图
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user