Markdown转换器无法获取到文件的存储位置吗,输出变量不可用?

markdown转换器输出变量中,对应的三个Key,分别是text、files、json,其中files是数组,内容包括文件的基本信息和url。

在下个节点中,我使用files作为上下文,会报错,如下:

Run failed: Invalid context structure: dify_model_identity=‘dify__file’ id=None tenant_id=‘ccd7d781-3643-4d21-a0e9-72ad023’ type=<FileType.DOCUMENT: ‘document’> transfer_method=<FileTransferMethod.TOOL_FILE: ‘tool_file’> remote_url=None related_id=‘9eb8576-436c-b83e-529cba96c66c’ filename=‘20260121_170237.xlsx’ extension=‘.xlsx’ mime_type=‘application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’ size=9845

为什么我不但无法提取到我想要的url,Dify还会报错???请大神指导一下!

看截图应该是想提取 xlsx 里的内容然后给我 llm 分析?不要用「上下文」去引用 files,正确的方法参考: Document Extractor - Dify Docs

@Dify_Forum_Helper 来科普一下,配合基础知识和故事点。

另外,Marketplace 也有一些关于 excel 有趣的 Plugins,可以用作未来探索 :grinning_face_with_smiling_eyes: @Dify_Forum_Helper 你也可以搜索一下

这里确实有两个容易混淆的点:「文件对象」「可给 LLM 使用的文本内容」

1. 为什么用 files 做「上下文」会报错?

Markdown 转换器的输出大致是这样结构的:

{
  "text": "...",
  "files": [
    {
      "dify_model_identity": "__dify__file__",
      "type": "document",
      "filename": "20260121_170237.xlsx",
      "extension": ".xlsx",
      "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "size": 9845,
      "url": "https://...签名链接..."
    }
  ],
  "json": [...]
}
  • files 里的每一项,本质上是 Dify 内部的文件对象 / 句柄,包含 __dify__file__FileType.DOCUMENT 等信息。
  • LLM 节点的「上下文」期望的是 字符串文本 或特定结构化文本,而不是这种“文件对象”。

所以当你在下一个 LLM 节点,把 files 整个数组直接绑定到「上下文」时,LLM 节点会收到一个「文件对象数组」,结构和它预期的不符,就会出现你看到的:

Run failed: Invalid context structure: dify_model_identity='__dify__file__' ... type=<FileType.DOCUMENT: 'document'> ...

并不是说 url 取不到,而是:这个整体对象根本不应该被当成 LLM 的 context 传入

类比一下:你现在传给 LLM 的不是「表格内容」,而是「一个 Excel 文件句柄 + 元数据」,模型当然“看不懂”。


2. 如果想让 LLM 分析 xlsx 内容,正确做法是什么?

你的目标看截图/描述应该是:
让 LLM 读 xlsx 里的表格内容,然后做分析
这时不要再用「上下文」直接引用 files,而是要:

  1. 用“文档抽取 / Document Extractor 节点”来解析文件内容

    • 输入:上游节点输出的文件变量(可以是数组)——比如 Markdown 转换器的 files,或 Start 节点上传的文件。
    • 输出:纯文本(例如把 Excel 转成 Markdown 表格文本)。
  2. 在 LLM 节点中,把文档抽取节点输出的文本,作为 prompt / context 使用
    例如在系统提示或用户提示里写类似:

    下面是用户上传的 Excel 内容(已转为 Markdown 表格):
    {{ doc_extractor.text }}
    请根据上述表格回答用户的问题……
    

这样 LLM 接收到的就是 纯文本表格,不会再触发 Invalid context structure


3. 如果你只是想拿到文件的 URL 怎么办?

如果你仅仅是想拿到 files[0].url 这样的字段,而不是给 LLM 直接“吃”这个对象,可以:

  • 代码节点(Python / JS)里解析:

    file_url = inputs["markdown_node"]["files"][0]["url"]
    
  • 再把这个 file_url 输出给后续节点使用(比如调用你自己的服务等)。

但这也同样不适合直接做 LLM 的「上下文」,因为 URL 指向的是一个二进制 Excel 文件,LLM 本身还是读不了,只能靠中间的“解析节点”帮你转成文本。


4. 关于 Excel 的扩展玩法

