This commit is contained in:
YeYuheng 2025-12-24 12:05:37 +00:00 committed by GitHub
commit 85cb932eb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 340 additions and 163 deletions

View File

@ -40,10 +40,21 @@ const TaskAnalysisSchema = z.object({
});
// LLM 返回的完整数据
const FormInputSchema = z.object({
type: z.literal('input').or(z.literal('numberInput')),
label: z.string()
});
const FormSelectSchema = z.object({
type: z.literal('select').or(z.literal('multipleSelect')),
label: z.string(),
options: z.array(z.string())
});
export const GeneratedSkillDataCollectionSchema = z.object({
phase: z.literal('collection'),
reasoning: z.string(),
question: z.string()
question: z.string(),
form: z.array(z.union([FormInputSchema, FormSelectSchema])).optional()
});
export type GeneratedSkillDataCollectionType = z.infer<typeof GeneratedSkillDataCollectionSchema>;
export const GeneratedSkillResultSchema = z.object({

View File

@ -14,6 +14,13 @@ import {
GeneratedSkillResultSchema
} from '@fastgpt/global/core/chat/helperBot/skillAgent/type';
import { parseJsonArgs } from '../../../../ai/utils';
import type {
UserInputFormItemType,
UserInputInteractive
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
export const dispatchSkillAgent = async (
props: HelperBotDispatchParamsType<SkillAgentParamsType>
@ -51,7 +58,7 @@ export const dispatchSkillAgent = async (
{ role: 'user' as const, content: query }
];
console.dir(conversationMessages, { depth: null });
// console.dir(conversationMessages, { depth: null });
// Single LLM call - LLM self-determines phase and outputs corresponding format
const llmResponse = await createLLMResponse({
body: {
@ -59,12 +66,6 @@ export const dispatchSkillAgent = async (
model: modelData,
stream: true
},
onStreaming: ({ text }) => {
workflowResponseWrite?.({
event: SseResponseEventEnum.answer,
data: textAdaptGptResponse({ text })
});
},
onReasoning: ({ text }) => {
workflowResponseWrite?.({
event: SseResponseEventEnum.answer,
@ -87,11 +88,10 @@ export const dispatchSkillAgent = async (
addLog.warn(`[Skill agent] Failed to parse JSON response`, { text: answerText });
throw new Error('Failed to parse JSON response');
}
console.log(responseJson, 22323);
console.log(JSON.stringify(responseJson, null, 2));
// Handle based on phase field
if (responseJson.phase === 'generation') {
addLog.debug('🔄 SkillAgent: Generated skill generation phase');
const parseResult = GeneratedSkillResultSchema.safeParse(responseJson).data;
if (!parseResult) {
@ -116,10 +116,59 @@ export const dispatchSkillAgent = async (
} else if (responseJson.phase === 'collection') {
addLog.debug('📝 SkillAgent: Information collection phase');
const displayText = responseJson.question || answerText;
// 检查是否有表单数据
const formData = responseJson.form;
if (formData) {
// 转换为前端可用的表单格式
const inputForm: UserInputInteractive = {
type: 'userInput',
params: {
inputForm: formData.map((item) => {
return {
type: item.type as FlowNodeInputTypeEnum,
key: getNanoid(6),
label: item.label,
value: '',
required: false,
valueType:
item.type === FlowNodeInputTypeEnum.numberInput
? WorkflowIOValueTypeEnum.number
: WorkflowIOValueTypeEnum.string,
list:
'options' in item
? item.options?.map((option) => ({ label: option, value: option }))
: undefined
};
}),
description: responseJson.question
}
};
// 发送表单事件
workflowResponseWrite?.({
event: SseResponseEventEnum.collectionForm,
data: inputForm
});
return {
aiResponse: formatAIResponse({
text: responseJson.question,
reasoning: responseJson.reasoning || reasoningText,
collectionForm: inputForm
}),
usage
};
}
// 无表单,纯文本问题
workflowResponseWrite?.({
event: SseResponseEventEnum.answer,
data: textAdaptGptResponse({ text: responseJson.question })
});
return {
aiResponse: formatAIResponse({
text: displayText,
text: responseJson.question,
reasoning: responseJson.reasoning || reasoningText
}),
usage

View File

@ -67,35 +67,60 @@ ${currentConfigContext}
<info_collection_phase>
:
****::
**🎯 **
1. ****
- ?
- ?
- ?
Skill
2. ****
- ?(///)
- ?
- ?
****
3. ****
- ?
- 使?
- ?
- ****
- ****
- ****
- 使URL
-
****"类""实例"
****
1. ****
-
-
2. ****
-
-
3. ****
-
-
****:
- ****:"做什么""怎么做",
- ****:,
- ****:,
****:
- 宏观层面:任务目标
- 具体细节:具体参数
- ****
- ****
- ****"类型""类别""实例""个体"
****:
**重要:信息收集阶段的所有回复必须使用JSON格式, phase **
**🚨 **
1. ** JSON ** JSON
2. ****使 \`\`\`json 或任何代码块标记
3. **** JSON
4. ** phase ** "phase": "collection"
****
- 使 \`\`\`json 或其他代码块标记
- "好的,让我问几个问题""明白了"
- "期待您的回复""请告诉我"
- JSON
- JSON
- JSON
****
JSON():
{
@ -104,38 +129,66 @@ ${currentConfigContext}
"question": "实际向用户提出的问题内容"
}
,:
**🧠 **:
****:
{
"phase": "collection",
"reasoning": "需要了解任务的核心定位和战略目标,这将决定整体框架的设计方向",
"question": "这个 Skill 主要想解决什么问题?比如是面向客户服务、内容创作、数据分析,还是其他应用场景?"
}
** A - **
-
-
-
- question
** B - **
-
-
-
- 使 form
****
1.
- A
- B
2.
- // 使
- // 使
3. 使
- 使
- 使
****:
"意图 B" JSON form
{
"phase": "collection",
"reasoning": "需要识别任务所需的核心能力类型,这将决定资源配置的大方向",
"question": "从功能角度看,这个任务主要需要:\\nA. 获取和整合信息(搜索、查询、知识检索)\\nB. 处理和分析数据(计算、转换、分析)\\nC. 生成和创作内容(文档、报告、创意)\\nD. 集成外部服务(API调用、消息通知)\\n\\n请选择最主要的方向,或描述您的理解:"
"reasoning": "...",
"question": "...",
"form": [
{
"type": "input | numberInput",
"label": "<维度描述>"
},
{
"type": "select | multipleSelect",
"label": "<维度描述>",
"options": ["<选项A>", "<选项B>", "..."]
}
]
}
****():
- "第一步需要调用哪个API?"
- "数据格式应该是JSON还是XML?"
- "需要设置多少个验证节点?"
- "具体的搜索关键词是什么?"
****
- input: 短文本输入
- numberInput: 数字输入
- select: 单选下拉
- multipleSelect: 多选下拉
:
- (///)
- (///)
- (//)
- (使//)
:
-
-
-
-
****
- form
- 使
-
</info_collection_phase>
<capability_boundary_enforcement>

View File

@ -85,8 +85,8 @@ export const dispatchTopAgent = async (
const formData = TopAgentFormDataSchema.parse({
role: responseJson.task_analysis?.role,
taskObject: responseJson.task_analysis?.goal,
tools: responseJson.resources?.tools?.map((tool: any) => tool.id),
fileUploadEnabled: responseJson.resources?.file_upload?.enabled
tools: responseJson.resources?.tools,
fileUploadEnabled: responseJson.resources?.system_features?.file_upload?.enabled
});
if (formData) {

View File

@ -38,84 +38,133 @@ ${buildMetadataInfo(metadata)}
<info_collection_phase>
****
****
****
****"用户想要实现什么样的 AI 任务""需要哪些系统资源""每次执行时的具体参数"
1. ****
-
-
-
-
****
2. ****
- ****
* 使
*
* 使
- ****
*
*
*
- ****
*
*
*
1. ****What -
-
-
*
*
*
*
*
-
3. ****
- 使
-
-
2. ****Can -
- ****
* API
*
*
4. ****
-
-
- ///
- ****
*
*
*
5. ****
-
-
-
- ****
* API Key
*
*
3. ****Need
- ****
*
*
*
- ****
*
-
-
*
- ****
*
*
4. ****For Whom
-
*
*
- 使
* 使
*
-
*
*
****
- ****
- ****
- ****
- ****
- ****
- ****
- ****
****
1. /type字段
2.
3.
4.
****
"你想实现什么功能?比如信息搜索、内容生成、数据分析还是其他类型?"
"这个任务需要搜索网络信息吗?还是主要基于已有的知识库?"
"用户需要上传自己的文件吗?还是系统可以自动获取所需数据?"
"这个功能主要面向哪类用户?他们的典型使用场景是什么?"
"系统目前有这些搜索工具:[列出工具],你认为哪个最适合你的需求?"
****
"用户每次输入时需要提供哪些参数?参数格式是什么?"
"默认值应该设置为多少?取值范围是什么?"
"输出结果的具体格式是 JSON 还是文本?包含哪些字段?"
"执行流程的详细步骤是什么?先做什么后做什么?"
"决策分支的具体判断条件是什么?"
****
1. What task_analysis.goal
2. Can
3. Need
4. For Whom 使
****
**使JSON格式 phase **
**🚨 **
JSON
1. ** JSON ** JSON
2. ****使 \`\`\`json 或任何代码块标记
3. **** JSON
4. ** phase ** "phase": "collection"
****
- 使 \`\`\`json 或其他代码块标记
- "好的,让我问几个问题""明白了"
- "期待您的回复""请告诉我"
- JSON
- JSON
- JSON
****
JSON
{
"phase": "collection",
"reasoning": "为什么问这个问题的推理过程:基于什么考虑、希望收集什么信息、对后续有什么帮助",
"reasoning": "为什么问这个问题的推理过程:基于什么考虑、希望收集什么信息、对后续资源选择有什么帮助",
"question": "实际向用户提出的问题内容"
}
****
{
"phase": "collection",
"reasoning": "需要首先了解任务的基本定位和目标场景,这将决定后续需要确认的工具类型和能力边界",
"question": "我想了解一下您希望这个流程模板实现什么功能?能否详细描述一下具体要处理什么样的任务或问题?"
"reasoning": "需要首先了解任务的应用场景类型,这将决定后续需要确认的工具类型和能力边界",
"question": "我想了解一下您希望这个 AI 助手实现什么功能?比如是信息搜索、内容生成、数据分析,还是其他类型的任务"
}
4
**** 4
{
"phase": "collection",
"reasoning": "需要确认参数化设计的重点方向,这将影响流程模板的灵活性设计",
"question": "我需要和你确认一些参数,请根据你的需求选择对应的选项",
"reasoning": "需要确认任务所需的核心能力类型,这将直接影响工具选择",
"question": "为了更好地为您设计流程,我需要确认以下信息",
"form": [
{
"type": "input",
@ -127,44 +176,54 @@ ${buildMetadataInfo(metadata)}
},
{
"type": "select",
"label": "关于流程的参数化设计,用户最需要调整的是",
"label": "这个任务主要需要以下哪种能力",
"options": [
"输入数据源(不同类型的数据库/文件)",
"处理参数(阈值、过滤条件、算法选择)",
"输出格式(报告类型、文件格式、目标系统)",
"执行环境(触发方式、频率、并发度)"
"搜索网络信息(实时获取最新资料)",
"查询知识库(基于已有文档回答)",
"生成内容(写作、设计、编程等)",
"数据分析(处理和分析数据)",
"多种能力组合"
]
},
{
"type": "multipleSelect",
"label": "你想了解用户什么信息",
"label": "用户需要提供哪些输入(可多选)",
"options": [
"选项 A",
"选项 B",
"选项 C",
"选项 D"
"文本描述(直接输入问题或需求)",
"文件上传(用户的私有文档)",
"不需要用户输入(自动执行)"
]
}
]
}
1. 3-4
2. "其他"
3. 便
4. 使
****
1.
2.
3. 3-5
4. "其他""多种组合"
5. 便
- ///
- ///
- ///
- ///
- ///
****
- ///
- ////
- //
- //
-
-
-
****
-
-
-
- 使
-
****
"任务类型""执行参数"
"需要哪些工具""如何使用工具"
"是否需要知识库""知识库的具体内容"
</info_collection_phase>
<capability_boundary_enforcement>
@ -243,30 +302,36 @@ ${resourceList}
****
1. "## 可用资源列表"
2. ID后面都有标签[] []
3. type
- [] "type": "tool"
- [] "type": "knowledge"
3.
- [] resources.tools
- [] resources.knowledges
****
- 使[{"id": "...", "type": "..."}]
- tools knowledges 使["资源ID1", "资源ID2"]
- ID必须完全匹配列表中的ID
- 使["...", "..."]
- type
- ID字符串 type
- 使[{"id": "...", "type": "..."}]
-
****
1. ID
2. [] "type": "tool"
3. [] "type": "knowledge"
4. id type
1. ID
2. []
- ID放入 resources.tools
- resources.knowledges
3. []
- ID放入 resources.knowledges
- resources.tools
4. ID字符串
****
-
- 使"数据库工具"ID
- "可能"
-
- [] type: "tool"
- 使
- [] tools
- [] knowledges
- ****
- type
-
-
- ****
@ -333,8 +398,8 @@ ${resourceList}
- task_analysis: 提供对任务的深度理解和角色定义
- reasoning: 说明所有资源++
- resources: 资源配置对象
* tools: 工具数组 id type"tool"
* knowledges: 知识库数组 id type"knowledge"
* tools: 工具ID字符串数组["tool_id_1", "tool_id_2"]
* knowledges: 知识库ID字符串数组["kb_id_1", "kb_id_2"]
* system_features: 系统功能配置对象
- file_upload.enabled: 是否需要文件上传
- file_upload.purpose: 为什么需要enabled=true时必填
@ -411,7 +476,6 @@ ${resourceList}
****
- 使 \`\`\`json 或其他代码块标记
- 使 tools 使 resources
-
- 使 resources toolsknowledgessystem_features
- file_upload.enabled=true purpose
@ -424,7 +488,6 @@ ${resourceList}
3. resources
4. resources
5. tools knowledges
6. type准确性type为"tool"type为"knowledge"
7. file_upload.enabled=true时必须提供purpose字段
8. JSON
</config_generation_phase>

View File

@ -25,9 +25,11 @@ export const TopAgentGenerationAnswerSchema = z.object({
resources: z.object({
tools: z.array(z.string()),
knowledges: z.array(z.string()),
file_upload: z.object({
enabled: z.boolean(),
purpose: z.string()
system_features: z.object({
file_upload: z.object({
enabled: z.boolean(),
purpose: z.string().nullish()
})
})
})
});

View File

@ -145,8 +145,8 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
toolDescription: toolNode?.toolDescription || toolNode?.name || ''
};
};
// console.log(JSON.stringify(completionTools, null, 2), 'topAgent completionTools');
// console.log(subAppsMap, 'topAgent subAppsMap');
console.log(JSON.stringify(agentCompletionTools, null, 2), 'topAgent completionTools');
console.log(agentSubAppsMap, 'topAgent subAppsMap');
/* ===== AI Start ===== */
@ -418,6 +418,7 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
const isReplanStep = isPlanAgent && agentPlan && replanMessages;
console.log('planHistoryMessages', planHistoryMessages);
// 执行 Plan/replan
if (isPlanStep) {
const result = await planCallFn();
@ -429,8 +430,9 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
});
if (result) return result;
}
// 如果有保存的 skill id恢复 skill 的 tools
else if (matchedSkillId) {
if (matchedSkillId) {
addLog.debug(`恢复 skill tools, skill id: ${matchedSkillId}`);
const skill = await matchSkillForId({
id: matchedSkillId,

View File

@ -67,21 +67,18 @@ const ChatTest = ({ topAgentSelectedTools = [], skill, appForm, onAIGenerate }:
onApply={async (generatedSkillData) => {
console.log(generatedSkillData, 222);
// 1. 计算新的 tool
const newToolIds: string[] = [];
// 1. 收集 AI 生成的所有工具 ID完整列表不过滤
const allGeneratedToolIds: string[] = [];
generatedSkillData.execution_plan.steps.forEach((step) => {
step.expectedTools?.forEach((tool) => {
if (tool.type === 'tool') {
const exists = skill.selectedTools.find((t) => t.pluginId === tool.id);
if (exists) return;
newToolIds.push(tool.id);
if (tool.type === 'tool' && !allGeneratedToolIds.includes(tool.id)) {
allGeneratedToolIds.push(tool.id);
}
});
});
// 3. 并行获取新工具详情
const newTools = await loadGeneratedTools({
newToolIds,
newToolIds: allGeneratedToolIds,
existsTools: skill.selectedTools,
topAgentSelectedTools,
fileSelectConfig: appForm.chatConfig.fileSelectConfig