AWSLab

Difyだけでパワポを作る【md_to_pptxで外部API不要】

スポンサーラベル
Difyだけでパワポを作る流れ。依頼文をLLMがPandocのスライド記法のMarkdownに変換し、md_to_pptxがローカルで.pptxにする。#が章扉、##が1枚のスライドになる AWS

当サイトはアフィリエイト広告を利用しています。

「議題を渡したらスライドの叩き台が出てくる」仕組みが欲しくなりました。

この用途ではAIでパワポを作るOSS「Presenton」をAWSに構築するのように専用のツールを立てる手もありますが、サーバが1台増えます。 すでに Dify があるなら、Dify の中だけで完結させたほうが運用は明らかに軽いはずです。

結論から言うとできました。 プラグイン1個で、外部APIも追加のサーバも要りません。

Difyだけでパワポを作る流れ。依頼文をLLMがPandocのスライド記法のMarkdownに変換し、md_to_pptxがローカルで.pptxにする。#が章扉、##が1枚のスライドになる

1. 変換はプラグインがローカルでやる

使うのは Dify マーケットプレイスの Markdown Exporterbowenliang123/md_exporter)です。マーケットプレイスで「md exporter」と検索すると出てきます。

このプラグインは Markdown を各種フォーマットへ変換します。md_to_pptx のほか、PDF・DOCX・XLSX・PNG など17種類の変換ツールが入っています。

重要なのは、変換がプラグインのコンテナの中で完結することです。

  • 外部のAPIを叩かない
  • APIキーの登録が要らない
  • 追加の課金が発生しない

資料の中身が社外に出ないので、社内利用を前提にすると、この点だけでも選ぶ理由になります。

md_to_pptx が受け取るパラメータは3つです。

パラメータ必須中身
md_text変換元の Markdown
pptx_template_file見た目のひな形にする .pptx
output_filename出力ファイル名(拡張子は不要)

2. Markdown なら何でもいいわけではない

ここがこの記事でいちばん伝えたい点です。

md_to_pptx が解釈するのは、Pandoc のスライドショー記法です。普通の Markdown を渡すと、全部が1枚のスライドに流し込まれます。

覚えるのは実質2つだけです。

# 導入

## いま何が起きているか

- 本文は必ず ## の下に書く
- ## の外に書いた文章は、どのスライドにも載らない

# が章の区切りで、セクション見出しのスライド(章扉)になります。## が1枚。 これさえ守れば形になります。

先頭には YAML のタイトルブロックを置きます。表紙になります。

---
title: 社内向け 生成AI入門
author:
---

2段組みと発表者ノートも書けます。コロンの数が違うので、写すときは注意してください。

::::: columns
::: column
左に置く内容
:::
::: column
右に置く内容
:::
:::::

::: notes
ここは投影されない。発表者ノートに入る
:::

表は普通の Markdown のテーブルがそのまま通ります。

3. LLM に記法を守らせる

人間が上の記法を覚える必要はありません。LLM に書かせます。

ただし何も指定しないと、LLM は親切心で前置きを付けたり、全体をコードフェンスで囲んだりします。囲みのバッククォートは、そのまま文字としてスライドに入ります。 そこでシステムプロンプトで縛ります。

あなたはプレゼン資料の構成作家です。
ユーザーの依頼から、PowerPoint に変換するための Markdown 原稿を出力してください。

## 出力の決まり

- Markdown だけを出力する。前置き・後書き・コードフェンスで囲むことは一切しない。
- 先頭に YAML のタイトルブロックを置く。
- # は章の区切り(セクション見出しのスライドになる)。
- ## が 1 枚のスライドになる。本文は必ず ## の下に書く。
- 1 枚あたりの箇条書きは 3〜5 行。1 行は 40 文字以内。
- 枚数の指定がなければ、表紙・章扉を除いて 6 枚前後にする。
- 出力言語はユーザーの入力言語に合わせる。

## 禁止事項

- コードフェンスで全体を囲まない(そのまま文字として PPTX に入ってしまう)
- ## の外に本文を書かない(どのスライドにも載らない)
- 画像の URL を勝手に作らない(存在しない URL は変換エラーになる)

「1行は40文字以内」は効きます。 指定しないと1枚に長文を詰め込み、スライドとしては読めないものが出てきます。

「画像のURLを作らない」も入れておいたほうが安全です。LLM は存在しない画像パスを平気で書き、変換がそこで失敗します。

4. チャットフローは4ノード

構成はこれだけです。

開始 → LLM(原稿を書く)→ ツール(md_to_pptx)→ 回答

ツールノードの md_text には、LLM の出力をそのまま渡します。

