mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-24 22:03:54 +00:00
perf: tool promot and reg slice;query extension prompt (#1576)
This commit is contained in:
@@ -64,4 +64,22 @@ export const getNanoid = (size = 12) => {
|
|||||||
return `${firstChar}${randomsStr}`;
|
return `${firstChar}${randomsStr}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Custom text to reg, need to replace special chats */
|
||||||
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
|
||||||
|
/* slice json str */
|
||||||
|
export const sliceJsonStr = (str: string) => {
|
||||||
|
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
|
||||||
|
|
||||||
|
const jsonRegex = /{[^{}]*}/g;
|
||||||
|
const matches = str.match(jsonRegex);
|
||||||
|
|
||||||
|
if (!matches) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到第一个完整的 JSON 字符串
|
||||||
|
const jsonStr = matches[0];
|
||||||
|
|
||||||
|
return jsonStr;
|
||||||
|
};
|
||||||
|
@@ -24,6 +24,7 @@ export const RunAppModule: FlowNodeTemplateType = {
|
|||||||
intro: '可以选择一个其他应用进行调用',
|
intro: '可以选择一个其他应用进行调用',
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
|
isTool: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.runAppSelectApp,
|
key: NodeInputKeyEnum.runAppSelectApp,
|
||||||
|
@@ -65,7 +65,7 @@ Q: FastGPT 如何收费?
|
|||||||
A: FastGPT 收费可以参考……
|
A: FastGPT 收费可以参考……
|
||||||
"""
|
"""
|
||||||
原问题: 你知道 laf 么?
|
原问题: 你知道 laf 么?
|
||||||
检索词: ["laf是什么?","如何使用laf?","laf的介绍。"]
|
检索词: ["laf 的官网地址是多少?","laf 的使用教程。","laf 有什么特点和优势。"]
|
||||||
----------------
|
----------------
|
||||||
历史记录:
|
历史记录:
|
||||||
"""
|
"""
|
||||||
@@ -75,7 +75,7 @@ A: 1. 开源
|
|||||||
3. 扩展性强
|
3. 扩展性强
|
||||||
"""
|
"""
|
||||||
原问题: 介绍下第2点。
|
原问题: 介绍下第2点。
|
||||||
检索词: ["介绍下 FastGPT 简便的优势", "FastGPT 为什么使用起来简便?","FastGPT的有哪些简便的功能?"]。
|
检索词: ["介绍下 FastGPT 简便的优势"]。
|
||||||
----------------
|
----------------
|
||||||
历史记录:
|
历史记录:
|
||||||
"""
|
"""
|
||||||
@@ -85,7 +85,7 @@ Q: 什么是 Laf?
|
|||||||
A: Laf 是一个云函数开发平台。
|
A: Laf 是一个云函数开发平台。
|
||||||
"""
|
"""
|
||||||
原问题: 它们有什么关系?
|
原问题: 它们有什么关系?
|
||||||
检索词: ["FastGPT和Laf有什么关系?","FastGPT的RAG是用Laf实现的么?"]
|
检索词: ["FastGPT和Laf有什么关系?","介绍下FastGPT","介绍下Laf"]
|
||||||
----------------
|
----------------
|
||||||
历史记录:
|
历史记录:
|
||||||
"""
|
"""
|
||||||
|
@@ -12,7 +12,7 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
|
|||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||||
import { Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
import { Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { replaceVariable, sliceJsonStr } from '@fastgpt/global/common/string/tools';
|
||||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
import { getHistories } from '../utils';
|
import { getHistories } from '../utils';
|
||||||
import { ModelTypeEnum, getLLMModel } from '../../../ai/model';
|
import { ModelTypeEnum, getLLMModel } from '../../../ai/model';
|
||||||
@@ -348,10 +348,9 @@ Human: ${content}`
|
|||||||
const answer = data.choices?.[0].message?.content || '';
|
const answer = data.choices?.[0].message?.content || '';
|
||||||
|
|
||||||
// parse response
|
// parse response
|
||||||
const start = answer.indexOf('{');
|
const jsonStr = sliceJsonStr(answer);
|
||||||
const end = answer.lastIndexOf('}');
|
|
||||||
|
|
||||||
if (start === -1 || end === -1) {
|
if (!jsonStr) {
|
||||||
return {
|
return {
|
||||||
rawResponse: answer,
|
rawResponse: answer,
|
||||||
tokens: await countMessagesTokens(messages),
|
tokens: await countMessagesTokens(messages),
|
||||||
@@ -359,11 +358,6 @@ Human: ${content}`
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsonStr = answer
|
|
||||||
.substring(start, end + 1)
|
|
||||||
.replace(/(\\n|\\)/g, '')
|
|
||||||
.replace(/ /g, '');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
rawResponse: answer,
|
rawResponse: answer,
|
||||||
|
@@ -3,7 +3,7 @@ export const Prompt_Tool_Call = `<Instruction>
|
|||||||
|
|
||||||
工具使用了 JSON Schema 的格式声明,其中 toolId 是工具的 description 是工具的描述,parameters 是工具的参数,包括参数的类型和描述,required 是必填参数的列表。
|
工具使用了 JSON Schema 的格式声明,其中 toolId 是工具的 description 是工具的描述,parameters 是工具的参数,包括参数的类型和描述,required 是必填参数的列表。
|
||||||
|
|
||||||
请你根据工具描述,决定回答问题或是使用工具。在完成任务过程中,USER代表用户的输入,TOOL_RESPONSE代表工具运行结果。ASSISTANT 代表你的输出。
|
请你根据工具描述,决定回答问题或是使用工具。在完成任务过程中,USER代表用户的输入,TOOL_RESPONSE代表工具运行结果,ANSWER 代表你的输出。
|
||||||
你的每次输出都必须以0,1开头,代表是否需要调用工具:
|
你的每次输出都必须以0,1开头,代表是否需要调用工具:
|
||||||
0: 不使用工具,直接回答内容。
|
0: 不使用工具,直接回答内容。
|
||||||
1: 使用工具,返回工具调用的参数。
|
1: 使用工具,返回工具调用的参数。
|
||||||
@@ -12,14 +12,20 @@ export const Prompt_Tool_Call = `<Instruction>
|
|||||||
|
|
||||||
USER: 你好呀
|
USER: 你好呀
|
||||||
ANSWER: 0: 你好,有什么可以帮助你的么?
|
ANSWER: 0: 你好,有什么可以帮助你的么?
|
||||||
USER: 今天杭州的天气如何
|
USER: 现在几点了?
|
||||||
ANSWER: 1: {"toolId":"testToolId",arguments:{"city": "杭州"}}
|
ANSWER: 1: {"toolId":"timeToolId"}
|
||||||
|
TOOL_RESPONSE: """
|
||||||
|
2022/5/5 12:00 Thursday
|
||||||
|
"""
|
||||||
|
ANSWER: 0: 现在是2022年5月5日,星期四,中午12点。
|
||||||
|
USER: 今天杭州的天气如何?
|
||||||
|
ANSWER: 1: {"toolId":"testToolId","arguments":{"city": "杭州"}}
|
||||||
TOOL_RESPONSE: """
|
TOOL_RESPONSE: """
|
||||||
晴天......
|
晴天......
|
||||||
"""
|
"""
|
||||||
ANSWER: 0: 今天杭州是晴天。
|
ANSWER: 0: 今天杭州是晴天。
|
||||||
USER: 今天杭州的天气适合去哪里玩?
|
USER: 今天杭州的天气适合去哪里玩?
|
||||||
ANSWER: 1: {"toolId":"testToolId2",arguments:{"query": "杭州 天气 去哪里玩"}}
|
ANSWER: 1: {"toolId":"testToolId2","arguments":{"query": "杭州 天气 去哪里玩"}}
|
||||||
TOOL_RESPONSE: """
|
TOOL_RESPONSE: """
|
||||||
晴天. 西湖、灵隐寺、千岛湖……
|
晴天. 西湖、灵隐寺、千岛湖……
|
||||||
"""
|
"""
|
||||||
@@ -35,5 +41,4 @@ ANSWER: 0: 今天杭州是晴天,适合去西湖、灵隐寺、千岛湖等地
|
|||||||
下面是正式的对话内容:
|
下面是正式的对话内容:
|
||||||
|
|
||||||
USER: {{question}}
|
USER: {{question}}
|
||||||
ANSWER:
|
ANSWER: `;
|
||||||
`;
|
|
||||||
|
@@ -20,7 +20,7 @@ import { dispatchWorkFlow } from '../../index';
|
|||||||
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
import { DispatchToolModuleProps, RunToolResponse, ToolNodeItemType } from './type.d';
|
||||||
import json5 from 'json5';
|
import json5 from 'json5';
|
||||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
||||||
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { getNanoid, replaceVariable, sliceJsonStr } from '@fastgpt/global/common/string/tools';
|
||||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { updateToolInputValue } from './utils';
|
import { updateToolInputValue } from './utils';
|
||||||
@@ -33,6 +33,8 @@ type FunctionCallCompletion = {
|
|||||||
toolAvatar?: string;
|
toolAvatar?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ERROR_TEXT = 'Tool run error';
|
||||||
|
|
||||||
export const runToolWithPromptCall = async (
|
export const runToolWithPromptCall = async (
|
||||||
props: DispatchToolModuleProps & {
|
props: DispatchToolModuleProps & {
|
||||||
messages: ChatCompletionMessageParam[];
|
messages: ChatCompletionMessageParam[];
|
||||||
@@ -122,14 +124,23 @@ export const runToolWithPromptCall = async (
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const parseAnswerResult = parseAnswer(answer);
|
const { answer: replaceAnswer, toolJson } = parseAnswer(answer);
|
||||||
// console.log(parseAnswer, '==11==');
|
// console.log(parseAnswer, '==11==');
|
||||||
// No tools
|
// No tools
|
||||||
if (typeof parseAnswerResult === 'string') {
|
if (!toolJson) {
|
||||||
|
if (replaceAnswer === ERROR_TEXT && stream && detail) {
|
||||||
|
responseWrite({
|
||||||
|
res,
|
||||||
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
|
text: replaceAnswer
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
// No tool is invoked, indicating that the process is over
|
// No tool is invoked, indicating that the process is over
|
||||||
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
const gptAssistantResponse: ChatCompletionAssistantMessageParam = {
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
content: parseAnswerResult
|
content: replaceAnswer
|
||||||
};
|
};
|
||||||
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
const completeMessages = filterMessages.concat(gptAssistantResponse);
|
||||||
const tokens = await countGptMessagesTokens(completeMessages, undefined);
|
const tokens = await countGptMessagesTokens(completeMessages, undefined);
|
||||||
@@ -148,18 +159,16 @@ export const runToolWithPromptCall = async (
|
|||||||
|
|
||||||
// Run the selected tool.
|
// Run the selected tool.
|
||||||
const toolsRunResponse = await (async () => {
|
const toolsRunResponse = await (async () => {
|
||||||
if (!parseAnswerResult) return Promise.reject('tool run error');
|
const toolNode = toolNodes.find((item) => item.nodeId === toolJson.name);
|
||||||
|
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === parseAnswerResult.name);
|
|
||||||
if (!toolNode) return Promise.reject('tool not found');
|
if (!toolNode) return Promise.reject('tool not found');
|
||||||
|
|
||||||
parseAnswerResult.toolName = toolNode.name;
|
toolJson.toolName = toolNode.name;
|
||||||
parseAnswerResult.toolAvatar = toolNode.avatar;
|
toolJson.toolAvatar = toolNode.avatar;
|
||||||
|
|
||||||
// run tool flow
|
// run tool flow
|
||||||
const startParams = (() => {
|
const startParams = (() => {
|
||||||
try {
|
try {
|
||||||
return json5.parse(parseAnswerResult.arguments);
|
return json5.parse(toolJson.arguments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -172,11 +181,11 @@ export const runToolWithPromptCall = async (
|
|||||||
event: SseResponseEventEnum.toolCall,
|
event: SseResponseEventEnum.toolCall,
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
tool: {
|
tool: {
|
||||||
id: parseAnswerResult.id,
|
id: toolJson.id,
|
||||||
toolName: toolNode.name,
|
toolName: toolNode.name,
|
||||||
toolAvatar: toolNode.avatar,
|
toolAvatar: toolNode.avatar,
|
||||||
functionName: parseAnswerResult.name,
|
functionName: toolJson.name,
|
||||||
params: parseAnswerResult.arguments,
|
params: toolJson.arguments,
|
||||||
response: ''
|
response: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -211,7 +220,7 @@ export const runToolWithPromptCall = async (
|
|||||||
event: SseResponseEventEnum.toolResponse,
|
event: SseResponseEventEnum.toolResponse,
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
tool: {
|
tool: {
|
||||||
id: parseAnswerResult.id,
|
id: toolJson.id,
|
||||||
toolName: '',
|
toolName: '',
|
||||||
toolAvatar: '',
|
toolAvatar: '',
|
||||||
params: '',
|
params: '',
|
||||||
@@ -237,7 +246,7 @@ export const runToolWithPromptCall = async (
|
|||||||
// 合并工具调用的结果,使用 functionCall 格式存储。
|
// 合并工具调用的结果,使用 functionCall 格式存储。
|
||||||
const assistantToolMsgParams: ChatCompletionAssistantMessageParam = {
|
const assistantToolMsgParams: ChatCompletionAssistantMessageParam = {
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||||
function_call: parseAnswerResult
|
function_call: toolJson
|
||||||
};
|
};
|
||||||
const concatToolMessages = [
|
const concatToolMessages = [
|
||||||
...filterMessages,
|
...filterMessages,
|
||||||
@@ -248,7 +257,7 @@ export const runToolWithPromptCall = async (
|
|||||||
...concatToolMessages,
|
...concatToolMessages,
|
||||||
{
|
{
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Function,
|
role: ChatCompletionRequestMessageRoleEnum.Function,
|
||||||
name: parseAnswerResult.name,
|
name: toolJson.name,
|
||||||
content: toolsRunResponse.toolResponsePrompt
|
content: toolsRunResponse.toolResponsePrompt
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -266,7 +275,7 @@ export const runToolWithPromptCall = async (
|
|||||||
: [toolsRunResponse.moduleRunResponse];
|
: [toolsRunResponse.moduleRunResponse];
|
||||||
|
|
||||||
// get the next user prompt
|
// get the next user prompt
|
||||||
lastMessage.content += `${answer}
|
lastMessage.content += `${replaceAnswer}
|
||||||
TOOL_RESPONSE: """
|
TOOL_RESPONSE: """
|
||||||
${toolsRunResponse.toolResponsePrompt}
|
${toolsRunResponse.toolResponsePrompt}
|
||||||
"""
|
"""
|
||||||
@@ -362,24 +371,37 @@ async function streamResponse({
|
|||||||
return { answer: textAnswer.trim() };
|
return { answer: textAnswer.trim() };
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseAnswer = (str: string): FunctionCallCompletion | string => {
|
const parseAnswer = (
|
||||||
// 首先,使用正则表达式提取TOOL_ID和TOOL_ARGUMENTS
|
str: string
|
||||||
const prefix = '1:';
|
): {
|
||||||
|
answer: string;
|
||||||
|
toolJson?: FunctionCallCompletion;
|
||||||
|
} => {
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
if (str.startsWith(prefix)) {
|
// 首先,使用正则表达式提取TOOL_ID和TOOL_ARGUMENTS
|
||||||
const toolString = str.substring(prefix.length).trim();
|
const prefixReg = /^1(:|:)/;
|
||||||
|
|
||||||
|
if (prefixReg.test(str)) {
|
||||||
|
const toolString = sliceJsonStr(str);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const toolCall = json5.parse(toolString);
|
const toolCall = json5.parse(toolString);
|
||||||
return {
|
return {
|
||||||
id: getNanoid(),
|
answer: `1: ${toolString}`,
|
||||||
name: toolCall.toolId,
|
toolJson: {
|
||||||
arguments: JSON.stringify(toolCall.arguments || toolCall.parameters)
|
id: getNanoid(),
|
||||||
|
name: toolCall.toolId,
|
||||||
|
arguments: JSON.stringify(toolCall.arguments || toolCall.parameters)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return str;
|
return {
|
||||||
|
answer: ERROR_TEXT
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return str;
|
return {
|
||||||
|
answer: str
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user