Skip to content

Commit 3f6579c

Browse files
committed
収集の依頼を市区町村単位で回せるようにし、いま開いている種別も削除できるようにする
情報を集めさせる(知識サーバーへの収集依頼)を実際に走らせて出た問題を直した。 - プロンプトを都道府県単位から市区町村単位へ。件数を数字で書くのをやめた —— 下限を書くとちょうどその数で止まる。1回で埋まらなければ次回も同じ地域に 留まり、カーソルは起点を中心に放射状に広げる。知識サーバー側の辞典 (地名・POI・百科事典)も裏取りに使わせる - 収集の起点を種別の設定に持てるようにした。指定が無いと日本の端から始まり、 見たい地域が埋まるまでに何周もかかっていた - 相手・モデル・深さを管理画面から選べるようにした。未指定のままだと いちばん浅い既定で動き、3分で8件しか集まらなかった - 地図の「この周辺の集めた情報」が、円の中が収集済みかどうかも返すようにした。 0件と未収集は別のことなので、足りない範囲があればその場で集めさせられる。 円が市区町村の境界をまたぐ場合に備えて中心と円周8方位を突く - 管理画面に知識サーバーが収集中かどうかを出す。同時に1本しか走らないので、 押してから断られるのと押せないことが見えているのとでは別物 あわせて2つ。 - ランク(rank_enabled)の既定をtrueにした。種別を足すたびに開けさせるより、 要らない種別で閉じるほうが手数が少ない。ランクの付いていないスポットは 見た目が変わらないため、開けても既存のピンは動かない - 「別のスポット種別の管理」で、いま開いている種別も削除できるようにした。 作っては試す種別ほど、いま開いたまま消したくなる。消したあとはURLの種別 セグメントが無くなるので、別の種別の管理画面へ移す Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent e82d0b9 commit 3f6579c

12 files changed

Lines changed: 846 additions & 86 deletions

File tree

