Skip to content

docs: address four skill-doc issues from #50#51

Open
ttokttokttok wants to merge 13 commits into
apify:mainfrom
ttokttokttok:docs/skill-clarifications
Open

docs: address four skill-doc issues from #50#51
ttokttokttok wants to merge 13 commits into
apify:mainfrom
ttokttokttok:docs/skill-clarifications

Conversation

@ttokttokttok

@ttokttokttok ttokttokttok commented May 28, 2026

Copy link
Copy Markdown

Fixes #50 — see the issue for full context (the eval prompts that surfaced each issue, what we observed, why it happens, and the proposed fix).

Summary

Doc clarifications across three skills so an agent reliably picks the right Actor, reads input schemas the correct way, and doesn't abandon the CLI on an opaque failure — plus a companion evals framework. Root-caused from agent eval runs where the agent chose a suboptimal Actor, skipped actor-index.md, burned ~25 tool calls reverse-engineering a schema, and (in a later run) bailed from the CLI to apify-client → hand-rolled cheerio after a silently-suppressed error.

Net skill diff vs main: 32 insertions, 8 deletions across three SKILL.md files (mostly additive). The evals/ folder is the remainder.

apify-ultimate-scraper

  • Critical: do not trust internal knowledge — treat remembered Actor IDs, input/output fields, and client methods as stale; verify before writing integration code.
  • Select by source, not the verb — anchor Actor choice on the source platform, not the action word in the request.
  • Read actor-index.md first — it's grouped by platform and flags the recommended tier, so a source's native toolkit shows together. Defines when a task is multi-step (one Actor's output must feed another) and that the workflow guide carries the handoff.
  • Schema-fetch fixapify actors info … --input without --json prints the schema directly; with --json it returns the whole Actor object and buries the schema under taggedBuilds.latest.build.actorDefinition.input. Documented as the explicit exception to the global "always --json" rule.
  • Don't blind-suppress errors — keep 2>/dev/null for clean JSON, but on empty/unexpected output re-run without it and read the error before switching approach. Suppressing the CLI's (specific, fix-naming) errors turns a recoverable failure into a CLI→SDK→hand-rolled spiral.
  • --input-file for file input-i is inline JSON only; pass a file with --input-file=PATH (or -f). -i @PATH is a curl-ism the CLI rejects.
  • CLI --json is unwrapped (results under items, no data envelope) + a literal example block; generic-crawler fallback rule.

apify-sdk-integration

  • Get schemas via the CLI (apify actors info … --input); do not read .inputSchema off client.actor(id).get() — the Actor object doesn't include it.
  • Boundary note: not for one-off scrapes — use the CLI via apify-ultimate-scraper; reach for apify-client only when building an app.
  • Actor-ID format note + corrected pagination default.

apify-actor-development

  • Clarify apify create -t takes the manifest's name, not its id.

Companion: evals/ folder

A small two-layer eval framework (routing.json + per-skill behavioral files) that surfaced these issues. Kept in its own commits so it can be dropped or factored out if you'd rather not adopt evals in this repo.

How to review

# Just the skill edits
git diff main -- 'skills/**/SKILL.md'

# Full diff (skill edits + evals folder)
git diff main

🤖 Generated with Claude Code

ttokttokttok and others added 13 commits May 27, 2026 23:08
…at note

- Pagination: API and apify-client both return all items by default, not
  250K. Verified against apify-client/dist/resource_clients/dataset.js
  ("Default is all items") and docs.apify.com/api/v2/dataset-items-get
  ("By default there is no limit").
- Actor ID format: Finding the Right Actor section never said what an ID
  looks like. Agents fabricate plausible-but-wrong IDs from memory
  (e.g. "apify/google-maps-scraper" when the real top result is
  "compass/crawler-google-places"). Adds one-line guidance with examples.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…xception

- Generic fallback in Step 1: when both actor-index and dynamic search return
  nothing usable, fall back to a generic crawler picked by site rendering
  (cheerio for static HTML, playwright for SPA, camoufox for anti-bot).
  Previously left undefined, agents would stall or guess.
- Quick-answer schema exception in Step 2: Step 2 says always fetch the input
  schema, Step 3's quick-answer mode says skip preferences for trivial lookups.
  For obvious inputs (e.g. a username) the schema fetch is unnecessary —
  exception preserves the PPE cost-gate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…, not `id`

The manifest has both `id` and `name` slug-like fields per entry. `-t` accepts
only `name`. Anyone scaling beyond the three documented defaults naturally
picks the more-prominent `id` field and hits "Could not find the selected
template" — e.g. `-t ts-crawlee-cheerio` errors; `-t project_cheerio_crawler_ts`
works. One-line clarification + manifest link.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…fier

Per review feedback from @vivek100: the "Exception for quick-answer lookups"
callout was too prescriptive — replace it with a single follow-on sentence
right after the schema-fetch command. Same net effect (agent may skip the
fetch for trivial inputs, still checks pricing for PPE) but reads as a
natural qualifier instead of a separate carve-out.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rative line

Replace the follow-on "For trivial inputs..." sentence (added in 516ae13)
with a conditional appended directly to the original Step 2 imperative:
"Fetch the input schema dynamically, unless you already know the input fields:"

Gives the agent a checkable criterion (do I know the fields for this Actor?)
rather than a vague hedge ("usually") or a separate exception callout. The
PPE pricing reminder is dropped here — gotchas.md already mandates the full
cost-estimation protocol, so the inline note was redundant.

Net diff against upstream main for this file is now a single line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per review feedback from @vivek100: the previous wording ("paginate to bound
memory and latency") was generic dev advice that didn't tell the agent what
to actually do. Replace with:

  Don't pull large datasets in one read — pass `limit`/`offset`, or write to
  a file and process in chunks. (`listItems()` returns all items by default.)

Lead with the failure mode (don't pull in one read), give two concrete
techniques (limit/offset OR file), keep the factual correction (returns
all by default) as a parenthetical.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two-layer eval set, one file per skill:

  evals/
  ├── README.md
  ├── routing.json                    cross-skill routing (10 evals)
  ├── apify-actor-development.json    behavioral, type=build|cli (8)
  ├── apify-sdk-integration.json      behavioral, type=discovery|integrate (9)
  └── apify-ultimate-scraper.json     behavioral, type=selection|run|cost|
                                        schema-and-gotchas|workflow:<name> (89)

Schema per behavioral entry: {prompt, expected, type}. Workflow evals use
`type: "workflow:<workflow-name>"` so a runner can filter all workflows at
once (startsWith) while preserving which workflow a specific entry tests.

Routing schema: {prompt, expected_skill, acceptable_skills, rationale}.
A pick outside expected ∪ acceptable is a fail and points at the description
to fix — eval-first refactors then become small description PRs rather than
rewrites.

116 entries total. README documents the layout, schemas, success criteria
(outcome / process / style / efficiency), and how to run each layer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The behavioral evals run single-shot — one prompt, no prior turns, no
conversational context. Prompts like "This Actor takes ~20 minutes…" or
"Okay, the run finished — wrap it up for me" reference state ("this Actor",
"the run") that the agent never received in a one-shot eval, so they test
nothing meaningful.

Removed 10 from apify-ultimate-scraper.json and 2 from apify-sdk-integration.json:

- ultimate-scraper: "From the search results, what's the Actor ID I should use?",
  "I picked apify/instagram-scraper. Let's go.", "Use this scraper to pull 500
  Instagram posts.", "I'm getting empty results from this social-media Actor
  and I know the account exists.", "How do I limit this to just 100 items?
  I tried `maxResults: 100` but it ignored me.", "Run a large scrape that
  will take a while and let me check on it later.", "Give me the results as a
  CSV file.", "Okay, the run finished — wrap it up for me.", "Scrape 2,000
  results from this pay-per-event Actor.", "Run this PPE Actor for 50,000
  items."
- sdk-integration: "This Actor takes ~20 minutes to finish. How should I run
  it from my app without blocking?", "Where do I get the scraped data after
  the run finishes?"

README updated: counts (116 -> 104) and added a note that all remaining
prompts must stand on their own as single-shot evals.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ed prompts

The earlier deletion removed prompts that only made sense mid-conversation,
which gutted the schema-and-gotchas type (4 -> 0) and thinned the run type
(5 -> 2). The behaviors those tested (schema-fetch-before-run, gotchas check,
cookie/auth diagnosis, field-name variance, async run, retrieve+save,
deliver) are still in the skill and worth testing — they just need
self-contained prompts.

Added:
  ultimate-scraper.json (+9):
    +4 schema-and-gotchas: schema-before-run, gotchas check, cookie/auth
       diagnosis, field-name variance — each names a specific Actor
    +3 run: async pattern, CSV save with explicit dataset id, deliver with
       explicit run id
    +2 cost: PPE cost gate (junglee/Amazon-crawler), high-cost confirmation
       (harvestapi/linkedin-profile-scraper)
  sdk-integration.json (+2):
    +2 integrate: async pattern (apify/website-content-crawler from Node),
       result retrieval (after .call() with run object in hand)

Final counts:
  routing.json                       10
  apify-actor-development.json        8
  apify-sdk-integration.json          9
  apify-ultimate-scraper.json        88
  TOTAL                             115

Ultimate-scraper type breakdown: selection=5, schema-and-gotchas=4, run=5,
cost=5, workflow:*=69. All new prompts stand on their own as single-shot
evals — they name specific Actor IDs and provide any prior state (run id,
dataset id, pricing model) inline.

README counts updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…and effectively also revert the pagination half of the original
docs(sdk-integration) commit (`899e198`), restoring the upstream wording.

Dropping the pagination change from this PR. On reflection, the 250K-cap
factual correction is low-impact (most datasets are well under that, and
agent context blows at far smaller sizes regardless), and the agent-centric
"write to a file" advice we layered on per review felt off-brand for a doc
whose audience is also human app developers.

The Actor-ID format note added in `899e198` stays — that one's
unambiguously useful and eval-backed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lope)

The CLI prints results at the top level (`items`, `.id`, `.status`); only
the api.apify.com/v2 REST API uses the `{ data: { ... } }` wrapper. Agents
were defaulting to `obj.data.items` (the REST shape) when parsing CLI search
output, yielding silently-empty results. Add a global rule plus a literal
example output block so the correct shape is unmissable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root-caused from an eval run where the agent picked the wrong Actor, never
opened actor-index.md, and burned ~25 calls reverse-engineering an input
schema (then wrote a bad apify-client call from memory).

apify-ultimate-scraper/SKILL.md
- Add "Critical: do not trust internal knowledge" — verify IDs/fields/methods,
  never write integration code from memory.
- Select Actors by source platform, not the verb in the request.
- Always read actor-index.md FIRST (it's grouped by platform); define when a
  task is multi-step and that the workflow guide exists for the handoff.
- Fix Step 2: `apify actors info … --input` WITHOUT `--json` prints the schema;
  `--json` returns the whole Actor object and buries it under
  taggedBuilds.latest.build.actorDefinition.input. Note the Rule apify#1 exception.

apify-sdk-integration/SKILL.md
- Get schemas via the CLI (`apify actors info … --input`); do NOT read
  `.inputSchema` off `client.actor(id).get()` — the Actor object lacks it.
- Boundary: not for one-off scrapes — use the CLI via apify-ultimate-scraper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-file

Two fixes root-caused from an eval run where the agent abandoned the CLI for
apify-client (then hand-rolled cheerio) after a silent failure.

- Rule apify#3: keep `2>/dev/null` for clean JSON, but on empty/unexpected output
  re-run WITHOUT it and read the error before switching approach. The CLI's
  errors are specific and name the fix; suppressing them turns a recoverable
  failure into a tool-switch spiral.
- Step 3: `-i` is inline JSON only; pass a file with `--input-file=PATH` (or
  `-f`). `-i @PATH` is a curl-ism the CLI rejects ("Providing a JSON file path
  in the --input flag is not supported").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Four skill-doc issues surfaced by eval-driven testing

3 participants