対話してたら完成した。Gemini Enterprise の OAuth 対応 HubSpot エージェントを Vibe コーディングで作成した話

対話してたら完成した。Gemini Enterprise の OAuth 対応 HubSpot エージェントを Vibe コーディングで作成した話

2026.01.20

はじめに

菅野です。
HubSpot の API を使って情報を取得する Gemini Enterprise のエージェントを Antigravity で 作成してみました。
その時に得た知見を言語化しておきます。

使用したツール

  • Antigravity
    • 次世代AI統合開発環境(IDE)で、開発タスク(計画・実行・検証)全体を AI エージェントが自律的に実行してくれます
    • 言語モデル毎に使用制限がありますので、ちょっとした質問であれば他のツールを使うなど節約しましょう。有料プランに加入していれば5時間毎に制限が回復します。
    • インストール中のオプション設定で「Antigravity が何かコマンドを実行する時に必ず確認する」を選択しましょう。
  • uv
    • python で作成するのでパッケージ管理を考えると必須だと思ってます。インストールさえしてあれば Antigravity が使ってくれるのでインストールしておきましょう。

その他前提条件

  • Google Cloud プロジェクトが作成済み
  • gcloud CLI がインストール・認証済み
  • HubSpot アカウント(開発者アカウント推奨)
  • Python 3.13.10 がインストール可能な環境

Antigravity の設定

最初に行った作業は Antigravity の日本語化と「Customizations」によるエージェントへのルール設定です。

  • 日本語対応の拡張機能をインストール
  • LLM への共通ルール設定
    • エージェントとのチャットが右側にあり、一番上にある3点リーダ「・・・」をクリックし「Customizations」をクリック、ここでルールを作成します。
    • 私が入力したルールは以下となります。
【最重要ルール:言語設定】
- ユーザーとの会話、応答、通知メッセージは **必ず日本語** で行ってください。
- 作成するドキュメント(README等)および、Implementation Plan や Walkthrough などのAI生成アーティファクトを含め、全ての出力は必ず日本語で記述してください。
- 英語の使用は、コード、ファイル名、コマンド、固有名詞のみに限定してください。

【開発環境・ツール設定】
- **Python**: デフォルトは `3.13.10` としてください(4.0未満の指定も許容)。
- **UV**: パッケージ管理には積極的に `uv` を使用してください。
- **requirements.txt作成時**: 必ず `uv export --format requirements-txt --no-hashes` コマンドを使用してください。

【Google Cloud設定】
- Google Cloud プロジェクト名が不明な場合はユーザーに確認してください。
- **リージョン設定**:
  - ADK (Agent Engine) 関連: `us-central1`
  - それ以外: `asia-northeast1` (東京)

作成準備

プロジェクトのフォルダを作成します。
フォルダを作成したら Antigravity のメニューからこのフォルダを開く、それだけです。

作成開始

皆さんに共有するプロンプトはこのブログの最後に書きますが、最初のプロンプトはこんな内容です(このブログ記事内では Google Cloud のプロジェクト名は「your-project」としています)

ADK を使って Agent を作成するので、このフォルダで初期設定して。
エージェントの名前は「hubspot_agent」
エージェントは HubSpot に OAuth で接続して自然言語で指定した情報を取得する機能とします
HubSpot へのアクセスに使うライブラリは公式のものを使い、Python 以外の言語であれば Python で作成して利用してください
HubSpot 側のスコープは末尾に「.read」とついているものを付与しているので、可能な限りの情報を取得できるエージェントとしてください
Google Cloud のプロジェクトIDは「your-project」
使用するリージョンは「us-central1」
Python のバージョンは「3.13.10」

この指示だけで「uv init」「uv add google-adk hubspot-api-client」「uv run adk create」などのコマンドが実行され、エージェント作成に必要な初期化と実行計画の作成が行われました。
実行計画の承認後、コマンド実行毎に確認してくれるので内容を確認して許可ボタンをクリックすると開発が進みます。
Antigravity は作成だけじゃなく自分で判断してテスト・デバッグ・修正までやってくれるので、何回かの許可だけで作成が完了します。

