feat(frontend/backend):增加自定义用户提问模板功能

- 在 Settings 模型中添加 questionTemplate 字段
- 在 OpenAi 结构体中添加 QuestionTemplate 字段
- 更新前端设置组件,增加用户 prompt 配置选项
- 修改后端 API调用,支持使用自定义用户 prompt
This commit is contained in:
spark 2025-02-12 14:47:50 +08:00
parent a3b718c149
commit 1f78cc3589
4 changed files with 51 additions and 23 deletions

View File

@ -31,6 +31,7 @@ type OpenAi struct {
Temperature float64 `json:"temperature"` Temperature float64 `json:"temperature"`
Prompt string `json:"prompt"` Prompt string `json:"prompt"`
TimeOut int `json:"time_out"` TimeOut int `json:"time_out"`
QuestionTemplate string `json:"question_template"`
} }
func NewDeepSeekOpenAi(ctx context.Context) *OpenAi { func NewDeepSeekOpenAi(ctx context.Context) *OpenAi {
@ -44,6 +45,7 @@ func NewDeepSeekOpenAi(ctx context.Context) *OpenAi {
Temperature: config.OpenAiTemperature, Temperature: config.OpenAiTemperature,
Prompt: config.Prompt, Prompt: config.Prompt,
TimeOut: config.OpenAiApiTimeOut, TimeOut: config.OpenAiApiTimeOut,
QuestionTemplate: config.QuestionTemplate,
} }
} }
@ -102,7 +104,14 @@ func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
"content": o.Prompt, "content": o.Prompt,
}, },
} }
question := strutil.ReplaceWithMap(o.QuestionTemplate, map[string]string{
"{{stockName}}": strutil.RemoveNonPrintable(stock),
"{{stockCode}}": strutil.RemoveNonPrintable(stockCode),
})
logger.SugaredLogger.Infof("NewChatStream stock:%s stockCode:%s", stock, stockCode)
logger.SugaredLogger.Infof("Prompt%s", o.Prompt) logger.SugaredLogger.Infof("Prompt%s", o.Prompt)
logger.SugaredLogger.Infof("User Prompt config:%v", o.QuestionTemplate)
logger.SugaredLogger.Infof("User question:%s", question)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(5) wg.Add(5)
@ -207,7 +216,7 @@ func (o OpenAi) NewChatStream(stock, stockCode string) <-chan string {
wg.Wait() wg.Wait()
msg = append(msg, map[string]interface{}{ msg = append(msg, map[string]interface{}{
"role": "user", "role": "user",
"content": stock + "分析和总结", "content": question,
}) })
client := resty.New() client := resty.New()
client.SetBaseURL(o.BaseUrl) client.SetBaseURL(o.BaseUrl)

View File

@ -25,6 +25,7 @@ type Settings struct {
OpenAiApiTimeOut int `json:"openAiApiTimeOut"` OpenAiApiTimeOut int `json:"openAiApiTimeOut"`
Prompt string `json:"prompt"` Prompt string `json:"prompt"`
CheckUpdate bool `json:"checkUpdate"` CheckUpdate bool `json:"checkUpdate"`
QuestionTemplate string `json:"questionTemplate"`
} }
func (receiver Settings) TableName() string { func (receiver Settings) TableName() string {
@ -61,6 +62,7 @@ func (s SettingsApi) UpdateConfig() string {
"prompt": s.Config.Prompt, "prompt": s.Config.Prompt,
"check_update": s.Config.CheckUpdate, "check_update": s.Config.CheckUpdate,
"open_ai_api_time_out": s.Config.OpenAiApiTimeOut, "open_ai_api_time_out": s.Config.OpenAiApiTimeOut,
"question_template": s.Config.QuestionTemplate,
}) })
} else { } else {
logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config) logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config)
@ -80,6 +82,7 @@ func (s SettingsApi) UpdateConfig() string {
Prompt: s.Config.Prompt, Prompt: s.Config.Prompt,
CheckUpdate: s.Config.CheckUpdate, CheckUpdate: s.Config.CheckUpdate,
OpenAiApiTimeOut: s.Config.OpenAiApiTimeOut, OpenAiApiTimeOut: s.Config.OpenAiApiTimeOut,
QuestionTemplate: s.Config.QuestionTemplate,
}) })
} }
return "保存成功!" return "保存成功!"

View File

@ -28,8 +28,9 @@ const formValue = ref({
temperature: 0.1, temperature: 0.1,
maxTokens: 1024, maxTokens: 1024,
prompt:"", prompt:"",
timeout: 5 timeout: 5,
}, questionTemplate: "{{stockName}}分析和总结"
}
}) })
onMounted(()=>{ onMounted(()=>{
@ -53,7 +54,8 @@ onMounted(()=>{
temperature:res.openAiTemperature, temperature:res.openAiTemperature,
maxTokens:res.openAiMaxTokens, maxTokens:res.openAiMaxTokens,
prompt:res.prompt, prompt:res.prompt,
timeout:res.openAiApiTimeOut timeout:res.openAiApiTimeOut,
questionTemplate:res.questionTemplate?res.questionTemplate:'{{stockName}}分析和总结',
} }
console.log(res) console.log(res)
}) })
@ -77,7 +79,8 @@ function saveConfig(){
openAiTemperature:formValue.value.openAI.temperature, openAiTemperature:formValue.value.openAI.temperature,
tushareToken:formValue.value.tushareToken, tushareToken:formValue.value.tushareToken,
prompt:formValue.value.openAI.prompt, prompt:formValue.value.openAI.prompt,
openAiApiTimeOut:formValue.value.openAI.timeout openAiApiTimeOut:formValue.value.openAI.timeout,
questionTemplate:formValue.value.openAI.questionTemplate
}) })
//console.log("Settings",config) //console.log("Settings",config)
@ -143,7 +146,8 @@ function importConfig(){
temperature:config.openAiTemperature, temperature:config.openAiTemperature,
maxTokens:config.openAiMaxTokens, maxTokens:config.openAiMaxTokens,
prompt:config.prompt, prompt:config.prompt,
timeout:config.openAiApiTimeOut timeout:config.openAiApiTimeOut,
questionTemplate:config.questionTemplate,
} }
// formRef.value.resetFields() // formRef.value.resetFields()
}; };
@ -243,8 +247,18 @@ window.onerror = function (event, source, lineno, colno, error) {
}" }"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="22" v-if="formValue.openAI.enable" label="模型用户 Prompt" path="openAI.questionTemplate">
<n-input v-model:value="formValue.openAI.questionTemplate"
type="textarea"
:show-count="true"
placeholder="请输入用户prompt:例如{{stockName}}[{{stockCode}}]分析和总结"
:autosize="{
minRows: 5,
maxRows: 8
}"
/>
</n-form-item-gi>
</n-grid> </n-grid>
<n-gi :span="24"> <n-gi :span="24">
<div style="display: flex; justify-content: center"> <div style="display: flex; justify-content: center">
<n-space> <n-space>

View File

@ -76,6 +76,7 @@ export namespace data {
openAiApiTimeOut: number; openAiApiTimeOut: number;
prompt: string; prompt: string;
checkUpdate: boolean; checkUpdate: boolean;
questionTemplate: string;
static createFrom(source: any = {}) { static createFrom(source: any = {}) {
return new Settings(source); return new Settings(source);
@ -102,6 +103,7 @@ export namespace data {
this.openAiApiTimeOut = source["openAiApiTimeOut"]; this.openAiApiTimeOut = source["openAiApiTimeOut"];
this.prompt = source["prompt"]; this.prompt = source["prompt"];
this.checkUpdate = source["checkUpdate"]; this.checkUpdate = source["checkUpdate"];
this.questionTemplate = source["questionTemplate"];
} }
convertValues(a: any, classs: any, asMap: boolean = false): any { convertValues(a: any, classs: any, asMap: boolean = false): any {