【緊急】axiosがサプライチェーン攻撃に遭遇:汚染バージョン(1.14.1 / 0.30.4)と今すぐ取るべき対策

【緊急】axiosがサプライチェーン攻撃に遭遇:汚染バージョン(1.14.1 / 0.30.4)と今すぐ取るべき対策

2026年3月31日、世界で最も利用されているHTTPクライアントライブラリの一つである「axios」のnpmパッケージが侵害されました。攻撃者はメンテナーのnpmアカウントを乗っ取り、マルウェアを依存関係に含ませた汚染バージョンを直接パブリッシュするという、典型的なサプライチェーン攻撃を実行しました。

注目ポイント

axiosのバージョン 1.14.1 および 0.30.4 が侵害されました。これらは plain-crypto-js という悪意あるパッケージを依存に追加しており、インストール時にRAT(遠隔操作ツール)をドロップします。即座に 1.14.0 または 0.30.3 へのダウングレードを実行してください。

01. 発生の概要:メンテナーのアカウント乗っ取り

今回の攻撃は、axiosの主要なメンテナーの一人である jasonsaayman 氏のnpmアカウントが侵害されたことに起因します。攻撃者はnpmの正規のリリースライン(GitHub Actionsを通じたOIDC署名付きリリース)を完全にバイパスし、npm CLIから直接汚染されたパッケージをアップロードしました。

攻撃の露出時間は約2時間〜3時間程度でしたが、axiosのダウンロード数を考えれば数万人以上の開発者が影響を受けた可能性があります。

02. 影響を受けるバージョンと悪意あるパッケージ

侵害されたバージョンは以下の通りです:

  • axios@1.14.1(最新安定版系)
  • axios@0.30.4(レガシー系)

以上のバージョンの package.json には、本来不要な plain-crypto-js という依存関係が追加されています。このパッケージの postinstall フックが実行されることで、システムが感染します。

03. 潜伏するRAT(遠隔操作ツール)の検知方法

plain-crypto-js は、インストール後にOSに応じたRATをバックグラウンドで実行します。実行後に自身を node_modules から消去したり、package.json を書き戻したりして痕跡を隠蔽する巧妙な仕組みを持っています。

プラットフォーム別のIoC(侵害の痕跡)

  • macOS: /Library/Caches/com.apple.act.mond
  • Windows: %PROGRAMDATA%\wt.exe, %TEMP%\6202033.vbs
  • Linux: /tmp/ld.py

C2サーバー(指令サーバー)への通信先:sfrclak[.]com:8000

04. 即時対応:ダウングレードとクリーンアップ

もし npm ls axios 等で汚染バージョンが検知された場合、以下の手順を直ちに実施してください。

# 1. 悪意のあるディレクトリの削除
rm -rf node_modules/plain-crypto-js

# 2. 安全なバージョンへのダウングレード
# ※--ignore-scripts を必ず付け、マルウェアの再実行を防ぐ
npm install axios@1.14.0 --ignore-scripts
# または
npm install axios@0.30.3 --ignore-scripts

# 3. 各OSごとのバックドアファイルの削除(例:macOS)
rm -f /Library/Caches/com.apple.act.mond
警告

単なるバージョン変更ではなく、--ignore-scripts を使用してスクリプトの再実行を防ぎつつダウングレードを行うことが重要です。

05. 二次被害防止:クレデンシャル・ローテーション

汚染されたバージョンが一度でも実行された環境では、「全ての秘密情報が漏洩した」と仮定して行動すべきです。

  • 環境変数(.env)に記載されたAPIキーの再発行
  • AWS / GCP などのクラウドプロバイダーの環境変数の変更
  • SSH秘密鍵の再生成
  • npm、GitHub等のログインパスワード・トークンの変更

年度末の忙しい時期にこのような大きな事案が発生したことは非常に不幸ですが、これを機にチームのセキュリティ意識を底上げし、より堅牢な開発体制を築いていきましょう。 「明日は我が身」という健全な危機感を持つことが、最大の防御となります。

SSL証明書の有効期限短縮ショック…手動更新の限界と自動化へ

SSL証明書の有効期限短縮ショック…手動更新の限界と自動化への道

先日、インフラ担当の若手から「あれ、この証明書、前更新したの最近じゃなかったでしたっけ?」とボヤかれまして。確かに、昔は2年や3年有効なのが当たり前だったSSL/TLS証明書ですが、ここ最近はすっかり短命になりましたね。

