1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パワポつくるちゃん開発記 〜Copilot Studio vs DLPポリシー、そしてKiroへ〜

1
Last updated at Posted at 2026-05-14

こんにちは。

「会社のテンプレートを使ったパワーポイント資料をAIに作ってもらいたい」

そんなシンプルな願いから始まった開発が、企業環境の壁にぶつかり、最終的に思わぬ形で解決した話をお届けします。

この記事は、Kiro(AI搭載IDE)とAnthropic Claude Opus(今回使用したKiroのバックエンドLLM)を用いて開発した記録です。実際にKiroと会話しながら実装し、試行錯誤した過程をそのままお伝えしています。

開発環境

Kiroとは?

Kiroは、AWSが提供するAI搭載の統合開発環境(IDE)です。

主な特徴:

  • Spec駆動開発: 仕様書(Spec)をAIと一緒に作りながら開発を進めるスタイル。要件定義→設計→タスク分解→実装を一貫してサポート
  • AIとの対話: チャットしながらコードを書ける
  • ローカル実行: Pythonスクリプトなどをローカルで実行可能
  • IDE版とCLI版: デスクトップアプリとコマンドライン両方で使える
  • Web検索機能: 最新情報を取得してコードに反映

Anthropic Claudeとは?

Claudeは、Anthropic社が開発した大規模言語モデル(LLM)です。

主な特徴:

  • 高い推論能力: 複雑なコーディングタスクも的確に処理
  • 長いコンテキスト: 大量のコードや仕様書を一度に理解
  • 安全性重視: 有害なコンテンツを生成しにくい設計

今回の開発では、KiroのバックエンドモデルとしてClaude Opus 4.6を使用しました。Kiroに話しかけると、Claude Opusが考えてコードを生成してくれます。

開発スタイル

私: 「パワポを自動生成する機能を作りたい」
Kiro: 「いいね!まずは要件を整理しよう...」
         ↓
    Spec作成 → 設計 → 実装 → テスト
         ↓
私: 「ここ、ちょっと違うかも」
Kiro: 「了解!修正するね」

このように、会話しながら開発を進められるのがKiro + Claudeの強みです。

開発のきっかけ

みのるんさん(@minorun365)が公開しているパワポつくるマンを見て、「これ、社内でやりたい!」と考えました。

名前の使い分け: みのるんさんのツールが「パワポつくるマン」、わたしがKiroで作ったのが「パワポつくるちゃん」です。

パワポつくるマンは、AIが情報を集め、Markdownを組み立てて、スライドを自動生成する素敵なツール。でも、会社のPowerPointテンプレートを使いたい...
そして、社内のルールが厳しいので、みのるんさんのアプリをそのまま利活用するのは難しい...

そこで思いついたのが、社内で利用可能な Copilot Studio + Code Interpreter での実現でした。

目指したアーキテクチャ

┌─────────────────────────────────────────────────────────┐
│                    Microsoft 365 環境                    │
├─────────────────────────────────────────────────────────┤
│  Teams / Copilot Studio UI                              │
│         ↓                                               │
│  Copilot Studio Agent (GPT-4.1)                         │
│         ↓                                               │
│  Code Interpreter (python-pptx)                         │
│         ↓                                               │
│  Power Automate → SharePoint Online                     │
└─────────────────────────────────────────────────────────┘
  • フロントエンド: Teams / Copilot Studio チャットUI
  • AI処理: Copilot Studio Agent + GPT-4.1
  • コード実行: Code Interpreter (python-pptx)
  • ワークフロー: Power Automate エージェントフロー
  • ストレージ: SharePoint Online

完璧なプランに見えました...この時点では。

Step 1: SharePointセットアップ

まずはSharePointにテンプレートと生成ファイルを保存するライブラリを作成。

  • PPTXTemplates(テンプレート格納)
  • GeneratedPresentations(生成ファイル格納)

Step 2: Copilot Studioエージェント作成

エージェント名は、みのるんさんのアプリにあやかって「パワポつくるちゃん」に決定。

設定内容

  • モデル: GPT-4.1 (Standard)
  • プロンプトアクション①: スライド構成生成(JSON出力)
  • プロンプトアクション②: PPTX生成(Code Interpreter有効)
  • Web検索: Bing検索を有効化

プロンプトアクション①: スライド構成生成

