KarpathyのLLM Wiki投稿が届いたとき、私はすでにTickTickのセマンティック検索を—ベクトルストアにはqdrant、埋め込みにはollama経由のnomic-embed-text、インデックスを常に新鮮に保つための毎日のcronなど—揃えていました。いわゆる“機能一式”はできていたのです。エージェント側のリトリーブは欠けているピースではありませんでした。
欠けていたのは構造でした。Karpathyの切り口—ウィキを指定する、LLMの読者向けにメモを書く、分類体系(taxonomy)ではなくリトリーブに寄せる—によって、私のセットアップのうち、まだ形になっていない部分が浮かび上がりました。つまり、耐久性のある知識がどこにあるのかと一時的なタスクの違い、エージェントが人間の書いたメモからどうやって構造化データを引き出すのか、そして、既存のセマンティック検索がときどき正しい答えを返し、別のときは役に立たない結果しか返さない理由です。
それでも私は、とりあえず素のmarkdownへ移行しそうになりました。何千もの耐久的なメモ—制作プレイブック、APIのクセ、そして来月のタスクリストにも耐えてほしい意思決定—はすでにTickTick上にあります。スマホとも同期しています。摩擦のないキャプチャです。移行すると、それがすべて壊れます。
そこで私は、TickTickの上にウィキ構造を構築し、ストレージ層を差し替え可能にしました。リトリーブ、ウィキの慣習、エージェント用データのメモのパターン、ベンチハーネス—それらはどれもTickTick固有ではありません。それらは小さなフレームワークです。あなたがすでに何年もキャプチャ習慣として投資してきたものに向けてポイントするだけです。TickTick / Notion / Obsidian / Things / markdownフォルダ / あるいは何でも。
私はそれをAgentic Knowledge Baseと呼んでいます。
フレームワークを1つの図で
┌───────────────────────────────────┐
│ エージェント(Claude / スクリプト / cron) │
└─────────────────┬─────────────────┘
│ akb find / get / url / links
▼
┌───────────────────────────────────┐
│ AKB Core │
│ • 並列リトリーブ + RRF │
│ • コーパスキャッシュ(5分TTL) │
│ • ベンチハーネス │
│ • 利用ロギング │
└─────────────────┬─────────────────┘
│ adapterインターフェース
┌─────────────────┼──────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ adapter- │ │ adapter- │ │ adapter- │
│ ticktick │ │ obsidian │ │ notion │
│ (参照) │ │ (ファイルシステム) │ (次はあなたの番) │
└────────────────┘ └────────────────┘ └────────────────┘
Coreはストレージに依存しません。リトリーブ、キャッシュ、ベンチ、利用ロガー—それらはどれもTickTickが何かを知りません。少数のアダプタ・インターフェース(約6メソッド)を呼び出すだけです。
この枠組みにおけるKarpathyのセットアップは、より広いパターンのファイルシステム・アダプタです。私のはTickTickアダプタ。あなたのはNotionかObsidianのものになるかもしれません。
アダプタ・インターフェース
6つのメソッド。2つのペイロード形式。
interface KnowledgeAdapter {
listProjects(): Promise<Project[]>
listTasksInProject(projectId: string): Promise<Task[]>
getTask(projectId: string, taskId: string): Promise<Task>
createTask(input: TaskInput): Promise<Task>
updateTask(projectId: string, taskId: string, patch: TaskPatch): Promise<Task>
urlFor(ref: { projectId: string, taskId: string }): string // deep-link文字列
}
type Project = { id: string, name: string, kind?: 'tasks' | 'notes' }
type Task = { id: string, title: string, content: string, projectId: string, tags: string[], dueDate?: string, modifiedTime: string}
一覧表示できる、取得できる、そしてリンクにできるもの—タスク管理システム、ノートアプリ、プレーンなフォルダ—はすべてアダプタになり得ます。
ストレージにネイティブの検索エンドポイントがあるなら、アダプタはオプションのsearchByQuery(query)を実装でき、Coreは並列リトリーブの分岐の1つとしてそれを使います。ない場合は、Coreはコーパスに対する自前のキーワードスキャンへフォールバックします。
それがインターフェースのすべてです。面白いものはすべてCoreにあります。
Coreが実装する2つのパターン(ぜひ盗む価値あり)
1. エージェント用データのメモ
本文に fenced を持つ通常のメモ(
json(または
yaml)のブロック)。人間は先頭の散文を読みます。エージェントはアダプタ経由でJSONを抽出します:
返却形式: {"translated": "翻訳されたHTML"}
**タイプ:** agent-data
**消費先:** EOD triage cron、capture-time relevance enrichment
「トランク(trunk)」は、ユーザーが関心を寄せているアクティブなプロジェクトです。プロジェクトが開始・完了・方向転換したときに、このリストを編集します。
```
json
{
"trunks": [
{ "name": "release-engineering", "desc": "出荷サイクル、デプロイの儀式、オンコールのローテーション" },
{ "name": "writing-projects", "desc": "個人およびクライアントのチャネルにまたがる下書きと編集" }
]
}
```
```
`
どのcronやエージェントからでも読み取れます:
```bash
akb get "Trunk Catalog" --extract json | jq '.trunks[].name'
```
利点はこれです。1つのメモを、既存アプリ上でモバイル編集でき、エージェントが構造化データとして消費できます。**単一の真実(Single source of truth)、スキーマ移行なし。** このパターンは、エージェントがプログラム的に必要とし、人が移動中に編集する必要があるものなら何にでも機能します。たとえばプロンプトテンプレート、動画プロジェクト用のキャラクターロック、定期的なクエリ、cron設定などです。
### 2. 出所(provenance)付きで並列取得
3つのリトリーバーを、共有されたキャッシュ済みコーパスに対して並列で実行します。結果はRRFで融合し、上位-Kには「どのリトリーバーが一致したか」をタグ付けして返します:
- **Hybrid** — 緻密コサイン(qdrant + nomic-embed)+スパースキーワード、内部でRRF融合
- **Keyword** — タイトル+コンテンツの部分文字列マッチ
- **Notes-find** — 指定したWikiプロジェクトに対するタイトルのファジーマッチ
`openrouter api key` のようなクエリでは、3つのリトリーバーが同じ“金(gold)ノート”を返します。融合結果は `sources: [hybrid, keyword, notes_find]` とタグ付けします。3つの独立したシグナルが一致しているなら、高い確度です。順位が低い結果にはソースが1つしかありません。そこは懐疑的に見てください。
`ffmpeg commands` のようなクエリでは、キーワードツールが外します(そのリテラルなフレーズがどのドキュメントにも入っていないため)。純粋な意味ベースでも外します(nomic-embedは `ffmpeg` のような短いタイトルを過小評価しがち)。しかしHybridが拾います。非対称性にも破綻せず対応できるのは、異なるクエリが異なるリトリーバーに寄りかかり、そして中核が「単一のアルゴリズムが普遍的に最適」だと見せかけないからです。
5分間ディスクに裏付けされたコーパスキャッシュにより、ウォームクエリはサブ100msです。コールドスタート直後の最初の呼び出しは、あなたのフルのタスク/ノート一覧を取得します(1バッチ——それをサポートするアダプタは単一のAPI呼び出しで済ませ、不対応なアダプタはプロジェクトごとの反復にフォールバックします)。作業セッション中は、取得が実質無料です。
## ベンチ(bench)
`bench/` に小さなベンチハーネスを作りました。質問はゴールド回答(答えそのものを含むタスクまたはノート)とペアにしています。各リトリーバーは同じ質問セットに対して動作し、hit@1 / recall@5 / MRR でスコア付けします。
5つのエージェント発行クエリ(Opus 4.7が実際に生成するリフレーズ版。人がタイプする自然言語形ではありません):
| Method | hit@1 | recall@5 | MRR | warm latency |
| ----------------------------------- | ----- | -------- | ---- | ------------- |
| keyword (substring) | 20% | 20% | 0.20 | <100ms |
| semantic (dense only) | 20% | 40% | 0.30 | ~300ms |
| hybrid (dense + sparse RRF) | 60% | 80% | 0.70 | ~500ms |
| **find** (parallel + cache) | **60%** | **80%** | **0.70** | **~93ms** |
`find` は精度で `hybrid` に匹敵し、ウォームレイテンシでは約5倍速いです。さらに出所タグ(provenance tags)も付きます。ベンチは5つの質問から一般化するものではありません——これは先行指標です。特定のアダプタへの確信が積み上がるにつれて、ベンチも育てていってください。
## なぜ私は“自分のため”ではなく“モデルのため”に最適化したのか
着地するまでに、あまりにも多くの試行回数を要した微妙な見方の転換があります。
検索するとき、私は単語を1つ打ちます: `ffmpeg`。キーワードツールは即座に正しいノートを返します。
一方で *Claude* が私の代わりに検索するとき—「ffmpegのワークフローをどこに記録したっけ?」— だいたい `find "What ffmpeg commands do I have notes on?"` のような形のものを発行します。形がまるで違います。モデルはより長いクエリを書きます。質問としての言い回しを使い、スコープ語も含めます。
人間のクエリに最適化するのは間違った目的でした。ユーザー(私)がこれらのツールを直接使っていたわけではなく、Claudeが使っていました。すべての取得テストは、Opus 4.7が実際に生成する形で書く必要がありました——私がタイプするであろう書き方ではありません。これが勝者のリトリーバーを変えます。
明日のモデルは、クエリを書き方が変わります。ベンチは、クエリ形状についての固定された仮定ではなく、**実際に使っているモデル**を追跡するべきです。ベンチファイルは短くて古いので、モデルが変わったら再チューニングしてください。
## 私が(まだ)意図的に作らなかったもの
KarpathyのWiki投稿では、事実が変わったときにノートを定期的に更新する——知識ベース全体へ新情報を伝播させる——という話が触れられています。大規模では有用ですが、自動でノートを書き換えるのは影響範囲が大きく、信頼できるようになるまで承認の段階が必要です。私はスケッチしました。影響を受けたノートをセマンティック検索で拾う週次のcron、下書き更新の作成、私の承認のキュー投入、承認済みのみ適用。——見送りました。
同じく「Wikiをlintする」パス(Karpathyのアイデア: エージェントが毎週すべてのノートを読み、欠けている要約、宙にぶら下がった参照、矛盾をフラグする)もあります。これも大規模では有用ですが、Wiki自体がまだ建設中の段階では早すぎます。
どちらも、出荷時にはCoreに含まれる予定です——設計としてアダプタ非依存です。
## 毎日のフロー(私のセットアップ、あなたのツールは任意)
これが回ります:
- **Capture(モバイル、手動).** 自分のストレージアプリにタスクまたはノートを追加します。CLIは関係ありません。摩擦はゼロであるべきです。
- **Capture-time relevance prompt(Claudeセッションのとき).** `akb create "..." --relevance` は、結果に小さな指示ブロックを付加します。アクティブなClaudeがそれを読み取り、プロジェクトのトランクを選び、`akb update` を呼び出して `why: <trunk> — <reason>` の1行を追記します。LLM側の推論が5秒あることで、そのタスクは後でかなり検索されやすくなります。
- **EOD triage(cron、毎朝).** 昨日完了したタスクを取り出し、トランクに対して0〜3でスコア付けします(`Trunk Catalog` の agent-data メモからライブに読み取ります)。そしてトランクごとにグループ化した“キーパー”をTelegramメッセージで送ります。朝食と一緒に、私はそれをスマホで読みます。
- **Retrieval(仕事中、あらゆる場面で).** Claudeが文脈を必要とするとき — `akb find <query>` は出所付きで上位-Kを返します。キャッシュされ、並列化され、ウォーム時はサブ100ms。
「TickTick app」を「Notion / Obsidian / Things」に置き換えても、フローは同一です。アダプタが変わるだけで、日々の儀式は変わりません。
## ロードマップ
- **v0.1** — Core+リファレンスTickTickアダプタ+ベンチ(現在の私はここ)
- **v0.2** — ファイルシステムアダプタ(Karpathyスタイルの手元ローカルmarkdown)。たぶん週末1つ分の作業。
- **v0.3** — Notionアダプタ(コミュニティ貢献として最も可能性が高い)
- **v0.4** — lintパス+事実伝播キュー(承認ゲート付き)
- **v0.5** — Apple Notes / Things / iA Writer 向けアダプタ(Macネイティブなキャプチャ)
## コード
> https://github.com/renezander030/agentic-knowledge-base — https://github.com/renezander030/agentic-knowledge-base
> https://gist.github.com/renezander030/6c8c3cd62dedaf6e78ffb5b5493830c6 — https://gist.github.com/renezander030/agentic-knowledge-base
KarpathyのWikiのアイデアは正しいです。既存のシステムに収まる実装とは、markdownのフォルダ群ではありません。あなたがすでに持っているものを、モデルが推論できる形に変える、エージェント側のプリミティブのことです。
自分でアダプタを書くなら、それを見せてほしいです。
—
*https://renezander.com/p/agentic-knowledge-base/ から投稿。ソースは https://github.com/renezander030/agentic-knowledge-base.*
---
*実際のビルドからフィールドノートを書いています — AI統合、cron駆動の自動化、本番で壊れる部分。2週間ごとに新しい投稿を [renezander.com](https://renezander.com) に掲載します。*




