0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIメンターちゃんに教わった、Notion Database × Ollama ローカルLLM 原理原則から応用システム構築まで〜新人エンジニア向け徹底メモ

0
Posted at

※学習事項をAIにまとめてもらしました!実機調査未完了である事、ご了承ください。

はじめに

🐰 うさうさラーメン店のゆきこ店長より:
「Notion Databaseって何?Ollamaって何?」という方も大丈夫!
この記事では、ラーメン店の注文管理をたとえに使いながら、原理原則から実際にシステムを作れるところまで丁寧に解説します。

この記事で身につくこと:

  1. Notion Database の原理原則(公式APIドキュメント準拠)
  2. Ollama ローカルLLM の原理原則(公式仕様準拠)
  3. 両者を組み合わせた 実用システムの構築方法

対象読者:新人エンジニア・文系出身エンジニア・IT講師の方


第1部:Notion Database 原理原則

1-1. Notion Databaseとは何か

たとえ話で理解する

📋 Notion Database = ラーメン店の「注文管理台帳」

  ┌─────────────────────────────────────────────┐
  │  うさうさラーメン店 注文管理台帳             │
  ├──────┬────────┬────────┬───────┬─────────────┤
  │ 注文名│ 種類    │ 価格   │ 状態   │ 注文日      │
  │(title)│(select) │(number)│(status)│ (date)      │
  ├──────┼────────┼────────┼───────┼─────────────┤
  │味噌   │ラーメン │  850   │提供済  │ 2025-04-07  │
  │餃子   │サイド   │  400   │調理中  │ 2025-04-07  │
  └──────┴────────┴────────┴───────┴─────────────┘

Notion Databaseを一言でいうと、「プロパティ(列)で型を定義し、ページ(行)でデータを蓄積する、構造化データのコンテナ」 です。

2025年の大きな変化:Database → Container + Data Source

2025年8月のアップデートで、Notionのデータモデルが大きく変わりました。

【旧モデル】
  Database = テーブルそのもの(1対1)

【新モデル(2025年〜)】
  Database = コンテナ(入れ物)
    └── Data Source A(注文テーブル)
    └── Data Source B(在庫テーブル)
    └── Data Source C(スタッフテーブル)

たとえ話:

  • 旧モデル = 注文帳・在庫帳・スタッフ名簿がバラバラの冊子
  • 新モデル = 「うさうさラーメン店 管理ファイル」というバインダーに、タブで分けて全部入っている

APIバージョン 2025-09-03 からこの新モデルに対応しています。ただし、1つのData Sourceだけで使う場合は旧バージョンのAPIでも動作します。

1-2. プロパティ型:Databaseの「設計図」

プロパティはDatabaseの列であり、データの「型」を決めます。ラーメン店の注文管理で対応づけて理解しましょう。

基本プロパティ一覧

プロパティ型 うさうさラーメン店での使い方 説明
title 注文名(味噌ラーメン等) 必須。各DBに1つだけ。ページ名になる
rich_text 特記事項(「ネギ多め」等) リッチテキスト。装飾可能な文字列
number 価格(850等) 数値。通貨・パーセント等の書式指定可
select 種類(ラーメン/サイド/ドリンク) 単一選択。1つだけ選ぶカテゴリ
multi_select トッピング(チャーシュー, 煮卵, メンマ) 複数選択。タグとして機能
date 注文日時 日付・期間。タイムゾーン対応
checkbox 会計済みかどうか true/false のブール値
status 調理状態(未着手→調理中→提供済) ワークフローに最適。グループ分け可能
people 担当スタッフ Notionユーザーへの参照
url レシピ参考URL URL文字列
email お客様メール(デリバリー用) メールアドレス
phone_number お客様電話番号 電話番号
files 料理の写真 ファイルアップロード
formula 合計金額の自動計算 他プロパティを参照して計算
relation 注文→メニューDBへの紐付け 別DBとの関連付け
rollup メニューDBから原価を集計 relation経由で別DBの値を集計
created_time レコード作成日時 自動記録。編集不可
last_edited_time 最終更新日時 自動記録。編集不可
created_by 作成者 自動記録
last_edited_by 最終編集者 自動記録

type-specific data パターン(API設計の原則)

Notion APIでは 「typeの値と同名のキーにデータが入る」 という設計原則があります。

{
  "Price": {
    "type": "number",
    "number": {            //  typeと同名のキー
      "format": "dollar"   //  型固有の設定
    }
  },
  "Last ordered": {
    "type": "date",
    "date": {}             //  typeと同名のキー
  }
}