そして今回、ブラウザベンダー界隈からさらに胃の痛くなるようなロードマップが聞こえてきました。どうやら現場の気合いや、スプレッドシートの手動管理では乗り切れないフェーズに突入したようです。

CA/ブラウザフォーラムで可決された短縮ロードマップ

現在: 最大有効期間 398日
2026年3月15日以降: 200日に短縮
2027年3月15日以降: 100日に短縮
2029年3月15日以降: 47日に短縮(審査情報の再利用も10日へ劇的に制限)

いよいよ来る、SSL証明書「200日」時代

Googleをはじめとするブラウザベンダーが主導する形で、セキュリティ向上のためにSSL証明書の有効期限短縮が着々と進んでいます。

CA/ブラウザフォーラムにおいて、以下のように段階的な短縮方針が正式に可決されました。

適用日 最大有効期間 ドメイン審査情報
再利用期間
組織認証情報
再利用期間
現在 398日 398日 825日
2026年3月15日以降 200日 200日 398日
2027年3月15日以降 100日 100日 398日
2029年3月15日以降 47日 10日 398日

一気に短くなる印象ですね。2029年には47日…約1.5ヶ月ごとですよ?四半期のサイクルより短いうえに、ドメイン審査情報の再利用期間も10日になるため、これまでの「ついでに更新」感覚では全然通らなくなります。ちょっと他の大きなプロジェクトにかまけていたり、メンバーが長めの休暇を取ったりしていたら、あっという間に期限がやってきます。これはインフラチームにとって笑えない冗談です。

プレイングマネージャー視点で見る「本当のコスト」

この期限短縮、技術的な変化というより、実は「運用コストとリスクの問題」なんですよね。私もプレイングマネージャーとして部門の予算や工数を見る立場にあるので、ここはシビアに考えざるを得ません。手動更新を続けた場合の影響を整理してみましょう。

項目 現場と管理のリアルな影響
証明書費用 購入総額自体には大きな変動はないと見込んでいます。(経理へは「予算は維持できます」と説明はつきます)
管理工数 問題はここです。年1回の更新が年8回以上になる計算です。CSD作成、申請、審査対応、サーバーへのインストール、再起動…それぞれの作業時間は短くても「チリツモ」で人件費が急増します。現場の時間を奪うのは大きな痛手です。
失効リスク 更新頻度が上がるということは、単純に「ヒューマンエラー(更新作業の失念)」の確率が跳ね上がるということです。証明書切れによるサイト閉鎖やサービス停止になった場合、始末書どころか顧客からの信用失墜という取り返しのつかない事態になります。

「気合い」を捨ててACMEで自動化しよう

結論から言うと、もう「カレンダーに予定を入れて手動更新で頑張る」という選択肢は捨てた方がいいです。現場の疲弊を招くだけですし、何より事故の元です。手動管理によるコスト増とリスクを回避するためには、以下のような体制への移行が必須です。

  • ACMEプロトコルの活用: 証明書の発行からインストール、更新までを自動化する標準規約ですね。Let's Encryptなどでお馴染みですが、最近は商用証明書でもACME対応が広がっています。
  • 管理ツールの刷新: 複数サーバーの証明書を一元管理し、ダッシュボードで状況を可視化できるツールの導入を検討・構築します。
  • 運用フローの見直し: 実はここが一番重要かもしれません。「期限が来たら人が動く」という旧来のフローから、「自動更新システムが正常に動いているかを監視する(エラー時のみ人が介入する)」というモダンな運用へのパラダイムシフトです。

インフラの自動化は組む時の面白さもありますが、これは何よりも「ビジネスの継続性を守り、チームの疲弊を防ぐための必須投資」です。

「いや、今のままでも気合いでいけるだろう」と先延ばしにするより、いまの時期から「証明書の短命化によって今の運用では確実に破綻します」と上を説得して、自動化への工数と予算を確保しておくことを強くお勧めします。後で泣きを見るのは現場のエンジニアや私たちですからね。

さて、私も明日の定例で提案するための説得資料でも作りますか。みなさんも早めの準備を。

YouTube LiveのチャットだけをAndroidで別画面表示するアプリを作ってリリースした