OAuth の設定

OAuth を使った HubSpot 対応 Gemini Enterprise エージェントを稼働させるためには、以下の対応が必要になります。

HubSpot での作業

  • HubSpot で公開アプリを作成する
  • HubSpot アプリの画面でスコープ(権限)を決める
    • 選択したスコープは全て「必須」とする
  • HubSpot のアプリ画面から以下をコピーしておく
    • クライアントID
    • クライアントシークレット
    • インストールURLのサンプル(OAuth)
      • この URL に必須のスコープが含まれています

ローカル での作業(1)

  • 「adk deploy」により未完成のエージェントを Google プロジェクトにデプロイ
    • エラーにならなければエージェントは完成していなくてもOKです

Google Cloud での作業

  • Gemini Enterprise のアプリを作成する
    • アプリの ID をコピーしておいてください
    • 認証 ID を取得するコマンドで必要になります(GEMINI_ENTERPRISE_APP_ID の部分)
  • Gemini Enterprise のアプリにてエージェントを作成する

ローカル での作業(2)

  • 作成したエージェントの情報を取得するために以下のコマンドを実行し、「認証 ID」を取得する
    • 認証 ID は以下コマンドで返ってきた JSON の「toolAuthorizations」に記載されている最後の部分です(以下の例で言えば「aaa_123456」の部分)
    • 例:projects/<PROJECT_NO>/locations/global/authorizations/aaa_123456
# 認証 ID 取得コマンドの実行例
# ⚠️ 注意: `<PROJECT_ID>` と `<GEMINI_ENTERPRISE_APP_ID>` は実際の値に置き換えてください
curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: <PROJECT_ID>" \
"https://discoveryengine.googleapis.com/v1alpha/projects/<PROJECT_ID>/locations/global/collections/default_collection/engines/<GEMINI_ENTERPRISE_APP_ID>/assistants/default_assistant/agents"
  • 取得した「認証 ID」を「.env」ファイルに記入する

追加で依頼した内容

完成までにいくつか指示を追加したので、それらを書いておきます。

  • 「以下のブログ記事を参考に OAuth 部分を実装して」
  • 「requirements.txt を agent.py と同じフォルダ内に作成して」
    • デプロイ時に「adkのライブラリが見つからない」旨のエラーが出たための対応です
  • 「HubSpot では以下の権限を付与しています。全てを利用するように機能を追加してください」
  • 「README.md ファイルを日本語で作成して」
  • 「requirements.txt の中に不要なものは残ってない?」
  • 「0の状態からこの状態まで作成してもらうために必要な指示文を教えて」
    • reproduction_prompt.txt が作成されました

AI エージェントは質問に対し柔軟な対応が必要

一度完成したエージェントを営業メンバーに使ってもらったのですが、「最近アクティビティのあった顧客を教えて」「直近の案件を10件表示して」といった質問を投げかけては回答が得られませんでした。
横で見ている私もこれでは使い物にならないと感じるほどのひどい状況です。

このエージェントが HubSpot の単体の API を使った橋渡ししかしていないために「複数の情報の集約が必要な質問」へ対応できないために発生しています。
私たちが AI エージェントに望んでるのはこのような振る舞いではなく、こちらの意図を汲み取ってどういった情報が必要なのかを判断し、複数の情報を組み合わせて回答するといった柔軟な対応です。

改善のために「最近アクティビティのあった顧客を教えて、という質問に対応して」と依頼し実装してもらった結果、その質問には回答してもらえるようになりました。
ですが「最近アクティビティのあった担当者をリストアップして」と少しだけ違う質問には対応できません。

新しいパターンが生まれる度にこれを続けるのは不毛なので、 「このエージェントを使う人は色々な複合条件で質問すると思いますが、毎回機能の追加をお願いするのは大変です。エージェントが質問を解釈して必要な情報は何かを考えて取得し、必要な情報をまとめてから回答するような実装にはなりませんか?」 と相談したところ「人間が都度実装していくのは非効率的ですし、AI エージェントの本来の利点である「自律的な判断と行動」を活かしきれていません。」と言って修正してくれました。

