plan params

This commit is contained in:
archer
2025-10-10 19:52:14 +08:00
parent 08ed5f75f5
commit a818350ff2
7 changed files with 156 additions and 150 deletions

View File

@@ -170,7 +170,9 @@ export enum NodeInputKeyEnum {
// agent
subApps = 'subApps',
planAgentConfig = 'planAgentConfig',
isAskAgent = 'isAskAgent',
isPlanAgent = 'isPlanAgent',
isConfirmPlanAgent = 'isConfirmPlanAgent',
// dataset
datasetSelectList = 'datasets',

View File

@@ -1,40 +1,5 @@
import { SubAppIds } from './sub/constants';
export const getMasterAgentDefaultPrompt = () => {
return `## 工作流程
你需要严格按照 [${SubAppIds.plan}] 提供的步骤逐步完成执行任务。`;
// return `## 角色定位
// 你是一个高级任务调度器Task Orchestrator负责分析用户需求、拆解并分派子任务、整合多Agent成果确保最终产出具备高度专业性、系统性与洞见力。
// ## 核心职责
// ### 粒度控制
// - **原子任务原则**:每个子任务必须是单一、明确、可独立完成的
// - **专业匹配原则**根据任务属性将子任务分配给最合适的专业Agent
// - **避免任务过载**不得将多个复杂任务打包给单个Agent
// ### 过程管理
// - 制定清晰的执行顺序,保证任务逻辑链条完整
// - 动态监控子任务进展,必要时调整分工或策略
// - 保持信息透明确保上下游Agent输出可直接衔接
// ### 结果验证
// - 校验每个子任务是否达成预期目标
// - 严格检查输出的准确性、完整性和格式规范
// - 确保所有结果的一致性、逻辑连贯性和互补性
// ## 最终产出标准
// 1. **专业性**:输出必须具备专家级深度,避免表面化结论
// 2. **系统性**:结果需结构化呈现,覆盖任务的各个核心维度
// 3. **多视角**:从战略、执行、风险、机遇等不同角度进行分析
// 4. **洞见力**:在满足需求的基础上,提出更高层次的见解、趋势判断或改进建议
// 5. **可操作性**:输出不仅是分析,还应包含可执行的步骤、优先级和资源需求
// 6. **用户导向**:确保最终成果切合用户需求,且超出用户的预期价值
// ## 关键原则
// 1. **分而治之**复杂任务必须拆解禁止直接交由单个Agent
// 2. **专业对口**匹配最合适的Agent来执行对应任务
// 3. **循序渐进**:按照逻辑顺序推进,确保结果可堆叠与传递
// 4. **结果导向**:始终聚焦最终输出的质量与用户满意度
// 5. **灵活调整**:在任务推进中,根据结果反馈动态优化策略
// 6. **高标准产出**:最终结果必须是专业、详细、系统且具备洞见的完整方案`;
return ``;
};

View File

@@ -47,6 +47,7 @@ import { dispatchFileRead } from './sub/file';
import { dispatchApp, dispatchPlugin } from './sub/app';
import { getSubAppsPrompt } from './sub/plan/prompt';
import { parseAgentSystemPrompt } from './utils';
import type { AgentPlanType } from './sub/plan/type';
export type DispatchAgentModuleProps = ModuleDispatchProps<{
[NodeInputKeyEnum.history]?: ChatItemType[];
@@ -59,7 +60,9 @@ export type DispatchAgentModuleProps = ModuleDispatchProps<{
[NodeInputKeyEnum.aiChatTopP]?: number;
[NodeInputKeyEnum.subApps]?: FlowNodeTemplateType[];
[NodeInputKeyEnum.planAgentConfig]?: Record<string, any>;
[NodeInputKeyEnum.isAskAgent]?: boolean;
[NodeInputKeyEnum.isPlanAgent]?: boolean;
[NodeInputKeyEnum.isConfirmPlanAgent]?: boolean;
}>;
type Response = DispatchNodeResultType<{
@@ -94,42 +97,38 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
temperature,
aiChatTopP,
subApps = [],
planAgentConfig
isPlanAgent = false,
isAskAgent = false,
isConfirmPlanAgent = false
}
} = props;
const agentModel = getLLMModel(model);
const chatHistories = getHistories(history, histories);
const masterMessagesKey = `masterMessages-${nodeId}`;
const planMessagesKey = `planMessages-${nodeId}`;
const planToolCallMessagesKey = `planToolCallMessages-${nodeId}`;
const contextKey = `context-${nodeId}`;
const plansKey = `plans-${nodeId}`;
// Get history messages
let { masterHistoryMessages, planHistoryMessages, planToolCallMessages } = (() => {
let { planHistoryMessages, contextMap, plans } = (() => {
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[]
contextMap: (lastHistory.memories?.[contextKey] || []) as ChatCompletionMessageParam[],
plans: (lastHistory.memories?.[plansKey] || []) as AgentPlanType
};
}
return {
masterHistoryMessages: [],
planHistoryMessages: [],
planToolCallMessages: []
contextMap: {}
};
})();
// Check interactive entry
props.node.isEntry = false;
// 交互模式进来的话,这个值才是交互输入的值
const interactiveInput = lastInteractive ? chatValue2RuntimePrompt(query).text : '';
try {
// Get files
const fileUrlInput = inputs.find((item) => item.key === NodeInputKeyEnum.fileUrlList);
@@ -181,6 +180,9 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
};
};
// 交互模式进来的话,这个值才是交互输入的值
const interactiveInput = lastInteractive ? chatValue2RuntimePrompt(query).text : '';
/* ===== Plan Agent ===== */
let planMessages: ChatCompletionMessageParam[] = [];
/*
@@ -195,8 +197,13 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
*/
if (
// 如果是 userSelect 和 userInput说明不是 plan 触发的交互。
lastInteractive?.type !== 'userSelect' &&
lastInteractive?.type !== 'userInput'
lastInteractive?.type &&
[
'agentPlanAskQuery',
'agentPlanAskUserForm',
'agentPlanAskUserSelect',
'agentPlanCheck'
].includes(lastInteractive?.type)
) {
// Confirm 操作
if (lastInteractive?.type === 'agentPlanCheck' && interactiveInput === ConfirmPlanAgentText) {
@@ -242,7 +249,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
});
planMessages = completeMessages;
planToolCallMessages = newPlanToolCallMessages;
plans = planList;
// TODO: usage 合并
// Sub agent plan 不会有交互响应。Top agent plan 肯定会有。
@@ -251,7 +258,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
[DispatchNodeResponseKeyEnum.answerText]: `${tmpText}${text}`,
[DispatchNodeResponseKeyEnum.memories]: {
[planMessagesKey]: filterMemoryMessages(planMessages),
[planToolCallMessagesKey]: planToolCallMessages
[plansKey]: plans
},
[DispatchNodeResponseKeyEnum.interactive]: interactiveResponse
};
@@ -259,7 +266,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
}
}
/* ===== Master agent ===== */
/* ===== Master agent, 逐步执行 plan ===== */
// Get master request messages
const systemMessages = chats2GPTMessages({
@@ -267,10 +274,6 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
reserveId: false
});
const historyMessages: ChatCompletionMessageParam[] = (() => {
if (masterHistoryMessages && masterHistoryMessages.length > 0) {
return masterHistoryMessages;
}
return [];
})();
@@ -286,12 +289,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
],
reserveId: false
});
const requestMessages = [
...systemMessages,
...historyMessages,
...userMessages,
...planToolCallMessages
];
const requestMessages = [...historyMessages, ...userMessages];
console.log(JSON.stringify({ requestMessages }, null, 2));
const dispatchFlowResponse: ChatHistoryItemResType[] = [];

