MENU

問い合わせ


    【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る

    「社内のマニュアルや規程に質問すると、根拠つきで答えてくれるチャットがほしい」。生成AIが広まってから、発注側からいちばんよく聞くようになった要望のひとつです。この連載では、その社内ナレッジ検索(RAG)チャットを、PythonのFastAPIで一から作っていきます。

    先に結論をお伝えします。社内ナレッジ検索は「AIに文書を丸ごと読ませる」仕組みではなく、「質問に関係する部分だけを探して、AIに渡す」仕組みです。第0回では完成像と使う技術を整理し、最初の土台として、質問を受け取って返事をする最小のAPIを作って動かします。

    「社内の文書をAIに答えさせたい。でも、何を作ればいいのか、どこまで自前でできるのかが分からない」

    目次

    この連載で作るもの:社内ナレッジ検索(RAG)チャット

    RAGとは、Retrieval-Augmented Generation(検索で見つけた情報を材料に、AIに答えを作らせる方式)の略です。もともとは2020年に発表された研究の考え方です。

    📰 出典:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks(arXiv)

    仕組みはシンプルで、次の3ステップです。

    1. 質問が来たら、社内文書の中から関係が深そうな断片を探す(検索)
    2. 見つけた断片を「この資料だけを根拠に答えて」とAIに渡す(回答生成)
    3. 答えと一緒に「どの文書のどこを根拠にしたか」を表示する(出典)

    連載の最終形は次のとおりです。

    • 文書(Markdown・テキスト・PDF)を取り込み、断片に分けて保存する
    • 質問に意味が近い断片を、PostgreSQLの拡張機能pgvectorで探す
    • 見つけた断片だけを根拠に回答し、出典を必ず付ける。根拠がなければ「わかりません」と答える
    • 部署ごとの閲覧権限を、検索の段階で効かせる
    • 回答の品質を自動テストで確かめ、Google Cloud Runへ公開する

    なぜこの構成にしたか(技術選定の理由)

    区分採用理由
    言語Python 3.13AI関連のライブラリが最も豊富で、担当できる開発会社が多い
    Web APIFastAPI入力チェックとAPI仕様書(/docs)が自動で作られ、少ないコードで堅く作れる
    データ保存PostgreSQL + pgvector既存の業務データと同じDBで検索でき、専用のベクトルDBを増やさずに済む
    実行環境Docker → Cloud Run「サーバーを常時借りる」のではなく、コンテナを必要な時に動かす形。運用の手間が少ない

    以前の連載ではAWSのECS(コンテナを動かすサービス)を扱いました。今回は別の選択肢として、サーバーレス寄りの構成を最後の回で扱います。

    実装:最小のAPIを作る

    まずはAIも検索も入れず、「質問を受け取って返事をする箱」だけを作ります。土台を先に固めると、後の回で機能を足しても壊れていないかをテストで確認できます。

    プロジェクトの構成

    rag-naibu/code/
    ├ pyproject.toml        … 使うライブラリとバージョン
    ├ .env.example          … 環境変数の見本(実際の値は .env に書く)
    ├ Dockerfile
    ├ app/
    │  ├ config.py          … 設定の読み込み
    │  └ main.py            … APIの本体
    └ tests/test_main.py    … 自動テスト

    使うライブラリ(pyproject.toml)

    [project]
    name = "rag-naibu"
    requires-python = ">=3.13"
    dependencies = [
        "fastapi==0.141.1",
        "uvicorn==0.54.0",
        "pydantic-settings==2.15.0",
    ]

    バージョンを固定(==)しているのは、数か月後に同じ手順で動かせなくなる事態を避けるためです。バージョンは2026年9月時点のものです。

    設定の読み込み(app/config.py)

    from functools import lru_cache
    
    from pydantic_settings import BaseSettings, SettingsConfigDict
    
    
    class Settings(BaseSettings):
        model_config = SettingsConfigDict(env_file=".env", extra="ignore")
    
        app_name: str = "社内ナレッジ検索"
        llm_api_key: str | None = None
    
    
    @lru_cache
    def get_settings() -> Settings:
        return Settings()

    生成AIのAPIキーは、コードに直接書かず環境変数から読みます。.env ファイルはgitに入れません。入れてしまうと、キーが第三者に見える場所に残ってしまうためです。

    APIの本体(app/main.py)

    from typing import Annotated
    
    from fastapi import Depends, FastAPI
    from pydantic import BaseModel, Field
    
    from app.config import Settings, get_settings
    
    app = FastAPI(title="社内ナレッジ検索 API")
    
    
    class AskRequest(BaseModel):
        question: str = Field(min_length=1, max_length=500)
    
    
    class AskResponse(BaseModel):
        answer: str
        sources: list[str]
    
    
    @app.get("/health")
    def health(settings: Annotated[Settings, Depends(get_settings)]) -> dict[str, str]:
        return {"status": "ok", "app": settings.app_name}
    
    
    @app.post("/api/ask")
    def ask(body: AskRequest) -> AskResponse:
        # 第0回は「箱」だけ。検索と回答生成は第3〜4回で実装する。
        return AskResponse(answer=f"(仮回答)「{body.question}」を受け取りました。", sources=[])

    ポイントは2つです。

    • sources(出典)を最初から返事の形に含めています。「出典なしの回答は返さない」という設計を、土台の段階から決めておくためです。
    • 質問は1〜500文字に制限しています。空の質問や極端に長い入力は、自動で拒否されます。

    動作確認の方法

    自動テスト

    def test_ask_rejects_empty_question():
        assert client.post("/api/ask", json={"question": ""}).status_code == 422

    テスト全体は tests/test_main.py にあります。次のコマンドで実行します。

    pip install -e ".[dev]"
    ruff check .
    pytest

    筆者の環境(Python 3.13)では、テスト3件とコードの静的チェック(ruff)がすべて成功しました。

    実際に起動して確かめる

    uvicorn app.main:app --port 8000
    curl -s http://localhost:8000/health
    curl -s -X POST http://localhost:8000/api/ask -H "content-type: application/json" -d '{"question":"テスト"}'

    /health は {"status":"ok", ...} を、/api/ask は仮の回答を返します。ブラウザで http://localhost:8000/docs を開くと、自動生成されたAPI仕様書から試すこともできます。

    なお、この回のDockerfileは用意しましたが、筆者の実行環境にDockerが無かったため、docker build は未確認です。次回以降でDocker Composeを使う際に確認します。

    つまずきやすい点・セキュリティ上の注意

    • APIキーをコードやチャットに貼らない:漏れると第三者に使われ、利用料が発生することがあります。.env はgit管理外にします。
    • テスト用クライアントの警告:現時点では、テストで使う httpx について「httpx2 を使ってください」という非推奨の警告が表示されます。動作には影響しませんが、連載の途中で切り替えを検討します。
    • この段階の /api/ask には認証がありません:公開サーバーに置く前に、必ず認証と権限を入れる必要があります(第6回)。

    発注者向けメモ:社内ナレッジ検索を頼むときの確認点

    この連載は作る側の解説ですが、開発会社へ依頼する場合も、次の点は事前に押さえておくと安心です。

    • ☐ 検索の対象にしたい文書の種類と量(Word・PDF・Excel・紙のスキャンなど)を洗い出した
    • ☐ 部署や役職によって「見せてはいけない文書」があるかを整理した
    • ☐ 「AIが間違った答えを出す」ことがあるとの前提で、誰が最終確認するかを決めた
    • ☐ 文書の内容が、AIの事業者側に送られるかどうか、学習に使われるかどうかを確認する方針を決めた
    • ☐ 文書が更新された時に、誰がどのタイミングで検索対象を更新するかを決めた

    開発会社への質問例:

    • 「回答には、どの文書のどこを根拠にしたかを必ず表示できますか」
    • 「文書に答えが無い質問をしたとき、それらしい答えを作らず『わかりません』と返す仕組みはありますか」
    • 「部署ごとの閲覧権限は、AIに渡す前の検索の段階で効かせられますか」
    • 「回答の品質は、どのような方法で測り、改善するのですか」
    • 「利用するAIサービスに、社内文書はどこまで送信されますか」

    工数が増えやすいのは、文書の種類が多い場合(特にスキャンした紙や表の多いPDF)と、権限の区分が細かい場合です。最初は対象を1部署・1種類の文書に絞ると、見積もりも品質確認も進めやすくなります。

    まとめと次回予告

    第0回では、社内ナレッジ検索(RAG)の仕組みと連載の全体像を整理し、質問を受け取って返事をする最小のAPIを作って動かしました。技術選定の理由と、発注時に確認したい点もあわせて紹介しました。

    次回の第1回では、社内文書を読み込んで、検索しやすい断片(チャンク)に分ける処理を作ります。

    この連載の記事一覧

    この記事は連載「FastAPIで作る社内ナレッジ検索(RAG)」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      コメント

      コメント一覧 (5件)

      目次