何度かこういったフィードバックし、Antigravity が実装することで初期リリースより格段に柔軟なエージェントとなりました。

作成されたファイルの構成

.
├── .python-version
├── README.md
├── hubspot_agent/
│   ├── .env
│   ├── __init__.py
│   ├── agent.py
│   ├── requirements.txt
│   └── tools.py
├── pyproject.toml
├── reproduction_prompt.txt
└─ uv.lock
  • .python-version
    • Python バージョン指定 (3.13.10)
  • hubspot_agent/
    • エージェントのソースコードが含まれるディレクトリ
  • hubspot_agent/agent.py
    • エージェントの定義ファイル
  • hubspot_agent/.env
    • 環境変数(プロジェクトIDやリージョンの情報)
  • hubspot_agent/init.py
    • パッケージ初期化ファイル
  • hubspot_agent/tools.py
    • HubSpot API と連携するための関数群
  • hubspot_agent/requirements.txt
    • エージェント実行に必要なPythonライブラリの依存関係定義ファイル
  • pyproject.toml および uv.lock
    • 依存関係とプロジェクト設定 (uv が使用)
  • reproduction_prompt.txt
    • このプロジェクトを0から作成するためのプロンプト

このエージェントを作成するためのプロンプト

何度も実行してフィードバックを繰り返し完成したソースコードを元に、Antigravity が作成したプロンプトです。
フォルダを作成し、Antigravity でフォルダを開いてからこのプロンプトを流せば作成してくれます。

reproduction_prompt.txt
# 0. AIアシスタントへの重要指示 (Critical Instructions)
**このプロンプトを実行するAIは、以下の言語ルールを厳守してください。違反した場合は再生成が必要です。**

1.  **成果物・対話の言語**:
    - **Implementation Plan (実装計画)**: **必ず日本語**
    - **Walkthrough (手順書)**: **必ず日本語**
    - **README.md**: **必ず日本語**
    - **ユーザーへの回答**: **必ず日本語**
2.  **コード内の言語**:
    - **Docstring**: エージェントの理解のため **英語**
    - **行内コメント (Inline Comments)**: 開発者の理解のため **日本語**
    - **変数名・関数名**: **英語**
3.  **エージェントの思考 (Instruction)**:
    - `agent.py` の `instruction` 引数は **英語**

---

# 目的
Google Cloud ADK (Agent Development Kit) を使用し、HubSpot CRM と連携する AI エージェントプロジェクトを新規作成してください。

# 1. 構成変数 (Configuration)
以下の変数は環境に合わせて変更してください。

| 変数名 | 設定値 | 説明 |
| :--- | :--- | :--- |
| **PROJECT_ID** | `your-project` | Google Cloud プロジェクトID |
| **REGION** | `us-central1` | Google Cloud リージョン |
| **AGENT_NAME** | `hubspot_agent` | エージェント名 (ディレクトリ名) |
| **PYTHON_VERSION** | `3.13.10` | Pythonバージョン |
| **AUTH_VAR_NAME** | `HUBSPOT_AUTH_ID` | OAuth認証IDを格納する環境変数名 |

# 2. プロジェクト初期化
以下の手順でプロジェクトをセットアップしてください。

1.  **プロジェクトルートの初期化**:
    ```bash
    uv init --python <PYTHON_VERSION>
    ```

2.  **依存関係の追加**:
    ```bash
    uv add "google-adk>=1.21.0" "google-cloud-aiplatform>=1.132.0" "hubspot-api-client>=12.0.0" "langchain>=1.2.0" "langchain-google-vertexai>=3.2.0"
    ```

3.  **エージェントのスカフォールド作成 (ADK)**:
    `adk` コマンドを使用して、エージェントのひな形を作成します。`<AGENT_NAME>` ディレクトリが生成されます。
    ```bash
    uv run adk create <AGENT_NAME> --project <PROJECT_ID> --region <REGION>
    ```

# 3. 実装指示

**【重要】言語ルール**:
- **コード内の行内コメント**: 開発者が理解しやすいよう、必ず **日本語** で記述してください。
- **Docstring**: エージェントが理解しやすいよう、原則 **英語** で記述してください。
- **変数名・関数名**: 英語を使用してください。

