mirror of
https://github.com/langgenius/dify-docs.git
synced 2026-03-27 13:28:32 +07:00
* move files & renames * rename files and doc entries * sync develop plugin files * update group label translations * some cleanups * update configs * update links * add remote debug doc * delete redundant slashes and unnecessary notes * update ja and zh links --------- Co-authored-by: Riskey <riskey47@dify.ai>
550 lines
15 KiB
Plaintext
550 lines
15 KiB
Plaintext
---
|
||
title: '构建 Markdown 导出插件'
|
||
description: '学习如何创建一个将对话导出为不同文档格式的插件'
|
||
language: en
|
||
standard_title: Building a Markdown Exporter Plugin
|
||
---
|
||
|
||
<Note> ⚠️ 本文档由 AI 自动翻译。如有任何不准确之处,请参考[英文原版](/en/develop-plugin/dev-guides-and-walkthroughs/develop-md-exporter)。</Note>
|
||
|
||
## 你将构建什么
|
||
|
||
在本指南中,你将学习如何构建一个实用的 Dify 插件,将对话导出为流行的文档格式。完成后,你的插件将能够:
|
||
|
||
- 将 markdown 文本转换为 Word 文档 (.docx)
|
||
- 将对话导出为 PDF 文件
|
||
- 处理文件创建并保持正确的格式
|
||
- 为文档导出提供简洁的用户体验
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="所需时间" icon="clock">
|
||
15 分钟
|
||
</Card>
|
||
<Card title="前提条件" icon="list-check">
|
||
基本的 Python 知识和熟悉文档处理库
|
||
</Card>
|
||
</CardGroup>
|
||
|
||
## 步骤 1:设置你的环境
|
||
|
||
<Steps>
|
||
<Step title="安装 Dify CLI">
|
||
<Tabs>
|
||
<Tab title="Mac">
|
||
```bash
|
||
brew tap langgenius/dify
|
||
brew install dify
|
||
```
|
||
</Tab>
|
||
<Tab title="Linux">
|
||
从 [Dify GitHub releases 页面](https://github.com/langgenius/dify-plugin-daemon/releases)获取最新的 Dify CLI
|
||
|
||
```bash
|
||
# Download appropriate version
|
||
chmod +x dify-plugin-linux-amd64
|
||
mv dify-plugin-linux-amd64 dify
|
||
sudo mv dify /usr/local/bin/
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
验证安装:
|
||
```bash
|
||
dify version
|
||
```
|
||
</Step>
|
||
|
||
<Step title="创建插件项目">
|
||
初始化一个新的插件项目:
|
||
|
||
```bash
|
||
dify plugin init
|
||
```
|
||
|
||
按照提示操作:
|
||
- 名称:"md_exporter"
|
||
- 类型:"tool"
|
||
- 按提示完成其他详细信息
|
||
</Step>
|
||
</Steps>
|
||
|
||
## 步骤 2:定义插件清单
|
||
|
||
创建 `manifest.yaml` 文件来定义你的插件元数据:
|
||
|
||
```yaml
|
||
version: 0.0.4
|
||
type: plugin
|
||
author: your_username
|
||
label:
|
||
en_US: Markdown Exporter
|
||
zh_Hans: Markdown导出工具
|
||
created_at: "2025-09-30T00:00:00Z"
|
||
icon: icon.png
|
||
|
||
resource:
|
||
memory: 134217728 # 128MB
|
||
permission:
|
||
storage:
|
||
enabled: true # We need storage for temp files
|
||
|
||
plugins:
|
||
tools:
|
||
- word_export.yaml
|
||
- pdf_export.yaml
|
||
|
||
meta:
|
||
version: 0.0.1
|
||
arch:
|
||
- amd64
|
||
- arm64
|
||
runner:
|
||
language: python
|
||
version: 3.11
|
||
entrypoint: main
|
||
```
|
||
|
||
## 步骤 3:定义 Word 导出工具
|
||
|
||
创建一个 `word_export.yaml` 文件来定义 Word 文档导出工具:
|
||
|
||
```yaml
|
||
identity:
|
||
author: your_username
|
||
name: word_export
|
||
label:
|
||
en_US: Export to Word
|
||
zh_Hans: 导出为Word文档
|
||
description:
|
||
human:
|
||
en_US: Export conversation content to a Word document (.docx)
|
||
zh_Hans: 将对话内容导出为Word文档(.docx)
|
||
llm: >
|
||
A tool that converts markdown text to a Word document (.docx) format.
|
||
Use this tool when the user wants to save or export the conversation
|
||
content as a Word document. The input text should be in markdown format.
|
||
credential_schema: {} # No credentials needed
|
||
tool_schema:
|
||
markdown_content:
|
||
type: string
|
||
required: true
|
||
label:
|
||
en_US: Markdown Content
|
||
zh_Hans: Markdown内容
|
||
human_description:
|
||
en_US: The markdown content to convert to Word format
|
||
zh_Hans: 要转换为Word格式的Markdown内容
|
||
document_name:
|
||
type: string
|
||
required: false
|
||
label:
|
||
en_US: Document Name
|
||
zh_Hans: 文档名称
|
||
human_description:
|
||
en_US: Name for the exported document (without extension)
|
||
zh_Hans: 导出文档的名称(无需扩展名)
|
||
```
|
||
|
||
## 步骤 4:定义 PDF 导出工具
|
||
|
||
为 PDF 导出创建一个 `pdf_export.yaml` 文件:
|
||
|
||
```yaml
|
||
identity:
|
||
author: your_username
|
||
name: pdf_export
|
||
label:
|
||
en_US: Export to PDF
|
||
zh_Hans: 导出为PDF文档
|
||
description:
|
||
human:
|
||
en_US: Export conversation content to a PDF document
|
||
zh_Hans: 将对话内容导出为PDF文档
|
||
llm: >
|
||
A tool that converts markdown text to a PDF document.
|
||
Use this tool when the user wants to save or export the conversation
|
||
content as a PDF file. The input text should be in markdown format.
|
||
credential_schema: {} # No credentials needed
|
||
tool_schema:
|
||
markdown_content:
|
||
type: string
|
||
required: true
|
||
label:
|
||
en_US: Markdown Content
|
||
zh_Hans: Markdown内容
|
||
human_description:
|
||
en_US: The markdown content to convert to PDF format
|
||
zh_Hans: 要转换为PDF格式的Markdown内容
|
||
document_name:
|
||
type: string
|
||
required: false
|
||
label:
|
||
en_US: Document Name
|
||
zh_Hans: 文档名称
|
||
human_description:
|
||
en_US: Name for the exported document (without extension)
|
||
zh_Hans: 导出文档的名称(无需扩展名)
|
||
```
|
||
|
||
## 步骤 5:安装所需依赖
|
||
|
||
创建或更新 `requirements.txt` 并添加必要的库:
|
||
|
||
```text
|
||
python-docx>=0.8.11
|
||
markdown>=3.4.1
|
||
weasyprint>=59.0
|
||
beautifulsoup4>=4.12.2
|
||
```
|
||
|
||
## 步骤 6:实现 Word 导出功能
|
||
|
||
在 `utils/docx_utils.py` 中创建一个实用工具模块:
|
||
|
||
<CodeGroup>
|
||
```python utils/docx_utils.py
|
||
import os
|
||
import tempfile
|
||
import uuid
|
||
from docx import Document
|
||
from docx.shared import Pt
|
||
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
||
import markdown
|
||
from bs4 import BeautifulSoup
|
||
|
||
def convert_markdown_to_docx(markdown_text, document_name=None):
|
||
"""
|
||
Convert markdown text to a Word document and return the file path
|
||
"""
|
||
if not document_name:
|
||
document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
|
||
|
||
# Convert markdown to HTML
|
||
html = markdown.markdown(markdown_text)
|
||
soup = BeautifulSoup(html, 'html.parser')
|
||
|
||
# Create a new Word document
|
||
doc = Document()
|
||
|
||
# Process HTML elements and add to document
|
||
for element in soup.find_all(['h1', 'h2', 'h3', 'h4', 'p', 'ul', 'ol']):
|
||
if element.name == 'h1':
|
||
heading = doc.add_heading(element.text.strip(), level=1)
|
||
heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
||
elif element.name == 'h2':
|
||
doc.add_heading(element.text.strip(), level=2)
|
||
elif element.name == 'h3':
|
||
doc.add_heading(element.text.strip(), level=3)
|
||
elif element.name == 'h4':
|
||
doc.add_heading(element.text.strip(), level=4)
|
||
elif element.name == 'p':
|
||
paragraph = doc.add_paragraph(element.text.strip())
|
||
elif element.name in ('ul', 'ol'):
|
||
for li in element.find_all('li'):
|
||
doc.add_paragraph(li.text.strip(), style='ListBullet')
|
||
|
||
# Create temp directory if it doesn't exist
|
||
temp_dir = tempfile.gettempdir()
|
||
if not os.path.exists(temp_dir):
|
||
os.makedirs(temp_dir)
|
||
|
||
# Save the document
|
||
file_path = os.path.join(temp_dir, f"{document_name}.docx")
|
||
doc.save(file_path)
|
||
|
||
return file_path
|
||
```
|
||
</CodeGroup>
|
||
|
||
## 步骤 7:实现 PDF 导出功能
|
||
|
||
在 `utils/pdf_utils.py` 中创建一个实用工具模块:
|
||
|
||
<CodeGroup>
|
||
```python utils/pdf_utils.py
|
||
import os
|
||
import tempfile
|
||
import uuid
|
||
import markdown
|
||
from weasyprint import HTML, CSS
|
||
from weasyprint.text.fonts import FontConfiguration
|
||
|
||
def convert_markdown_to_pdf(markdown_text, document_name=None):
|
||
"""
|
||
Convert markdown text to a PDF document and return the file path
|
||
"""
|
||
if not document_name:
|
||
document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
|
||
|
||
# Convert markdown to HTML
|
||
html_content = markdown.markdown(markdown_text)
|
||
|
||
# Add basic styling
|
||
styled_html = f"""
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>{document_name}</title>
|
||
<style>
|
||
body {{ font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }}
|
||
h1 {{ text-align: center; color: #333; }}
|
||
h2, h3, h4 {{ color: #444; margin-top: 20px; }}
|
||
p {{ margin-bottom: 15px; }}
|
||
ul, ol {{ margin-left: 20px; }}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
{html_content}
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
# Create temp directory if it doesn't exist
|
||
temp_dir = tempfile.gettempdir()
|
||
if not os.path.exists(temp_dir):
|
||
os.makedirs(temp_dir)
|
||
|
||
# Output file path
|
||
file_path = os.path.join(temp_dir, f"{document_name}.pdf")
|
||
|
||
# Configure fonts
|
||
font_config = FontConfiguration()
|
||
|
||
# Render PDF
|
||
HTML(string=styled_html).write_pdf(
|
||
file_path,
|
||
stylesheets=[],
|
||
font_config=font_config
|
||
)
|
||
|
||
return file_path
|
||
```
|
||
</CodeGroup>
|
||
|
||
## 步骤 8:创建工具实现
|
||
|
||
首先,在 `tools/word_export.py` 中创建 Word 导出工具:
|
||
|
||
<CodeGroup>
|
||
```python tools/word_export.py
|
||
import os
|
||
import base64
|
||
from collections.abc import Generator
|
||
from typing import Any
|
||
from dify_plugin import Tool
|
||
from dify_plugin.entities.tool import ToolInvokeMessage
|
||
from utils.docx_utils import convert_markdown_to_docx
|
||
|
||
class WordExportTool(Tool):
|
||
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
|
||
# Extract parameters
|
||
markdown_content = tool_parameters.get("markdown_content", "")
|
||
document_name = tool_parameters.get("document_name", "exported_document")
|
||
|
||
if not markdown_content:
|
||
yield self.create_text_message("Error: No content provided for export.")
|
||
return
|
||
|
||
try:
|
||
# Convert markdown to Word
|
||
file_path = convert_markdown_to_docx(markdown_content, document_name)
|
||
|
||
# Read the file as binary
|
||
with open(file_path, 'rb') as file:
|
||
file_content = file.read()
|
||
|
||
# Encode as base64
|
||
file_base64 = base64.b64encode(file_content).decode('utf-8')
|
||
|
||
# Return success message and file
|
||
yield self.create_text_message(
|
||
f"Document exported successfully as Word (.docx) format."
|
||
)
|
||
|
||
yield self.create_file_message(
|
||
file_name=f"{document_name}.docx",
|
||
file_content=file_base64,
|
||
mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||
)
|
||
|
||
except Exception as e:
|
||
yield self.create_text_message(f"Error exporting to Word: {str(e)}")
|
||
return
|
||
```
|
||
</CodeGroup>
|
||
|
||
接下来,在 `tools/pdf_export.py` 中创建 PDF 导出工具:
|
||
|
||
<CodeGroup>
|
||
```python tools/pdf_export.py
|
||
import os
|
||
import base64
|
||
from collections.abc import Generator
|
||
from typing import Any
|
||
from dify_plugin import Tool
|
||
from dify_plugin.entities.tool import ToolInvokeMessage
|
||
from utils.pdf_utils import convert_markdown_to_pdf
|
||
|
||
class PDFExportTool(Tool):
|
||
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
|
||
# Extract parameters
|
||
markdown_content = tool_parameters.get("markdown_content", "")
|
||
document_name = tool_parameters.get("document_name", "exported_document")
|
||
|
||
if not markdown_content:
|
||
yield self.create_text_message("Error: No content provided for export.")
|
||
return
|
||
|
||
try:
|
||
# Convert markdown to PDF
|
||
file_path = convert_markdown_to_pdf(markdown_content, document_name)
|
||
|
||
# Read the file as binary
|
||
with open(file_path, 'rb') as file:
|
||
file_content = file.read()
|
||
|
||
# Encode as base64
|
||
file_base64 = base64.b64encode(file_content).decode('utf-8')
|
||
|
||
# Return success message and file
|
||
yield self.create_text_message(
|
||
f"Document exported successfully as PDF format."
|
||
)
|
||
|
||
yield self.create_file_message(
|
||
file_name=f"{document_name}.pdf",
|
||
file_content=file_base64,
|
||
mime_type="application/pdf"
|
||
)
|
||
|
||
except Exception as e:
|
||
yield self.create_text_message(f"Error exporting to PDF: {str(e)}")
|
||
return
|
||
```
|
||
</CodeGroup>
|
||
|
||
## 步骤 9:创建入口点
|
||
|
||
在项目根目录创建一个 `main.py` 文件:
|
||
|
||
<CodeGroup>
|
||
```python main.py
|
||
from dify_plugin import PluginRunner
|
||
from tools.word_export import WordExportTool
|
||
from tools.pdf_export import PDFExportTool
|
||
|
||
plugin = PluginRunner(
|
||
tools=[
|
||
WordExportTool(),
|
||
PDFExportTool(),
|
||
],
|
||
providers=[] # No credential providers needed
|
||
)
|
||
```
|
||
</CodeGroup>
|
||
|
||
## 步骤 10:测试你的插件
|
||
|
||
<Steps>
|
||
<Step title="设置你的调试环境">
|
||
首先,从模板创建你的 `.env` 文件:
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
使用你的 Dify 环境详细信息进行配置:
|
||
```
|
||
INSTALL_METHOD=remote
|
||
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
|
||
REMOTE_INSTALL_PORT=5003
|
||
REMOTE_INSTALL_KEY=your_debug_key
|
||
```
|
||
</Step>
|
||
|
||
<Step title="安装依赖">
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
</Step>
|
||
|
||
<Step title="以调试模式启动插件">
|
||
```bash
|
||
python -m main
|
||
```
|
||
</Step>
|
||
</Steps>
|
||
|
||
## 步骤 11:打包分发
|
||
|
||
当你准备好分享你的插件时:
|
||
|
||
```bash
|
||
dify plugin package ./
|
||
```
|
||
|
||
这将创建一个 `plugin.difypkg` 文件用于分发。
|
||
|
||
## 创意用例
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="报告生成" icon="file-lines">
|
||
使用此插件将分析摘要转换为专业报告提供给客户
|
||
</Card>
|
||
<Card title="会话文档" icon="book">
|
||
将辅导或咨询会话笔记导出为格式化文档
|
||
</Card>
|
||
</CardGroup>
|
||
|
||
## 超越基础
|
||
|
||
以下是一些扩展此插件的有趣方式:
|
||
|
||
- **自定义模板**:添加公司品牌或个性化样式
|
||
- **多格式支持**:扩展导出为 HTML、Markdown 或其他格式
|
||
- **图像处理**:处理并包含对话中的图像
|
||
- **表格支持**:为数据表格实现正确的格式化
|
||
- **协作编辑**:添加与 Google Docs 或类似平台的集成
|
||
|
||
<Accordion title="技术见解">
|
||
文档转换的核心挑战是保持格式和结构。此插件使用的方法首先将 markdown 转换为 HTML(一种中间格式),然后将该 HTML 处理为目标格式。
|
||
|
||
这个两步过程提供了灵活性——你可以通过简单地添加与 HTML 表示配合工作的新输出模块来扩展它以支持其他格式。
|
||
|
||
对于 PDF 生成,选择 WeasyPrint 是因为它提供具有 CSS 支持的高质量 PDF 渲染。对于 Word 文档,python-docx 提供对文档结构的精细控制。
|
||
</Accordion>
|
||
|
||
## 总结
|
||
|
||
你已经构建了一个实用的插件,通过使用户能够以专业文档格式导出对话,为 Dify 平台增添了真正的价值。此功能弥合了 AI 对话与传统文档工作流之间的差距。
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="文档" icon="book">
|
||
用英语 (en_US) 编写你的 README.md,描述功能、设置和使用示例
|
||
</Card>
|
||
<Card title="本地化" icon="language">
|
||
为其他语言创建额外的 README 文件,如 `readme/README_zh_Hans.md`
|
||
</Card>
|
||
</CardGroup>
|
||
|
||
<CheckList>
|
||
<CheckListItem id="privacy">
|
||
如果发布你的插件,请添加隐私政策 (PRIVACY.md)
|
||
</CheckListItem>
|
||
<CheckListItem id="documentation">
|
||
在文档中包含全面的示例
|
||
</CheckListItem>
|
||
<CheckListItem id="testing">
|
||
使用各种文档大小和格式进行彻底测试
|
||
</CheckListItem>
|
||
</CheckList>
|
||
|
||
{/*
|
||
Contributing Section
|
||
DO NOT edit this section!
|
||
It will be automatically generated by the script.
|
||
*/}
|
||
|
||
---
|
||
|
||
[编辑此页面](https://github.com/langgenius/dify-docs/edit/main/en/develop-plugin/dev-guides-and-walkthroughs/develop-md-exporter.mdx) | [报告问题](https://github.com/langgenius/dify-docs/issues/new?template=docs.yml) |