feat(frontend):增加AI函数工具调用开关

- 在市场和股票组件中添加启用/禁用 AI 函数工具调用的开关
- 修改相关函数以支持 enableTools 参数,控制是否启用工具调用
- 优化 AI 总结新闻和聊天流函数,根据 enableTools 决定是否使用工具
This commit is contained in:
ArvinLovegood 2025-07-03 14:25:30 +08:00
parent 5b9a81d770
commit 5e7f34652a
6 changed files with 80 additions and 26 deletions

35
app.go
View File

@ -60,13 +60,21 @@ func AddTools(tools []data.Tool) []data.Tool {
Type: "function",
Function: data.ToolFunction{
Name: "SearchStockByIndicators",
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据。输入股票名称可以获取当前股票最新的股价交易数据和基础财务指标信息,多个股票名称使用,分隔",
Description: "根据自然语言筛选股票,返回自然语言选股条件要求的股票所有相关数据。输入股票名称可以获取当前股票最新的股价交易数据和基础财务指标信息,多个股票名称使用,分隔。工具限制:不允许并行调用",
Parameters: data.FunctionParameters{
Type: "object",
Properties: map[string]any{
"words": map[string]any{
"type": "string",
"description": "选股自然语言,并且条件使用;分隔,或者条件使用,分隔。例1创新药;PE<30;净利润增长率>50%。 例2上证指数,科创50。 例3长电科技,上海贝岭",
"type": "string",
"description": "选股自然语言。" +
"例1创新药,半导体;PE<30;净利润增长率>50%。 " +
"例2上证指数,科创50。 " +
"例3长电科技,上海贝岭。" +
"例4长电科技,上海贝岭;KDJ,MACD,RSI,BOLL,主力净流入/流出" +
"例5换手率大于3%小于25%.量比1以上. 10日内有过涨停.股价处于峰值的二分之一以下.流通股本<100亿.当日和连续四日净流入;股价在20日均线以上.分时图股价在均线之上.热门板块下涨幅领先的A股. 当日量能20000手以上.沪深个股.近一年市盈率波动小于150%.MACD金叉;不要ST股及不要退市股非北交所每股收益>0。" +
"例6沪深主板.流通市值小于100亿.市值大于10亿.60分钟dif大于dea.60分钟skdj指标k值大于d值.skdj指标k值小于90.换手率大于3%.成交额大于1亿元.量比大于2.涨幅大于2%小于7%.股价大于5小于50.创业板.10日均线大于20日均线;不要ST股及不要退市股;不要北交所;不要科创板;不要创业板。" +
"例7股价在20日线上一月之内涨停次数>=1量比大于1换手率大于3%,流通市值大于 50亿小于200亿。" +
"例8基本条件前期有爆量回调到 10 日线,当日是缩量阴线,均线趋势向上。;优选条件:一月之内涨停次数>=1",
},
},
Required: []string{"words"},
@ -78,7 +86,7 @@ func AddTools(tools []data.Tool) []data.Tool {
Type: "function",
Function: data.ToolFunction{
Name: "GetStockKLine",
Description: "获取股票日K线数据",
Description: "获取股票日K线数据。工具限制:不允许并行调用",
Parameters: data.FunctionParameters{
Type: "object",
Properties: map[string]any{
@ -795,8 +803,13 @@ func (a *App) SendDingDingMessageByType(message string, stockCode string, msgTyp
return data.NewDingDingAPI().SendDingDingMessage(message)
}
func (a *App) NewChatStream(stock, stockCode, question string, sysPromptId *int) {
msgs := data.NewDeepSeekOpenAi(a.ctx).NewChatStream(stock, stockCode, question, sysPromptId, a.AiTools)
func (a *App) NewChatStream(stock, stockCode, question string, sysPromptId *int, enableTools bool) {
var msgs <-chan map[string]any
if enableTools {
msgs = data.NewDeepSeekOpenAi(a.ctx).NewChatStream(stock, stockCode, question, sysPromptId, a.AiTools)
} else {
msgs = data.NewDeepSeekOpenAi(a.ctx).NewChatStream(stock, stockCode, question, sysPromptId, []data.Tool{})
}
for msg := range msgs {
runtime.EventsEmit(a.ctx, "newChatStream", msg)
}
@ -1176,8 +1189,14 @@ func (a *App) GlobalStockIndexes() map[string]any {
return data.NewMarketNewsApi().GlobalStockIndexes(30)
}
func (a *App) SummaryStockNews(question string, sysPromptId *int) {
msgs := data.NewDeepSeekOpenAi(a.ctx).NewSummaryStockNewsStreamWithTools(question, sysPromptId, a.AiTools)
func (a *App) SummaryStockNews(question string, sysPromptId *int, enableTools bool) {
var msgs <-chan map[string]any
if enableTools {
msgs = data.NewDeepSeekOpenAi(a.ctx).NewSummaryStockNewsStreamWithTools(question, sysPromptId, a.AiTools)
} else {
msgs = data.NewDeepSeekOpenAi(a.ctx).NewSummaryStockNewsStream(question, sysPromptId)
}
for msg := range msgs {
runtime.EventsEmit(a.ctx, "summaryStockNews", msg)
}

View File

@ -718,13 +718,24 @@ func AskAi(o OpenAi, err error, messages []map[string]interface{}, ch chan map[s
for _, choice := range streamResponse.Choices {
if content := choice.Delta.Content; content != "" {
//ch <- content
ch <- map[string]any{
"code": 1,
"question": question,
"chatId": streamResponse.Id,
"model": streamResponse.Model,
"content": content,
"time": time.Now().Format(time.DateTime),
if content == "###" || content == "##" || content == "#" {
ch <- map[string]any{
"code": 1,
"question": question,
"chatId": streamResponse.Id,
"model": streamResponse.Model,
"content": "\r\n" + content,
"time": time.Now().Format(time.DateTime),
}
} else {
ch <- map[string]any{
"code": 1,
"question": question,
"chatId": streamResponse.Id,
"model": streamResponse.Model,
"content": content,
"time": time.Now().Format(time.DateTime),
}
}
//logger.SugaredLogger.Infof("Content data: %s", content)
@ -866,7 +877,7 @@ func AskAiWithTools(o OpenAi, err error, messages []map[string]interface{}, ch c
//ch <- content
//logger.SugaredLogger.Infof("Content data: %s", content)
if content == "###" {
if content == "###" || content == "##" || content == "#" {
currentAIContent.WriteString("\r\n" + content)
ch <- map[string]any{
"code": 1,

View File

@ -70,6 +70,7 @@ const nowTab = ref("市场快讯")
const indexInterval = ref(null)
const indexIndustryRank = ref(null)
const stockCode= ref('')
const enableTools= ref(true)
function getIndex() {
GlobalStockIndexes().then((res) => {
@ -186,7 +187,7 @@ function reAiSummary() {
aiSummary.value = ""
summaryModal.value = true
loading.value = true
SummaryStockNews(question.value, sysPromptId.value)
SummaryStockNews(question.value, sysPromptId.value,enableTools.value)
}
function getAiSummary() {
@ -211,7 +212,7 @@ function getAiSummary() {
aiSummaryTime.value = ""
aiSummary.value = ""
modelName.value = ""
SummaryStockNews(question.value, sysPromptId.value)
//SummaryStockNews(question.value, sysPromptId.value,enableTools.value)
}
})
}
@ -615,6 +616,17 @@ function ReFlesh(source) {
</n-flex>
</template>
<template #action>
<n-flex justify="left" style="margin-bottom: 10px">
<n-switch v-model:value="enableTools" :round="false">
<template #checked>
启用AI函数工具调用
</template>
<template #unchecked>
不启用AI函数工具调用
</template>
</n-switch>
<n-gradient-text type="error" style="margin-left: 10px">*AI函数工具调用可以增强AI获取数据的能力,但会消耗更多tokens</n-gradient-text>
</n-flex>
<n-flex justify="space-between" style="margin-bottom: 10px">
<n-select style="width: 49%" v-model:value="sysPromptId" label-field="name" value-field="ID"
:options="sysPromptOptions" placeholder="请选择系统提示词"/>

View File

@ -63,6 +63,7 @@ import vueDanmaku from 'vue3-danmaku'
import {keys, padStart} from "lodash";
import {useRoute, useRouter} from 'vue-router'
import MoneyTrend from "./moneyTrend.vue";
import {TaskTools} from "@vicons/carbon";
const route = useRoute()
const router = useRouter()
@ -101,6 +102,7 @@ const modalShow3 = ref(false)
const modalShow4 = ref(false)
const modalShow5 = ref(false)
const addBTN = ref(true)
const enableTools= ref(false)
const formModel = ref({
name: "",
code: "",
@ -1356,7 +1358,7 @@ function aiReCheckStock(stock, stockCode) {
//
//message.info("sysPromptId:"+data.sysPromptId)
NewChatStream(stock, stockCode, data.question, data.sysPromptId)
NewChatStream(stock, stockCode, data.question, data.sysPromptId,enableTools.value)
}
function aiCheckStock(stock, stockCode) {
@ -2072,7 +2074,17 @@ function searchStockReport(stockCode) {
</n-flex>
</template>
<template #action>
<n-flex justify="left" style="margin-bottom: 10px">
<n-switch v-model:value="enableTools" :round="false">
<template #checked>
启用AI函数工具调用
</template>
<template #unchecked>
不启用AI函数工具调用
</template>
</n-switch>
<n-gradient-text type="error" style="margin-left: 10px">*AI函数工具调用可以增强AI获取数据的能力,但会消耗更多tokens</n-gradient-text>
</n-flex>
<n-flex justify="space-between" style="margin-bottom: 10px">
<n-select style="width: 49%" v-model:value="data.sysPromptId" label-field="name" value-field="ID"
:options="sysPromptOptions" placeholder="请选择系统提示词"/>

View File

@ -81,7 +81,7 @@ export function InvestCalendarTimeLine(arg1:string):Promise<Array<any>>;
export function LongTigerRank(arg1:string):Promise<any>;
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any):Promise<void>;
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any,arg5:boolean):Promise<void>;
export function NewsPush(arg1:any):Promise<void>;
@ -115,7 +115,7 @@ export function StockNotice(arg1:string):Promise<Array<any>>;
export function StockResearchReport(arg1:string):Promise<Array<any>>;
export function SummaryStockNews(arg1:string,arg2:any):Promise<void>;
export function SummaryStockNews(arg1:string,arg2:any,arg3:boolean):Promise<void>;
export function UnFollow(arg1:string):Promise<string>;

View File

@ -158,8 +158,8 @@ export function LongTigerRank(arg1) {
return window['go']['main']['App']['LongTigerRank'](arg1);
}
export function NewChatStream(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4);
export function NewChatStream(arg1, arg2, arg3, arg4, arg5) {
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4, arg5);
}
export function NewsPush(arg1) {
@ -226,8 +226,8 @@ export function StockResearchReport(arg1) {
return window['go']['main']['App']['StockResearchReport'](arg1);
}
export function SummaryStockNews(arg1, arg2) {
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2);
export function SummaryStockNews(arg1, arg2, arg3) {
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2, arg3);
}
export function UnFollow(arg1) {