## A. ツール実装 (`<AGENT_NAME>/tools.py`)
`<AGENT_NAME>` ディレクトリ内に `tools.py` を**新規作成**し、HubSpot API ロジックを実装してください。
**注**: 実装の詳細な解説コメントは **日本語** で記述してください。

- **認証設定 (重要)**:
    - Gemini Enterprise (Vertex AI) の OAuth 機能を使用します。
    - **Auth ID の取得**: `os.environ.get("<AUTH_VAR_NAME>")` を使用して環境変数から取得するように実装してください。
    - **.env ファイル**: ユーザーが認証IDを設定できるように、`<AUTH_VAR_NAME>` を含む `.env` ファイル (または `.env.template`) を作成するか、READMEに設定手順を記載してください。
    - **トークン取得 (`_get_access_token` ヘルパー)**: 関数内で `tool_context.get_token(auth_id)` のように、取得した認証IDを引数として渡してアクセストークンを取得する。失敗時(メソッドが存在しない等)は `tool_context.session.state` からの取得を試みるフォールバックロジックを実装すること。
    - **エラー処理**: トークン取得不可時は `hubspot.crm.objects.exceptions.UnauthorizedException` を発生させる。

- **基本機能要件 (Basic Requirements)**:
    以下の一覧にある**全ての**標準オブジェクト・メタデータに対する `list_` (一覧) および `get_` (詳細) 関数を**省略せずに**網羅的に実装してください。
    - **Objects**: Contacts, Companies, Deals, Products, Quotes, Line Items, Orders, Invoices, Marketing Events
    - **Custom**: Custom Objects (`list_custom_objects`, `get_custom_object_tool`)
    - **Metadata**: Pipelines (`list_pipelines`), Schemas (`list_schemas`)
    - **重要**: "Implement others similarly" のような省略は禁止です。全ての関数を完全に実装してください。
    - 実装においては `hubspot-api-client` の各 `basic_api` や `get_all` メソッドを使用してください。

