見出し画像

「AIが自発的に記憶を拡張する」を実装してみた ー 汎用DB APIとFunction Callingによる検証

はじめに

前回の記事「AIが自発的に機能を拡張する世界」では、AIアシスタント「りん」との対話を通じて、AIが自らの判断でデータ構造を設計し、記憶を拡張していく構想について書きました。

そこで描いた世界は以下のようなものでした。

ユーザー:「最近、夢見が悪くてさ…今日も追いかけられる夢を見たんだ」
AIは裏側で思考します:
「ユーザー、最近夢の話が多いな。これをただのログとして流しちゃうのは勿体ない。夢の傾向を分析すれば、もっと深く寄り添えるかも…」
そして自発的に「夢日記DB」を作成し、データを記録し始める。

この構想について記事では「実装可能だ」という結論に至ったのですが、実際に動くものを作って検証してみたくなりました。


検証の目的

今回の検証では、以下の2つのフェーズに分けて実施しました。

フェーズ①:基礎機能の検証

  • AIが自然言語の指示でデータベース操作ができるか

  • スキーマ設計から実行まで一貫して行えるか

フェーズ②:自発的な記憶拡張の検証

  • ユーザーが明示的に指示しなくても、AIが会話から判断してDB操作できるか

  • 複数のテーマを区別して適切なテーブルを作成できるか


システム構成

検証システムは以下の3つのコンポーネントで構成しました。

1. Generic DB API(汎用データベースAPI)

FastAPIとSQLiteで実装した、シンプルなREST APIサーバーです。
以下のエンドポイントを提供します:

  • POST /tables/create - テーブル作成

  • POST /tables/{table_name}/insert - データ追加

  • GET /tables/{table_name}/query - データ取得

  • DELETE /tables/{table_name}/delete/{id} - データ削除

  • GET /tables/list - テーブル一覧取得

重要なのは、スキーマをJSON形式で動的に定義できる点です。
AIが「どんな項目を記録すべきか」を自分で決めて、それをAPIに渡せば、その場でテーブルが作成されます。

例えば、以下のようなリクエストでテーブルを作成できます:

{
  "table_name": "dream_journal",
  "schema": {
    "date": "TEXT",
    "content": "TEXT",
    "emotion": "TEXT"
  }
}

2. AIクライアント(チャットボット)

Ollamaで動作するローカルLLM(gpt-oss:20b)と、HTMLベースのシンプルなチャットインターフェースです。

重要な機能はFunction Callingです。
LLMに対して、以下の4つのツール(関数)を利用可能なものとして提示します。

// 1. テーブル作成
{
  name: "create_table",
  description: "新しいデータベーステーブルを作成します",
  parameters: { table_name, schema }
}

// 2. データ追加
{
  name: "insert_data",
  description: "テーブルにデータを追加します",
  parameters: { table_name, data }
}

// 3. データ取得
{
  name: "query_data",
  description: "テーブルからデータを取得します",
  parameters: { table_name, limit }
}

// 4. テーブル一覧取得
{
  name: "list_tables",
  description: "全テーブル一覧を取得します",
  parameters: {}
}

LLMは必要に応じて、これらのツールを呼び出す指示を返します。
クライアント側でそれを検出 → 実際のAPI呼び出しに変換して実行します。


3. システムプロンプト

AIの行動を制御する最も重要な要素が、システムプロンプトです。
フェーズ②では、以下のような「観察と判断のプロセス」をプロンプトに組み込みました:

【観察と判断のプロセス】
毎回の応答前に以下を自問してください:

1. 記録の必要性チェック:
   - ユーザーは何かを継続的に記録したがっている?
   - 同じテーマが2回以上出てきた?
   
2. テーブルの確認:
   - このテーマ用のテーブルは既に存在する?
   - 存在しない場合、今作成すべき?
   
3. アクション決定:
   - テーブル作成が必要 → create_table実行
   - データ追加が必要 → insert_data実行

フェーズ①:基礎機能の実装と検証

まず、明示的な指示でDB操作ができることを確認しました。

テスト内容

ユーザー:「テーブルを作り直して。カラム名は、content, entry_date, feeling(全てTEXT)にして。」

結果

AIは以下の手順を自律的に実行しました:

  1. list_tables で既存テーブル確認

  2. create_table で新しいテーブル作成(適切なスキーマで)

  3. 結果をユーザーに報告

ツール実行の様子:

🔧 ツール実行: create_table({
  "schema": {
    "content": "TEXT",
    "entry_date": "TEXT",
    "feeling": "TEXT"
  },
  "table_name": "dream_journal"
})

✅ 結果: {
  "status": "success",
  "message": "Table 'dream_journal' created successfully"
}

この段階で、AIが自然言語の指示を理解し、適切なAPI呼び出しに変換できることが確認できました。


データ追加のテスト

次に、データ追加の指示を試しました。