Copilot Studioで「プロンプトアクション」を作成し、以下の設定を行います。

アクション名: Generate-Slide-Structure

入力パラメータ:

名前 説明
Theme テキスト プレゼンテーションのテーマ
AdditionalInstructions テキスト(オプション) 追加指示(スライド枚数、対象者など)

プロンプト指示:

あなたはプレゼン資料の構成を考えるエキスパートです。

ユーザーが入力したテーマ「{Theme}」に基づいて、プレゼンテーションのスライド構成を考えてください。

追加指示: {AdditionalInstructions}

## 出力形式

以下のJSON形式で出力してください。JSONのみを出力し、説明文は不要です。

{
    "metadata": {
        "theme": "プレゼンテーションのテーマ",
        "createdAt": "ISO8601形式の日時",
        "slideCount": スライド数,
        "targetAudience": "対象者(指定がない場合は「一般」)"
    },
    "slides": [
        {
            "slideNumber": 1,
            "layoutType": "title",
            "title": "プレゼンタイトル",
            "subtitle": "サブタイトル(オプション)",
            "notes": "発表者ノート(オプション)"
        },
        {
            "slideNumber": 2,
            "layoutType": "content",
            "title": "スライドタイトル",
            "content": {
                "type": "bullet_points",
                "items": ["ポイント1", "ポイント2", "ポイント3"]
            },
            "notes": null
        }
    ]
}

## レイアウトタイプ

以下のレイアウトタイプを適切に使い分けてください:

- title: タイトルスライド(1枚目に使用)
- content: コンテンツスライド(箇条書き)
- two_column: 2カラムレイアウト(比較や対比に使用)
- section_header: セクション区切り(大きなセクションの開始時)
- blank: 白紙スライド(画像のみの場合など)

## ルール

1. 1枚目は必ず title レイアウトのタイトルスライド
2. 最後は「まとめ」または「ご清聴ありがとうございました」のスライド
3. 各スライドの箇条書きは3〜5個程度
4. ビジネス向けの簡潔な表現を使用
5. 追加指示でスライド枚数が指定されていない場合は5〜8枚程度

Step 3: スライド構成生成の確認 → 成功!

テーマを入力すると、Web検索で情報を集めて、JSON形式でスライド構成を自動生成!

{
  "metadata": { "theme": "AWS Ambassador", "slideCount": 6 },
  "slides": [
    { "slideNumber": 1, "layoutType": "title", "title": "AWS Ambassador とは?" },
    { "slideNumber": 2, "layoutType": "content", "title": "プログラムの概要", "content": {...} }
  ]
}

ここまでは順調でした。

プロンプトアクション②: PPTX生成(Code Interpreter)

重要: このアクションでは Code Interpreter を有効 にする必要があります。

アクション名: Generate-PPTX-From-Template

入力パラメータ:

名前 説明
SlideStructure テキスト スライド構成のJSON文字列
TemplateFile ファイル 会社テンプレート(.pptx)
UserId テキスト ユーザー識別子

設定:

  • Code Interpreter: 有効
  • 出力: ドキュメント/画像

プロンプト指示:

以下のスライド構成データ {SlideStructure} と、
添付されたPowerPointテンプレート {TemplateFile} を使って、
新しいプレゼン資料を生成してください。

ユーザーID: {UserId}

## 処理手順

1. テンプレートファイルを読み込む
2. テンプレートの既存スライドを削除(マスターは保持)
3. スライド構成に従って新しいスライドを追加
4. 各スライドはテンプレートのレイアウトを使用
5. ファイル名にタイムスタンプとユーザーIDを含める

## Pythonコード

以下のコードを実行してPPTXファイルを生成してください:

(以下のPythonコードをプロンプトに含める)

Code Interpreter用Pythonコード:

クリックしてPythonコードを展開
from pptx import Presentation
from pptx.util import Inches, Pt
import json
from datetime import datetime
import re

# スライド構成をパース
slide_structure = json.loads('''{SlideStructure}''')

# テンプレート読み込み
prs = Presentation('template.pptx')

# 既存スライドを削除(マスターは残る)
while len(prs.slides) > 0:
    rId = prs.slides._sldIdLst[0].rId
    prs.part.drop_rel(rId)
    del prs.slides._sldIdLst[0]

# レイアウトマッピング(テンプレートに合わせて調整)
# ※ご自身のテンプレートに合わせてインデックスを変更してください
layout_mappings = {
    "title": 0,           # タイトルスライド
    "content": 1,         # タイトルとコンテンツ
    "two_column": 3,      # 2つのコンテンツ
    "section_header": 2,  # セクション見出し
    "blank": 6            # 白紙
}

def get_layout(layout_type):
    idx = layout_mappings.get(layout_type, 1)
    if idx >= len(prs.slide_layouts):
        idx = 1
    return prs.slide_layouts[idx]

def add_title_slide(slide_data):
    layout = get_layout("title")
    slide = prs.slides.add_slide(layout)
    if slide.shapes.title:
        slide.shapes.title.text = slide_data.get("title", "")
    subtitle = slide_data.get("subtitle")
    if subtitle:
        for shape in slide.placeholders:
            if shape.placeholder_format.idx == 1:
                shape.text = subtitle
                break

def add_content_slide(slide_data):
    layout = get_layout("content")
    slide = prs.slides.add_slide(layout)
    if slide.shapes.title:
        slide.shapes.title.text = slide_data.get("title", "")
    content = slide_data.get("content", {})
    items = content.get("items", [])
    for shape in slide.placeholders:
        if shape.placeholder_format.idx == 1:
            tf = shape.text_frame
            tf.clear()
            for i, item in enumerate(items):
                if i == 0:
                    tf.paragraphs[0].text = item
                else:
                    p = tf.add_paragraph()
                    p.text = item
                    p.level = 0
            break

def add_two_column_slide(slide_data):
    layout = get_layout("two_column")
    slide = prs.slides.add_slide(layout)
    if slide.shapes.title:
        slide.shapes.title.text = slide_data.get("title", "")
    content = slide_data.get("content", {})
    left_col = content.get("leftColumn", {})
    right_col = content.get("rightColumn", {})
    placeholders = list(slide.placeholders)
    # 左カラム
    if len(placeholders) > 1:
        tf = placeholders[1].text_frame
        tf.clear()
        if left_col.get("header"):
            tf.paragraphs[0].text = left_col["header"]
            tf.paragraphs[0].font.bold = True
        for item in left_col.get("items", []):
            p = tf.add_paragraph()
            p.text = item
    # 右カラム
    if len(placeholders) > 2:
        tf = placeholders[2].text_frame
        tf.clear()
        if right_col.get("header"):
            tf.paragraphs[0].text = right_col["header"]
            tf.paragraphs[0].font.bold = True
        for item in right_col.get("items", []):
            p = tf.add_paragraph()
            p.text = item

def add_section_header_slide(slide_data):
    layout = get_layout("section_header")
    slide = prs.slides.add_slide(layout)
    if slide.shapes.title:
        slide.shapes.title.text = slide_data.get("title", "")
    subtitle = slide_data.get("subtitle")
    if subtitle:
        for shape in slide.placeholders:
            if shape.placeholder_format.idx == 1:
                shape.text = subtitle
                break

# スライド追加
for slide_data in slide_structure["slides"]:
    layout_type = slide_data.get("layoutType", "content")
    if layout_type == "title":
        add_title_slide(slide_data)
    elif layout_type == "content":
        add_content_slide(slide_data)
    elif layout_type == "two_column":
        add_two_column_slide(slide_data)
    elif layout_type == "section_header":
        add_section_header_slide(slide_data)
    else:
        slide = prs.slides.add_slide(get_layout("blank"))

# ファイル名生成
def sanitize(text):
    text = re.sub(r'[<>:"/\\|?*]', '', text)
    text = re.sub(r'\s+', '_', text)
    return text[:50]

timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
theme = sanitize(slide_structure["metadata"]["theme"])
user_id = sanitize("{UserId}")
filename = f"{theme}_{user_id}_{timestamp}.pptx"

# 保存
prs.save(filename)
print(f"生成完了: {filename}")

テンプレートのレイアウト確認方法

自分のテンプレートのレイアウトインデックスを確認するには、以下のPythonコードを使います。

from pptx import Presentation

prs = Presentation('your_template.pptx')
for i, layout in enumerate(prs.slide_layouts):
    print(f"{i}: {layout.name}")

Step 4: PPTX生成...しかし

Code Interpreterでpython-pptxを実行 → 成功!(のハズ)

PPTXファイル生成完了のメッセージが表示され、ダウンロードリンクも出てきました。

...しかし、クリックしても何も起きない

Microsoft TeamsにAgentを公開しても状況変わらず。ファイルは生成されているハズなのに、ダウンロードができない!

立ちはだかるDLPの壁

「Power Automateでファイルを保存すればいいのでは?」
しかし... DLPポリシー(データ損失防止ポリシー)でブロック!

トリガー: エージェントがフローを呼び出したとき
アクション: SharePointにファイルを作成
エラー: コネクタが競合しています
- SharePoint (ビジネス)
- Direct Line channels in Copilot Studio (非ビジネス)

会社のセキュリティポリシーが厳格で、SharePointコネクタとCopilot Studioの組み合わせが許可されていませんでした。

代替案を模索

仕方ないので、以下のような代替案を考えました。

No. 結果
1 Base64エンコードで出力しローカルで復号化 PPTXファイルは大きすぎて非現実的
2 会社提供の生成AIチャットBot Sharepoint出力対応はこれから
3 IT管理者にDLPポリシー例外申請 恐らく承認されない

そこで思いついたのが...

Kiroをパワポつくるちゃんに!

発想の転換: これまで伴走してもらっていた Kiro 自身をパワポつくるちゃんになってもらう。

前述の通り、KiroはAWSが提供するAI搭載IDE。ローカルでPythonスクリプトを実行できます。

つまり、以下のような環境を想定しました。

┌─────────────────────────────────────────┐
│                環境                  │
├─────────────────────────────────────────┤
│  Kiro (IDE版 or CLI版)                  │
│         ↓                               │
│  slide_builder.py (python-pptx)         │
│         ↓                               │
│  ローカルにPPTX保存                      │
└─────────────────────────────────────────┘

パワポつくるちゃんにおけるKiroの強み

冒頭でKiroの概要を紹介しましたが、今回のパワポ生成において特に活きたポイントは以下の通りです。

  1. Web検索機能内蔵: テーマに関する最新情報を取得してスライドに反映
  2. 生成物をローカルに保存: Python スクリプトの実行はローカルなので、DLPポリシーに抵触しない
  3. 会話しながらPPTX生成: 構成を確認→修正→生成のサイクルが速い
  4. Spec駆動開発: slide_builder.pyの設計から実装まで一貫してサポート
  5. IDE版とCLI版の連携: 同じワークスペースでどちらからでも作業可能

Kiro版「パワポつくるちゃん」の実装

slide_builder.py - スライド生成ライブラリ

Kiro版では、slide_builder.pyというライブラリを作成しました。Markdown風の記法でスライドを定義できます。

slide_builder.pyの特徴