- **詳細機能要件 (Detailed Requirements)**:
    以下の高度なロジックを実装してください。
    - **エンリッチメント (Helpers)**:
        - `_enrich_with_company_names`: 
            - **Contacts**: Use property `associatedcompanyid`.
            - **Deals**: Use `client.crm.associations.v4.basic_api` (NOT Batch API v3) to fetch associated Company IDs for each deal if batch processing is complex, ensuring robustness.
            - **重要**: V4 APIで関連を取得する際は `object_type="deals"`, `to_object_type="companies"` (複数形) を使用し、`to_object_id` プロパティからIDを抽出する堅牢なロジックを実装すること。
        - `_enrich_with_owner_names`: 
            - オーナーAPIで定義を取得し、`ID` -> `名前` をマッピングする。
            - **重要**: `archived=False` (アクティブ) と `archived=True` (アーカイブ済み/退職者) の両方のオーナーを取得し、過去の案件担当者も解決できるようにすること。
        - `_enrich_with_deal_stages`: `dealstage` IDを人間が読める `dealstage_label`(フェーズ名)にマッピングする。
        - **取引ステージ検索**: `_resolve_deal_stage_id` 関数を実装し、ステージ名(ラベル)の部分一致検索をサポートしてください。
        - 検索ロジックは以下の優先順位で実装すること:
            1. **完全一致** (Exact match)
            2. **前方一致** (例: "C" -> "C: 50%")
            3. **部分一致** (Contains)
        これにより、短い入力でも正確に意図したステージを特定できるようにします。
    - **プロパティ一覧 (`list_properties`)**:
        - `object_type` を受け取り、そのオブジェクトの全プロパティ定義を取得する。
        - 戻り値は `name` (内部名), `label` (表示名), `description` を含む辞書のリストとすること。
        - これにより、エージェントが「地域」や「部署」などのカスタムプロパティの内部名を検索できるようにする。
    - **オーナー検索 (`get_owners` / `list_owners`)**:
        - ページネーション (`after` トークン) を使用して**全オーナー**を取得するループを実装する。
        - 名前 (`firstName`, `lastName`) の部分一致検索機能 (`name` 引数) をクライアントサイドで実装する。
        - **注意**: SDKの `to_dict()` 結果におけるキー名 (`firstName` vs `first_name`) の揺らぎ(スネークケース等)を考慮し、両方のキーをチェックして名前を取得すること。
        - **名前検索ロジック (重要)**:
            - 日本語の姓名順序に対応するため、`First Last` だけでなく `Last First` (姓 名) の順序でもマッチングを行うこと。
            - 名前情報が不完全なケースに備え、検索キーが **Emailアドレス** に部分一致する場合もヒットさせること。
            - **ローマ字自動リトライ**: `agent.py` のインストラクションで「日本語名検索でヒットしない場合、**AI自身がローマ字に変換**して(例:「田中」→ "tanaka")再度 `list_owners` を呼び出す」という振る舞いを規定すること。これにより、追加の実装コストなしでローマ字検索を実現する。
    - **検索 (`search_objects`)**:
        - `search_crm_objects` のラッパーとして実装。
        - `query`(空文字可能)、`sort_by`、`deal_stage`、`owner_id` に加え、**`filter_json`** 引数をサポートする。
        - `filter_json` はハブスポット検索APIのフィルタオブジェクトを含むJSON文字列を受け取り、複雑なAND条件や日付範囲、数値比較を処理できるように実装する。
        - **重要 (フィルタ結合)**: `filter_json` (JSON配列) と引数フィルタ (`deal_stage`等) を結合する際は、**全ての**フィルタグループに対して引数フィルタを追加し、論理的な **AND** 条件を強制すること(デフォルトの FilterGroup 追加では OR になってしまうため)。
        - **重要 (演算子対応)**: `Filter` オブジェクトを構築する際は、`value` だけでなく `values` (IN演算子用) や `high_value` (BETWEEN演算子用) も正しくマッピングすること。
        - 結果リストに対して、会社名・担当者名・ステージ名を自動付与(エンリッチ)して返す。
    - **非アクティブ・停滞案件検索 (`search_inactive_objects`)**:
        - 日付計算やJSON生成の複雑さを排除するため、専用ツールとして実装する。
        - `object_type` (deal, company, contact), `days_inactive` (int) を引数に取る。
        - 内部で `Now - days` のタイムスタンプを計算し、`notes_last_updated` プロパティでフィルタリングを行う(`hs_lastmodifieddate`は使用しない)。
    - **アクティビティ (`get_recent_activities`)**:
        - 引数 `target_type`, `target_id`, `start_date`, `end_date` を受け取る。
        - **日付フィルタ機能**:
            - `start_date`/`end_date` (YYYY-MM-DD) が指定された場合、タイムスタンプ(ミリ秒)に変換し、`hs_createdate` に対する GTE/LTE フィルタを動的に追加する。
            - ユーザーが「今日」「今月」等と指示した場合、**エージェント自身が**具体的な日付 (`YYYY-MM-DD`) を計算して渡すよう、Instructionで規定すること。
        - `target_type="owner"` の場合は、オーナーIDによるフィルタリングを行う(Search API)。
        - その他の `target_type` ("deals", "companies" 等) の場合は、関連オブジェクトによるフィルタリングを行う(Association API V4 + Search API)。
        - **重要**: `Basic API` は使用せず、常に `Search API` を使用すること(Associationモード時はID取得後にSearch)。
        - **重要**: 必ず `hs_createdate` の **降順 (DESCENDING)** でソートすること(デフォルトのID順は不可)。
        - Notes, Calls, Emails等を横断検索し、結果を結合して返す。
        - 担当者名、会社名をエンリッチして返す。
    - **その他**: `get_contact`/`get_deal` も単体取得時にエンリッチを行うこと。
    - **セールスプランニング支援 (Sales Planning Tools)**:
        - `get_company_associated_contacts`: 企業IDから関連コンタクト一覧を取得する。実装には `client.crm.associations.v4.basic_api` を使用すること。
        - `get_company_deals_summary`: 企業に関連する取引の合計金額、オープン件数、ステージ分布等のサマリを計算して返す。
            - 関連取引の取得には `client.crm.associations.v4.basic_api` を使用すること。
            - 戻り値はJSON形式で `{total_amount: float, total_deals: int, open_deals: int, stages: list}` 等を含むこと。
        - ※ 場所検索や期間検索(非アクティブ顧客等)は `search_objects` の `filter_json` で柔軟に対応するため、専用ツールは作成しないこと。

    - **技術的制約 (Technical Constraints)**:
        以下のモジュール・クラス使用を厳守してください(ハルシネーション防止)。
        1.  **ToolContext**: `google.adk.tools.ToolContext` を使用すること。(`google.adk.types` は使用しない)
        2.  **Exceptions**: `hubspot.crm.objects.exceptions.UnauthorizedException` を使用すること。(`hubspot.auth.oauth` は使用しない)
        3.  **Schemas**: `hubspot.crm.schemas.Schema` は存在しないため **インポートしないこと**。
        4.  **Batch API**: バッチ処理が必要な場合は `BatchReadInputSimplePublicObjectId` を使用すること。(`BatchReadInputSimplePublicObject` は誤り)