CLAUDE.md

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ GitHub Actions(`.github/workflows/docker-publish.yml`)がビルド時に`<JST日
168168

169169
### スポット種別ごとのON/OFF設定(EAV: `spot_type_settings`)
170170

171-
`reviews_enabled`/`wikipedia_enabled`/`public_visible`/`rank_enabled`は`spot_types`に列を持たず、EAV形式の`spot_type_settings`テーブル(`spot_type_id, key, value` — boolean設定は`'true'`/`'false'`の文字列。同じテーブルに`series_styles`・`region_scope`・`wikipedia_lang`・`categories`のような文字列値のキーも同居する)に保存する。新しい設定を増やす際にDBマイグレーションが要らないようにするための設計で、キー・既定値・表示名は`lib/types.ts`の`SPOT_TYPE_SETTING_DEFAULTS`/`SPOT_TYPE_SETTING_LABELS`に登録するだけでよい(行が存在しないキーは設定ごとの既定値扱い、`getSpotTypeSetting`参照)。`public_visible`と`rank_enabled`は既定`false`(前者は種別追加当初は非公開・admin/spot_admin限定、後者はランクを使わない)で、他2つは既定`true`。`app/api/spot-types/[id]/route.ts`のPATCHは`{ settings: { key: boolean, ... } }`を受け取り`spot_type_settings`へupsertする汎用エンドポイントで、設定を増やしてもAPI自体の変更は不要。`SpotType`型の`settings`フィールド(`key→value`の文字列マップ)は`lib/spot-types-query.ts`の`SPOT_TYPE_SELECT`(`spot_type_settings`をjsonbに集約するSELECT共通部品)を使うクエリでのみ埋まる点に注意(`select * from spot_types`だけでは`settings`は付与されない)。
171+
`reviews_enabled`/`wikipedia_enabled`/`public_visible`/`rank_enabled`は`spot_types`に列を持たず、EAV形式の`spot_type_settings`テーブル(`spot_type_id, key, value` — boolean設定は`'true'`/`'false'`の文字列。同じテーブルに`series_styles`・`region_scope`・`wikipedia_lang`・`categories`のような文字列値のキーも同居する)に保存する。新しい設定を増やす際にDBマイグレーションが要らないようにするための設計で、キー・既定値・表示名は`lib/types.ts`の`SPOT_TYPE_SETTING_DEFAULTS`/`SPOT_TYPE_SETTING_LABELS`に登録するだけでよい(行が存在しないキーは設定ごとの既定値扱い、`getSpotTypeSetting`参照)。既定`false`は`public_visible`(種別追加当初は非公開・admin/spot_admin限定)と`ai_discovery_enabled`(**押すとAIの枠を使う**ので、全部の種別のメニューに最初から並んでいてほしいものではない)の2つで、他は既定`true`。**`rank_enabled`は既定で開ける** —— 種別を足すたびに開けさせるより、要らない種別で閉じるほうが手数が少ない。**開けても既存のデータは動かない**(ランクの付いていないスポットは`rankStyleOf(null)`がどちらでも同じ`NO_RANK_STYLE`を返すので、ピンの見た目が変わらない)。`app/api/spot-types/[id]/route.ts`のPATCHは`{ settings: { key: boolean, ... } }`を受け取り`spot_type_settings`へupsertする汎用エンドポイントで、設定を増やしてもAPI自体の変更は不要。`SpotType`型の`settings`フィールド(`key→value`の文字列マップ)は`lib/spot-types-query.ts`の`SPOT_TYPE_SELECT`(`spot_type_settings`をjsonbに集約するSELECT共通部品)を使うクエリでのみ埋まる点に注意(`select * from spot_types`だけでは`settings`は付与されない)。
172172

173173
### 見た目の軸: ランク・シリーズ・カテゴリ
174174

@@ -208,7 +208,7 @@ GitHub Actions(`.github/workflows/docker-publish.yml`)がビルド時に`<JST日
208208
旧: 26 / 22 / 18 / 15 / 12 → 現: **30 / 26 / 23 / 20 / 18**。Eの12pxは地図上で
209209
点にしか見えず、ピンの中のアイコンも潰れていた。段の差は詰めて全体を上げてある
210210
- **ランクなしはBと同じ大きさで白**。小さくすると「まだ決めていない」ものが埋もれる
211-
- **種別ごとに使うかを選ぶ**(`rank_enabled`**既定は使わない**)。使わない種別では
211+
- **種別ごとに使うかを選ぶ**(`rank_enabled`**既定で使う**)。使わない種別では
212212
ランクは常になし扱いで、大きさはランクなし相当に固定される
213213
- **色はシリーズが勝つ。** シリーズに`color`の指定があればそれを使い、無いときだけ
214214
ランクの色になる —— **ランクと色を別の軸に使いたい種別がある**ため
@@ -983,9 +983,17 @@ travel-log-data(MIT側)へ持ち込まないよう、還元するときは出ど
983983
AIが動かないことが、あの口を外へ開けておける理由。**画面は止まっている間その旨を出す**
984984
(これが読めないと「依頼したのに何も集まらない」で止まる)。
985985

986-
**間隔は30日**(`COLLECT_INTERVAL_MINUTES`)。あちらは間隔が必須(最小5分)で
987-
「手動のみ」が無いので、レポートのように欲しくなったときだけ起こす使い方に合わせるには、
988-
長く置いて予定のほうを事実上使わない形にするしかない。
986+
**間隔は3時間**(`COLLECT_INTERVAL_MINUTES`)。**1回あたりを長く・広く取り、周回で
987+
精度を上げる**使い方に合わせてある —— 1回で薄く10件ずつ拾っても全国は埋まらないし、
988+
間隔を詰めても知識サーバーは同時に1本しか走らせない(混んでいれば断られるだけ)。
989+
1回でどこまで広く扱うかはプロンプト側の仕事で、足りなければ画面の「いま集める」で
990+
予定を待たずに起こせる。
991+
992+
**どこを回り終えたかは知識サーバーが持つ**(あちらの`covered`)。`{cursor}`
993+
「次はどこ」の1本なのに対し、あれは一覧 —— これが無いと「2周目で埋める」も
994+
「この円は収集済みか」も判定できない。既定のプロンプト(`defaultCollectPrompt`)は
995+
`{cursor}``{covered}`を両方使い、**件数を決めず**「その地域にあるだけ挙げる」
996+
「全部回り終えたら、いちばん古く回った地域へ戻って前回より細かく」と書いてある。
989997

