AI 一連上 MCP 就知道從哪開始(leo 追問揪出的最後一哩)

leo:「arcrun_get_skill 放在 MCP 裡,現在人類說『幫我用 arcrun 寫 xxx』,
Haiku 會說『arcrun 是什麼?我看看』然後發現有個 arcrun mcp 就去執行?
如果不會,要寫什麼在外面讓它一聽到就知道要用這些資源?」

實測答案:**不會**。
① 總管寫的指引沒有任何入口指向它(真實 AI 找不到)
② MCP 五個 skill 沒有「怎麼寫意圖工作流」這支
③ AI 只看到 41 個工具名 → 自己猜 → 很可能直接 push_workflow 瞎編,
   或去讀 registry/examples 那 8/13 引用不存在零件的壞範例

兩件修法:
1. registry/skills/write_intent_workflow.md — 把指引變成 AI 拿得到的 skill
   (範本全取自實跑 verdict=success 的四支 workflow;含「查詢現在回假 found」的已知限制警告)
2. mcp-handler.ts instructions 開場指路 — instructions 是唯一「AI 一連上就必看」的欄位
   ⇒ 開場就寫明五步順序(先讀 write_intent_workflow → whoami → 查零件 → list_skills → 缺件怎麼補)
   +邊只有兩種、第一個節點是 input、不要因查不到就改寫 code
   ⚠️ 靜態常數:KBDB 掛了也必出現(守「不擋連線」鐵律)