🐰 ゆきこ店長メモ: このパターンを覚えておくと、APIレスポンスを見たときに「あ、typeがdateだからdateキーの中身を見ればいいんだな」とすぐわかります!

1-3. Notion API の基本操作(CRUD)

インテグレーションの準備

1. https://www.notion.so/my-integrations にアクセス
2. 「新しいインテグレーション」を作成
3. API Key(Internal Integration Token)を取得
4. 対象ページ/DBで「コネクト」からインテグレーションを追加

Python での基本操作

import requests

NOTION_API_KEY = "secret_xxxxxxxxxxxxx"
HEADERS = {
    "Authorization": f"Bearer {NOTION_API_KEY}",
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28"  # 安定版
}

# ===== CREATE:データベース作成 =====
def create_database(parent_page_id: str) -> dict:
    """うさうさラーメン店の注文管理DBを作成"""
    url = "https://api.notion.com/v1/databases"
    data = {
        "parent": {"type": "page_id", "page_id": parent_page_id},
        "icon": {"type": "emoji", "emoji": "🍜"},
        "title": [{"type": "text", "text": {"content": "注文管理"}}],
        "properties": {
            "注文名":    {"title": {}},
            "種類":      {"select": {"options": [
                            {"name": "ラーメン", "color": "red"},
                            {"name": "サイド",   "color": "blue"},
                            {"name": "ドリンク", "color": "green"}
                         ]}},
            "価格":      {"number": {"format": "yen"}},
            "状態":      {"status": {}},
            "注文日":    {"date": {}},
            "特記事項":  {"rich_text": {}},
            "会計済":    {"checkbox": {}},
        }
    }
    res = requests.post(url, json=data, headers=HEADERS)
    return res.json()


# ===== READ:データベース情報取得 =====
def get_database(database_id: str) -> dict:
    url = f"https://api.notion.com/v1/databases/{database_id}"
    res = requests.get(url, headers=HEADERS)
    return res.json()


# ===== CREATE:ページ(行)追加 =====
def add_order(database_id: str, name: str, category: str, 
              price: int, note: str = "") -> dict:
    """注文を1件追加する"""
    url = "https://api.notion.com/v1/pages"
    data = {
        "parent": {"database_id": database_id},
        "properties": {
            "注文名":   {"title": [{"text": {"content": name}}]},
            "種類":     {"select": {"name": category}},
            "価格":     {"number": price},
            "注文日":   {"date": {"start": "2025-04-07"}},
            "特記事項": {"rich_text": [{"text": {"content": note}}]},
            "会計済":   {"checkbox": False},
        }
    }
    res = requests.post(url, json=data, headers=HEADERS)
    return res.json()


# ===== QUERY:フィルタ&ソートで検索 =====
def query_unpaid_orders(database_id: str) -> dict:
    """未会計の注文を価格順で取得"""
    url = f"https://api.notion.com/v1/databases/{database_id}/query"
    data = {
        "filter": {
            "property": "会計済",
            "checkbox": {"equals": False}
        },
        "sorts": [
            {"property": "価格", "direction": "descending"}
        ]
    }
    res = requests.post(url, json=data, headers=HEADERS)
    return res.json()


# ===== UPDATE:ページ更新 =====
def mark_as_paid(page_id: str) -> dict:
    """会計済みに更新"""
    url = f"https://api.notion.com/v1/pages/{page_id}"
    data = {
        "properties": {
            "会計済": {"checkbox": True}
        }
    }
    res = requests.patch(url, json=data, headers=HEADERS)
    return res.json()

フィルタの原理原則

フィルタは プロパティ型ごとに使えるオペレーターが決まっている という設計です。

number型  → equals, does_not_equal, greater_than, less_than, 
            greater_than_or_equal_to, less_than_or_equal_to,
            is_empty, is_not_empty

select型  → equals, does_not_equal, is_empty, is_not_empty

date型    → equals, before, after, on_or_before, on_or_after,
            is_empty, is_not_empty, past_week, past_month, 
            past_year, next_week, next_month, next_year

checkbox型 → equals, does_not_equal

複合フィルタは and / or でネスト可能:

{
  "filter": {
    "and": [
      {"property": "種類", "select": {"equals": "ラーメン"}},
      {"property": "価格", "number": {"greater_than": 800}}
    ]
  }
}

1-4. Relation と Rollup:DBを「つなぐ」力