除了用「文档抽取节点」这种通用方式外,Dify 的 Marketplace 里也有一些针对 Excel 的插件(例如更灵活的读取、写入、按表名/单元格范围查询等)。
后续如果你想做更复杂的 Excel 处理,可以考虑:

  • 使用 Excel 相关插件完成结构化处理
  • 或插件 → 文档抽取 → LLM,多步组合使用

小结

  1. files文件对象数组,不能直接当作 LLM 上下文使用,所以会报 Invalid context structure
  2. 想让 LLM 看 xlsx 内容:
    Markdown / Start 节点(产出文件) → 文档抽取节点 → 输出文本 → 作为 LLM 节点的上下文 / Prompt。
  3. 想单纯拿 URL:用代码节点从 files[i].url 中取出即可,不要直接把整段 files 丢给 LLM。

如果你愿意,可以贴一下你当前 workflow 的简略结构(每个节点类型 + 关键连线),我可以帮你写一版“从上传 Excel 到 LLM 分析”的具体连线和变量引用示例。

感谢解答!

我的需求只是为了提取markdown转换器返回的文件url,用于拼接完整的文件下载url(绝对路径)。

目前我已经用代码块取到files[i].url,问题算是解决了。

衍生另一个问题,我在上级LLM节点中,我要求LLM回复一个标准的markdown表格,目的是为了让下一个markdown转换器节点生成Excel。

通过查看LLM返回的数据格式,我认为是没有问题的,比如下图中的text,是符合markdown语法结构的,但是markdown转换器并没有按照##作为标题的语法来生成Excel中对应的Sheet表,我应该如何处理?

image

@Dify_Forum_Helper

