Sandbox
@sami-mag07/scraping-agent-skeleton

Research agent skeleton with skills and fallback tools

This repo gives you a local research agent skeleton that searches the web, reads pages as text, and checks facts against a second source before answering. The behavior lives in the system prompt, the skill files in `skills/`, and the tool loop that keeps calling search and read tools until the answer stops improving.

36 stars1 forksPythonUpdated 26d ago
Who it's for

Builders who want an agent that can do sourced research and web scraping from their own machine.

What it delivers

You can run a research workflow that keeps digging, cross-checks facts, and uses reusable skills on demand.

What it does

Search fan

Runs multiple search queries in parallel, including verbatim, synonym, and English variants, to find better sources.

Full-page reading

Reads pages as clean text instead of relying on snippets, with fallback readers when one service fails.

Cross-checking

Checks numbers, dates, prices, and names against a second source and marks single-source claims as unverified.

Loadable skills

Loads Markdown skills on demand from `skills/`, so the agent only pulls the method it needs.

Fallback integrations

Uses Tavily, Exa, Jina Reader, raw fetch, and yt-dlp fallbacks so one broken service does not stop the run.

Notes integration

Can search, read, and save Markdown notes when `NOTES_DIR` is set.

How to get it

  1. 1Run
    cp .env.example .env          # then fill in .env (at least OPENAI_API_KEY)
    python3 -m venv .venv
    .venv/bin/pip install -r requirements.txt
    
    .venv/bin/python main.py "Was kostet ein Lastenrad-Abo in Berlin?"
  2. 2More invocations
    .venv/bin/python main.py --skills            # which methods exist
    .venv/bin/python main.py --prompt            # see the assembled system prompt
    .venv/bin/python main.py --mock "Testfrage"  # run through without an API call
    echo "Auftrag" | .venv/bin/python main.py    # from a pipe
    .venv/bin/python -m pytest                   # tests, without network and without a key

README

Research and Scraping Agent (Skeleton)

An agent that takes a question, searches the web, actually reads pages, pulls structured data out of them, and returns a sourced answer. It does not stop at the first hit, and it checks every number against a second source before stating it.

This is the skeleton: the complete logic and all methods, without anyone's data. You put your own credentials into a .env and it runs on your machine.

New here? The step-by-step setup is in SETUP.md, written as a recipe you can also hand directly to a coding agent (Claude Code, Cursor, Codex).


How it works

The special part is not in the code but in the working method, which is laid down in the system prompt and in the skills:

Search fan instead of a single search. For every question, at least three search queries go out at once: the term verbatim, a synonym or technical term, the English variant. Then it compares which phrasing brings fresh and official sources. Only that track gets refined.

Reading instead of collecting snippets. At least six hits from at least four domains are read in full text. A search snippet is not evidence.

Cross-checking is mandatory. Every number, every date, every price, and every name is run against a second, independent source. Whatever rests on a single source is marked [unbestätigt]. If two sources contradict each other, both values appear in the answer instead of the agent picking one.

Stopping criterion. It is only done when another round yields nothing but known material, not as soon as a plausible answer is on the page.


Architecture

A job always takes the same path:

main.py            CLI, or your own caller (bot, web backend, another agent)
      |
  agent.py         system prompt (the working method) + tool definitions
      |
 core/llm.py       tool loop: query the model, run tools, repeat
      |            reading tools run concurrently, writing tools sequentially
      |
      +-- core/skills.py         loads skills/*.md on demand
      +-- integrations/reach.py  search, read pages, YouTube transcripts
      +-- integrations/notes.py  read and write a Markdown folder
File / folderWhat lives there
main.pyEntry point from the command line
agent.pySystem prompt, tool schemas, handlers. The place where behavior changes
core/llm.pyTool loop, parallel execution of reading tools, mock mode, token counting
core/skills.pySkill loader: parse frontmatter, build the index, provide read_skill
core/config.pyRead the .env. No values in the code
integrations/reach.pyTavily, Exa, Jina Reader, yt-dlp. Every function with a fallback
integrations/notes.pyNotes folder: search, read, create new (never overwrite)
integrations/web_search.pyEmergency exit in case the tool loop fails completely
skills/*.mdThe detailed methods. New method = new file, no code
beispiele/Template for the target-audience note
tests/Runs without network and without an API key

Tools

ToolWhat it doesFalls back to
web_searchWeb search with a short summary, optionally narrowed to specific domainsTavily (filters domains server-side), then Exa (free, no key, gets site: added to the query)
read_pagePage as clean textJina Reader, then Tavily Extract, then raw fetch with HTML stripping
youtube_transcriptSubtitles of a video, including auto-subsyt-dlp locally. If it is missing, the agent says so instead of crashing
search_notesSearch your own notesonly active with NOTES_DIR
read_noteRead a noteonly active with NOTES_DIR
save_researchStore a result as a Markdown noteonly active with NOTES_DIR
read_skillLoad a method on demandalways there

No function depends on a single service. Without a Tavily key, everything runs over the free routes, just a bit shakier.


Skills

Skills are the reason the prompt stays small. The system prompt contains only the index (name plus one sentence). The agent loads the full text itself via read_skill when the task calls for it. Following the principle of the Anthropic Agent Skills.

SkillWhat for
web-scrapingData instead of prose: open datasets (CKAN, Socrata), JSON in the HTML (__NEXT_DATA__ and relatives, JSON-LD), endpoints (/wp-json/, /products.json), probing pagination, limits of the tool
seiten-indexAll URLs of a domain at once: robots.txt, sitemaps, news sitemaps, Wayback index
archiv-rechercheFetch dead, blocked, or changed pages via the Internet Archive and compare versions
such-operatorenNarrow down when the search fan brings only noise: domain filters, filetype:, exact phrase
deep-researchBreak the question apart, keep a source register, look for the counter-thesis, provide evidence
quellenarbeitComplete source citations for school and university, quotation versus paraphrase
lead-rechercheSearch for leads and filter them against your own target-audience note

Why four skills for scraping and not one or eight: They are cut by trigger, not by topic. web-scraping applies when you are pulling data from a known page. seiten-index when you still have to find the pages first. archiv-recherche when the page is no longer there. such-operatoren when the search yields nothing usable. Four different situations, no overlap, and the agent loads only the one that fits.

A pure signpost skill that only points to others would, by contrast, be a wasted tool round: the switch sits for free in the system prompt, which the model sees anyway. That is why web-scraping keeps its details itself.

Three tests hold this together: every cross-reference must point to an existing skill, no skill may recommend itself, and none may grow beyond MAX_SKILL_CHARS (otherwise read_skill silently cuts off exactly the sections at the end where the limits and the etiquette live).

Adding a new skill: drop a .md into skills/, with frontmatter:

---
name: mein-skill
description: Ein Satz, der im Index steht und entscheidet, ob der Agent lädt
agents: research
---
# Mein Skill

Die Anleitung.

No code, no restart ritual. On the next run, the skill is in the index.


Quick start

cp .env.example .env          # then fill in .env (at least OPENAI_API_KEY)
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

.venv/bin/python main.py "Was kostet ein Lastenrad-Abo in Berlin?"

More invocations:

.venv/bin/python main.py --skills            # which methods exist
.venv/bin/python main.py --prompt            # see the assembled system prompt
.venv/bin/python main.py --mock "Testfrage"  # run through without an API call
echo "Auftrag" | .venv/bin/python main.py    # from a pipe
.venv/bin/python -m pytest                   # tests, without network and without a key

Embedding instead of invoking: The agent is not tied to any channel. If you put it into a bot or a web backend, call agent.run(config, auftrag) and get the finished text back. The call blocks, so in an async program it belongs in asyncio.to_thread().


Costs

The only strictly paid service is the LLM API. A Tavily key makes the search more reliable and costs credits, but it is optional: without a key the search runs over the free Exa endpoint. Jina Reader and yt-dlp are free.

A thorough research run consumes more tokens than a normal chat, because six or more pages go through the model in full text. MAX_ROUNDS caps that from above. After every run, the usage is printed to stderr.

Security

  • Secrets live exclusively in the .env, never in the code. The .env is in .gitignore. If a key ever does land in the repo, the only fix is: generate a new key.
  • Note paths are checked against the root folder so that an invented ../../.ssh/id_rsa does not get through.
  • create_note only creates new files and never overwrites an existing one.
  • Errors in tools go back to the model as text instead of as a crash, so that the agent can react to a broken fetch.
  • The scraping skill says explicitly: respect robots.txt, no circumventing bot protection, no fetch barrages against the same domain, no collecting personal data that the task does not require.

Origin

This is the extracted research half of a larger personal assistant, depersonalized and made runnable on its own. That is why the prompts and skills are in German: they grew that way, and translating them would water down the phrasings on which the quality depends. The code is named in English and usable without knowing German.

Some skills build on prior work by others, each named in the quelle: line in the frontmatter:

The skill principle itself (index in the prompt, full text loaded on demand) comes from the Anthropic Agent Skills.

License

MIT, see LICENSE.

Files in the repo

Repository payload13 top-level entries
  • beispiele
  • core
  • integrations
  • skills
  • tests
  • .env.example
  • .gitignore
  • agent.py
  • LICENSE
  • main.py
  • README.md
  • requirements.txt
  • SETUP.md

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More harnesses

affaan-m/
ECC
affaan-m/ECCHarnesses

The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

258k

The job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.

42k
ruvnet/rufloHarnesses

🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated

72k

Practical patterns, starters & CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.

11k