Kiro に相談しながら少しずつブラッシュアップを重ねて、以下のような機能を作りこんでいきました。

  • 会社テンプレート対応(レイアウトインデックスを調整可能)
  • Markdown風の書式(例:##で見出し、-で箇条書き)
  • PPTネイティブの箇条書き機能を使用(編集時も箇条書きが維持される)
  • テーブルスライドも対応
  • メソッドチェーンで直感的に記述可能
  • アジェンダ(目次)スライド対応
  • 章扉(セクション区切り)スライド対応
  • Closingスライド対応
  • 番号付きリスト(1. テキスト)対応
  • コードブロック(```で囲む)→ ダーク背景+黄緑文字のターミナル風表示
  • 本文が長い場合の自動スライド分割(プレースホルダー高さベースの動的計算)
  • タイトルが長い場合のフォントサイズ自動縮小

Markdown風記法の全ルール

記法 意味
## テキスト 小見出し(太字) ## 本日のアジェンダ
- テキスト 箇条書き(レベル1) - 項目1
- テキスト サブ箇条書き(レベル2) - サブ項目
1. テキスト 番号付きリスト 1. 最初の項目
1. テキスト サブ番号付きリスト 1. サブ項目
`````` コードブロック(ターミナル風) コマンドやコード
通常テキスト 箇条書きなし これは通常テキストです
空行 空白行 ""

スライド種別一覧

メソッド 説明
add_cover(title) 表紙スライド
add_agenda(title, items) アジェンダ(目次)スライド
add_section(section_title) 章扉スライド(セクション区切り)
add_body(section_title, lines) 本文スライド(自動分割・コードブロック対応)
add_table(section_title, headers, rows) テーブルスライド
add_closing() Closingスライド

最終的な動作

Kiroに作ってほしいテーマをプロンプトで指定することで、スライドを作ってくれます。
例えば、「AWS Ambassadorについて5枚のスライドを作って」と話しかけるだけで、Web検索で最新情報を取得し、会社テンプレートを使ったPPTXファイルを生成してくれます。

# Kiroが内部で実行するコード例
from slide_builder import SlideBuilder

sb = SlideBuilder("output/aws_ambassador.pptx")
sb.add_cover("AWS Ambassador とは?")
sb.add_body("今日話すこと", [
    "## アジェンダ",
    "- AWS Ambassadorプログラムの概要",
    "- 認定要件と特典",
    "- 活動内容の紹介",
])
# ... 以下続く
sb.save()

成果物は以下のようになりました。

AWS Ambassador について 5枚のスライドを作って の例

image.png

Anthropic Claude のライセンス形態についてまとめて の例

image.png

必要に応じて、この成果物を直接編集したり、 Kiro に修正させてブラッシュアップします。
問題なければそのまま使ってもOK。

既存資料の読み込み機能

「この資料を参考にしてスライドを作って」という使い方もできます。

doc_to_markdown.py - PPTX/Word → Markdownに変換

既存のPPTXファイルやWordファイルをMarkdownに変換して、Kiroが読み取れるようにするスクリプトです。

from doc_to_markdown import convert_to_markdown, convert_folder

# 単一ファイル変換
convert_to_markdown("資料.pptx")

# フォルダ内の全ファイルを変換
convert_folder("C:\\資料フォルダ")

使い方の例:

Kiroに対して以下のように指示するだけ:

「C:\資料\企画書.pptx と C:\資料\要件定義.docx を
参考にして、プロジェクト概要のスライドを10枚で作って」
「C:\資料\ フォルダの中身を参考にしてスライド作って」

Kiroが自動で:

  1. PPTX/Wordファイルをスクリプトで変換
  2. 変換されたMarkdownを読み込み
  3. 内容を把握してスライドを作成

Kiro IDE版とCLI版の連携

Kiro IDE版とkiro-cli(CLI版)を同じワークスペースで実行することで、どちらからでもスライドを作ることが可能です。

# CLI版でパワポ作成
kiro-cli chat "AWS Ambassadorについて5枚のスライドを作って"

IDE版で修正したコードは、CLI版でもそのまま使えます。その逆も同様です。

学び

  1. 企業環境にはよっては、DLPポリシーなどの制約がある
  2. うまくいかなくても代替案を探す。 クラウドがダメならローカル
  3. AIツールは使い分けが大事。 Copilot Studio、Kiro、それぞれに得意分野がある
  4. 発想の転換。 制約を回避する方法は必ずある
  5. python-pptxは奥が深い。 箇条書きの制御にはXMLレベルの操作(a:buChara:buNonea:lstStyleなど)が必要で、python-pptxのAPIだけでは完結しなかった

補足:Copilot Studioで成功させるには

DLPポリシーが許可されている環境であれば、Copilot Studioでの実現は可能です。

なお、Code Interpreterで実行するPythonコードは開発途中のため、完璧に動作させるには追加の実装が必要です。

以下のポイントを確認してください。

  1. DLPポリシーの確認: IT管理者に以下のコネクタの組み合わせが許可されているか確認

    • SharePoint コネクタ
    • Direct Line channels in Copilot Studio
  2. Power Automateフローの設定:

    • トリガー: 「エージェントがフローを呼び出したとき」
    • アクション: 「SharePointにファイルを作成」
  3. Copilot Studioのアクション設定:

    • 「アクション」タブ → 「フローを追加」
    • 作成したPower Automateフローを選択

もしDLPに抵触する場合は、この記事で紹介したKiro版「パワポつくるちゃん」をお試しください!

まとめ

Copilot Studioでの実現は叶いませんでしたが、結果的にKiroを用いた使い勝手の良いツールを生み出せました。

「パワポつくるちゃん」は今、Kiroとともに元気に動いています。会社のテンプレートを使って、会話しながらスライドを生成してくれます。

企業環境でのAIの利活用は制約が多くありますが、それを乗り越える過程で新しい発見があるものですね。


記載されている会社名、製品名、サービス名、ロゴ等は各社の商標または登録商標です。

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?