「AIに自動化できる業務があるはずなのに、何から始めればいいかわからない」
そんな声を中小企業の経営者から聞くたびに、ここに大きなビジネス機会があると感じます。実際、中小企業の業務自動化市場は広大ですが、既製品のSaaSでは業種固有の要件に対応しきれない領域がまだたくさん残っています。
Gemini 2.5 Pro のFunction Callingは、そのギャップを埋めるために理想的なツールです。構造化された関数呼び出しによって、AIが外部システムと連携しながら複数ステップの業務を自動化できます。
Gemini 2.5 ProのFunction Callingを使ってB2B業務自動化SaaSを構築し、実際に収益を上げるまでの全プロセスを順を追って整理していきます。
Function Callingとは何か:B2B自動化における役割
Function Calling(関数呼び出し)は、GeminiにPythonなどの関数を定義して渡すことで、AIが適切なタイミングで外部システムのAPIを呼び出せるようにする機能です。
単なるテキスト生成と何が違うか、具体的に見てみましょう。
import google.generativeai as genai
import json
from datetime import datetime
genai.configure(api_key="YOUR_GEMINI_API_KEY")
# 関数定義(Geminiが使える「ツール」を宣言)
tools = [
{
"function_declarations": [
{
"name": "get_inventory_status",
"description": "指定した商品の在庫数と入荷予定を取得する",
"parameters": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "商品ID(SKU)"
},
"warehouse_id": {
"type": "string",
"description": "倉庫ID(省略可)"
}
},
"required": ["product_id"]
}
},
{
"name": "create_purchase_order",
"description": "発注書を作成してERP系システムに送信する",
"parameters": {
"type": "object",
"properties": {
"product_id": {"type": "string"},
"quantity": {"type": "integer"},
"supplier_id": {"type": "string"},
"delivery_date": {"type": "string", "description": "YYYY-MM-DD形式"}
},
"required": ["product_id", "quantity", "supplier_id"]
}
}
]
}
]
def get_inventory_status(product_id: str, warehouse_id: str = None) -> dict:
"""実際の在庫システムに問い合わせる(実装例)"""
# 実際にはERPのAPIを呼び出す
return {
"product_id": product_id,
"current_stock": 45,
"reorder_point": 30,
"next_delivery": "2026-05-10",
"supplier_lead_time_days": 7
}
def create_purchase_order(product_id: str, quantity: int,
supplier_id: str, delivery_date: str = None) -> dict:
"""発注書を作成する(実装例)"""
order_id = f"PO-{datetime.now().strftime('%Y%m%d%H%M%S')}"
# 実際にはERPのAPIで発注書を作成する
return {
"order_id": order_id,
"status": "created",
"product_id": product_id,
"quantity": quantity,
"estimated_delivery": delivery_date or "2026-05-15"
}
# Geminiに業務タスクを指示する
model = genai.GenerativeModel("gemini-2.5-pro-preview-05-06", tools=tools)
def run_inventory_automation(user_request: str) -> str:
"""在庫管理の自動化エージェントを実行する"""
chat = model.start_chat()
# ユーザーの自然言語指示をGeminiに渡す
response = chat.send_message(user_request)
# Geminiが関数呼び出しを要求した場合
while response.candidates[0].content.parts[0].function_call.name:
fc = response.candidates[0].content.parts[0].function_call
# 関数を実際に実行する
if fc.name == "get_inventory_status":
result = get_inventory_status(**dict(fc.args))
elif fc.name == "create_purchase_order":
result = create_purchase_order(**dict(fc.args))
else:
result = {"error": "Unknown function"}
# 結果をGeminiに返す
response = chat.send_message(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=fc.name,
response={"result": json.dumps(result, ensure_ascii=False)}
)
)
)
# テキスト回答が出たらループ終了
if not hasattr(response.candidates[0].content.parts[0], 'function_call'):
break
return response.text
# 実行例
result = run_inventory_automation(
"SKU-00123の在庫を確認して、在庫切れの懸念があれば自動で発注書を作成してください"
)
print(result)
# 例: 「SKU-00123の在庫は45個です。再注文点(30個)を上回っていますが、
# 7日のリードタイムと現在の消費ペースから見ると、次の入荷前に
# 在庫切れになる可能性があります。安全在庫として50個の発注書を
# 作成しました。発注番号: PO-20260505143022」この「自然言語でタスクを指示→AIが必要な関数を判断して実行→構造化された結果を返す」という流れが、B2B業務自動化の核心です。
B2B受注につながる「業務課題の見つけ方」
Function Callingで何が作れるかより重要なのは、何を作るかの選定です。私が中小企業の業務自動化案件で繰り返し見てきた「収益になりやすい領域」を整理します。
収益になりやすい業務自動化の特徴:
- 毎日・毎週繰り返す定型処理(頻度が高い)
- 複数のシステムをまたぐデータ転記(人手が介在している)
- ルールが明確だが複雑なケース分岐がある(AIが得意)
- ミスが起きると損失が大きい(価値訴求しやすい)
業種別・高需要の自動化ユースケース:
【製造業・卸売業】
- 在庫アラート & 発注自動化(在庫量→発注判断→発注書作成)
- 見積書自動生成(材料コスト + 工数 → 見積書PDF)
- 品質検査レポート集計・異常検知
【不動産業】
- 内見希望の自動調整・スケジュール確認
- 物件情報更新の自動同期(ポータルサイト間)
- 家賃督促フロー自動化(滞納判定→メール→報告)
【サービス業・士業】
- 顧客対応メール自動分類 & 担当者振り分け
- 請求書・領収書の自動作成・送付
- 月次レポート自動生成(データ取得→分析→PDF出力)
見つけ方は、ターゲット業種の「業務マニュアル」を読むのが最も効果的です。工程が細かく書いてあるほど、自動化の余地があります。
プロダクト設計:3つの自動化エンジンの実装
実際のB2B SaaSでは、複数の自動化ユースケースを組み合わせます。以下は在庫・見積・レポートの3エンジン構成の実装例です。
# automation_engine.py - B2B業務自動化の中核エンジン
import google.generativeai as genai
from typing import Any, Callable
import logging
logger = logging.getLogger(__name__)
class B2BAutomationEngine:
"""Gemini Function Callingベースの業務自動化エンジン"""
def __init__(self, api_key: str, tools: list, handlers: dict[str, Callable]):
"""
Args:
api_key: Gemini API キー
tools: Function Callingの定義リスト
handlers: 関数名→実装関数のマッピング
"""
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel("gemini-2.5-pro-preview-05-06", tools=tools)
self.handlers = handlers
self.max_iterations = 10 # 無限ループ防止
def run_task(self, instruction: str, context: dict = None) -> dict:
"""
自然言語の指示を受け取り、Function Callingで実行して結果を返す
Returns:
{"success": bool, "result": str, "actions_taken": list}
"""
context_str = f"\n\n追加情報: {context}" if context else ""
chat = self.model.start_chat()
response = chat.send_message(instruction + context_str)
actions_taken = []
iterations = 0
while iterations < self.max_iterations:
iterations += 1
# テキスト回答が来たら終了
if not response.candidates[0].content.parts:
break
part = response.candidates[0].content.parts[0]
if not hasattr(part, 'function_call') or not part.function_call.name:
break
# 関数呼び出しを実行
fc = part.function_call
func_name = fc.name
func_args = dict(fc.args)
logger.info(f"Calling function: {func_name}({func_args})")
if func_name not in self.handlers:
result = {"error": f"Function {func_name} not found"}
else:
try:
result = self.handlers[func_name](**func_args)
actions_taken.append({
"function": func_name,
"args": func_args,
"result": result
})
except Exception as e:
result = {"error": str(e)}
logger.error(f"Function {func_name} failed: {e}")
# 結果をGeminiに返す
import json
response = chat.send_message(
genai.protos.Part(
function_response=genai.protos.FunctionResponse(
name=func_name,
response={"result": json.dumps(result, ensure_ascii=False)}
)
)
)
final_text = ""
for part in response.candidates[0].content.parts:
if hasattr(part, 'text'):
final_text += part.text
return {
"success": True,
"result": final_text,
"actions_taken": actions_taken
}このエンジンを使い、クライアントごとに異なるFunction定義を注入することでマルチテナント対応が可能になります。
マルチテナント設計と課金の実装
複数クライアントに同じSaaSを提供するには、テナント分離と使用量課金の仕組みが必要です。
# tenant_manager.py - マルチテナント管理
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
import uuid
@dataclass
class TenantConfig:
"""テナント(クライアント企業)の設定"""
tenant_id: str
company_name: str
plan: str # "starter" | "growth" | "enterprise"
monthly_automation_limit: int
enabled_functions: list[str]
api_integrations: dict # 外部APIの認証情報
created_at: datetime = field(default_factory=datetime.now)
@dataclass
class UsageRecord:
"""使用量の記録"""
tenant_id: str
month: str # YYYY-MM
automations_run: int = 0
gemini_tokens_used: int = 0
successful_automations: int = 0
class TenantManager:
"""テナント管理とアクセス制御"""
PLANS = {
"starter": {
"monthly_automations": 500,
"price_jpy": 50_000,
"max_functions": 5
},
"growth": {
"monthly_automations": 3_000,
"price_jpy": 150_000,
"max_functions": 15
},
"enterprise": {
"monthly_automations": 20_000,
"price_jpy": 400_000,
"max_functions": -1 # 無制限
}
}
def __init__(self):
self.tenants: dict[str, TenantConfig] = {}
self.usage: dict[str, UsageRecord] = {}
def onboard_tenant(self, company_name: str, plan: str) -> TenantConfig:
"""新規クライアントのオンボーディング"""
tenant_id = str(uuid.uuid4())[:8]
plan_config = self.PLANS[plan]
config = TenantConfig(
tenant_id=tenant_id,
company_name=company_name,
plan=plan,
monthly_automation_limit=plan_config["monthly_automations"],
enabled_functions=[],
api_integrations={}
)
self.tenants[tenant_id] = config
return config
def check_and_record_usage(self, tenant_id: str) -> bool:
"""使用量チェック(上限超過時はFalse)"""
month = datetime.now().strftime("%Y-%m")
key = f"{tenant_id}:{month}"
if key not in self.usage:
self.usage[key] = UsageRecord(tenant_id=tenant_id, month=month)
record = self.usage[key]
tenant = self.tenants.get(tenant_id)
if not tenant:
return False
if record.automations_run >= tenant.monthly_automation_limit:
return False # 上限超過
record.automations_run += 1
return True
def get_monthly_invoice_data(self, tenant_id: str) -> dict:
"""月次請求データを生成する"""
month = datetime.now().strftime("%Y-%m")
key = f"{tenant_id}:{month}"
record = self.usage.get(key, UsageRecord(tenant_id=tenant_id, month=month))
tenant = self.tenants.get(tenant_id)
plan = tenant.plan if tenant else "starter"
plan_price = self.PLANS[plan]["price_jpy"]
overage = max(0, record.automations_run - self.PLANS[plan]["monthly_automations"])
overage_charge = overage * 15 # ¥15/実行(超過分)
return {
"tenant_id": tenant_id,
"company_name": tenant.company_name if tenant else "Unknown",
"plan": plan,
"base_price": plan_price,
"automations_run": record.automations_run,
"overage_automations": overage,
"overage_charge": overage_charge,
"total": plan_price + overage_charge
}よくある落とし穴:B2B自動化SaaSで失敗しないために
落とし穴1: Function Callingのエラーハンドリングを怠る
外部APIはたびたびダウンします。エラー時にGeminiが「わかりません」と応答するだけでは、クライアントの業務が止まります。必ず再試行ロジックと人間へのエスカレーションを実装してください。
import time
from functools import wraps
def with_retry(max_retries: int = 3, delay_seconds: float = 2.0):
"""外部API呼び出しに自動リトライを追加するデコレータ"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
last_error = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
last_error = e
if attempt < max_retries - 1:
time.sleep(delay_seconds * (2 ** attempt)) # 指数バックオフ
logger.warning(f"Retry {attempt + 1}/{max_retries}: {e}")
# 全リトライ失敗時 → 人間にエスカレーション
notify_human_operator(
f"自動化処理が失敗しました: {func.__name__}, エラー: {last_error}"
)
raise Exception(f"All retries failed: {last_error}")
return wrapper
return decorator
def notify_human_operator(message: str):
"""担当者にSlackやメールで通知する"""
# 実際の実装: Slack webhook / メール送信
logger.critical(f"HUMAN INTERVENTION REQUIRED: {message}")落とし穴2: 同じ自動化を2回実行させてしまう(冪等性の欠如)
「昨日の発注が実は2重になっていた」はB2B SaaSで最も深刻なバグです。Function定義の冪等性を必ず確保してください。
def create_purchase_order_idempotent(
product_id: str,
quantity: int,
supplier_id: str,
idempotency_key: str # クライアントが生成するユニークキー
) -> dict:
"""
冪等な発注書作成 — 同じidempotency_keyで2回呼ばれても1回しか発注しない
"""
existing = db.get_order_by_idempotency_key(idempotency_key)
if existing:
return {"status": "already_exists", "order_id": existing.order_id}
# 新規作成
order = db.create_order(product_id, quantity, supplier_id, idempotency_key)
return {"status": "created", "order_id": order.id}落とし穴3: トークンコストの見積もりが甘い
Gemini 2.5 Proは高性能ですが、複雑な業務自動化では1タスクあたり5,000〜20,000トークンを消費することがあります。1テナント・月3,000タスクで最大6,000万トークン——コスト計算をしっかり行い、プラン価格に反映させてください。
初契約獲得の実践営業フロー
ステップ1: 「業務の棚卸しミーティング」として提案する
「AIを導入したいですか?」より「毎週3時間以上かかっている業務を教えてください」の方が、決断者が乗ってきます。
ステップ2: 1週間でPoC(概念実証)を作る
相手の業務1つに絞り、実際に動くデモを作って見せます。完璧でなくていい。「こういうことが自動化できる」という体験を提供することが目的です。
ステップ3: 費用対効果の数字で話す
提案書のROI計算例:
現状:
手作業で在庫確認・発注 → 担当者3名 × 2時間/日 = 6人時/日
月稼働20日 × 人件費¥2,500/時 = ¥300,000/月
自動化後:
Gemini自動化システム → 担当者確認5分/日 = 0.25人時/日
月稼働20日 = ¥12,500/月(人件費)
削減額: ¥287,500/月
システム月額: ¥150,000/月(Growthプラン)
純利益: ¥137,500/月
ROI: 2ヶ月で初期費用回収(初期設定費¥300,000の場合)
この数字を見せれば、「高い」とはなりません。
6ヶ月間でMRR300万円を達成した開発者の共通点は、「技術を売るのではなく、コスト削減を売る」という意識の転換でした。Gemini 2.5 ProのFunction Callingは、その転換を支える強力な基盤です。
まず身近な業種で「この作業、自動化できそうだな」と感じる業務を見つけ、1週間でPoCを作ることから始めてみてください。