YouTube LiveのチャットだけをAndroidで別画面表示するアプリを作ってリリースした

テレビやモニターでYouTube Liveを全画面再生していると、ライブチャットが見えなくなります。スマホでYouTubeアプリを開き直しても動画が再生されてしまう。「Live Chatだけをスマホで別画面表示したい」という需要、ライブ配信を見る人なら絶対あると思っていました。そのまま自分で作ってリリースしました。

作ろうと思ったきっかけ

大画面でライブ配信を見ているとき、Live Chatの流れをリアルタイムで追いたいことがよくあります。でもテレビのフルスクリーン表示ではチャット欄が隠れてしまう。スマホでYouTubeアプリを開き直すと動画が二重再生になって邪魔。

既存のアプリを探してみたのですが、ちょうどいいものが見つからなかったので、自分で作ることにしました。シンプルに「URLを登録したらLive Chatだけが表示される」、それだけのアプリです。

アプリでできること

  • YouTube LiveのURLを登録してLive Chatのみをリアルタイム表示する
  • YouTubeアプリのシェアボタンから直接URLを送って登録できる
  • 動画を再生せずチャット画面だけを表示する
  • テレビ・モニターでライブを見ながらスマホでチャットを追える
ポイント

YouTubeアプリのシェアボタン経由で登録できるのが地味に便利です。URLをコピペする手間がなく、見ているライブ配信をそのまま登録してチャット画面を開けます。

使い方

  1. テレビやモニターでYouTube Liveをフルスクリーン再生する
  2. スマホのYouTubeアプリで同じライブ配信を開き、シェアボタンをタップ
  3. 共有先から「ChatTube」を選択
  4. アプリにLive Chatがリアルタイムで表示される

もちろんURLを直接入力して登録することもできます。動画IDだけでも対応しています。

こんな場面で使える

  • ライブ配信をテレビで見ながら、スマホでLive Chatをリアルタイムに追う
  • PC作業中に別モニターでライブ再生しつつ、スマホでチャットだけ確認する
  • 家族と大画面でライブを見ながら、自分だけチャットを追いたいとき
  • 配信者がチャットの反応を別画面で確認しながら配信するとき
セカンドスクリーン活用

テレビのリモコンでYouTubeを操作しているとチャット欄へのアクセスが面倒です。このアプリを使えばスマホをLive Chat専用画面として使えます。

ダウンロード

Android向けに Google Play で公開しています。無料でダウンロードできます(広告あり)。

Google Play で「ChatTube」を見る

不具合・要望があればコメントかレビューで教えてもらえると助かります。使ってみた感想もぜひ。

LangChainでAI駆動開発を自動化する:要件定義からテストまでフェーズ別エージェント実装ガイド

LangChainでAI駆動開発を自動化する:要件定義からテストまでフェーズ別エージェント実装ガイド

うちのチームに新しいメンバーが入ってきて、「LangChainを使えば開発全部自動化できますよね?」と言い出しまして。気持ちはわかるんですが、そこには大きな落とし穴があります。要件定義で使うべき思考と、テストケースを考えるときの思考は、根本的に違う。それを1つのエージェントに丸投げするとどうなるか——自分で試して痛い目を見ました。この記事では、フェーズごとに専門エージェントを分業させるアーキテクチャをLangChainで実装する方法を、実際に動くコードとともに解説します。

注目ポイント

エージェントはステートレスに設計し、フェーズ間の引き渡しは「ドキュメントオブジェクト」で行うのが安定運用の鍵。コンテキストを引き継ぐのではなく、前フェーズの成果物を次フェーズへの入力として渡す。

なぜ「1エージェント・全工程」は破綻するのか

LangChainのReActエージェントやLCELチェーンを使って「ユーザーの要望を入力したら設計書とコードとテストが出てくる」パイプラインを作ろうとした経験がある方は多いと思います。最初の数回は動く。でも少し複雑な要件を入れた途端、出力が崩れ始める。

原因はシンプルで、LLMのコンテキストウィンドウには限界があるからです。要件定義の議論をしながら、同時に実装コードの詳細まで考えさせると、どちらも中途半端になる。さらに問題なのは、エラーになるのではなく「それっぽい何か」が出力されてしまうこと。これが一番タチが悪い。

よくある失敗パターン

