Youtu-Parsing模型ComfyUI插件开发指南:可视化文档解析流程搭建
Youtu-Parsing模型ComfyUI插件开发指南:可视化文档解析流程搭建
你是不是也遇到过这样的烦恼?手里有一堆PDF、图片格式的文档,想快速提取里面的文字、表格,甚至把图片和文字分开,手动操作起来又慢又容易出错。虽然市面上有一些现成的文档解析工具,但往往不够灵活,没法根据自己的需求定制流程。
如果你恰好又是ComfyUI的爱好者,喜欢那种通过拖拽节点就能搭建复杂AI工作流的直观方式,那么今天这个教程就是为你准备的。我们将一起动手,为强大的Youtu-Parsing模型开发一个ComfyUI自定义节点插件。这样一来,原本需要写代码调用的复杂文档解析能力,就能变成一个可以随意拖拽、连接的可视化节点,无论是图文分离、表格识别还是关键信息提取,都能在你的工作流里轻松完成。
这个教程的目标很明确:即使你不是专业的Python开发者,只要对ComfyUI有一定了解,就能跟着步骤,一步步打造出属于自己的文档解析工具节点。我们会从最基础的节点定义讲起,一直到如何与解析API对接,最后再编排一个实用的工作流示例。整个过程,就像搭积木一样简单有趣。
1. 环境准备与项目初始化
在开始敲代码之前,我们得先把“工地”准备好。这里不需要复杂的深度学习环境,重点在于ComfyUI的插件开发框架。
首先,确保你已经安装好了ComfyUI。如果还没装,可以去官方仓库按照说明进行安装,过程并不复杂。我们的插件将作为一个独立的文件夹,放置在ComfyUI的 custom_nodes 目录下。
接下来,我们创建插件的核心目录结构。在你的 custom_nodes 文件夹里,新建一个文件夹,名字可以起得直观一些,比如 comfyui-youtu-parsing。然后,在这个文件夹里创建以下几个必要的文件:
comfyui-youtu-parsing/
├── __init__.py
├── nodes.py
└── web
└── logo.png (可选,用于在节点列表中显示图标)
__init__.py:这是一个空文件,它的存在告诉Python,这个文件夹是一个可导入的包。nodes.py:这是我们插件的心脏,所有自定义节点的代码都将写在这里。web/logo.png:可选,放一个图标文件,可以让你的节点在ComfyUI的节点列表里更醒目。
现在,打开 nodes.py 文件,我们先来搭建一个最基础的骨架。导入ComfyUI开发所需的模块:
import torch
import numpy as np
import json
import requests
from PIL import Image
import io
import folder_paths
from comfy.sd import CLIP
from comfy.utils import common_upscale
import comfy.utils
import nodes
# 我们后续的节点类都将在这里定义
准备工作就绪,我们的“开发车间”已经搭建好了。接下来,就要开始设计这个解析节点的“蓝图”了。
2. 定义核心解析节点类
在ComfyUI里,一切功能都封装在节点(Node)中。我们要创建一个节点,它接收文档(图片或PDF路径),调用Youtu-Parsing服务,然后输出结构化的解析结果。
我们先来定义这个节点的类。在 nodes.py 文件中,添加如下代码:
class YoutuParsingNode:
"""
一个ComfyUI自定义节点,用于调用Youtu-Parsing模型解析文档。
支持图片和PDF文件,输出图文分离、表格结构化的JSON结果。
"""
# 这两个变量是ComfyUI识别节点的关键
@classmethod
def INPUT_TYPES(cls):
"""
定义节点的输入参数类型和UI显示。
"""
return {
"required": {
"document_path": ("STRING", {
"default": "",
"multiline": False,
"dynamicPrompts": False,
"placeholder": "输入文档的完整路径,如:C:/docs/report.pdf"
}),
"parse_mode": (["full", "text_only", "table_only", "layout_only"], {
"default": "full"
}),
},
"optional": {
"api_endpoint": ("STRING", {
"default": "https://your-youtu-parsing-service.com/v1/parse",
"multiline": False,
}),
"api_key": ("STRING", {
"default": "your-api-key-here",
"multiline": False,
}),
}
}
# 这个字符串会显示在节点上
RETURN_TYPES = ("STRING", "JSON")
# 这是输出在UI上的标签名
RETURN_NAMES = ("parsed_text", "structured_data")
# 节点在菜单中的分类
FUNCTION = "parse_document"
CATEGORY = "Youtu-Parsing"
# 节点的核心执行函数
def parse_document(self, document_path, parse_mode, api_endpoint="", api_key=""):
"""
调用Youtu-Parsing API解析文档。
参数:
document_path: 待解析文档的路径。
parse_mode: 解析模式,如全文、仅文本等。
api_endpoint: API服务地址。
api_key: API密钥。
返回:
解析后的纯文本和结构化JSON数据。
"""
# 1. 首先检查文件是否存在(简单示例)
import os
if not os.path.exists(document_path):
# 在实际开发中,这里可以添加更友好的错误处理或使用ComfyUI的文件加载器
print(f"[Youtu-Parsing] 警告:文件不存在 - {document_path}")
# 返回空结果占位
return ("", {})
# 2. 准备调用API(这里用伪代码展示逻辑)
# 实际调用需要根据Youtu-Parsing API的具体要求来构建请求
print(f"[Youtu-Parsing] 开始解析文档: {document_path}, 模式: {parse_mode}")
# 模拟一个API调用和响应处理的过程
try:
# 假设我们有一个函数来实际处理API调用
structured_result = self._call_parsing_api(document_path, parse_mode, api_endpoint, api_key)
# 从结构化结果中提取纯文本部分(示例)
extracted_text = self._extract_text_from_result(structured_result)
print(f"[Youtu-Parsing] 文档解析完成。")
return (extracted_text, structured_result)
except Exception as e:
print(f"[Youtu-Parsing] 解析过程中发生错误: {e}")
# 返回错误信息或空数据
return (f"解析失败: {str(e)}", {})
def _call_parsing_api(self, file_path, mode, endpoint, key):
"""
模拟调用解析API。
在实际开发中,这里需要替换为真实的HTTP请求代码。
"""
# 这是一个占位函数。你需要根据Youtu-Parsing API的实际文档来实现它。
# 通常包括:读取文件、构建请求头和数据、发送POST请求、处理响应。
print(f"[模拟API调用] 向 {endpoint} 发送 {file_path} 的解析请求,模式: {mode}")
# 返回一个模拟的结构化数据
mock_result = {
"document_info": {"filename": file_path.split("/")[-1], "mode": mode},
"layout": [{"type": "text", "bbox": [10, 20, 100, 50], "content": "这是一个标题"}],
"text_blocks": ["这是一个标题", "这是第一段正文..."],
"tables": [{"data": [["姓名", "年龄"], ["张三", "25"]]}],
"images": [{"bbox": [200, 300, 300, 400], "caption": "示例图片"}]
}
return mock_result
def _extract_text_from_result(self, structured_data):
"""
从结构化数据中拼接出纯文本。
"""
text_blocks = structured_data.get("text_blocks", [])
return "\n".join(text_blocks)
代码看起来有点长,但其实结构很清晰。INPUT_TYPES 定义了节点需要什么参数(比如文档路径、解析模式),FUNCTION 指定了当节点被执行时,该运行哪个函数。parse_document 方法就是我们的主逻辑,它负责组织整个解析流程。
现在,节点类定义好了,但我们还需要让ComfyUI知道它的存在。
3. 注册节点与对接真实API
定义好的类只是一个模板,我们需要把它“注册”到ComfyUI的系统里。在 nodes.py 文件的末尾,添加以下代码:
# 所有自定义节点都需要在这个字典里注册
NODE_CLASS_MAPPINGS = {
"YoutuParsingNode": YoutuParsingNode
}
# 节点在UI菜单中显示的名字
NODE_DISPLAY_NAME_MAPPINGS = {
"YoutuParsingNode": "Youtu Parsing (文档解析)"
}
这样,当你重启ComfyUI后,在节点搜索框里输入“Youtu Parsing”,应该就能找到我们刚刚创建的节点了,可以把它拖到画布上使用。
不过,我们之前的 _call_parsing_api 函数还是个“模拟器”。要让它真正工作,我们需要对接真实的Youtu-Parsing API。由于我无法得知该API的具体端点、请求格式和认证方式,这里提供一个通用的、基于 requests 库的实现框架,你需要根据官方文档填充细节:
def _call_parsing_api_real(self, file_path, mode, endpoint, key):
"""
真实调用Youtu-Parsing API的示例框架。
**请根据实际API文档修改此函数。**
"""
import requests
# 1. 读取文件内容
with open(file_path, 'rb') as f:
file_data = f.read()
# 2. 构建请求,根据API要求可能是multipart/form-data或JSON
files = {'document': (file_path, file_data)}
# 或者,如果API接受base64编码
# import base64
# payload = {
# 'image': base64.b64encode(file_data).decode('utf-8'),
# 'mode': mode
# }
headers = {
'Authorization': f'Bearer {key}', # 如果使用Bearer Token
# 'X-API-Key': key, # 或者其他认证头
}
params = {
'parse_mode': mode
}
try:
# 3. 发送POST请求
# 注意:是使用`files`参数还是`data`/`json`参数,取决于API
response = requests.post(
endpoint,
files=files,
headers=headers,
params=params,
timeout=30 # 设置超时
)
response.raise_for_status() # 如果状态码不是200,抛出异常
# 4. 解析响应
result = response.json()
return result
except requests.exceptions.RequestException as e:
print(f"[Youtu-Parsing] API请求失败: {e}")
if hasattr(e.response, 'text'):
print(f"错误响应: {e.response.text}")
raise # 将异常抛给上层处理
将 parse_document 方法中的 self._call_parsing_api 调用替换为 self._call_parsing_api_real,并确保你的 api_endpoint 和 api_key 输入正确,节点就应该能真正调用服务了。
一个功能完整的节点,除了核心逻辑,通常还需要一些“贴心”的辅助功能。
4. 完善节点功能与UI体验
为了让节点更好用,我们可以添加一些提升体验的功能。比如,直接从ComfyUI内置的文件选择器里选文件,而不是手动输入路径。
修改 INPUT_TYPES 中的 document_path 定义,我们可以尝试将其与ComfyUI的输入类型关联。不过,ComfyUI对文件路径输入没有直接的内置类型。一个更实用的方法是提供一个示例,并引导用户使用“Load Image”等节点获取路径,或者我们创建一个简单的文件选择字符串输入。
另一种思路是,我们的节点可以接收一个 IMAGE 类型的输入,直接处理ComfyUI工作流中的图片张量。我们来扩展一下节点,使其更灵活:
class YoutuParsingNodeAdvanced:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# 方式一:直接输入图像张量(来自ComfyUI其他节点)
"image_input": ("IMAGE",),
"parse_mode": (["full", "text_only", "table_only", "layout_only"], {"default": "full"}),
},
"optional": {
"api_endpoint": ("STRING", {"default": "https://your-api.com/parse"}),
"api_key": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING", "JSON", "IMAGE")
RETURN_NAMES = ("parsed_text", "structured_data", "annotated_image")
FUNCTION = "parse_image_tensor"
CATEGORY = "Youtu-Parsing"
OUTPUT_IS_LIST = (False, False, False)
def parse_image_tensor(self, image_input, parse_mode, api_endpoint="", api_key=""):
"""
接收IMAGE张量,将其转换为图片文件后调用解析API。
并返回一个标注了解析结果的示意图(模拟)。
"""
# 1. 将ComfyUI的IMAGE张量(形状为[N, H, W, C])转换为PIL Image
# 这里假设我们处理批次中的第一张图
import torch
from PIL import Image
import numpy as np
i = 255. * image_input[0].cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
# 2. 将PIL Image保存为临时文件,或直接转换为字节流用于API调用
import io
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
# 3. 调用解析API(这里省略具体实现,沿用之前的框架)
print(f"[Youtu-Parsing Adv] 解析图像张量,模式: {parse_mode}")
# structured_data = self._call_api_with_bytes(img_byte_arr, ...)
# 4. 模拟生成一个带标注的图像(例如,把原图加上一个边框作为示意)
# 这里简单地将原图转换为张量返回
annotated_tensor = image_input.clone()
# 模拟解析结果
mock_text = f"解析模式: {parse_mode}\n从图像中提取的模拟文本内容..."
mock_json = {"status": "success", "mode": parse_mode}
return (mock_text, mock_json, annotated_tensor)
这个高级版本节点可以直接连入图像处理流程。别忘了把它也注册到 NODE_CLASS_MAPPINGS 里。
节点功能丰富了,我们最终要把它用起来。来看看如何在工作流中编排它。
5. 编排可视化工作流示例
节点开发完成后,最大的乐趣就是用它来搭建工作流。我们设计一个简单的场景:加载一个包含文字和表格的文档图片,解析它,然后将提取出的结构化表格数据用文本形式展示出来。
在ComfyUI中,你可以通过右键菜单搜索并添加节点。一个可能的工作流编排如下(用文字描述节点连接):
- 加载图像:使用
Load Image节点,载入你的文档图片。 - 文档解析:添加我们刚创建的
Youtu Parsing (文档解析)节点。- 将
Load Image节点的IMAGE输出,连接到本节点的image_input输入。 - 在节点的参数面板中,选择
parse_mode为full(全面解析)。 - 填写正确的
api_endpoint和api_key。
- 将
- 结果预览与使用:
- 节点的
parsed_text输出可以连接到一个Preview Text节点,快速查看提取出的所有纯文本。 - 节点的
structured_data输出(JSON)可以连接到一个JSON Path或自定义的文本处理节点,来提取特定字段。例如,你可以写一个简单的Python节点,接收这个JSON,专门提取其中的表格数据,并格式化为Markdown表格字符串。
- 节点的
- 格式化输出:将处理后的表格字符串,再连接到一个
Preview Text节点,就能在工作流界面直接看到从文档中识别并提取出来的规整表格了。
这个工作流的好处是可视化。你可以清晰地看到“原始图片” -> “解析服务” -> “提取文本/表格”的数据流动过程。如果解析结果不理想,你可以很容易地回到上一步,调整解析模式,或者对原图进行预处理(比如使用 Image Scale 节点调整大小,或用 Image Filter 节点增强对比度),然后再尝试解析,整个过程无需修改代码。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐



所有评论(0)