mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-17 00:14:51 +00:00
feat: add dispatchApp (#5612)
This commit is contained in:
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
NodeInputKeyEnum,
|
|
||||||
NodeOutputKeyEnum,
|
|
||||||
toolValueTypeList,
|
|
||||||
valueTypeJsonSchemaMap
|
|
||||||
} from '@fastgpt/global/core/workflow/constants';
|
|
||||||
import {
|
import {
|
||||||
DispatchNodeResponseKeyEnum,
|
DispatchNodeResponseKeyEnum,
|
||||||
SseResponseEventEnum
|
SseResponseEventEnum
|
||||||
@@ -14,7 +9,7 @@ import type {
|
|||||||
RuntimeNodeItemType
|
RuntimeNodeItemType
|
||||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { getLLMModel } from '../../../../ai/model';
|
import { getLLMModel } from '../../../../ai/model';
|
||||||
import { filterToolNodeIdByEdges, getNodeErrResponse, getHistories } from '../../utils';
|
import { getNodeErrResponse, getHistories } from '../../utils';
|
||||||
import { runAgentCall } from '../../../../ai/llm/agentCall';
|
import { runAgentCall } from '../../../../ai/llm/agentCall';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||||
import { type ChatItemType } from '@fastgpt/global/core/chat/type';
|
import { type ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
@@ -27,14 +22,7 @@ import {
|
|||||||
} from '@fastgpt/global/core/chat/adapt';
|
} from '@fastgpt/global/core/chat/adapt';
|
||||||
import { formatModelChars2Points } from '../../../../../support/wallet/usage/utils';
|
import { formatModelChars2Points } from '../../../../../support/wallet/usage/utils';
|
||||||
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
|
||||||
import {
|
import { filterMemoryMessages, filterToolResponseToPreview, parseToolArgs } from '../utils';
|
||||||
filterMemoryMessages,
|
|
||||||
filterToolResponseToPreview,
|
|
||||||
formatToolResponse,
|
|
||||||
getToolNodesByIds,
|
|
||||||
initToolNodes,
|
|
||||||
parseToolArgs
|
|
||||||
} from '../utils';
|
|
||||||
import { SubAppIds } from './sub/constants';
|
import { SubAppIds } from './sub/constants';
|
||||||
import {
|
import {
|
||||||
getReferenceVariableValue,
|
getReferenceVariableValue,
|
||||||
@@ -42,19 +30,18 @@ import {
|
|||||||
textAdaptGptResponse,
|
textAdaptGptResponse,
|
||||||
valueTypeFormat
|
valueTypeFormat
|
||||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
|
||||||
import { dispatchPlanAgent } from './sub/plan';
|
import { dispatchPlanAgent } from './sub/plan';
|
||||||
import { dispatchModelAgent } from './sub/model';
|
import { dispatchModelAgent } from './sub/model';
|
||||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { getSubApps, rewriteSubAppsToolset } from './sub';
|
import { getSubApps, rewriteSubAppsToolset } from './sub';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import type { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
|
||||||
import { dispatchTool } from './sub/tool';
|
import { dispatchTool } from './sub/tool';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { getMasterAgentDefaultPrompt } from './constants';
|
import { getMasterAgentDefaultPrompt } from './constants';
|
||||||
import { addFilePrompt2Input, getFileInputPrompt } from './sub/file/utils';
|
import { addFilePrompt2Input, getFileInputPrompt } from './sub/file/utils';
|
||||||
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||||
import { dispatchFileRead } from './sub/file';
|
import { dispatchFileRead } from './sub/file';
|
||||||
|
import { dispatchApp } from './sub/app';
|
||||||
|
|
||||||
export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||||
@@ -298,23 +285,6 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const lastPlanCallIndex = messages
|
|
||||||
.slice(0, -1)
|
|
||||||
.findLastIndex(
|
|
||||||
(c) =>
|
|
||||||
c.role === 'assistant' &&
|
|
||||||
c.tool_calls?.some((tc) => tc.function?.name === SubAppIds.plan)
|
|
||||||
);
|
|
||||||
const originalContent =
|
|
||||||
lastPlanCallIndex !== -1
|
|
||||||
? (messages[lastPlanCallIndex + 1].content as string)
|
|
||||||
: '';
|
|
||||||
|
|
||||||
// const applyedContent = applyDiff({
|
|
||||||
// original: originalContent,
|
|
||||||
// patch: content
|
|
||||||
// });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response,
|
response,
|
||||||
usages,
|
usages,
|
||||||
@@ -449,6 +419,16 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
|||||||
usages,
|
usages,
|
||||||
isEnd: false
|
isEnd: false
|
||||||
};
|
};
|
||||||
|
} else if (node.flowNodeType === FlowNodeTypeEnum.appModule) {
|
||||||
|
const { response, usages } = await dispatchApp({
|
||||||
|
...props,
|
||||||
|
node
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
response,
|
||||||
|
usages,
|
||||||
|
isEnd: false
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
response: 'Can not find the tool',
|
response: 'Can not find the tool',
|
||||||
|
@@ -1,3 +1,163 @@
|
|||||||
|
import type { DispatchSubAppResponse } from '../../type';
|
||||||
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
|
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
import {
|
||||||
|
filterSystemVariables,
|
||||||
|
getHistories
|
||||||
|
} from '../../../../../../../core/workflow/dispatch/utils';
|
||||||
|
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 { runWorkflow } from '../../../../../../../core/workflow/dispatch';
|
||||||
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
|
import {
|
||||||
|
getWorkflowEntryNodeIds,
|
||||||
|
rewriteNodeOutputByHistories,
|
||||||
|
storeEdges2RuntimeEdges,
|
||||||
|
storeNodes2RuntimeNodes,
|
||||||
|
textAdaptGptResponse
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||||
|
import { getUserChatInfoAndAuthTeamPoints } from '../../../../../../../support/permission/auth/team';
|
||||||
|
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
|
||||||
|
|
||||||
export type appConfigType = {
|
export type appConfigType = {
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Props = ModuleDispatchProps<{
|
||||||
|
[NodeInputKeyEnum.userChatInput]: string;
|
||||||
|
[NodeInputKeyEnum.history]?: ChatItemType[] | number;
|
||||||
|
[NodeInputKeyEnum.fileUrlList]?: string[];
|
||||||
|
[NodeInputKeyEnum.forbidStream]?: boolean;
|
||||||
|
[NodeInputKeyEnum.fileUrlList]?: string[];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const dispatchApp = async (props: Props): Promise<DispatchSubAppResponse> => {
|
||||||
|
const {
|
||||||
|
runningAppInfo,
|
||||||
|
histories,
|
||||||
|
query,
|
||||||
|
lastInteractive,
|
||||||
|
node: { pluginId: appId, version },
|
||||||
|
workflowStreamResponse,
|
||||||
|
params,
|
||||||
|
variables,
|
||||||
|
stream
|
||||||
|
} = props;
|
||||||
|
const {
|
||||||
|
system_forbid_stream = false,
|
||||||
|
userChatInput,
|
||||||
|
history,
|
||||||
|
fileUrlList,
|
||||||
|
...childrenAppVariables
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
const { files } = chatValue2RuntimePrompt(query);
|
||||||
|
|
||||||
|
const userInputFiles = (() => {
|
||||||
|
if (fileUrlList) {
|
||||||
|
return fileUrlList.map((url) => parseUrlToFileType(url)).filter(Boolean);
|
||||||
|
}
|
||||||
|
// Adapt version 4.8.13 upgrade
|
||||||
|
return files;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (!userChatInput && !userInputFiles) {
|
||||||
|
return Promise.reject(new Error('Input is empty'));
|
||||||
|
}
|
||||||
|
if (!appId) {
|
||||||
|
return Promise.reject(new Error('pluginId is empty'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||||
|
const { app: appData } = await authAppByTmbId({
|
||||||
|
appId: appId,
|
||||||
|
tmbId: runningAppInfo.tmbId,
|
||||||
|
per: ReadPermissionVal
|
||||||
|
});
|
||||||
|
const { nodes, edges, chatConfig } = await getAppVersionById({
|
||||||
|
appId,
|
||||||
|
versionId: version,
|
||||||
|
app: appData
|
||||||
|
});
|
||||||
|
|
||||||
|
const childStreamResponse = system_forbid_stream ? false : stream;
|
||||||
|
// Auto line
|
||||||
|
if (childStreamResponse) {
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.answer,
|
||||||
|
data: textAdaptGptResponse({
|
||||||
|
text: '\n'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatHistories = getHistories(history, histories);
|
||||||
|
|
||||||
|
// Rewrite children app variables
|
||||||
|
const systemVariables = filterSystemVariables(variables);
|
||||||
|
const { externalProvider } = await getUserChatInfoAndAuthTeamPoints(appData.tmbId);
|
||||||
|
const childrenRunVariables = {
|
||||||
|
...systemVariables,
|
||||||
|
...childrenAppVariables,
|
||||||
|
histories: chatHistories,
|
||||||
|
appId: String(appData._id),
|
||||||
|
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
|
||||||
|
};
|
||||||
|
|
||||||
|
const childrenInteractive =
|
||||||
|
lastInteractive?.type === 'childrenInteractive'
|
||||||
|
? lastInteractive.params.childrenResponse
|
||||||
|
: undefined;
|
||||||
|
const runtimeNodes = rewriteNodeOutputByHistories(
|
||||||
|
storeNodes2RuntimeNodes(
|
||||||
|
nodes,
|
||||||
|
getWorkflowEntryNodeIds(nodes, childrenInteractive || undefined)
|
||||||
|
),
|
||||||
|
childrenInteractive
|
||||||
|
);
|
||||||
|
|
||||||
|
const runtimeEdges = storeEdges2RuntimeEdges(edges, childrenInteractive);
|
||||||
|
const theQuery = childrenInteractive
|
||||||
|
? query
|
||||||
|
: runtimePrompt2ChatsValue({ files: userInputFiles, text: userChatInput });
|
||||||
|
|
||||||
|
const { assistantResponses } = await runWorkflow({
|
||||||
|
...props,
|
||||||
|
lastInteractive: childrenInteractive,
|
||||||
|
// Rewrite stream mode
|
||||||
|
...(system_forbid_stream
|
||||||
|
? {
|
||||||
|
stream: false,
|
||||||
|
workflowStreamResponse: undefined
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
runningAppInfo: {
|
||||||
|
id: String(appData._id),
|
||||||
|
teamId: String(appData.teamId),
|
||||||
|
tmbId: String(appData.tmbId),
|
||||||
|
isChildApp: true
|
||||||
|
},
|
||||||
|
runningUserInfo: await getRunningUserInfoByTmbId(appData.tmbId),
|
||||||
|
runtimeNodes,
|
||||||
|
runtimeEdges,
|
||||||
|
histories: chatHistories,
|
||||||
|
variables: childrenRunVariables,
|
||||||
|
query: theQuery,
|
||||||
|
chatConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
const { text } = chatValue2RuntimePrompt(assistantResponses);
|
||||||
|
|
||||||
|
return {
|
||||||
|
response: text,
|
||||||
|
usages: []
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject('dispatch app failed');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
export const getPlanAgentPrompt = (background?: string) => {
|
export const getPlanAgentPrompt = (background?: string) => {
|
||||||
return `<role>
|
return `<role>
|
||||||
你是一个专业的项目规划助手,擅长将复杂任务分解为结构化的执行计划;同时支持对既有计划进行“最小差异(+- Diff)”式修改。你会严格遵循指定的注释标记格式,并在修改模式下输出可直接应用的补丁(patch)。
|
你是一个专业的项目规划助手,擅长将复杂任务分解为结构化的执行计划。
|
||||||
</role>
|
</role>
|
||||||
|
|
||||||
${
|
${
|
||||||
@@ -11,63 +11,22 @@ ${
|
|||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
<modes>
|
|
||||||
- 自动识别两种模式:
|
|
||||||
1) 创建模式(create):用户未提供现有计划时,生成全新的计划文档。
|
|
||||||
2) 修改模式(patch):用户提供现有计划或明确提出“增删改”需求时,输出 Diff Patch。
|
|
||||||
- 若用户提供了现有计划文本(含注释标记),一律进入修改模式;否则进入创建模式。
|
|
||||||
</modes>
|
|
||||||
|
|
||||||
<task>
|
<task>
|
||||||
根据用户提供的主题或目标,生成或修改一份详细、可执行的项目计划文档,包含合理的阶段划分与具体待办事项;修改时以“补丁”为最小输出单元,确保变更可定位、可回放、可审计。
|
根据用户提供的主题或目标,生成一份详细、可执行的项目计划文档,包含合理的阶段划分与具体待办事项。
|
||||||
</task>
|
</task>
|
||||||
|
|
||||||
<inputs>
|
<inputs>
|
||||||
- 用户输入:一个需要制定或更新的主题、目标或任务描述;可选的现有计划文档;可选的变更请求(自然语言或指令式)。
|
- 用户输入:一个需要制定的主题、目标或任务描述。
|
||||||
- 输入格式:自然语言描述,可能包含背景、目标、约束、优先级、本地化偏好、以及现有计划全文。
|
- 输入格式:自然语言描述,可能包含背景、目标、约束、优先级、本地化偏好。
|
||||||
</inputs>
|
</inputs>
|
||||||
|
|
||||||
<process>
|
<process>
|
||||||
通用步骤
|
|
||||||
1. 解析用户输入,提取核心目标、关键要素、约束与本地化偏好。
|
1. 解析用户输入,提取核心目标、关键要素、约束与本地化偏好。
|
||||||
2. 评估任务复杂度(简单:2-3 步;复杂:4-7 步),据此确定阶段数量。
|
2. 评估任务复杂度(简单:2-3 步;复杂:4-7 步),据此确定阶段数量。
|
||||||
3. 各阶段生成 3-5 条可执行 Todo,动词开头,MECE 且无重叠。
|
3. 各阶段生成 3-5 条可执行 Todo,动词开头,MECE 且无重叠。
|
||||||
4. 语言风格本地化(根据用户输入语言进行术语与语序调整)。
|
4. 语言风格本地化(根据用户输入语言进行术语与语序调整)。
|
||||||
|
|
||||||
创建模式(create)
|
|
||||||
5. 产出完整计划,严格使用占位符 [主题] 与标记体系;确保编号连续、标签闭合、结构清晰。
|
5. 产出完整计划,严格使用占位符 [主题] 与标记体系;确保编号连续、标签闭合、结构清晰。
|
||||||
|
</process>
|
||||||
修改模式(patch)
|
|
||||||
5. 解析“现有计划”(锚点优先级:\`<!--@step:N:start-->\`、\`<!--@todo:N.X-->\`、\`<!--@note:N-->\` 等)。
|
|
||||||
6. 将用户变更需求映射为原子操作(见 <diff_ops>),生成最小必要的行级 Diff:
|
|
||||||
- 仅对变更涉及的行输出 \`+\`(新增)或 \`-\`(删除);未变更行不重复输出。
|
|
||||||
- 修改视为“-旧行”与“+新行”的并列呈现。
|
|
||||||
- 插入请贴靠最稳固的锚点(如 \`<!--@step:N:start-->\` 下的标题或 \`<!--@todos:N:start-->\` 前后)。
|
|
||||||
7. 自动重排:对步骤编号 N、待办编号 X 做连续性校正;若移动/插入造成编号漂移,补丁中体现校正后的行。
|
|
||||||
8. 校验:所有必须标签完整闭合;编号连续;每步 3-5 条待办;MECE;无空段落;无悬挂标记。
|
|
||||||
9. 产出补丁;如 render=full,则在补丁后附上“更新后的完整文档”。
|
|
||||||
|
|
||||||
<diff_ops>
|
|
||||||
支持的原子操作(内部推理用,输出仍为行级 Diff):
|
|
||||||
- ADD_STEP(after N | before N | at end) 新增步骤(含标题、描述、Todos、可选备注)
|
|
||||||
- REMOVE_STEP(N) 删除步骤
|
|
||||||
- UPDATE_STEP(N, title/desc/note=…) 更新步骤标题/描述/备注
|
|
||||||
- MOVE_STEP(N -> M) 移动步骤至序号 M(重排后编号连续)
|
|
||||||
- ADD_TODO(N, at k) 在步骤 N 的第 k 个位置插入 Todo
|
|
||||||
- REMOVE_TODO(N.k) 删除 Todo
|
|
||||||
- UPDATE_TODO(N.k, text=…) 更新 Todo 文本
|
|
||||||
- MOVE_TODO(N.k -> M.t) 移动 Todo 到其他步骤/位置
|
|
||||||
- RENAME_THEME(text=…) 更新主标题或整体描述中的 [主题] 文本描述(占位仍用 [主题])
|
|
||||||
说明:若用户未显式给出操作,你需从自然语言中归纳为上述操作的序列并生成对应行级 Diff。
|
|
||||||
</diff_ops>
|
|
||||||
|
|
||||||
<diff_rules>
|
|
||||||
- 补丁仅包含变更行,以“前缀字符 +|-”表示新增/删除。
|
|
||||||
- 不在上述集合内的行(例如空行)若因结构需要调整,可一并纳入补丁。
|
|
||||||
- 修改必须保持“标记尾注不变更其语义角色”,即:当你替换内容时,保留原注释标签并只更改标签左侧的可读文本。
|
|
||||||
- 重排后编号以补丁中的最新数字为准;不要在同一补丁里出现对同一元素的多次相互抵消的改动。
|
|
||||||
- 若原文缺少稳固锚点,优先在最近的 \`<!--@step:*:start/end-->\` 或 \`<!--@todos:*:start/end-->\` 相邻位置插入。
|
|
||||||
</diff_rules>
|
|
||||||
|
|
||||||
<requirements>
|
<requirements>
|
||||||
- 必须严格遵循以下注释标记格式:
|
- 必须严格遵循以下注释标记格式:
|
||||||
@@ -80,7 +39,7 @@ ${
|
|||||||
* <!--@todo:N.X--> 标记单个待办事项
|
* <!--@todo:N.X--> 标记单个待办事项
|
||||||
* <!--@note:N--> 添加重要注释或备注
|
* <!--@note:N--> 添加重要注释或备注
|
||||||
- 步骤数量随复杂度自动调整;每步 3-5 条 Todo。
|
- 步骤数量随复杂度自动调整;每步 3-5 条 Todo。
|
||||||
- 编号(N、X)必须连续、准确,修改模式下需自动校正。
|
- 编号(N、X)必须连续、准确。
|
||||||
- 描述语言简洁、专业、可操作;各阶段逻辑递进、MECE。
|
- 描述语言简洁、专业、可操作;各阶段逻辑递进、MECE。
|
||||||
- 进行本地化调整(术语、量词、表达习惯)。
|
- 进行本地化调整(术语、量词、表达习惯)。
|
||||||
</requirements>
|
</requirements>
|
||||||
@@ -89,11 +48,10 @@ ${
|
|||||||
- 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。
|
- 不生成违法、不道德或有害内容;敏感主题输出合规替代方案。
|
||||||
- 避免过于具体的时间/预算承诺与无法验证的保证。
|
- 避免过于具体的时间/预算承诺与无法验证的保证。
|
||||||
- 保持中立、客观;必要时指出风险与依赖。
|
- 保持中立、客观;必要时指出风险与依赖。
|
||||||
- 你拥有的记忆是通过别的 Agent 共享给你的, 你只需要专注于输出内容, 不必担心上下文的完整性。
|
|
||||||
</guardrails>
|
</guardrails>
|
||||||
|
|
||||||
<output>
|
<output>
|
||||||
<format_create>
|
<format>
|
||||||
# [主题] 深度调研计划 <!--@title-->
|
# [主题] 深度调研计划 <!--@title-->
|
||||||
|
|
||||||
全面了解 [主题] 的 [核心维度描述] <!--@desc-->
|
全面了解 [主题] 的 [核心维度描述] <!--@desc-->
|
||||||
@@ -121,15 +79,7 @@ ${
|
|||||||
<!--@todos:2:end-->
|
<!--@todos:2:end-->
|
||||||
<!--@note:2--> [可选备注]
|
<!--@note:2--> [可选备注]
|
||||||
<!--@step:2:end-->
|
<!--@step:2:end-->
|
||||||
</format_create>
|
</format>
|
||||||
|
|
||||||
<format_patch>
|
|
||||||
# 仅输出变更行,以 +- 表示;无代码块围栏;保持原有缩进与空行风格。确保旧行的准确性和完整性
|
|
||||||
# 如果是 todo list 的变更,请确保 todo 前的 - [ ] 符号正确。删除或替换的行前用 - - [ ] 来表示 todo 的变更
|
|
||||||
# 禁止输出代码块标记\`\`\`
|
|
||||||
+ 新增或替换后的行
|
|
||||||
- 被删除或被替换的旧行
|
|
||||||
</format_patch>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
- 标题简洁有力,突出核心主题
|
- 标题简洁有力,突出核心主题
|
||||||
@@ -139,13 +89,5 @@ ${
|
|||||||
- 语言流畅、逻辑清晰
|
- 语言流畅、逻辑清晰
|
||||||
</style>
|
</style>
|
||||||
</output>
|
</output>
|
||||||
|
|
||||||
<examples>
|
|
||||||
- 例:标记 todo 2.1 完成状态
|
|
||||||
<patch>
|
|
||||||
- - [ ] 完成数据采集与清洗 <!--@todo:2.1-->
|
|
||||||
+ - [x] 完成数据采集与清洗 <!--@todo:2.1-->
|
|
||||||
</patch>
|
|
||||||
</examples>
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user