
AIエージェントにOfficeファイルを触らせようとすると、思いのほか早い段階で詰まります。
COMは環境を選ぶ、python-docxは書式が吹き飛ぶ、LibreOfficeはコンテナに乗せると重い。
そういう経験が積み重なって「もっとシンプルな方法はないか」と探している方に向けて、 OfficeCLI を実務の文脈で掘り下げます。
セットアップから基本コマンド、エージェントへの組み込みパターン、そして「使えないケース」まで一通り見ていきます。
COMもpython-docxも、エージェントに渡すと壊れる
COMオートメーションは、Windowsマシンに正規ライセンスのOfficeが入っていれば確かに強力です。
ExcelのVBAをPythonから叩けるし、書式も数式も完全に扱えます。
ただ、エージェント連携の文脈で使おうとすると、すぐに壁にぶつかります。
Dockerコンテナの中では動きません。
COMはWindowsのレジストリとOfficeのCOMサーバーに依存しているので、Linuxベースのコンテナとは根本的に相性が悪い。
CI/CDをLinux環境で回しているプロジェクトでは、そもそも選択肢に入りません。
仮にWindowsのCIランナーを用意したとしても、今度はプロセス管理が厄介です。
ExcelのCOMプロセスが正常に終了しないまま残り続けて、次のジョブでファイルロックが発生する。
「なぜか2回目の実行から失敗する」という現象に悩まされた経験がある方は少なくないはずです。
エラーハンドリングを丁寧に書いても、プロセスの後始末は完全にはコントロールしきれません。
python-docxやopenpyxlは、環境依存の問題がない分だけ扱いやすいです。
LinuxでもMacでも動くし、Officeのインストールも不要。
シンプルな帳票生成やデータ抽出なら十分に機能します。
ただ、エージェントに渡す道具として見たときに、別の問題が出てきます。
これらのライブラリは 低レベルのDOM操作 を前提に設計されています。
「段落オブジェクトを取得して、runを走査して、テキストを抽出する」という手順をコードで書く必要があって、エージェントが自然言語の指示から操作に変換するには間接的すぎます。
さらに、複雑な書式が入ったファイルを読み込むと、スタイル情報が欠落したり、セル結合やネストしたテーブルが正しく再現されなかったりします。
出力もPythonオブジェクトなので、LLMに渡すには自前でシリアライズする処理が必要になります。
LibreOffice headlessはPDF変換やレンダリング系のタスクは得意ですが、コンテナに乗せると起動コストが無視できません。
初期化に数秒かかることがあって、エージェントが頻繁に呼び出すツールとしては重たい。
イメージサイズも大きくなりがちで、CI環境のビルド時間に影響します。
「動く」と「エージェントに安全に渡せる」は、実は別の話です。
COMもpython-docxも、単体のスクリプトとして使う分には問題ありません。
でも、エージェントが自律的に呼び出すツールとして使うには、環境依存・プロセス管理・出力の構造化という3つの問題を同時にクリアする必要があります。
そこに対して別のアプローチを提供しているのが OfficeCLI です。
OfficeCLIのセットアップと基本コマンド:最短で動かすまで
OfficeCLIは単一バイナリで配布されています。
Officeのインストールは不要で、LinuxでもmacOSでもWindowsでも同じバイナリが動きます。
Dockerfileへの組み込みも、リリースページからバイナリを取得する1行で済みます。
# GitHubリリースからバイナリを取得
curl -L https://github.com/iOfficeAI/OfficeCLI/releases/latest/download/officecli-linux-x64 \
-o officecli && chmod +x officecli
# パスの通った場所に移動(任意)
sudo mv officecli /usr/local/bin/
# 動作確認
officecli --version
Officeライセンスの調達もCOMサーバーの設定も不要なので、CI環境への導入コストが大幅に下がります。
基本的なコマンド体系は、操作の種類(view / get / query / set / create)とファイルパスの組み合わせです。
まずWordファイルの内容を確認してみます。
# Wordファイルのテキストをアウトライン形式で表示
officecli view report.docx --mode outline
# JSON形式で構造化出力(LLMに渡しやすい)
officecli view report.docx --mode text --json
--json フラグ をつけると出力がJSON形式になります。
LLMのコンテキストに直接流し込める構造になっているので、エージェントがパースする手間がありません。
これがpython-docxとの大きな違いで、「エージェントに渡すことを前提に設計されている」という点がよく表れています。
ExcelとPowerPointも同じ体系で操作できます。
# 条件付きでセルを絞り込む(CSSセレクタ的な記法)
officecli query data.xlsx "row[Salary>5000]" --json
# セルの値を書き換える
officecli set data.xlsx "Sheet1/B2" --value "更新後の値"
# スライドの特定シェイプのテキストを書き換える
officecli set deck.pptx "slide[2]/shape[title]" --value "新しいタイトル"
# 新規ファイルの作成(拡張子でファイルタイプを判定)
officecli create output.pptx
queryコマンドの記法が特徴的です。
「Salaryが5000を超える行」のような条件を自然な形で書けるので、エージェントが「条件に合うレコードを取得して」という指示を受けたときに、そのままコマンドに変換しやすい。
view・get・query・setという4つのコマンドを覚えれば、Word・Excel・PowerPointの基本的な操作はほぼカバーできます。
フォーマットごとに別々のAPIを覚えさせる必要がないのは、エージェントに教えるスキルとして重要なポイントです。
AIエージェントからOfficeCLIを呼び出す最小構成
エージェントとの連携には、大きく2つのアプローチがあります。
ひとつは、OfficeCLIに内蔵されているMCPサーバーとして起動する方法です。
Claude CodeやCursorを使っているなら、これが一番手っ取り早い。
# Claude Codeに登録
officecli install claude
# Cursorに登録
officecli install cursor
# MCPサーバーとして手動起動
officecli mcp
installコマンドを実行すると、OfficeCLIが既知の設定ディレクトリを自動検出してMCPクライアントに登録します。
JSON-RPCでドキュメント操作ツールが公開されるので、Claude CodeやCursorから直接「このExcelファイルのB列の合計を出して」と指示できるようになります。
もうひとつは、function callingやtool useからsubprocessで叩くパターンです。
Claude Code / Cursor以外のフレームワーク(LangChainや独自実装など)を使っている場合は、こちらのほうが汎用性があります。
import json
import subprocess
from openai import OpenAI
client = OpenAI()
def run_officecli(args: list[str]) -> dict:
result = subprocess.run(
["officecli"] + args + ["--json"],
capture_output=True,
text=True,
check=True,
)
return json.loads(result.stdout)
tools = [
{
"type": "function",
"function": {
"name": "read_office_file",
"description": "Word・Excel・PowerPointファイルの内容を読み取る",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "読み取るファイルのパス(作業ディレクトリ内のみ許可)",
},
"mode": {
"type": "string",
"enum": ["text", "outline", "stats"],
"description": "表示モード",
},
},
"required": ["file_path"],
},
},
},
]
def dispatch_tool(name: str, args: dict) -> str:
# パス制限:作業ディレクトリ外へのアクセスを防ぐ
work_dir = "/app/workspace"
file_path = args["file_path"]
if not os.path.realpath(file_path).startswith(os.path.realpath(work_dir)):
return json.dumps({"error": "作業ディレクトリ外へのアクセスは許可されていません"})
if name == "read_office_file":
mode = args.get("mode", "text")
return json.dumps(run_officecli(["view", file_path, "--mode", mode]))
この実装で重要なのは パス制限の部分 です。
エージェントが生成したファイルパスをそのままsubprocessに渡すと、作業ディレクトリ外のファイルを読み書きされるリスクがあります。
os.path.realpathで正規化してからチェックすることで、シンボリックリンクを使ったパストラバーサルも防げます。
複数の操作をまとめて実行したい場合は、batchコマンドが便利です。
subprocessの呼び出し回数を減らせるので、エージェントが複数ステップの操作を一度に処理できます。
officecli batch report.xlsx --commands '[
{"cmd": "set", "selector": "Sheet1/B2", "value": "1200"},
{"cmd": "set", "selector": "Sheet1/B3", "value": "980"},
{"cmd": "set", "selector": "Sheet1/B4", "value": "1540"}
]'
Claude Code / Cursorを使っているならinstallコマンド1行で終わるMCPアプローチが速い。
それ以外のフレームワークや独自パイプラインで使うなら、subprocessを薄くラップするfunction callingパターンが汎用的です。
使えるケース・使えないケース:導入前に確認しておきたいこと
導入を判断する前に、対応範囲と制約を整理しておきます。
- 対応フォーマット:.docx / .xlsx / .pptx が主軸。.doc や .hwpx などの旧形式・独自形式はプラグインで拡張できるが、デフォルトでは非対応
- マクロ(VBA)の実行:非対応。OfficeCLIはOpenXMLの構造を直接操作するツールなので、VBAの実行エンジンは持っていない
- 数式の再計算:Excelバックエンドがないため、セルの値を書き換えても数式は自動再計算されない。refreshコマンドで部分的に対応できるが、完全ではない
- パス制限:エージェントにファイル操作を渡す場合、作業ディレクトリを明示的に制限しないと任意パスへの書き込みが可能になる。前節のコード例のように、ツール定義の中でパスを検証する処理を必ず入れること
- python-docx / openpyxlとの使い分け:段落やセルを細かくプログラムで制御したい場合は低レベルライブラリのほうが向いている。エージェントに自然言語で指示して操作させたい・CI/コンテナで動かしたい場合はOfficeCLIが強い
「python-docxで十分なケース」は確かにあります。
決まったテンプレートに値を埋め込むだけの帳票生成や、セル単位でロジックを組む集計処理は、低レベルライブラリのほうがコントロールしやすい。
OfficeCLIはあくまで「エージェントが自律的にファイルを読み書きする」という文脈で力を発揮するツールです。
向いているプロジェクトを一言で言うなら、「Officeファイルを扱う処理をLinux/Dockerで動かしたい」「エージェントに自然言語でOffice操作を指示したい」「COMの環境依存に疲れた」という3つのどれかに当てはまるケースです。
逆に、Windowsオンリーの環境でVBAと連携する必要がある、あるいは数式の再計算結果をリアルタイムで取得したいという要件があるなら、COMオートメーションのほうが現実的な選択です。
OfficeCLIは Apache 2.0ライセンスのオープンソース で公開されています。
まずローカルでバイナリを取得して、手元のdocxやxlsxに対してviewとgetを試してみるのが一番早い判断材料になります。
株式会社ホコサキは、山口県宇部を拠点にAI活用支援・業務システム開発・DX推進に取り組んでいます。
OfficeCLIのようなエージェント連携の実装支援や、既存業務へのAI組み込みについてご相談があれば、お気軽にどうぞ。
詳しくは お問い合わせページ からご連絡ください。

