Agent Schemas
Agents communicate through structured, versioned JSON. This keeps provider-backed agents interchangeable and makes all outputs auditable.
The core contract is:
AgentContext -> Prompt Builder -> LLM Provider -> AgentResponse
AgentContext
AgentContext is the structured input assembled from EASM platform data before prompt generation.
It should include:
- organization metadata
- scan metadata
- approved scope
- assets
- vulnerabilities
- graph edges
- scan history
- previous findings
- risk statistics
Example:
{
"organization": {
"id": "uuid",
"name": "Example Corp"
},
"scan": {
"id": "uuid",
"profile": "deep",
"started_at": "2026-05-31T10:00:00Z",
"completed_at": "2026-05-31T10:20:00Z"
},
"scope": [
{
"type": "domain",
"value": "example.com",
"approved": true
}
],
"assets": [
{
"id": "uuid",
"type": "url",
"value": "https://admin.example.com/login",
"confidence": 0.98,
"source_plugin": "httpx",
"metadata": {
"status_code": 200,
"title": "Admin Login"
}
}
],
"vulnerabilities": [
{
"id": "uuid",
"asset_id": "uuid",
"title": "Exposed admin panel",
"severity": "high",
"cvss_score": 7.5,
"risk_score": 8.5,
"confidence": 0.86,
"status": "new",
"source_plugin": "nuclei",
"evidence": {}
}
],
"graph": [
{
"from": "uuid-domain",
"to": "uuid-url",
"relation": "contains"
}
],
"historical_findings": [],
"previous_scans": [],
"statistics": {
"asset_count": 128,
"critical_count": 2,
"high_count": 12,
"medium_count": 34,
"recurring_findings": 5
}
}
Recommended Go shape:
type AgentContext struct {
Organization AgentOrganization `json:"organization"`
Scan AgentScan `json:"scan"`
Scope []AgentScopeItem `json:"scope"`
Assets []AgentAsset `json:"assets"`
Vulnerabilities []AgentVuln `json:"vulnerabilities"`
Graph []AgentEdge `json:"graph"`
HistoricalFindings []AgentVuln `json:"historical_findings"`
PreviousScans []AgentScan `json:"previous_scans"`
Statistics AgentStatistics `json:"statistics"`
}
AgentContext is transformed into prompts before being sent to the LLM. Large contexts should be reduced, ranked, or summarized while preserving IDs needed for structured output.
AgentResponse
AgentResponse is the structured output contract. It is intended for:
- OpenAI function calling or structured outputs
- Claude structured outputs
- Gemini JSON mode
- local model adapters
The response must remain machine-readable.
Example:
{
"agent_type": "risk",
"category": "advisory",
"summary": "Risk is concentrated around exposed administrative URLs and recurring high-severity findings.",
"confidence": 0.91,
"findings": [
{
"asset_id": "uuid",
"vulnerability_id": "uuid",
"title": "Exposed administrative entry point",
"reasoning": "The asset is internet-facing, linked to a high-severity finding, and appears in previous scan history."
}
],
"actions": [
{
"type": "risk_update",
"asset_id": "uuid",
"vulnerability_id": "uuid",
"priority": "high",
"requires_approval": false,
"note": "Prioritize remediation due to exposure and recurrence."
}
],
"recommendations": [
"Review administrative access controls.",
"Prioritize exposed high-severity findings before internal-only issues."
]
}
Recommended Go shape:
type AgentResponse struct {
AgentType string `json:"agent_type"`
Category string `json:"category"`
Summary string `json:"summary"`
Confidence float64 `json:"confidence"`
Findings []AgentFinding `json:"findings"`
Actions []AgentAction `json:"actions"`
Recommendations []string `json:"recommendations"`
}
AgentAction
Allowed advisory action types:
| Type | Meaning | Requires Approval |
|---|---|---|
create_note |
Store analyst-facing note | No |
report_summary |
Add text to a report draft | No |
risk_update |
Recommend severity, score, or priority changes | Policy-dependent |
attack_path |
Store attack path narrative | No |
false_positive_candidate |
Mark finding as review candidate | Yes by default |
Allowed operational action types:
| Type | Meaning | Requires Approval |
|---|---|---|
suggest_scan |
Recommend a targeted scan | Yes |
flag_asset |
Add note/context to an asset | Policy-dependent |
suggest_profile |
Recommend a deeper scan profile | Yes |
suggest_plugin_run |
Recommend a specific plugin run | Yes |
Operational suggestion example:
{
"type": "suggest_scan",
"plugin": "nuclei",
"target": "https://admin.example.com",
"priority": "high",
"requires_approval": true,
"note": "Validate the exposed administrative interface with a targeted template set."
}
AgentFinding
AgentFinding captures model reasoning tied to existing platform objects.
{
"asset_id": "uuid",
"vulnerability_id": "uuid",
"title": "Likely recurring exposure",
"reasoning": "This finding has appeared across multiple scans and affects an internet-facing asset.",
"confidence": 0.88
}
agent_runs Storage
The initial database schema already contains agent_runs:
CREATE TABLE IF NOT EXISTS agent_runs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
scan_id UUID REFERENCES scans(id) ON DELETE SET NULL,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
agent_type TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
input_context JSONB NOT NULL DEFAULT '{}',
output JSONB NOT NULL DEFAULT '{}',
actions_taken JSONB NOT NULL DEFAULT '{}',
notes TEXT NOT NULL DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
finished_at TIMESTAMP
);
Recommended statuses:
| Status | Meaning |
|---|---|
pending |
Agent run was created but not started |
running |
Prompt is being built or provider call is in progress |
success |
Agent returned valid structured output |
failed |
Provider failed or returned invalid output |
skipped |
Agent was disabled or policy prevented execution |
Validation Rules
Every model response should be validated before it is stored or applied:
- response must parse as JSON
- response must match
AgentResponse agent_typemust match the running agentcategorymust match the registered agent category- action type must be known and allowed for the agent
- operational actions must include
requires_approval = true - targets must be inside approved scope
- referenced asset and vulnerability IDs must belong to the same organization
- confidence must be in range
0..1 - risk score recommendations must be in range
0..10 - free-form text must not be treated as executable instructions