diff --git a/SKILL.md b/SKILL.md index 24b36c2..401e456 100644 --- a/SKILL.md +++ b/SKILL.md @@ -86,9 +86,29 @@ VISION_API_BASE=http://localhost:11434/v1 VISION_MODEL=llava:13b ``` -## Rules — MUST follow +## Execution rules -1. **Execute only, do not reason about internals.** Run the CLI and return the output. -2. **No fallback strategies.** Report errors as-is; do NOT try alternative approaches. -3. **No retry loops.** If detection or search fails, report the failure. -4. **Trust the tool's output.** The CLI handles session management and error formatting internally. +### For `detect` and `detect-and-search` (slow — use sub-agent) + +Spawn a sub-agent via `sessions_spawn`. Do **not** run the command directly. + +``` +sessions_spawn( + task: "Run this command and return the raw JSON output:\n\nbun dist/run.js \n\nCopy the entire JSON output as your reply.", + label: "video-product-snapshot", + runTimeoutSeconds: 300, +) +``` + +- Announce immediately that processing has started and share the `runId`. +- Wait for the sub-agent announcement, then parse and format the result for the user. + +### For `search` and `session` (fast — run directly) + +Run the CLI command inline, no sub-agent needed. + +### General rules + +1. **No fallback strategies.** Report errors as-is; do NOT try alternative approaches. +2. **No retry loops.** If detection or search fails, report the failure. +3. **Trust the tool's output.** The CLI handles session management and error formatting internally. diff --git a/src/index.ts b/src/index.ts index b5608a2..fd89663 100644 --- a/src/index.ts +++ b/src/index.ts @@ -198,10 +198,12 @@ async function generateChineseKeyword(description: string, visionConfig: VisionC model, prompt: `You are generating a 1688.com (Chinese B2B wholesale) product search keyword. Rules: -- Output ONLY 2-4 Chinese words — the product category + 1-2 key material/feature words +- Output ONLY 2-4 Chinese words — the product OBJECT TYPE + 1-2 key material/feature words +- CRITICAL: If the product is a container, organizer, rack, shelf, bag, box, or holder, the keyword MUST name THAT object — NOT the items it holds. + Examples: shoe rack → "金属鞋架", cable organizer → "理线器", storage shelf → "收纳架", toolbox → "工具箱" - Use common Chinese commerce terms, NOT a literal translation - No English, no punctuation, no explanation -- Short broad terms work better than long specific phrases (e.g. "金属鞋架" not "黑色Z型金属网格鞋架") +- Short broad terms work better than long specific phrases Product description: ${description} diff --git a/src/product-detector.ts b/src/product-detector.ts index 6bb673f..aa08051 100644 --- a/src/product-detector.ts +++ b/src/product-detector.ts @@ -135,23 +135,35 @@ export async function cropProduct( return outputPath; } +async function withConcurrency( + tasks: (() => Promise)[], + limit: number, +): Promise { + const results: T[] = new Array(tasks.length); + let next = 0; + async function worker() { + while (next < tasks.length) { + const i = next++; + results[i] = await tasks[i](); + } + } + await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker)); + return results; +} + export async function detectProductFrames( frames: ExtractedFrame[], minConfidence: number, - concurrency: number = 5, + concurrency: number = 10, visionConfig: VisionConfig, ): Promise { const model = createVisionModel(visionConfig); - // Pass 1: parallel filter — discard junk frames - const keepFlags: boolean[] = []; - for (let i = 0; i < frames.length; i += concurrency) { - const chunk = frames.slice(i, i + concurrency); - const flags = await Promise.all( - chunk.map((f) => filterFrame(f, model).catch(() => false)) - ); - keepFlags.push(...flags); - } + // Pass 1: all frames in parallel, bounded by concurrency + const keepFlags = await withConcurrency( + frames.map((f) => () => filterFrame(f, model).catch(() => false)), + concurrency, + ); const candidates = frames.filter((_, i) => keepFlags[i]); if (candidates.length === 0) return [];