
Craft Agents 表格数据呈现全解datatable / spreadsheet 块与 transform_data 工具实战指南【免费下载链接】craft-agents-oss项目地址: https://gitcode.com/GitHub_Trending/cr/craft-agents-oss本篇指南围绕 Craft Agents 中结构化表格数据的呈现体系展开如何在小数据量时内联datatable/spreadsheet块以及在 20 行以上的大数据集场景下如何用transform_data工具将数据落盘为 JSON 文件并通过src字段引用从而大幅降低 token 消耗。读完本文你将掌握列类型column type的完整语义、transform_data的路径约定与脚本参数约定、五类常见数据转换配方以及沙箱执行的安全边界与故障排查方法。三种表格形态与选型原则Craft Agents 支持三种方式展示表格数据选型依据是数据规模、交互需求与导出需求格式适用场景交互能力Markdown 表格小型简单数据3-4 行无datatable块查询结果、对比数据、用户可能需要排序/过滤的数据排序、过滤、分组、搜索spreadsheet块财务报告、导出数据、用户可能需要下载为 .xlsx 的数据排序、导出 Excel/CSV核心原则对于 20 行以上20 rows的数据集使用transform_data工具将数据写入 JSON 文件再通过src字段引用而不是把所有行内联进块里。这能显著降低 token 使用量与成本100 行数据内联约消耗 $1 token。渲染侧的实现在 MarkdownDatatableBlock.tsx 与 MarkdownSpreadsheetBlock.tsxExcel/CSV 导出能力由 table-export.ts 提供。内联表格小数据集对于少于 20 行的数据集直接将数据内联在 markdown 块中。Datatable 完整示例datatable { title: Top Users, columns: [ { key: name, label: Name, type: text }, { key: revenue, label: Revenue, type: currency }, { key: growth, label: Growth, type: percent }, { key: active, label: Active, type: boolean }, { key: tier, label: Tier, type: badge } ], rows: [ { name: Acme Corp, revenue: 4200000, growth: 0.152, active: true, tier: Enterprise }, { name: StartupCo, revenue: 85000, growth: -0.03, active: true, tier: Starter } ] } Spreadsheet 完整示例spreadsheet { filename: q4-revenue.xlsx, sheetName: Revenue, columns: [ { key: month, label: Month, type: text }, { key: revenue, label: Revenue, type: currency } ], rows: [ { month: October, revenue: 125000 }, { month: November, revenue: 142000 } ] } 列类型Column Types参考类型输入格式渲染为示例输入示例输出text任意字符串纯文本John DoeJohn Doenumber数字格式化数字15000001,500,000currency原始数字不要预格式化美元金额4200000$4,200,000percent小数0-1 范围带颜色百分比0.15215.2%绿色booleantrue/falseYes/NotrueYesdate日期字符串格式化日期2025-01-15Jan 15, 2025badge字符串彩色状态徽章ActiveActive徽章关键注意事项currency— 传原始数字而不是格式化字符串。4200000渲染为$4,200,000。percent— 以小数形式传入。0.152渲染为15.2%。正值为绿色负值为红色。boolean— 使用真实的true/false而不是字符串。这些语义由渲染层在 MarkdownDatatableBlock.tsx 中按type字段分发格式化脚本侧无需也不应在转换脚本里预格式化数值——把渲染交给列类型即可。文件支撑表格大数据集适用场景在以下情况应使用transform_data工具 src字段数据集有20 行以上——100 行内联约消耗 $1 token数据来自大型 API 响应或工具结果需要在展示前对原始数据过滤、重塑或聚合数据是需要解析的 CSV、TSV 或非结构化文本需要联合多个来源的数据。transform_data 工具详解transform_data在隔离的子进程中执行脚本读取输入文件并写出结构化 JSON 输出。参数参数类型说明languagepython3|node|bun脚本运行时scriptstring转换脚本源码inputFilesstring[]相对于会话目录的输入文件路径outputFilestring输出文件名写入会话data/目录路径约定输入文件相对于会话目录session directory。常见位置long_responses/tool_result_abc.txt— 保存的工具结果data/previous_output.json— 前一次 transform 的输出attachments/data.csv— 用户附带的文件输出文件相对于会话的data/目录。只需提供文件名如transactions.json。从源码看transform-data.ts 中的允许输入目录除会话目录外还包含 skills 目录用于读取 skill 资产这一能力在测试用例 transform-data.test.ts 中得到验证。脚本参数约定输入文件路径作为位置命令行参数传入最后一个参数始终是输出文件路径Pythonsys.argv[1:-1] 输入文件sys.argv[-1] 输出路径Node/Bunprocess.argv.slice(2, -1) 输入文件process.argv.at(-1) 输出路径。这个约定直接对应实现中的参数拼装逻辑transform-data.tsconst spawnArgs [...runtime.argsPrefix, tempScript, ...resolvedInputs, resolvedOutput];脚本被写入临时文件后按「临时脚本 → 输入文件们 → 输出文件」的顺序作为位置参数传给子进程因此输出路径永远位于参数列表末尾。输出 JSON 格式输出文件必须是有效 JSON支持以下三种格式之一完整格式推荐{ title: Recent Transactions, columns: [ { key: date, label: Date, type: date }, { key: amount, label: Amount, type: currency }, { key: status, label: Status, type: badge } ], rows: [ { date: 2025-01-15, amount: 250.00, status: Completed } ] }仅行格式Rows-only{ rows: [ { date: 2025-01-15, amount: 250.00, status: Completed } ] }或裸数组bare array[ { date: 2025-01-15, amount: 250.00, status: Completed } ]合并语义Merge semantics使用src时markdown 块中内联的columns和title优先于文件中的值。这允许你在块中定义列类型列宽、格式化类型同时从文件拉取行数据。渲染层对该优先级的实现见 MarkdownDatatableBlock.tsx当块内columns非空时采用块内定义否则回退到文件中的columns。引用输出文件transform_data成功后会返回输出文件的绝对路径。将该路径原样用作 datatable 或 spreadsheet 块的src值datatable { src: /absolute/path/returned/by/transform_data, title: Recent Transactions, columns: [ { key: date, label: Date, type: date }, { key: amount, label: Amount, type: currency }, { key: status, label: Status, type: badge } ] } 注意始终使用transform_data工具结果中返回的绝对路径不要手动构造相对路径。工具成功时的返回消息形如Output written to: 绝对路径并明确提示该路径可用作datatable、spreadsheet、html-preview、pdf-preview、image-preview块的src值transform-data.ts。完整工作流示例用户提问Show me all Stripe transactions from last month显示我上个月的所有 Stripe 交易。第 1 步通过 MCP 工具调用 Stripe API——获得大型 JSON 响应。第 2 步调用transform_data提取并结构化数据transform_data({ language: python3, script: import json, sys\nwith open(sys.argv[1]) as f:\n data json.load(f)\nrows [{\n id: t[id],\n date: t[created],\n amount: t[amount] / 100,\n status: t[status].title(),\n customer: t.get(customer_email, N/A)\n} for t in data.get(data, data.get(transactions, []))]\nwith open(sys.argv[-1], w) as f:\n json.dump({rows: rows}, f), inputFiles: [long_responses/stripe_result.txt], outputFile: transactions.json })第 3 步使用transform_data结果中返回的绝对路径输出 datatable 块datatable { src: /absolute/path/from/transform_data/result, title: Stripe Transactions — Last Month, columns: [ { key: id, label: ID, type: text }, { key: date, label: Date, type: date }, { key: amount, label: Amount, type: currency }, { key: status, label: Status, type: badge }, { key: customer, label: Customer, type: text } ] } 常见模式与配方JSON API 响应 → Datatable最常见的模式从 JSON API 响应中提取字段。Pythonimport json, sys with open(sys.argv[1]) as f: data json.load(f) # Handle common API response shapes items data.get(data, data.get(items, data.get(results, data))) if not isinstance(items, list): items [items] rows [{ id: item[id], name: item.get(name, ), created: item.get(created_at, ), } for item in items] with open(sys.argv[-1], w) as f: json.dump({rows: rows}, f)CSV/TSV → Spreadsheet解析 CSV 数据为 spreadsheet 以便导出Pythonimport csv, json, sys with open(sys.argv[1]) as f: reader csv.DictReader(f) rows list(reader) # Auto-detect columns from CSV headers columns [{key: k, label: k.replace(_, ).title(), type: text} for k in rows[0].keys()] if rows else [] with open(sys.argv[-1], w) as f: json.dump({columns: columns, rows: rows}, f)多源数据联合Multi-Source Join合并来自多个工具结果的数据Pythonimport json, sys # sys.argv[1:-1] are input files, sys.argv[-1] is output with open(sys.argv[1]) as f: users {u[id]: u for u in json.load(f)[data]} with open(sys.argv[2]) as f: orders json.load(f)[data] rows [{ order_id: o[id], customer: users.get(o[user_id], {}).get(name, Unknown), amount: o[total], status: o[status], } for o in orders] with open(sys.argv[-1], w) as f: json.dump({rows: rows}, f)调用方式注意inputFiles顺序与脚本中sys.argv[1]、sys.argv[2]一一对应transform_data({ language: python3, script: ..., inputFiles: [long_responses/users.txt, long_responses/orders.txt], outputFile: orders-with-customers.json })过滤与聚合Filtering Aggregation展示前先汇总数据Pythonimport json, sys from collections import defaultdict with open(sys.argv[1]) as f: data json.load(f) # Group by category and sum totals defaultdict(lambda: {count: 0, total: 0}) for item in data[transactions]: cat item.get(category, Other) totals[cat][count] 1 totals[cat][total] item[amount] rows [{category: k, count: v[count], total: v[total]} for k, v in sorted(totals.items(), keylambda x: -x[1][total])] with open(sys.argv[-1], w) as f: json.dump({rows: rows}, f)Node.js 替代方案当 Python 不可用或偏好 JavaScript 时Nodeconst fs require(fs); const data JSON.parse(fs.readFileSync(process.argv[2], utf-8)); const rows data.items.map(item ({ id: item.id, title: item.title, status: item.state, created: item.created_at, })); fs.writeFileSync(process.argv.at(-1), JSON.stringify({ rows }));安全与约束源码级验证文档声明的五条安全约束均可在源码中得到印证隔离子进程脚本运行在子进程中无法访问 API 密钥、凭据或敏感环境变量。环境变量清理逻辑在 sandbox-env.ts 的BLOCKED_ENV_VARS中维护实际屏蔽的变量包括ANTHROPIC_API_KEY、CLAUDE_CODE_OAUTH_TOKEN、AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKEN、GITHUB_TOKEN、GH_TOKEN、OPENAI_API_KEY、GOOGLE_API_KEY、STRIPE_SECRET_KEY、NPM_TOKEN与文档中的AWS_*等通配描述对应源码采用的是逐项显式枚举。30 秒超时超时脚本会被强制终止。实现上transform-data.ts、L116-L127并不依赖spawn()内置的timeout选项——那只发送可被捕获的 SIGTERM——而是手动计时后直接发送SIGKILL保证进程一定被杀掉。路径沙箱输入文件必须位于会话目录或 skills 目录内输出文件必须位于会话data/目录内路径穿越../会被阻断。校验函数isPathWithinDirectory/isPathWithinDirectoryForCreation定义在 path-security.tstransform-data.test.ts 中的测试覆盖了共享前缀的兄弟目录逃逸、符号链接逃逸含 skills 目录下的符号链接等攻击路径并确认合法子孙路径正常写入。无网络访问实践约定脚本继承宿主进程环境去掉密钥后但不应在脚本内发起网络调用——数据获取请使用 MCP 工具然后本地转换。运行时缓存重定向从源码看沙箱还会将TMPDIR/TMP/TEMP重定向到会话data/.tmpPython 场景额外重定向UV_CACHE_DIR、XDG_CACHE_HOME、PYTHONPYCACHEPREFIX到会话目录sandbox-env.ts使沙箱执行不依赖宿主 home 目录下的默认缓存位置。运行时解析机制language参数对应的可执行文件由 resolve-script-runtime.ts 按以下优先级解析环境变量覆盖CRAFT_UV/CRAFT_NODE/CRAFT_BUN→ 打包内置二进制bundled→ PATH 查找仅开发模式允许打包模式下 PATH 回退被禁用。Python 脚本实际通过uv run --python 3.12执行因此脚本应只依赖 Python 标准库json、csv 等无需任何pip install。最佳实践决策树Is the data 20 rows? → YES: Inline it directly in the datatable/spreadsheet block → NO: Use transform_data src field Is the data already structured JSON? → YES: Write a simple extraction script → NO: Use Pythons csv, json, or string parsing to structure it Does the user need to export/download? → YES: Use spreadsheet block (supports .xlsx export) → NO: Use datatable block (better sort/filter/group UX)命名约定输出文件描述性、kebab-case——stripe-transactions.json、monthly-revenue.json与上下文匹配——如果用户问的是 Q4 sales就命名为q4-sales.json脚本中的错误处理处理前始终验证输入数据存在对 JSON 解析使用try/exceptPython或try/catchNode尽可能写出部分结果——有部分数据好过直接报错保持脚本简洁——复杂逻辑在 30 秒超时内更难调试脚本编写技巧数据转换优先使用 Python——它是处理 JSON/CSV 最可靠的运行时保持脚本自包含——不要pip install或引入外部依赖使用json.dump默认序列化——不要在脚本里格式化数字交给列类型处理渲染日期输出 ISO 格式字符串YYYY-MM-DD——date列类型会处理格式化故障排查Script failed (exit code 1)检查错误输出中的语法错误或缺失的 import确认输入文件在指定路径存在确保脚本正确地从sys.argv/process.argv读取参数对应实现非零退出码会返回Script failed (exit code N):及截断的 stderr/stdout见 transform-data.ts。Output file was not created确保脚本写入的是sys.argv[-1]/process.argv.at(-1)最后一个参数检查json.dump/fs.writeFileSync是否成功完成验证输出是合法 JSON实现中在脚本成功退出后还会显式检查输出文件是否存在不存在则返回Script completed but output file was not created见 transform-data.ts。Input file not found输入路径是相对于会话目录的对照产生该文件的工具结果核对精确路径保存的工具结果使用long_responses/前缀用户上传的文件使用attachments/前缀表格中行为空或缺失验证 JSON 结构必须有rows键且值为数组或者是裸数组检查行键与列的key字段完全一致大小写敏感确保值类型匹配预期currency/percent需要数字而不是字符串表格一直显示 Loading...src路径必须是transform_data返回的绝对路径——不要使用相对路径确认文件确实由transform_data创建检查工具结果消息完整的官方文档位于 apps/electron/resources/docs/data-tables.mdtransform_data工具的对外描述与参数 schema 定义在 tool-defs.ts 与同文件的工具注册处第 561 行。【免费下载链接】craft-agents-oss项目地址: https://gitcode.com/GitHub_Trending/cr/craft-agents-oss创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考