「1つの巨大プロンプトに全フェーズの指示を詰め込む」アプローチ。最初は動いて見えるが、要件が複雑になった瞬間に崩壊する。プロンプトエンジニアリングで解決しようとするより、設計を分けるほうが早い。

全体設計と前提環境

今回実装する構成はこうなります。4つの専門エージェントを順番に実行し、各エージェントの出力をPydanticモデルで型定義した「ドキュメントオブジェクト」として次のエージェントに渡します。

# 全体のフロー
ユーザー入力(要件メモ)
    ↓
RequirementsAgent  → RequirementsDoc(曖昧さリスト・確定仕様)
    ↓
DesignAgent        → DesignDoc(アーキテクチャ・API定義・DB設計)
    ↓
CodingAgent        → CodeDoc(実装コード・ファイル構成)
    ↓
TestAgent          → TestDoc(テストケース・テストコード)

前提環境

pip install langchain langchain-community langchain-core
pip install pydantic ollama

# Ollamaでモデルを起動しておく
ollama run gpt-oss:20b

OpenAI APIを使う場合は langchain-openai に差し替えるだけです。今回はローカルLLM(Ollama)ベースで書きますが、モデル部分は差し替え可能な設計にします。

要件定義エージェント:曖昧さを構造化する

要件定義エージェントの仕事は2つ。ひとつは入力テキストから曖昧な箇所を検出して質問リストを生成すること、もうひとつは確定した仕様を構造化されたオブジェクトに変換することです。

from langchain_community.llms import Ollama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel, Field
from typing import List

# 出力の型定義
class RequirementsDoc(BaseModel):
    ambiguities: List[str] = Field(description="曖昧な点・確認が必要な事項のリスト")
    confirmed_specs: List[str] = Field(description="確定した仕様のリスト")
    constraints: List[str] = Field(description="技術的・ビジネス的制約のリスト")
    summary: str = Field(description="要件の概要(2〜3文)")

class RequirementsAgent:
    def __init__(self, model_name: str = "gpt-oss:20b"):
        self.llm = Ollama(model=model_name, temperature=0.1)
        self.parser = JsonOutputParser(pydantic_object=RequirementsDoc)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """あなたはシニアのシステムアナリストです。
入力された要件テキストを分析し、以下を必ずJSON形式で出力してください。
- 定義が曖昧で確認が必要な項目
- 確定した仕様
- 技術的・ビジネス的制約
- 要件の概要

{format_instructions}"""),
            ("human", "以下の要件を分析してください:\n\n{requirements_text}")
        ])

    def run(self, requirements_text: str) -> RequirementsDoc:
        chain = self.prompt | self.llm | self.parser
        result = chain.invoke({
            "requirements_text": requirements_text,
            "format_instructions": self.parser.get_format_instructions()
        })
        return RequirementsDoc(**result)
設計のポイント

temperature=0.1 に設定するのがミソ。要件定義は「創造性」より「網羅性・一貫性」が重要なので、低めのtemperatureで安定した出力を得る。

設計エージェント:仕様書からアーキテクチャを生成する

設計エージェントは RequirementsDoc を受け取り、アーキテクチャ案・API定義・DBスキーマを出力します。ここでのポイントは、前フェーズの「曖昧さリスト」も一緒に渡すこと。曖昧なままの仕様を無視した設計を出力させないためです。

class DesignDoc(BaseModel):
    architecture: str = Field(description="アーキテクチャの説明(テキスト)")
    api_endpoints: List[dict] = Field(description="APIエンドポイント定義リスト")
    db_schema: List[dict] = Field(description="DBテーブル定義リスト")
    tech_stack: List[str] = Field(description="使用技術スタック")
    assumptions: List[str] = Field(description="設計上の前提・仮定(曖昧仕様への対応)")

class DesignAgent:
    def __init__(self, model_name: str = "gpt-oss:20b"):
        self.llm = Ollama(model=model_name, temperature=0.2)
        self.parser = JsonOutputParser(pydantic_object=DesignDoc)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """あなたはシニアのソフトウェアアーキテクトです。
確定仕様と制約に基づいてシステム設計を行い、JSON形式で出力してください。
曖昧な仕様については、設計上の前提として assumptions に明記してください。

{format_instructions}"""),
            ("human", """確定仕様: {confirmed_specs}
制約: {constraints}
曖昧な点(前提として扱う): {ambiguities}
要件概要: {summary}""")
        ])

    def run(self, req_doc: RequirementsDoc) -> DesignDoc:
        chain = self.prompt | self.llm | self.parser
        result = chain.invoke({
            "confirmed_specs": "\n".join(req_doc.confirmed_specs),
            "constraints": "\n".join(req_doc.constraints),
            "ambiguities": "\n".join(req_doc.ambiguities),
            "summary": req_doc.summary,
            "format_instructions": self.parser.get_format_instructions()
        })
        return DesignDoc(**result)