990998
#### 取り出し(`./candidates`)
991999

@@ -1017,10 +1025,52 @@ AIが動かないことが、あの口を外へ開けておける理由。**画
10171025
「位置を直す」を押せば選べるようになる。**`addDiscoveryRows`でも同じ歯止めを掛ける**
10181026
(画面だけだと、経路を1つ足したときに赤道上へスポットが立つ)。
10191027

1028+
#### 円の中が収集済みか(`coverage`)
1029+
1030+
地図の長押しメニューの「この周辺の集めた情報」は、**その円の中が収集済みかも一緒に返す**
1031+
(`?lat=&lng=&radius=`)。**集まっているぶんは出したうえで、足りない範囲を言う** ——
1032+
「0件」と「まだ集めていない」は別のことで、後者なら集めさせれば済む。
1033+
1034+
**中心だけでは足りない。** 円が市区町村の境界を跨いでいると隣を見落とすので、
1035+
**中心と円周8方位**を突く(`areaProbePoints`)。円の中を隙間なく確かめることはできない
1036+
(地図辞典は地物の点しか持たず、市区町村の形は持っていない)ので、代表点で見る。
1037+
8方位なのは市区町村が円より大きいのが普通だからで、増やしても同じ地域を何度も引くだけ。
1038+
1039+
**1点の所在は辞典を2つとも引く**(`areaAt`)。市区町村は**Overtureの`locality`**
1040+
(OSMは持たない)、都道府県は**OSMの`area`** —— Overtureの`area`はJISのコードで
1041+
日本ではほぼ空。実測でも、片方だけだと同じ円の中で「東京都新宿区」と「新宿区」が
1042+
混ざり、`Tokyo`のような英字の広い地名まで地域として数えられた。**いちばん多いものを採る**
1043+
(先頭1件だと、境界のすぐ内側にある隣の市区町村の地物を掴む)。
1044+
1045+
**突き合わせは書き方の揺れを吸う**(`isCovered`)。回り終えた印はAIが書くので
1046+
「東京都新宿区」とも「新宿区」とも来る。市区町村が入っていることを必須にし、
1047+
都道府県は**別の県を名乗っているときだけ**弾く(同名の市が別の県にあるため)。
1048+
実測: 印が`["東京都新宿区","中野区","大阪府渋谷区"]`のとき、新宿区と中野区は収集済み、
1049+
東京都渋谷区は未収集と出る。
1050+
1051+
**まだのところは「いま集める」で先に回らせる**(`POST .../run?area=`)。専用の口は
1052+
知識サーバーに無いが、「次はどこ」のカーソルは元から書き換えられるので、
1053+
**書き換えてから起こす**だけで足りる。書き換えるのは1回ぶんで、AIが`next_cursor`
1054+
返せば順番は元の流れへ戻る。**1つずつしか頼めない**(あちらは同時に1本しか走らせない)
1055+
ので、足りない地域のうち先頭を渡す。
1056+
1057+
**円を指定すると候補もその中だけ**返す。座標の引けなかった候補は所在地で見る ——
1058+
新しい店は地図辞典に載っていないのが普通で、そこを落とすとこの機能で拾いたいものが
1059+
ちょうど落ちる。
1060+
10201061
#### 画面
10211062

10221063
**取り出しと選択は周辺を探すのパネルを流用する**(地図の長押しメニューの
1023-
「集めた候補を見る」→ `openCollected`)。出てくるのは同じ`DiscoveryResult`なので、
1064+
「この周辺の集めた情報」→ `openCollected`)。円の半径は**周辺を探すより広い2km**
1065+
(`COLLECT_CIRCLE_RADIUS`)—— あちらは「いま歩いて行ける範囲」を見るが、
1066+
こちらが答えるのは「この辺りは収集済みか」で、収集の単位は市区町村。300mで聞くと
1067+
同じ市区町村の中を何度も聞き直すことになる。
1068+
1069+
**管理画面には「いま収集中か」を出す**(`GET /v1/ingest/status`)。知識サーバーは
1070+
同時に1本しか受けないので、走っている間に頼んでも断られる —— 押してから断られるのと、
1071+
押せないことが見えているのとでは別物。回り終えた件数と直近に回った地域も並べる
1072+
(積む順が回った順なので、末尾が直近)。
1073+
出てくるのは同じ`DiscoveryResult`なので、
10241074
地図の印・行ごとのシリーズ・登録済みの印・まとめて追加がそのまま使える ——
10251075
出どころが違うだけのものに別の画面を作る理由が無い。**出どころは3つ目**として
10261076
`DiscoverySource``collect`を足し、地図の印もバッジも専用の色(teal)にする。
@@ -1029,7 +1079,7 @@ AIが動かないことが、あの口を外へ開けておける理由。**画
10291079

