はじめに
「会社のフォーマットでPowerPoint資料を作るのに時間がかかる」「AIに資料を作らせたいけど、会社のテンプレートが適用されない」という課題を抱えていませんか?
本記事では、既存のPowerPointフォーマット(テンプレート)に合わせて、AIが資料を自動生成する仕組みを3つのアプローチで解説します。
graph LR
A[会社のPPTXテンプレート] --> B[AIによる分析]
B --> C[コンテンツ生成]
C --> D[フォーマット適用]
D --> E[完成したPPTX]
style A fill:#4285f4,color:#fff
style E fill:#34a853,color:#fff
3つのアプローチの概要
| アプローチ | 難易度 | コスト | カスタマイズ性 | セキュリティ |
|---|
| Copilot for M365 | 低 | 高(ライセンス費用) | 中 | 高(Microsoft管理) |
| Python + python-pptx | 中〜高 | 低〜中(API費用のみ) | 高 | 要設計 |
| Power Automate | 低〜中 | 中 | 中 | 高(Microsoft管理) |
graph TD
A[どのアプローチを選ぶ?] --> B{既にM365 Copilot
ライセンスがある?}
B -->|Yes| C[Copilot for M365
最速で導入可能]
B -->|No| D{カスタマイズ性
を重視する?}
D -->|Yes| E[Python + python-pptx
最大の自由度]
D -->|No| F{ローコードで
実装したい?}
F -->|Yes| G[Power Automate
ノーコードで構築]
F -->|No| E
style C fill:#4285f4,color:#fff
style E fill:#f9ab00,color:#000
style G fill:#34a853,color:#fff
アプローチ1: Copilot for Microsoft 365
概要
Microsoft公式のAI機能を使って、既存のテンプレートに沿った資料を生成します。最も導入が簡単で、エンタープライズ環境に最適です。
graph LR
A[ユーザー] --> B[Copilotに指示]
B --> C[テンプレート選択]
C --> D[AIがコンテンツ生成]
D --> E[スライド自動作成]
E --> F[完成したPPTX]
subgraph Microsoft 365
B
C
D
E
end
前提条件
| 項目 | 要件 |
|---|
| ライセンス | Microsoft 365 E3/E5 + Copilot for Microsoft 365 アドオン |
| 費用 | 約4,500円/ユーザー/月(2026年1月時点) |
| テンプレート形式 | .potx または .pptx |
| 対応言語 | 日本語対応済み |
実装手順
ステップ1: 会社テンプレートの準備
まず、会社の標準テンプレートをPowerPointのテンプレート形式で保存します。
1. 既存のPowerPointファイル(.pptx)を開く
2. 「ファイル」→「名前を付けて保存」
3. ファイル形式を「PowerPoint テンプレート (*.potx)」に変更
4. SharePointの共有ライブラリに保存
重要なポイント:
graph TD
A[テンプレートに含めるべき要素] --> B[スライドマスター]
A --> C[カラーパレット]
A --> D[フォント設定]
A --> E[ロゴ・背景画像]
A --> F[レイアウトバリエーション]
B --> B1[タイトルスライド]
B --> B2[コンテンツスライド]
B --> B3[セクション区切り]
B --> B4[比較レイアウト]
B --> B5[画像付きレイアウト]
ステップ2: ブランドキットの設定(オプション)
Microsoft 365管理センターでブランドアセットを登録すると、Copilotが自動的に適用します。
1
2
3
4
5
6
7
8
9
10
| # SharePoint管理センター → ブランドセンター
ブランドアセット設定:
- ロゴ: company-logo.png
- カラーパレット:
プライマリ: "#0078D4"
セカンダリ: "#50E6FF"
アクセント: "#FFB900"
- フォント:
日本語: "游ゴシック"
英語: "Segoe UI"
|
ステップ3: Copilotでスライド生成
PowerPointを開き、Copilotを使って資料を生成します。
方法A: テンプレートから新規作成
1. PowerPointで「新規作成」
2. 会社テンプレートを選択
3. 「Copilot」パネルを開く(右上のCopilotアイコン)
4. プロンプトを入力
効果的なプロンプト例:
【基本】
「クラウド移行プロジェクトの進捗報告資料を10枚で作成してください。
現状、課題、解決策、スケジュール、次のステップを含めてください。」
【詳細指定】
「以下の構成で営業提案資料を作成してください:
1. 表紙
2. 会社概要(1枚)
3. 課題の整理(2枚)
4. ソリューション概要(3枚)
5. 導入事例(2枚)
6. 価格・プラン(1枚)
7. 次のステップ(1枚)
各スライドには図やグラフを含めてください。」
方法B: 既存ドキュメントからスライド生成
1. Copilotパネルで「ファイルからプレゼンテーションを作成」を選択
2. Word文書、PDF、またはテキストファイルを指定
3. Copilotが内容を分析し、テンプレートに沿ったスライドを生成
graph LR
A[Word文書] --> B[Copilot分析]
B --> C[構造化]
C --> D[スライド生成]
D --> E[テンプレート適用]
E --> F[完成PPTX]
G[PDF] --> B
H[メール] --> B
I[Teams会議メモ] --> B
ステップ4: 生成後の微調整
Copilotで生成したスライドは、追加のプロンプトで調整できます。
【レイアウト変更】
「3枚目のスライドを2カラムレイアウトに変更してください」
【内容の追加】
「各スライドに話者ノートを追加してください」
【デザイン調整】
「グラフをすべて棒グラフから円グラフに変更してください」
【アニメーション】
「各スライドにフェードインのアニメーションを追加してください」
メリット・デメリット
メリット:
- 追加の開発が不要、すぐに使い始められる
- Microsoft 365との完全な統合
- セキュリティとコンプライアンスが担保される
- 日本語対応が優秀
- 継続的な機能アップデート
デメリット:
- ライセンス費用が高い(月額約4,500円/ユーザー)
- カスタマイズに限界がある
- 複雑なテンプレートロジックには対応できない
- オフライン環境では使用不可
コスト試算
【100名の組織の場合】
Copilot for M365: 4,500円 × 100名 × 12ヶ月 = 5,400,000円/年
【削減される工数】
資料作成時間: 平均2時間/件 → 0.5時間/件
月間資料数: 50件
削減時間: 1.5時間 × 50件 × 12ヶ月 = 900時間/年
人件費換算: 900時間 × 5,000円 = 4,500,000円/年
【ROI】
投資: 5,400,000円
効果: 4,500,000円 + 品質向上効果
→ 品質向上を考慮すると1-2年で投資回収可能
アプローチ2: Python + python-pptx + LLM API
概要
Pythonのpython-pptxライブラリを使って既存テンプレートを解析し、Claude APIやOpenAI APIでコンテンツを生成する方法です。最大の柔軟性とカスタマイズ性を提供します。
graph TD
A[既存テンプレート.pptx] --> B[python-pptxで解析]
B --> C[スライドマスター抽出]
B --> D[レイアウト情報抽出]
B --> E[スタイル情報抽出]
F[ユーザー入力] --> G[LLM API]
G --> H[コンテンツ生成]
C --> I[新規PPTX生成]
D --> I
E --> I
H --> I
I --> J[完成したPPTX]
style G fill:#f9ab00,color:#000
前提条件
| 項目 | 要件 |
|---|
| Python | 3.9以上 |
| 主要ライブラリ | python-pptx, anthropic(またはopenai) |
| LLM API | Claude API または OpenAI API |
| 費用 | API使用量に応じた従量課金 |
実装手順
ステップ1: 環境セットアップ
1
2
3
4
5
6
| # 仮想環境の作成
python -m venv pptx-generator
source pptx-generator/bin/activate # Windowsの場合: pptx-generator\Scripts\activate
# 必要なパッケージのインストール
pip install python-pptx anthropic python-dotenv Pillow
|
requirements.txt:
python-pptx>=0.6.21
anthropic>=0.18.0
python-dotenv>=1.0.0
Pillow>=10.0.0
ステップ2: テンプレート解析スクリプト
まず、既存テンプレートの構造を解析するスクリプトを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
| # template_analyzer.py
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.shapes import MSO_SHAPE_TYPE
import json
class TemplateAnalyzer:
"""既存のPowerPointテンプレートを解析するクラス"""
def __init__(self, template_path: str):
self.prs = Presentation(template_path)
self.template_info = {}
def analyze(self) -> dict:
"""テンプレート全体を解析"""
self.template_info = {
"slide_width": self.prs.slide_width,
"slide_height": self.prs.slide_height,
"slide_masters": self._analyze_slide_masters(),
"slide_layouts": self._analyze_slide_layouts(),
"color_scheme": self._extract_color_scheme(),
"fonts": self._extract_fonts()
}
return self.template_info
def _analyze_slide_masters(self) -> list:
"""スライドマスターを解析"""
masters = []
for master in self.prs.slide_masters:
master_info = {
"name": master.name if hasattr(master, 'name') else "Default",
"placeholders": []
}
for shape in master.placeholders:
master_info["placeholders"].append({
"idx": shape.placeholder_format.idx,
"type": str(shape.placeholder_format.type),
"left": shape.left,
"top": shape.top,
"width": shape.width,
"height": shape.height
})
masters.append(master_info)
return masters
def _analyze_slide_layouts(self) -> list:
"""スライドレイアウトを解析"""
layouts = []
for master in self.prs.slide_masters:
for layout in master.slide_layouts:
layout_info = {
"name": layout.name,
"placeholders": []
}
for shape in layout.placeholders:
layout_info["placeholders"].append({
"idx": shape.placeholder_format.idx,
"type": str(shape.placeholder_format.type),
"left": shape.left,
"top": shape.top,
"width": shape.width,
"height": shape.height
})
layouts.append(layout_info)
return layouts
def _extract_color_scheme(self) -> dict:
"""カラースキームを抽出"""
theme = self.prs.slide_masters[0].theme_color_scheme
colors = {}
# テーマカラーの抽出(利用可能な場合)
return colors
def _extract_fonts(self) -> list:
"""使用されているフォントを抽出"""
fonts = set()
for slide in self.prs.slides:
for shape in slide.shapes:
if shape.has_text_frame:
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs:
if run.font.name:
fonts.add(run.font.name)
return list(fonts)
def get_layout_by_name(self, name: str):
"""名前でレイアウトを取得"""
for master in self.prs.slide_masters:
for layout in master.slide_layouts:
if layout.name == name:
return layout
return None
def print_layout_names(self):
"""利用可能なレイアウト名を表示"""
print("利用可能なレイアウト:")
for master in self.prs.slide_masters:
for layout in master.slide_layouts:
print(f" - {layout.name}")
if __name__ == "__main__":
# 使用例
analyzer = TemplateAnalyzer("company_template.pptx")
info = analyzer.analyze()
print("\n=== テンプレート解析結果 ===")
analyzer.print_layout_names()
# JSON形式で保存
with open("template_info.json", "w", encoding="utf-8") as f:
json.dump(info, f, ensure_ascii=False, indent=2, default=str)
print("\nテンプレート情報を template_info.json に保存しました")
|
ステップ3: LLMによるコンテンツ生成
Claude APIを使ってスライドコンテンツを生成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| # content_generator.py
import anthropic
from typing import List, Dict
import json
class SlideContentGenerator:
"""LLMを使ってスライドコンテンツを生成するクラス"""
def __init__(self, api_key: str):
self.client = anthropic.Anthropic(api_key=api_key)
def generate_presentation_structure(
self,
topic: str,
num_slides: int,
presentation_type: str = "提案資料"
) -> List[Dict]:
"""プレゼンテーションの構造とコンテンツを生成"""
prompt = f"""
あなたはプレゼンテーション資料の専門家です。
以下の条件で{num_slides}枚のスライド構成を作成してください。
【トピック】
{topic}
【資料タイプ】
{presentation_type}
【出力形式】
以下のJSON形式で出力してください。各スライドには適切なレイアウトタイプを指定してください。
```json
{{
"title": "プレゼンテーションのタイトル",
"slides": [
{{
"slide_number": 1,
"layout_type": "title",
"title": "スライドタイトル",
"subtitle": "サブタイトル(任意)",
"content": null
}},
{{
"slide_number": 2,
"layout_type": "content",
"title": "スライドタイトル",
"subtitle": null,
"content": [
"箇条書き項目1",
"箇条書き項目2",
"箇条書き項目3"
]
}},
{{
"slide_number": 3,
"layout_type": "two_column",
"title": "比較スライド",
"left_column": {{
"heading": "現状",
"points": ["項目1", "項目2"]
}},
"right_column": {{
"heading": "改善後",
"points": ["項目1", "項目2"]
}}
}}
]
}}
|
【レイアウトタイプの種類】
- title: タイトルスライド
- content: 箇条書きコンテンツ
- two_column: 2カラム比較
- section: セクション区切り
- image_content: 画像付きコンテンツ
- chart: グラフ用スライド
各スライドの内容は具体的で、{presentation_type}として説得力のあるものにしてください。
JSONのみを出力し、他の説明は不要です。
"""
message = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[
{"role": "user", "content": prompt}
]
)
# レスポンスからJSONを抽出
response_text = message.content[0].text
# JSONブロックを抽出
if "```json" in response_text:
json_str = response_text.split("```json")[1].split("```")[0]
elif "```" in response_text:
json_str = response_text.split("```")[1].split("```")[0]
else:
json_str = response_text
return json.loads(json_str)
def enhance_slide_content(self, slide: Dict) -> Dict:
"""個別スライドのコンテンツを詳細化"""
prompt = f"""
以下のスライド情報をより詳細で説得力のある内容に拡充してください。
ただし、1スライドに収まる分量を維持してください。
現在のスライド情報:
{json.dumps(slide, ensure_ascii=False, indent=2)}
同じJSON形式で、内容を拡充して返してください。
JSONのみを出力してください。
"""
message = self.client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
response_text = message.content[0].text
if "```json" in response_text:
json_str = response_text.split("```json")[1].split("```")[0]
elif "```" in response_text:
json_str = response_text.split("```")[1].split("```")[0]
else:
json_str = response_text
return json.loads(json_str)
if name == “main”:
import os
from dotenv import load_dotenv
load_dotenv()
generator = SlideContentGenerator(os.getenv("ANTHROPIC_API_KEY"))
# テスト生成
result = generator.generate_presentation_structure(
topic="クラウド移行プロジェクトの提案",
num_slides=8,
presentation_type="経営層向け提案資料"
)
print(json.dumps(result, ensure_ascii=False, indent=2))
#### ステップ4: PPTX生成スクリプト
テンプレートとLLM生成コンテンツを組み合わせてPPTXを生成します。
```python
# pptx_generator.py
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from typing import Dict, List, Optional
import os
class PPTXGenerator:
"""テンプレートベースでPowerPointを生成するクラス"""
# レイアウト名とインデックスのマッピング(テンプレートに応じて調整)
LAYOUT_MAP = {
"title": 0, # タイトルスライド
"content": 1, # タイトルと内容
"section": 2, # セクション見出し
"two_column": 3, # 2つのコンテンツ
"comparison": 4, # 比較
"title_only": 5, # タイトルのみ
"blank": 6, # 白紙
"image_content": 7 # 画像付きコンテンツ
}
def __init__(self, template_path: str):
"""テンプレートを読み込んで初期化"""
self.template_path = template_path
self.prs = Presentation(template_path)
self._analyze_layouts()
def _analyze_layouts(self):
"""テンプレートのレイアウトを解析"""
self.available_layouts = {}
for master in self.prs.slide_masters:
for idx, layout in enumerate(master.slide_layouts):
self.available_layouts[layout.name] = {
"index": idx,
"layout": layout
}
print(f"利用可能なレイアウト: {list(self.available_layouts.keys())}")
def _get_layout(self, layout_type: str):
"""レイアウトタイプからレイアウトを取得"""
# まず直接名前でマッチを試みる
if layout_type in self.available_layouts:
return self.available_layouts[layout_type]["layout"]
# インデックスでフォールバック
idx = self.LAYOUT_MAP.get(layout_type, 1)
master = self.prs.slide_masters[0]
if idx < len(master.slide_layouts):
return master.slide_layouts[idx]
return master.slide_layouts[1] # デフォルト
def create_presentation(self, content_data: Dict) -> None:
"""コンテンツデータからプレゼンテーションを作成"""
# 既存のスライドを削除(テンプレートのサンプルスライド)
while len(self.prs.slides) > 0:
rId = self.prs.slides._sldIdLst[0].rId
self.prs.part.drop_rel(rId)
del self.prs.slides._sldIdLst[0]
# 各スライドを生成
for slide_data in content_data.get("slides", []):
self._create_slide(slide_data)
def _create_slide(self, slide_data: Dict) -> None:
"""個別スライドを作成"""
layout_type = slide_data.get("layout_type", "content")
layout = self._get_layout(layout_type)
slide = self.prs.slides.add_slide(layout)
# レイアウトタイプに応じた処理
if layout_type == "title":
self._populate_title_slide(slide, slide_data)
elif layout_type == "section":
self._populate_section_slide(slide, slide_data)
elif layout_type == "two_column":
self._populate_two_column_slide(slide, slide_data)
else:
self._populate_content_slide(slide, slide_data)
def _populate_title_slide(self, slide, data: Dict) -> None:
"""タイトルスライドを設定"""
if slide.shapes.title:
slide.shapes.title.text = data.get("title", "")
# サブタイトルを探して設定
for shape in slide.placeholders:
if shape.placeholder_format.idx == 1: # サブタイトル
shape.text = data.get("subtitle", "")
break
def _populate_section_slide(self, slide, data: Dict) -> None:
"""セクションスライドを設定"""
if slide.shapes.title:
slide.shapes.title.text = data.get("title", "")
def _populate_content_slide(self, slide, data: Dict) -> None:
"""コンテンツスライドを設定"""
if slide.shapes.title:
slide.shapes.title.text = data.get("title", "")
# コンテンツプレースホルダーを探す
content_placeholder = None
for shape in slide.placeholders:
if shape.placeholder_format.idx == 1: # 通常のコンテンツ
content_placeholder = shape
break
if content_placeholder and data.get("content"):
tf = content_placeholder.text_frame
tf.clear()
for i, item in enumerate(data["content"]):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = item
p.level = 0
def _populate_two_column_slide(self, slide, data: Dict) -> None:
"""2カラムスライドを設定"""
if slide.shapes.title:
slide.shapes.title.text = data.get("title", "")
# 左右のプレースホルダーを探す
placeholders = list(slide.placeholders)
left_data = data.get("left_column", {})
right_data = data.get("right_column", {})
for idx, shape in enumerate(placeholders[1:3]): # 最初の2つのコンテンツプレースホルダー
column_data = left_data if idx == 0 else right_data
if column_data:
tf = shape.text_frame
tf.clear()
# 見出し
if column_data.get("heading"):
p = tf.paragraphs[0]
p.text = column_data["heading"]
p.font.bold = True
# 項目
for item in column_data.get("points", []):
p = tf.add_paragraph()
p.text = item
p.level = 1
def save(self, output_path: str) -> None:
"""プレゼンテーションを保存"""
self.prs.save(output_path)
print(f"プレゼンテーションを保存しました: {output_path}")
def main():
"""メイン実行関数"""
from content_generator import SlideContentGenerator
from dotenv import load_dotenv
load_dotenv()
# 1. コンテンツ生成
print("=== コンテンツを生成中... ===")
generator = SlideContentGenerator(os.getenv("ANTHROPIC_API_KEY"))
content = generator.generate_presentation_structure(
topic="DX推進による業務効率化提案",
num_slides=10,
presentation_type="経営層向け提案資料"
)
# 2. PPTX生成
print("\n=== PowerPointを生成中... ===")
pptx_gen = PPTXGenerator("templates/company_template.pptx")
pptx_gen.create_presentation(content)
pptx_gen.save("output/generated_presentation.pptx")
print("\n完了!")
if __name__ == "__main__":
main()
ステップ5: Webインターフェース(オプション)
Streamlitを使った簡単なWebインターフェースを作成できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| # app.py
import streamlit as st
from content_generator import SlideContentGenerator
from pptx_generator import PPTXGenerator
import os
import tempfile
st.set_page_config(page_title="AI PowerPoint Generator", layout="wide")
st.title("AI PowerPoint Generator")
st.markdown("会社のテンプレートに沿った資料をAIで自動生成します")
# サイドバー: 設定
with st.sidebar:
st.header("設定")
template_file = st.file_uploader(
"会社テンプレート(.pptx)をアップロード",
type=["pptx"]
)
num_slides = st.slider("スライド枚数", min_value=3, max_value=20, value=8)
presentation_type = st.selectbox(
"資料タイプ",
["提案資料", "進捗報告", "技術解説", "研修資料", "営業資料"]
)
# メインエリア
topic = st.text_area(
"資料のトピック・概要を入力してください",
height=150,
placeholder="例:クラウド移行プロジェクトの提案。現状のオンプレミス環境の課題と、Azure移行によるメリットを説明する資料。"
)
if st.button("資料を生成", type="primary"):
if not topic:
st.error("トピックを入力してください")
elif not template_file:
st.error("テンプレートファイルをアップロードしてください")
else:
with st.spinner("AIが資料を生成中..."):
# テンプレートを一時ファイルに保存
with tempfile.NamedTemporaryFile(delete=False, suffix=".pptx") as tmp:
tmp.write(template_file.read())
template_path = tmp.name
try:
# コンテンツ生成
generator = SlideContentGenerator(os.getenv("ANTHROPIC_API_KEY"))
content = generator.generate_presentation_structure(
topic=topic,
num_slides=num_slides,
presentation_type=presentation_type
)
# PPTX生成
pptx_gen = PPTXGenerator(template_path)
pptx_gen.create_presentation(content)
# 出力ファイルを一時保存
output_path = tempfile.mktemp(suffix=".pptx")
pptx_gen.save(output_path)
# ダウンロードボタン
with open(output_path, "rb") as f:
st.download_button(
label="生成した資料をダウンロード",
data=f,
file_name="generated_presentation.pptx",
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation"
)
# 生成内容のプレビュー
st.subheader("生成されたスライド構成")
for slide in content.get("slides", []):
with st.expander(f"スライド {slide['slide_number']}: {slide.get('title', '')}"):
st.json(slide)
finally:
os.unlink(template_path)
|
システム構成図
graph TD
subgraph "クライアント"
A[Webブラウザ] --> B[Streamlit UI]
end
subgraph "アプリケーションサーバー"
B --> C[template_analyzer.py]
B --> D[content_generator.py]
B --> E[pptx_generator.py]
C --> F[テンプレート解析結果]
D --> G[LLM生成コンテンツ]
F --> E
G --> E
E --> H[生成されたPPTX]
end
subgraph "外部サービス"
D --> I[Claude API]
end
subgraph "ストレージ"
J[テンプレート.pptx] --> C
H --> K[出力.pptx]
end
Azure OpenAIを使用する場合
企業環境でAzure OpenAI Serviceを使用する場合のコード例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| # azure_content_generator.py
from openai import AzureOpenAI
import os
class AzureSlideContentGenerator:
"""Azure OpenAI Serviceを使用したコンテンツ生成"""
def __init__(self):
self.client = AzureOpenAI(
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-02-15-preview",
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)
self.deployment_name = os.getenv("AZURE_OPENAI_DEPLOYMENT")
def generate_presentation_structure(
self,
topic: str,
num_slides: int,
presentation_type: str = "提案資料"
) -> dict:
"""プレゼンテーション構造を生成"""
response = self.client.chat.completions.create(
model=self.deployment_name,
messages=[
{
"role": "system",
"content": "あなたはプレゼンテーション資料の専門家です。JSON形式でスライド構成を出力してください。"
},
{
"role": "user",
"content": f"トピック「{topic}」で{num_slides}枚の{presentation_type}を作成してください。"
}
],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
|
メリット・デメリット
メリット:
- 最大の柔軟性とカスタマイズ性
- 複雑なテンプレートロジックに対応可能
- API費用のみで運用可能(初期投資が低い)
- オンプレミス環境でも動作可能
- バッチ処理で大量生成可能
デメリット:
- Python開発スキルが必要
- テンプレート解析の初期実装コスト
- テンプレート変更時にコード修正が必要
- セキュリティ設計は自己責任
コスト試算
【100名の組織で月50件の資料を生成する場合】
Claude API費用:
- 1資料あたり: 約5,000トークン(入力) + 2,000トークン(出力)
- Claude Sonnet: 入力 $3/1M tokens, 出力 $15/1M tokens
- 1資料コスト: ($3 × 5/1000) + ($15 × 2/1000) = $0.045 ≈ 7円
- 月間費用: 7円 × 50件 = 350円/月 = 4,200円/年
サーバー費用(Azure App Service B1):
- 約3,000円/月 = 36,000円/年
合計: 約40,000円/年
vs Copilot for M365(同条件): 約5,400,000円/年
→ 約99%のコスト削減(ただし開発・運用コストは別途必要)
アプローチ3: Power Automate + AI Builder
概要
Microsoft Power Automateを使って、ローコード/ノーコードでPowerPoint生成ワークフローを構築します。Microsoft 365環境に完全に統合され、IT部門以外でも運用可能です。
graph LR
A[トリガー] --> B[AI Builder
テキスト生成]
B --> C[Office Scripts
または
PowerPointコネクタ]
C --> D[SharePointに保存]
D --> E[通知送信]
subgraph "Power Platform"
B
C
end
前提条件
| 項目 | 要件 |
|---|
| ライセンス | Power Automate Premium または Power Apps Premium |
| AI Builder | AI Builder クレジット(Premium含む、または追加購入) |
| 費用 | 約2,000-4,000円/ユーザー/月 |
| スキル | ローコード開発の基礎知識 |
実装手順
ステップ1: Power Automateフローの作成
1
2
3
4
5
6
7
8
9
| フロー概要:
名前: "AI PowerPoint Generator"
トリガー: 手動トリガー(またはFormsからの送信)
入力パラメータ:
- topic: テキスト(資料のトピック)
- slide_count: 数値(スライド枚数)
- presentation_type: 選択肢(資料タイプ)
- template_url: URL(テンプレートファイルのURL)
|
ステップ2: フローの構成
graph TD
A[手動トリガー] --> B[変数の初期化]
B --> C[AI Builderでテキスト生成]
C --> D[JSONの解析]
D --> E[テンプレートをコピー]
E --> F[Office Scriptsでスライド編集]
F --> G[SharePointに保存]
G --> H[Teamsで通知]
各ステップの詳細:
ステップ2-1: 変数の初期化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"action": "初期化 - 変数",
"variables": [
{
"name": "slideContents",
"type": "Array",
"value": []
},
{
"name": "outputFileName",
"type": "String",
"value": "@{concat('Generated_', formatDateTime(utcNow(), 'yyyyMMdd_HHmmss'), '.pptx')}"
}
]
}
|
ステップ2-2: AI Builderでテキスト生成
AI Builderの「GPTでテキストを作成」アクションを使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| アクション: GPTでテキストを作成
設定:
プロンプト: |
あなたはプレゼンテーション資料の専門家です。
以下の条件でスライド構成をJSON形式で出力してください。
【トピック】@{triggerBody()['topic']}
【スライド枚数】@{triggerBody()['slide_count']}
【資料タイプ】@{triggerBody()['presentation_type']}
出力形式:
{
"slides": [
{"number": 1, "layout": "title", "title": "...", "subtitle": "..."},
{"number": 2, "layout": "content", "title": "...", "bullets": ["...", "..."]}
]
}
最大トークン数: 2000
温度: 0.7
|
ステップ2-3: Office Scriptsによるスライド編集
Power AutomateからOffice Scriptsを呼び出してPowerPointを編集します。
まず、Excel Online(Office Scripts)でスクリプトを作成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // Office Script: populatePowerPoint.ts
// 注意: Office ScriptsはExcel用ですが、Graph APIと組み合わせてPowerPointを操作
function main(workbook: ExcelScript.Workbook, slideData: string) {
// このスクリプトはExcel経由でデータを処理
// 実際のPowerPoint操作はGraph APIで行う
const data = JSON.parse(slideData);
// 処理結果をシートに出力(Power Automateで読み取り)
const sheet = workbook.getActiveWorksheet();
sheet.getRange("A1").setValue(JSON.stringify(data));
return data;
}
|
Graph APIを使用した直接操作(Power Automate HTTPアクション):
1
2
3
4
5
6
7
8
9
10
| {
"method": "PATCH",
"uri": "https://graph.microsoft.com/v1.0/sites/{site-id}/drive/items/{file-id}/workbook/worksheets('Sheet1')/range(address='A1')",
"headers": {
"Content-Type": "application/json"
},
"body": {
"values": [["@{variables('slideContent')}"]]
}
}
|
ステップ2-4: PowerPointコネクタでスライド追加
Power AutomateのPowerPointコネクタを使用:
1
2
3
4
5
6
7
8
9
10
11
12
| # Apply to each: スライドデータをループ
アクション: PowerPoint - スライドの入力
設定:
場所: SharePoint
ドキュメントライブラリ: Documents
ファイル: "@{variables('outputFileName')}"
# 各スライドの設定
スライドインデックス: "@{items('Apply_to_each')['number']}"
タイトル: "@{items('Apply_to_each')['title']}"
サブタイトル: "@{items('Apply_to_each')['subtitle']}"
本文: "@{join(items('Apply_to_each')['bullets'], char(10))}"
|
ステップ3: 完全なフロー定義
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| {
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"triggers": {
"manual": {
"type": "Request",
"kind": "Button",
"inputs": {
"schema": {
"type": "object",
"properties": {
"topic": { "type": "string" },
"slide_count": { "type": "integer" },
"presentation_type": { "type": "string" },
"template_url": { "type": "string" }
}
}
}
}
},
"actions": {
"Initialize_slideContents": {
"type": "InitializeVariable",
"inputs": {
"variables": [{
"name": "slideContents",
"type": "array"
}]
}
},
"AI_Builder_Generate": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['aibuilder']['connectionId']"
}
},
"method": "post",
"path": "/v1.0/appintelligence/chat/completions"
},
"runAfter": {
"Initialize_slideContents": ["Succeeded"]
}
},
"Parse_JSON": {
"type": "ParseJson",
"inputs": {
"content": "@body('AI_Builder_Generate')?['text']",
"schema": {
"type": "object",
"properties": {
"slides": {
"type": "array",
"items": {
"type": "object",
"properties": {
"number": { "type": "integer" },
"layout": { "type": "string" },
"title": { "type": "string" },
"subtitle": { "type": "string" },
"bullets": { "type": "array" }
}
}
}
}
}
},
"runAfter": {
"AI_Builder_Generate": ["Succeeded"]
}
}
}
}
}
|
ユーザーが簡単に資料生成をリクエストできるよう、Formsと連携します。
1
2
3
4
5
6
7
| Forms質問項目:
1. 資料のタイトル(テキスト、必須)
2. 資料の概要・目的(長文テキスト、必須)
3. スライド枚数(選択肢: 5, 8, 10, 15, 20)
4. 資料タイプ(選択肢: 提案資料, 報告資料, 研修資料, etc.)
5. 対象者(選択肢: 経営層, 管理職, 一般社員, 顧客)
6. 使用するテンプレート(選択肢: 標準, フォーマル, カジュアル)
|
Formsトリガーのフロー:
graph TD
A[Forms送信] --> B[回答データ取得]
B --> C[テンプレート選択]
C --> D[AI Builderで生成]
D --> E[PowerPoint作成]
E --> F[SharePointに保存]
F --> G[送信者にメール通知]
G --> H[ダウンロードリンク付き]
ステップ5: 承認フローの追加(オプション)
重要な資料には承認フローを追加できます。
1
2
3
4
5
6
7
8
9
10
11
12
| 承認フロー追加:
条件: presentation_type == "顧客向け提案資料"
アクション:
1. 上長に承認依頼を送信
2. 承認待ち
3. 承認の場合:
- 最終版としてSharePointに保存
- 関係者に通知
4. 却下の場合:
- 修正コメントと共に送信者に返却
- 修正後に再送信可能
|
デモ用フローのエクスポート
Power Automateソリューションとしてエクスポート可能な構成:
AI-PowerPoint-Generator/
├── solution.xml
├── Flows/
│ ├── AI-PPT-Generator-Main.json
│ ├── AI-PPT-Generator-FromForms.json
│ └── AI-PPT-Generator-WithApproval.json
├── ConnectionReferences/
│ ├── SharePoint.json
│ ├── AIBuilder.json
│ ├── Office365Users.json
│ └── Approvals.json
└── EnvironmentVariables/
├── TemplateLibraryUrl.json
├── OutputLibraryUrl.json
└── DefaultSlideCount.json
メリット・デメリット
メリット:
- コーディング不要でワークフローを構築
- Microsoft 365との完全な統合
- Formsやeamsとの連携が容易
- 承認フローを簡単に追加可能
- IT部門以外でもメンテナンス可能
デメリット:
- PowerPointの細かいレイアウト制御には限界
- AI Builderのクレジット消費
- 複雑なテンプレートロジックは実装困難
- 大量生成には向かない
コスト試算
【100名の組織で月50件の資料を生成する場合】
Power Automate Premium:
- 2,248円/ユーザー/月 × 必要ユーザー数
- 資料生成担当者のみ: 5名 × 2,248円 × 12 = 134,880円/年
AI Builderクレジット:
- Premiumライセンスに含まれるクレジット: 月間500クレジット
- 1資料あたり: 約10クレジット
- 月50件 × 10 = 500クレジット(ギリギリ収まる)
- 追加購入の場合: 100クレジット = 約500円
合計: 約150,000円/年
vs Copilot for M365: 約5,400,000円/年
→ 約97%のコスト削減
3つのアプローチの比較まとめ
機能比較表
| 機能 | Copilot for M365 | Python + python-pptx | Power Automate |
|---|
| 導入の容易さ | 最も簡単 | 開発が必要 | ローコードで構築 |
| テンプレート対応 | 標準テンプレート | 完全カスタム対応 | 基本的な対応 |
| レイアウト制御 | Copilot任せ | 完全制御 | 限定的 |
| バッチ処理 | 不可 | 可能 | 可能(制限あり) |
| オンプレミス | 不可 | 可能 | 不可 |
| 日本語対応 | 優秀 | LLM依存 | 優秀 |
| セキュリティ | Microsoft管理 | 自己管理 | Microsoft管理 |
| 更新頻度 | 自動更新 | 手動更新 | 自動更新 |
コスト比較(100名組織・月50件)
graph LR
subgraph "年間コスト比較"
A[Copilot for M365
約540万円]
B[Python + python-pptx
約4万円 + 開発費]
C[Power Automate
約15万円]
end
style A fill:#ff6b6b
style B fill:#4ecdc4
style C fill:#45b7d1
推奨シナリオ
graph TD
A[どのアプローチを選ぶ?] --> B{予算は十分?}
B -->|Yes| C{全社展開?}
C -->|Yes| D[Copilot for M365
全員が使える環境を構築]
C -->|No| E{開発リソースあり?}
B -->|No| E
E -->|Yes| F[Python + python-pptx
最大の柔軟性とコスト効率]
E -->|No| G[Power Automate
ローコードで素早く構築]
style D fill:#4285f4,color:#fff
style F fill:#f9ab00,color:#000
style G fill:#34a853,color:#fff
選択ガイド
| シナリオ | 推奨アプローチ | 理由 |
|---|
| 全社でAIを活用したい | Copilot for M365 | 全員が同じ体験で利用可能 |
| 特定チームのみで利用 | Power Automate | コスト効率が良い |
| 複雑なテンプレートがある | Python + python-pptx | 完全なカスタマイズが可能 |
| 大量の資料を自動生成 | Python + python-pptx | バッチ処理に最適 |
| セキュリティ要件が厳しい | Copilot for M365 または Power Automate | Microsoft管理で安心 |
| オンプレミス環境 | Python + python-pptx | クラウド不要 |
| 素早く導入したい | Copilot for M365 | 設定のみで即利用可能 |
ハイブリッドアプローチ
実際の運用では、複数のアプローチを組み合わせることも効果的です。
graph TD
A[資料生成リクエスト] --> B{資料タイプ}
B -->|簡易資料| C[Copilot for M365
個人で素早く作成]
B -->|標準資料| D[Power Automate
Formsからワンクリック生成]
B -->|複雑な資料| E[Python + python-pptx
カスタム処理で高品質生成]
C --> F[完成資料]
D --> F
E --> F
F --> G[SharePointで共有]
段階的な導入プラン
Phase 1(1-2ヶ月):
- Power Automateで基本フローを構築
- 標準テンプレート3種類に対応
- 社内パイロット運用
Phase 2(3-4ヶ月):
- Python版を開発し複雑なテンプレートに対応
- バッチ処理機能を追加
- API統合(社内システム連携)
Phase 3(5-6ヶ月):
- Copilot for M365の評価
- 利用頻度の高い部門への導入
- 3つのアプローチを適材適所で運用
まとめ
AIを使って会社のPowerPointフォーマットに合わせた資料を自動生成する方法として、3つのアプローチを紹介しました。
各アプローチの要点
Copilot for Microsoft 365
- 最も簡単に導入可能
- Microsoft 365との完全統合
- コストは高いが、全社展開に最適
Python + python-pptx + LLM API
- 最大の柔軟性とカスタマイズ性
- コスト効率が最も高い
- 開発スキルが必要
Power Automate + AI Builder
- ローコードで構築可能
- Microsoft 365との統合が容易
- バランスの取れた選択肢
次のステップ
- 現在のPowerPoint作成プロセスを分析
- 会社のテンプレートを整理・標準化
- 小規模なパイロットで効果を検証
- 成功したアプローチを段階的に展開
AIによる資料作成の自動化は、業務効率化の大きな一歩です。ぜひあなたの組織に最適なアプローチを選んで、導入を検討してみてください。
参考リンク
更新履歴: