Skip to content

Latest commit

 

History

History
1197 lines (993 loc) · 91 KB

File metadata and controls

1197 lines (993 loc) · 91 KB

API リファレンス

Chiezo から知識を取り出す口の詳細仕様です。経路は 3 つあり、どれも中身は同じ関数を呼びます。

  • RESTcurl と人間向け HTML 画面
  • MCP — Streamable HTTP。chiezo-app 自身が MCP サーバー
  • Claude Code 連携 — CLAUDE.md ブロック・権限・MCP 登録の自動生成

なぜこの形なのかは 設計メモ が正です。

REST

BASE=http://<サーバーIP>:7010

# 日本語・スペース等を含むパラメータ(q/title/prefix/area/feature/tag)は、URL に直接埋め込む
# のではなく `-G --data-urlencode` で渡す(生の UTF-8 はサーバーに弾かれるため)
curl -s "$BASE/v1/sources"                                          # ソース一覧
curl -sG "$BASE/v1/jawiki/search?limit=5" --data-urlencode "q=浅草寺"                    # 全文検索
curl -sG "$BASE/v1/jawiki/doc?fields=title,opening,tags" --data-urlencode "title=浅草寺" # 文書概要
curl -sG "$BASE/v1/jawiki/doc?max_chars=8000" --data-urlencode "title=浅草寺"            # 文書全文(切り詰め)
curl -sG "$BASE/v1/jawiki/doc?fields=title,extra" --data-urlencode "title=浅草寺"        # ページビュー等の付加情報
curl -sG "$BASE/v1/jawiki/titles" --data-urlencode "prefix=浅草"                         # タイトル前方一致
curl -sG "$BASE/v1/jawiki/links" --data-urlencode "title=浅草寺"                         # リンク先一覧
curl -s "$BASE/v1/jawiki/random?limit=3"                            # ランダム文書

# タグ(Wikipedia のカテゴリ)。カテゴリの全記事を列挙するのはこちらで、
# 本文の全文検索で "Category:" 行を拾ってはいけない(後述の取りこぼしがある)
curl -sG "$BASE/v1/jawiki/tags" --data-urlencode "contains=ラーメン"                     # タグ名を文書数つきで探す
curl -sG "$BASE/v1/jawiki/filter?limit=200" --data-urlencode "tag=ラーメン店"            # そのカテゴリの記事を全件

curl -sG "$BASE/v1/osm_japan/search?limit=5" --data-urlencode "q=富士山"                 # 地名・POI検索(同一エンドポイント)
curl -sG "$BASE/v1/osm_japan/doc?fields=title,extra" --data-urlencode "title=京都市"     # 座標・OSMタグ等

# 属性での一括抽出(全文検索ではなく等価・範囲条件。Overpass API 相当)
curl -sG "$BASE/v1/osm_japan/filter?limit=200" \
  --data-urlencode "feature=amenity=place_of_worship" --data-urlencode "area=京都府"
curl -sG "$BASE/v1/osm_japan/filter?bbox=34.9,135.6,35.1,135.9" --data-urlencode "feature=tourism=museum"
curl -s "$BASE/v1/jawiki/filter?wikidata=Q17221&fields=title,extra" # Q 番号 → 記事の逆引き

エンドポイントごとの仕様

  • searchlimit 既定 10・最大 50。3 文字以上の語が無いクエリは自動的にタイトル前方一致へ フォールバックし、レスポンスの "mode""title_prefix" になります(通常は "fts")。 並び順は 2 段で、タイトルが検索語と完全一致する文書が先、残りが 関連度(bm25)× 知名度(rank_score)の順です(理由は 設計メモ)。rank_score が入っていない古い DB では 2 段目が bm25 だけになります(既存 DB の rank_score を入れ直す)。 filter と同じ area / feature / bbox を併用でき、同名の別地物を掴む取り違えを避けられます (例: search?q=八坂神社&area=京都府)。

  • doctitle 完全一致 → リダイレクト(alias)解決 → 見つからなければ 404 と近似候補 5 件。 fields(既定 title,opening,body,tags,updated_at)と max_chars で応答サイズを制御できます。 同名の別地物が他にもある場合は alternatives(doc_id / title / feature / area / lat / lon を最大 5 件)を併記するので、取り違えにその場で気づけます。area / feature / bbox で最初から絞り込むこともできます(例: doc?title=博多駅&feature=railway%3Dstation)。

  • filter — 属性での絞り込み一括抽出。feature(amenity=place_of_worship 形式。カンマ区切りで 複数可)・area(所属行政区名)・bbox(min_lat,min_lon,max_lat,max_lon)・wikidata(Q 番号)・ tag(タグ = Wikipedia のカテゴリ等。カンマ区切りで複数可、その中は OR)を AND で組み合わせます。1 つ以上の条件が必須(無指定は 400)。limit 既定 50・最大 500、応答の totaloffset でページングできます。並びは rank_score の降順です。 schema_version 2 以降が必要で(1 の DB には 409)、tag は 3 以降、bbox と 大きな該当件数の並べ替えは 4 以降が実用的な速さになります。古い DB は その場で移行できます (どの版で何が速くなるかは 設計メモ)。

  • tags — タグ名を文書数つきで列挙します(prefix = 前方一致 / contains = 部分一致 / 無指定 = 文書数の多い順)。filter?tag= はタグ名の完全一致なので、Wikipedia の カテゴリのように表記の揺れがあるものは、まずここで実在する名前を確かめてから引くのが 確実です(schema_version 3 以降。4 以降は集計表を引くので部分一致も速い)。

  • query(POST) — SQL で直に引きます(SELECT だけ・読むだけ)。search / filter では数えられないこと —— タグの共起・期間ごとの件数・上位 N —— のための口で、 無いと引く側が全件を持ち帰って自分で数えることになります。 書き込み・ATTACHPRAGMA・複数文は落とします(接続は読み取り専用で開いたうえ、 authorizer が SELECT 以外を拒みます。とくに ATTACH は、読み取り専用でも別のファイルを 足せるため)。LIMIT は書かなくても付きます(既定 50・最大 500。切ったときは truncated)。 エラーはそのまま返します —— 列名が違うのか表が無いのかを確かめられないと直せません。

    curl -s "$BASE/v1/jawiki/query" -H 'Content-Type: application/json' -d '{
      "sql": "SELECT a.tag, b.tag, COUNT(*) c FROM doc_tags a JOIN doc_tags b ON a.doc_id = b.doc_id AND a.tag < b.tag GROUP BY 1,2 ORDER BY c DESC",
      "limit": 20
    }'

    主な表(どのソースも同じ形): docs(doc_id, title, opening, body, tags, links, updated_at, rank_score, extra)(tags / links / extra は JSON。feature / area / lat / lon / wikidataextra からの生成列)、doc_tags(tag, doc_id)tag_counts(tag, docs)aliases(alias, doc_id)doc_coords(lat, lon, doc_id)docs_fts(title, body)

  • titles / links / random — タイトル前方一致 / その文書から出ているリンク先の一覧 / ランダム抽出。links出リンクのみで、被リンク(その文書を指している文書)は取れません。

  • 全クエリ 5 秒タイムアウト(超過は 504)。エラーは {"error": "..."} 形式。 504 は「該当 0 件」ではなく「取れなかった」を意味しますfilter でページングしながら 全件集める用途では、空の結果として扱うとそのまとまりを丸ごと取りこぼすので、 limit を小さくして取り直してください。

カテゴリの全記事を列挙したいときは、本文の全文検索(search?q=Category:ラーメン店)ではなく filter?tag= を使ってください。 全文検索だとソートキー付きのカテゴリを静かに 取りこぼします(実データで 115 件中 16 件が漏れていました。 設計メモ)。

extra フィールドの中身

ソース固有の情報はすべて docs.extra(JSON)に入ります。コアスキーマは全ソース共通なので、 API 側はソース種別を意識しません。

jawiki(Wikipedia 系)

  • 座標を持つ記事({{Coord}} テンプレートや、駅・空港・施設の Infobox の 緯度度/経度度 系、 会社の Infobox(基礎情報 会社)の 本社緯度度/本店緯度度 系の引数から抽出)には {"lat": ..., "lon": ...} が入ります。
  • ページビューを突合できた記事には {"pageviews_month": <月間閲覧数>, "pageviews_period": "YYYY-MM"} が入ります(Wikimedia の pageview_complete 月次ダンプ由来、bot 除外・全アクセス種別合算)。突合できなかった記事は null
  • page_props ダンプから wikidata の Q 番号が取れた記事には {"wikidata": "Q17221"} も入り、 filter?wikidata= で逆引きできます(OSM 側の wikidata タグと突き合わせられます)。

osm_japan(OSM 系)

{"osm_type": "node|way|relation", "osm_id": ..., "lat": ..., "lon": ..., "feature": "place=city", "tags": {<OSM 生タグ>}, ...}

  • way / relation の座標は構成ノードの平均(近似重心)で、行政境界は admin_centre / label ノードを優先します。
  • 同名地物はタイトルを「名前 (node:123)」形式で弁別し、元の名前は alias として引けます。
  • POI(amenity / shop / tourism / leisure / historic / craft / office / healthcare)では、住所・電話・サイト・営業時間が取れれば address / phone / website / opening_hours も入ります。
  • 地名・POI・交通インフラ(駅・空港・港・IC/SA 等)は同一ソース内に混在し、search は すべてをヒットさせます(駅が同名の店舗より上に来るよう rank_score を高くしてあります)。
  • 座標を持つ地物には所属行政区が area として付きます(日本では都道府県。粒度は OSM_AREA_ADMIN_LEVEL で変更可)。

geonames

{"lat": ..., "lon": ..., "feature": "P=PPLC", "country_code": "FR"} が常に入り、 取れたものだけ area(1 次行政区。無ければ国名)・countrypopulationtimezoneelevationwikidata が加わります。extra.feature は OSM と同じ <class>=<code> 形式に 揃えてあるので、filter?feature= の使い方は共通です。GeoNames は本文を持たないため、 opening / body には「名前(行政区, 国)— コード / 分類、人口 N」の 1 行を組み立てて 入れています(FTS を効かせるため)。