10301080
### 公開スポットの全削除・スポット種別の削除
10311081

1032-
管理画面の`/[type]/admin`にはadmin専用の「公開スポットの全削除」(`app/api/spots/purge/route.ts`)と「スポット種別の削除」(`DELETE /api/spot-types/[id]`、同ファイルのPATCHと同居)がある。前者は`spot_types`の行自体は消さず、対象種別の公開(published)スポットのみを全件削除する(承認待ち・却下・非公開のスポットは残す。CSVで作り直す対象=CSVインポートが取り込む公開スポットに限定するため)。削除される公開スポットに紐づく`visits`/`visit_plans`/`reviews`(FKの`on delete cascade`)・写真ファイルと、対象種別のルート(`spot_routes`。status問わず丸ごと)も一括で消す。後者はstatus問わず対象種別の全スポットを削除(紐づくデータ・写真ファイルの扱いは前者と同じ)した上で`spot_types`の行自体も削除する(「別のスポット種別の管理」一覧には現在表示中の種別も含めて全種別を出すが、現在表示中の種別だけはリンク化・削除ボタンをUI側で出さないことで自分が今開いている種別を誤って消せないようにしている)。後者は`public_visible`がtrue(一般公開中)の種別、または対象種別が`app_settings.active_spot_type_id`(ルート`/`リダイレクトのフォールバック既定)の場合はAPIレベルで拒否する(既定の種別は常にpublic_visible=trueであるため後者は実質前者に含まれるが、防御的に両方チェックしている)。どちらもCSVでデータを作り直す前提の機能で、spot_adminには許可していない(ユーザー管理と同様、他ユーザーのデータを巻き込むため)。ルート`/`アクセス時に開く既定の種別(最後に開いていた種別のCookieが無い・開けないときのフォールバック)の変更は、この一括削除等の管理系操作とは別の独立したセレクトボックス(`app_settings.active_spot_type_id`を更新)として`/[type]/admin`に置いている。
1082+
管理画面の`/[type]/admin`にはadmin専用の「公開スポットの全削除」(`app/api/spots/purge/route.ts`)と「スポット種別の削除」(`DELETE /api/spot-types/[id]`、同ファイルのPATCHと同居)がある。前者は`spot_types`の行自体は消さず、対象種別の公開(published)スポットのみを全件削除する(承認待ち・却下・非公開のスポットは残す。CSVで作り直す対象=CSVインポートが取り込む公開スポットに限定するため)。削除される公開スポットに紐づく`visits`/`visit_plans`/`reviews`(FKの`on delete cascade`)・写真ファイルと、対象種別のルート(`spot_routes`。status問わず丸ごと)も一括で消す。後者はstatus問わず対象種別の全スポットを削除(紐づくデータ・写真ファイルの扱いは前者と同じ)した上で`spot_types`の行自体も削除する(「別のスポット種別の管理」一覧には現在表示中の種別も含めて全種別を出す。現在表示中の種別だけはリンク化しない(いま開いている画面へのリンクになるため)が、**削除はできる** —— 作っては試す種別ほど「いまここ」で消したくなるため。**削除したら別の種別の管理画面へ移す**(`AdminView`の`handleDeleteType`。URLの`[type]`が消えた状態に留まると以降の読み込みが全部404になる。残りが1つも無ければ`/`へ送る)。確認ダイアログには「いま開いている種別です」の一文を足す。削除ボタンを出さないのは**公開中(`public_visible`)の種別**だけで、これはAPIも断るため押せても意味が無い)。後者は`public_visible`がtrue(一般公開中)の種別、または対象種別が`app_settings.active_spot_type_id`(ルート`/`リダイレクトのフォールバック既定)の場合はAPIレベルで拒否する(既定の種別は常にpublic_visible=trueであるため後者は実質前者に含まれるが、防御的に両方チェックしている)。どちらもCSVでデータを作り直す前提の機能で、spot_adminには許可していない(ユーザー管理と同様、他ユーザーのデータを巻き込むため)。ルート`/`アクセス時に開く既定の種別(最後に開いていた種別のCookieが無い・開けないときのフォールバック)の変更は、この一括削除等の管理系操作とは別の独立したセレクトボックス(`app_settings.active_spot_type_id`を更新)として`/[type]/admin`に置いている。
10331083