{{#llm.text#}}

回答ノードはファイルを返す必要があります。ツールノードの出力変数 files を参照します。

スライドを作成しました。

{{#pptx.files#}}

pptx の部分はツールノードのID(画面上の表示名ではありません)です。これを書き忘れると、テキストだけ返ってファイルが出てきません。

5. プロンプト欄を自動で埋めようとして画面を壊した

ここは寄り道ですが、同じことをする人がいそうなので残します。

長いシステムプロンプトを自動操作で打ち込もうとしたら、Dify の編集画面が真っ白になりました。

このコンポーネントのレンダリング中に予期しないエラーが発生しました。

ブラウザのコンソールに原因が出ていました。

Error: Minified React error #185
    at ...
    at rS.dispatchCommand (...)

React error #185 は「Maximum update depth exceeded」、つまり無限再描画です。 スタックの末尾が dispatchCommand、すなわち Dify のプロンプト欄が使っている Lexical エディタでした。

手で打つ分には起きないはずですが、合成した入力イベントを高速に流し込むと壊れます。 短い文字列でも再現しました。

そこで方針を変えて、画面ではなく DSL(YAML)でフローを組みました。

6. DSL で組んでインポートする

Dify のアプリは YAML で丸ごと定義できます。「作成」→「DSL ファイルをインポート」で新しいアプリになります。

肝心なのはツールノードの書き方です。プラグインの識別子を正確に書く必要があります。

- id: pptx
  type: custom
  data:
    type: tool
    title: PPTXに変換
    # provider_id は「プラグインID / プロバイダ名」をつなげた形
    provider_id: bowenliang123/md_exporter/md_exporter
    provider_type: builtin
    provider_name: bowenliang123/md_exporter/md_exporter
    tool_name: md_to_pptx
    tool_label: Markdown ⮕ PPTX
    tool_configurations: {}
    tool_parameters:
      md_text:
        type: mixed
        value: '{{#llm.text#}}'
      output_filename:
        type: mixed
        value: slides
    tool_node_version: '2'

識別子が分からないときは、サーバ側のDBに入っています。

# plugin_id と provider をつなげたものが provider_id になる
docker compose exec -T db_postgres psql -U postgres -d dify_plugin -A -F'|' \
  -c "select plugin_id, provider from tool_installations;"
plugin_id|provider
bowenliang123/md_exporter|md_exporter

DSL で組むほうが、結果的に速くて確実でした。 画面をクリックして回る必要がなく、同じものを何度でも作り直せます。ファイルとして手元に残るので、壊しても戻せます。

7. Claude 5 が呼べずに止まる

組み終えて実行したら、LLM ノードで落ちました。

PluginInvokeError: {"args":...

サーバ側のログに理由が出ていました。

AccessDeniedException: anthropic.claude-sonnet-5 is not available for this account.

Claude 5 系は推論プロファイル経由でしか呼べません。 私は最初「global. の接頭辞が付いていないせいだ」と考えました。プラグインの設定を疑い、ソースまで読みに行きました。

これは外れです。接頭辞を付けても、まったく同じエラーが返りました。

# 接頭辞ありで直接叩いてみる
aws bedrock-runtime converse --model-id global.anthropic.claude-sonnet-5 \
  --messages '[{"role":"user","content":[{"text":"hi"}]}]' \
  --inference-config '{"maxTokens":16}'
An error occurred (AccessDeniedException) when calling the Converse operation:
anthropic.claude-sonnet-5 is not available for this account.

AWS は接頭辞を外した基底モデル名でエラーを返します。 実際はモデルアクセスの権限が無いだけなのに、メッセージだけ見ると設定ミスに見えます。 ここで時間を溶かしました。

もうひとつ紛らわしいのが、一覧には出てくることです。

aws bedrock list-inference-profiles --max-results 200 \
  --query "inferenceProfileSummaries[?contains(inferenceProfileId,'claude')].[inferenceProfileId,status]" \
  --output text
global.anthropic.claude-sonnet-5                ACTIVE
jp.anthropic.claude-sonnet-4-5-20250929-v1:0    ACTIVE

ACTIVE は「そのプロファイルが存在する」だけで、「あなたが呼べる」ではありません。 呼べるかどうかは、実際に1回投げるのがいちばん早いです。

# 候補を順に叩いて、返事が返るものを探す
for M in jp.anthropic.claude-sonnet-4-5-20250929-v1:0 \
         global.anthropic.claude-opus-4-5-20251101-v1:0; do
  R=$(aws bedrock-runtime converse --model-id "$M" \
      --messages '[{"role":"user","content":[{"text":"say ok"}]}]' \
      --inference-config '{"maxTokens":16}' \
      --query 'output.message.content[0].text' --output text 2>&1 | tail -1)
  echo "$M => $R"
done

4.5系は通りました。そこで Claude 4.5 Sonnet に変更し、あわせて cross-regionjapan にしました。jp. のプロファイルを使うと、推論が東京・大阪の中だけで行われます。 社内資料を扱うなら、こちらのほうが説明しやすいはずです。

8. 実際に出てきたもの

「社内向けに『AWS上にDifyを自前構築するときの勘所』を説明するスライドを作ってください」の1文だけを投げました。

19枚の .pptx(約59KB)が返ってきました。表紙・章扉5枚・本文13枚という構成です。

中身は Python で確認できます。.pptx は ZIP なので、ライブラリを入れなくても読めます。

import zipfile, re

z = zipfile.ZipFile("slides.pptx")
# スライドは ppt/slides/slideN.xml に1枚ずつ入っている
slides = sorted((n for n in z.namelist() if re.match(r"ppt/slides/slide\d+\.xml$", n)),
                key=lambda s: int(re.search(r"(\d+)", s).group(1)))
print("枚数:", len(slides))

for n in slides:
    xml = z.read(n).decode("utf-8", "replace")
    # <a:t> が画面に出る文字列
    for t in re.findall(r"<a:t>(.*?)</a:t>", xml, re.S):
        print(" ", t)

指定した記法は、ちゃんと形になっていました。

  • が表として入っている(AWSサービス構成の3列表)
  • 2段組みが成立している(フロントエンド/バックエンド)
  • 章扉が独立したスライドになっている

箇条書きの文字数も指定どおりに収まっていました。プロンプトで縛った内容が、そのまま結果に出ます。

9. Presenton と、どちらを使うか

同じ「AIでパワポ」でも、性格がまったく違います。

md_to_pptx(Dify内)Presenton
追加のサーバ不要必要(EC2 1台)
追加コスト0円インスタンス代
見た目素朴。文字が主体テンプレートで作り込める
画像自動生成しないスライドごとに生成できる
向く用途社内の叩き台・議事の素材社外に出す資料

体裁を整えるのは人がやる前提なら、md_to_pptx で十分です。「構成を考えて枠を作る」ところが自動化できれば、あとは PowerPoint で直すほうが速いことも多いはずです。

見た目を寄せたいときは、pptx_template_file に自社のひな形を渡す手があります(本記事では未検証です)。

10. まとめ

Dify だけでパワポを作るときの要点です。

  • 使うのは Markdown Exporter プラグインの md_to_pptx。変換はコンテナ内で完結し、APIキーも外部通信も要らない
  • 受け付けるのは Pandoc のスライド記法。 普通の Markdown を渡すと1枚に潰れる
  • 覚えるのは実質2つ。# が章扉、## が1枚。本文は必ず ## の下に書く
  • LLM にはコードフェンスで囲ませない。囲むとバッククォートがスライドに載る
  • 「1行40文字以内」のような分量の指定は効く。入れないと読めないスライドになる
  • 回答ノードは {{#ノードID.files#}} を書く。書き忘れるとファイルが返らない
  • プロンプト欄への自動入力は避ける。 Lexical が無限再描画(React error #185)で落ちる
  • 構成はDSL(YAML)で組むほうが速いprovider_idtool_installations テーブルで確認できる
  • Bedrock の AccessDeniedException接頭辞を外した基底モデル名で返る。設定ミスに見えるが権限の問題
  • list-inference-profilesACTIVE存在の証明であって、呼べる証明ではない

一番の収穫は、「作文」と「体裁」を分けて考えると、必要な道具がぐっと小さくなると分かったことでした。きれいなスライドを自動生成しようとすると話が大きくなりますが、構成案を .pptx の形で受け取るだけなら、プラグイン1個で足ります。

AWSを効率的に学習する方法

私が効率的にAWSを学習するために実施した方法は以下の通りです。
 ①最初に書籍(ハンズオンができる)を購入、座学でAWSの基礎を学習
 ②AWS資格試験を取得ための学習

※私の場合は①と②を合わせて2か月でソリューションアーキテクトを取得できました。

①AWS基礎学習

最初に購入した書籍は「Amazon Web Services 基礎からのネットワーク&サーバー構築」です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

この本では、AWSの基本サービスを利用したハンズオンを通じて、AWSの基礎を学習することができます。

また、タイトル通りAWSのネットワークやインフラに関しても網羅しているため、もともとインフラ系の技術者ではない人たちにとっても分かりやすい内容だと思います。

とりあえずAWS上にサーバーを設定して開発を行うための準備までするには最良の一冊です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

②AWS資格取得

AWSの基礎をある程度学習することができたら、次はAWS資格を取得しましょう。まずはAWSソリューションアーキテクトを目指しましょう。

資格勉強のための問題集をひたすら解きながら、AWSの知識を積み重ねて習得していきましょう!

苦行ではありますがこの問題集を3周ほどすればAWS用語や構成に関しても習得できているはずです。

AWS認定ソリューションアーキテクト-アソシエイト問題集

独学だと手が止まってしまう場合は、ササエル のようなインフラエンジニア向けのオンラインスクールを使う手もあります。ネットワークやサーバーの分野に絞ったカリキュラムなので、業務でインフラを触る人が学び直すのにも使えます

あわせて読みたい

コメント

タイトルとURLをコピーしました