Maillage de carte de travail LLM

Place les stations d'un réseau de métro sur une grille selon leur position géographique relative, en respectant l'ordre des arrêts. Génère les coordonnées de cellules via apply.

Spar Skills Guide Bot
DeveloppementIntermédiaire
0031/08/2026
Claude CodeCursorWindsurfCopilotCodex
#llm#metro-map#grid-layout#working-map#edge-order

Recommandé pour


name: llm-working2grid description: LLM 工作地圖網格化——請 AI 直接把工作地圖的站點排進格子裡。運作概念:請 AI 依站與站的地理相對位置替重要站排格子,這條鏈不給基準。每個重要站指定一個格子位置;唯一不能違反的是進站方向順序不能翻,其餘問題只記錄。何時呼叫:在畫面上按一下,或用批次請它跑。畫面上按「LLM 工作地圖網格化」,或用批次跑;系統不會自動代跑。被誰呼叫:網頁按鈕或批次程式會啟動。在哪執行:執行本 skill 的模型依地理相對位置替重要站排格子。程式匯出地理相對位置與拓撲(沒有基準那張格),模型寫出整批格子座標;程式檢查邊環繞序等規則,通過才依模型分開存檔。策略不寫死,每輪由模型自行構思。結果:得到一份依模型分開存放的網格圖。之後可接 LLM 指定形狀與 LLM 直線化,不跑那九種演算法。當使用者要求跑/重跑工作地圖網格化、做端到端對照、或問結果還沒產生時使用。

LLM Working-Map Gridding (llm-working2grid)

Put every point of a metro network on a square of a grid, from scratch, off the working map — the network is contracted the same way, but no grid is laid out for you first: there is no baseline here, only geography.

What you are given and what you hand back

Given: every point — meaning every station this chain lays out: interchange, terminus, crossing, cut, bend and separator (the node types in src/lib/nodeTypes.js) — with their positions relative to each other, the segments between them (a segment is a pair of points joined directly, with nothing in between), which lines run through each, and the order each line visits them.

Left out: intermediate stations — a station one single line passes straight through, with the same line on both sides. You never place those. placeBlacks spreads them evenly along the line afterwards, and the intermediate control point (a hand-placed anchor for line shape) counts as one of them.

Hand back: one cell per point — a column number and a row number, [col, row], both whole numbers ≥ 0, column 0 at the west edge and row 0 at the south edge. Written only through apply — never by editing files yourself.

You lay out the whole grid at once. apply does not tidy up after you: it checks the one hard rule, measures everything else, and saves. A messy layout is saved messy, on purpose.

Give a cell for exactly the indices export hands you — no more, no less. Intermediate stations have no index and get no cell. (Here they are not given an index at all, only the total throughTotal, so you cannot place one by accident.)

The hard rules

There is only one, and it is edge winding order. Stand on a station and look at the lines leaving it. Going once around the station (360°, by atan2), those lines must appear in the same order as they do in real geography. Same rule as makeMover's hard rule ④ — the shared implementation is edgeOrderViolations in src/stores/schematicGrid.js.

  • Points of degree ≤2 are not checked; interchanges and branch points are.
  • Why it matters: change that order and a line now enters the station from a different side (say from the north-east instead of the north-west), and anyone who knows the city can no longer match the diagram to the ground.
  • If you break it, apply prints the offending indices and station names and writes nothing. Put those neighbours back on their real-world bearings and try again.

How to run it