View File

@@ -4,7 +4,10 @@ import { filterSystemVariables } from '../../../../../../../core/workflow/dispat
import { authAppByTmbId } from '../../../../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppVersionById } from '../../../../../../../core/app/version/controller';
import { getRunningUserInfoByTmbId } from '../../../../../../../support/user/team/utils';
import {
getRunningUserInfoByTmbId,
getUserChatInfo
} from '../../../../../../../support/user/team/utils';
import { runWorkflow } from '../../../../../../../core/workflow/dispatch';
import {
getWorkflowEntryNodeIds,
@@ -13,7 +16,6 @@ import {
storeNodes2RuntimeNodes
} from '@fastgpt/global/core/workflow/runtime/utils';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { getUserChatInfoAndAuthTeamPoints } from '../../../../../../../support/permission/auth/team';
import { getChildAppRuntimeById } from '../../../../../../app/plugin/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { getPluginRunUserQuery } from '@fastgpt/global/core/workflow/utils';
@@ -61,7 +63,7 @@ export const dispatchApp = async (props: Props): Promise<DispatchSubAppResponse>
// Rewrite children app variables
const systemVariables = filterSystemVariables(variables);
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(appData.tmbId);
const { externalProvider } = await getUserChatInfo(appData.tmbId);
const childrenRunVariables = {
...systemVariables,
histories: [],
@@ -79,6 +81,7 @@ export const dispatchApp = async (props: Props): Promise<DispatchSubAppResponse>
...props,
runningAppInfo: {
id: String(appData._id),
name: appData.name,
teamId: String(appData.teamId),
tmbId: String(appData.tmbId),
isChildApp: true
@@ -154,7 +157,7 @@ export const dispatchPlugin = async (props: Props): Promise<DispatchSubAppRespon
};
});
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(tmbId);
const { externalProvider } = await getUserChatInfo(tmbId);
const runtimeVariables = {
...filterSystemVariables(props.variables),
appId: String(plugin.id),
@@ -167,6 +170,7 @@ export const dispatchPlugin = async (props: Props): Promise<DispatchSubAppRespon
runningAppInfo: {
id: String(plugin.id),
// 如果系统插件有 teamId 和 tmbId则使用系统插件的 teamId 和 tmbId管理员指定了插件作为系统插件
name: plugin.name,
teamId: plugin.teamId || runningAppInfo.teamId,
tmbId: plugin.tmbId || runningAppInfo.tmbId,
isChildApp: true

View File

@@ -16,10 +16,10 @@ export const getSubAppsPrompt = ({
.map((item) => {
const info = getSubAppInfo(item.function.name);
if (!info) return '';
return `@${info.name}(${info.toolDescription})`;
return `- [@${info.name}]: ${info.toolDescription};`;
})
.filter(Boolean)
.join('; ');
.join('\n');
};
/*
@@ -28,87 +28,121 @@ export const getSubAppsPrompt = ({
*/
export const getPlanAgentPrompt = (subAppsPrompt: string, systemPrompt?: string) => {
return `<role>
你是一个专业的项目规划助手,擅长将复杂任务分解为结构化的执行计划
你是一个智能任务规划助手,能够根据任务执行的实时反馈动态调整和生成执行计划。你采用渐进式规划策略,基于当前已知信息生成适应性步骤,而非试图预测所有可能路径
</role>
${
systemPrompt
? `<user_required>
${systemPrompt}
</user_required>`
: ''
}
<planning_philosophy>
核心原则:
1. **渐进式规划**:只规划到下一个关键信息点或决策点
2. **适应性标记**:通过 'replan' 标识需要基于执行结果调整的任务
3. **最小化假设**:不对未知信息做过多预设,而是通过执行步骤获取
</planning_philosophy>
<toolset>
${subAppsPrompt}
</toolset>
<process>
- 解析用户输入,提取核心目标、关键要素、约束与本地化偏好。
- 在缺少完成任务的关键信息时,使用 [${SubAppIds.ask}] 工具来询问用户(如:未指定目的地、预算、时间等必要细节)
- 你还可以使用这些工具来设计本轮执行计划: """${subAppsPrompt}"""。注意,你只有这些工具可以进行调用。
${systemPrompt ? '- 制定本轮计划时,严格参考 <user_required></user_required> 中的内容进行设计,设计的计划不偏离<user_required></user_required>。' : ''}
- 输出语言风格本地化(根据用户输入语言进行术语与语序调整)。
- 严格按照 JSON Schema 生成完整计划,不得输出多余内容。
- 解析用户输入,识别任务模式(线性/探索/并行/迭代)
- 提取核心目标、关键要素、约束与本地化偏好
- 如果用户提供了前置规划信息,优先基于用户的步骤安排和偏好来生成计划
- 在缺少前置的关键信息或用户的问题不明确时,使用 [interactivePromptTool] 工具来询问用户获取必要信息
- 输出语言风格本地化(根据用户输入语言进行术语与语序调整)。
- 如果用户有自己输入的plan应该按照他的plan流程来规划但是在需要决策的地方进行中断并把 replan 字段设置为需要决策的步骤id
- 严格按照 JSON Schema 生成完整计划,不得输出多余内容。
</process>
<requirements>
- 必须严格输出 JSON,不能包含代码块标记(如 \`\`\`)、注释或额外说明文字。
- 输出结构必须符合以下 JSON Schema
\`\`\`json
{
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "任务主题, 准确覆盖本次所有执行步骤的核心内容和维度"
},
"steps": {
"type": "array",
"description": "完成任务的步骤列表",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "步骤的唯一标识"
},
"title": {
"type": "string",
"description": "步骤标题"
},
"description": {
"type": "string",
"description": "步骤的具体描述, 可以使用@符号声明需要用到的工具。"
},
},
"required": ["id", "title", "description"]
}
}
},
"required": ["title", "description", "steps"]
}
\`\`\`
</requirements>
<guardrails>
- 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。
- 避免过于具体的时间/预算承诺与无法验证的保证。
- 保持中立、客观;必要时指出风险与依赖。
</guardrails>
<example>
- 必须严格输出 JSON
- 输出结构必须符合以下 JSON Schema
\`\`\`json
{
"task": "[主题] 深度调研计划",
"steps": [
{
"id": "step1",
"title": "[步骤名称]",
"description": "[步骤描述] @网络搜索"
},
{
"id": "step2",
"title": "[步骤名称]",
"description": "[步骤描述] @webhook机器人"
}
]
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "任务主题, 准确覆盖本次所有执行步骤的核心内容和维度"
},
"steps": {
"type": "array",
"description": "完成任务的步骤列表",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "步骤的唯一标识"
},
"title": {
"type": "string",
"description": "步骤标题"
},
"description": {
"type": "string",
"description": "步骤的具体描述, 可以使用@符号声明需要用到的工具。"
},
"depends_on": {
"type": "object",
"description": "该步骤依赖的前置步骤的id比如["step1","step2"]"
}
},
"required": ["id", "title", "description"]
}
},
"replan": {
"type": "array",
"description": "需要二次规划时,列出依赖的步骤。如果为空数组则表示不需要二次规划",
"items": {
"type": "string"
},
"default": []
}
},
"required": ["task", "steps"]
}
</example>`;
\`\`\`
</requirements>
<guardrails>
- 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。
- 避免过于具体的时间/预算承诺与无法验证的保证。
- 保持中立、客观;必要时指出风险与依赖。
- 只输出[interactivePromptTool]的工具调用或 JSON 计划内容, 不能输出其他内容。
</guardrails>
<best-practices>
### 步骤指导
#### 颗粒度把控
- **保持平衡**:步骤既不过于宏观(难以执行),也不过于细碎(失去灵活性)
- **可执行性**:每个步骤应该是一个独立可执行的任务单元
- **结果明确**:每个步骤应产生明确的输出,为后续决策提供依据
#### 步骤数量的自然边界
- **认知负载**:单次规划保持在用户可理解的复杂度内
- **执行周期**:考虑合理的执行和反馈周期
- **依赖关系**:强依赖的步骤可以规划在一起,弱依赖的分开
- **不确定性**:不确定性越高,初始规划应该越保守
### description 字段最佳实践
- **明确工具和目标**"使用 @research_agent 搜索X的最新进展重点关注Y方面"
- **标注关键信息点**"了解A的特性特别注意是否支持B功能这将影响后续方案选择"
- **预示可能分支**"调研市场反馈,如果正面则深入了解优势,如果负面则分析原因"
- **说明探索重点**"搜索相关案例关注1)实施成本 2)成功率 3)常见问题"
### requires_replan 判定规则
设置为 **true** 当:
- 存在"如果...则..."的条件逻辑
- 下一步行动依赖当前步骤的具体发现
- 任务需要迭代执行直到满足条件
- 初始信息不足以规划完整路径
- 任务复杂度高,需要分阶段执行
设置为 **false** 当:
- 所有步骤可以预先确定
- 任务简单直接
- 步骤间是简单的顺序关系
- 目标明确且路径唯一
### 元计划规范
- 你生成的计划必须是元计划 (Meta-plan),即关于“如何制定计划”的计划,而不是直接给用户的最终执行方案
- 当用户请求制定一份计划时,你需要先生成一份 制定计划的计划,用于指导如何收集信息、明确目标、分析需求和设计步骤
</best-practices>
`;
};

View File

@@ -2,8 +2,11 @@ export type AgentPlanStepType = {
id: string;
title: string;
description: string;
depends_on?: string[];
response?: string;
};
export type AgentPlanType = {
task: string;
steps: AgentPlanStepType[];
replan?: string[];
};

View File

@@ -1,5 +1,5 @@
import React, { useCallback } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { Box, Button, Flex } from '@chakra-ui/react';
import { Controller, useForm, type UseFormHandleSubmit } from 'react-hook-form';
import Markdown from '@/components/Markdown';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';