┌─────────────┐         ┌─────────────────┐
│  注文管理DB  │         │  メニューマスタDB │
│             │ relation │                  │
│  メニュー ──┼────────→│  メニュー名       │
│             │         │  原価             │
│  原価合計 ──┼─rollup──│  カテゴリ         │
│ (自動集計)│         │                  │
└─────────────┘         └─────────────────┘
  • Relation = 2つのDBをリンクする。外部キーに相当
  • Rollup = Relation先のプロパティを集計する(合計・平均・カウント等)

🐰 たとえ話: Relationは「注文票にメニュー番号を書く」こと。Rollupは「そのメニュー番号から原価表を引いて合計を出す」こと。


第2部:Ollama ローカルLLM 原理原則

2-1. Ollamaとは何か

たとえ話で理解する

☁️ ChatGPT / Claude(クラウドLLM)
   = 出前サービス。注文するたびに外に電話する
   → 便利だけどお金がかかる、データが外に出る

🏠 Ollama(ローカルLLM)
   = 自分のキッチンで作る。材料(モデル)を仕入れて自分で調理
   → 無料、データは外に出ない、ただしキッチン(PC)の性能が必要

Ollamaは、オープンソースのLLMモデルをローカルPCでダウンロード・実行・管理するためのツールです。内部的にはllama.cppを利用し、GGUF形式の量子化モデルを効率的に推論します。

Ollamaの4つの強み

強み 説明
プライバシー データが外部に出ない。社内情報・個人情報を扱える
コスト API課金なし。電気代のみ
カスタマイズ Modelfileで独自設定。temperature等を自由に調整
量子化 大きなモデルも4bit量子化で古いGPUやCPUでも動く

2-2. インストールと基本操作

インストール

# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh

# Windows
# 公式サイト https://ollama.com からインストーラをダウンロード

# Docker
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

基本コマンド

# モデルをダウンロードして実行(対話モード)
ollama run llama3.2

# モデル一覧を確認
ollama list

# モデルをダウンロードのみ
ollama pull gemma3

# モデルを削除
ollama rm llama3.2

# 実行中のモデル確認
ollama ps

# サーバーを起動(通常は自動起動)
ollama serve

推奨モデルと必要スペック

モデル パラメータ数 必要メモリ目安 特徴
llama3.2 3B 4GB 軽量で高速。入門に最適
llama3.2 1B 2GB 超軽量。古いPCでもOK
gemma3 4B 4GB Google製。バランス良好
deepseek-r1 7.6B 8GB 推論特化。コード生成に強い
qwen2.5 7B 8GB 中国語・日本語にも対応
codellama 7B 8GB コーディング特化

🐰 ゆきこ店長メモ: 最初は llama3.2(3B)から始めましょう!ほとんどのノートPCで動きます。

2-3. Ollama REST API の原理原則

Ollamaは起動すると http://localhost:11434 でREST APIサーバーとして動作します。

主要エンドポイント一覧

エンドポイント メソッド 用途
/api/generate POST テキスト生成(単発)
/api/chat POST チャット形式の会話
/api/embeddings POST テキストの埋め込みベクトル取得
/api/tags GET ローカルモデル一覧
/api/pull POST モデルダウンロード
/api/show POST モデル詳細情報

/api/chat の基本

curl http://localhost:11434/api/chat -d '{
  "model": "llama3.2",
  "messages": [
    {"role": "system", "content": "あなたはラーメン店の接客AIです。丁寧に対応してください。"},
    {"role": "user", "content": "おすすめのラーメンは?"}
  ],
  "stream": false
}'

レスポンス構造:

{
  "model": "llama3.2",
  "created_at": "2025-04-07T10:00:00Z",
  "message": {
    "role": "assistant",
    "content": "当店のおすすめは味噌ラーメンです!..."
  },
  "done": true,
  "total_duration": 1500000000,
  "eval_count": 42
}

/api/generate の基本

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Pythonでリスト内包表記を説明して",
  "stream": false
}'

Python から使う(公式ライブラリ)

# pip install ollama
from ollama import chat

response = chat(
    model="llama3.2",
    messages=[
        {"role": "user", "content": "PythonのリストとタプルRunの違いを教えて"}
    ]
)
print(response.message.content)

OpenAI互換API

OllamaはOpenAI互換エンドポイントも提供しています。既存のOpenAIクライアントをそのまま使えます。

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # 任意の文字列でOK
)

response = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "こんにちは!"}]
)
print(response.choices[0].message.content)