chiezo_memory(唯一書き込めるソース)の REST

curl -s "$BASE/v1/chiezo_memory" -H 'Content-Type: application/json' \
  -d '{"text":"開発環境を WSL2 へ移行する","tags":"環境,決定"}'

curl -s "$BASE/v1/chiezo_memory/recall"                                  # 新しい順に 20 件
curl -sG "$BASE/v1/chiezo_memory/recall" --data-urlencode "q=移行"        # 全文検索
curl -sG "$BASE/v1/chiezo_memory/recall" -d since=2026-07-01             # 期間で絞る
curl -sG "$BASE/v1/chiezo_memory/recall" --data-urlencode "tag=決定"      # タグで絞る
curl -sG "$BASE/v1/chiezo_memory/recall" -d fields=title,updated_at      # 当たりを付ける(本文を載せない)
curl -sG "$BASE/v1/chiezo_memory/recall" -d max_chars=0                  # 本文を切らずに返す
curl -sG "$BASE/v1/chiezo_memory/recall" -d fields=doc_id,extra           # 構造だけ取る(既定では返らない)
curl -s -X PATCH "$BASE/v1/chiezo_memory/3" -H 'Content-Type: application/json' \
  -d '{"tags":"環境,決定,完了"}'                                  # 書き換え(渡した項目だけ)
curl -s -X DELETE "$BASE/v1/chiezo_memory/3"                             # 取り消し

書き換え(PATCH)は渡した項目だけを差し替えます(text / title / tags / extra)。 tags はカンマ区切りの丸ごと置き換えで、空文字を渡すと全部外れます。 extra も丸ごと置き換えで、空の dict を渡すと外れます。 updated_at が現在時刻になるので、書き換えたメモは recall の先頭に浮きます。

extraタグで表せない構造の置き場です(いまはタスク・ルールの並び順 sort_order)。 種別・状態・所属はタグで表すのが基本で、extra に入れるのはタグにすると読めなくなるもの だけにしてください。recall の既定の項目には入りません —— 入れると extra を持たない ほとんどのメモにも "extra": null が並び、recall を読む AI のコンテキストを無駄に食う ためで、要る側が fields で名指しして取りに来ます。

タグには定番の語彙があります(task = 作業、着手中完了難所rule無効決定runbook環境本番設計メモトラブルシュート_chiezo_tombstone = 長期記憶から落とす墓標。 プロジェクトはリポジトリ名を小文字で)。task / rule を持つメモは ToDo の画面(/admin/todo)の担当で、どれも持たないメモは ブラウズ画面(/search/chiezo_memory/)で読みます。 語彙は app/notes.pyCANONICAL_TAGS が 1 か所で持ち、MCP の remember の ツール定義として配られます —— 書き手が変わっても同じ意味に同じ表記が付くようにするためで、 ここに無いタグも自由に付けられます。curl で書くときもこの表記に合わせてください。

本文は既定で先頭 400 文字までしか返りません(max_chars)。切れたメモには truncated: true が付くので、全文が要るものだけ /v1/chiezo_memory/doc/{doc_id} で取り直します。 当たった件数ぶんの全文が会話のコンテキストに載るのを避けるためで、他ソースの search(冒頭だけ)→ doc(全文)と同じ二段構えです。fields で項目を選べば 本文そのものを外せます(doc_id / title / text / tags / updated_at / url)。

専用の口は追記・書き換え・削除・時系列の想起だけです。読み出しはコアスキーマなので /v1/chiezo_memory/searchdocfiltertags/search/chiezo_memory/(ブラウズ画面)もそのまま効きます。

変数 既定 説明
CHIEZO_NOTES_DIR /notes(compose) 書き込み可能なディレクトリ。空にすると機能ごと無効(/v1/chiezo_memory は 503、MCP の道具も出ない)

compose では既定で有効で、./notes に SQLite が 1 つできます(初回アクセス時に自動生成 されるので、取り込みを回す必要はありません)。/data は読み取り専用マウントのままです。 notes を別ディレクトリに置いているのは、/data の変化を監視して全ソースを再走査する 仕組みと干渉させないためで、その理由となぜ Chiezo に置くのかは 設計メモにあります。

認証はありません。 /v1/chiezo_memory に到達できる相手は誰でも書けます(LAN 内前提という このサービス全体の方針と同じですが、書き込みができる唯一の口である点は留意してください)。

集める(AI に集めさせて溜めていく)

まとまったダンプの無いもの ——直近のニュース、入れ替わりの速い店、人物の関係——を AI に集めさせ、引ける形で溜めていく層(app/collect.py)。 CHIEZO_STATE_DIR(収集の定義の置き場)と CHIEZO_TRIGGER_URL(取り込みを起こす相手)が 要る。途中の置き場は持たない —— 集めるのも焼くのも取り込みの中で起きるため。 trigger が条件に入るのは、それが無いと集める手段が 1 つも無いから(corpus を持たない面では 定義を置いても永遠に走らない)。

定義は機械が書き換えるものの置き場(state/chiezo_settings.dbapp/machine_store.py)に 入る。かつては短期記憶(CHIEZO_NOTES_DIR)に 1 件のメモとして置いていたが、あそこは 人と AI が読み書きする場所なので、混ぜると人が消せてしまう1 件に収める都合で中身に上限が要る(消したものの控えが 2,000 件で頭打ちになり、 溢れると古いものから静かに戻ってきた)。

置いてあるものは管理画面から読める(記憶の面の「設定の置き場」)。人が触らない 置き場だが、見えないままだと「設定がどこに、いつの姿で残っているか」を確かめる 手立てが無い —— 収集が消えた・戻ってきたのような話を追うときに要る。 読むだけで、直すのはそれぞれの画面から(収集なら「集める」の各収集の面)。

集めるのは取り込みの中。ingest が素材を取りに来た瞬間(GET /v1/collect/fetch)に AI を呼び、集めたものをそのまま NDJSON で返す。焼くのは ingest (corpus/ に書けるのは ingest だけ、という線を崩さないため)。焼いた後は /v1/<収集名>/search のように普通のソースとして引ける。

取ってきた素材は他のソースのダンプと同じく dumps/ に置き、焼き上がってから消す。 途中で落ちても、次に走らせたときは AI を呼び直さずその場に残ったものから焼ける (ダンプと違って、取り直しても同じものは返ってこない)。

毎回焼き直すが中身は積み上がる。 素材が「前世代 + いま集めたぶん」なので、 ブルーグリーンに乗せたまま追記として振る舞う(世代は今と 1 つ前だけ残る)。 同じ見出しは新しく集めたほうで置き換わり、件数としては増えない。

最初の 1 回を機械で埋める(extract)

名前・年代・出典のように既に手元の長期記憶に書いてあることは、引けば済むのに AI に 書かせると存在しないものが混ざる。extract を持つ収集は、進み具合が空のあいだの 1 回だけ AI を呼ばずに手元から組み立てる。

{
  "source": "jawiki",
  "tag_suffix": "の画家",
  "not_tag": "俳優,小説家,児童文学作家",
  "tags": [
    {"const": "画家"},
    {"patterns": ["^(\\d{3,4})年生$", "^(\\d{3,4})年没$"], "format": "年代:{1}-{2}"},
    {"linked": "mutual", "format": "関連:{1}"}
  ],
  "extra": ["pageviews_month"]
}

元の記事に載っている事実は extra で運ぶ。 知名度(月次ページビュー)や座標のように、 長期記憶に既に入っている値を名指しすると、集めた 1 件の extra にそのまま載る。 読む側が 1 件ずつ引き直さなくて済む —— 引き直していた頃は、図を開くたびに 画家の数だけ往復していた(初回で 9,000 回以上)。

  • 名指ししたものだけ(記事の丸写しにはしない)。持っていない記事は黙って飛ばす
  • そのまま載る値だけ(文字列・数・真偽)。入れ子は通さない
脇書きは重ねる(extra)

1 件は焼くたびに丸ごと置き換わるが、extra だけは重ねる

  • 書いたものだけが変わる。 触れなかった鍵はそのまま残る —— 機械で運んだ事実は AI が手を入れる回には返ってこないので、置き換えると最初の手入れで静かに消える (1 件ずつ減るので、消えすぎの歯止めもすり抜ける)
  • null は「この鍵を消して」の印。 書かなければ残る作りなので、間違って入った値を 落とす口がここに要る
  • 足すだけの回(only_new)は、まだ無い鍵だけを埋める。 上書きも削除もしない —— 後から指定に鍵を足しても、既にあるものには届かないままになるため
  • 数には天井がある(消さない作りなので、置かないと回を重ねるだけ増える)

族をなすカテゴリは tag_suffix で指す。 「〜の画家」「〜の作曲家」のように同じ形の 名前がずらりと並ぶものを tag に書き並べると、書く側が名前を思い出しで補うことになり、 抜けても気づけない。実例: 地域を書き並べた画家の指定で「アメリカ合衆国」(879 記事)が 丸ごと落ち、「イギリス」と書いたせいで中身の大半がある「イングランド」(608 記事)が 取れていなかった。末尾で指せば推測が要らず、あとからカテゴリが増えても勝手に入る

  • tag と併用できる(両方の和。族に入らない 1 つを足したいときに使う)
  • 要らないものは not_tag で外す
  • 当たりすぎたら断る(黙って切らない)。1 つも当たらないときも断る —— それらしい末尾を書いた側には、静かな 0 件を確かめようがない
  • 展開は tag_counts(タグ名 → 文書数の集計表)を引くので、転置表は舐めない

溜まった別のソースを材料にする({material})

{feed} が外から引く道具なら、materialChiezo に既に溜まっているものを材料にする 指定。集めたものを読んで、別の見方を別の収集に育てるための口で、例えば 「技術ニュースの収集に溜まった記事を読んで、話題の網(トピック)を育てる」が書ける。