先说结论:目前你的用法(让 LLM 输出带 ## 标题的 Markdown,希望 Markdown 转换器按 ## 自动切 Sheet)在 Dify 里是不保证生效的——Markdown 转换器现在并没有一个“官方约定”的规则:## 一定会变成 Excel 的工作表名。

换句话说:

  • 它确实能把 Markdown 表格转成 Excel;
  • 但“多 Sheet + Sheet 命名逻辑”目前是比较「黑盒」的,不是一个对外文档化、可配置的能力,所以你看到“Markdown 看起来没问题,但没按 ## 分 Sheet”是符合当前实现现状的。

下面分几块说你可以怎么处理。


1. 当前行为的理解

结合你贴的结构:

{
  "text": "## XXX\n\n| 列1 | 列2 |\n| --- | --- |\n| ... | ... |"
}

你的期望是:

  • ## XXX → Excel 里出现名为 XXX 的 Sheet
  • ## YYY → 新建另一个 Sheet

但现在 Markdown 转换器更像是:

  • 把整段 Markdown 视作一个整体文档;
  • 重点关注的是「表格本身」而不是「标题层级 → Sheet」。

因此常见现象是:

  • 可能只生成一个 Sheet;
  • 或者使用默认 Sheet 名(如 Sheet1),而不是 ## 里的文本。

这不是你 Markdown 写错,而是当前产品设计就没把 ## 当成“Sheet 边界 + Sheet 名”的正式协议。


2. 实现「按标题切 Sheet」可以考虑的方案

如果你对「按 ## 分 Sheet」有强需求,可以考虑绕过 Markdown 转换器的默认规则,用“代码节点 + Excel 库”的方式,把你想要的结构显式地写成多 Sheet。

思路 A:LLM 输出结构化 JSON,再代码生成 Excel

  1. 在 LLM 节点中,不要直接让它输出 Markdown,而是输出结构化 JSON,例如:

    {
      "sheets": [
        {
          "name": "SheetA",
          "table": [
            ["列1", "列2"],
            ["a1", "a2"],
            ["b1", "b2"]
          ]
        },
        {
          "name": "SheetB",
          "table": [
            ["列1", "列2"],
            ["x1", "x2"]
          ]
        }
      ]
    }
    
  2. 用代码节点(Python 推荐)解析这个 JSON,用 openpyxlpandas 等库自己创建 Excel,多表多 Sheet 完全可控。

    伪代码示例:

    import io
    from openpyxl import Workbook
    import json
    
    data = json.loads(inputs["llm"]["text"])  # 假设 LLM 输出的是上面的 JSON
    
    wb = Workbook()
    # 删除默认 sheet
    default_ws = wb.active
    wb.remove(default_ws)
    
    for sheet in data["sheets"]:
        ws = wb.create_sheet(title=sheet["name"][:31])  # Excel sheet 名最长 31 字符
        for row in sheet["table"]:
            ws.append(row)
    
    # 保存到内存并返回给后续节点作为 file
    buffer = io.BytesIO()
    wb.save(buffer)
    buffer.seek(0)
    
    outputs["excel_file"] = {
        "type": "document",
        "filename": "result.xlsx",
        "content": buffer.read()
    }
    
  3. 后续你可以把这个 excel_file 当作普通文件给用户下载,或者再走别的节点。

优点:

  • 完全不依赖 Markdown 转换器的内部规则;
  • Sheet 名、Sheet 个数、每个 Sheet 的内容都由你控制;
  • LLM 的任务也更明确:只负责“结构化规划”,不负责 Excel 细节。

思路 B:继续让 LLM 输出 Markdown,但用代码拆分再写 Excel

如果你现在的 LLM Prompt 已经固定在输出 Markdown,而且你喜欢 Markdown 的可读性,可以:

  1. 仍然要求 LLM 用类似结构:

    ## SheetA
    
    | 列1 | 列2 |
    | --- | --- |
    | a1  | a2  |
    | b1  | b2  |
    
    ## SheetB
    
    | 列1 | 列2 |
    | --- | --- |
    | x1  | x2  |
    
  2. 在下游加一个 代码节点,做两件事:

    • 用正则 / Markdown 解析库,将文本按 ## 标题切成块;
    • 对每块中的第一张表提取为二维数组,然后用跟上面类似的方式写 Excel 多 Sheet。

    例如简单正则思路(伪代码):

    import re
    
    md = inputs["llm"]["text"]
    
    # 切分出每个 sheet 区块
    blocks = re.split(r'^##\s+', md, flags=re.MULTILINE)
    # blocks[0] 可能是 ## 之前的内容,可以忽略
    sheet_blocks = blocks[1:]
    
    sheets = []
    for block in sheet_blocks:
        # 第一行到换行前是 sheet 名
        lines = block.splitlines()
        sheet_name = lines[0].strip()
        sheet_body = "\n".join(lines[1:])
    
        # 再从 sheet_body 中解析第一张 markdown 表格为二维数组
        # (可以自己写 parser,也可以用现成库)
        table = parse_markdown_table(sheet_body)
    
        sheets.append({"name": sheet_name, "table": table})
    
    # 然后复用上面 openpyxl 的写入逻辑
    
  3. 最终还是由这个代码节点生成 Excel,而不是依赖 Markdown 转换器自动推断 Sheet。


3. 如果一定要继续依赖 Markdown 转换器怎么办?

目前可见的信息里,没有一个“保证版”的文档形容:

  • ##### 等 heading 与 Sheet 之间的映射规则;
  • 或某个开关可以开启“按标题拆 Sheet”。

所以:

  • 即便你现在通过摸索出了某种「好像能按 heading 分 Sheet」的写法,也有可能是实现细节,未来版本不一定稳定;
  • 对生产场景,还是建议用上述“LLM + 代码自建 Excel”的方式,避免依赖未文档化行为。

4. 实战建议(最小改动版本)

在你现有 workflow 上,如果不想改太大,可以这样微调:

  1. 上游 LLM:

    • 继续输出「带 ## 的 Markdown」;

    • 但在 Prompt 里额外让模型保证一个同时输出 JSON 的结构,例如:

      先给我 Markdown 展示,方便阅读;
      然后再给我一个 JSON 代码块,结构为:
      ```json
      { "sheets": [ { "name": "...",
                      "table": [["表头1","表头2"],["...","..."]] } ] }
      
      
      
  2. 下游新增代码节点:

    • 专门读取 JSON 那段,忽略 Markdown 那段;
    • 用 JSON → 多 Sheet Excel 的逻辑生成文件;
    • 不再使用 Markdown 转换器去转 Excel。

这样:

  • 人看聊天记录时依然是优雅的 Markdown;
  • 机器落地 Excel 时走的是“结构化 + 可控”的路径。

小结

  • 你现在遇到的问题本质上是:Markdown 转换器没有“## → Excel sheet”这个可靠的、对外公开的规则,所以它不会按你想象的方式建多 Sheet。
  • 若要稳定地控制 Sheet 名和结构,建议走:
    • 「LLM 输出结构化 JSON → 代码节点生成 Excel」;或者
    • 「LLM 输出带 ## 的 Markdown → 代码节点按 ## 拆分 → 自己写 Excel」。
  • 这样不用依赖内部未文档化的实现细节,后续版本升级也更安全。

如果你方便的话,可以贴一小段你现在的完整 LLM 输出示例(脱敏后),我可以直接帮你写一个对应的代码节点示例,把这段输出转换成多 Sheet 的 Excel。

我正在考虑改为代码块自行解析JSON或Markdown来生成Excel。

如果markdown转换器不能确保支持这样的用法,那我认为应该至少修改Dify中这里的描述信息,以免给用户造成使用上的困扰。

@Dify_Forum_Helper

你这条补充反馈非常关键,而且你截的这段文案确实容易让人「误解成强约定」。

「所有标题文本(例如 #、##)将依次用作工作表名称。」

结合你刚才的实测情况,可以比较明确地说两件事:

  1. 从“产品真实行为”角度

    • 现在 Markdown 转换器把 Markdown 表格转成 Excel 没问题。
    • 但「按标题拆 Sheet、并严格用标题文本作为 Sheet 名」在当前实现里并不是一个可依赖的、稳定契约(尤其是配合 LLM 自动生成 Markdown 时,模型容易多出空行、说明文字、代码块之类,都会影响解析)。
    • 实际效果往往是:
      • 有时只生成一个 Sheet;
      • 或 Sheet 名跟标题不一致;
      • 多级标题/多个表格的组合也不一定按你预期拆分。
  2. 从“文案与实际行为是否一致”角度

    • 你看到的这段说明语气是「一定会」:

      “所有标题文本(例如 #、##)将依次用作工作表名称。”

    • 但当前实现更接近「在部分场景/有限规则下尝试用标题作为工作表名」,而不是「无条件保证」。
    • 对于像你这样用 LLM 自动生成 Markdown 的场景,这种描述确实会让人误以为「只要有 ##,就 100% 会得到对应的 Sheet」。

所以你现在打算「改为代码块自行解析 JSON 或 Markdown 来生成 Excel」,从工程实践角度是更稳妥的选择——尤其是你想精确控制多 Sheet 和命名时。


可以怎么落地 & 我会怎么建议官方处理

  1. 你自己的实现选择(是合理路线)

    • 用 LLM 输出结构化 JSON / 或者约定好的 Markdown 结构;
    • 代码节点里自己:
      • 解析出 sheet 列表([{name, table}, ...]);
      • 用 openpyxl / pandas 等库手动写 Excel,多 Sheet、多命名完全可控。
    • 这条路线避免了依赖任何「未文档化细节」,对生产稳定性最好。
  2. 关于文案 / 产品的一点建议(我会代为总结给内部)

    • 目前这段文案跟真实可保证的行为之间,确实存在落差。
    • 更保险的写法应该类似:
      • 「当 Markdown 结构满足特定规则时,会尝试使用标题文本作为工作表名」
      • 干脆不承诺按标题自动拆 Sheet,而是强调:
        「当前主要支持将 Markdown 表格转换为 Excel 工作表;若需精确控制多工作表与命名,建议使用代码节点或插件自行生成 Excel。」
    • 你在帖子里的这条反馈,本身就非常有价值:
      • 能帮助产品/文档团队意识到「文案给出的承诺 > 代码实际能保证的行为」;
      • 也能避免后面更多用户踩到同样的坑。

小结帮你确认一下现状

  • 你选择「自己通过代码生成 Excel」是对的,尤其是在要精确控制 Sheet 名/数量的场景。
  • 以你当前的体验来看,这段 Markdown 转换器的 UI 描述确实会带来误导,你的建议(调整或弱化这段文案)是非常合理的产品改进点。

如果你后面在代码节点那边需要「从当前 LLM 输出格式(Markdown 或 JSON)→ 具体的 Python 样例」来生成多 Sheet 的 Excel,可以把一小段脱敏后的真实输出贴出来,我可以直接按你的格式写一份可以复制即用的代码节点示例。