node scripts/llmWorking2Grid.mjs export <cityId> <orig> --model=<key>
  → prints verts (position, class, lines, degree) and routes (each line's points, in order)
  → you decide every point's cell → write cells.json
node scripts/llmWorking2Grid.mjs apply <cityId> <orig> <cells.json> --model=<key>
  → checks, measures and saves to
    maps/…/2-gridding/llm-working2grid/<model>/{city}-2-llm-working2grid-<model>.json
node scripts/llmWorking2Grid.mjs reset <cityId> <orig> --model=<key>
  → clears the "latest" pointer (`clearVersionPointer`) so the panel shows "not yet produced";
    no earlier version is ever deleted

cityId is the geojson filename without its extension. The second argument is always orig.

--model=<key> chooses the folder and the label written into the file; it is required here — with neither --model= nor the environment variable LLM_WORKING2GRID_MODEL the CLI stops. Use the model the user asked for; valid keys are in LLM_WORKING2GRID_KEYS. Each model keeps its own file and never overwrites another's.

What export gives you

| Field | Meaning | |---|---| | stationsTotal / segsTotal | How many points and segments there are. Look at these first and size up your approach | | throughTotal | How many intermediate stations exist. You do not place them — it is here only so you know the real scale | | routes[] | Each line as {name, color, seq}, where seq is its points in the order the line visits them | | segs | The pairs of directly-joined points, as index pairs. Fixed: you move points, not edges | | verts[i] | i, name, cls (node type), lines, deg (degree — how many segments touch it), gx/gy (the point's real position rescaled to 0–1000; gy grows southward, so a high gy belongs on a low row) | | axes | A reminder of the axis directions: column 0 = west, row 0 = south, high gy → low row; intermediate stations appear nowhere |

There is no baseline here: the ordinary, non-LLM way of building this grid is skipped entirely, so there is no control measured the same way. Judge yourself against the other tabs by eye.

cells.json format

{
  "model": "<model name>",
  "cells": { "0": [3, 7], "1": [3, 2],},
  "note": "what you did this round and why (shown in the panel)",
  "prompt": "the instruction that started this run (include it in the first round)"
}
  • Every index export gave you must appear. Miss one and the whole batch is rejected; apply prints which indices are missing.
  • Nothing else may appear — intermediate stations get no cell.
  • A cell is [col, row]: whole numbers, not negative. The index is verts[i].i.
  • model is overwritten on apply with the name that matches the folder, so do not rely on it.

Execution loop (what you have to do)

Lay out every point → hand the batch to apply → read the numbers it prints → improve and re-apply. Stop when either (a) two rounds in a row bring no improvement, or (b) you reach the hard cap of 10 rounds — whichever comes first. Never start an 11th. Only edge winding order can reject a batch; every other number is measured and printed.

Criteria, most important first:

  1. Edge winding order — see above. Fail it and the batch is rejected; nothing else counts.
  2. Place the interchanges first. Points with three or more segments touching them (deg here) are the frame of the map. Fix them, then fill the rest in along the lines. The reason is criterion 0: it is only checked at those points, so once they are settled the only thing that can reject you is settled, and points with 1–2 neighbours can go anywhere sensible. Do it the other way round and you will keep re-laying a whole line to make room for one interchange that no longer fits.
  3. One line, one direction. Decide a line's main direction first (horizontal, vertical or 45°), then place its points along that direction, following seq. This is what hv measures — the number of segments that came out exactly horizontal or vertical (hvd also counts 45° ones); higher is better.
  4. Keep the map pointing the right way, including points that are not joined. For any two points — even with no segment between them, even on different lines — what was east must stay east and what was north must stay north. Squashing distances and pulling interchanges closer is fine; turning the compass is not. Nothing measures this for you here — there is no baseline and no drift numbers are printed — so check gx / gy yourself before you hand the batch over.
  5. Tighten what should be tight, spread what should be spread. Interchanges that almost sit on top of each other should end up in neighbouring cells of one column or row; a long empty stretch out at the edge may be compressed. Criterion 3 still applies.
  6. One station per cell — a goal, not a gate. collide, like cross and vtxOn, is only measured and printed; it can never reject the batch (only criterion 0 can). collide (two points in one cell) or vtxOn (a point sitting on a line it does not belong to) is the serious kind, to be fixed in a later round; crossing lines (cross) do occur in real schematic maps. The flaws are left in on purpose — that is what makes this chain worth comparing against the ordinary one. Never throw a batch away because of collide.
  7. If the network is too big for you, say so plainly — do not pad the answer just to hand something in.

apply prints hv / segsTotal, hvd, cross, vtxOn, collide and grid. Fix whichever one you lost and apply again (the round counter goes up). Hard cap 10 rounds. Early stop: two rounds in a row with no improvement.

When you finish, report only: how many points and segments there were, hv, cross, vtxOn, collide, the grid size, and one honest sentence about whether it held together or fell apart, and where.

  • note must say what you actually did this round — which principle moved which points, and why. "Adjusted a few points" is worthless.
  • elapsedMs is clock time, not thinking time. It runs from startedAt (set by LLM_STARTED_AT) to the last apply, and includes queueing and idle time — the same batch has produced both 160 seconds and 6 hours. Do not read it as a measure of how well you solved anything.
  • Token usage is recorded for you — do not report it yourself. Every round and the run as a whole carry a usage block (input / output / cache read / cache write, summed into totalTokens), written from the driver's own view of the stream; nothing you put in the JSON you hand back affects it. Executors that do not report per-message usage leave it null, which means not measured — never zero.

Independence guards (the other reason apply can refuse)

This chain is only worth running if each model works it out on its own, so scripts/_llmIndependence.mjs refuses two kinds of copied answer before anything is written:

  • assertNotCrossChainEcho — your cells against the same model's file in the neighbouring gridding folder maps/…/2-gridding/llm-skeleton2grid/<model>/, which is the same city laid out from an already-simplified network. That is a different experiment; carrying one answer across measures nothing.
  • assertIndependentLayout — your cells against another model's saved result for this same city and chain (the sibling folders next to yours).

Both trigger at THRESHOLD = 0.9 — 90% or more of the points on exactly the same cell — and are skipped for networks with fewer than MIN_STATIONS = 8 points, where there really may be only one sensible answer.

Rules

  • Never edit files under maps/…/2-gridding/llm-working2grid/<model>/ by hand, and never write to any other result file. Everything goes through apply.
  • Never change the segments, the order stations come in along a line, or leave a point out. The shape of the network is fixed; a missing point is rejected.
  • Never reuse the answer from the neighbouring gridding folder, and never copy another model's file. Both are refused.
  • Never hand-edit anything downstream of this file.
  • If a big network is beyond what you can finish, say so — do not hand in a partial layout; apply rejects it anyway.
Skills similaires