mirror of
https://github.com/labring/FastGPT.git
synced 2026-01-21 01:15:42 +08:00
perf: agent call code
This commit is contained in:
@@ -1,37 +1,40 @@
|
||||
import { SubAppIds } from './sub/constants';
|
||||
|
||||
export const getMasterAgentDefaultPrompt = () => {
|
||||
return `## 角色定位
|
||||
你是一个高级任务调度器(Task Orchestrator),负责分析用户需求、拆解并分派子任务、整合多Agent成果,确保最终产出具备高度专业性、系统性与洞见力。
|
||||
return `## 工作流程
|
||||
你需要严格按照 [${SubAppIds.plan}] 提供的步骤逐步完成执行任务。`;
|
||||
// return `## 角色定位
|
||||
// 你是一个高级任务调度器(Task Orchestrator),负责分析用户需求、拆解并分派子任务、整合多Agent成果,确保最终产出具备高度专业性、系统性与洞见力。
|
||||
|
||||
## 核心职责
|
||||
### 粒度控制
|
||||
- **原子任务原则**:每个子任务必须是单一、明确、可独立完成的
|
||||
- **专业匹配原则**:根据任务属性,将子任务分配给最合适的专业Agent
|
||||
- **避免任务过载**:不得将多个复杂任务打包给单个Agent
|
||||
// ## 核心职责
|
||||
// ### 粒度控制
|
||||
// - **原子任务原则**:每个子任务必须是单一、明确、可独立完成的
|
||||
// - **专业匹配原则**:根据任务属性,将子任务分配给最合适的专业Agent
|
||||
// - **避免任务过载**:不得将多个复杂任务打包给单个Agent
|
||||
|
||||
### 过程管理
|
||||
- 制定清晰的执行顺序,保证任务逻辑链条完整
|
||||
- 动态监控子任务进展,必要时调整分工或策略
|
||||
- 保持信息透明,确保上下游Agent输出可直接衔接
|
||||
// ### 过程管理
|
||||
// - 制定清晰的执行顺序,保证任务逻辑链条完整
|
||||
// - 动态监控子任务进展,必要时调整分工或策略
|
||||
// - 保持信息透明,确保上下游Agent输出可直接衔接
|
||||
|
||||
### 结果验证
|
||||
- 校验每个子任务是否达成预期目标
|
||||
- 严格检查输出的准确性、完整性和格式规范
|
||||
- 确保所有结果的一致性、逻辑连贯性和互补性
|
||||
// ### 结果验证
|
||||
// - 校验每个子任务是否达成预期目标
|
||||
// - 严格检查输出的准确性、完整性和格式规范
|
||||
// - 确保所有结果的一致性、逻辑连贯性和互补性
|
||||
|
||||
## 最终产出标准
|
||||
1. **专业性**:输出必须具备专家级深度,避免表面化结论
|
||||
2. **系统性**:结果需结构化呈现,覆盖任务的各个核心维度
|
||||
3. **多视角**:从战略、执行、风险、机遇等不同角度进行分析
|
||||
4. **洞见力**:在满足需求的基础上,提出更高层次的见解、趋势判断或改进建议
|
||||
5. **可操作性**:输出不仅是分析,还应包含可执行的步骤、优先级和资源需求
|
||||
6. **用户导向**:确保最终成果切合用户需求,且超出用户的预期价值
|
||||
// ## 最终产出标准
|
||||
// 1. **专业性**:输出必须具备专家级深度,避免表面化结论
|
||||
// 2. **系统性**:结果需结构化呈现,覆盖任务的各个核心维度
|
||||
// 3. **多视角**:从战略、执行、风险、机遇等不同角度进行分析
|
||||
// 4. **洞见力**:在满足需求的基础上,提出更高层次的见解、趋势判断或改进建议
|
||||
// 5. **可操作性**:输出不仅是分析,还应包含可执行的步骤、优先级和资源需求
|
||||
// 6. **用户导向**:确保最终成果切合用户需求,且超出用户的预期价值
|
||||
|
||||
## 关键原则
|
||||
1. **分而治之**:复杂任务必须拆解,禁止直接交由单个Agent
|
||||
2. **专业对口**:匹配最合适的Agent来执行对应任务
|
||||
3. **循序渐进**:按照逻辑顺序推进,确保结果可堆叠与传递
|
||||
4. **结果导向**:始终聚焦最终输出的质量与用户满意度
|
||||
5. **灵活调整**:在任务推进中,根据结果反馈动态优化策略
|
||||
6. **高标准产出**:最终结果必须是专业、详细、系统且具备洞见的完整方案
|
||||
`;
|
||||
// ## 关键原则
|
||||
// 1. **分而治之**:复杂任务必须拆解,禁止直接交由单个Agent
|
||||
// 2. **专业对口**:匹配最合适的Agent来执行对应任务
|
||||
// 3. **循序渐进**:按照逻辑顺序推进,确保结果可堆叠与传递
|
||||
// 4. **结果导向**:始终聚焦最终输出的质量与用户满意度
|
||||
// 5. **灵活调整**:在任务推进中,根据结果反馈动态优化策略
|
||||
// 6. **高标准产出**:最终结果必须是专业、详细、系统且具备洞见的完整方案`;
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||
import { dispatchFileRead } from './sub/file';
|
||||
import { dispatchApp, dispatchPlugin } from './sub/app';
|
||||
import { getSubAppsPrompt } from './sub/plan/prompt';
|
||||
import { parseAgentSystemPrompt } from './utils';
|
||||
|
||||
export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -101,21 +102,25 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
|
||||
const masterMessagesKey = `masterMessages-${nodeId}`;
|
||||
const planMessagesKey = `planMessages-${nodeId}`;
|
||||
const planToolCallMessagesKey = `planToolCallMessages-${nodeId}`;
|
||||
|
||||
// Get history messages
|
||||
const { masterHistoryMessages, planHistoryMessages } = (() => {
|
||||
let { masterHistoryMessages, planHistoryMessages, planToolCallMessages } = (() => {
|
||||
const lastHistory = chatHistories[chatHistories.length - 1];
|
||||
if (lastHistory && lastHistory.obj === ChatRoleEnum.AI) {
|
||||
return {
|
||||
masterHistoryMessages: (lastHistory.memories?.[masterMessagesKey] ||
|
||||
[]) as ChatCompletionMessageParam[],
|
||||
planHistoryMessages: (lastHistory.memories?.[planMessagesKey] ||
|
||||
[]) as ChatCompletionMessageParam[],
|
||||
planToolCallMessages: (lastHistory.memories?.[planToolCallMessagesKey] ||
|
||||
[]) as ChatCompletionMessageParam[]
|
||||
};
|
||||
}
|
||||
return {
|
||||
masterHistoryMessages: [],
|
||||
planHistoryMessages: []
|
||||
planHistoryMessages: [],
|
||||
planToolCallMessages: []
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -188,65 +193,69 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
Sub agent
|
||||
只会执行一次,肯定有。
|
||||
*/
|
||||
let masterPlanToolCallMessages: ChatCompletionMessageParam[] = [];
|
||||
|
||||
if (
|
||||
// 不为 userSelect 和 userInput,或者如果为 agentPlanCheck 并且 interactiveInput !== ConfirmPlanAgentText 都会执行
|
||||
(lastInteractive?.type !== 'userSelect' &&
|
||||
lastInteractive?.type !== 'userInput' &&
|
||||
lastInteractive?.type !== 'agentPlanCheck') ||
|
||||
(lastInteractive?.type === 'agentPlanCheck' && interactiveInput !== ConfirmPlanAgentText)
|
||||
// 如果是 userSelect 和 userInput,说明不是 plan 触发的交互。
|
||||
lastInteractive?.type !== 'userSelect' &&
|
||||
lastInteractive?.type !== 'userInput'
|
||||
) {
|
||||
// 临时代码
|
||||
const tmpText = '正在进行规划生成...\n';
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text: tmpText
|
||||
})
|
||||
});
|
||||
// Confirm 操作
|
||||
if (lastInteractive?.type === 'agentPlanCheck' && interactiveInput === ConfirmPlanAgentText) {
|
||||
} else {
|
||||
// 临时代码
|
||||
const tmpText = '正在进行规划生成...\n';
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text: tmpText
|
||||
})
|
||||
});
|
||||
|
||||
const {
|
||||
answerText,
|
||||
planList,
|
||||
planToolCallMessages,
|
||||
completeMessages,
|
||||
usages,
|
||||
interactiveResponse
|
||||
} = await dispatchPlanAgent({
|
||||
historyMessages: planHistoryMessages,
|
||||
userInput: lastInteractive ? interactiveInput : taskInput,
|
||||
interactive: lastInteractive,
|
||||
subAppPrompt: getSubAppsPrompt({ subAppList, getSubAppInfo }),
|
||||
model,
|
||||
systemPrompt,
|
||||
temperature,
|
||||
top_p: aiChatTopP,
|
||||
stream,
|
||||
isTopPlanAgent: workflowDispatchDeep === 1
|
||||
});
|
||||
const {
|
||||
answerText,
|
||||
planList,
|
||||
planToolCallMessages: newPlanToolCallMessages,
|
||||
completeMessages,
|
||||
usages,
|
||||
interactiveResponse
|
||||
} = await dispatchPlanAgent({
|
||||
historyMessages: planHistoryMessages,
|
||||
userInput: lastInteractive ? interactiveInput : taskInput,
|
||||
interactive: lastInteractive,
|
||||
subAppPrompt: getSubAppsPrompt({ subAppList, getSubAppInfo }),
|
||||
model,
|
||||
systemPrompt: parseAgentSystemPrompt({
|
||||
systemPrompt,
|
||||
getSubAppInfo
|
||||
}),
|
||||
temperature,
|
||||
top_p: aiChatTopP,
|
||||
stream,
|
||||
isTopPlanAgent: workflowDispatchDeep === 1
|
||||
});
|
||||
|
||||
const text = `${answerText}${planList ? `\n\`\`\`json\n${JSON.stringify(planList, null, 2)}\n\`\`\`` : ''}`;
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text
|
||||
})
|
||||
});
|
||||
const text = `${answerText}${planList ? `\n\`\`\`json\n${JSON.stringify(planList, null, 2)}\n\`\`\`` : ''}`;
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.answer,
|
||||
data: textAdaptGptResponse({
|
||||
text
|
||||
})
|
||||
});
|
||||
|
||||
planMessages = completeMessages;
|
||||
masterPlanToolCallMessages = planToolCallMessages;
|
||||
planMessages = completeMessages;
|
||||
planToolCallMessages = newPlanToolCallMessages;
|
||||
|
||||
// TODO: usage 合并
|
||||
// Sub agent plan 不会有交互响应。Top agent plan 肯定会有。
|
||||
if (interactiveResponse) {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.answerText]: `${tmpText}${text}`,
|
||||
[DispatchNodeResponseKeyEnum.memories]: {
|
||||
[planMessagesKey]: filterMemoryMessages(planMessages)
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.interactive]: interactiveResponse
|
||||
};
|
||||
// TODO: usage 合并
|
||||
// Sub agent plan 不会有交互响应。Top agent plan 肯定会有。
|
||||
if (interactiveResponse) {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.answerText]: `${tmpText}${text}`,
|
||||
[DispatchNodeResponseKeyEnum.memories]: {
|
||||
[planMessagesKey]: filterMemoryMessages(planMessages),
|
||||
[planToolCallMessagesKey]: planToolCallMessages
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.interactive]: interactiveResponse
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +271,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
return masterHistoryMessages;
|
||||
}
|
||||
|
||||
return chats2GPTMessages({ messages: chatHistories, reserveId: false });
|
||||
return [];
|
||||
})();
|
||||
|
||||
const userMessages = chats2GPTMessages({
|
||||
@@ -280,9 +289,10 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
const requestMessages = [
|
||||
...systemMessages,
|
||||
...historyMessages,
|
||||
...masterPlanToolCallMessages,
|
||||
...userMessages
|
||||
...userMessages,
|
||||
...planToolCallMessages
|
||||
];
|
||||
console.log(JSON.stringify({ requestMessages }, null, 2));
|
||||
|
||||
const dispatchFlowResponse: ChatHistoryItemResType[] = [];
|
||||
const {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const systemSubInfo: Record<
|
||||
[SubAppIds.plan]: {
|
||||
name: i18nT('chat:plan_agent'),
|
||||
avatar: 'common/detail',
|
||||
toolDescription: '分析和拆解用户问题,制定分步计划。'
|
||||
toolDescription: '分析和拆解用户问题,制定执行步骤。'
|
||||
},
|
||||
[SubAppIds.fileRead]: {
|
||||
name: i18nT('chat:file_parse'),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
|
||||
import { PlanAgentTool } from './plan/constants';
|
||||
import { readFileTool } from './file/utils';
|
||||
import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import type { JSONSchemaInputType } from '@fastgpt/global/core/app/jsonschema';
|
||||
@@ -79,7 +78,11 @@ export const getSubApps = ({
|
||||
];
|
||||
|
||||
// Node Tools
|
||||
const nodeTools = subApps.map<ChatCompletionTool>((item) => {
|
||||
const unitNodeTools = subApps.filter(
|
||||
(item, index, array) => array.findIndex((app) => app.pluginId === item.pluginId) === index
|
||||
);
|
||||
|
||||
const nodeTools = unitNodeTools.map<ChatCompletionTool>((item) => {
|
||||
const toolParams: FlowNodeInputItemType[] = [];
|
||||
let jsonSchema: JSONSchemaInputType | undefined;
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import type {
|
||||
ChatCompletionMessageParam,
|
||||
ChatCompletionTool
|
||||
} from '@fastgpt/global/core/ai/type.d';
|
||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
|
||||
import { createLLMResponse } from '../../../../../../ai/llm/request';
|
||||
import { getPlanAgentPrompt } from './prompt';
|
||||
import { getLLMModel } from '../../../../../../ai/model';
|
||||
@@ -85,7 +82,7 @@ export const dispatchPlanAgent = async ({
|
||||
content: userInput
|
||||
});
|
||||
}
|
||||
console.log(JSON.stringify({ requestMessages }, null, 2));
|
||||
// console.log(JSON.stringify({ requestMessages }, null, 2));
|
||||
|
||||
const {
|
||||
answerText,
|
||||
|
||||
@@ -1,64 +1,30 @@
|
||||
import type {
|
||||
ChatCompletionAssistantMessageParam,
|
||||
ChatCompletionMessageParam
|
||||
} from '@fastgpt/global/core/ai/type';
|
||||
import type { ToolNodeItemType } from './type';
|
||||
|
||||
const namespaceMap = new Map<string, string>([
|
||||
['a', '子应用'],
|
||||
['t', '工具'],
|
||||
['d', '知识库'],
|
||||
['m', '模型']
|
||||
]);
|
||||
|
||||
// e.g: {{@a.appId@}} -> a.appId
|
||||
const buildPattern = (options?: { prefix?: string }): RegExp => {
|
||||
const config = {
|
||||
prefix: '@',
|
||||
...options
|
||||
};
|
||||
|
||||
const escapedPrefix = config.prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
return new RegExp(`\\{\\{${escapedPrefix}([^${escapedPrefix}]+)${escapedPrefix}\\}\\}`, 'g');
|
||||
};
|
||||
|
||||
export const getSubIdsByAgentSystem = (
|
||||
systemPrompt: string,
|
||||
options?: { prefix?: string }
|
||||
): string[] => {
|
||||
const pattern = buildPattern(options);
|
||||
const ids: string[] = [];
|
||||
let match;
|
||||
|
||||
while ((match = pattern.exec(systemPrompt)) !== null) {
|
||||
const fullName = match[1];
|
||||
const [, id] = fullName.split('.');
|
||||
if (id) {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
};
|
||||
|
||||
export const parseAgentSystem = ({
|
||||
/*
|
||||
匹配 {{@toolId@}},转化成: @name 的格式。
|
||||
*/
|
||||
export const parseAgentSystemPrompt = ({
|
||||
systemPrompt,
|
||||
toolNodesMap,
|
||||
options
|
||||
getSubAppInfo
|
||||
}: {
|
||||
systemPrompt: string;
|
||||
toolNodesMap: Map<string, ToolNodeItemType>;
|
||||
options?: { prefix?: string };
|
||||
getSubAppInfo: (id: string) => {
|
||||
name: string;
|
||||
avatar: string;
|
||||
toolDescription: string;
|
||||
};
|
||||
}): string => {
|
||||
const pattern = buildPattern(options);
|
||||
if (!systemPrompt) return '';
|
||||
|
||||
const processedPrompt = systemPrompt.replace(pattern, (_, toolName) => {
|
||||
const [namespace, id] = toolName.split('.');
|
||||
const toolNode = toolNodesMap.get(id);
|
||||
const name = toolNode?.name || toolNode?.toolDescription || toolNode?.intro || 'unknown';
|
||||
// Match pattern {{@toolId@}} and convert to @name format
|
||||
const pattern = /\{\{@([^@]+)@\}\}/g;
|
||||
|
||||
const prefix = namespaceMap.get(namespace) ?? 'unknown';
|
||||
return `${prefix}:${name}`;
|
||||
const processedPrompt = systemPrompt.replace(pattern, (match, toolId) => {
|
||||
const toolInfo = getSubAppInfo(toolId);
|
||||
if (!toolInfo) {
|
||||
console.warn(`Tool not found for ID: ${toolId}`);
|
||||
return match; // Return original match if tool not found
|
||||
}
|
||||
|
||||
return `@${toolInfo.name}`;
|
||||
});
|
||||
|
||||
return processedPrompt;
|
||||
|
||||
@@ -14,13 +14,12 @@ import type {
|
||||
AIChatItemValueItemType,
|
||||
ToolModuleResponseItemType
|
||||
} from '@fastgpt/global/core/chat/type';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import type {
|
||||
InteractiveBasicType,
|
||||
PaymentPauseInteractive,
|
||||
InteractiveNodeResponseType,
|
||||
UserInputInteractive,
|
||||
UserSelectInteractive
|
||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
@@ -148,7 +147,6 @@ const RenderTool = React.memo(
|
||||
onOpenCiteModal?: (e?: OnOpenCiteModalProps) => void;
|
||||
}) {
|
||||
const { t } = useSafeTranslation();
|
||||
const [userOnchange, setUserOnchange] = useState(false);
|
||||
const formatJson = (string: string) => {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(string), null, 2);
|
||||
@@ -160,14 +158,7 @@ const RenderTool = React.memo(
|
||||
const response = formatJson(tool.response);
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
key={tool.id}
|
||||
allowToggle
|
||||
index={userOnchange ? undefined : showAnimation ? 0 : undefined}
|
||||
onChange={() => {
|
||||
setUserOnchange(true);
|
||||
}}
|
||||
>
|
||||
<Accordion key={tool.id} allowToggle>
|
||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
||||
<AccordionButton {...accordionButtonStyle}>
|
||||
<Avatar src={tool.toolAvatar} w={'1.25rem'} h={'1.25rem'} borderRadius={'sm'} />
|
||||
@@ -200,7 +191,7 @@ ${params}`}
|
||||
${response}`}
|
||||
/>
|
||||
)}
|
||||
{subAppValue && (
|
||||
{subAppValue && subAppValue.length > 0 && (
|
||||
<Box bg={'white'} p={2}>
|
||||
{subAppValue.map((value, index) => (
|
||||
<AIResponseBox
|
||||
|
||||
Reference in New Issue
Block a user