実装エージェント:設計書をコードに落とす

実装エージェントは DesignDoc のAPIエンドポイント定義とDBスキーマを受け取り、コードを生成します。全体を一度に生成しようとするとコンテキスト超過するので、エンドポイントごとに分割して呼び出す設計にしています。

class CodeDoc(BaseModel):
    files: List[dict] = Field(description="生成したファイルのリスト(filename, content)")
    setup_instructions: str = Field(description="セットアップ手順")

class CodingAgent:
    def __init__(self, model_name: str = "gpt-oss:20b"):
        self.llm = Ollama(model=model_name, temperature=0.15)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """あなたはシニアのバックエンドエンジニアです。
設計書に基づいてPythonコードを生成してください。
- FastAPIを使用
- 型ヒントを必ず付ける
- docstringは英語で記述
- エラーハンドリングを適切に実装"""),
            ("human", """以下のAPIエンドポイントを実装してください:
{endpoint}

DBスキーマ: {db_schema}
技術スタック: {tech_stack}""")
        ])

    def run(self, design_doc: DesignDoc) -> CodeDoc:
        all_files = []
        # エンドポイントごとに分割生成
        for endpoint in design_doc.api_endpoints:
            chain = self.prompt | self.llm
            code = chain.invoke({
                "endpoint": str(endpoint),
                "db_schema": str(design_doc.db_schema),
                "tech_stack": ", ".join(design_doc.tech_stack)
            })
            all_files.append({
                "filename": f"api_{endpoint.get('path','').strip('/').replace('/','_')}.py",
                "content": code
            })
        return CodeDoc(
            files=all_files,
            setup_instructions=f"Tech stack: {', '.join(design_doc.tech_stack)}"
        )

テストエージェント:コードからテストを生成する

テストエージェントは生成されたコードファイルを受け取り、pytestベースのテストコードを生成します。ここでもファイルごとに分割呼び出しです。一度に全コードを渡すとテストの質が落ちる。

class TestDoc(BaseModel):
    test_files: List[dict] = Field(description="テストファイルのリスト(filename, content)")
    coverage_notes: List[str] = Field(description="カバレッジ上の注意点・未テスト箇所")

class TestAgent:
    def __init__(self, model_name: str = "gpt-oss:20b"):
        self.llm = Ollama(model=model_name, temperature=0.1)
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """あなたはQAエンジニアです。pytestを使ったテストコードを生成してください。
- 正常系・異常系・境界値を網羅
- モックは unittest.mock を使用
- テスト関数名は test_[動詞]_[対象]_[条件] の形式で"""),
            ("human", "以下のコードのテストを生成してください:\n\n{source_code}")
        ])

    def run(self, code_doc: CodeDoc) -> TestDoc:
        test_files = []
        for file in code_doc.files:
            chain = self.prompt | self.llm
            test_code = chain.invoke({"source_code": file["content"]})
            test_files.append({
                "filename": f"test_{file['filename']}",
                "content": test_code
            })
        return TestDoc(
            test_files=test_files,
            coverage_notes=["境界値テストは手動での確認を推奨"]
        )

オーケストレーション:4エージェントをパイプラインで繋ぐ

最後に、4つのエージェントを順番に実行するオーケストレーターを実装します。各フェーズの結果をログに残しておくと、どこで品質が落ちたか後から追跡できます。

import json
from datetime import datetime
from pathlib import Path