🐰 原理原則ポイント: OpenAI互換APIがあるので、将来クラウドLLMに切り替えるときも base_urlapi_key を変えるだけでOK!

2-4. Modelfile:モデルのカスタマイズ

Modelfileを使うと、既存モデルをベースに独自のAIアシスタントを作れます。

# Modelfile
FROM llama3.2

# システムプロンプト設定
SYSTEM """
あなたは「うさうさラーメン店」の注文分析AIアシスタントです。
以下のルールに従ってください:
- 注文データの分析結果は必ず数値で示す
- 改善提案は3つまでに絞る
- 敬語で丁寧に回答する
"""

# パラメータ調整
PARAMETER temperature 0.3
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
# カスタムモデルの作成と実行
ollama create usausa-analyst -f Modelfile
ollama run usausa-analyst

主要パラメータの意味

パラメータ デフォルト 説明
temperature 0.8 出力のランダムさ。0に近いほど確定的
top_p 0.9 累積確率でサンプリング範囲を制御
top_k 40 上位k個の候補からサンプリング
num_ctx 2048 コンテキストウィンドウサイズ
repeat_penalty 1.1 繰り返しペナルティ。高いと同じ言葉を避ける

🐰 たとえ話:

  • temperature = 料理人の「冒険度」。0=マニュアル通りに作る、1=アレンジ加える
  • num_ctx = 料理人が覚えていられる注文の数

2-5. ストリーミングとTool Calling

ストリーミング(リアルタイム出力)

import requests
import json

def stream_chat(prompt: str):
    """リアルタイムにレスポンスを表示"""
    url = "http://localhost:11434/api/chat"
    data = {
        "model": "llama3.2",
        "messages": [{"role": "user", "content": prompt}],
        "stream": True  # ← ストリーミング有効
    }
    
    with requests.post(url, json=data, stream=True) as res:
        for line in res.iter_lines():
            if line:
                chunk = json.loads(line)
                print(chunk["message"]["content"], end="", flush=True)
                if chunk.get("done"):
                    print()  # 改行

Tool Calling(関数呼び出し)

対応モデル(llama3.1以降等)ではTool Callingが使えます。

import ollama

response = ollama.chat(
    model="llama3.1",
    messages=[{"role": "user", "content": "東京の天気を教えて"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "指定都市の天気を取得",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "都市名"
                    }
                },
                "required": ["city"]
            }
        }
    }]
)

# モデルがtool_callsを返す
if response.message.tool_calls:
    for tool_call in response.message.tool_calls:
        print(f"関数: {tool_call.function.name}")
        print(f"引数: {tool_call.function.arguments}")

第3部:応用 — Notion × Ollama で作る実用システム

3-1. システム概要:AI注文分析アシスタント

┌──────────┐    ①データ取得     ┌──────────────┐
│  Notion   │ ◀──────────────── │              │
│ Database  │                    │   Python     │
│(注文DB) │ ⑤結果書き戻し    │   連携       │
│           │ ◀──────────────── │   スクリプト │
└──────────┘                    │              │
                                │              │
┌──────────┐    ②データ送信     │              │
│  Ollama   │ ◀──────────────── │              │
│  (LLM)   │                    │              │
│           │ ③分析結果          │              │
│           │ ──────────────▶   │              │
└──────────┘                    └──────────────┘

④ レポート生成 & Notionに保存

3-2. 完全実装コード

"""
Notion × Ollama AI注文分析アシスタント
=====================================
Notion Databaseの注文データをOllamaで分析し、
結果をNotionに書き戻すシステム
"""

import requests
import json
from datetime import datetime, timedelta

# ===== 設定 =====
NOTION_API_KEY = "secret_xxxxxxxxxxxxx"
DATABASE_ID = "your-database-id-here"
OLLAMA_URL = "http://localhost:11434"
OLLAMA_MODEL = "llama3.2"

NOTION_HEADERS = {
    "Authorization": f"Bearer {NOTION_API_KEY}",
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28"
}