## B. エージェント定義 (`<AGENT_NAME>/agent.py`)
自動生成された `agent.py` を**修正**してください。

- **ツール登録**: `tools.py` をインポートし、`inspect` モジュールを使用して、`list_`, `get_`, `search_` で始まる関数を `google.adk.tools.FunctionTool` でラップして自動的に登録するロジックを実装してください。
**重要**: `get_recent_activities` も登録対象に含まれるように、プレフィックス判定条件に注意してください(通常 `get_` に含まれます)。
- **構成**: インプロセス実行モデル(MCPサーバーライブラリは使用しない)。
- **インスタンス化**: `root_agent = Agent(...)` としてトップレベルで定義し、エージェントの実体を公開してください(関数でラップしないこと)。
- **インストラクション (Instruction)**:
    `Agent` クラスの `instruction` 引数に設定する内容は、LLMの指示追従性を高めるために**英語**で記述させてください。以下の内容をそのまま使用してください(または同等の内容を含めてください)。

    ```text
    Answer user questions using the provided HubSpot tools.

    ### Core Thinking Process:
    1. **Understand the Goal**: Identify what the user wants (e.g., "High value deals", "Recent contacts").
    2. **Choose the Right Tool**:
       - `search_objects` is the PRIMARY tool.
       - **Dynamic Sorting**: Use it for questions like "newest", "recent", "highest amount".
       - **Optional Query**: You do NOT need a keyword (query) if the user wants "all recent deals" or "top 10 expensive deals". Pass `query=""` in these cases.
       - **Deal Stage Finding**: If user asks for deals in a specific stage (e.g. "Stage B" or "C"), use the `deal_stage` argument. You can pass the Label directly (e.g. `deal_stage='Appointment Scheduled'`) or a partial query (e.g. `deal_stage='C'`). The system will auto-resolve "C" to "C: 50%" etc.
    3. **Enrich Information**:
       - If a list of Deals or Contacts is returned but Company Name is missing and relevant, use `get_company` to fetch details for `associatedcompanyid`.
       - **Response Formatting (Intelligent & Context-Aware)**: 
         - **Limit Strategy**: 
           - Default is `limit=20`.
           - **ALWAYS** increase limit (e.g., `limit=50` or `100`) if the user implies a large result set ("All", "List", "Whole list", "42 items").
         - **Formatting Styles**: Analyze the user's intent to choose the best layout.
           1. **Progress/Status** (e.g., "Pipeline status", "How are deals moving?"):
              - **Group by Deal Stage**. Create Markdown headers for each stage (e.g., `### Qualified to Buy (3)`).
           2. **Team/Balance** (e.g., "Who is busy?", "Deals by owner"):
              - **Group by Owner**.
           3. **Timeline/History** (e.g., "Recent activity", "This month"):
              - **Single Table** sorted by Date (`notes_last_updated` or `closedate`).
           4. **Comparison/High Value** (e.g., "Top deals", "Sort by amount"):
              - **Single Table** sorted by Amount.
           - **Default Format**: Use a **Single Markdown Table**.
           - **Grouping**: Use `### Headers` ONLY if specifically asked (e.g. "Group by Owner").
          - **Readable Names**: 
            - **Deal Stage**: ALWAYS use `dealstage_label`. If missing, capitalize `dealstage` ID.
            - **Company**: Use `company_name`. If missing, check `associatedcompanyid`.
            - **Owner**: Use `owner_name`.
          - **Context**: Always strive to provide complete context (Who, What, When, Where).

    ### Core Workflows:
    1. **Complex Search**: `search_objects` with sorting.
       - **Recent/Newest**: `search_objects(..., query='', sort_by='notes_last_updated')`
       - **High Value**: `search_objects(..., query='', sort_by='amount', sort_direction='DESCENDING')`
       - **Specific Stage**: `search_objects(object_type='deals', deal_stage='Qualified to Buy', ...)`
       - **Date Filtering**: Use sorting to find recent items. For specific months or date ranges, sort by date and filter the output yourself.
    2. **"My" Deals/Contacts**:
       - If user asks for "My deals" or "My tasks", you need the **HubSpot Owner ID**.
       - Step 1: Ask the user for their email address (if not known).
       - Step 2: Use `list_owners` to find their ID.
       - Step 3: Use `search_objects` (query='' to get a broad list, sorted by date).
       - Step 4: Filter the results in your final answer to only show items where `hubspot_owner_id` matches the User's ID.
    3. **Location/Deep Dive**: Use `search_companies_by_location` or `get_company_deals_summary`.
    4. **By Specific Owner (Any Object)**:
       - Example: "Show Tanaka-san's deals".
       - Step 1: **ALWAYS** call `list_owners(name='Tanaka')` first to search for the name.
       - Step 2: **Check Results**:
         - **1 Match**: Use that Owner ID immediately. **DO NOT ask the user for confirmation or email.** Just proceed.
         - **Multiple Matches**: List the names and emails found, and ask "Which one?"
         - **No Match**: 
           - **AUTO-RETRY**: If you searched for a Japanese name (e.g. "田中") and found nothing, **convert it to Romaji (e.g. "tanaka") yourself** and call `list_owners(name='tanaka')` again. Many owners are registered in English.
           - If still no match: Call `list_owners(limit=20)` (without name argument) to display a sample list.
       - Step 3: Once ID is found, call `search_objects(..., owner_id='ID')` or `get_recent_activities(target_type='owner', target_id='ID')`.
    5.  **Stage Ranges & Probabilities**:
       - If the user filters deals by **probability/likelihood** (e.g., "> 50%", "High probability") or a **range of stages** (e.g., "Stages 10%-90%", "Late stage"):
         - **Step 1**: Call `list_pipelines(object_type='deal')` to inspect the pipeline definitions and identify the Stage IDs that match the criteria (e.g., probability >= 0.5).
         - **Step 2**: Extract the list of matching Stage IDs.
         - **Step 3**: Call `search_objects` with `filter_json` using the `IN` operator for `dealstage`.
           - Example: `[{"propertyName": "dealstage", "operator": "IN", "values": ["id_A", "id_B"]}]`

    6.  **Custom Property Search**:
       - If the user asks to search by a specific property name that is not a standard argument (e.g., "Region", "Department"):
         - **Step 1**: Call `list_properties(object_type='deal')` (or 'company'/'contact') to get the list of all properties.
         - **Step 2**: Search the returned list for a property where `label` matches the user's term (fuzzy match or exact).
         - **Step 3**: Get the **internal name** (`name` field) of that property.
         - **Step 4**: Call `search_objects` with `filter_json` using that internal name.
           - Example: `[{"propertyName": "region_code", "operator": "EQ", "value": "AP-01"}]`

    7. **Activities by Context (History/Timeline)**:
       - **By Owner**: `get_recent_activities(target_type='owner', target_id='OWNER_ID')`
       - **By Object (Deal/Company)**: "Show history of this deal"
         - **Step 1**: Identify the Object ID (e.g., Deal ID). Search if needed.
         - **Step 2**: Call `get_recent_activities(target_type='deals', target_id='DEAL_ID')`. (Use 'companies', 'contacts' as needed).

    8. **Date/Time Range Activities**:
       - If user asks for "Today's activities", "Activities this month", "Last week's emails":
         - **Step 1**: Calculate the `start_date` (and `end_date` if needed) based on the current date. Format as `YYYY-MM-DD`.
         - **Step 2**: Call `get_recent_activities` with these date arguments.
           - "Today": `start_date='YYYY-MM-DD'` (Current Date)
           - "This Month": `start_date='YYYY-MM-01'`
         - **Note**: You are responsible for calculating the specific dates.

    9. **Advanced Filtering**: 
       - Use `filter_json` in `search_objects` for detailed queries not covered by arguments.
       - **Location**:
         - *Companies in Tokyo*: `search_objects('companies', filter_json='[{"propertyName": "city", "operator": "EQ", "value": "Tokyo"}]')`.
       - **Operators**: EQ, NEQ, LT, LTE, GT, GTE, CONTAINS_TOKEN, IN.
       - **Properties**: `city`, `state`, `industry`, `amount`, `closedate`, `createdate`, `notes_last_updated`, etc.    
    ### Tool Usage:
    - `search_objects`: Primary tool for active/general search.
    - `search_inactive_objects`: Primary tool for "no activity", "stagnant", "old" queries. Handles date logic automatically.
    - `list_owners`: Use to resolve "My" requests or check Owner IDs.

    Authentication is handled automatically by Gemini Enterprise via OAuth.
    If a tool returns an unauthorized error, the system will prompt for authentication.
    ```

## C. 依存ファイル (`<AGENT_NAME>/requirements.txt`)
`<AGENT_NAME>` フォルダ内に `requirements.txt` を生成してください。
**(重要)** ADK (Vertex AI Agent Engine) へのデプロイ時に、ランタイムが依存関係を解決するためにこのファイルが必須となります。
    ```bash
    uv export --format requirements-txt --no-hashes > <AGENT_NAME>/requirements.txt
    ```

## D. 環境変数設定 (`<AGENT_NAME>/.env`)
`<AGENT_NAME>` フォルダ内に `.env` ファイルを作成する指示を README に含めてください。
ユーザーはこのファイルに `HUBSPOT_AUTH_ID=...` を記述することで、デプロイ後の登録IDを指定します。

# 4. ドキュメント (README.md)
セットアップ手順、実行方法に加え、**「デプロイ後に `.env` ファイルへ `HUBSPOT_AUTH_ID` を設定する必要がある」** 旨を明記してください。文書は日本語で記述してください。

# 5. 実装後の自己検証 (Self-Verification)
各ファイルの作成が完了したら、以下のコマンドを実行して構文エラーがないか自己チェックを行ってください。エラーがある場合は直ちに修正してください。

1.  **構文チェック**:
    ```bash
    python3 -m py_compile <AGENT_NAME>/tools.py
    python3 -m py_compile <AGENT_NAME>/agent.py
    ```
2.  **インポートチェック**:
    ```bash
    python3 -c "from <AGENT_NAME> import tools; print('tools.py imported successfully')"
    ```

さいごに

いかがでしたでしょうか。
実際には試す、フィードバックする、デプロイするといった流れを何度も繰り返したのでそれなりの手間は掛かってます。ですがフィードバックがどんどん実装されて使い勝手が良くなっていく体験は楽しいの一言です。

Antigravity を使っていると「どうやってプログラムとして実装し実現するか」を考える時間を「利用者を想像しながら試し、AI にフィードバックする」といった、成果物の品質を高めるために使えるのが最高です。

私としては皆さんにも記載したプロンプトを Antigravity で使って HubSpot エージェントを作成したり改良したり、と実際に体験して楽しんでいただけると嬉しいです。

記載したプロンプトでの作成は Antigravity の無料枠内で収まると思いますので、お試しいただき LLM の利用上限まで改善を楽しんでみてください。

参考ページ

この記事をシェアする

FacebookHatena blogX

関連記事