先日、下記のつぶやきを拝見して心が躍りました。
マイクロソフトが開発した MarkItDown
— daka | Microsoft | AI (@daiki15036604) December 14, 2024
PDFだけじゃなく、Word、Excel、PowerPointなどの多様なファイル形式をMarkdownに変換し、テキスト分析やインデックス作成を容易できるのよい!https://t.co/DgCxamEpTs
マイクロソフトが開発した MarkItDown PDFだけじゃなく、Word、Excel、PowerPointなどの多様なファイル形式をMarkdownに変換し、テキスト分析やインデックス作成を容易できる
非常に心が踊るフレーズです。
さっそくPower Apps
で試されている方もいらっしゃいます。
この記事ではAzure Functions
を使って、このMarkItDownを試してみたいと思います。
作ったもの
-
Power Apps
-SharePoint サイト
のドキュメントライブラリを指定するインターフェース -
Azure Functions
- MarkItDownを関数用に加工したもの -
Power Automate
- (1)のPower Apps
から、ファイル識別子
を受け取り、ファイル コンテンツを取得、Azure Functions
を起動
Power Apps
はあくまで結果のプレビュー用に用いています。
キャプチャを載せると、ファイルに対して結果がある程度上手くかえってきていることがわかります。
■ Word
■ PowerPoint
実際につかったところ、PowerPoint
やWord
、PDF
のMarkdownへの変換は精度が高いです。
Power Appsでは、赤枠のギャラリー コントロールにドキュメント ライブラリ
の結果を出力し、OnSelect
プロパティで結果を取得しています。
UpdateContext({Result: MarkdownConvert.Run(ThisItem.識別子).response});
結果はテキスト入力
で表示しています。
小ネタ: 環境変数
Power Apps
、Power Automate
でSharePointのサイトやリスト情報であったり、今回はAzure Functionsのエンドポイントを指定したりしますが、そのような値は環境変数に設定しておくと管理が容易です。
ソリューションを作成し、環境変数を追加すると、Power Apps
とPower Automate
の共通の値(例: データソースの情報)、エンドポイントの情報をまとめて管理出来ます。
また開発に置ける環境戦略開発者環境
、テスト環境
、本稼働環境
を分ける際にも非常に重宝する機能です。
今回は下記の値を環境変数
として定義します。
項目名 | 環境変数名 | 説明 |
---|---|---|
SharePoint サイト | SP_SITE_URL |
SharePoint サイト |
ドキュメント ライブラリ | SP_DOCUMENT_LIBRARY |
SharePoint ドキュメントライブラリ |
Azure Function エンドポイント | AZURE_FUNCTION_ENDPOINT |
Azure Functions URL |
シークレット | AZURE_SECRET |
シークレット情報 |
Azure Functions
- MarkItDownを関数用に加工したもの
Visual Studio Code 用 Azure Functions 拡張機能を使って作成を進めます。
私のような知識が少ないものでも簡単に作れる利便性の高いツールです。
作業するディレクトリを選択し、Azure Functions プロジェクトを作成します。
HTTP トリガー関数テンプレートを選択
することで必要なファイルがそろいます。
Pythonの仮想環境はvenv
を使います。
markitdown-function/
├── .venv/
├── markitdown/
│ ├── __init__.py
│ ├── __about__.py
│ └── _markitdown.py
├── function_app.py
├── requirements.txt
├── host.json
└── local.settings.json
テンプレートの中にmarkitdown
ディレクトリを設けて、MarkItDownの必要なファイルを組み込みます。
必要なファイルは下記の三つです。
- init.py
- about.py
- markitdown.py
依存関係
azure-functions
以外はMarkItDownで使われるものです。
azure-functions
beautifulsoup4>=4.9.3
markdownify>=0.11.6
mammoth>=1.5.1
pandas>=1.3.0
pdfminer.six>=20201018
python-pptx>=0.6.21
puremagic>=1.15
requests>=2.26.0
charset-normalizer>=2.0.0
function_app.py
エントリーポイント
であるfunction_app.py
はClaude 3.5 Sonnet
に作成してもらいました。
AI
様様です。
Power Automateからbase64
文字列で受け取るファイルをもとにMarkItDown
を呼び出します。
import azure.functions as func
import logging
import json
import base64
import tempfile
import os
from markitdown import MarkItDown, FileConversionException, UnsupportedFormatException
app = func.FunctionApp()
@app.route(route="convert", auth_level=func.AuthLevel.FUNCTION)
async def convert_to_markdown(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
try:
# Get request body
req_body = req.get_json()
if not req_body or 'file' not in req_body:
return func.HttpResponse(
json.dumps({
"error": "Please pass a base64 encoded file in the request body"
}),
mimetype="application/json",
status_code=400
)
file_content = req_body.get('file')
file_extension = req_body.get('extension', '')
# Decode base64 content
try:
decoded_content = base64.b64decode(file_content)
except Exception as e:
return func.HttpResponse(
json.dumps({
"error": f"Invalid base64 content: {str(e)}"
}),
mimetype="application/json",
status_code=400
)
# Create a temporary file
handle, temp_path = tempfile.mkstemp(suffix=file_extension)
try:
with os.fdopen(handle, 'wb') as temp_file:
temp_file.write(decoded_content)
# Initialize MarkItDown converter
converter = MarkItDown()
# Convert the file
result = converter.convert_local(
temp_path,
file_extension=file_extension
)
# Prepare response
response_data = {
"title": result.title,
"content": result.text_content
}
return func.HttpResponse(
body=json.dumps(response_data),
mimetype="application/json",
status_code=200
)
except FileConversionException as e:
return func.HttpResponse(
json.dumps({
"error": f"File conversion error: {str(e)}"
}),
mimetype="application/json",
status_code=422
)
except UnsupportedFormatException as e:
return func.HttpResponse(
json.dumps({
"error": f"Unsupported format: {str(e)}"
}),
mimetype="application/json",
status_code=415
)
except Exception as e:
logging.error(f"Error converting file: {str(e)}")
return func.HttpResponse(
json.dumps({
"error": f"Internal server error: {str(e)}"
}),
mimetype="application/json",
status_code=500
)
finally:
# Clean up temporary file
try:
os.unlink(temp_path)
except:
pass
except Exception as e:
logging.error(f"Error processing request: {str(e)}")
return func.HttpResponse(
json.dumps({
"error": f"Internal server error: {str(e)}"
}),
mimetype="application/json",
status_code=500
)
認証はFunction
レベルとなっています。
こちらをAzure Functions
としてデプロイすると、Power Automate
経由でMarkItDownが使えます。
Power Automate フロー
- Power Apps がフローを呼び出したとき (V2) - SharePoint ドキュメントライブラリのファイル識別子を受け取る
- ファイル コンテンツの取得
-
作成
アクション - base64関数でファイル コンテンツ
の文字列の base64 エンコード バージョンに変換 -
HTTP
- Azure Functionsを実行 -
Power Apps
に値を返す
前述のとおり、Power Appsからファイル識別子を受け取っています。
こちらからSharePoint
のファイル コンテンツを取得します。
base64関数で取得したファイルコンテンツを、文字列の base64 エンコード バージョンに変換します。これにより、Azure Functions
に渡す準備が整います。
@{base64(outputs('ファイル_コンテンツの取得')?['body'])}
HTTP - Azure Functionsを実行
URI
はデプロイしたリソースから取得します。
今回は環境変数
で値を設定しています。
@{parameters('AZURE_FUNCTION_ENDPOINT (gatsuo_AZURE_FUNCTION_ENDPOINT)')}?code=@{parameters('AZURE_SECRET (gatsuo_AZURE_SECRET)')}
Method POST
{
"Content-Type": "application/json"
}
{
"file": "@{outputs('作成_base64')}"
}
設定から入出力
をマスキングすることも可能です。
こちらを実行した結果がcontent
として取得できます。
@{body('HTTP')?['content']}
おわりに
構造化された文字列になるとAI
活用の幅が広がります。
「Copilot活用のためにAzure Functionsを使いたいです!」は市民開発の立ち位置から打ち手として首を傾げるところですが、こういった技術が普及することにより、ファイルの形式関係なくAI
との対話ができそうなことにワクワクしますね。
今回もブログを読んでいただき、ありがとうございました!