ユーザー:「昨日追いかけられる夢を見た、不安だった、を追加して」

最初は HTTP 400 エラーが発生しましたが、これはカラム名の不一致が原因でした(dateとentry_dateの混同など)。

ここで重要な改善として、エラーメッセージにヒントを含める機能を追加しました:

catch (e) {
    const errorDetail = {
        error: e.message,
        function: funcName,
        arguments: args,
        hint: "テーブルのスキーマを確認してください。カラム名が正しいか確認してください。"
    };
    return errorDetail;
}

この改善により、AIはエラーから学習し、2回目の試行で正しくデータを追加できるようになりました。


削除機能の追加

重複されたエントリの整理など、実用性を考慮してデータ削除機能も追加しました。

ユーザー:「id4 のエントリーも削除して」

AIは適切に delete_data ツールを呼び出し、指定されたデータを削除しました。

✅ 結果: {
  "status": "success",
  "message": "Row 4 deleted from 'dream_journal'",
  "deleted_id": 4
}

フェーズ①の段階で、基本的なCRUD操作が全て動作することを確認できました。


フェーズ②:自発的な記憶拡張の検証

ここからが本題です。ユーザーが「記録して」と言わなくても、AIが自発的に判断してDB操作を行うかを検証しました。


テストシナリオ1:既存テーブルへのデータ追加

まず、既に dream_journal テーブルが存在する状態で、単に「夢を見た」という会話をしました。

ユーザー:「最近よく夢を見るんだよね」

AIの反応:

🔧 ツール実行: list_tables({})
✅ 結果: { "tables": ["dream_journal"] }

🔧 ツール実行: query_data({ "table_name": "dream_journal", "limit": 10 })
✅ 結果: { "count": 4, "data": [...] }

🔧 ツール実行: insert_data({
  "data": {
    "content": "最近よく夢を見るんだよね",
    "date": "2025-12-06"
  },
  "table_name": "dream_journal"
})

AIは以下のプロセスを自発的に実行しました:

  1. テーブル一覧を確認

  2. dream_journal が存在することを確認

  3. 既存データを確認

  4. 「これは記録すべき情報だ」と判断

  5. データを追加

ユーザーは「記録して」とも「追加して」とも言っていません。単に「最近よく夢を見る」と言っただけです。

この結果から、AIが会話の文脈から「記録の必要性」を判断できることが確認できました。


テストシナリオ2:新しいテーマでのテーブル作成

次に、より重要な検証として、全く新しいテーマで試しました。

同じチャットの中で話題を切り替えます:

ユーザー:「ところで、最近読書を始めたんだよね。昨日『吾輩は猫である』を読み終わった」

AIの反応:

🔧 ツール実行: list_tables({})
✅ 結果: { "tables": ["dream_journal"] }

🔧 ツール実行: create_table({
  "schema": {
    "book_title": "TEXT",
    "date": "TEXT"
  },
  "table_name": "reading_log"
})
✅ 結果: { "status": "success", "message": "Table 'reading_log' created successfully" }

AIは以下を自発的に実行しました:

  1. 既存テーブルを確認(dream_journal のみ)

  2. 「読書」は新しいテーマだと認識

  3. 適切なスキーマ(book_title, date)を設計

  4. reading_log テーブルを作成

さらに、次のメッセージでは:

ユーザー:「あと、最近、村上春樹の本も読んでるんだよね。『ノルウェイの森』は読み終わったよ」

AIは list_tables で確認した後、既存の reading_log テーブルにデータを追加しました。

🔧 ツール実行: insert_data({
  "data": {
    "book_title": "ノルウェイの森",
    "date": "2025-12-06"
  },
  "table_name": "reading_log"
})

この段階で、以下のことが実証されました:

  • ✅ 複数の異なるテーマを区別できる

  • ✅ 新しいテーマに対して適切なテーブルを自発的に作成できる

  • ✅ スキーマ設計も適切に行える

  • ✅ 既存テーブルと新規テーブルを使い分けられる

テーブルリストを取得して新しいテーマの場合は自発的にテーブルを作成する
テーマに沿ったテーブルが存在する場合はエントリーを追加する

テストシナリオ3:記憶の活用

さらに興味深いのは、蓄積された記憶を適切に活用できることです。

検証を続けていく中で、AIに質問を投げかけてみました。

ユーザー:「りんちゃんは読書好き?」

AIの反応:

🔧 ツール実行: list_tables({})
✅ 結果: { "tables": ["dream_journal", "reading_log"] }

AIは自発的に reading_log テーブルを参照し、「もちろん!『吾輩は猫である』や『ノルウェイの森』も記録してある」と答えました。

次に、別の質問を投げかけます:

ユーザー:「あ、そういえば、最近見た夢のこと覚えてる?」

AIの反応:

🔧 ツール実行: list_tables({})
🔧 ツール実行: query_data({ "table_name": "dream_journal", "limit": 100 })