class DevPipeline:
    def __init__(self, model_name: str = "gpt-oss:20b", output_dir: str = "output"):
        self.req_agent = RequirementsAgent(model_name)
        self.design_agent = DesignAgent(model_name)
        self.coding_agent = CodingAgent(model_name)
        self.test_agent = TestAgent(model_name)
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(exist_ok=True)

    def run(self, requirements_text: str) -> dict:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        results = {}

        print("▶ Phase 1: 要件定義エージェント")
        req_doc = self.req_agent.run(requirements_text)
        results["requirements"] = req_doc.model_dump()
        self._save(f"{timestamp}_requirements.json", results["requirements"])
        print(f"  曖昧な点: {len(req_doc.ambiguities)}件 / 確定仕様: {len(req_doc.confirmed_specs)}件")

        print("▶ Phase 2: 設計エージェント")
        design_doc = self.design_agent.run(req_doc)
        results["design"] = design_doc.model_dump()
        self._save(f"{timestamp}_design.json", results["design"])
        print(f"  APIエンドポイント: {len(design_doc.api_endpoints)}件")

        print("▶ Phase 3: 実装エージェント")
        code_doc = self.coding_agent.run(design_doc)
        results["code"] = code_doc.model_dump()
        self._save(f"{timestamp}_code.json", results["code"])
        print(f"  生成ファイル: {len(code_doc.files)}件")

        print("▶ Phase 4: テストエージェント")
        test_doc = self.test_agent.run(code_doc)
        results["tests"] = test_doc.model_dump()
        self._save(f"{timestamp}_tests.json", results["tests"])
        print(f"  テストファイル: {len(test_doc.test_files)}件")

        return results

    def _save(self, filename: str, data: dict):
        with open(self.output_dir / filename, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)

# 実行例
if __name__ == "__main__":
    pipeline = DevPipeline(model_name="gpt-oss:20b")
    requirements = """
    ユーザー管理APIを作りたい。
    ユーザーの登録・ログイン・プロフィール更新ができること。
    認証はJWTを使う。DBはPostgreSQLを想定。
    """
    results = pipeline.run(requirements)
運用Tips

各フェーズの出力をJSONファイルとして保存しておくと、途中のフェーズからやり直せます。要件定義だけ人間がレビューして修正し、設計以降を再実行する、という使い方が現実的です。

実運用での限界と人間の介在ポイント

このパイプラインを実際に使ってみて気づいた限界をいくつか挙げておきます。まず、要件が曖昧なまま進むと雪だるま式に品質が落ちる。RequirementsAgentが出した曖昧さリストを無視してDesignAgentに進むと、設計の前提が崩れ、コードもテストも連鎖的に歪みます。

もうひとつは、テストエージェントのエッジケース認識が甘いこと。正常系は網羅してくれますが、「このAPIに不正なJWTを渡したらどうなるか」といった攻撃的なテストケースは自分で足す必要があります。コストの話をすると、ローカルLLMなのでAPI費用はゼロですが、20Bモデルで全フェーズ回すと5〜10分かかります。小規模プロジェクトなら十分実用的な速度です。

人間が介在すべきポイントは明確で、Phase 1終了後の曖昧さレビューPhase 3終了後のコードレビューの2点。それ以外はエージェントに任せて、人間は判断に集中する——これがこのアーキテクチャの狙いです。

Google Antigravityで変わるAI駆動開発:フェーズ別エージェント分業の実践

Google Antigravityで変わるAI駆動開発:フェーズ別エージェント分業の実践

先月、お客さんから「AI使えばもう設計書いらないですよね?」と言われまして。思わず苦笑いしてしまいました。確かに、Copilotやら何やらで「コードを書く速度」は上がった。でも要件定義でAIに曖昧な指示を投げると、自信満々で的外れなものを返してくる。「1つのAIに全部やらせる」という発想は、もう少し慎重に考えたほうがいいんじゃないかと思っていたんですよね。そこに出てきたのが、GoogleのAntigravityです。

注目ポイント

Antigravityの本質は「コード補完の強化」ではなく、「フェーズごとに専門エージェントを分業させ、人間はレビューに集中する」という設計思想の転換にある。

なぜ「1つのAI」では限界が来るのか

CursorやGitHub Copilotを使っている方なら実感があると思いますが、あのツールは基本的に「今書いているコードの文脈」しか見ていません。要件定義の話をしながら同時に実装コードを提案させると、途端にコンテキストが混乱する。結果、中途半端な出力になる。

これは設計の問題でもあって、要件定義・設計・実装・テストはそれぞれ「考え方の粒度」が全然違います。要件定義は「何を作るか」の議論、設計は「どう作るか」の整理、実装は「書く」作業、テストは「壊す」作業。これを1つのLLMに同時にやらせようとするから歪みが出る。Antigravityはここに目を向けた、という点で面白い。

