Many teams start with a single prompt that tells an agent to “research the market, keep an eye on competitors and pull the numbers into a table”. It works in a demo. In production it drifts, because those are three jobs with different shapes. Research is a bounded question that should end. Monitoring is a recurring question that never ends and should stay quiet most of the time. Extraction is a schema applied to material someone already has. You can run all three in one agent, but each needs its own plan, its own output contract and its own definition of done, and all three should share a single store of evidence. This guide shows how to structure that, and where the agent should stop and ask before anything leaves your system.
Three jobs, three shapes
| Aspect | Research | Monitoring | Extraction |
|---|---|---|---|
| Input | A question and a list of permitted sources | A recurring question, sources, cadence and a materiality rule | A schema and the documents to read |
| Plan | Ordered steps, approved before running | A versioned definition plus a baseline | A field list with types, where unknown is allowed |
| Output | Findings with quotations, plus open questions | Change events awaiting review | Records with a value, a passage and a status per field |
| Done when | Acceptance criteria are met or the budget runs out | Each run ends; the monitor itself carries on | Every field is filled or marked unknown |
| Typical failure | Silent scope creep | Alert fatigue, or acting on a change | Plausible guesses in empty fields |
The bottom row is the reason to keep them apart. Each failure has a different cure, and a single blended loop tends to suffer from all three at once. Before you pick frameworks, it is worth reading about agent architecture for web access, because the tool layer underneath is shared by every job.
Separate plans, stored as data
Each job should start from a plan object that a person can read before anything runs. Keep plans as data rather than prose buried in a system prompt, so they can be versioned, compared and approved.
research_plan:
question: Which vendors on our shortlist publish a data retention policy?
steps: [list vendor sites, locate policy pages, quote retention terms, note gaps]
allowed_sources: [vendor domains on the shortlist]
budget: {max_tool_calls: 40, max_minutes: 20}
acceptance: every vendor has a quoted policy passage or an explicit not-found entry
monitor_definition:
version: 3
question: Has any shortlisted vendor changed its retention terms?
sources: [policy URLs found by the research plan]
material: a change to retention period, deletion process or data location
cadence: weekly, Monday 09:00, Europe/Istanbul
notify: email
extraction_schema:
retention_period: {type: text, unknown_allowed: true}
deletion_on_request: {type: yes_no_unknown}
data_location: {type: text, unknown_allowed: true}Two rules keep plans honest. An empty source list should mean no sources at all, never every source. And when the question changes, write a new plan version instead of letting the running job widen itself; the note on why a changed question needs a new plan makes the full case. Budgets belong in the plan too: a cap on tool calls, a cap on wall time and, if the agent spends money, a cap on spend.
Separate outputs with strict contracts
If the agent returns one free-form report for everything, nothing downstream can tell a verified finding from a guess. Give each job its own output type and validate every result against it:
Finding: claim, quote, source_id, status: supported | no_source_found ChangeEvent: monitor_version, before_source_id, after_source_id, diff, materiality, status: pending_review ExtractedValue: record_id, field, value, passage, source_id, status: found | unknown
Notice what the contracts leave out. A finding without a quote cannot be marked supported; it becomes an open question, and open questions are kept rather than deleted. A change event has no field for an action taken, because noticing a change and acting on it are different steps that belong to different people. An extracted value without a passage is not a value. Reject output that breaks its contract instead of quietly repairing it, and log the rejection. A pattern of rejections is the quickest way to find a weak prompt or an unreliable tool.
One evidence store for all three jobs
The three jobs should not each fetch and keep their own copies of the web. Put every retrieved document in one store and let the jobs refer to it by ID.
source_id: src-7f3a url: https://vendor.example/legal/retention retrieved_at: YYYY-MM-DDTHH:MM:SSZ content_hash: sha256 of the normalized text method: search result, then page fetch robots_ok: true
This pays off quickly. A research finding can cite the same snapshot the monitor later compares against. Extraction reads exactly what the researcher read, so a disputed value can be traced to the text it came from. Duplicate fetches disappear, and so does the argument about which version of a page somebody saw. A shared store also keeps the honest limits in view: a citation points to evidence but does not prove anything by itself, and a reviewer still has to check that the passage means what the claim says. For the retrieval layer, see structured data extraction at scale. Fetch politely, respect robots rules and site terms, and never use a browser tool to get past a login nobody gave you.
Approvals where actions leave the system
Reading, comparing and drafting can run inside budgets without a person watching each step. The pause belongs at the boundary: where an action changes something outside the agent's own workspace, or widens what the agent is able to do. Typical examples:
- sending an email, message or report to anyone;
- writing to a CRM, a database or a shared document;
- exporting files;
- deleting anything;
- creating or changing a schedule, including starting a new monitor;
- starting another agent, or editing the agent's own instructions.
Enforce this in the tool layer, not in the prompt. A prompt that says “ask before sending” is a suggestion. A wrapper that refuses the call is a control.
CONSEQUENTIAL = {'send_email', 'crm_write', 'export_files', 'delete', 'create_schedule', 'spawn_agent'}
def call_tool(name, args, decisions):
if name in CONSEQUENTIAL:
decision = decisions.get(attempt_key(name, args))
if decision is None or not decision.approved:
return request_approval(name, args) # show the exact call, word for word
if decision.args != args:
return request_approval(name, args) # arguments changed since approval
return TOOLS[name](**args)Three details matter. The approval is tied to one attempt with specific arguments, so approving one email does not approve the next one. The check runs again at execution time, in case the arguments or the context moved in between. And the person approving sees the call exactly as the agent made it, not a friendly paraphrase. The note on the seven action classes that pause for a person maps out this boundary in more detail.
Where GoCux fits
GoCux is neither a crawler nor a search API. Your agent's tools still do the fetching, through a browsing or export step you authorize, and imported pages keep their original page reference and retrieval date. What GoCux supplies is the structure this guide describes, already in place around the work.
Research in GoCux is a plan built from a question, ordered steps and a source allowlist. It is saved as a draft, needs approval before it runs, and cannot grow wider while executing. Pulse covers the monitoring job with an approved, versioned definition and occurrences reviewed one change at a time. Extraction returns values with the passage behind each one and keeps missing values as unknown. A work order ties a piece of work to its goal, its scope and its acceptance criteria, and each step is written to the order's record as it happens. Sending, exporting, deleting, scheduling and putting another agent to work are among the seven classes that stop for a decision. To write the work order itself, start with how to specify a task.
Build checklist
- Each job has its own plan object, readable and approvable before it runs.
- An empty source list means no sources.
- Output types are validated; contract breaks are rejected and logged.
- Findings without quotations are kept as open questions, not dropped.
- All jobs read from one evidence store with URL, retrieval time and content hash.
- Consequential tool calls are blocked in code until a decision for that exact attempt exists.
- Budgets for tool calls, time and spend are set per job.
- A changed question produces a new plan version, never a silently widened run.
Bring this workflow into your workspace.
Set the scope. Review the evidence. Approve the steps that need it.