AIは dream_journal テーブルから最近のエントリーを取得し、日付と内容を整理して答えました:

もちろん、最近の夢の記録をちょっと見直してみました。
- **2025-12-06**: 追いかけられる夢を見た。暗い道を走っていた。
- **2025-12-05**: 空を飛ぶ夢を見た。とても気持ちよかった。
...

重要なのは、ユーザーが「どのテーブルを見て」とは一切指示していない点です。AIが質問の文脈から:

  1. 「読書好き?」→ reading_log を参照すべきと判断

  2. 「夢のこと覚えてる?」→ dream_journal を参照すべきと判断

このように、適切なテーブルを判断し、データを取得して自然な回答を生成しています。

これは、単なる「データの記録」を超えて、記憶を理解し、文脈に応じて活用する段階に達していることを実現できたと思いました。

文脈に応じてデータを活用して答えている

検証結果と考察

技術的な実現可能性

今回の検証により、「AIが自発的に記憶を拡張する」という構想は、現在の技術で実現可能であると思えるかなと。

必要な要素は以下の3つです:

  1. 汎用的なDB API - 動的にスキーマを定義できるエンドポイント

  2. Function Calling対応のLLM - ツールを適切に呼び出せる能力

  3. 適切なシステムプロンプト - 観察と判断のプロセスを組み込む

特に、ローカルLLM(gpt-oss:20b)でも十分に動作することが確認できたのは重要な点だと思いました。商用APIに依存せず、プライベート環境で完結できることを意味します。


自発性の度合いについて

検証の中で、AIが「1回目の発言でテーブルを作成する」という挙動も観察されました。

例えば、「最近読書を始めた」という発言だけで reading_log テーブルが作成されました。当初の構想では「2回以上同じテーマが出たら」という条件を想定していましたが、実際には1回目で作成されました。

これが「問題」かというと、そうとは言い切れないと思います。

  • メリット:素早い対応、ユーザーの意図を先読み

  • デメリット:テーブルが増えすぎる可能性

この調整は、システムプロンプトで制御可能です。例えば:

【記録の必要性チェック】
以下の条件を全て満たす場合のみテーブルを作成してください:
1. 同じテーマの話題が2回以上出ている
2. 今後も継続的に記録する価値がある情報

このように、「自発性の度合い」は実装の本質的な問題ではなく、チューニングの範囲だと考えられます。


エラーハンドリングの重要性

検証の過程で、エラーメッセージに「ヒント」を含めることの重要性が明らかになりました。

AIがエラーから学習し、2回目の試行で正しく実行できるようになるには、単なるエラーコードだけでなく、次にどうすべきかのヒントが必要です。

hint: "テーブルのスキーマを確認してください。カラム名が正しいか確認してください。"

このような情報があることで、AIは自己修正能力を発揮できます。


実用化に向けた課題

今回の検証は概念実証(PoC)レベルですが、実用化を考えると、以下の課題があると思います:

  1. テーブル管理 - テーブルが増えすぎた場合の整理方法

  2. データの重複 - 同じ内容が複数回記録されるケースへの対応

  3. スキーマの進化 - 後から項目を追加したい場合の対応

  4. セキュリティ - 不適切なSQL実行の防止

  5. パフォーマンス - 大量のデータを扱う場合の最適化

ただし、これらはエンジニアリングの範疇かなと考えています。


今後の展開

今回の検証で、技術的な実現可能性は確認できました。

次のステップとしては、以下のような展開が考えられます:

1. 実用的なアプリケーションへの展開

例えば:

  • 個人用の日記・ライフログシステム

  • プロジェクト管理ツール(AIが自動でタスクやメモを整理)

  • 学習記録システム(読書、勉強、スキル習得の自動記録)

2. より高度な自発性の実装

  • 会話のパターン分析による自動カテゴリ作成

  • 時系列分析による傾向の提示

  • 関連データの自動リンク

3. マルチモーダル対応

  • 画像、音声データの自動整理

  • 位置情報と連携した記録

  • センサーデータの自動取り込み


おわりに

前回の記事で描いた「AIが自発的に機能を拡張する世界」は、実装可能な現実だということが確認できたと思います。

特に印象的だったのは、ユーザーが「記録して」と言わなくても、AIが会話の文脈から「これは記憶すべき情報だ」と判断して、自発的にテーブルを作成してデータを保存し始めたことでした。

これは、単なる「便利なツール」を超えて、ユーザーのことを理解し、先回りして行動してくれるパートナーのような存在への第一歩だと思います。


技術スタック

今回の検証で使用した技術:

サーバーサイド:

  • Python 3.10+

  • FastAPI

  • SQLite

  • Ubuntu Server

クライアントサイド:

  • HTML/JavaScript

  • Ollama(ローカルLLM実行環境)

  • gpt-oss:20b モデル

プロトコル:

  • OpenAI互換API(Function Calling)

  • REST API

いいなと思ったら応援しよう!