docs(registry): seed 10 examples + 5 skills (LI SDD M3.1 + M3.3)
對應 .agents/specs/llm-interface/ Milestone 3.1 + 3.3。
registry/examples/ — 10 個可直接 push 的 workflow 範本:
starter: webhook-to-http
common: cron-watcher, llm-classify, rag-search-answer, daily-digest
external: email-summary (gmail+claude+telegram), pdf-to-blocks,
github-issue-bot
advanced: parallel-fanout (trigger_workflow fan-out),
error-retry (try_catch+wait pattern)
每個含:workflow.yaml(可直接 push)+ description.md(解決什麼問題 /
改成你自己的 / 學到什麼)+ tags.json(搜尋用)
registry/skills/ — 5 個 AI playbook(markdown):
build_watcher_workflow — cron + filter + trigger 模式
debug_paused_workflow — claude_api callback paused 怎麼追
migrate_http_to_trigger_workflow — 從 self-fetch 換 trigger_workflow
rag_with_arcrun — KBDB + claude_api 組裝 RAG
add_new_wasm_component — TinyGo 寫 + 部署全流程
兩者差異:
examples = 可直接拿來改的 YAML
skills = 面對 X 問題該怎麼想 + 該用哪個 example
兩者後續:CI 自動同步進 KBDB(type=workflow-example / type=agent-skill),
MCP arcrun_search_examples / arcrun_list_skills 走 KBDB semantic search。
(CI sync 是 M3.4 工作)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# Arcrun Skill Library
|
||||
|
||||
> 給 AI 操盤手用的 playbook(pattern + 流程指引)。
|
||||
> 比 examples 更高層 — examples 是「可直接用的 YAML」,skills 是「面對 X 問題該怎麼想 + 該用什麼 example」。
|
||||
>
|
||||
> 對應 SDD: `matrix/arcrun/.agents/specs/llm-interface/` Milestone 3.1
|
||||
|
||||
## 結構
|
||||
|
||||
每個 skill 是一份 markdown:
|
||||
|
||||
```
|
||||
{skill-name}.md
|
||||
```
|
||||
|
||||
## Skill 清單
|
||||
|
||||
| Skill | 何時用 |
|
||||
|---|---|
|
||||
| `build_watcher_workflow` | 用戶想「每 X 分鐘掃資料,找到符合的處理」 |
|
||||
| `debug_paused_workflow` | workflow 卡 paused 不動了 |
|
||||
| `migrate_http_to_trigger_workflow` | 看到舊 workflow 用 http_request 自打,CF self-fetch 死鎖 |
|
||||
| `rag_with_arcrun` | 用戶想做「問問題 + 用我的資料回答」 |
|
||||
| `add_new_wasm_component` | 缺零件需要寫新的(TinyGo WASM) |
|
||||
|
||||
## CI 自動同步
|
||||
|
||||
GH Actions 監聽本目錄變動 → PATCH 每個 skill 進 KBDB type=agent-skill block。
|
||||
MCP `arcrun_list_skills(tag?)` / `arcrun_get_skill(slug)` 給 AI 查。
|
||||
@@ -0,0 +1,156 @@
|
||||
# Skill: Add New WASM Component
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
`arcrun_list_components()` 沒有你需要的零件。要寫一個新的 TinyGo / AssemblyScript WASM。
|
||||
|
||||
**重要**:寫零件 = **改 arcrun 平台本身**,不是改 user workflow。
|
||||
這 skill 預設你有 arcrun repo write access。沒有 → 告訴用戶「需要 X 零件,請聯絡平台維護者」,停手。
|
||||
|
||||
## 7 步流程
|
||||
|
||||
### 1. 確認真的需要新零件
|
||||
|
||||
先想:能不能用 `http_request` 加組合搞定?
|
||||
- 多數第三方 API → `http_request` 已夠(搭配 `auth_recipe` 處理 auth)
|
||||
- 簡單轉換 → 用 logic primitives(`set` / `filter` / `array_ops`)
|
||||
- 複雜流程編排 → cypher binding 多步而非單一大零件
|
||||
|
||||
真的需要新零件的場景:
|
||||
- 跟 cypher-executor host functions 互動(KV、加解密、簽 JWT)
|
||||
- 邏輯太複雜不適合多節點分解
|
||||
- 為效能(一次 worker call 取代 10 次 fetch)
|
||||
|
||||
### 2. 讀規範
|
||||
|
||||
- `matrix/arcrun/.claude/rules/03-component-architecture.md` — 部署慣例
|
||||
- `matrix/arcrun/.claude/rules/01-tech-stack.md` — TinyGo 限制
|
||||
- 既有相似零件範例:`matrix/arcrun/registry/components/{name}/main.go`
|
||||
|
||||
### 3. 開新目錄
|
||||
|
||||
```
|
||||
matrix/arcrun/registry/components/{your_component}/
|
||||
├── main.go TinyGo source
|
||||
├── component.contract.yaml input/output schema + 描述
|
||||
└── (build 後產出 .wasm)
|
||||
```
|
||||
|
||||
合約格式(contract.yaml):
|
||||
```yaml
|
||||
canonical_id: your_component
|
||||
display_name: 中文顯示名
|
||||
category: data | auth | api | logic
|
||||
version: 0.1.0
|
||||
description: |
|
||||
做什麼用、限制、注意事項。AI 看這份決定要不要用你的零件
|
||||
input_schema:
|
||||
type: object
|
||||
required: [foo, bar]
|
||||
properties:
|
||||
foo: { type: string, description: "..." }
|
||||
bar: { type: number, description: "..." }
|
||||
output_schema:
|
||||
type: object
|
||||
properties:
|
||||
result: { type: string }
|
||||
success: { type: boolean }
|
||||
gherkin_tests:
|
||||
- given: "input foo=hello"
|
||||
when: "component runs"
|
||||
then: "result contains hello"
|
||||
```
|
||||
|
||||
### 4. 寫 main.go
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
Foo string `json:"foo"`
|
||||
Bar int `json:"bar"`
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
Result string `json:"result"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
bytes, _ := io.ReadAll(os.Stdin)
|
||||
var in Input
|
||||
json.Unmarshal(bytes, &in)
|
||||
|
||||
// 你的邏輯
|
||||
result := in.Foo + ":" + string(rune(in.Bar))
|
||||
|
||||
out := Output{Result: result, Success: true}
|
||||
json.NewEncoder(os.Stdout).Encode(out)
|
||||
}
|
||||
```
|
||||
|
||||
限制:
|
||||
- 只 import:`os`、`io`、`encoding/json`、`encoding/base64`、`strings`、`time` 等 stdlib
|
||||
- **禁用**:`net/http`(用 host function `u6u.http_request`)、`crypto/rsa`(用 host function)
|
||||
- 全部 logic 在 main(),stdin/stdout JSON I/O
|
||||
|
||||
### 5. 本地 build + 測
|
||||
|
||||
```bash
|
||||
cd registry/components/your_component
|
||||
tinygo build -target=wasi -o your_component.wasm main.go
|
||||
echo '{"foo":"hello","bar":42}' | wasmtime your_component.wasm
|
||||
```
|
||||
|
||||
### 6. 部署成獨立 Worker
|
||||
|
||||
```
|
||||
.component-builds/your_component/
|
||||
├── wrangler.toml name = "arcrun-your-component"
|
||||
├── package.json
|
||||
├── component.wasm 從上面 build 複製過來
|
||||
└── src/index.ts 固定 WASI shim(複製 component-worker-template)
|
||||
```
|
||||
|
||||
`wrangler.toml`:
|
||||
```toml
|
||||
name = "arcrun-your-component"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2025-02-19"
|
||||
workers_dev = true # 必須 true,cypher-executor 走 workers.dev 對內 URL
|
||||
[[routes]]
|
||||
pattern = "your-component.arcrun.dev/*"
|
||||
zone_name = "arcrun.dev"
|
||||
```
|
||||
|
||||
push → CI 自動部署。
|
||||
|
||||
### 7. 註冊到 cypher-executor 白名單
|
||||
|
||||
⚠️ **目前的架構債**(M2 計畫修):每加零件要手動加 `cypher-executor/src/lib/component-loader.ts`:
|
||||
|
||||
```ts
|
||||
const WASM_HTTP_RUNNER_IDS: ReadonlySet<string> = new Set([
|
||||
// ... 既有
|
||||
'your_component', // ← 加這行
|
||||
]);
|
||||
```
|
||||
|
||||
不加 → workflow 用你的零件會噴「找不到零件」。
|
||||
|
||||
未來會改成 registry KV 動態查(`cypher-executor-dynamic-component-discovery` SDD 待開)。
|
||||
|
||||
## 驗證上線
|
||||
|
||||
```
|
||||
arcrun_list_components() → 應該看到 your_component
|
||||
arcrun_get_component_contract(canonical_id='your_component') → 看 schema
|
||||
```
|
||||
|
||||
寫個 test workflow 用你的零件,跑通就完成。
|
||||
@@ -0,0 +1,86 @@
|
||||
# Skill: Build Watcher Workflow
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
用戶說:
|
||||
- 「每 X 分鐘 / 小時掃 Y → 找到符合條件的處理」
|
||||
- 「監聽某資料源,新資料進來自動處理」
|
||||
- 「定期巡 X 看有沒有新的」
|
||||
|
||||
## 核心 pattern
|
||||
|
||||
```
|
||||
cron → list (撈候選) → filter (過濾未處理) → 對每個 → trigger 處理 workflow
|
||||
```
|
||||
|
||||
## 5 步流程
|
||||
|
||||
### 1. 確認資料源
|
||||
|
||||
問用戶(或從上下文推):
|
||||
- 資料在哪?KBDB / 外部 API / 檔案系統?
|
||||
- 用什麼欄位區分「已處理 vs 未處理」?常見:
|
||||
- tag(`tags_json` 有沒有 `"processed"`)
|
||||
- 狀態欄位(`status: pending`)
|
||||
- 缺某 metadata(如沒 `summary`)
|
||||
- 不要靠時間判斷 — 因為 cron 漏跑會永久 miss
|
||||
|
||||
### 2. 看範例 + 改
|
||||
|
||||
`arcrun_search_examples('cron watcher')` → 命中 `cron-watcher` 範例。
|
||||
複製 YAML 改三處:
|
||||
- `watch_cron.cron_expr` — 改頻率
|
||||
- `list_unprocessed` — 改 query
|
||||
- `filter_new.condition` — 改你的「未處理」定義
|
||||
- `trigger_processor.workflow_name` — 改你的處理 workflow 名
|
||||
|
||||
### 3. 處理 workflow 要 idempotent
|
||||
|
||||
watcher 可能重跑(cron 漏跑後補跑、手動 trigger 補跑)。處理 workflow 必須:
|
||||
- 第一步檢查「我是不是已處理過此 record」
|
||||
- 或在末步 mark 已處理(加 tag / 改 status)
|
||||
- 失敗時 graceful(記 telemetry,不重 crash)
|
||||
|
||||
### 4. 永遠用 `trigger_workflow` 不用 `http_request` 自打
|
||||
|
||||
**這是 #1 死坑**。cypher-executor 走 `http_request` 打自己的 `cypher.arcrun.dev` 或
|
||||
`arcrun-cypher-executor.*.workers.dev` 都被 CF self-fetch 防護擋(1042 / 522 錯誤)。
|
||||
|
||||
用內建 `trigger_workflow` 零件:
|
||||
```yaml
|
||||
trigger_processor:
|
||||
component: trigger_workflow
|
||||
workflow_name: "your_processor"
|
||||
api_key: "{{api_key}}"
|
||||
input:
|
||||
api_key: "{{api_key}}"
|
||||
block_id: "{{item.id}}"
|
||||
```
|
||||
|
||||
### 5. 部署 + 驗證
|
||||
|
||||
```
|
||||
arcrun_validate_yaml(yaml) → arcrun_push_workflow(yaml) → wait 5 min → arcrun_list_recent_executions
|
||||
```
|
||||
|
||||
第一次 cron tick 跑完後看 executions list 確認有運作;若沒有,看 `arcrun_list_paused_executions` 看有沒有卡住。
|
||||
|
||||
## 常見陷阱
|
||||
|
||||
| 症狀 | 原因 | 解 |
|
||||
|---|---|---|
|
||||
| watcher 跑了但每次處理同樣 N 筆 | 沒做 mark 已處理 | 處理 workflow 末步加 tag / status 變更 |
|
||||
| watcher 跑了沒處理任何 | filter condition 寫錯 | acr validate 過但邏輯錯,curl 觸發測一次手動觸發看 trace |
|
||||
| 處理 workflow 永遠 paused | claude_api callback 沒回 | mira daemon 健康檢查;正常是 30-60 秒回 |
|
||||
| 處理量大爆 worker | 一次 trigger 太多 | list_unprocessed 加 limit,分多次 cron 跑 |
|
||||
| cron 沒 fire | 首節點不是 cron 零件 | scheduled() 只認首節點 cron — 確認 YAML flow 第一行是 `cron_node >> X` |
|
||||
|
||||
## 真實案例
|
||||
|
||||
`mira_feed_watcher.yaml` (polaris/mira/arcrun/) 是這 pattern 的生產使用:
|
||||
- cron `*/5 * * * *` 掃 leo 河道貼文
|
||||
- filter `tags_json eq "[]"` 抓未處理
|
||||
- trigger_workflow 觸發 `wiki_synthesis`
|
||||
- wiki_synthesis 內部末步 mark `wiki-processed` tag 確保 idempotency
|
||||
|
||||
完整 YAML 見 mira repo。
|
||||
@@ -0,0 +1,81 @@
|
||||
# Skill: Debug Paused Workflow
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
- 你 `arcrun_run_workflow(...)` 得到 error 含「workflow paused at node X waiting for task task_XXX」
|
||||
- 用戶說「workflow 跑了卻沒結果」/「等很久」
|
||||
- 看到 `error_code: paused_awaiting_resume`
|
||||
|
||||
## 重要觀念:paused **不是錯誤**
|
||||
|
||||
某些零件設計為 async:發起任務 → 立刻回 paused → 等外部 callback POST `/workflows/resume` → cypher-executor 接續執行。
|
||||
|
||||
典型 paused 零件:
|
||||
- `claude_api` — 打 mira daemon,daemon 跑 Claude(30-60 秒)→ 回 callback
|
||||
- `http_request_async`(未來會有)— 發 webhook 後等回應
|
||||
- 任何用 `pending: true, task_id: X` 模式的零件
|
||||
|
||||
paused 的 workflow **正在跑**,只是 cypher-executor 不浪費 CPU 等它,把 state 持久化到 KV 等 callback。
|
||||
|
||||
## Debug 流程
|
||||
|
||||
### Step 1:確認是不是真 paused(不是 fail)
|
||||
|
||||
```
|
||||
arcrun_list_paused_executions(api_key=ak_xxx, limit=20)
|
||||
```
|
||||
|
||||
看回傳的 paused 陣列:
|
||||
- 找你的 workflow 名稱
|
||||
- 看 `expires_at`(距離 24h TTL 還多久)
|
||||
- 拿 `task_id` 進下一步
|
||||
|
||||
### Step 2:看 paused state 細節
|
||||
|
||||
```
|
||||
arcrun_get_execution_trace(api_key=ak_xxx, task_id=task_XXX)
|
||||
```
|
||||
|
||||
回傳 `paused_pending_result` 含外部任務 id(如 mira daemon 的 task_id),`paused_node_id` 告訴你卡在哪。
|
||||
|
||||
### Step 3:判斷卡住原因
|
||||
|
||||
| 觀察 | 原因 | 解 |
|
||||
|---|---|---|
|
||||
| `expires_at` 已過 | 24h 沒 callback,state 已 GC | 重 trigger workflow |
|
||||
| paused_node 是 `claude_api` 且 mira daemon 503 | daemon 掛了 | `ssh cto && systemctl status cloud-cto` |
|
||||
| paused_node 是 `claude_api` 且 daemon 正常 | callback 還沒回 | 等 30-90 秒 |
|
||||
| `paused_pending_result` 沒 `task_id` | 零件實作 bug | 看零件源碼 |
|
||||
| `paused_pending_result.callback_url` 錯 | 部署 URL 設錯 | 看零件 env config |
|
||||
|
||||
### Step 4:手動 resume(救急用)
|
||||
|
||||
若已知 callback 結果(從外部 log / 直接打外部 API 拿到),可手動:
|
||||
|
||||
```bash
|
||||
curl -X POST https://cypher.arcrun.dev/workflows/resume \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"task_id": "task_XXX",
|
||||
"result": { ... 模擬 callback 應該回的東西 ... }
|
||||
}'
|
||||
```
|
||||
|
||||
cypher-executor 找出對應 paused state 接續執行。
|
||||
|
||||
### Step 5:避免再卡住
|
||||
|
||||
部署 watcher / async 流程時:
|
||||
- 設合理 timeout(claude_api 預設 30s,重 prompt 可拉到 60-90s)
|
||||
- 處理 daemon 健康檢查(monitor 加 alert)
|
||||
- 別在 high-load 時段同時 trigger 太多 paused workflow(KV write 量爆)
|
||||
|
||||
## paused 跟 fail 的差異速查
|
||||
|
||||
| 狀態 | success 欄 | error 含 | 該做 |
|
||||
|---|---|---|---|
|
||||
| **成功完成** | true | — | 看 data 結果 |
|
||||
| **paused** | false(但其實算成功) | "workflow paused at node X" | 等 callback / get_execution_trace |
|
||||
| **真錯** | false | 各種 error 訊息(非 paused) | 看 trace 第一個 failed node |
|
||||
|
||||
`trigger_workflow` 內建零件已把 paused 視為 status='paused_awaiting_resume' 而非 fail(commit 5216242)。
|
||||
@@ -0,0 +1,90 @@
|
||||
# Skill: Migrate http_request → trigger_workflow
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
你看到既有 workflow YAML 內有:
|
||||
|
||||
```yaml
|
||||
some_node:
|
||||
component: http_request
|
||||
url: "https://cypher.arcrun.dev/webhooks/named/another_workflow/trigger"
|
||||
# 或
|
||||
url: "https://arcrun-cypher-executor.uncle6-me.workers.dev/webhooks/named/X/trigger"
|
||||
```
|
||||
|
||||
這是 **錯誤 pattern** — CF Workers self-fetch 防護會擋掉,回 1042 / 522。
|
||||
**永遠改用 `trigger_workflow` 內建零件**。
|
||||
|
||||
## 為什麼會擋
|
||||
|
||||
Cloudflare Workers 有反同 zone 自循環防護:
|
||||
- 同 zone(`*.arcrun.dev`)Worker 互打容易死鎖
|
||||
- workers.dev 也擋(Worker → 自身 URL)
|
||||
|
||||
歷史背景:mira_feed_watcher 之前用 http_request 自打,怎麼設都失敗,最終加 `trigger_workflow` 內建零件繞掉(commit b8ecef0, 2026-05-16)。
|
||||
|
||||
## 怎麼遷移(3 行改動)
|
||||
|
||||
### Before
|
||||
```yaml
|
||||
trigger_synthesis:
|
||||
component: http_request
|
||||
url: "https://arcrun-cypher-executor.uncle6-me.workers.dev/webhooks/named/wiki_synthesis/trigger"
|
||||
method: POST
|
||||
headers:
|
||||
X-Arcrun-API-Key: "{{api_key}}"
|
||||
Content-Type: "application/json"
|
||||
body_json:
|
||||
api_key: "{{api_key}}"
|
||||
raw_block_id: "{{item.id}}"
|
||||
```
|
||||
|
||||
### After
|
||||
```yaml
|
||||
trigger_synthesis:
|
||||
component: trigger_workflow
|
||||
workflow_name: "wiki_synthesis"
|
||||
api_key: "{{api_key}}"
|
||||
input:
|
||||
api_key: "{{api_key}}"
|
||||
raw_block_id: "{{item.id}}"
|
||||
```
|
||||
|
||||
key 對應:
|
||||
- `url` → 拆 `workflow_name`
|
||||
- `headers.X-Arcrun-API-Key` → `api_key`
|
||||
- `body_json` → `input`
|
||||
- method / Content-Type → 不需要(in-process call)
|
||||
|
||||
## 行為差異
|
||||
|
||||
| 維度 | http_request 自打 | trigger_workflow |
|
||||
|---|---|---|
|
||||
| 走的路徑 | 外部 HTTP(被擋) | in-process call executeWebhookGraph |
|
||||
| latency | 一次 round-trip 50-200ms | < 1ms |
|
||||
| paused 狀態回報 | http 收 5xx 視為失敗 | status='paused_awaiting_resume' 算成功 |
|
||||
| auth 注入 | 手寫 header | 自動 |
|
||||
| 跨 zone | 會撞 self-fetch | 完全繞掉 |
|
||||
| 計量 | 算外部 fetch quota | 算同 Worker CPU |
|
||||
|
||||
## 例外:什麼時候真的需要 http_request
|
||||
|
||||
`trigger_workflow` 只能觸發**同一 arcrun 帳號**的 workflow(同 api_key namespace)。
|
||||
|
||||
跨帳號 / 跨環境 / 觸發其他平台需要 http_request:
|
||||
- 觸發另一個 arcrun 用戶的 webhook(少見場景)
|
||||
- 觸發外部 API(zapier / n8n / 自家別的 service)
|
||||
- 跨 Cloudflare account 的 worker
|
||||
|
||||
這些**不會** self-fetch 問題(因為目的地不是自己 Worker),http_request 仍適用。
|
||||
|
||||
## 部署前驗證
|
||||
|
||||
```
|
||||
arcrun_validate_yaml(yaml)
|
||||
arcrun_push_workflow(yaml)
|
||||
arcrun_run_workflow(your_watcher_name, {...})
|
||||
arcrun_list_recent_executions(workflow_name='your_watcher_name')
|
||||
```
|
||||
|
||||
確認 verdict='success' 且 duration_ms < 500ms(trigger_workflow 應該很快)。
|
||||
@@ -0,0 +1,115 @@
|
||||
# Skill: RAG with Arcrun
|
||||
|
||||
## 何時用這個 skill
|
||||
|
||||
用戶說:
|
||||
- 「我有一堆 X 資料,想問問題它幫我答」
|
||||
- 「Claude 不知道我的私人資料,怎麼讓它知道」
|
||||
- 「客服 bot 看我們的 docs 回答」
|
||||
- 「企業內部知識庫問答」
|
||||
|
||||
## 三步 RAG 架構
|
||||
|
||||
```
|
||||
用戶問 → 搜尋 → 把 context 餵 LLM → 回答
|
||||
```
|
||||
|
||||
arcrun 對應:
|
||||
```yaml
|
||||
flow:
|
||||
- "input >> ON_SUCCESS >> search" # KBDB semantic search
|
||||
- "search >> ON_SUCCESS >> answer" # claude_api 帶 context
|
||||
```
|
||||
|
||||
完整範本見 `arcrun_search_examples('rag')` → `rag-search-answer`。
|
||||
|
||||
## 5 個關鍵決定
|
||||
|
||||
### 1. 資料怎麼進 KBDB?
|
||||
|
||||
源頭決定品質:
|
||||
- **PDF / 文件** → 用 `pdf-to-blocks` workflow(自動 chunk + embedding)
|
||||
- **Logseq / Notion / 手記** → 寫腳本 ingest 或讓 mira 平台處理
|
||||
- **Web crawl** → http_request → `kbdb_ingest`
|
||||
- **每天 RSS** → cron + kbdb_ingest
|
||||
|
||||
關鍵:
|
||||
- 用 `source` 欄位區分來源(之後 query 可篩 source)
|
||||
- 用 `user_id` 區分 namespace(多租戶或多 domain)
|
||||
- chunk 大小:500-1000 字最佳(太小無 context,太大稀釋 relevance)
|
||||
|
||||
### 2. search 怎麼設?
|
||||
|
||||
```yaml
|
||||
search:
|
||||
component: kbdb_search
|
||||
api_key: "{{api_key}}"
|
||||
query: "{{input.question}}"
|
||||
topK: 5 # 3-10 都合理
|
||||
user_id: "{{input.user_id}}" # 限定 namespace(多租戶必要)
|
||||
```
|
||||
|
||||
進階參數:
|
||||
- `source` — 限定來源(只查 "pdf:*" 或 "wiki:*")
|
||||
- `tag` — 限定 tag("verified" / "policy" / 等)
|
||||
- semantic search 走 embedding,query 用自然語言即可,不用打對 keyword
|
||||
|
||||
### 3. prompt 怎麼餵 context?
|
||||
|
||||
關鍵:**明確標 context 邊界 + 給 LLM 拒絕回答的權力**
|
||||
|
||||
```
|
||||
你是知識庫助手。**只用 context 內的資訊**回答問題。
|
||||
|
||||
規則:
|
||||
1. context 沒講的,老實說「資料庫裡查不到」
|
||||
2. 引用時標 [block_id],方便用戶追原始
|
||||
3. 不要外推、不要編造
|
||||
|
||||
Context:
|
||||
{{search.results}}
|
||||
|
||||
問題:{{input.question}}
|
||||
|
||||
回答:
|
||||
```
|
||||
|
||||
不給 LLM「拒絕的權力」,它會亂猜。
|
||||
|
||||
### 4. 引用怎麼顯示?
|
||||
|
||||
進階:用 `_recipe_output_format: json` 讓 claude 回結構化:
|
||||
```json
|
||||
{
|
||||
"answer": "...",
|
||||
"citations": [{"block_id": "abc-123", "snippet": "..."}],
|
||||
"confidence": "high"
|
||||
}
|
||||
```
|
||||
|
||||
前端可 render 成可點擊的 citation 連結。
|
||||
|
||||
### 5. 怎麼測準度?
|
||||
|
||||
`arcrun_search_examples('rag-eval')` 暫無範例。手動:
|
||||
1. 準備 N 個「黃金 QA pair」(問題 + 應有的答案)
|
||||
2. 跑 workflow N 次,比對結果
|
||||
3. 若準度 < 70%:先檢查 KBDB chunk 品質、再 tune topK、最後 tune prompt
|
||||
|
||||
## 常見陷阱
|
||||
|
||||
| 症狀 | 原因 | 解 |
|
||||
|---|---|---|
|
||||
| 答案不準 | chunk 太大 / 太小 | re-ingest 改 chunk size |
|
||||
| 答案編造 | prompt 沒給拒絕權 | prompt 加「不知道就說不知道」 |
|
||||
| 找不到該找到的 | semantic 不命中 | 換 query rewriting / 增 topK |
|
||||
| 答案太長 | prompt 沒限制 | prompt 加「答案 < 100 字」 |
|
||||
| 慢 | claude_api timeout | 拉 timeout_ms 或減 context |
|
||||
|
||||
## 進階變體
|
||||
|
||||
- **多輪 query rewriting**:claude 先改寫 question → search → 答
|
||||
- **mix sources**:KBDB + web search + DB query → merge
|
||||
- **cache**:相同 question 的答案存 KBDB,下次 lookup hit 直接回(省 LLM call)
|
||||
- **conversational**:傳 chat history 進 prompt,支援 follow-up
|
||||
- **filter-then-rerank**:semantic search 撈 20 → claude rerank 取前 5 → 餵 final answer
|
||||
Reference in New Issue
Block a user