curl -s "$BASE/v1/collect" -H 'Content-Type: application/json' -d '{
  "name": "tech_topics",
  "description": "技術ニュースから育てる話題の網",
  "prompt": "{material}\n\n{current}\n\n上の記事を読んで、話題の網を育ててください。",
  "interval_minutes": 360,
  "kind": "stock",
  "material": {"source": "tech_news", "tag": "ニュース,記事", "limit": 60}
}'
  • 渡るのは、前回この巡回が走ってからそのソースに入ったぶんだけ({recent} と同じ読み方)。 全部を渡すと入り切らず、毎回同じものを読み直すことになる
  • tag で絞れる。 1 つの収集には種類の違うものが混ざる(記事とまとめ、など)ので、 絞れないと材料にまとめが混ざる
  • 中身は写さない。 読むだけで、この収集に溜まるのは AI が返したものだけ —— extract(別ソースから機械的に取り込む)とはそこが違う
  • 材料の側がまだ焼かれていなくても失敗にしない(その収集がまだ 1 度も走っていない だけのことがある)。空だったことは差し込みの文に書く

同じ収集に混ぜる形と比べたときの利得はここ。 混ぜると、育てたものが流れの期限で 消えるうえ、{current} が集めたもので埋まって、育てているほうが差し込みから押し出される。

外向きの道具({feed})

extract が手元の長期記憶から機械的に引く道具なら、feed外から引く道具。 どちらも狙いは同じで、AI が間違えるところと、間違えないところを分ける —— 見出しと URL と日付は機械が正確に取れる。何が重要でどう束ねるかは取れない。

curl -s "$BASE/v1/collect" -H 'Content-Type: application/json' -d '{
  "name": "tech_news",
  "description": "技術ニュース",
  "prompt": "{feed}\n\n{cursor} 以降に出た技術ニュースを10件。日本語で。",
  "interval_minutes": 480,
  "feed": {
    "urls": ["https://example.com/feed", "https://example.org/atom"],
    "since": "last_run"
  }
}'

取ってきたものをそのまま溜めるわけではない。 {feed}参考として差し込むだけで、 何を溜めるかは AI が決める —— 自分でも web を検索し、渡されたぶんも含めて採否を判断する。 差し込む文にもそう書いてある(「これが全部ではありません。自分でも調べてください」)。 情報源として扱わせると、フィードが拾わなかったものは永遠に入らず、フィードが拾った 宣伝記事はそのまま溜まる生のまま入る経路は無い —— 溜まるのは常に AI が書いたもの。

外へ出る以上は app/websearch.py と同じ契約を守る:

  • 本文は取りに行かない。 取るのはフィードが配っている見出し・要約・URL・日付だけ
  • 自分でレート制限をかける。 無人で回る層なので、こちらが加減しないと 相手のログに等間隔の足跡だけが延々と残る
  • User-Agent はプロジェクト名だけ(連絡先・個人名・メールアドレスは入れない)
  • DOCTYPE のある XML は読まない。 実体参照の展開でメモリを食い尽くす細工に 付き合わないため(フィードに DTD が付くことは実際まず無い)
  • 1 本落ちても収集は止めない。 参考の素材なので、1 本の不調で止めるほうが損。 取れなかった数は素材の中で伝える —— 少ないのが世の中の都合か道具の不調かで 意味が違う

since: "last_run" を書くと、前回の実行より後のものだけを渡す。 日付を持たないものは落とさない(そういうフィードは普通にあり、絞ると静かに 0 件になる)。

外の URL を叩けるのは、有効にした収集だけ。 定義は誰でも置けるが、走り出すのは Chiezo の管理画面で有効にしたときだけなので、そこが唯一の関門になる。

端から端まで舐める({partition})

「全国の店を順に」「作家を1人ずつ」のように範囲を舐めていく収集には、{cursor} (次はどこ)だけでは足りない。全部を舐めきったかも、取りこぼしがどこかも分からない からで、そこを埋めるのが区画(app/partition.py)。

割るのは対象としている空間であって、集まったものではない。 ここが要 —— 集まった点だけから区画を作ると、まだ 1 件も集めていない範囲には区画が生まれず、 永遠に空のままになる。母集団を別のソースから取れるようにしてあるのはこのため (source)。全国の飲食店を集めるなら、こちらが 1 件も持っていなくても osm_japan は北海道にも店があることを知っている。

面積では等分しない。 都市部は狭い範囲に大量の対象があり、地方は広い範囲に少ししか ない。同じ面積の区画を配ると、片方は 1 回で見切れず、もう片方は空振りになる。 点の数が target を切るまで再帰的に二分するので、密なところは細かく・疎なところは 粗く、かつ全域に区画がある状態になる。

by 何で割るか 要る鍵
geo 矩形(緯度経度)。密度で再帰的に二分する bbox(省くと母集団の外接矩形)
tag タグ 1 つが 1 区画。束ねない(対象が付けた区切りなので混ぜない) prefix
title 見出しの順に target 件ずつ。鍵は「どこからどこまで」
band 分類のタグ × 数のタグ。同じ括りの中を、数の軸に沿って人数が揃うところで区切る prefix / value

by=geo では origin([緯度, 経度])を書くと、そこから近い順に広がります。 書かなければ鍵の文字列順で、矩形の鍵は南西の角から始まるので日本の地図なら 八重山から北上します —— 一周に何十日もかかる台帳では、主要なところに着くのが 何か月も先になります(例: 10,457 区画・1 回 5 区画・1 時間おきで一周 87 日)。 2 周目からは何もしなくて構いません(1 周目を近い順に回れば印も近い順に付くので、 「古い順」がそれをなぞります)。origin を足しても台帳は捨てません —— 配る順が変わるだけで、どの文書がどの区画に入るかは動かないためです (targetbbox を変えたときは、鍵の意味が変わるので捨てます)。 設定は管理画面の区画の一覧にも出ます。

curl -s "$BASE/v1/collect" -H 'Content-Type: application/json' -d '{
  "name": "tl_lunch",
  "description": "全国のランチ",
  "prompt": "いま見る範囲: {partition}\n{current}\nこの範囲に足すべき店が無いかを調べ、あれば足す。\n既に入っているもので閉店・移転しているものは直す。",
  "interval_minutes": 180,
  "mode": "refine",
  "partition": {
    "by": "geo", "target": 200, "source": "osm_japan",
    "feature": "amenity=restaurant", "bbox": [20.0, 122.0, 46.0, 154.0]
  },
  "requested_by": "travel-log"
}'

# どこまで回ったか(台帳の中身は 1 件ぶんの口にだけ入る)
curl -s "$BASE/v1/collect/tl_lunch" | jq '.partitions_visited, .partitions_total, .next_partition'

# 最初から回り直す(2 周目を粗いまま繰り返させず、精度を上げ直したいとき)
curl -s -X PATCH "$BASE/v1/collect/tl_lunch" -H 'Content-Type: application/json' -d '{"partitions": []}'

{partition} には「今回見る範囲」が差し込まれる。 矩形だけでは AI にどこの話か 分からないので、母集団から近くのものを数件添えた文になる(「緯度 34.90〜35.10 / 経度 135.60〜135.90 の範囲(このあたり: …)」)。範囲の選び方を AI に決めさせない —— 選ばせると、数え上げられないので一周が読めなくなる。

{current} はその区画のぶんだけになる。これが区画のいちばんの利得。 全体を差し込もうとすると入り切らず、切ったぶんは「今回の対象外」になる (MAX_MATERIAL_CHARS)。区画で切れば全部見せられるので、漏れているものを足させることも 重複をまとめることも初めて成り立つ。

矩形で割る収集では、AI に lat / lon も返させる(system 側で頼む)。 座標が無いと、集めたものがどの区画にも入らない —— 次にその区画を見るとき 「まだ何も無い」と見えて、同じものを集め直す。ついでにコアスキーマの生成列に乗るので、 filter?bbox= で普通のソースとして引けるようになる。

割り直しは自動だが、頻繁にはしない。 母集団を外のソースから取っているなら、 こちらが何件集めようと点の数は変わらないので区画は動かない(動かすと巡回の記録が 毎回リセットされ、一周が永遠に終わらない)。母集団を書かず自分自身を割っている ときだけ、育って target の 2 倍を超えた区画が出たら割り直す。 どの区画にも入らない文書が出たときも割り直します(年代の帯で割っている場合)—— 割った時点でその分類の全員が生年を持っていると「不明」の置き場が作られないので、 あとから生年の無い 1 件が入ると行き場がありません。エラーにはならず、{current} にも 件数にも出ないまま残るので、気づけるのは割り直しの判定だけです。 割り直しても、鍵の変わらない区画は巡回の記録を引き継ぎます。

割り直しだけを単独で走らせられます(管理画面の「区画を割り直す」。 POST /admin/collect/{name}/repartition)。割り直しは母集団を丸ごと 1 周舐めて 全点をメモリに載せるので、素材を流すのと同じ回に乗ると山が二つ重なります —— 本番で、台帳が空の状態から 686,602 件を割り直す回が、素材を 280,270 件まで流した ところで切れました。ふだんは台帳を使い回すので走りませんが、台帳が消えると 次の 1 回に必ず乗る(いちばん重い回が、いちばん条件の悪いときに来る)ので、 先に台帳だけ整えておける口を分けてあります。

  • AI は動かず、焼きもしません(枠も使わないし、長期記憶も世代も動きません)
  • 進み具合も次回の予定も動きません —— 動くのは台帳だけ
  • 一周は巻き戻りません(巡回の記録を引き継ぐ)
  • 割り直しが要らなければ数え直すだけ(巡回が組むときと同じ判断)
  • その収集を焼いている最中は 409 で断ります(あちらも終わりに台帳を書き戻すため)。 別のソースを焼いている最中なら押せます

一覧の口(GET /v1/collect)には台帳を載せない(進み具合だけ)。 上限まで割ると 1 件で数百 KB になり、収集の数だけ倍になるため。

特定の範囲をいますぐ集めさせたいときは、カーソルを書き換えてから起こす。 専用の口は持たない —— cursor は元から書き換えられるので、これで足りる。

curl -s -X PATCH "$BASE/v1/collect/tl_lunch" -H 'Content-Type: application/json' \
  -d '{"cursor": "東京都新宿区"}'
curl -s -X POST "$BASE/v1/collect/tl_lunch/run"

2 種類の直し方を別々の時計で回す(sweeps)

育てる収集には、直し方が 2 つ要る。 ざっと全体を拾って訂正するもの(1 週間で 一周するくらいの頻度と量)と、少数をじっくり調べるもの。進み方も、頼む相手も、 1 回に食べる量も違うので、interval_minutes 1 本では表せない。

curl -s -X PATCH "$BASE/v1/collect/tl_lunch" -H 'Content-Type: application/json' -d '{
  "sweeps": [
    {"name": "ざっと",   "interval_minutes": 360,  "cover_days": 7},
    {"name": "じっくり", "interval_minutes": 1440, "partitions_per_run": 1, "effort": "high"},
    {"name": "割り込み", "on_demand": true, "backend": "antigravity"}
  ]
}'
  • cover_days に「7」と書けば、1 回に見る区画数は Chiezo が計算する (区画数 ÷ 7 日あたりの実行回数)。手で書かせると、区画が増えた日に一周が静かに 伸びる —— そして誰も気づかない
  • 書かなかった項目は収集のものを使う(プロンプト・相手・モデル・深さ・間隔)。 違うところだけ書けば済むほうが、2 本目を足すときに間違えにくい
  • 収集(または巡回)を止めると、待ち行列に残っているぶんも流れません。 積んだあとに止めたぶんは行列から外れます —— 押した「止める」が効かずに 1 回ぶんの取り込みが走ってしまうため。管理画面の「今すぐ実行」だけは別で、 止めてある収集でも走ります(有効にする前に試せる道です)。
  • 区画の巡回記録は巡回ごと(partitions[].visits)。ざっとが一周した区画を じっくりはまだ見ていない、が普通に起きる
  • enabled: false で止められる(消すと進み具合まで消える)。収集そのものの enabled とは別の段 —— 止めている収集は 1 本も走らない
  • 名前が鍵。 同じ名前が 2 本あると片方の進み具合がもう片方に化けるので、 重複は落とす。巡回を消すと、その巡回の記録も台帳から落ちる
  • sweeps を書かない収集は、定義そのものが 1 本の巡回(既定)として動く。 今までの収集は何も変えなくてよい
  • prompt を書けば、その巡回だけ別の依頼文で走る(空なら収集のもの)。頼むことが 巡回ごとに違う(埋める / 見直して消す / 漏れを足す)のに、1 つの文で全部を頼むと、 どの回も同じ薄さの仕事になる
  • use_extract: true の巡回は、AI を呼ばずに extract の指定をもう一度走らせる。 名簿を最新に保つための回 —— 機械で埋めるのは「進み具合が空の 1 回目だけ」なので、 そのあと外のカテゴリが増えても入らない。only_new と組にして使う (組にしないと、AI が肉付けしたぶんを名簿の薄い内容で上書きする)
  • 消したものは消さずに、消えた印(_chiezo_removed)を付けて残す。 消していた頃は、消す回と足す回が別々に走るせいで外したものが次の回で戻ってきた —— 足すほうは「いま名簿にいるか」しか見られず、なぜ居ないのか(一度も入っていないのか、 調べたうえで外したのか)までは分からない。見出しの控えを定義に持って止めていたが、 あれは 1 件に収める都合で上限が要り、溢れると古いものから静かに戻っていた。 残せば、その問題ごと消える —— 見出しは既にいるので足す回は素通りし、上限も要らない。 読み口は既定でこの印の付いた文書を返さないので人には出ず、集める層は焼いた DB を 直に読むので AI からは見えたまま(何を外したかを自分で確かめられる)。 本文はそのまま残る —— 印を外せば元の中身のまま戻る。消した理由は本文を 置き換えずに脇書き(extra.removed_reason / removed_at)へ置く —— 置き換えていた頃は、消し間違いを戻しても「なぜ消したか」の 1 行しか残っておらず、 育てたぶんを集め直すことになっていた
  • 消しすぎの歯止めは「印を付けた件数」で数える(keep_ratio)。件数そのものは 減らないので、結果の件数で見ていると 9 割に印が付いた回が素通りする
  • 区画の中身は AI が自分で引ける(GET /v1/collect/{name}/partition?key=…、 MCP の collect_partition)。差し込み({current})でしか見えなかった頃は、 1 回の依頼に載る量が上限だった —— 入り切らないぶんは黙って落ちるので、 「この範囲の全員」を前提にした漏れ探しが成り立たなかった。 この口は消えたものも返す(何を外したのかが分からないと、同じものをもう一度 挙げることになる)。読み口の既定と違うのは、ここが集める層のための口だから。 画面からも同じところを見られる —— 管理画面の台帳で区画の名前を押すと、 その区画に入っているものが並ぶ(/admin/collect/<収集名>/partition?key=…)。 どの巡回がいつ見終えたかも出ます(日本時間)。一周に何十日もかかる台帳では 「見た」より「いつ見た」のほうが知りたい —— 古い順に配られるので、次にどこが 回ってくるかもそこから読めます
  • 細かく切れすぎた区画は隣とまとめる。 割り直しの引き金は「育った」と 「空になった」しかないので、中身が別の区画へ移って痩せた帯は痩せたまま回り続ける —— 数人のために 1 回ぶんの枠を使うことになる。まとめるのは周回の記録が同じ 隣どうしで、合わせても target の 8 割に満たないときだけ。 まとめられるのは「幅」で表した区画だけ(年代の帯・見出しの範囲・矩形)—— 隣り合うぶんを覆う 1 つの幅があるので、鍵の読み方も、どの文書がどこへ入るかの 規則も変わらない
  • 範囲で区切っていない小さい分類は、値を並べた 1 区画に寄せ集める (アルメニア|エストニア|-)。隣とまとめる道は分類をまたげない(2 つの名前は 1 つの名前で表せない)ので、1 人しかいない国はその 1 人で 1 区画のまま残っていました —— その 1 人のために巡回の 1 回ぶんの枠を使うことになります。寄せるのは 数の軸で 1 つも割れていない(その分類ぜんぶで 1 帯)・5 件以下周回の記録が同じもの。引き受けている分類は鍵に全部並べて持ちます —— どこまでがその区画かが分からなければ、漏れを問えないためです。 数が target の 8 割に届く前でも、並べる値が 20 を超えたら止めます
  • only_new: true の巡回は足すだけ。 既にある見出しが返ってきても触らず、 墓標も読まない。「この範囲/括りから漏れているものを足して」と頼む回に要る印で、 AI の判断に頼らずに Chiezo 側で保証する —— 見せられるのはその区画のぶんだけなので、 AI には「もう居るかどうか」が分からない(別の括りに入っていることも、タグが 間違っていることもある)。触らせると、既にいるものが薄い内容で上書きされ、 持っていたタグごと落ちる
  • on_demand: true の巡回は時計を持たない。 定時には走らず、割り込みで 頼まれたときだけ動く —— 狙いは頼む相手を定時のものと別に決めておくこと。 割り込みは人が待っている場面なので速い相手に頼みたい / 1 件をじっくり 調べさせたい、のどちらもあり、定時の巡回の設定を流用すると 「どちらの都合で選んだ相手か」が言えなくなる

1 回の実行で区画ごとに AI を 1 回ずつ呼ぶ。 まとめて 1 回で聞くこともできるが、 それだと {current} がその区画のぶんだけになる意味が消える(区画を切った理由そのもの)。 1 回に見る区画は MAX_PARTITIONS_PER_RUN で頭打ちにしてあり、1 回の取り込みが 何十分にもならないようにしている。

どの巡回を起こしたかは定義側に控える(pending_sweep)。取り込みは収集の名前しか 運べない(GET /v1/collect/fetch?source=…)ので、素材を作る側はそこを読む。

この部分を集中的に直させる(割り込み)

巡回は端から順に回るので、「ここが間違っている」と分かっているものを今すぐ直す 手段が別に要る。POST /v1/collect/{name}/focus がそれで、定時の巡回には影響しない

curl -s -X POST "$BASE/v1/collect/tl_lunch/focus" -H 'Content-Type: application/json' -d '{
  "note": "この店は移転しているはず。住所を確かめて直して",
  "titles": ["○○食堂"],
  "requested_by": "travel-log"
}'

頼む相手は割り込み用の巡回から取る(on_demand: true の 1 本)。 持っていない収集では、次に走るはずの定時の巡回のものを使う。 "sweep": "じっくり" と名指しすれば、「じっくりの相手で、いま 1 回だけ」も頼める。

  • 動かないもの: 進み具合(cursor)・どの巡回の予定・区画の巡回記録。 動かすと、割り込むたびに一周が伸びたり、見ていない区画に印が付いたりする
  • note は必須。 何をどう直すかが書かれていない割り込みは、1 回ぶんの AI の 呼び出しにしかならない(巡回でやれば済む)。取り込みを起こす前に断る
  • titles に名指ししたものは必ず AI に見せる。 区画を渡すだけでは、直してほしい 1 件が差し込みに載る保証がない。手元に無い見出しは「無い」と伝える —— 見出しの書き方が違うのか、そもそも入っていないのか、AI に判断させたほうがよい
  • partition を渡せばその区画を見る。 省くと、名指ししたものだけを見る (区画を渡されていないのに全件を差し込むと、直す相手が切り落とされる)
  • 必ず「直す」側で走る。 足すだけの収集(mode: append)でも、名指しで渡された 1 件を直せなければ割り込みの意味が無い。消すのは今までどおり墓標を付けたときだけ
  • 止めている収集は 403(run と同じ。呼んだだけでは AI が動かない、が この口を外へ開けておける理由)
  • 成否にかかわらず依頼は片付く。 残すと、次に走る定時の回が割り込みとして走る
  • prompt を渡すと、その回だけその依頼文で走る。 差し込み口({cursor} {current} {partition} {feed})はいつもどおり効く。保存はしない —— 定義のプロンプトは育てながら使うもので、1 回きりの頼みごとで書き換わると、 次の定時の回が知らない文で走ることになる