Google Antigravityとは何者か

2025年11月にGoogleが発表したエージェント・ファーストのIDEです。VS Codeフォークなので、今使っている拡張機能や設定はそのまま使えます。この点はチームへの展開コストが低くて助かりますね。

特徴的な機能は3つです。エージェント・マネージャーでバックグラウンドの複数エージェントを並行稼働させられること、ブラウザ・エージェントがAI自身でドキュメントを調査してスクリーンショットまで撮れること、そしてArtifactsでAIの処理結果をタスクリスト・プラン・動画として可視化できること。この3つが噛み合うと、開発フローが変わります。

要件定義フェーズ:曖昧さを炙り出すエージェント

要件定義で一番つらいのは「曖昧なまま進んでしまうこと」です。お客さんの言う「使いやすいUI」って何なのか、「リアルタイム」って何ミリ秒を指しているのか。ここを詰め切れないまま設計に入ると、後で痛い目を見ます。

Antigravityでは、要件ヒアリングのメモや議事録を投げると、エージェントが「この要件は定義が曖昧です。以下を確認してください」という質問リストをArtifactsとして出力します。人間がやると遠慮が入るこの「ツッコミ」を、AIに任せるのは案外合理的です。

注意

エージェントが出した質問リストをそのままお客さんに送るのは危険です。技術的すぎる、あるいは失礼な表現が混じることがある。あくまで「人間がレビューする素材」として使うのが正解です。

設計フェーズ:Artifactsで叩き台を作らせる

設計フェーズでのAntigravityの使い方は「叩き台の高速生成」です。確定した要件をまとめたドキュメントを渡すと、エージェントがアーキテクチャ案・テーブル設計・APIエンドポイント一覧をArtifactsとして出力します。

これが地味に効いてくる。設計レビューって、ゼロから書いたものを見るより「これでいいか?」と叩き台を渡された方が指摘しやすいんですよね。エンジニア全員が設計を書ける訳じゃないし、特にジュニアメンバーは「何から書けばいいかわからない」という状態になりやすい。Artifactsが最初のたたき台を作ってくれるだけで、議論の密度が上がります。

実装フェーズ:ブラウザ・エージェントに調査を丸投げする

実装中に一番時間を取られるのって、コードを書くことより「ドキュメントを読む時間」じゃないかと思っています。新しいライブラリを使うとき、公式ドキュメントを読んで、Stack Overflowを漁って、GitHubのIssueを確認して…。

Antigravityのブラウザ・エージェントは、AIが自分でブラウザを操作してドキュメントを調査し、「このライブラリでやりたいことを実現するコード例」を持ってきます。自分が読む必要がなくなる、とは言いませんが、調査の起点として使うと明らかに速い。うちのチームでは「まずAntigravityに調べさせて、人間が裏取りする」という流れに変わりつつあります。

テストフェーズ:バックグラウンドで回し続ける

テストエージェントをバックグラウンドで動かしながら実装を続ける、というのがAntigravityの真骨頂です。実装中にエージェントが並行して「このコードに対するテストケース案」を生成し続けてくれる。

正直、テストを書く習慣がないチームには少し敷居が高いですが、「テストの素材をAIに作らせて、人間が選ぶ」というプロセスなら受け入れやすい。全部信用するのは危ないですが(エッジケースの認識が甘いことがある)、カバレッジの穴を埋める用途には十分使えます。

実践メモ

バックグラウンドエージェントはリソースを食います。ローカルマシンのスペックが低いと逆に開発体験が悪化します。チーム導入前にスペック要件を確認しておくのが無難です。

人間がやるべき仕事は何か

ここまで書いてきて思うのは、Antigravityを使いこなすには「何をエージェントに任せ、何を自分で判断するか」のタスク設計センスが問われるということです。ツールが変わっても、要件の本質を掴む力や、設計の良し悪しを判断する経験値は、引き続き人間の仕事です。

コストの話をすると、現時点でAntigravityのエンタープライズプランは月額が安くない。ただ、要件定義のやり直しが1回減るだけで、十分に元が取れる規模感の案件が多いのも事実です。まずは小規模プロジェクトで試して、上司への稟議のネタを作るところから始めるのが現実的かなと思っています。