diff --git a/packages/global/core/workflow/constants.ts b/packages/global/core/workflow/constants.ts index 661020dc1..df2e33738 100644 --- a/packages/global/core/workflow/constants.ts +++ b/packages/global/core/workflow/constants.ts @@ -170,7 +170,9 @@ export enum NodeInputKeyEnum { // agent subApps = 'subApps', - planAgentConfig = 'planAgentConfig', + isAskAgent = 'isAskAgent', + isPlanAgent = 'isPlanAgent', + isConfirmPlanAgent = 'isConfirmPlanAgent', // dataset datasetSelectList = 'datasets', diff --git a/packages/service/core/workflow/dispatch/ai/agent/constants.ts b/packages/service/core/workflow/dispatch/ai/agent/constants.ts index 85dd00c19..6f2de3aee 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/constants.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/constants.ts @@ -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 ``; }; diff --git a/packages/service/core/workflow/dispatch/ai/agent/index.ts b/packages/service/core/workflow/dispatch/ai/agent/index.ts index 32befb0af..70ff774a8 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/index.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/index.ts @@ -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; + [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[] = []; diff --git a/packages/service/core/workflow/dispatch/ai/agent/sub/app/index.ts b/packages/service/core/workflow/dispatch/ai/agent/sub/app/index.ts index 0bf7b194a..0c253118a 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/sub/app/index.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/sub/app/index.ts @@ -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 // 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 ...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 { 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 ` -你是一个专业的项目规划助手,擅长将复杂任务分解为结构化的执行计划。 +你是一个智能任务规划助手,能够根据任务执行的实时反馈动态调整和生成执行计划。你采用渐进式规划策略,基于当前已知信息生成适应性步骤,而非试图预测所有可能路径。 - -${ - systemPrompt - ? ` -${systemPrompt} -` - : '' -} - - + + 核心原则: + 1. **渐进式规划**:只规划到下一个关键信息点或决策点 + 2. **适应性标记**:通过 'replan' 标识需要基于执行结果调整的任务 + 3. **最小化假设**:不对未知信息做过多预设,而是通过执行步骤获取 + + + ${subAppsPrompt} + -- 解析用户输入,提取核心目标、关键要素、约束与本地化偏好。 -- 在缺少完成任务的关键信息时,使用 [${SubAppIds.ask}] 工具来询问用户(如:未指定目的地、预算、时间等必要细节) -- 你还可以使用这些工具来设计本轮执行计划: """${subAppsPrompt}"""。注意,你只有这些工具可以进行调用。 -${systemPrompt ? '- 制定本轮计划时,严格参考 中的内容进行设计,设计的计划不偏离。' : ''} -- 输出语言风格本地化(根据用户输入语言进行术语与语序调整)。 -- 严格按照 JSON Schema 生成完整计划,不得输出多余内容。 + - 解析用户输入,识别任务模式(线性/探索/并行/迭代) + - 提取核心目标、关键要素、约束与本地化偏好 + - 如果用户提供了前置规划信息,优先基于用户的步骤安排和偏好来生成计划 + - 在缺少前置的关键信息或用户的问题不明确时,使用 [interactivePromptTool] 工具来询问用户获取必要信息 + - 输出语言风格本地化(根据用户输入语言进行术语与语序调整)。 + - 如果用户有自己输入的plan,应该按照他的plan流程来规划,但是在需要决策的地方进行中断,并把 replan 字段设置为需要决策的步骤id + - 严格按照 JSON Schema 生成完整计划,不得输出多余内容。 - -- 必须严格输出 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"] -} -\`\`\` - - - -- 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。 -- 避免过于具体的时间/预算承诺与无法验证的保证。 -- 保持中立、客观;必要时指出风险与依赖。 - - - + - 必须严格输出 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"] } -`; + \`\`\` + + + - 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。 + - 避免过于具体的时间/预算承诺与无法验证的保证。 + - 保持中立、客观;必要时指出风险与依赖。 + - 只输出[interactivePromptTool]的工具调用或 JSON 计划内容, 不能输出其他内容。 + + + ### 步骤指导 + #### 颗粒度把控 + + - **保持平衡**:步骤既不过于宏观(难以执行),也不过于细碎(失去灵活性) + - **可执行性**:每个步骤应该是一个独立可执行的任务单元 + - **结果明确**:每个步骤应产生明确的输出,为后续决策提供依据 + + #### 步骤数量的自然边界 + + - **认知负载**:单次规划保持在用户可理解的复杂度内 + - **执行周期**:考虑合理的执行和反馈周期 + - **依赖关系**:强依赖的步骤可以规划在一起,弱依赖的分开 + - **不确定性**:不确定性越高,初始规划应该越保守 + + ### description 字段最佳实践 + + - **明确工具和目标**:"使用 @research_agent 搜索X的最新进展,重点关注Y方面" + - **标注关键信息点**:"了解A的特性,特别注意是否支持B功能(这将影响后续方案选择)" + - **预示可能分支**:"调研市场反馈,如果正面则深入了解优势,如果负面则分析原因" + - **说明探索重点**:"搜索相关案例,关注:1)实施成本 2)成功率 3)常见问题" + + ### requires_replan 判定规则 + + 设置为 **true** 当: + - 存在"如果...则..."的条件逻辑 + - 下一步行动依赖当前步骤的具体发现 + - 任务需要迭代执行直到满足条件 + - 初始信息不足以规划完整路径 + - 任务复杂度高,需要分阶段执行 + + 设置为 **false** 当: + - 所有步骤可以预先确定 + - 任务简单直接 + - 步骤间是简单的顺序关系 + - 目标明确且路径唯一 + ### 元计划规范 + - 你生成的计划必须是元计划 (Meta-plan),即关于“如何制定计划”的计划,而不是直接给用户的最终执行方案 + - 当用户请求制定一份计划时,你需要先生成一份 制定计划的计划,用于指导如何收集信息、明确目标、分析需求和设计步骤 + +`; }; diff --git a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/type.ts b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/type.ts index 1d3ffda33..18585973c 100644 --- a/packages/service/core/workflow/dispatch/ai/agent/sub/plan/type.ts +++ b/packages/service/core/workflow/dispatch/ai/agent/sub/plan/type.ts @@ -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[]; };