変更履歴には割り込みとして残る(GET /v1/collect/changessweep割り込み)。 どこを見た回かも scope に入るので、「直近どこに修正が入ったか」が件数以上に読める。

集める(append)と整理する(refine)

集め方は 2 つある(mode。既定は append)。同じ機械を使うが、 AI に何を見せて、何を返させるかが変わる。どちらも前世代を消さない

append(集める) refine(整理する)
AI に見せるもの 見せない {current} に今ある内容
返させるもの 集めてきたもの 直すものと足すものだけ
返さなかったもの 残る 残る
消し方 消えない 墓標(_chiezo_tombstone タグ)を付けて返したときだけ
使うところ 外から新しいものを取ってきて積む 既にある内容を育てる(分類をやり直す・重複をまとめる・言い回しを揃える)
歯止め 要らない keep_ratio(既定 0.5)

整理のプロンプトには {current} が要る(無いと 400 で断る)。ここへ今ある内容が 差し込まれるので、AI は何を直すかを見たうえで書ける。

変えないものまで返させない。 触れなかったものはそのまま残る —— 「返ったものが新しい全体」にしていた頃は、返し忘れが黙って消えた。無人で回る層で いちばん起きやすい壊れ方で、しかも 1 件ずつ削れていくのは歯止めをすり抜ける。 件数が受け取りの上限を超えると「全部返す」自体が成り立たない、という詰みもあった。

消すのは墓標で明示したときだけ。消したい見出しで tags_chiezo_tombstone を入れて返す(本文は空でよい)。重複をまとめるときは、まとめた先を 返し、元のものに墓標を付ける。

区画には隙間ができません。 見出しで割った区画も年代の帯も、境目は必ずどちらかの 区画のもので(区切りは「どこから始まるか」で読みます)、端の区画は割ったときの値より 外側も引き受けます(値の軸に上限・下限は指定できないので、端は開いたままです)。 年代の帯は鍵もそう書きます —— 日本|-1849(下が開いている)・日本|1850-(上が 開いている)・日本|-(その分類ぜんぶで 1 帯なので、値では絞らない)。 矩形は親を二分して作るので初めから隙間がありません。{partition} に差し込む文も 実際の受け持ちで書きます —— 端は「1699 以下」「1800 以上」、中は「1700〜1799」、 見出しなら「「さ」から「な」の手前まで」。区画が 1 つだけならその軸では絞りません (「すべて」)。鍵のまま伝えると、引き受けているのに誰も探しに行かない範囲が できるためです。 分類そのもの(prefix のタグ)は別で、そのタグを持つものがいなければ区画も ありません(「まだ 1 件も無い分類に漏れが無いか」は、この仕組みでは聞けません)。

消したものは消えずに印が付いて残る(_chiezo_removed)。読み口は既定で隠しますが (include_removed=true で出ます)、集める層は焼いた DB を直に読むので AI からは 見えたままです —— 何を外したかを自分で確かめられるようにするため。

その見せ方は 2 つに分かれます。区画の母集団には入りません(区画の大きさは 「この回に見てもらう量」なので、消したものを混ぜると実際より多く見え、中身が墓標 だけの区画にも巡回の 1 回が回ってきます)。そのぶん区画が消えることはありません —— 中身が消えたものだけになった範囲は隣に吸収され、吸収先が無ければそのまま残ります (消すと「この範囲に足すべきものが無いか」を問う回ごと無くなるため)。一方 {current} には、生きているものとは 別の一覧として後ろに付きます —— 「もう一度足さないでください」と添えて渡すためで、 渡さないと消したものを「抜けている」と読んで足し直されます。枠も別に持つので (生きているもの 8 万字 / 消したもの 2 万字)、消すほど直す相手が見えなくなることは ありません

墓標で減りすぎたら焼く前に止まる(keep_ratio)。前世代の何割を下回ったら断るかの値で、 既定は 0.5。返し忘れでは減らなくなったので、ここが止めるのは明示的な大量削除だけ —— そのぶん、止まったときの意味が鋭い。意図して大きく減らすときは値を下げるか 0 にして外す。 止まったときは焼いていないので、いまの内容はそのまま。

# 整理する収集を依頼する({current} が要る)
curl -s "$BASE/v1/collect" -H 'Content-Type: application/json' -d '{
  "name": "spot_kinds",
  "description": "スポット種別の語彙",
  "mode": "refine",
  "prompt": "いまの分類:\n{current}\n重複をまとめ、粒度を揃えて全部返して。",
  "interval_minutes": 1440,
  "keep_ratio": 0.8,
  "requested_by": "travel-log"
}'

# 焼かずに 1 回試して、前世代との差分だけ見る(止まっている収集は403)
curl -s -X POST "$BASE/v1/collect/spot_kinds/preview" | jq .
# 巡回を名指しして試す(相手も 1 回に見る量も巡回ごとに違う)
curl -s -X POST "$BASE/v1/collect/spot_kinds/preview?sweep=%E3%81%98%E3%81%A3%E3%81%8F%E3%82%8A" | jq .
# → {"previous":120,"total":118,"added":3,"kept":115,"removed":5,
#    "removed_titles":["…"],"blocked":null}

下見(preview)は焼かない。長期記憶に一切書かず、進み具合も次回の予定も動かさない。 整理はプロンプトを育てながら使うものなので、何が増えて・何が残って・何が消えるかを 見てから焼ける必要がある(件数と勘で調整することにならないように)。 焼こうとしたら止まる状態かどうかも blocked で分かる。 ?sweep= で巡回を名指しできる(run も同じ)。相手も 1 回に見る量も巡回ごとに 違うので、名指しできないと「じっくりで聞いたらどうなるか」を試せない。 時計を持たない巡回(割り込み用)は 400 で断る —— 自前の依頼文を持たないので、 そのまま走らせても収集のプロンプトで普通の回が 1 本増えるだけになる。 知らない名前も断る(別の巡回へ倒して走らせない)。

集めた文書には出所が残る(extra.web)。web を開けて集めたかどうかで、 手元に置くだけなら私的利用の範囲でも、公開リポジトリへ出すかは別の判断になる —— 後から辿れないと、その判断ができない(出典 url と合わせて手掛かりにする)。

外のアプリからも依頼できる。ただし勝手には走り出さない。 POST /v1/collect で 作られる収集は必ず止まった状態で、動き出すのは Chiezo の管理画面で「有効にする」を 押したときだけ。enabled は REST から触れず、POST /v1/collect/{name}/run も 止まっている収集は 403 で断る —— 呼んだだけでは AI が動かないことが、この口を 外へ開けておける理由。requested_by に名乗ると、有効にするか決める人の手がかりになる (印であって認証ではない)。

# 依頼する(name はソース名になる。英小文字・数字・_)。止まった状態で作られる
curl -s "$BASE/v1/collect" -H 'Content-Type: application/json' -d '{
  "name": "tech_news",
  "description": "技術ニュース",
  "prompt": "{cursor} 以降に出た技術ニュースを10件。日本語で。",
  "interval_minutes": 480,
  "requested_by": "travel-log"
}'

# 一覧(間隔・次にいつ走るか・前回の結果が入る)
curl -s "$BASE/v1/collect" | jq .

# 予定を待たずに1回、集めて焼く(有効にしてある収集だけ。止まっていれば403)。
# 返るのは「取り込みを起こした」まで —— 進み具合は管理画面で見る
curl -s -X POST "$BASE/v1/collect/tech_news/run" | jq .
# 巡回を名指しして走らせる(?sweep=)。時計を持たない巡回は 400 で断る

# 間隔や相手を変える(渡した項目だけ差し替わる)
curl -s -X PATCH "$BASE/v1/collect/tech_news" -H 'Content-Type: application/json' \
  -d '{"interval_minutes": 240, "backend": "antigravity"}'

# 直近どこに修正が入ったか(新しい順。name を省くと全部の収集)
curl -sG "$BASE/v1/collect/changes" --data-urlencode "name=tech_news" | jq .

# 溜めたものを引く(普通のソースとして)
curl -sG "$BASE/v1/tech_news/search" --data-urlencode "q=<検索語>"

変更履歴は 1 回 = 1 行で残る(GET /v1/collect/changes)。一覧に出る last_added などは 最新の 1 回で上書きされるので、6 時間ごとに回る収集なら朝には昨夜の 1 回しか残っていない —— 減り続けているのか、ある日だけ荒れたのかは並べないと読めない。1 行には件数のほかに 動いた見出しの頭のほう(足した・直した・消した)が入る。失敗も 1 行として残る —— 「走ったが何も入らなかった」と「そもそも走っていない」は別物で、成功だけ残すと同じ空白に見える。 誰に頼んだ回かも入る(backend / model / effort)。巡回ごとに相手を変えられるので、 回の名前だけでは何で走ったのか読めない —— 「じっくりだけ荒れている」が相手のせいなのか 考える量のせいなのかは、並べて初めて見分けられる。かかった時間も入る(ms。画面では 「かかった」の欄)—— 遅くなったことは件数からは読めず、同じ件数でも 5 分が 20 分に なっていれば一周の見込みが 4 倍ずれます。測るのは集めるところまでで、焼くぶんは 入りません(控えを書いてから素材を流すため)。失敗した回も測ります(すぐ落ちたのか、 待ち切って落ちたのかで打つ手が違う)。測る前の古い行は null です。 記録先は CHIEZO_STATE_DIRcollect_runs.db で、未設定なら空で返す(404 にはしない)。

管理画面では動いた見出しを押せる(/admin/collect/{name}/doc?title=…)。 名前だけ並べても「どう書き換わったか」は読めないので、書き換わった中身を 1 枚で出す。 比べるのは、その 1 件が控えている「直す前」(extra.before)—— 直した回が 控えを 1 回分だけ持っているので、後から何度焼き直しても読める。本文・タグ・脇書きを 別々に出す。控えを持たない 1 件だけ、いまの世代と 1 つ前の世代の比較に落ちる (そちらは 1 つ前まで —— ブルーグリーンが残す世代がそこまで)。 どちらとの比較かは画面に書いてある。

