3.8 新增 write_recipe skill——recipe 指路終於有目的地
📋 SDD:workflow-discovery task 3.8(3.7 的 suggestion 指 skill write_recipe, 之前是空地——指路會指到不存在的 skill)。 - registry/skills/write_recipe.md:從真 code 反推,不是憑空教學—— schema=routes/recipes.ts RecipeDefinition(canonical_id/endpoint/method/auth_service…); 真範例=api-recipe-seeds.ts 的 telegram_send(URL path 注入)+gmail_send(service account); auth recipe 必填欄位(required_secrets 的 help_url 必填)照 POST /auth-recipes 驗證邏輯; 常犯錯收錄實錄(sheets append PUT→POST、telegram auth recipe 漏種、金鑰只准名字 D36) - INDEX.md 補 write_recipe 入口;「/cypher/search 假 found」坑改標已修(2026-07-31) - write_intent_workflow.md §6 status 表改 not_found 契約+suggestion/similar_* 欄說明 安裝器 seed:registry/skills/*.md 由 sync-registry-to-kbdb.py 自動收,新檔即納入。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
| 要做檢索問答(RAG) | `rag_with_arcrun` | `arcrun_get_skill('rag_with_arcrun')` |
|
||||
| workflow 卡住不動/paused | `debug_paused_workflow` | `arcrun_get_skill('debug_paused_workflow')` |
|
||||
| 想把 http 呼叫改成觸發別的 workflow | `migrate_http_to_trigger_workflow` | 同上 |
|
||||
| **缺某個外部 API 的 recipe**(查詢回 not_found 指 recipe 路)| `write_recipe` | `arcrun_get_skill('write_recipe')` |
|
||||
| **真的需要新零件**(罕見)| `add_new_wasm_component` | 同上 ⚠️ 先確認工作流做不到 |
|
||||
|
||||
## 二、我要查「有沒有現成的東西」
|
||||
@@ -39,7 +40,7 @@
|
||||
|
||||
| 坑 | 現況 | 怎麼避 |
|
||||
|---|---|---|
|
||||
| **`/cypher/search` 回假 `found`** | 對**任何**節點名都回 found(不查 registry)| status 目前不可信,改用 `list_components` 自己確認。修復中(CP `arcrun-usable` 步驟 3)|
|
||||
| **`/cypher/search` 曾回假 `found`** | 2026-07-31 已修:兩庫(零件+recipe)都查,缺件回 `not_found`+`suggestion` 指路。舊實例(未更新部署)仍是假 found | 拿到 `not_found` 照 `suggestion` 走;拿到 `unknown`=查不到 registry ≠ 不存在 |
|
||||
| **引擎沒有條件分支** | `grep ON_TRUE\|ON_FALSE` = 0;`if_control` 只回 boolean | 判斷寫成獨立節點接 `ON_SUCCESS`。見 Gitea Arcrun#5 |
|
||||
| **`registry/examples/` 8/13 是壞的** | 引用不存在的零件(把 recipe 當零件寫)| **別照抄 examples**,改用 `arcrun_get_workflow` 拿實跑過的 |
|
||||
| **registry 可能是空的** | 安裝器無註冊步驟 ⇒ 新實例查不到零件 | 查不到 ≠ 不存在,別據此改寫成 code |
|
||||
|
||||
@@ -121,13 +121,12 @@ curl -s -X POST https://arcrun-cypher-executor.<subdomain>.workers.dev/cypher/se
|
||||
|
||||
| status | 意思 | 你該做什麼 |
|
||||
|---|---|---|
|
||||
| `found` | 有零件,附 `input_schema`(怎麼填 payload)與 `success_rate` | **只填 payload** |
|
||||
| `missing` | 沒有這個零件 | 缺 API → 寫 recipe;缺能力 → 投稿零件 PR |
|
||||
| `found` | 有這個節點。`source: component` 附 `input_schema`(怎麼填 payload)與 `success_rate`;`source: recipe` 附 description/endpoint | **只填 payload** |
|
||||
| `not_found` | **兩庫(零件 registry+recipe 庫)都查過,確定沒有** | 照 `suggestion` 欄走:缺 API → 寫 recipe(skill `write_recipe`);缺計算能力 → 投稿零件 PR(skill `add_new_wasm_component`)。`similar_components`/`similar_recipes` 是相近候選——先看有沒有現成的能直接用 |
|
||||
| `unknown` | 查不到 registry | **不代表不存在**,別據此改寫成 code |
|
||||
|
||||
⚠️ **2026-07-30 已知限制**:`/cypher/search` 目前對**任何**節點名都回 `found`
|
||||
(不查 registry)⇒ **這個 status 現在不可信**。修復中(CP `arcrun-usable` 步驟 3)。
|
||||
在它修好前:用 `arcrun_list_components` / `arcrun_search_components` 自己確認零件是否存在。
|
||||
> 註(2026-07-31):`/cypher/search` 曾對任何節點名都回假 `found`,已修為真查兩庫。
|
||||
> 舊實例(未更新部署)仍可能假 found——status 可信度以該實例部署版本為準。
|
||||
|
||||
---
|
||||
|
||||
@@ -137,7 +136,7 @@ curl -s -X POST https://arcrun-cypher-executor.<subdomain>.workers.dev/cypher/se
|
||||
2. **第一個節點不是 `input`**
|
||||
3. **把 recipe 當零件寫**——`telegram_send`/`gmail`/`kbdb_get` 是 **recipe** 不是零件
|
||||
→ 寫成 `http_request` + 該 recipe
|
||||
4. 🔴 **查詢回 `missing` 就改寫成 `code` 節點**
|
||||
4. 🔴 **查詢回 `not_found` 就改寫成 `code` 節點**
|
||||
→ 那叫「腹語術」(表面用 Arcrun、實際全寫 JS)。正解:缺 API 寫 recipe、缺能力投稿零件。
|
||||
`code` 只用在**局部整形**(例:剝掉 LLM 回應的雜訊),不用來取代零件與流程控制。
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
# Skill: Write Recipe(寫 recipe)
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
**`/cypher/search` 回 `not_found` 且 `suggestion` 指「寫 recipe」時,讀這支。**
|
||||
|
||||
- 要打某個外部 API(Google Slides / Slack / 任何有 HTTP API 的服務),但 `arcrun_recipe_list()` 沒有
|
||||
- `acr recipe search <關鍵字>` 落空,回「公庫無符合的 recipe」
|
||||
- 你想把一條常用的 API 呼叫封裝成可重用、可投稿的配方
|
||||
|
||||
> 🔴 **不要因為沒有 recipe 就改寫成 `code` 節點**——那是「腹語術」。
|
||||
> recipe 不用改平台、不用部署 Worker、不用寫程式,**幾行 YAML 就能自己補上**。
|
||||
|
||||
## recipe 是什麼
|
||||
|
||||
**「http_request + 參數模板」的具名封裝**(真身存在 cypher-executor 的 RECIPES KV)。
|
||||
執行時 cypher-executor 直接 fetch 該 endpoint——不 deploy Worker、不寫 WASM。
|
||||
|
||||
| 你缺的是… | 走哪條路 |
|
||||
|---|---|
|
||||
| 打**外部 API**(服務有 HTTP API) | **recipe(本 skill)** |
|
||||
| **計算能力**(加解密/壓縮這類純運算) | 零件 PR → `arcrun_get_skill('add_new_wasm_component')` |
|
||||
|
||||
## 1. Recipe 的欄位(schema)
|
||||
|
||||
```yaml
|
||||
canonical_id: google_slides_create # 必填。小寫底線,全庫唯一的可讀名
|
||||
endpoint: https://slides.googleapis.com/v1/presentations # 必填。要打的 URL
|
||||
method: POST # 選填,預設 POST(GET/PUT/PATCH/DELETE 皆可)
|
||||
display_name: Google Slides Create # 選填,人看的名字
|
||||
description: 建立一份新簡報。POST presentations,body 帶 title。auth: google service_account。
|
||||
auth_service: google_slides_sa # 選填。指向 auth recipe(見第 3 節)
|
||||
headers: {} # 選填。額外固定 header
|
||||
body: {} # 選填。固定 body 欄位(會與節點 payload 合併)
|
||||
```
|
||||
|
||||
- `hash_id`(`rec_xxxxxxxx`)與 `uuid` 由系統自動生成,不用寫。
|
||||
- **description 認真寫**:AI(包括未來的你)靠它決定要不要用這個 recipe。
|
||||
照庫裡的慣例寫「做什麼。怎麼打。auth 用什麼。」三段。
|
||||
|
||||
## 2. 真範例(從實際 seed 且實跑過的 recipe 照抄)
|
||||
|
||||
### A. token 在 URL path(`telegram_send`)
|
||||
|
||||
```yaml
|
||||
canonical_id: telegram_send
|
||||
display_name: Telegram Send
|
||||
description: Telegram sendMessage。token 在 URL path({{auth.bot_token}}),body 帶 chat_id+text。auth: static_key path 注入。
|
||||
endpoint: https://api.telegram.org/bot{{auth.bot_token}}/sendMessage
|
||||
method: POST
|
||||
auth_service: telegram
|
||||
```
|
||||
|
||||
### B. Bearer/service account(`gmail_send`)
|
||||
|
||||
```yaml
|
||||
canonical_id: gmail_send
|
||||
display_name: Gmail Send
|
||||
description: 寄 Gmail。POST messages/send,body 帶 raw(base64url MIME)。auth: google service_account。
|
||||
endpoint: https://gmail.googleapis.com/gmail/v1/users/me/messages/send
|
||||
method: POST
|
||||
auth_service: google_gmail_sa
|
||||
```
|
||||
|
||||
### 模板變數(endpoint 裡可用)
|
||||
|
||||
| 變數 | 來源 | 例 |
|
||||
|---|---|---|
|
||||
| `{{auth.X}}` | auth recipe 的 `inject.path` 注入 | `bot{{auth.bot_token}}/sendMessage` |
|
||||
| `{{_path}}` | 節點 payload 的 `_path` 欄位(路徑由工作流決定時用) | `https://sheets.googleapis.com{{_path}}` |
|
||||
|
||||
🔴 **金鑰只准名字,不准真身**:endpoint/headers 裡**絕不**寫死 token。
|
||||
真值走 credential 中心(`acr creds push`),recipe 只留 `{{auth.X}}` 這種名字引用。
|
||||
|
||||
## 3. auth 怎麼接(多數 recipe 需要)
|
||||
|
||||
`auth_service: telegram` 表示執行時去拿 `auth_recipe:telegram` 做認證注入。
|
||||
先查有沒有:`acr auth-recipe list` 或 `GET /auth-recipes/<service>`。
|
||||
|
||||
**沒有就要一併建**(`POST /auth-recipes`,缺這步 recipe 會注入空值打不通):
|
||||
|
||||
```yaml
|
||||
service: telegram # 必填
|
||||
primitive: static_key # 必填:static_key | oauth2 | service_account
|
||||
base_url: https://api.telegram.org # 必填
|
||||
required_secrets: # 必填,每個 secret 的 help_url 也必填(官方文件連結)
|
||||
- key: telegram_bot_token
|
||||
label: Bot Token(從 @BotFather 取得)
|
||||
help_url: https://core.telegram.org/bots/features#botfather
|
||||
inject: # 必填:secret 注入到哪(header / query / body / path)
|
||||
path:
|
||||
bot_token: "{{secret.telegram_bot_token}}"
|
||||
```
|
||||
|
||||
然後用戶端上傳真值:`acr creds push`(存進 credential 中心,AI 拿不到真身)。
|
||||
|
||||
## 4. 裝上(三個介面同一個 API)
|
||||
|
||||
```bash
|
||||
acr recipe push my_recipe.yaml # CLI:push 時會實打 endpoint 做打通檢查
|
||||
```
|
||||
|
||||
- MCP:`arcrun_recipe_push(...)`
|
||||
- HTTP:`POST /recipes`(JSON,欄位同上)
|
||||
|
||||
裝好後 workflow 直接引用(節點名=canonical_id,或 config 指定):
|
||||
|
||||
```yaml
|
||||
config:
|
||||
notify:
|
||||
component: telegram_send # 或穩定引用 rec_xxxxxxxx
|
||||
```
|
||||
|
||||
payload(如 `chat_id`、`text`)由上游節點或 context 給——這正是「AI 只填 payload」。
|
||||
|
||||
## 5. 驗收(誠實原則)
|
||||
|
||||
1. push 時的**打通檢查**只是提醒級——真驗收=**跑一次 workflow、`verdict=success`(2xx)**
|
||||
2. 缺 credential 打不到 2xx → 誠實標「未驗收:缺 X」,**不 mock 充綠燈**
|
||||
3. 打通後可投稿公庫讓別人用:`acr recipe submit-p <canonical_id>`(成為該 recipe 的作者版本)
|
||||
|
||||
## 6. 常犯的錯
|
||||
|
||||
1. **token 寫死在 endpoint/headers** → 金鑰鐵律違規。只准 `{{auth.X}}` 名字引用
|
||||
2. **method 用猜的** → 查官方文件。實錄:Sheets append 被猜成 PUT(官方是 POST `:append`),
|
||||
seed 到壞 recipe 每個新用戶都打 400
|
||||
3. **只建 recipe 忘了 auth recipe** → `{{auth.X}}` 注入空值打不通。實錄:telegram auth recipe
|
||||
漏進種子,所有新實例的 telegram 發訊全斷
|
||||
4. **落空就寫 code 節點** → 腹語術。recipe 是正路,且寫好可投稿讓全生態重用
|
||||
5. **canonical_id 用大寫/空白** → 一律小寫底線(系統會 trim+lowercase,但別靠它救)
|
||||
|
||||
## 7. 相關
|
||||
|
||||
- 寫意圖工作流(上游,先寫意圖再查缺什麼):`arcrun_get_skill('write_intent_workflow')`
|
||||
- 缺的是計算零件不是 API:`arcrun_get_skill('add_new_wasm_component')`
|
||||
- 查公庫現貨:`acr recipe search <關鍵字>` / `arcrun_recipe_search(...)`
|
||||
- 拉別人寫好的:`acr recipe pull <canonical_id> [--author=<name>]`
|
||||
Reference in New Issue
Block a user