「社内のマニュアルや規程に質問すると、根拠つきで答えてくれるチャットがほしい」。生成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ステップです。
- 質問が来たら、社内文書の中から関係が深そうな断片を探す(検索)
- 見つけた断片を「この資料だけを根拠に答えて」とAIに渡す(回答生成)
- 答えと一緒に「どの文書のどこを根拠にしたか」を表示する(出典)
連載の最終形は次のとおりです。
- 文書(Markdown・テキスト・PDF)を取り込み、断片に分けて保存する
- 質問に意味が近い断片を、PostgreSQLの拡張機能pgvectorで探す
- 見つけた断片だけを根拠に回答し、出典を必ず付ける。根拠がなければ「わかりません」と答える
- 部署ごとの閲覧権限を、検索の段階で効かせる
- 回答の品質を自動テストで確かめ、Google Cloud Runへ公開する
なぜこの構成にしたか(技術選定の理由)
| 区分 | 採用 | 理由 |
|---|---|---|
| 言語 | Python 3.13 | AI関連のライブラリが最も豊富で、担当できる開発会社が多い |
| Web API | FastAPI | 入力チェックと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回です。連載のほかの回は次のとおりです(連載の一覧ページ)。
- 【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る(この記事)
- 【FastAPIで作る社内ナレッジ検索(RAG) 第1回】社内文書を読み込んで、検索しやすい断片に分ける
- 【FastAPIで作る社内ナレッジ検索(RAG) 第2回】PostgreSQLとpgvectorをDocker Composeで動かし、文書の断片を保存する
- 【FastAPIで作る社内ナレッジ検索(RAG) 第3回】質問に近い社内文書を探す(ベクトル検索)を実装する
- 【FastAPIで作る社内ナレッジ検索(RAG) 第4回】見つけた社内文書を根拠に、出典つきで生成AIに回答させる
- 【FastAPIで作る社内ナレッジ検索(RAG) 第5回】答えが社内文書に無いときは「見つかりません」と返す仕組みを作る
- 【FastAPIで作る社内ナレッジ検索(RAG) 第6回】部署ごとに見てよい文書だけを検索する、アクセス制御を実装する
- 【FastAPIで作る社内ナレッジ検索(RAG) 第7回】社内のPDFを取り込み、ページ番号つきの出典で答えさせる
- 【FastAPIで作る社内ナレッジ検索(RAG) 第8回】回答の品質を評価ケースで測り、直したつもりの後退を自動テストで見つける










コメント
コメント一覧 (5件)
[…] 前回の記事:【第0回】全体像と、動く最小APIを作る […]
[…] 【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る […]
[…] 【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る […]
[…] 【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る […]
[…] 【FastAPIで作る社内ナレッジ検索(RAG) 第0回】全体像と、動く最小APIを作る […]