巡回が動かした 1 件には、動かした AI とそのモデルが署名として残る (extra.changed_ai。例 codex / gpt-6-astra-max)。どの回が動かしたかは extra.changed_by が持っているが、巡回はワーカーに頼むので、同じ 1 回の中でも 区画ごとに相手が振り替わる(枠が詰まれば次の段へ落ちる)—— 実行履歴の 1 行に並ぶのは その回に走った相手ぜんぶなので、目の前の 1 件を誰が書いたのかはそこからは決まらない。 署名が付くのは AI が返した 1 件だけで、機械で引いた回(extract)と フィードで引いた回(feed)には付かない —— どちらも AI は読んでいない。

溜まったものは新しい順に引ける(GET /v1/<name>/recent。MCP の recent)。 search は語が要り、filter はタグや属性なので、「この 1 日で何が入ったか」を引く 手段がこれまで無かった —— 集めたものを読む側(重要なものを選ばせる、通知の候補にする)が まず訊くのはこれ。since に前回の updated_at を渡すと続きだけを取り直せる。

curl -sG "$BASE/v1/tech_news/recent?limit=20"
curl -sG "$BASE/v1/tech_news/recent" --data-urlencode "since=2026-09-07T00:00:00+00:00"

設定と一緒に見本も引ける(GET /v1/collect/{name}?samples=20)。こちらも 消えたもの・まだ AI が目を通していないものは既定で外しますinclude_removed=true で含められます)—— ここだけ外していなかったころは、読む側が自分で落とす必要があり、 実際に外のアプリがその受け止めを持っていました。印が増えるたびに読み手を全部 直して回ることになり、しかも漏れるのは読者の画面だけなので気づくのが遅くなります。

since はその時刻を含む。時刻は秒までしか持たないので、「より後」にすると 同じ秒に入ったものを黙って落とす —— 取りこぼすより、同じものが 1 件返るほうが後から直せる (読む側は doc_id で重複を落とす)。対応するのは溜まっていくソースだけで、 ダンプ由来のソースは 409 で断る(updated_at が新しさを表さないうえ、索引が無いので 全走査になる)。

{cursor} が要。実行ごとに進む印で、AI が答えの next_cursor で次を返す。 これで「前回以降」「次の地域」「次に調べる人」が同じ 1 つの仕組みに乗る。

prompt の書き方 カーソルに入るもの
ニュース {cursor} 以降のニュースを10件 前回集めた時点
店の情報 {cursor} の周辺の飲食店を20件。終わったら次に回る市区町村を next_cursor に 次に回る地域
人物の関係 {cursor} の交友関係を調べ、出てきた人名を next_cursor に 次に調べる人

AI に返させる形は {"items":[{"title","body","tags","url"}],"next_cursor":"…"}title が重複の鍵なので、同じものを指す見出しは同じ文字列にするよう頼む。

1 回に集める件数に上限は無い。 天井は組み上がる素材の大きさのほうに置いてあり (CHIEZO_COLLECT_MAX_MATERIAL_BYTES、既定 192 MB)、超えたら黙って切らずに 409 で断る。 焼いていないので、断られても今の内容はそのまま残る。件数で切らないのは、それが 守るべきものを何も守らないため —— プロンプトへ差し込む前世代は文字数で切っており、 応答時間は相手ごとのタイムアウトが縛り、1 件の本文にも別の上限がある。

地図辞典から国ぶんの名簿を作るときは、この天井に当たる。 1 件のバイト数は運ぶ 項目で倍以上違い、見出しと本文だけなら 256 バイト、住所・電話・サイト・座標まで運ぶと 474 バイト(どちらも実測)。60 万件の名簿は 280 MB を超えるので、既定のままでは断られる —— 素材は 1 本の文字列で組むため、上げたぶんはそのままメモリに載ることを見込んで決める。

時計は Chiezo が持つ(CHIEZO_COLLECT_TICK_SECONDS、既定 60 秒で見に来る)。 予定が来たら chiezo-trigger に取り込みを 1 本起こさせるだけで、1 周に 1 件ずつ (trigger は同時に 1 ジョブしか受けない)。混んでいて断られた回は予定を進めないので、 空いたときに走る。失敗しても次回の予定は入るので、一時的に相手が落ちていても回り続ける。

誰に頼むかは収集ごとに持てる(backend / model / effort)。未指定なら Chiezo の 既定(有効にしてある相手の先頭)。依頼のときに名指しでき、後から画面でも変えられる —— 無人で回る層なので、どの相手に頼んでいるかが後から読めることが要る。

巡回の backend にはワーカーも書けるworker:<名前>GET /v1/ai/backendskind: "worker" として返す値をそのまま入れる)。その巡回の modeleffort は 落ちます —— 渡す先はそのときの枠で決まるので、どの相手に対する指定なのかが 決まらないためです(段ごとの指定はワーカーの側が持ちます)。worker に直に書く 形も通ります。

管理画面(/admin/collect)からも足せる。一覧には相手・間隔・次にいつ・長期記憶の件数・ 前回の結果が並び、行ごとに「有効にする / 止める」「削除」がある。 「今すぐ実行」と「ドライラン」は巡回ごと(巡回の欄の中)—— 相手も 1 回に見る量も 巡回ごとに違うので、収集に 1 つだけ置くと「どの設定で走ったのか」が押した本人にも 読めない。時計を持たない巡回(割り込み用)には出ない(口のほうでも断る)。画面の「削除」は設定と焼いたものをまとめて消す (元に戻せない)。設定だけ消して中身が残ると、一覧から消えたのに検索には出続けるものが できるため。REST の DELETE /v1/collect/{name} は設定だけで、溜まったものは残る。整理(作り直し)の行は名前の横にそう出て、 前回の欄には消えた件数と見出しが並ぶ。 画面から足したものは止めた状態で作る(プロンプトを見直してから動かす)。

見本の収集は置かない。 プロンプトの書き方 —— とくに {cursor}next_cursor の 使い方 —— は、追加のフォームの下書き(placeholder)で見せる。勝手に置いていた頃は、 消しても戻ってきた(「いま定義が 1 件も無ければ置く」で判じていたので、作り直そうと して全部消した拍子に復活した)。

名前はソース名になるので 1 つしか持てない。ハイフンは使えない —— 世代ファイル名 <source>-<date>.db の区切りと衝突する。

人間向けの画面

管理画面(/admin)

ブラウザで http://<サーバーIP>:7010/(/admin へ自動リダイレクト)を開きます。

スマホのホーム画面に追加できます(/manifest.webmanifest)。追加すると ブラウザの帯を消して開くので、戻る・読み直す・進むを画面の下に自前で出します —— 帯が消えたぶんを補うためで、ブラウザで開いているときは出ません。 下に引っ張ると読み直します(いちばん上にいるときだけ。入力の中では掴みません)。 Service Worker は持ちません —— 抱え込むと、更新しても古い版が出続けるためです (オフラインでは動きません)。

面は 5 つに分かれています。 見に来る目的が違うものを 1 枚に積み上げると、いま見たい節に 着くまで無関係な表を何度もスクロールすることになるためです。/admin は玄関で、設定は持たず、 状態と数、それに「いま頼めるか」を読むための表だけを出します。

何があるか
/admin/memory 記憶。短期記憶・長期記憶(再構築と削除)・未初期化の初期化
/admin/collect 収集。無人で回る層の巡回・区画・変更履歴と、回す相手の並び(ワーカー)
/admin/ai AI と鍵。話せる相手、使用量、AI への依頼
/admin/media 見比べ。作らせたものを並べて選ぶ。手元のものも持ち込める
/admin/server その他。Claude Code 連携と、いま動いているビルド

どの面にも同じ帯を出すので、玄関へ戻らずに行き来できます。どの画面にもフッタがあり、 いま動いているビルド(ビルド日時と元のコミット)が出ます —— 様子がおかしいときに、 古いイメージのままかどうかをその場で確かめられます。

玄関に出るのは、ディスクの空き(この画面から始まる操作がいちばんディスクを食うため)、 走っている取り込みAI の使用量走っている AI への依頼、そして各面の要約です。 走っている依頼が無ければ何も出しません(空の表を置くと、動いていないことのほうが目立つため)。

