关于 n8n-code-tool
为使用 n8n LangChain AI Agent 的开发者提供 Custom Code Tool 编写指南,说明其独立于 Code 节点的输入、运行环境和返回约定。
主要功能
- 01区分 Code Tool 与 Code 节点
- 02支持非结构化和结构化输入
- 03规定字符串返回格式
- 04说明沙箱可用能力与限制
- 05提供错误诊断和工具描述建议
适用场景
- 01为 Agent 编写计算或格式转换工具
- 02为多参数工具设置输入 schema
- 03排查工具调用或返回类型错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ czlonkowski/ n8n-skills --skill n8n-code-tool安装后在 Claude Code 中输入/n8n-code-tool确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
ERROR_PATTERNS.mdINPUT_SCHEMA.mdREADME.md详细介绍
n8n Custom Code Tool
关于如何在 @n8n/n8n-nodes-langchain.toolCode 中编写代码的专家指南——这是 AI Agent 可以调用的工具,不是常规工作流 Code 节点。
⚠️ 这不是 Code 节点
Custom Code Tool 在编辑器中看起来像 Code 节点——使用相同的 JavaScript 编辑器,布局也类似——但它是来自另一个软件包的完全不同的节点,遵循不同的运行时契约。
| Code 节点 | Custom Code Tool | |
|---|---|---|
| 节点类型 | n8n-nodes-base.code | @n8n/n8n-nodes-langchain.toolCode |
| 软件包 | n8n-nodes-base | @n8n/n8n-nodes-langchain |
| 调用方 | 上一个节点(工作流流转) | AI Agent(LangChain) |
| 输入 | $input.all() — 条目流 | query — 来自大模型的字符串或对象 |
| 返回值 | [{json: {...}}](条目数组) | 字符串 |
$fromAI() | 不适用 | 不可用(参见“错误”) |
| HTTP 助手 | this.helpers.httpRequest(认证助手不可用) | 工具沙盒中未提供 |
| 状态 | 每次运行的执行数据 | 没有 getContext,也没有 $getWorkflowStaticData |
如果把它当作 Code 节点使用,就会失败。 本指南其余部分将介绍 Code Tool 的实际契约。
快速入门
最简 JavaScript Code Tool
// `query` is whatever the AI sent (a string by default)
return `You asked: ${query}`;
最简 Python Code Tool
# `_query` is whatever the AI sent (a string by default)
return f"You asked: {_query}"
基本规则
- 返回字符串。 数字会自动转换。其他任何类型都会抛出
"The response property should be a string, but it is an object"。 - 输入变量是固定的:
query(JS)、_query(Python)。不能重命名。 - 不要在 Code Tool 沙盒中使用
$fromAI()——这会抛出"No execution data available"。 - 不要使用
[{json: {...}}]这种返回格式——它适用于 Code 节点。使用后会抛出"Wrong output type returned"。 - 使用描述清晰的工具名称(字母、数字和下划线,v1.1+)。Agent 会按名称调用工具。
- 编写准确的描述——大模型会据此决定是否调用工具。
两种输入模式
Code Tool 有两种输入形式,由 specifyInputSchema 控制:
模式 1:非结构化(默认,specifyInputSchema: false)
AI 会将一个字符串作为 query 传入。如果需要多个字段,AI 必须将它们塞进这个字符串中,再由你解析出来。实际上,只要在描述中说明,大模型通常会直接传入 JSON 字符串。
// Parse a JSON string the AI sent
let params;
try {
params = typeof query === 'string' ? JSON.parse(query) : query;
} catch (e) {
throw new Error('Expected a JSON object. Parser said: ' + e.message);
}
const price = Number(params.price);
const months = Number(params.months);
// ...
return JSON.stringify({ monthly_payment: /* ... */ });
优点:设置最简单,只需描述一个字段。 缺点:没有架构验证——如果大模型忘记某个字段,工具就会在运行时抛出错误。
适用场景:快速原型、只有一个自然输入的工具(问题、URL 或文本内容)。
模式 2:结构化(specifyInputSchema: true)
工具会变为 LangChain DynamicStructuredTool。大模型会看到带类型的参数架构,并将经过验证的对象作为 query 传入。你可以直接访问各个字段。
// query is now an object matching your schema
const price = query.price;
const months = query.months;
const residual_percent = query.residual_percent;
const monthly = computeAnnuity(price, months, residual_percent);
return JSON.stringify({ monthly_payment: monthly });
架构可通过以下任一方式定义:
schemaType: "fromJson"+jsonSchemaExample(n8n v≥1.3)——粘贴一个 JSON 示例,n8n 会推断架构schemaType: "manual"+inputSchema——自行编写完整的 JSON Schema
优点:大模型可获取类型提示;无效调用会在代码运行前被拒绝;代码更简洁。 缺点:需要多做一些设置;要求 n8n 版本支持架构。
适用场景:包含多个带类型参数的生产级工具(计算器、API 封装,或任何大模型容易将数值参数转成字符串的工具)。
参见:INPUT_SCHEMA.md,了解完整的架构设置方法。
返回格式
返回值必须是字符串。 大模型会将其作为工具的观察结果读取。
// ✅ String
return "42";
// ✅ Number (auto-converted to string by n8n)
return 42;
// ✅ JSON-encoded structured result (recommended for rich output)
return JSON.stringify({ result: 42, currency: "SEK" });
// ❌ Raw object → "The response property should be a string, but it is an object"
return { result: 42 };
// ❌ Workflow item format → "Wrong output type returned"
return [{ json: { result: 42 } }];
// ❌ Array → "The response property should be a string, but it is an object"
return [1, 2, 3];
最佳实践:将结构化结果序列化为 JSON 字符串
如果工具的输出不只是简单的标量值,请返回 JSON 字符串:
return JSON.stringify({
monthly_payment_sek: 5405,
loan_amount: 351920,
total_cost_of_credit: 63295
});
大模型能够可靠地解析 JSON,并挑选出向用户展示所需的字段。
错误处理:Agent 会读取你的错误信息
错误不会只是让工作流停止,还会返回给大模型,而大模型通常会修正调用并重试。善用这一点:
// Option A: throw — n8n surfaces the message to the agent
if (!isFinite(price)) throw new Error('price must be a number, e.g. 439900');
// Option B: return an error string — agent reads it like any tool result
if (!isFinite(price)) return JSON.stringify({ error: 'price must be a number, e.g. 439900' });
无论哪种情况,错误信息都要写给大模型看:说明哪里出错了,以及有效的调用应该是什么样的。简单地写一句 throw new Error('invalid input') 会浪费重试机会;提供指导性信息通常能修正下一次调用。
工具名称和描述
这些字段不是文档,而是大模型看到的工具契约。请将它们视为提示词工程。
名称
- 必须匹配
[A-Za-z0-9_]+(v1.1+)。不能包含空格、连字符或表情符号。 - 使用动词开头且含义明确的名称:
calculate_car_loan、get_weather、search_orders。 - Agent 会通过此名称调用工具。
Code Tool(默认名称)毫无用处——Agent 不知道何时该调用它。
描述
- 说明何时使用该工具,以及要发送什么内容。
- 如果使用非结构化模式,请包含大模型应发送的 JSON 字符串示例。
- 如果使用结构化模式,架构本身已经说明一切,只需描述用途。
非结构化示例(JSON 字符串模式):
Deterministiskt beräknar månadskostnad för billån. Anropa med EN JSON-sträng:
{"price":439900,"down_payment":87980,"interest_rate":6.95,"months":36,"residual_percent":50}
Fält: price (SEK), down_payment (SEK), interest_rate (% per år), months, residual_percent (0-99).
结构化示例(由架构定义):
Deterministically computes the monthly car-loan payment given price, down payment,
annual interest rate, term, and residual percent. Use whenever the user asks for
monthly cost, total credit cost, or loan breakdown.
常见错误及修复方法
错误 1:"There was an error: 'Cannot assign to read only property \"name\" of object: Error: No execution data available'"
原因:你在 Code Tool 沙箱内调用了 $fromAI()。
修复方法:$fromAI() 是供其他启用了工具的节点使用的辅助函数(HTTP Request Tool、SendGrid Tool、toolWorkflow 等),在 toolCode 中不可用。请直接从 query 读取 AI 的输入(或对结构化字段使用 specifyInputSchema)。
错误 2:"Wrong output type returned"
原因:你返回了类似 [{ json: { ... } }] 的工作流风格数组。这是 Code 节点的契约,不是 Code 工具的契约。
修复方法:返回字符串。对于结构化数据,使用 return JSON.stringify(output)。
错误 3:"The response property should be a string, but it is an object"
原因:你返回了普通对象或数组。
修复方法:对结果使用 JSON.stringify(),或将其强制转换为字符串。
错误 4:AI 从不调用工具
原因:工具名称过于笼统(Code Tool、My Tool),或描述没有清楚说明何时使用该工具。
修复方法:将名称改为以动词开头且含义明确的名称(calculate_car_loan),并重写描述,明确说明触发条件(例如:“每当用户询问月度费用时,使用此工具”)。
错误 5:AI 向 query 发送乱码
原因:非结构化工具的描述含糊不清。大模型会猜测格式。
修复方法:可以选择 (a) 在描述中加入具体的 JSON 示例,或 (b) 将 specifyInputSchema: true 打开,让大模型获得类型化架构。
参见:ERROR_PATTERNS.md,了解包含复现步骤的完整目录。
沙箱中不可用的内容
Code Tool 沙箱的功能范围比 Code 节点沙箱窄。不要假定辅助函数也能通用:
| 辅助函数 | Code 节点 | Code Tool |
|---|---|---|
$input.all()、$input.first()、$input.item | ✅ | ❌ |
$node["NodeName"] | ✅ | ❌ |
$json、$binary | ✅ | ❌ |
$fromAI() | ❌ | ❌(即使它就在 AI Agent 旁边) |
this.helpers.httpRequest() | ✅ | ❌ |
DateTime(Luxon) | ✅ | ✅(JS 沙箱中的标准内容) |
$jmespath() | ✅ | ❌ |
this.getContext(...) | ✅ | ❌ |
$getWorkflowStaticData(...) | ✅ | ❌ |
影响:Code Tool 用于纯计算。如果需要 HTTP 调用、API 查询或跨调用状态,请使用其他工具节点:
- 用于外部 API 调用的 HTTP Request Tool
- 用于访问完整 Code 节点沙箱并执行多步逻辑的
toolWorkflow(Call Sub-workflow Tool) - 用于持久化状态的 MCP / 数据库工具
何时使用 Code Tool 与其他替代方案
在以下情况下使用 Code Tool:
- ✅ 纯确定性计算(数学运算、解析、格式化、验证)
- ✅ LLM 不应自行处理的轻量级转换(精确数学运算、正则表达式)
- ✅ 希望将代码内联到工作流中,而不是放在单独的子工作流里
在以下情况下使用 toolWorkflow(调用子工作流工具):
- ✅ 需要多个参数,并通过
$fromAI()进行清晰的类型定义 - ✅ 需要访问
this.helpers、凭据或其他节点 - ✅ 逻辑需要在多个 Agent 之间复用
- ✅ 希望使用结构化的类型化输入,而无需编写 JSON Schema
在以下情况下使用 HTTP Request Tool:
- ✅ 工具本质上只需调用单个 API
- ✅ 希望在 URL、查询参数或请求正文中,为每个参数绑定
$fromAI()
经验法则:如果你发现自己想使用 $fromAI(),那么你可能应该改用 toolWorkflow,而不是 toolCode。
完整示例
一个可用于生产环境的计算器工具(非结构化,采用 JSON 字符串模式):
{
"parameters": {
"name": "calculate_car_loan",
"description": "Computes monthly car-loan payment using an annuity formula with residual/balloon. Call with a single JSON string. Example: {\"price\":439900,\"down_payment\":87980,\"interest_rate\":6.95,\"months\":36,\"residual_percent\":50,\"setup_fee\":695,\"monthly_admin_fee\":59}. Required: price, down_payment, interest_rate, months, residual_percent. Optional: setup_fee, monthly_admin_fee (default 0).",
"language": "javaScript",
"jsCode": "let params;\ntry {\n params = typeof query === 'string' ? JSON.parse(query) : query;\n} catch (e) {\n throw new Error('Invalid JSON: ' + e.message);\n}\n\nconst price = Number(params.price);\nconst down_payment = Number(params.down_payment);\nconst interest_rate = Number(params.interest_rate);\nconst months = Number(params.months);\nconst residual_percent= Number(params.residual_percent);\nconst setup_fee = Number(params.setup_fee ?? 0) || 0;\nconst monthly_admin_fee = Number(params.monthly_admin_fee ?? 0) || 0;\n\nif (!isFinite(price) || price <= 0) throw new Error('price must be > 0');\nif (down_payment < 0 || down_payment >= price) throw new Error('down_payment must be in [0, price)');\n\nconst principal = price - down_payment;\nconst residual = price * (residual_percent / 100);\nconst r = interest_rate / 100 / 12;\nconst growth = Math.pow(1 + r, months);\nconst base = r === 0\n ? (principal - residual) / months\n : (principal - residual / growth) * r / (1 - 1 / growth);\nconst monthly_payment = base + monthly_admin_fee;\n\nreturn JSON.stringify({\n monthly_payment_sek: Math.round(monthly_payment),\n loan_amount: Math.round(principal),\n residual_value_sek: Math.round(residual),\n total_cost_of_credit: Math.round(monthly_payment * months + residual + setup_fee - principal)\n});"
},
"type": "@n8n/n8n-nodes-langchain.toolCode",
"typeVersion": 1.3,
"name": "calculate_car_loan"
}
通过 ai_tool 连接类型,将其接入 AI Agent。
与其他技能的集成
n8n-code-javascript:Code 节点技能。大多数 JavaScript 模式(数组、map/filter、DateTime)都适用,但 I/O 契约不同。不要复制数据访问代码。
n8n-node-configuration:specifyInputSchema 是由 displayOptions 驱动的经典条件字段。使用 get_node({detail: "standard"}) 查看 @n8n/n8n-nodes-langchain.toolCode 中与架构相关的属性。
n8n-workflow-patterns:Code Tool 属于“带工具的 AI Agent”模式。Agent 通常有多个工具;Code Tool 是其中的“本地计算”选项。
n8n-validation-expert:上文列出的三种 Code Tool 错误都有明显特征——如果验证时出现“Wrong output type returned”,就知道要将输出从数组项切换为字符串。
快速参考清单
部署 Code Tool 前:
- 节点类型为
@n8n/n8n-nodes-langchain.toolCode(而不是nodes-base.code) - 工具名称具有描述性、以动词开头并采用 snake_case(例如
calculate_car_loan) - 描述说明何时使用该工具;如果是非结构化工具,还应提供 JSON 示例
- 输入从
query(JS)或_query(Python)读取 - 代码主体中没有
$fromAI() - 没有
$input/$json/$helpers——这些不在沙盒中 - 返回值为字符串(结构化输出请使用
JSON.stringify()) - 已通过
ai_tool连接接入 AI Agent - 已使用 LLM 将发送的确切类型输入进行测试(字符串中的 JSON,或经过架构验证的对象)
更多资源
- INPUT_SCHEMA.md — 深入了解结构化输入(DynamicStructuredTool)
- ERROR_PATTERNS.md — 完整的错误目录及其原因和修复方法
官方资料
- n8n Custom Code Tool 文档
- ToolCode 源码 — 沙盒契约
- LangChain 工具文档 — DynamicTool / DynamicStructuredTool
请记住:Code Tool 是套着 Code 节点界面的 LangChain 工具。契约是:输入字符串,输出字符串。其他一切都由此而来。