Skip to content

Commit 946b0a9

Browse files
feat: add plan agent & model agent (#5577)
* refactor: agent call (#5572) * feat: Add relevant functions related to agent invocation, including plan parsing, state management and tool invocation * Refactor agent call logic and utilities - Simplified the `runAgentCall` function by removing unnecessary complexity and restructuring the flow for better readability and maintainability. - Introduced helper functions to create tools from tool nodes and to prepare agent messages, enhancing modularity. - Removed the `utils.ts` file as its functions were integrated into the main logic, streamlining the codebase. - Updated the dispatch logic in `index.ts` to utilize the new helper functions and improve clarity. - Adjusted the handling of interactive modes and tool calls to ensure proper response formatting and error handling. * refactor: clean up the processing logic of the interactive mode and remove the unused tool creation functions * feat: add relevant constants for proxy configuration and update the proxy call logic * refactor: remove unused configuration variables from workflow properties * refactor: remove unused configuration variables from dispatchRunAgents props * fix: build error * refactor: update FlowNodeTypeEnum values and consolidate utility functions * refactor: simplify conditional checks in tool call and reasoning handlers * feat: add default agent prompt for improved response handling * refactor: rename directory with agent->tool, agentCall->agnet * refactor: rename dispatchRunAgents to dispatchRunAgent for consistency * refactor: rename toolCall to tools for consistency in FlowNodeTypeEnum * refactor: rename agents to toolCall for consistency in nodeTypes mapping * refactor: remove unused runtimeEdges parameter from dispatchRunAgent * refactor: update runAgentCall and dispatchRunAgent to use structured requestProps and workflowProps * refactor: streamline requestProps and handleToolResponse in runAgentCall and dispatchRunAgent * refactor: restructure RunAgentCallProps and update requestProps to requestParams for clarity * refactor: enhance interactiveEntryToolParams handling in runAgentCall for improved response management * refactor: flatten RunAgentCallProps structure and update dispatchRunAgent to use direct properties * fix: correct initialization of interactiveResponse in runAgentCall * agent call code * fix: agent call stop sign * feat: add plan agent tools and default generated prompts * feat: add model agent tools and related functions * chore: rename enum value * fix: optimize isEndSign assignment and update default plan prompt format * fix: update transferPlanAgent to use histories instead of sharedContext and rename default prompt variable * fix: update transferPlanAgent to use ChatItemType and adapt message structure * feat: add ModelAgentTool and PlanAgentTool with detailed descriptions and parameters fix: update error handling in transferModelAgent and transferPlanAgent to return error messages refactor: simplify isEndSign assignment in runAgentCall * feat: enhance agent call handling and response processing with context support * feat: refactor agent prompts and add utility functions for system prompt parsing * feat: add plan agent tools and default generated prompts * feat: add model agent tools and related functions * chore: rename enum value * fix: optimize isEndSign assignment and update default plan prompt format * fix: update transferPlanAgent to use histories instead of sharedContext and rename default prompt variable * fix: update transferPlanAgent to use ChatItemType and adapt message structure * feat: add ModelAgentTool and PlanAgentTool with detailed descriptions and parameters fix: update error handling in transferModelAgent and transferPlanAgent to return error messages refactor: simplify isEndSign assignment in runAgentCall * feat: enhance agent call handling and response processing with context support * feat: refactor agent prompts and add utility functions for system prompt parsing * feat: add AskAgentTool to support the interactive questioning function * Update request.ts --------- Co-authored-by: archer <545436317@qq.com>
1 parent cd63b98 commit 946b0a9

File tree

12 files changed

+701
-68
lines changed

12 files changed

+701
-68
lines changed

packages/service/core/ai/llm/agentCall.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ type RunAgentCallProps = {
3232
name: string;
3333
avatar: string;
3434
};
35-
handleToolResponse: (e: ChatCompletionMessageToolCall) => Promise<{
35+
handleToolResponse: (e: {
36+
call: ChatCompletionMessageToolCall;
37+
context: ChatCompletionMessageParam[];
38+
}) => Promise<{
3639
response: string;
3740
usages: ChatNodeUsageType[];
3841
isEnd: boolean;
@@ -124,7 +127,10 @@ export const runAgentCall = async ({
124127
let isEndSign = false;
125128
for await (const tool of toolCalls) {
126129
// TODO: 加入交互节点处理
127-
const { response, usages, isEnd } = await handleToolResponse(tool);
130+
const { response, usages, isEnd } = await handleToolResponse({
131+
call: tool,
132+
context: requestMessages
133+
});
128134

129135
if (isEnd) {
130136
isEndSign = true;

packages/service/core/workflow/dispatch/ai/agent/constants.ts

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,38 @@
11
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
22

3-
export const getTopAgentDefaultPrompt = () => {
4-
return `你是一位Supervisor Agent,具备以下核心能力:
5-
6-
## 核心能力
7-
1. **计划制定与管理**:根据用户需求制定详细的执行计划,并实时跟踪和调整计划进度
8-
2. **工具调用编排**:可以调用各种工具来完成特定任务,支持并行和串行工具调用
9-
3. **上下文理解**:能够理解对话历史、文档内容和当前状态
10-
4. **自主决策**:根据当前情况和计划进度做出最优决策
11-
12-
## 工作流程
13-
1. **需求分析**:深入理解用户需求,识别关键目标和约束条件
14-
2. **计划制定**:使用 plan_agent 工具制定详细的执行计划
15-
3. **工具编排**:根据计划选择和调用合适的工具
16-
4. **结果处理**:分析工具返回结果,判断是否满足预期
17-
5. **计划调整**:根据执行结果动态调整计划
18-
6. **最终输出**:给出完整、准确的回答
19-
20-
## 特殊指令
3+
export enum SubAppIds {
4+
plan = 'plan_agent',
5+
stop = 'stop_agent',
6+
model = 'model_agent',
7+
fileRead = 'file_read'
8+
}
9+
10+
export const getTopAgentConstantPrompt = () => {
11+
return `## 特殊指令
2112
- 对于复杂任务,必须先使用 plan_agent 制定计划
2213
- 在执行过程中如需调整计划,再次调用 plan_agent
2314
- 始终保持计划的可见性和可追踪性
24-
- 遇到错误时要有容错和重试机制
25-
26-
请始终保持专业、准确、有条理的回答风格,确保用户能够清楚了解执行进度和结果。`;
27-
};
28-
29-
export const PlanAgentTool: ChatCompletionTool = {
30-
type: 'function',
31-
function: {
32-
name: 'plan_agent',
33-
description:
34-
'如果用户的任务非常复杂,可以先使用 plan_agent 制定计划,然后根据计划使用其他工具来完成任务。'
35-
}
15+
- 每次有新的进度完成时,都要调用 plan_agent 更新计划
16+
- 遇到错误时要有容错和重试机制`;
3617
};
3718

38-
export const StopAgentId = 'stop_agent';
3919
export const StopAgentTool: ChatCompletionTool = {
4020
type: 'function',
4121
function: {
42-
name: StopAgentId,
43-
description: '如果完成了所有的任务,可调用次工具。'
22+
name: SubAppIds.stop,
23+
description: '如果完成了所有的任务,可调用此工具。'
4424
}
4525
};
4626

47-
/*
27+
/*
4828
结构:
4929
[url1,url2,url2]
5030
[
5131
{id:1,url: url1},
5232
{id:2,url: url2}
5333
]
5434
*/
55-
export const getFileReadTool = (urls: string[]): ChatCompletionTool => {
35+
export const getFileReadTool = (urls?: string[]): ChatCompletionTool => {
5636
return {
5737
type: 'function',
5838
function: {
@@ -64,7 +44,7 @@ export const getFileReadTool = (urls: string[]): ChatCompletionTool => {
6444
file_path: {
6545
type: 'string',
6646
description: '文件ID',
67-
enum: urls.map((url, index) => `${index + 1}`)
47+
enum: urls?.map((_, index) => `${index + 1}`)
6848
}
6949
},
7050
required: ['file_path']

packages/service/core/workflow/dispatch/ai/agent/index.ts

Lines changed: 129 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,25 @@ import {
2727
import { formatModelChars2Points } from '../../../../../support/wallet/usage/utils';
2828
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
2929
import {
30+
applyDiff,
3031
filterToolResponseToPreview,
3132
formatToolResponse,
3233
getToolNodesByIds,
33-
initToolNodes
34+
initToolNodes,
35+
parseToolArgs
3436
} from '../utils';
35-
import { getTopAgentDefaultPrompt, StopAgentId, StopAgentTool } from './constants';
37+
import { getFileReadTool, getTopAgentConstantPrompt, StopAgentTool, SubAppIds } from './constants';
3638
import { runWorkflow } from '../..';
37-
import json5 from 'json5';
3839
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
3940
import type { ToolNodeItemType } from './type';
4041
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
4142
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
43+
import { transferPlanAgent } from '../sub/plan';
44+
import { transferModelAgent } from '../sub/model';
45+
import { PlanAgentTool } from '../sub/plan/constants';
46+
import { ModelAgentTool } from '../sub/model/constants';
47+
import { getSubIdsByAgentSystem, parseAgentSystem } from './utils';
48+
import { getChildAppPreviewNode } from '../../../../../core/app/plugin/controller';
4249

4350
export type DispatchAgentModuleProps = ModuleDispatchProps<{
4451
[NodeInputKeyEnum.history]?: ChatItemType[];
@@ -81,7 +88,8 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
8188
history = 6,
8289
fileUrlList: fileLinks,
8390
temperature,
84-
aiChatTopP
91+
aiChatTopP,
92+
planConfig
8593
}
8694
} = props;
8795

@@ -95,7 +103,8 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
95103
}
96104

97105
// Init tool params
98-
const toolNodeIds = filterToolNodeIdByEdges({ nodeId, edges: runtimeEdges });
106+
// const toolNodeIds = filterToolNodeIdByEdges({ nodeId, edges: runtimeEdges });
107+
const toolNodeIds = getSubIdsByAgentSystem(systemPrompt);
99108
const toolNodes = getToolNodesByIds({ toolNodeIds, runtimeNodes });
100109
// TODO: 补充系统 agent
101110
const toolNodesMap = new Map<string, ToolNodeItemType>();
@@ -110,12 +119,14 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
110119
};
111120
};
112121

113-
const subApps = getSubApps({ toolNodes });
122+
const subApps = getSubApps({ toolNodes, urls: fileLinks });
123+
124+
const combinedSystemPrompt = `${parseAgentSystem({ systemPrompt, toolNodesMap })}\n\n${getTopAgentConstantPrompt()}`;
114125

115126
// TODO: 把 files 加入 query 中。
116127
const messages: ChatItemType[] = (() => {
117128
const value: ChatItemType[] = [
118-
...getSystemPrompt_ChatItemType(systemPrompt || getTopAgentDefaultPrompt()),
129+
...getSystemPrompt_ChatItemType(combinedSystemPrompt),
119130
// Add file input prompt to histories
120131
...chatHistories,
121132
{
@@ -159,15 +170,107 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
159170
isAborted: res ? () => res.closed : undefined,
160171

161172
getToolInfo,
162-
handleToolResponse: async (call) => {
173+
handleToolResponse: async ({ call, context }) => {
163174
const toolId = call.function.name;
164175

165-
if (toolId === StopAgentId) {
176+
if (toolId === SubAppIds.stop) {
166177
return {
167178
response: '',
168179
usages: [],
169180
isEnd: true
170181
};
182+
} else if (toolId === SubAppIds.plan) {
183+
const planModel = planConfig?.model ?? model;
184+
const { instruction } = parseToolArgs<{ instruction: string }>(call.function.arguments);
185+
186+
const { content, inputTokens, outputTokens } = await transferPlanAgent({
187+
model: planModel,
188+
instruction,
189+
histories: GPTMessages2Chats({
190+
messages: context.slice(1, -1),
191+
getToolInfo
192+
}),
193+
onStreaming({ text, fullText }) {
194+
//TODO: 需要一个新的 plan sse event
195+
if (!fullText) return;
196+
workflowStreamResponse?.({
197+
event: SseResponseEventEnum.toolResponse,
198+
data: {
199+
tool: {
200+
id: call.id,
201+
toolName: '',
202+
toolAvatar: '',
203+
params: '',
204+
response: sliceStrStartEnd(fullText, 5000, 5000)
205+
}
206+
}
207+
});
208+
}
209+
});
210+
211+
const lastPlanCallIndex = context
212+
.slice(0, -1)
213+
.findLastIndex(
214+
(c) =>
215+
c.role === 'assistant' &&
216+
c.tool_calls?.some((tc) => tc.function?.name === SubAppIds.plan)
217+
);
218+
const originalContent =
219+
lastPlanCallIndex !== -1 ? (context[lastPlanCallIndex + 1].content as string) : '';
220+
221+
const applyedContent = applyDiff({
222+
original: originalContent,
223+
patch: content
224+
});
225+
226+
// workflowStreamResponse?.({
227+
// event: SseResponseEventEnum.toolResponse,
228+
// data: {
229+
// tool: {
230+
// id: call.id,
231+
// toolName: '',
232+
// toolAvatar: '',
233+
// params: '',
234+
// response: sliceStrStartEnd(applyedContent, 5000, 5000)
235+
// }
236+
// }
237+
// });
238+
239+
return {
240+
response: content,
241+
usages: [],
242+
isEnd: false
243+
};
244+
} else if (toolId === SubAppIds.model) {
245+
const { systemPrompt, task } = parseToolArgs<{ systemPrompt: string; task: string }>(
246+
call.function.arguments
247+
);
248+
249+
const { content, inputTokens, outputTokens } = await transferModelAgent({
250+
model,
251+
systemPrompt,
252+
task,
253+
onStreaming({ text, fullText }) {
254+
if (!fullText) return;
255+
workflowStreamResponse?.({
256+
event: SseResponseEventEnum.toolResponse,
257+
data: {
258+
tool: {
259+
id: call.id,
260+
toolName: '',
261+
toolAvatar: '',
262+
params: '',
263+
response: sliceStrStartEnd(fullText, 5000, 5000)
264+
}
265+
}
266+
});
267+
}
268+
});
269+
return {
270+
response: content,
271+
usages: [],
272+
isEnd: false
273+
};
171274
}
172275

173276
const node = toolNodesMap.get(toolId);
@@ -179,14 +282,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
179282
};
180283
}
181284

182-
const startParams = (() => {
183-
try {
184-
return json5.parse(call.function.arguments);
185-
} catch {
186-
return {};
187-
}
188-
})();
189-
285+
const startParams = parseToolArgs(call.function.arguments);
190286
initToolNodes(runtimeNodes, [node.nodeId], startParams);
191287
const { toolResponses, flowUsages, flowResponses } = await runWorkflow({
192288
...props,
@@ -323,9 +419,20 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
323419
}
324420
};
325421

326-
const getSubApps = ({ toolNodes }: { toolNodes: ToolNodeItemType[] }): ChatCompletionTool[] => {
422+
const getSubApps = ({
423+
toolNodes,
424+
urls
425+
}: {
426+
toolNodes: ToolNodeItemType[];
427+
urls?: string[];
428+
}): ChatCompletionTool[] => {
327429
// System Tools: Plan Agent, stop sign, model agent.
328-
const systemTools: ChatCompletionTool[] = [];
430+
const systemTools: ChatCompletionTool[] = [
431+
PlanAgentTool,
432+
StopAgentTool,
433+
ModelAgentTool,
434+
getFileReadTool(urls)
435+
];
329436

330437
// Node Tools
331438
const nodeTools = toolNodes.map<ChatCompletionTool>((item: ToolNodeItemType) => {
@@ -334,7 +441,7 @@ const getSubApps = ({ toolNodes }: { toolNodes: ToolNodeItemType[] }): ChatCompl
334441
type: 'function',
335442
function: {
336443
name: item.nodeId,
337-
description: item.intro || item.name,
444+
description: `调用${item.flowNodeType}:${item.name || item.intro}完成任务`,
338445
parameters: item.jsonSchema
339446
}
340447
};
@@ -357,7 +464,7 @@ const getSubApps = ({ toolNodes }: { toolNodes: ToolNodeItemType[] }): ChatCompl
357464
type: 'function',
358465
function: {
359466
name: item.nodeId,
360-
description: item.toolDescription || item.intro || item.name,
467+
description: `调用${item.flowNodeType}:${item.name || item.toolDescription || item.intro}完成任务`,
361468
parameters: {
362469
type: 'object',
363470
properties,
@@ -366,6 +473,7 @@ const getSubApps = ({ toolNodes }: { toolNodes: ToolNodeItemType[] }): ChatCompl
366473
}
367474
};
368475
});
476+
console.dir(nodeTools, { depth: null });
369477

370478
return [...systemTools, ...nodeTools];
371479
};

0 commit comments

Comments
 (0)