10341084
### スポットの新規登録フロー
10351085

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
| **対象地域** | 種別ごとに日本(都道府県)・特定の国(州・県)・世界全体(国)から選べる |
3737
| **AIに聞く** | スポットの詳細から、Claude・ChatGPT・Perplexity・Grok・Geminiに所在地と座標を添えて質問できる(営業時間とアクセスを先に答えるよう頼んである) |
3838
| **周辺を探す** | 管理者が地図の右クリック/長押しから検索語(「ランチ」など)を入れると、周辺の候補を地図に番号つきで並べる。**まず地図データ(Overture Maps と OpenStreetMap)から1秒かからず出し**、足りなければAIのweb検索で同じ一覧に探し足せる。右側のパネルで外す・選ぶ・まとめて追加ができる。任意機能で、知識サーバー(chiezo)への接続先を設定し、種別ごとに有効にしたときだけ出る |
39-
| **情報を集めさせる** | 周辺を探すの「待たない版」。知識サーバーに**依頼だけして離れ、集まった頃にスポットの候補として取り出す**(AIのレポートのような使い方)。集めさせる内容は種別ごとに管理画面で書く。座標は地図データから引き直すので、当てられなかったものには印が付く |
39+
| **情報を集めさせる** | 周辺を探すの「待たない版」。知識サーバーに**依頼だけして離れ、集まった頃にスポットの候補として取り出す**(AIのレポートのような使い方)。地域を順に舐めながら集めていき、**地図で範囲を指定すると「そこは収集済みか」まで答える** —— まだなら、その地域を先に集めさせられる。座標は地図データから引き直すので、当てられなかったものには印が付く |
4040
| **PWA対応** | ホーム画面に追加して、アドレスバーなしの独立アプリとして起動できる(オフライン対応は未実装) |
4141

4242
## スクリーンショット
@@ -181,11 +181,16 @@ AI が選んだ候補にはチェックと「AIが選定」の印が付き、ジ
181181
登録済みは灰色)。地図データは日本の種別のみ。
182182

183183
**その場で待たずに集めさせることもできる**(管理画面の「情報を集めさせる」)。
184-
何を集めさせるかを種別ごとに書いて依頼しておくと、知識サーバー側が時間を掛けて集める ——
185-
集まった頃に地図の長押しメニューの**「集めた候補を見る」**から、周辺を探すと同じパネルで
186-
選んで追加する。座標は同じく地図データから引き直し、当てられなかったものは
184+
何を集めさせるかを種別ごとに書いて依頼しておくと、知識サーバー側が数時間ごとに
185+
**地域を順に舐めながら**集めていく(1周目は粗く、2周目3周目で取りこぼしを埋める)。
186+
集まったものは地図の長押しメニューの**「この周辺の集めた情報」**から、周辺を探すと
187+
同じパネルで選んで追加する。座標は同じく地図データから引き直し、当てられなかったものは
187188
「座標なし」の印が付いて、住所から引き直すまで選べない。
188189

190+
**その範囲がまだ集まっていなければ、そう出る。** 円が跨ぐ市区町村のうち回り終えて
191+
いないものを挙げ、**「いま集める」でその地域を先に回らせられる**(予定の順番を飛ばす)。
192+
管理画面では、いま知識サーバーが収集中かどうかと、回り終えた地域の数も見られる。
193+
189194
**依頼しても勝手には走らない。** 知識サーバー側の管理画面で「有効にする」を押すまでは
190195
止まったままで、travel-log 側からは動かせない(外のアプリが勝手に AI を走らせないため)。
191196

0 commit comments

Comments
 (0)