FastGPT 统一工具参数转换与运行时编译(Tool Input Runtime)设计与实现解析
【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT
导读:本文以 .agents/design/app/tool-input-runtime.md 为骨架,深入剖析 FastGPT 中工具参数从「原始定义」到「模型可见参数」再到「最终执行参数」的统一编译链路。你将理解
ToolInputDefinition / ToolInputConfiguration / CompiledToolRuntime三层数据模型、AgentV2 与工作流 ToolCall 共用的 compiler 与参数合并策略、AJV 运行时校验的边界,以及这套设计如何在不迁移存量数据的前提下保持数据库与公开协议兼容。
一、背景:三类消费方为何需要统一编译 seam
FastGPT 的工具定义可能来自两种源头:
- JSON Schema:HTTP 工具(OpenAPI 解析)、MCP 工具、系统工具(System Tool)声明输入输出;
- NodeIO:工作流节点输入项(
FlowNodeInputItemType),用于插件工作流、普通工具节点和子应用。
这些工具定义会被三类运行时消费:
- AgentV2:Agent 节点中挂载的工具,按
{ key, mode } + config持久化; - 工作流 ToolCall:ToolCall 节点挂载的工具,按 NodeIO 保存;
- 普通工作流执行:通过工具节点直接调用的外部工具。
设计文档的核心诉求是建立统一编译 seam,把「原始定义」「持久化配置」「模型可见参数」「最终执行参数」四层解耦。这样三种消费方共享同一套编译、过滤、合并、校验逻辑,同时保持现有数据库结构与公开协议不变。
二、数据模型:四个核心概念
1.ToolInputDefinition:参数的原始定义
位于 packages/global/core/app/tool/runtime.ts:
export type ToolInputDefinition = { key: string; // 参数唯一 key,对应当前工具定义的 NodeIO key jsonSchema?: JsonSchemaPropertiesItemType; // 原始 JSON Schema property(如 HTTP/MCP 工具) nodeInput: FlowNodeInputItemType; // NodeIO 投影视图 allowedModes: AgentToolInputModeEnum[]; // 该参数允许的输入来源 };createToolInputDefinitions通过两个判断决定allowedModes:
canInputBeAgentGenerated(input):是否允许 Agent 生成(与 formEdit/utils.ts 中的agentGeneratedDenyRenderTypes黑名单强相关,password、selectLLMModel、selectDataset 等类型被排除);canUseFixedBinding:是否允许手工绑定。手工控件(manualInputRenderTypes)存在、或当前类型为reference(工作流连线)、或根本不允许 Agent 生成时,都允许手工模式。
2.ToolInputConfiguration:持久化的最终选择
export type ToolInputConfiguration = { key: string; mode: AgentToolInputModeEnum; // agentGenerated | manual binding?: unknown; // 可选手工绑定值 };createToolInputConfigurations读取 NodeIO 的最终输入来源,若请求模式不在allowedModes内,则回退到第一个允许的模式(allowedModes[0] ?? manual),这是运行时安全边界的重要一环。
3.CompiledToolRuntime:编译产物
export type CompiledToolRuntime = { modelTool: ChatCompletionTool; // 发给 LLM 的 function schema agentGeneratedKeys: string[]; // Agent 参数白名单 fixedInputBindings: Record<string, unknown>; // 固定输入绑定 };4. 两个编译函数
compileToolRuntime():从最新工具定义和当前配置生成唯一运行时描述(runtime.ts);mergeToolRuntimeParams():过滤模型未知字段并与固定绑定合并,拒绝两组 key 冲突(runtime.ts)。
三、转换规则:七条行为约束
文档定义了七条转换规则,与源码逐一对应:
HTTP、MCP 和系统工具保留完整 JSON Schema property,并投影为 NodeIO 配置视图。对应 jsonschema.ts 的
jsonSchema2NodeInput:每个 property 通过canProjectJsonSchemaToNodeInput判断可投影性,同时保留customJsonSchema副本与toolDescription。基础类型、对象、同类型数组、枚举和同基础类型 nullable union 支持手工配置。
canProjectJsonSchemaToNodeInput(jsonschema.ts)从type / const / enum / anyOf / oneOf分支归一共同基础类型;数组仅当items为单一 schema 且元素类型为基础类型时才允许投影。混合 union、tuple 和无法确定基础类型的 schema 只允许 Agent 生成。无法归一类型时返回
WorkflowIOValueTypeEnum.any,并在jsonSchema2NodeInput中降级为renderTypeList: [agentGenerated]+selectedType: agentGenerated(jsonschema.ts)。文件、知识库、模型等 NodeIO 专属输入只允许手工配置,不进入模型 schema。对应
unsupportedToolInputRenderTypes黑名单(formEdit/utils.ts),以及canInputBeAgentGenerated中的agentGeneratedDenyRenderTypes(formEdit/utils.ts)。AgentV2 继续保存
{ key, mode } + config;Simple 与 Workflow ToolCall 继续保存 NodeIO。canonical schema 定义于 packages/global/core/workflow/migration/schema.ts:CanonicalAgentToolInputConfigSchema = { key, mode },其中mode取AgentToolInputModeEnum(agentGenerated/manual,定义于 packages/global/core/app/tool/constants.ts)。isToolParam只决定首次默认模式;显式mode、selectedType和isToolParam: false优先。normalizeFlowNodeInputType与initAgentToolInputType(formEdit/utils.ts)体现该优先级:显式 mode 直接决定来源,旧版allAgentGenerated/toolDescription推断只在无 mode 时兜底。toolDescription的旧版默认推断只作用于 workflow tool 的原始pluginInput。Agent 运行时在legacyDefaultMode推断中区分 system/commercial 工具(allAgentGenerated)与pluginModule(toolDescription),见 agent/sub/tool/utils.ts。
四、编译流程:AgentV2 与 ToolCall 如何共用 compiler
AgentV2 侧
agent/sub/tool/utils.ts 中的getAgentRuntimeTools是 Agent 侧总入口,流程如下:
- 解析工具 id(
splitCombineToolId),按 source 区分 systemTool / commercial / mcp / http / 个人 App; - 对 App 类工具执行读权限鉴权(
authAppByTmbId,ReadPermissionVal); - 通过
formatSystemToolNode/formatPersonalAppNode/formatMcpToolNode/formatHttpToolNode生成运行时节点(关键点:toolSet会展开为多个子工具); - 用
initAgentToolInputType将持久化的{ key, mode }合并回最新工具定义; filterToolConfiguredParams剔除由 Agent 生成的参数,JSON Editor值经parseJsonEditorValue归一为原生值;getToolConfigStatus检查必填配置是否完整,不完整则直接跳过该工具注册(避免模型调用后才失败);- 调用
compileRuntimeTool(封装compileToolRuntime)产出agentGeneratedInputKeys、params(固定绑定)与requestSchema(模型 schema)。
ToolCall 侧
useToolCatalog.ts 中createToolSchema直接调用compileToolRuntime(...).modelTool生成 function schema,useToolCatalog同时维护nodeId -> 工具节点映射供执行与响应展示复用。注意 datasetSearchNode 不会作为工具暴露给 LLM。
普通工作流
普通工作流节点的 NodeIO 引用仍由工作流引擎解析(reference在编译前已解析为固定值,这正是canUseFixedBinding允许 reference 的原因),普通工作流不经过模型 compiler,仅在外部工具调用前用assertToolRuntimeParams复验参数(runtime.ts)。
五、参数合并与冲突拒绝
mergeToolRuntimeParams的合并策略(runtime.ts):
return { ...fixedInputBindings, ...Object.fromEntries(Object.entries(aiParams).filter(([key]) => generatedKeySet.has(key))) };- 固定绑定(手工配置 + 默认值)始终先展开,作为最终参数的底座;
- 模型返回的参数只保留白名单 key(
agentGeneratedKeys),其余字段一律丢弃; - 若发现某 key 同时出现在固定绑定与白名单中,直接抛错:
Tool input X cannot be both generated and fixed。
测试 packages/global/test/core/app/tool/runtime.test.ts 覆盖了「过滤未知字段、固定绑定权威」与「冲突拒绝」两种场景。
六、模型 schema 的白名单过滤
buildModelVisibleToolJsonSchema(jsonschema.ts)决定模型可见的 JSON Schema:
- 可见 key =
toolParams(Agent 生成输入)∪ schema 中显式isToolParam: true且不属于任何 NodeIO key 的字段; stripModelSchemaAnnotations递归移除title / default / isToolParam等展示与默认值注解,但保留const / enum / examples(它们属于数据本身);required同步按可见 key 过滤。
测试用例「removes NodeIO titles and defaults from model parameters without changing manual bindings」(runtime.test.ts)验证了该行为:query(agentGenerated)进入模型 schema 并保留 description,limit(manual + defaultValue 5)只进入fixedInputBindings。
七、JSON Schema 投影与 NodeIO 往返
jsonschema.ts 提供双向转换能力:
- Schema → NodeIO:
jsonSchema2NodeInput/jsonSchema2NodeOutput/jsonSchema2SecretInput。其中jsonSchema2SecretInput处理isSecret字段,将密钥配置转成hidden输入,绝不进入模型 schema——这与文档「内部 secret/config 字段不进入 schema」的风险条款一致(Agent 运行时中 secrets 通过NodeInputKeyEnum.systemInputConfig的 hidden input 承载,见 agent/sub/tool/utils.ts)。 - NodeIO → Schema:
nodeInput2JsonSchemaProperty/nodeInputs2JsonSchema,从valueTypeJsonSchemaMap推导基础 schema,并回填enum、default、minimum/maximum等约束;getJsonSchemaPropertyFromValueType显式处理any / arrayAny / arrayObject / chatHistory / datasetQuote / dynamic等无法无损表达的语义类型,避免默认退化误导模型 contract。 - Schema 严格校验:
ToolParamJsonSchemaSchema(jsonschema.ts)递归校验 type 与结构关系(如properties仅允许type: object、array必须有items、required引用的 key 必须存在于properties)。
八、AJV 运行时校验:三个校验入口
runtime.ts 构建了基于 AJV 的校验体系:
- 多版本方言支持:同时实例化 AJV draft-07 / 2019-09 / 2020-12,根据 schema 的
$schema字段自动选择; - validator 缓存:以
JSON.stringify(schema)为 key 缓存编译后的校验函数,避免重复编译; validateToolInputValue:用原始 property schema 校验单个手工配置值(配置表单提交时调用);validateToolRuntimeParams:用完整 schema 校验剔除内部字段后的最终执行参数(服务端外部调用前调用);assertToolRuntimeParams:校验失败的抛错版本,错误信息保留可定位的字段路径(/ 字段名 message)。
AJV 配置为{ allErrors: true, strict: false, validateFormats: false }——格式校验关闭,恰好对应文档风险条款「原始 JSON Schema 可能包含未知 format;本期保留结构约束,format 不作为阻断项」。
测试 runtime.test.ts 覆盖:单 property 校验、完整参数校验(含additionalProperties: false拦截未知字段)、根级 union(oneOf)、$ref/$defs、patternProperties以及 draft 2020-12 方言。
九、兼容性策略与风险边界
文档「风险与注意事项」在源码中逐一得到落实:
| 风险条款 | 源码落实 |
|---|---|
| 不迁移存量记录,兼容读取逻辑必须保留 | initAgentToolInputType的legacyDefaultMode推断、AgentToolInputConfigSchema.safeParse容错(非法存量项直接丢弃);canonical schema 只约束新写入 |
| 未知 format 不作为阻断项 | AJVvalidateFormats: false |
| 内部 secret/config 不进入 schema | jsonSchema2SecretInput+ hidden input +canInputBeAgentGenerated黑名单 |
| 内置节点只允许挂载到工作流 ToolCall | agentGeneratedDenyRenderTypes中的selectSkill / selectTool等类型天然排除在 Agent 工具之外 |
另外,compileToolRuntime在无 Agent 生成参数时省略parameters字段(OpenAI function calling 约定),避免空 schema 干扰模型判断。
十、关键代码位置索引
| 关注点 | 路径 |
|---|---|
| 统一编译 seam、参数合并、AJV 校验 | packages/global/core/app/tool/runtime.ts |
| JSON Schema ⇄ NodeIO 双向转换与模型可见过滤 | packages/global/core/app/jsonschema.ts |
| 工具输入模式归一、黑名单、默认模式推断 | packages/global/core/app/formEdit/utils.ts |
| AgentV2 运行时工具加载与编译调用 | packages/service/core/workflow/dispatch/ai/agent/sub/tool/utils.ts |
| ToolCall 工具目录与模型 schema | packages/service/core/workflow/dispatch/ai/toolcall/hooks/useToolCatalog.ts |
| Agent 工具持久化 canonical schema | packages/global/core/workflow/migration/schema.ts |
| 模式枚举与工具来源枚举 | packages/global/core/app/tool/constants.ts |
| 编译器与校验的单元测试 | packages/global/test/core/app/tool/runtime.test.ts |
十一、总结
FastGPT 的工具参数运行时设计以「一个 compiler、两个持久化格式、三个消费方」为核心:所有工具定义先归一为ToolInputDefinition,叠加用户配置形成ToolInputConfiguration,再由compileToolRuntime编译成模型 schema、Agent 白名单与固定绑定三件套;执行前通过mergeToolRuntimeParams过滤合并、通过 AJV 多方言校验兜底。这套 seam 让 AgentV2、ToolCall 与普通工作流共享同一套安全边界,同时借助defaultToAgentGenerated迁移语义与 canonical schema 的「只约束新写入」策略,实现了对存量数据与公开协议的零迁移兼容。
【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考