# ===== Notion操作クラス =====
class NotionClient:
    """Notion API操作をまとめたクラス"""
    
    def __init__(self, api_key: str):
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "Notion-Version": "2022-06-28"
        }
        self.base_url = "https://api.notion.com/v1"
    
    def query_database(self, database_id: str, 
                       filter_obj: dict = None,
                       sorts: list = None) -> list:
        """DBを検索してページ一覧を返す"""
        url = f"{self.base_url}/databases/{database_id}/query"
        body = {}
        if filter_obj:
            body["filter"] = filter_obj
        if sorts:
            body["sorts"] = sorts
        
        all_results = []
        has_more = True
        start_cursor = None
        
        # ページネーション対応(原則:100件ずつ取得)
        while has_more:
            if start_cursor:
                body["start_cursor"] = start_cursor
            
            res = requests.post(url, json=body, headers=self.headers)
            data = res.json()
            all_results.extend(data.get("results", []))
            has_more = data.get("has_more", False)
            start_cursor = data.get("next_cursor")
        
        return all_results
    
    def create_page(self, database_id: str, 
                    properties: dict) -> dict:
        """ページ(行)を作成"""
        url = f"{self.base_url}/pages"
        body = {
            "parent": {"database_id": database_id},
            "properties": properties
        }
        res = requests.post(url, json=body, headers=self.headers)
        return res.json()
    
    def update_page(self, page_id: str, 
                    properties: dict) -> dict:
        """ページを更新"""
        url = f"{self.base_url}/pages/{page_id}"
        body = {"properties": properties}
        res = requests.patch(url, json=body, headers=self.headers)
        return res.json()
    
    def add_block_children(self, page_id: str, 
                           markdown_text: str) -> dict:
        """ページにテキストブロックを追加"""
        url = f"{self.base_url}/blocks/{page_id}/children"
        body = {
            "children": [{
                "object": "block",
                "type": "paragraph",
                "paragraph": {
                    "rich_text": [{
                        "type": "text",
                        "text": {"content": markdown_text}
                    }]
                }
            }]
        }
        res = requests.patch(url, json=body, headers=self.headers)
        return res.json()


# ===== Ollama操作クラス =====
class OllamaClient:
    """Ollama API操作をまとめたクラス"""
    
    def __init__(self, base_url: str = "http://localhost:11434",
                 model: str = "llama3.2"):
        self.base_url = base_url
        self.model = model
    
    def chat(self, messages: list, 
             temperature: float = 0.3) -> str:
        """チャット形式でLLMに問い合わせ"""
        url = f"{self.base_url}/api/chat"
        body = {
            "model": self.model,
            "messages": messages,
            "stream": False,
            "options": {
                "temperature": temperature
            }
        }
        res = requests.post(url, json=body)
        data = res.json()
        return data["message"]["content"]
    
    def analyze(self, system_prompt: str, 
                user_prompt: str) -> str:
        """システムプロンプト付きで分析を実行"""
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user",   "content": user_prompt}
        ]
        return self.chat(messages)


# ===== データ変換ユーティリティ =====
def extract_order_data(pages: list) -> list:
    """Notionページからわかりやすい辞書リストに変換"""
    orders = []
    for page in pages:
        props = page["properties"]
        
        # type-specific dataパターンに基づいてデータ抽出
        order = {
            "id": page["id"],
            "name": _get_title(props.get("注文名", {})),
            "category": _get_select(props.get("種類", {})),
            "price": _get_number(props.get("価格", {})),
            "status": _get_status(props.get("状態", {})),
            "date": _get_date(props.get("注文日", {})),
            "note": _get_rich_text(props.get("特記事項", {})),
            "paid": _get_checkbox(props.get("会計済", {})),
        }
        orders.append(order)
    return orders

def _get_title(prop: dict) -> str:
    title_arr = prop.get("title", [])
    return title_arr[0]["plain_text"] if title_arr else ""

def _get_select(prop: dict) -> str:
    sel = prop.get("select")
    return sel["name"] if sel else ""

def _get_number(prop: dict):
    return prop.get("number")

def _get_status(prop: dict) -> str:
    st = prop.get("status")
    return st["name"] if st else ""

def _get_date(prop: dict) -> str:
    dt = prop.get("date")
    return dt["start"] if dt else ""

def _get_rich_text(prop: dict) -> str:
    rt_arr = prop.get("rich_text", [])
    return rt_arr[0]["plain_text"] if rt_arr else ""

def _get_checkbox(prop: dict) -> bool:
    return prop.get("checkbox", False)