驗:tsc 零錯誤。 待部署後由 leo 真的問 haiku「幫我用 arcrun 做 X」驗證它會不會自己讀 skill。
This commit is contained in:
2026-07-30 22:29:28 +08:00
parent 0686d39fa1
commit fac7d6c9bb
2 changed files with 179 additions and 1 deletions
+26 -1
View File
@@ -15,10 +15,35 @@ export async function handleMcpRequest(
//(選型理由見 lib/library-map.ts 檔頭);任何失敗回 null → 靜默略過,絕不擋 MCP 連線(鐵律)。
const mapInstructions = await buildLibraryMapInstructions(env);
// 2026-07-30leo 問「人類說『幫我用 arcrun 寫 xxx』,Haiku 會知道要用這些資源嗎?
// 如果不會,要寫什麼在外面讓它一聽到就知道?」):
// 實測答案是**不會**——AI 只看到 41 個工具名,會自己猜(很可能直接跳到 push_workflow
// 瞎編,或去讀 registry/examples 那 8/13 引用不存在零件的壞範例)。
// instructions 是唯一「AI 一連上就必看」的欄位 ⇒ 開場就指路,不依賴任何查詢成功。
// ⚠️ 這段是靜態常數:即使 KBDB 掛了、藏書地圖抓不到,它也必須出現(鐵律:不擋連線)。
const startHere = [
"【先讀這裡】要在 Arcrun 上做任何事(用戶說「幫我用 Arcrun 做 X」),**照這個順序**",
"",
"1. `arcrun_get_skill('write_intent_workflow')` — **必讀第一支**。",
" 教你用 `>>` 寫「意圖工作流」。你**不需要先知道有哪些零件**,先寫意圖。",
"2. `arcrun_whoami()` — 確認連到哪個帳號(勿自行 curl 猜帳號 URL)。",
"3. 把意圖丟 `POST /cypher/search` 或 `arcrun_validate_yaml` — 系統告訴你哪些零件存在。",
"4. `arcrun_list_skills()` — 看有沒有更貼近你這件事的 skillwatcherRAGdebug…)。",
"5. 缺零件時:缺 API → 寫 recipe`arcrun_recipe_push`);缺能力 → 投稿零件 PR。",
" 🔴 **不要因為查不到零件就改寫成 `code` 節點**——那叫「腹語術」(表面用 Arcrun、",
" 實際全寫 JS)。`code` 只用於局部整形(例:剝掉 LLM 回應的雜訊)。",
"",
"邊只有兩種:`ON_SUCCESS` 與 `對每個 <變數>`FOREACH)。",
"**沒有** `ON_TRUE``ON_FALSE``ON_FAILURE`——引擎目前不支援條件分支。",
"第一個節點固定是 `input`。",
].join("\n");
const instructions = mapInstructions ? `${startHere}\n\n---\n\n${mapInstructions}` : startHere;
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
const server = new McpServer(
{ name: "arcrun-mcp-server", version: "1.0.0" },
mapInstructions ? { instructions: mapInstructions } : undefined,
{ instructions },
);
registerAllTools(server, env, orgNamespace, partnerToken);
+153
View File
@@ -0,0 +1,153 @@
# Skill: Write Intent Workflow(寫意圖工作流)
## 何時用這個 skill
**用戶說「幫我用 Arcrun 做 X」時,第一個讀這支。** 其他 skill 都是它的下游。
- 「幫我用 Arcrun 寫一個…」
- 「用 Arcrun 做 X」
- 你要在 Arcrun 上做任何事,但不知道有哪些零件可用
> **你不需要知道有哪些零件。** 先把意圖寫出來丟去查,系統會告訴你哪些存在、哪些不存在。
## 核心 pattern
```
input >> ON_SUCCESS >> <第一步> >> ... → 丟 /cypher/search → 系統回哪些零件存在
```
---
## 1. 意圖工作流的語法
一串「誰接誰」,每行一個關係:
```
<節點A> >> <邊> >> <節點B>
```
- **節點**=一個步驟。用你想得到的名字(中文可以),**不必是真實零件名**
- **邊**=什麼情況下往下走
## 2. 邊只有兩種(真範本裡出現過的)
| 邊 | 意思 | 真例 |
|---|---|---|
| `ON_SUCCESS` | 上一步成功就往下 | `input >> ON_SUCCESS >> prep` |
| `對每個 <變數>` | 上一步產出清單,逐項處理(FOREACH)| `parse_card >> 對每個 block >> post_block` |
⚠️ **不要寫 `ON_FAILURE``ON_TRUE``ON_FALSE`**——引擎目前**沒有條件分支**
(實測 `grep ON_TRUE|ON_FALSE` 於 cypher-executor = 0;見 Gitea Arcrun#5)。
需要判斷時:**寫成一個獨立節點**(例 `check_amount`)再接 `ON_SUCCESS`
讓查詢告訴你有沒有零件可用。
## 3. 第一個節點固定是 `input`
所有真範本都以 `input` 起頭——那是「觸發時帶進來的資料」。
---
## 4. 真範本(照抄結構、改內容)
> 以下四份**全部是實際部署且 `verdict=success` 的 workflow**,不是簡化示範。
> 用 `arcrun_get_workflow(<name>)` 可以拿完整定義。
### A. 最短:取資料 → 處理 `graph_neighbors`
```
input >> ON_SUCCESS >> fetch_triplets
fetch_triplets >> ON_SUCCESS >> bfs_neighbors
```
### B. 長鏈:多次查詢 → 組裝 → 問 AI → 收尾 `rag_chat`
```
input >> ON_SUCCESS >> prep
prep >> ON_SUCCESS >> kw_search
kw_search >> ON_SUCCESS >> sem_search
sem_search >> ON_SUCCESS >> fetch_triplets
fetch_triplets >> ON_SUCCESS >> fetch_blocks_a
fetch_blocks_a >> ON_SUCCESS >> assemble
assemble >> ON_SUCCESS >> ask_llm
ask_llm >> ON_SUCCESS >> finalize
```
`prep` 前處理/`assemble` 組 prompt`finalize` 收拾回應——三個常見的整形節點。
### C. 一節點分岔兩條 FOREACH `rag_ingest_card`
```
input >> ON_SUCCESS >> parse_card
parse_card >> 對每個 block >> post_block
parse_card >> 對每個 rel >> post_triplet
```
同一節點可有多條出邊,各自處理不同清單。
### D. 混合:直線 兩段 FOREACH `rag_takedown_direct`
```
input >> ON_SUCCESS >> prep
prep >> ON_SUCCESS >> list_dead_blocks
list_dead_blocks >> ON_SUCCESS >> build_deprecations
build_deprecations >> 對每個 dead_entry >> deprecate_entry
build_deprecations >> ON_SUCCESS >> list_triplets
list_triplets >> ON_SUCCESS >> pick_dead_triplets
pick_dead_triplets >> 對每個 dead_record >> deprecate_triplet
```
`build_deprecations` 同時有 FOREACH 出邊與 `ON_SUCCESS` 出邊——
前者處理清單、後者繼續主線。
---
## 5. 節點怎麼命名(照真範本的模式,查詢較容易媒合)
| 意圖 | 模式 | 真例 |
|---|---|---|
| 前處理/正規化 | `prep` | `rag_chat.prep` |
| 取一批資料 | `fetch_*``list_*` | `fetch_triplets``list_dead_blocks` |
| 搜尋 | `*_search` | `kw_search``sem_search` |
| 解析/切塊 | `parse_*` | `parse_card` |
| 寫入 | `post_*` | `post_block``post_triplet` |
| 組裝 | `assemble``build_*` | `assemble``build_deprecations` |
| 問 AI | `ask_llm` | `rag_chat.ask_llm` |
| 收尾整形 | `finalize` | `rag_chat.finalize` |
---
## 6. 寫完一定要查(**不要直接部署**)
```bash
curl -s -X POST https://arcrun-cypher-executor.<subdomain>.workers.dev/cypher/search \
-H 'content-type: application/json' -H 'X-Arcrun-API-Key: <namespace>' \
-d '{"triplets":["input >> ON_SUCCESS >> fetch_data","fetch_data >> ON_SUCCESS >> notify"]}'
```
回應的每個節點會有:
| status | 意思 | 你該做什麼 |
|---|---|---|
| `found` | 有零件,附 `input_schema`(怎麼填 payload)與 `success_rate` | **只填 payload** |
| `missing` | 沒有這個零件 | 缺 API → 寫 recipe;缺能力 → 投稿零件 PR |
| `unknown` | 查不到 registry | **不代表不存在**,別據此改寫成 code |
⚠️ **2026-07-30 已知限制**`/cypher/search` 目前對**任何**節點名都回 `found`
(不查 registry)⇒ **這個 status 現在不可信**。修復中(CP `arcrun-usable` 步驟 3)。
在它修好前:用 `arcrun_list_components` / `arcrun_search_components` 自己確認零件是否存在。
---
## 7. 常犯的錯
1. **用不存在的邊**`ON_FAILURE``ON_TRUE`)→ 只有 `ON_SUCCESS``對每個 X`
2. **第一個節點不是 `input`**
3. **把 recipe 當零件寫**——`telegram_send``gmail``kbdb_get`**recipe** 不是零件
→ 寫成 `http_request` 該 recipe
4. 🔴 **查詢回 `missing` 就改寫成 `code` 節點**
→ 那叫「腹語術」(表面用 Arcrun、實際全寫 JS)。正解:缺 API 寫 recipe、缺能力投稿零件。
`code` 只用在**局部整形**(例:剝掉 LLM 回應的雜訊),不用來取代零件與流程控制。
---
## 8. 相關
- 完整版指引與十題考卷(含 haiku 實測 10/10):
頂層 repo `system-dev/docs/3-specs/arcrun-usable/`
- 下一步該讀哪支 skill`arcrun_list_skills()`
- 定期掃資料 → `build_watcher_workflow`
- RAG 檢索問答 → `rag_with_arcrun`
- workflow 卡住不動 → `debug_paused_workflow`