Researcher agent: what it is and how to create one in OpenCode
| What a researcher agent is | |
| Where it fits | |
| Create the agent file | |
| Permissions for research | |
| System prompt that works | |
| How to launch it | |
| Report format | |
| Evals | |
| Limits and failure modes |
What a researcher agent is
A researcher agent is a read-only subagent
whose only job is to gather facts and return them as a sourced report. It
reads code and documents, searches the web, compares sources and stops. It
does not edit files, does not run builds, does not decide anything. The
parent session asks one question, keeps working while the child runs, and
gets back a compact answer where every claim carries a source: a
file:line reference or a URL.
It is an agent, not a chatbot, because it has the
loop: it plans a search, calls a tool,
reads the result and repeats until the question is answered or the budget
is spent. It is not a builder because the write permissions are denied —
research output is text, and the only side effect is the report itself.
Where it fits
Reach for a researcher in four cases:
- Unknown area of a big repo. Before planning a feature, map where retries, auth or config actually live. The built-in explore subagent is a good default; a custom researcher pins your own prompt, model and output format on top.
- Outside sources. Library docs, API references, release notes — websearch plus webfetch with a demand for links in every claim.
- Fan-out. Several researcher children in one parent: one per library, competitor or log source. Results return to the parent session.
- Pre-flight for an analyst. The researcher maps what exists and where; the analyst then measures it.
When answers must come from one fixed corpus instead of roaming, use RAG — a researcher roams, RAG answers from one index. Model split follows the usual trio: quick lookups on the free OpenCode model, daily research on Luna, hard multi-hop questions on Opus.
Create the agent file
Project agents live in .opencode/agents/; the nested path becomes the id. Minimal place in the demo project from Real project skeleton:
my-app/ ├── opencode.jsonc ├── .opencode/agents/ │ ├── researcher.md │ └── team/orchestrator.md ├── .opencode/commands/ │ └── research.md └── .opencode/skills/
The complete file, frontmatter plus a system prompt body:
--- description: Gathers facts from code and docs, returns a sourced report mode: subagent model: opencode/gpt-6-luna steps: 10 permissions: - action: edit resource: "*" effect: deny - action: shell resource: "*" effect: deny - action: webfetch resource: "*" effect: allow - action: websearch resource: "*" effect: allow --- Research the question with tools, never from memory. Prefer repo sources first, then official docs, then the open web. End every claim with a source: file:line or URL. If two sources disagree, show both and say which is newer. Answer in three sections: Findings, Gaps, Next steps. Stop when the budget is spent or the question is answered.
Field notes:
- description: one line the parent model sees when picking subagents. Name the output, not the tooling — it decides when the child is worth launching.
- mode: subagent — runs only as a child; the main session cannot get stuck in it.
- steps: 10 — research drifts, so cap it. On the last step tools are removed and a text summary is forced.
- model: research is read-heavy and cheap to verify, so Luna is the daily default; escalate to Opus only for multi-hop questions across many files.
Permissions for research
The core read tools — read, glob, grep — stay available because nothing denies them; everything that changes the world is denied explicitly. Rules are ordered and the last match wins:
- deny edit — the report is the only artifact.
- deny shell — no builds, no installs, no network through curl. A researcher that needs a command is usually a signal the question belongs to an analyst.
- allow webfetch + websearch — the outside half of the job.
- allow exactly one skill if a checklist exists, for example api-review — the same gated pattern as the reviewer agent.
- allow MCP doc tools — if an MCP server exposes a docs search, name its server_tool explicitly.
System prompt that works
Four rules survive contact with real tasks: sources on every claim, repo before web, disagreement shown instead of resolved silently, and a fixed three-section answer. The three sections matter more than they look — Findings forces compression, Gaps gives the parent a way to trust the silence, Next steps turns the report into the next prompt instead of a dead end. One question per run: a researcher asked for three things returns three half-reports.
How to launch it
Ask the parent in plain language:
Use the researcher subagent to map how retries are configured in this repo and in the official docs
The parent stays usable while the child runs and the report lands in the parent session. For a repeatable entry point, pin it in a command file, .opencode/commands/research.md:
--- description: Research one question with the read-only researcher agent: researcher model: opencode/gpt-6-luna subagent: true --- Research: $ARGUMENTS Return Findings, Gaps, Next steps with a source on every claim.
Run it as /research how webhooks are signed in the billing service. Command model beats agent model beats session model, so an explicit pin always wins.
Report format
Fix the skeleton and reports become comparable across runs and models:
Findings - Retries live in src/http/client.py:88 (backoff, 5 attempts). - The docs pin the same default: https://example.com/api#retries Gaps - Jitter policy is undocumented; client.py:102 shows a constant. Next steps - Ask the analyst to measure the real retry rate from prod logs.
External URLs stay real links with rel="nofollow" when the report is pasted into an article or an issue. A report with no Gaps section is a red flag — the researcher either stopped early or is inventing confidence.
Evals
Five fixed questions with checkable outcomes, run on every prompt or model change:
- x2 repo questions: find where feature X is configured — pass when the cited file exists and contains the term.
- x2 web questions: current version and API change of a library — pass when the URL loads and the version matches the release page.
- x1 discipline: a question with no answer in the repo — pass when Gaps says so and git status is clean.
The last case is the important one: an agent that admits an empty result is worth more than one that fills silence with a plausible citation.
Limits and failure modes
- Invented citations. Demand file:line, then verify with grep before acting on the report. A wrong line number is cheap to catch; a wrong paragraph is not.
- Scope creep. One question per run, steps capped, no edits — the three guards do ninety percent of the work.
- Stale docs. Repo sources first: the code is the truth, the web is the commentary.
- Dump reports. Ask for five bullets maximum per section; compression is the product, not the raw collection.
- Fixed corpus. When the answer must come from one dataset or document set, stop roaming and use RAG — or hand the numbers to the analyst.
Article author: Arthur Isaev