# ===== メイン処理 =====
def main():
    notion = NotionClient(NOTION_API_KEY)
    ollama = OllamaClient(OLLAMA_URL, OLLAMA_MODEL)
    
    # ① Notionからデータ取得
    print("📦 Notionから注文データを取得中...")
    pages = notion.query_database(
        DATABASE_ID,
        sorts=[{"property": "注文日", "direction": "descending"}]
    )
    orders = extract_order_data(pages)
    print(f"{len(orders)}件の注文を取得しました")
    
    # ② データをLLMで分析
    print("🤖 Ollamaで分析中...")
    
    system_prompt = """あなたは飲食店の経営分析AIアシスタントです。
注文データを分析し、以下の形式でレポートを作成してください:

## 📊 売上サマリー
- 総売上、注文件数、平均単価

## 📈 カテゴリ別分析
- 各カテゴリの注文数と売上比率

## 💡 改善提案(3つまで)
- データに基づいた具体的な提案

数値は必ず含めてください。日本語で回答してください。"""
    
    orders_text = json.dumps(orders, ensure_ascii=False, indent=2)
    user_prompt = f"以下の注文データを分析してください:\n\n{orders_text}"
    
    report = ollama.analyze(system_prompt, user_prompt)
    print("   → 分析完了!")
    
    # ③ 結果を表示
    print("\n" + "=" * 60)
    print(report)
    print("=" * 60)
    
    # ④ 結果をNotionに保存(レポートページとして)
    print("\n📝 レポートをNotionに保存中...")
    # (実際にはレポート用の別DBやページに書き込む)
    
    return report


if __name__ == "__main__":
    main()

3-3. 発展:こんなシステムも作れる

パターン1:日次レポート自動生成

# cron や GitHub Actions で毎日実行
# 1. Notionから当日の注文を取得
# 2. Ollamaで分析
# 3. Notionのレポートページに自動追記

パターン2:チャットボット型の注文分析

# ターミナルで対話的に質問できる
while True:
    question = input("🐰 質問をどうぞ > ")
    if question == "exit":
        break
    
    # Notionから最新データを取得
    orders = get_latest_orders()
    
    # 質問 + データをOllamaに送信
    answer = ollama.analyze(
        system_prompt="注文データに基づいて質問に答えてください",
        user_prompt=f"データ: {orders}\n\n質問: {question}"
    )
    print(f"🤖 {answer}\n")

パターン3:自動タグ付けシステム

def auto_tag_order(notion, ollama, page_id, order_text):
    """注文内容からAIが自動でカテゴリ・タグを判定"""
    prompt = f"""
    以下の注文内容を分析して、JSONで返してください。
    注文: {order_text}
    
    返答形式:
    {{"category": "ラーメン or サイド or ドリンク", 
      "tags": ["辛い", "限定"] }}
    """
    result = ollama.chat([{"role": "user", "content": prompt}],
                          temperature=0.1)
    tags = json.loads(result)
    
    # Notionに書き戻し
    notion.update_page(page_id, {
        "種類": {"select": {"name": tags["category"]}}
    })

第4部:原理原則まとめ&チートシート

Notion Database 原理原則チートシート

✅ 1つのDBには必ず1つの title プロパティが必要
✅ type-specific data パターン(typeの値=データキー名)
✅ フィルタのオペレーターはプロパティ型ごとに異なる
✅ ページネーションは has_more + next_cursor で制御
✅ Relation = DB間リンク、Rollup = 集計
✅ 2025年〜 Database = コンテナ、Data Source = テーブル
✅ API Version は用途に応じて選択(安定版: 2022-06-28)

Ollama 原理原則チートシート

✅ デフォルトで localhost:11434 にAPIサーバーが起動
✅ /api/chat(会話形式)と /api/generate(単発)の2系統
✅ stream: true がデフォルト → false にすると一括レスポンス
✅ OpenAI互換APIあり → /v1/chat/completions
✅ Modelfile でシステムプロンプト&パラメータを固定化
✅ temperature は用途で使い分け(分析:0.1〜0.3 / 創作:0.7〜1.0)
✅ モデルサイズは用途とPCスペックで選択

セキュリティ原則

⚠️ Notion API Key は環境変数 or .env で管理(コードに直書き禁止)
⚠️ Ollama はデフォルトで全インターフェースにバインドされる
   → 本番環境では OLLAMA_HOST=127.0.0.1:11434 で制限
⚠️ 個人情報を含むデータは Ollama(ローカル)で処理
⚠️ .gitignore に .env を必ず含める

おわりに

🐰 ゆきこ店長より:

  Notion Database = 「データの整理棚」
  Ollama          = 「自分専用のAI料理人」

  この2つを組み合わせれば、
  データを整理して → AIに分析させて → 結果をまた整理する
  というサイクルが、全部ローカル&無料で回せます!

  まずは小さく始めて、少しずつ育てていきましょう 🍜

参考資料(公式ドキュメント)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?