Incident Response Agent
Build a BDI (Belief-Desire-Intention) cognitive agent for cybersecurity incident response with real-time WebSocket events, episodic memory, human-in-the-loop approval, and inter-agent coordination.
What you will learn:
- Creating and configuring cognitive agents
- Managing beliefs, goals, and drives as psi-terms
- Running BDI cognitive cycles and interpreting outcomes
- Subscribing to real-time agent events via WebSocket
- Human-in-the-loop approval workflows
- Episodic memory for learning from past incidents
- Inter-agent messaging and coordination
Prerequisites:
- Completion of the Loan Approval Engine tutorial (or familiarity with sorts, inference rules, and backward chaining)
- A running Reasoning Layer backend at
https://platform.ovh.reasoninglayer.ai - Node.js 18+ with
@kortexya/reasoninglayerinstalled
1. Why a Cognitive Agent?
Traditional incident response relies on static playbooks and human analysts. A BDI cognitive agent brings three advantages:
- Beliefs — the agent maintains a typed knowledge base of threat intelligence, correlated with the sort hierarchy. An IDS alert belief can unify with inference rules, not just pattern-match strings.
- Goals — response objectives (
contain_threat,identify_scope) are psi-terms in the sort lattice. The engine can compute GLB of two goals to find a more specific combined objective. - Intentions — committed plans are tracked with commitment strength. When new information arrives, the agent can reconsider its commitments using its reasoning engine.
The key insight: an agent’s beliefs ARE psi-terms, its rules ARE inference rules, and its goals ARE backward chaining goals. The cognitive layer builds on the inference layer from Tutorial 2.
2. Set Up the Security Domain
import { ReasoningLayerClient, SortBuilder, psi, constrained, guard,} from '@kortexya/reasoninglayer';
import type { SortDto } from '@kortexya/reasoninglayer';import type { Cognitive } from '@kortexya/reasoninglayer';type CreateAgentResponse = Cognitive.CreateAgentResponse;type AgentStateDto = Cognitive.AgentStateDto;type ExtendedAgentStateDto = Cognitive.ExtendedAgentStateDto;type AddBeliefResponse = Cognitive.AddBeliefResponse;type AddGoalResponse = Cognitive.AddGoalResponse;type RunCycleResponse = Cognitive.RunCycleResponse;type CycleOutcomeDto = Cognitive.CycleOutcomeDto;type AgentEvent = Cognitive.AgentEvent;type AgentEventHandlers = Cognitive.AgentEventHandlers;type AgentSubscription = Cognitive.AgentSubscription;type EpisodeStatsResponse = Cognitive.EpisodeStatsResponse;
const tenantId = '550e8400-e29b-41d4-a716-446655440000';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId, auth: { mode: 'cookie' },});2.1 Create the Security Ontology
// Base sortsconst securityEntity = await client.sorts.createSort( SortBuilder.create('security_entity') .description('Base sort for all security domain concepts') .build());
const networkEntity = await client.sorts.createSort( SortBuilder.create('network_entity') .description('Base sort for network infrastructure concepts') .build());
// Alert hierarchyconst alert = await client.sorts.createSort( SortBuilder.create('alert') .parent(securityEntity.id) .feature({ name: 'source', required: true }) .feature({ name: 'severity', required: true, constraint: { type: 'IntegerRange', value: { min: 1, max: 10 } } }) .feature({ name: 'timestamp', required: true }) .feature({ name: 'description', required: true }) .feature({ name: 'host', required: false }) .build());
// Multiple inheritance: IDS alert is both a security entity and a network entityconst idsAlert = await client.sorts.createSort( SortBuilder.create('ids_alert') .parents([alert.id, networkEntity.id]) .feature({ name: 'signature_id', required: true }) .feature({ name: 'src_ip', required: true }) .feature({ name: 'dst_ip', required: true }) .feature({ name: 'protocol', required: false }) .description('Inherits from both alert and network_entity') .build());
const siemAlert = await client.sorts.createSort( SortBuilder.create('siem_alert') .parent(alert.id) .feature({ name: 'correlation_id', required: false }) .feature({ name: 'log_sources', required: false }) .build());
// Indicator of Compromise (IoC)const indicator = await client.sorts.createSort( SortBuilder.create('indicator') .parent(securityEntity.id) .feature({ name: 'ioc_type', required: true }) // ip, domain, hash, url .feature({ name: 'ioc_value', required: true }) .feature({ name: 'confidence', required: true }) .feature({ name: 'source_feed', required: false }) .build());
// Host inventoryconst host = await client.sorts.createSort( SortBuilder.create('host') .parent(networkEntity.id) .feature({ name: 'hostname', required: true }) .feature({ name: 'ip_address', required: true }) .feature({ name: 'os', required: false }) .feature({ name: 'criticality', required: true, constraint: { type: 'IntegerRange', value: { min: 1, max: 5 } } }) .feature({ name: 'zone', required: true }) .build());
// Response actions — subsorts carry different featuresconst responseAction = await client.sorts.createSort( SortBuilder.create('response_action') .parent(securityEntity.id) .feature({ name: 'target_host', required: true }) .feature({ name: 'priority', required: true }) .feature({ name: 'requires_approval', required: true }) .build());
const isolate = await client.sorts.createSort( SortBuilder.create('isolate') .parent(responseAction.id) .feature({ name: 'isolation_method', required: true }) .feature({ name: 'duration_hours', required: false }) .build());
const blockIp = await client.sorts.createSort( SortBuilder.create('block_ip') .parent(responseAction.id) .feature({ name: 'blocked_ip', required: true }) .feature({ name: 'block_scope', required: false }) .build());3. Create the Cognitive Agent
A cognitive agent maintains its own belief base, goal stack, and intention commitments. Configuration parameters control how aggressively it pursues goals and when it requests human approval.
const agentResponse: CreateAgentResponse = await client.cognitive.createAgent({ name: 'incident-responder-alpha', config: { beliefConfidenceThreshold: 0.6, // Discard beliefs below 60% confidence enableLearning: true, // Learn from feedback maxBeliefHistory: 500, // Retain last 500 belief updates maxGoalsPerCycle: 3, // Pursue up to 3 goals per cycle },});
const agentId = agentResponse.agentId;console.log(`Created agent: ${agentResponse.name} (${agentId})`);3.1 Register Cognitive Sorts and Rules
Cognitive sorts and rules are registered in the agent’s cognitive registry, separate from the main sort hierarchy. They define how the agent reasons about its beliefs and goals.
// Register security-relevant sorts in the cognitive registryawait client.cognitive.addSort({ sortName: 'threat_detected', parentSortNames: ['security_entity'],});
await client.cognitive.addSort({ sortName: 'containment_action', parentSortNames: ['response_action'],});
// Cognitive rule: high-severity alert → create containment goalconst rule1 = await client.cognitive.addRule({ head: { sort: 'containment_action', features: { target_host: { name: '?Host' }, priority: { name: '?Priority' }, }, }, antecedents: [ { sort: 'ids_alert', features: { host: { name: '?Host' }, severity: { name: '?Severity' }, }, }, ], certainty: 0.85,});console.log(`Cognitive rule: ${rule1.ruleId}`);
// Rule: IoC match → block IP actionconst rule2 = await client.cognitive.addRule({ head: { sort: 'block_ip', features: { blocked_ip: { name: '?SrcIP' }, target_host: { name: '?DstHost' }, }, }, antecedents: [ { sort: 'indicator', features: { ioc_type: 'ip', ioc_value: { name: '?SrcIP' }, }, }, { sort: 'ids_alert', features: { src_ip: { name: '?SrcIP' }, host: { name: '?DstHost' }, }, }, ], certainty: 0.9,});console.log(`IoC correlation rule: ${rule2.ruleId}`);4. Populate Beliefs
Beliefs represent what the agent knows. Each belief carries a confidence score and a source attribution. Beliefs are psi-terms — they participate in the agent’s inference.
// Alert beliefs — incoming from detection systemsconst alertBelief: AddBeliefResponse = await client.cognitive.addBelief({ agentId: agentId, belief: { sort: 'ids_alert', features: { source: 'suricata', severity: 8, timestamp: '2024-04-01T14:23:00Z', description: 'ET MALWARE Win32/Emotet C2 Activity Detected', host: 'web-prod-03', signature_id: 'SID-2028765', src_ip: '198.51.100.42', dst_ip: '10.0.1.15', protocol: 'tcp', }, }, confidence: 0.92, source: 'ids_pipeline',});console.log(`Alert belief: ${alertBelief.beliefId}`);
// Threat intelligence beliefsawait client.cognitive.addBelief({ agentId: agentId, belief: { sort: 'indicator', features: { ioc_type: 'ip', ioc_value: '198.51.100.42', confidence: 0.95, source_feed: 'abuse_ch', }, }, confidence: 0.95, source: 'threat_intel_feed',});
// Host inventory beliefsawait client.cognitive.addBelief({ agentId: agentId, belief: { sort: 'host', features: { hostname: 'web-prod-03', ip_address: '10.0.1.15', os: 'Ubuntu 22.04', criticality: 4, // High criticality: production web server zone: 'dmz', }, }, confidence: 1.0, source: 'cmdb',});
// A second alert on a different hostawait client.cognitive.addBelief({ agentId: agentId, belief: { sort: 'siem_alert', features: { source: 'splunk', severity: 5, timestamp: '2024-04-01T14:25:00Z', description: 'Unusual outbound DNS traffic volume', host: 'db-staging-01', correlation_id: 'CORR-2024-0401-A', }, }, confidence: 0.7, source: 'siem_pipeline',});5. Set Goals and Run a BDI Cycle
Goals represent what the agent wants to achieve. The agent prioritizes goals and selects plans to pursue them.
// Primary goal: contain the threat on web-prod-03const goal1: AddGoalResponse = await client.cognitive.addGoal({ agentId: agentId, goal: { sort: 'containment_action', features: { target_host: 'web-prod-03', }, }, priority: 10, // Highest priority status: 'pending',});console.log(`Containment goal: ${goal1.goalId}`);
// Secondary goal: identify the blast radiusconst goal2: AddGoalResponse = await client.cognitive.addGoal({ agentId: agentId, goal: { sort: 'threat_detected', features: { target_host: 'web-prod-03', }, }, priority: 7, status: 'pending',});console.log(`Scope identification goal: ${goal2.goalId}`);
// Run a BDI cognitive cycleconst cycle: RunCycleResponse = await client.cognitive.runCycle({ agentId: agentId,});
console.log(`\n=== BDI Cycle Result ===`);console.log(`Message: ${cycle.message}`);
const outcome: CycleOutcomeDto = cycle.outcome;console.log(`Goals pursued: ${outcome.goalsPursued.length}`);console.log(`Goals achieved: ${outcome.goalsAchieved.length}`);console.log(`Goals failed: ${outcome.goalsFailed.length}`);console.log(`Actions executed: ${outcome.actionsExecuted.length}`);console.log(`New beliefs: ${outcome.newBeliefs.length}`);console.log(`Updated beliefs: ${outcome.updatedBeliefs.length}`);console.log(`Success rate: ${outcome.successRate}`);6. Real-Time WebSocket Events
Subscribe to the agent’s event stream for real-time notification of cognitive activity. Events are a discriminated union — switch on event.type for type-safe handling.
const handlers: AgentEventHandlers = { onEvent: (event: AgentEvent) => { switch (event.type) { case 'connected': console.log(`\n[WS] Connected to agent ${event.agentId}`); break;
case 'kb_change': console.log(`[WS] KB change: ${event.changeType} on ${event.sort} (term: ${event.termId})`); // Fires when beliefs are added, updated, or removed break;
case 'hitl_request': console.log(`\n[WS] === HUMAN APPROVAL REQUIRED ===`); console.log(` Review ID: ${event.reviewId}`); console.log(` Action: ${event.actionSort}`); console.log(` Confidence: ${event.confidence}`); if (event.deadline) { console.log(` Deadline: ${event.deadline}`); } // In production, route this to a human analyst via your ticketing system break;
case 'cycle_complete': console.log(`[WS] Cycle complete for agent ${event.agentId}`); console.log(` Actions: ${event.actionsExecuted}, Beliefs updated: ${event.beliefsUpdated}`); if (event.pendingReviews.length > 0) { console.log(` Pending reviews: ${event.pendingReviews.join(', ')}`); } break;
case 'attention_shift': console.log(`[WS] Attention shifted to goal: ${event.to} (priority: ${event.priority})`); if (event.from) { console.log(` From: ${event.from}`); } // Fires when the agent switches which goal it is pursuing break;
case 'goal_completed': console.log(`[WS] Goal ${event.goalId}: ${event.success ? 'ACHIEVED' : 'FAILED'}`); if (event.result) { console.log(` Result: ${event.result}`); } break;
case 'impasse_detected': console.log(`[WS] IMPASSE on goal ${event.blockedGoal}`); console.log(` Type: ${event.impasseType}`); console.log(` Suggestions: ${event.recoverySuggestions.join(', ')}`); // The agent cannot make progress — may need human intervention break;
case 'heartbeat': // Periodic health check — log sparingly in production break;
case 'error': console.error(`[WS] Agent error [${event.code}]: ${event.message}`); break; } },
onReconnect: () => { console.log('[WS] Reconnected after connection loss'); },
onError: (error: Error) => { console.error('[WS] Connection error:', error.message); },
onClose: () => { console.log('[WS] Connection closed'); },};
const subscription: AgentSubscription = client.cognitive.subscribeToEvents( agentId, handlers,);
console.log(`WebSocket connected: ${subscription.connected}`);6.1 A Real-Time Incident Scenario
With the WebSocket connected, trigger a sequence of events:
// Simulate a new high-severity alert arrivingconst newAlert: AddBeliefResponse = await client.cognitive.addBelief({ agentId: agentId, belief: { sort: 'ids_alert', features: { source: 'suricata', severity: 9, timestamp: '2024-04-01T14:30:00Z', description: 'ET TROJAN Possible Emotet Lateral Movement', host: 'app-prod-01', signature_id: 'SID-2028770', src_ip: '10.0.1.15', // web-prod-03 is now the source! dst_ip: '10.0.2.22', protocol: 'smb', }, }, confidence: 0.88, source: 'ids_pipeline',});// → WebSocket fires: kb_change event
// Run another BDI cycle — the agent should escalateconst cycle2: RunCycleResponse = await client.cognitive.runCycle({ agentId: agentId,});// → WebSocket fires: attention_shift (lateral movement is higher priority)// → WebSocket fires: hitl_request (isolating a production server requires approval)// → WebSocket fires: cycle_complete
console.log(`Cycle 2: ${cycle2.message}`);console.log(`Actions executed: ${cycle2.outcome.actionsExecuted.length}`);7. Human-in-the-Loop Approval
When the agent proposes a high-impact action (like isolating a production server), it emits a hitl_request event and waits for human approval. This is the bridge between autonomous reasoning and human oversight.
// Provide feedback/approval for the containment actionconst feedback = await client.cognitive.provideFeedback({ agentId: agentId, resultId: goal1.goalId, rating: 1, // Approve the action correction: 'Approved: isolate web-prod-03 from DMZ, maintain logging access', preference: 'Prefer network isolation over full shutdown for production hosts',});
console.log(`\n=== HITL Feedback ===`);console.log(`Feedback ID: ${feedback.feedbackId}`);
if (feedback.learnedRuleId) { console.log(`Agent learned a new rule: ${feedback.learnedRuleId}`); // The agent learned: "for production DMZ hosts, prefer network isolation"}
console.log(`Updated rules: ${feedback.updatedRules.length}`);for (const ruleId of feedback.updatedRules) { console.log(` Modified rule: ${ruleId}`);}8. Episodic Memory: Learning from Past Incidents
Episodic memory records past BDI cycles as episodes. The agent can recall similar past incidents to inform its current response.
// Get overall episode statisticsconst stats: EpisodeStatsResponse = await client.cognitive.getEpisodeStats({ agentId });console.log(`\n=== Episodic Memory ===`);console.log(`Total episodes: ${stats.totalEpisodes}`);console.log(`Successes: ${stats.successes} (${stats.successRate})`);console.log(`Failures: ${stats.failures}`);console.log(`Average reward: ${stats.averageReward}`);
// Recall episodes similar to the current incidentconst recalled = await client.cognitive.recallEpisodes({ agentId: agentId, goalId: goal1.goalId, context: ['emotet', 'lateral_movement', 'dmz'], maxResults: 5,});
console.log(`Recalled ${recalled.count} similar episodes:`);for (const ep of recalled.episodes) { console.log(` Episode ${ep.episodeId}: ${ep.outcome} (reward: ${ep.reward})`);}9. Inspect Agent State and Motivation
The agent’s extended state reveals its full BDI and motivation structure.
// Basic state: beliefs and goalsconst basicState: AgentStateDto = await client.cognitive.getAgent(agentId);console.log(`\n=== Agent State: ${basicState.name} ===`);console.log(`Has pending goals: ${basicState.hasPendingGoals}`);
console.log(`\nBeliefs (${basicState.beliefs.length}):`);for (const b of basicState.beliefs) { console.log(` [${b.sortName}] confidence: ${b.confidence}, source: ${b.source}`);}
console.log(`\nGoals (${basicState.goals.length}):`);for (const g of basicState.goals) { console.log(` [${g.goalSort ?? 'unknown'}] priority: ${g.priority}, status: ${g.status}`);}
// Extended state: intentions, drives, and curiosity targetsconst extState: ExtendedAgentStateDto = await client.cognitive.getExtendedAgentState({ agentId });
console.log(`\nIntentions (${extState.intentions.length}):`);for (const intention of extState.intentions) { console.log(` Goal: ${intention.goalId}`); console.log(` Progress: step ${intention.currentStep}/${intention.plan.length}`); console.log(` Commitment strength: ${intention.commitmentStrength}`);}
// Motivation: drives that influence goal selectionif (extState.motivation) { console.log(`\nMotivation:`); console.log(` Dominant drive: ${extState.motivation.dominantDrive ?? 'none'}`);
for (const drive of extState.motivation.drives) { console.log(` Drive: ${drive.driveType} (activation: ${drive.activation})`); }
for (const deficit of extState.motivation.deficits) { console.log(` Deficit: ${deficit.driveType} (urgency: ${deficit.urgency})`); }
// Curiosity: novel threats get higher attention for (const target of extState.motivation.curiosityTargets) { console.log(` Curiosity target: ${target.targetId} (novelty: ${target.novelty})`); }}
// Activationsconsole.log(`\nActivations (${extState.activations.length}):`);for (const a of extState.activations) { console.log(` ${a.label ?? a.termId}: total activation = ${a.total}`);}10. Inter-Agent Coordination
Create a second agent and demonstrate communication between agents.
// Create a forensics agentconst forensicsAgent = await client.cognitive.createAgent({ name: 'forensics-investigator', config: { beliefConfidenceThreshold: 0.5, enableLearning: true, maxBeliefHistory: 1000, maxGoalsPerCycle: 2, },});const forensicsId = forensicsAgent.agentId;
// Send a direct message between agentsconst msg = await client.cognitive.sendMessage({ fromAgentId: agentId, toAgentId: forensicsId, content: { action: 'request_forensic_analysis', host: 'web-prod-03', indicators: ['198.51.100.42', 'SID-2028765'], priority: 'critical', }, priority: 9,});console.log(`\nMessage sent: ${msg.messageId}`);
// Broadcast an alert to all agents in the tenantconst broadcast = await client.cognitive.broadcastMessage({ fromAgentId: agentId, content: { announcement: 'active_incident', affected_zone: 'dmz', threat_type: 'emotet_lateral_movement', containment_status: 'in_progress', },});console.log(`Broadcast to ${broadcast.recipientsCount} agents`);
// Mark messages as readawait client.cognitive.markMessagesRead({ agentId: forensicsId, messageIds: [msg.messageId],});11. Full Incident Timeline
Here is how all the pieces compose into a complete incident response workflow:
async function runIncidentResponse() { console.log('\n=== PHASE 1: Detection ==='); // Beliefs arrive from IDS, SIEM, threat intel feeds // → kb_change events fire on WebSocket // → Agent correlates IoC with alert via cognitive rules
console.log('\n=== PHASE 2: Triage ==='); // Run BDI cycle const triage = await client.cognitive.runCycle({ agentId: agentId, }); console.log(`Triage: ${triage.outcome.goalsPursued.length} goals pursued`); // → attention_shift events show goal prioritization // → Agent recalls similar past incidents from episodic memory
console.log('\n=== PHASE 3: Containment ==='); // Agent proposes isolation of compromised host // → hitl_request event: human analyst must approve // Human approves with feedback await client.cognitive.provideFeedback({ agentId: agentId, resultId: goal1.goalId, rating: 1, correction: 'Approved with network isolation', }); // → Agent learns preference for network isolation on DMZ hosts // → goal_completed event fires
console.log('\n=== PHASE 4: Eradication ==='); // Agent sends forensics request to forensics agent await client.cognitive.sendMessage({ fromAgentId: agentId, toAgentId: forensicsId, content: { action: 'eradicate', host: 'web-prod-03' }, priority: 8, }); // Forensics agent handles eradication
console.log('\n=== PHASE 5: Recovery ==='); // Run final BDI cycle to verify all goals achieved const recovery = await client.cognitive.runCycle({ agentId: agentId, }); console.log(`Recovery: success rate ${recovery.outcome.successRate}`);
// Final state inspection const finalState = await client.cognitive.getExtendedAgentState({ agentId }); console.log(`Final intentions: ${finalState.intentions.length}`); console.log(`Active beliefs: ${finalState.beliefs.length}`);}
await runIncidentResponse();12. Cleanup
// Close WebSocket subscriptionsubscription.close();console.log('WebSocket closed');
// Delete agentsawait client.cognitive.deleteAgent(agentId);await client.cognitive.deleteAgent(forensicsId);console.log('Agents deleted');What You Learned
| Concept | What It Means | Traditional Alternative |
|---|---|---|
| BDI cognitive agents | Beliefs + Desires + Intentions architecture for autonomous reasoning | Static playbooks, rule-based systems without state |
| Beliefs as psi-terms | Agent beliefs carry sort membership and participate in sort hierarchy unification | Prolog atoms, flat key-value stores |
| Goals as typed psi-terms | Goals are sorts in the hierarchy — GLB of two goals gives a more specific combined objective | String-labeled goals with no lattice operations |
| Cognitive rules | Rules that connect beliefs to actions, modifiable at runtime via feedback | Static configuration files |
| WebSocket events | Real-time discriminated union events: kb_change, hitl_request, cycle_complete, attention_shift, goal_completed, impasse_detected | Polling-based APIs |
| HITL approval | Human-in-the-loop workflow for high-impact actions with feedback-driven learning | Ad-hoc ticketing systems |
| Episodic memory | Recall similar past incidents to inform current response | Manual incident databases |
| Drives and motivation | Curiosity and security drives model analyst attention allocation and alert fatigue | Fixed priority queues |
| Inter-agent messaging | Direct and broadcast communication between agents in the same tenant | Message queues (Kafka, RabbitMQ) |
What Comes Next
You have now covered the three core layers of the Reasoning Layer SDK:
- Knowledge modeling — sorts, psi-terms, fuzzy values, lattice operations (Tutorial 1)
- Inference — rules, backward/forward chaining, NAF, homoiconic introspection (Tutorial 2)
- Cognitive agents — BDI reasoning, real-time events, learning, coordination (Tutorial 3)
For additional SDK capabilities, see the guides:
- Fuzzy Logic — Fuzzy unification, subsumption, and top-K similarity search
- Causal Reasoning — Pearl’s causal hierarchy, interventions, and counterfactuals
- Optimization — Linear programming and constraint solving