使用量は「AI と鍵」の面と同じ表ですが、使う相手だけに絞ってあります(全部の相手と 説明が要るときは /admin/ai#ai-usage へ)。窓は相手が返したぶんを全部並べます —— いちばん詰まっている窓 1 つに畳むと、5 時間の窓しか見えない相手が出るためです (短い窓が詰まっていても週の窓が空いていれば、重い仕事は頼めます)。取り直すボタンは まとめても 1 件ずつでも押した画面へ戻ります

記憶の面は知識の 2 層をそのまま節に分けてあります。短期記憶(覚えたこと) が先に出ます。notes の節で、 覚えている件数とタグの分布、検索画面への入口が並びます。取り込みで焼くソースではないので 再構築はできません(この節に再構築ボタンはなく、POST /admin/rebuild/notes も 409 で断ります)。 中身そのものは出しません —— 1 件ずつ読むのはブラウズ画面(/search/chiezo_memory/)の仕事です。 件数は画面を開いたときに数え直すので、MCP の remember や取り込みから書き込まれたぶんも そのまま反映されます。

長期記憶(ためた知識) は読み取り専用のソースで、ダンプから焼いたものと、集めたものが 並びます。登録済みソース(文書数・dump_date・構築日時・ スキーマバージョンなど)の一覧に加えて、最新のスキーマバージョン(いま取り込みを実行すると 焼かれる版。chiezo-trigger から取得)も表示され、それより古い DB の行には注意書きが付きます。 登録済みソースには「再構築」ボタンがあり、ダンプの取得から取り込みをやり直せます (古いスキーマの DB を最新にする、ダンプの新しい版を取り込み直す、が主な用途。 ブルーグリーンなので構築中も現行 DB での配信は続きます)。

その下に「長期記憶に何を入れるか」の操作があります。

  • 未初期化データの初期化chiezo-trigger 側の既知ソース一覧に載っているが /data に まだ .db が無いもの向けの「初期化」ボタン。長期記憶の節の中に畳んである (開くのは新しいソースを入れるときだけなので、日々見る表の間に挟まない)

初期化・再構築のボタンを押すと chiezo-triggerに ジョブが積まれ、進行状況(ログ tail 込み)が管理画面に表示されます(実行中は自動でリロードされます)。 ジョブが完了すると、chiezo-app が data/ の変化(シンボリックリンクの差し替え)を数秒以内に 検知して自動で新しい DB に切り替わります(再起動は不要。検知間隔は CHIEZO_RESCAN_INTERVAL 秒、既定 5。0 以下で無効化でき、その場合は従来どおり再起動で反映)。

chiezo-trigger を立てていなければ、初期化・再構築・削除のボタンは押せません (設定していないときも、到達できないときも同じ扱いです)。長期記憶へ書き込むときだけ 要るサービスなので、読むだけの構成では立てなくて構いません。そのときは状態欄に 「読むだけならこのままで動きます」と出ます(どのコンテナが何に要るか)。

「AI の相手」の下には使用量の節(/admin/ai#ai-usage)があり、相手ごとに 枠の残り(聞ける相手だけ)とChiezo が使ったぶん(全部の相手)が並びます。 枠は開いたときには聞きに行かず、控えてある値と取得時刻を出します(取り直しは行のボタン)。 同じ内容は GET /v1/ai/usage でも取れます —— 詳しくは AI と話す

その下はAI への依頼(/admin/ai#ai-history)です。会話・絵・音・動画・声のどれでも、 成功も失敗も新しい順に並びます(日時・依頼の種類・相手・結果・中身)。成功には やり取りの目方(依頼文と応答の大きさ・かかった時間)と、相手が言った場合のトークン、 失敗には理由と依頼文の大きさが付きます。10 件ずつのページで、?ai_failed=1 を付けると 失敗だけに絞れます(画面のリンクからも切り替えられます)。

目方の控え自体はプロンプトと応答を持ちません —— 呼んだ側の材料がそのまま入るためで、 大きさだけ残してあるのは失敗が大きさに寄っているのかを後から見分けるためです。 中身は別の控えCHIEZO_AI_TRANSCRIPT_DAYS。既定 14 日、0 で止まる)にあり、 同じ行の「中身」から開けます —— 渡した依頼文・相手の生の出力(CLI の stdout/stderr)・ 応答が、先頭だけ畳んで出ます(続きは「全文を開く」)。残る長さが別なので、 中身だけ先に消えた行は目方だけになります。以前は別の表に分けていましたが、 同じ 1 回が 2 か所に並ぶだけで、突き合わせる手がかりが時刻と相手しかありませんでした。 成功に目方を出すのは、トークン数を言わない相手(CLI ブリッジ・絵と音)の行が それ以外に読むところを持たないからです —— 中身を持たなくても「短く聞いて長く答えさせた」 「絵を 1 枚描かせて何分も待った」の区別は付きます。目方を残す前の古い控えは 「控えは回数だけ」と出ます(相手が黙っていたのではなく、こちらが測っていない)。 成功と失敗も、会話と生成も分けていません: 前者は「そもそも呼べていたのか」が読めなく なり、後者はどちらで落ちたか分かっていない人が探せなくなります。 成功の控えは 30 日、失敗の控えは直近 500 件まで。機械で読むなら GET /v1/ai/failures

走っている最中のものは表の先頭に出ます(GET /v1/ai/inflight)。 何を頼んでいるかも読めます —— 行の依頼文を開くと全文が出ます(長いものは途中で切れます)。 相手と大きさだけでは、同じ相手へ投げた 2 本のどちらを止めるべきか分からないためです。 文章の生成は 1 行にまとまります(中で会話の口を呼ぶので裏では 2 つ動いていますが、 依頼としては 1 本なので畳んであります)。 モデルの欄には実際に走ったものが出ます —— モデルを選ばずに頼むと、相手には 「そちらで決めて」という印を送るので、走り終えるまで実物は分かりません。 終わった行には応答が名乗ったモデル名が出て、走っている最中の行は相手の名前だけになります。 控えが書かれるのは 往復が終わってからなので、これが無いと、無人で回っているぶんは遅いのか止まっているのか 呼べてすらいないのかが読めません(CLI ブリッジ越しの相手は数分かかります)。 出るのは相手・モデル・種類・依頼文の大きさと経過時間までで、ここでも中身は持ちません。 終わると行は消え、結果の行に変わります。ページ送りからは外してあるので、 2 ページ目を見ているあいだに見えなくなることはありません。

画面の末尾にはいま動いているビルド(ビルド日時(JST)とビルド元のコミット)が出ます。 docker compose pull && docker compose up -d のあとにここが新しくなっていなければ、 古いイメージのままです(動いているビルドを確かめる)。

OSM の国別ソース(osm_<国>、195 件)と Wikipedia の言語版(<lang>wiki、348 件)は、 そのまま並べると他のソースが埋もれるため、一覧ではそれぞれ osm / wikipedia の 1 行に まとめてあります。osm 行の「国を選ぶ」から国選択の画面(/admin/osm)が開き、 大陸ごとに畳まれた一覧から国を選んで初期化できます。各国の pbf サイズと必要メモリの目安、 構築済みかどうかもそこに出ます(国名・region での絞り込み可)。同様に wikipedia 行の 「言語を選ぶ」から言語選択の画面(/admin/wikipedia)が開き、記事数の階層ごとに畳まれた 一覧から言語を選んで初期化できます(言語名・コードでの絞り込み可)。

見比べ(/admin/media)

何案か作らせたものを、依頼文つきで並べて選ぶところです。同じ group を付けて頼んだものが 1 組になり、組を開くと案が横に並びます。音は AI 自身が聴けないので、聴き比べる手段は ここにしかありません。絵は画像、音と動画はブラウザ内蔵のプレイヤー、文章は畳んだ本文として 出ます。

元にしたものも並んで出ます。 editreference で何かを渡した依頼には、 依頼文の隣に「これを直した」「これを参考にした」が付き、渡した絵・曲・動画をその場で 見聞きできます。依頼文だけでは出来上がりを読めないことがあるのが理由です —— 参考にしたものの欠点をそのまま引き継いだ生成物を前に、「指示が悪いのか、参考が悪いのか」 を切り分けられませんでした。控えているのは指し先(source_url)だけで、渡した中身は 持ちません。

姿勢は pose で渡します。 骨組みを描いた絵を渡すと、その姿勢で描かせられます。 edit(これを直す)や reference(絵柄を合わせる)とは役割が別です —— 言葉で姿勢は伝わりません(相手もコマ数も変えて 13 回とも同じ足が前に出ました)。 自前の GPU では ControlNet に、CLI ブリッジ越しの相手には参考の 1 枚目として渡ります。

参考は何枚でも渡せます。 reference に配列を渡すと、渡した順に相手側の ファイル名(source.pngsource-2.png、…)が決まります。役割の名前は持たないので、 「1 枚目は姿勢の見本、2 枚目は絵柄の見本」のように依頼文の中で書いてください —— 種類を増やすより、そのほうが後から効きます。直すほう(edit)は 1 枚だけです (何枚も同時に直すという指示が成立しません)。画面には渡した順に番号つきで並びます。

古い組へ遡れます。 一覧は新しい順で、?page=2 で前の頁へ進みます (GET /v1/media/groupsoffset)。置き場の掃除とは別の話で、 生成物と記録は 14 日で消えます(CHIEZO_MEDIA_KEEP_DAYS)。

Chiezo が動かしている CLI からの依頼は断ります。 CLI ブリッジのコンテナから AI を使う口(/v1/ask/v1/chat/v1/ai/complete・生成・収集の下書きと即時実行)へ 届いた依頼は 403 になります。相手を指名して頼んでいるのに、指名された側が別の相手へ 聞きに行くのは依頼として成立しないためです。知識を引く口はそのまま使えます。

誰が頼んだかも並んで出ます。 生成の口(/v1/media/image など)と MCP の各道具は requested_by を受け取り、相手・モデルの下に「依頼元」として出ます。必須ではありません —— 名乗りが無いだけで断るのは、鍵だけ借りに来る使い方を塞ぐことになるためです。 名乗らなかったぶんは 名乗り無し(<User-Agent>) として残ります。 自己申告だけに 頼ると、いちばん出どころを知りたい依頼(見に覚えのない 1 件)が空欄のままになるためです。 MCP は 1 往復ごとに独立しているので initialize の clientInfo は残らず、毎回必ず届く HTTP のヘッダから拾っています。

選んだ印は頼んだ側が GET /v1/media/picks で引きに来ます。 会話で「何番がいい?」と 聞かれても答えないでください —— 選ぶ人と頼んだ側が別のやり取りにいると拾えないので、 印はこの画面で付けます。1 組から選ばれるのは 1 つで、選び直すと前の印は外れます。

画面からも頼めます。 「作ってもらう」の欄に依頼文と相手を入れて押すと job が立ち、出来たものが同じ画面に組として並びます。会話できない相手(自前の GPU など)に頼む道はここだけです —— 会話の相手に作らせる道もありますが、そちらは話せる相手に限られます。

課金の走る操作を画面に置いてよいのは、管理画面が Chiezo を操作している人だけが開ける前提だからです (会話も収集の即時実行も、押せば枠を使います)。

手元で作ったものを持ち込む

curl -s "http://<サーバーIP>:7010/v1/media/upload" \
  -F "file=@案1.png" -F "prompt=案 1" -F "group=タイトルの一枚絵"

作らせたものと同じ組に並びます。 比べたいのは出どころではなく出来のほうなので、 別の道具で作ったものや手で仕上げたものも弾きません。持ち込んだものは画面に 「手元から持ち込み」と出ます。

項目 説明
file 中身(必須)。絵・音・動画・文章
prompt 何を作ったものか。見出しになります。空ならファイル名
group 束ねる名前。同じ名前が 1 組になります
kind image / audio / video / text。空なら中身と拡張子から見分けます
model 手元で使った道具やモデルの名前(控え)
requested_by 誰が持ち込んだかの名乗り。画面に「依頼元」として出ます

種類が見分けられないものは断ります(黙って「絵」として置くと、見比べの画面が壊れた絵を 並べることになるため)。上限は 128 MiB で、CHIEZO_MEDIA_UPLOAD_MAX_BYTES で変えられます。

ブラウズ画面(/search/{source}/)

各ソース名は /search/{source}/ にリンクしています。トップは全件一覧(doc_id 昇順。 notes のような小さなソースを頭から確かめる導線)で、検索するとその結果一覧に変わります。 一覧はどの経路(未検索・検索・タグ絞り込み)も同じ表(doc_id / title / tags / snippet)で、 1 ページ 100 件でページングします(?page=2)。/search/{source}/doc/{doc_id} で文書詳細 (本文・tags・links・extra)をブラウザで閲覧できます(/v1/... の JSON API を人間向け HTML で 薄くラップしたものです)。

画面はすべて前置き(/admin/search//ai/)の下に置いてあります。ルート直下を ソース名に使っていた頃は、askadmin という名前のソースを足せませんでした。

MCP から使う(/mcp)

REST と同じ機能を MCP(Model Context Protocol)のツールとしても提供しています。 chiezo-app 自身が MCP サーバーなので、クライアント側に何もインストールせずに繋がります。

claude mcp add --transport http chiezo http://<サーバーIP>:7010/mcp
# 次節の設定生成スクリプトを使うなら、この登録も既定で行われます(手動登録は不要)
scripts/gen_claude_config.sh -u http://<サーバーIP>:7010

公開しているツールは sources / search / doc / filter / tags / titles / links の 7 つ(notes が有効なら remember / recall / update / forget を加えて 11)で、引数は REST のクエリ パラメータと同じです。実体も REST のエンドポイント関数そのものなので、 挙動が二重管理になることはありません。REST と違うのは doc / filtermax_chars が 既定で 4000 字に切られる点だけです(MCP の応答はそのままモデルのコンテキストに載るため。 全文が要るときは max_chars を明示的に上げてください)。

トランスポートは Streamable HTTP(ステートレス)です。Streamable HTTP に未対応の クライアントからは mcp-remote 経由で繋いでください。

/mcp は既定で Host ヘッダの検証(DNS リバインディング対策)を無効にしています。 絞りたい場合の設定は 運用ドキュメントのセキュリティ節にあります。

Claude Code では従来の curl + CLAUDE.md 方式も引き続き使えます(次節)。大量取得では curl の方がトークン効率が良い場面があるため、どちらかに寄せる必要はありません。

Claude Code から使う(設定ファイル自動生成)

各アプリの環境で動く Claude に「Chiezo に載っている知識が必要なら Chiezo を使う」よう 促す CLAUDE.md ブロックを、稼働中の Chiezo の設定生成 API (GET /admin/claude-config.txt)に問い合わせて自動生成できます。登録済み (初期化済み)ソースだけを、実在タイトルを使った具体例つきで列挙します。ただし notes(手元で書くメモ)の中身は引き写しません —— 機密が混じりうるうえ、ブロックは --project でリポジトリ側にも生成できるため、プレースホルダーだけで例示します。 文書数は載せません(取り込みや書き込みのたびに変わるので、正確な値はブロック自身が 案内している /v1/sources で引きます)。 ブロック内の curl 例のベース URL は、Chiezo 側が「スクリプトがアクセスしてきた URL の プロトコル・ホスト名・ポート」から導出するため、--base-url に指定した到達可能な URL が そのまま生成物に載ります(リバースプロキシ越し・非標準ポートでも可)。 curl + POSIX シェルだけで動きます(既存の JSON 設定ファイルへマージする場面でのみ jq か python3 のどちらかを使います。詳細は後述)。

既定の書き込み先は ~/.claude/CLAUDE.md(全プロジェクトの Claude に効く推奨の使い方)。 あわせて既定で、書き込み先に対応する Claude Code 設定(--user なら ~/.claude/settings.json--project/--target なら .claude/settings.local.json)に Chiezo への curl を許可するルールを追記するため、生成後は Chiezo への curl が 毎回の許可プロンプトなしに実行できます(--no-permissions で無効化可)。 さらに既定で、Chiezo を MCP サーバーとしても登録します(後述)。

ブロックには「いまこのサーバーで頼めること」も入ります —— 話せる相手がいれば 手分けして調べる節(/v1/ai/complete を curl で叩く手順と、枠の残りの見方)、 絵・音・動画・声を作れるならその相手を名指しした案内です。使える相手だけを 名指しします —— 鍵の無い相手を勧めても、呼んで断られるまで分かりません。 生成のたびに現在の状態から書き起こすので、相手を増やしたら再生成してください。

# ~/.claude/CLAUDE.md を更新・localhost:7010 を参照
/path/to/chiezo/scripts/gen_claude_config.sh

# Chiezo が LAN 上の別ホストにある場合は場所を指定(環境変数 CHIEZO_URL でも可)
scripts/gen_claude_config.sh --base-url http://<サーバーIP>:7010

scripts/gen_claude_config.sh --project     # ~/.claude ではなく ./CLAUDE.md にする
scripts/gen_claude_config.sh --print       # 書き込まず内容だけ確認

既存 CLAUDE.md との共存:

  • 既定(--merge markers)は <!-- BEGIN chiezo --><!-- END chiezo --> で囲んだ ブロックだけを冪等に差し替えます。既存の記述は壊さず、再実行でソース一覧が最新化されます。
  • 既存内容との統合に人間的な判断が要る場合は --merge headless で Claude Code の ヘッドレスモード(claude -p)にマージを任せられます(claude CLI が必要)。

主なオプション: --base-url/-u(Chiezo の場所)、--user(既定・~/.claude/CLAUDE.md)、 --project(./CLAUDE.md)、--target/-o(書き込み先をパス指定)、--merge {markers,headless}--print--no-permissions(既定で行う上記の権限追記を無効化)、 --with-hook(下記の自動許可フックを設置。既定では設置しない)、 --no-mcp(既定で行う下記の MCP 登録を無効化)。 生成は Chiezo 本体が行うため、稼働中の Chiezo が必要です(旧 --offline --sources は廃止)。 生成される文面の要点は「まず search で当たりを付け、必要な文書だけ doc を取る(コンテキスト節約)」です。

MCP サーバーの登録(既定・--no-mcp で無効化)

Chiezo は MCP サーバーでもあるため、生成時に Claude Code へ登録も行います。 書き込み先は --user ならユーザースコープ(~/.claude.json)、--project/--target なら 対象ディレクトリの .mcp.json です。claude CLI があればどちらも claude mcp add --scope {user,project} に任せ、無ければ設定ファイルの mcpServers へ 直接マージします。どちらも再実行で重複しません。あわせて CLAUDE.md ブロックに「単発の参照は MCP ツール・大量取得は curl」の使い分けが書かれます (MCP の応答は必ずモデルのコンテキストを通るため、ページングや突合はファイルに落とせる curl の方が向くという理由です)。反映には Claude Code の再起動(新しいセッション)が必要です。

必要なもの(jq / python3 / claude CLI)

curl 以外は、既存の JSON 設定ファイルへマージするときだけ必要です (書き込み先がまだ無ければ API の応答をそのまま置くので何も要りません)。

場面 必要なもの
CLAUDE.md ブロックの生成 curl のみ
既存 settings.json への権限追記 jq か python3(どちらでも同じ結果。jq を優先)
MCP の登録 claude CLI。無ければ jq か python3
--with-hook python3(フック本体が Python スクリプトなので実行に必須)

権限と MCP は既定で入れる設定なので、入れられない環境では黙って飛ばさずエラーで停止します (「設定が入ったつもり」で使い始めるほうが困るため)。意図的に外すときは --no-permissions / --no-mcp を明示してください。--print は何も書き込まないので この検査を行いません。

大量取得でプロンプトが出てしまう場合(--with-hook)

上の権限ルールは Claude Code の仕様上コマンド文字列の前方一致でしか判定できません。 そのため単発の curl には効きますが、

for t in 東京都 浅草寺 多摩川; do curl -sG ".../doc" --data-urlencode "title=$t"; done
curl -sG ".../search" --data-urlencode "q=多摩川" | jq -r '.hits[].title'

のように curl が先頭に来ない形になると 1 本もマッチせず、大量取得のときだけ 毎回プロンプトが出ます。これを解消したい場合は --with-hook を付けると、 前方一致ではなくコマンドの構造で判定する PreToolUse フックを併せて設置します (<設定ディレクトリ>/hooks/chiezo-autoallow.py + settingshooks.PreToolUse)。

フックが自動許可するのは「Chiezo だけを読む、読み取り専用のコマンド」だけです (登場する URL が全て Chiezo / 実行されるコマンドが curljq 等の許可リスト内 / $(...)eval 等でコマンド位置を隠していない / ディスクへ書かない)。条件を外れたときは 何も出力せず通常の許可フローに戻る、判定に迷ったら黙る設計です。

ただしこれは Claude が打つ Bash を毎回検査して自動承認しうる仕掛けで、 権限ルールより影響範囲が広くなります。中身を読んで納得してから入れられるよう 既定では設置せず--with-hook を明示したときだけ設置します。 判定ロジックの全文は設置前に確認できます:

curl -s http://<サーバーIP>:7010/admin/claude-config.hook.py   # フック本体
# 管理画面 /admin/claude-config でもプレビューできます

設置には python3 が必要です(フック本体が Python スクリプトなので実行に必須。 settings のマージにも流用します)。 何度実行しても hooks.PreToolUse は重複せず、設置先を変えた場合も古いエントリは掃除されます。 反映には Claude Code の再起動(または一度 /hooks を開く)が必要な場合があります。