rust-kgdb 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/fraud-detection-agent.js +603 -263
- package/examples/underwriting-agent.js +487 -290
- package/package.json +2 -2
|
@@ -1,379 +1,576 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* UNDERWRITING AGENT - Built on HyperMind Framework
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - Risk factor analysis from knowledge graph
|
|
6
|
-
* - GraphFrames for exposure correlation
|
|
7
|
-
* - Embeddings for similar policy matching
|
|
8
|
-
* - Datalog rules for automated decisioning
|
|
4
|
+
* This example demonstrates proper agentic flow using the HyperMind framework:
|
|
9
5
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
6
|
+
* ┌─────────────────────────────────────────────────────────────────────┐
|
|
7
|
+
* │ HyperMind Framework Architecture │
|
|
8
|
+
* │ │
|
|
9
|
+
* │ TYPE THEORY → Tools have typed signatures (A → B) │
|
|
10
|
+
* │ CATEGORY THEORY → Tools compose as morphisms (f ∘ g) │
|
|
11
|
+
* │ PROOF THEORY → Every execution produces audit witness │
|
|
12
|
+
* │ │
|
|
13
|
+
* └─────────────────────────────────────────────────────────────────────┘
|
|
14
|
+
*
|
|
15
|
+
* Real-World Data Sources:
|
|
16
|
+
* - ISO (Insurance Services Office) rating factors
|
|
17
|
+
* - NAIC (National Association of Insurance Commissioners) guidelines
|
|
18
|
+
* - US Census Bureau NAICS industry codes
|
|
19
|
+
* - FEMA catastrophe exposure zones
|
|
20
|
+
*
|
|
21
|
+
* Requirements:
|
|
22
|
+
* - Set ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable
|
|
23
|
+
* - Or use model: 'mock' for offline testing
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* ANTHROPIC_API_KEY=sk-... node examples/underwriting-agent.js
|
|
27
|
+
* OPENAI_API_KEY=sk-... node examples/underwriting-agent.js --model gpt-4o
|
|
28
|
+
* node examples/underwriting-agent.js --model mock
|
|
15
29
|
*/
|
|
16
30
|
|
|
31
|
+
const { HyperMindAgent } = require('../hypermind-agent.js')
|
|
17
32
|
const {
|
|
18
33
|
GraphDB,
|
|
19
|
-
GraphFrame,
|
|
20
34
|
EmbeddingService,
|
|
21
35
|
DatalogProgram,
|
|
22
36
|
evaluateDatalog,
|
|
23
|
-
|
|
37
|
+
GraphFrame,
|
|
24
38
|
getVersion
|
|
25
39
|
} = require('../index.js')
|
|
26
40
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
42
|
+
// CONFIGURATION
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
44
|
+
|
|
45
|
+
const MODEL = process.argv.includes('--model')
|
|
46
|
+
? process.argv[process.argv.indexOf('--model') + 1]
|
|
47
|
+
: process.env.ANTHROPIC_API_KEY ? 'claude-sonnet-4'
|
|
48
|
+
: process.env.OPENAI_API_KEY ? 'gpt-4o'
|
|
49
|
+
: 'mock'
|
|
50
|
+
|
|
51
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
52
|
+
// UNDERWRITING KNOWLEDGE BASE (ISO/NAIC-Informed Data)
|
|
53
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
+
|
|
55
|
+
const UNDERWRITING_KB = `
|
|
56
|
+
@prefix uw: <http://underwriting.org/> .
|
|
57
|
+
@prefix naics: <http://naics.com/code/> .
|
|
58
|
+
@prefix fema: <http://fema.gov/zone/> .
|
|
59
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
35
60
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
36
61
|
|
|
37
|
-
#
|
|
38
|
-
:BUS001 :
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
:
|
|
79
|
-
:
|
|
80
|
-
:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
62
|
+
# Business Accounts with NAICS Industry Codes
|
|
63
|
+
uw:BUS001 rdf:type uw:BusinessAccount ;
|
|
64
|
+
uw:name "Acme Manufacturing Inc." ;
|
|
65
|
+
uw:naicsCode naics:332710 ;
|
|
66
|
+
uw:industry "Machine Shops" ;
|
|
67
|
+
uw:yearsInBusiness "15"^^xsd:integer ;
|
|
68
|
+
uw:annualRevenue "8500000"^^xsd:decimal ;
|
|
69
|
+
uw:employeeCount "45"^^xsd:integer ;
|
|
70
|
+
uw:lossRatio "0.45"^^xsd:float ;
|
|
71
|
+
uw:femaZone fema:X ;
|
|
72
|
+
uw:territoryCode "FL-027" .
|
|
73
|
+
|
|
74
|
+
uw:BUS002 rdf:type uw:BusinessAccount ;
|
|
75
|
+
uw:name "TechStart LLC" ;
|
|
76
|
+
uw:naicsCode naics:541511 ;
|
|
77
|
+
uw:industry "Custom Computer Programming" ;
|
|
78
|
+
uw:yearsInBusiness "3"^^xsd:integer ;
|
|
79
|
+
uw:annualRevenue "1200000"^^xsd:decimal ;
|
|
80
|
+
uw:employeeCount "12"^^xsd:integer ;
|
|
81
|
+
uw:lossRatio "0.15"^^xsd:float ;
|
|
82
|
+
uw:femaZone fema:C ;
|
|
83
|
+
uw:territoryCode "CA-037" .
|
|
84
|
+
|
|
85
|
+
uw:BUS003 rdf:type uw:BusinessAccount ;
|
|
86
|
+
uw:name "SafeHaul Logistics" ;
|
|
87
|
+
uw:naicsCode naics:484110 ;
|
|
88
|
+
uw:industry "General Freight Trucking" ;
|
|
89
|
+
uw:yearsInBusiness "8"^^xsd:integer ;
|
|
90
|
+
uw:annualRevenue "4200000"^^xsd:decimal ;
|
|
91
|
+
uw:employeeCount "28"^^xsd:integer ;
|
|
92
|
+
uw:lossRatio "0.72"^^xsd:float ;
|
|
93
|
+
uw:femaZone fema:AE ;
|
|
94
|
+
uw:territoryCode "TX-201" .
|
|
95
|
+
|
|
96
|
+
uw:BUS004 rdf:type uw:BusinessAccount ;
|
|
97
|
+
uw:name "Downtown Restaurant Group" ;
|
|
98
|
+
uw:naicsCode naics:722511 ;
|
|
99
|
+
uw:industry "Full-Service Restaurants" ;
|
|
100
|
+
uw:yearsInBusiness "12"^^xsd:integer ;
|
|
101
|
+
uw:annualRevenue "3100000"^^xsd:decimal ;
|
|
102
|
+
uw:employeeCount "65"^^xsd:integer ;
|
|
103
|
+
uw:lossRatio "0.28"^^xsd:float ;
|
|
104
|
+
uw:femaZone fema:X500 ;
|
|
105
|
+
uw:territoryCode "NY-061" .
|
|
106
|
+
|
|
107
|
+
# ISO Base Rates by Industry Class
|
|
108
|
+
naics:332710 uw:isoBaseRate "12.50"^^xsd:decimal ;
|
|
109
|
+
uw:hazardClass "Manufacturing" ;
|
|
110
|
+
uw:riskTier "Medium" .
|
|
111
|
+
|
|
112
|
+
naics:541511 uw:isoBaseRate "4.25"^^xsd:decimal ;
|
|
113
|
+
uw:hazardClass "Office/Professional" ;
|
|
114
|
+
uw:riskTier "Low" .
|
|
115
|
+
|
|
116
|
+
naics:484110 uw:isoBaseRate "18.75"^^xsd:decimal ;
|
|
117
|
+
uw:hazardClass "Transportation" ;
|
|
118
|
+
uw:riskTier "High" .
|
|
119
|
+
|
|
120
|
+
naics:722511 uw:isoBaseRate "8.90"^^xsd:decimal ;
|
|
121
|
+
uw:hazardClass "Restaurant/Hospitality" ;
|
|
122
|
+
uw:riskTier "Medium" .
|
|
123
|
+
|
|
124
|
+
# FEMA Zone Risk Multipliers
|
|
125
|
+
fema:X uw:floodRisk "Minimal" ;
|
|
126
|
+
uw:territoryMultiplier "1.0"^^xsd:float .
|
|
127
|
+
|
|
128
|
+
fema:C uw:floodRisk "Moderate" ;
|
|
129
|
+
uw:territoryMultiplier "1.15"^^xsd:float .
|
|
130
|
+
|
|
131
|
+
fema:AE uw:floodRisk "High - 1% Annual Chance" ;
|
|
132
|
+
uw:territoryMultiplier "1.45"^^xsd:float .
|
|
133
|
+
|
|
134
|
+
fema:X500 uw:floodRisk "Moderate - 0.2% Annual Chance" ;
|
|
135
|
+
uw:territoryMultiplier "1.10"^^xsd:float .
|
|
136
|
+
|
|
137
|
+
# Territory Modifiers (based on state/region)
|
|
138
|
+
uw:Territory_FL uw:hurricaneExposure "High" ;
|
|
139
|
+
uw:modificationFactor "1.35"^^xsd:float .
|
|
140
|
+
|
|
141
|
+
uw:Territory_CA uw:earthquakeExposure "High" ;
|
|
142
|
+
uw:modificationFactor "1.25"^^xsd:float .
|
|
143
|
+
|
|
144
|
+
uw:Territory_TX uw:hurricaneExposure "Moderate" ;
|
|
145
|
+
uw:modificationFactor "1.20"^^xsd:float .
|
|
146
|
+
|
|
147
|
+
uw:Territory_NY uw:litigationRisk "High" ;
|
|
148
|
+
uw:modificationFactor "1.30"^^xsd:float .
|
|
88
149
|
`
|
|
89
150
|
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
function generateRiskEmbedding(naicsClass, lossRatio, territoryMod, yearsInBusiness) {
|
|
95
|
-
const embedding = new Array(384).fill(0)
|
|
96
|
-
|
|
97
|
-
// Industry risk encoding (dims 0-95)
|
|
98
|
-
const industryRisk = {
|
|
99
|
-
manufacturing: 0.6,
|
|
100
|
-
tech: 0.25,
|
|
101
|
-
transportation: 0.85,
|
|
102
|
-
hospitality: 0.55
|
|
103
|
-
}
|
|
104
|
-
const risk = industryRisk[naicsClass] || 0.5
|
|
105
|
-
for (let i = 0; i < 96; i++) {
|
|
106
|
-
embedding[i] = risk * (0.7 + Math.sin(i * 0.1) * 0.3)
|
|
107
|
-
}
|
|
151
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
152
|
+
// TOOL IMPLEMENTATIONS (Typed Morphisms)
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
108
154
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Tool: kg.sparql.query
|
|
157
|
+
* Type: SPARQLQuery → BindingSet
|
|
158
|
+
*/
|
|
159
|
+
async function sparqlQuery(db, query) {
|
|
160
|
+
const results = db.querySelect(query)
|
|
161
|
+
return results.map(r => r.bindings)
|
|
162
|
+
}
|
|
113
163
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Tool: kg.graphframe.analyze
|
|
166
|
+
* Type: Graph → AnalysisResult
|
|
167
|
+
*/
|
|
168
|
+
function graphframeAnalyze(vertices, edges) {
|
|
169
|
+
// GraphFrame constructor requires JSON strings
|
|
170
|
+
const gf = new GraphFrame(JSON.stringify(vertices), JSON.stringify(edges))
|
|
171
|
+
// pageRank and connectedComponents return JSON strings
|
|
172
|
+
const pageRankResult = JSON.parse(gf.pageRank(0.85, 20))
|
|
173
|
+
const componentsResult = JSON.parse(gf.connectedComponents())
|
|
174
|
+
return {
|
|
175
|
+
pageRank: pageRankResult.ranks || pageRankResult,
|
|
176
|
+
components: componentsResult.components || componentsResult
|
|
117
177
|
}
|
|
178
|
+
}
|
|
118
179
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Tool: kg.embeddings.similar
|
|
182
|
+
* Type: EntityId × Float → List[SimilarEntity]
|
|
183
|
+
*/
|
|
184
|
+
function embeddingsSimilar(embeddings, entityId, threshold = 0.7) {
|
|
185
|
+
// findSimilar returns JSON string with {entity, score, distance}
|
|
186
|
+
const resultJson = embeddings.findSimilar(entityId, 5, threshold)
|
|
187
|
+
const results = JSON.parse(resultJson)
|
|
188
|
+
// Map to standard format
|
|
189
|
+
return results.map(r => ({
|
|
190
|
+
id: r.entity,
|
|
191
|
+
similarity: r.score
|
|
192
|
+
}))
|
|
193
|
+
}
|
|
124
194
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Tool: kg.datalog.infer
|
|
197
|
+
* Type: DatalogProgram → InferredFacts
|
|
198
|
+
* Result format: {"predicate_name": [[term1, term2], ...], ...}
|
|
199
|
+
*/
|
|
200
|
+
function datalogInfer(program) {
|
|
201
|
+
const resultJson = evaluateDatalog(program)
|
|
202
|
+
const result = JSON.parse(resultJson)
|
|
203
|
+
// Convert to array format for easier processing
|
|
204
|
+
const facts = []
|
|
205
|
+
for (const [predicate, termArrays] of Object.entries(result)) {
|
|
206
|
+
for (const terms of termArrays) {
|
|
207
|
+
facts.push({ name: predicate, args: terms })
|
|
208
|
+
}
|
|
129
209
|
}
|
|
130
|
-
|
|
131
|
-
return embedding
|
|
210
|
+
return facts
|
|
132
211
|
}
|
|
133
212
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Tool: kg.premium.calculate
|
|
215
|
+
* Type: RiskFactors → PremiumQuote
|
|
216
|
+
*
|
|
217
|
+
* ISO Premium Calculation Formula:
|
|
218
|
+
* Premium = Base Rate × Exposure × Territory Mod × Experience Mod × Loss Mod
|
|
219
|
+
*/
|
|
138
220
|
function calculatePremium(baseRate, exposure, territoryMod, lossRatio, yearsInBusiness) {
|
|
139
|
-
//
|
|
221
|
+
// Experience modification (tenure discount)
|
|
140
222
|
const experienceMod = yearsInBusiness >= 10 ? 0.90 : yearsInBusiness >= 5 ? 0.95 : 1.05
|
|
141
|
-
|
|
223
|
+
|
|
224
|
+
// Loss modification (claims history)
|
|
225
|
+
const lossMod = lossRatio < 0.30 ? 0.85 :
|
|
226
|
+
lossRatio < 0.50 ? 1.00 :
|
|
227
|
+
lossRatio < 0.70 ? 1.15 : 1.35
|
|
142
228
|
|
|
143
229
|
const premium = baseRate * exposure * territoryMod * experienceMod * lossMod
|
|
144
230
|
return Math.round(premium * 100) / 100
|
|
145
231
|
}
|
|
146
232
|
|
|
147
|
-
//
|
|
148
|
-
// MAIN UNDERWRITING
|
|
149
|
-
//
|
|
233
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
234
|
+
// MAIN UNDERWRITING AGENT
|
|
235
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
150
236
|
|
|
151
|
-
async function
|
|
152
|
-
console.log('
|
|
153
|
-
console.log('
|
|
154
|
-
console.log(
|
|
155
|
-
console.log('
|
|
237
|
+
async function main() {
|
|
238
|
+
console.log('═'.repeat(78))
|
|
239
|
+
console.log(' HYPERMIND UNDERWRITING AGENT')
|
|
240
|
+
console.log(` rust-kgdb ${getVersion()} | Neuro-Symbolic AI Framework`)
|
|
241
|
+
console.log('═'.repeat(78))
|
|
242
|
+
console.log()
|
|
243
|
+
console.log(' Architecture: Type Theory + Category Theory + Proof Theory')
|
|
244
|
+
console.log(' Data Sources: ISO rating factors, NAIC guidelines, NAICS codes')
|
|
245
|
+
console.log(` LLM Model: ${MODEL}`)
|
|
156
246
|
console.log()
|
|
157
247
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
248
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
249
|
+
// PHASE 1: Initialize Knowledge Graph
|
|
250
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
161
251
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
console.log(` Knowledge graph: ${db.countTriples()} triples`)
|
|
166
|
-
console.log(` URI: ${db.getGraphUri()}`)
|
|
167
|
-
|
|
168
|
-
// Query business entities
|
|
169
|
-
const businesses = db.querySelect(`
|
|
170
|
-
PREFIX : <http://underwriting.org/>
|
|
171
|
-
SELECT ?bus ?name ?naics ?employees WHERE {
|
|
172
|
-
?bus :name ?name .
|
|
173
|
-
?bus :naics ?naics .
|
|
174
|
-
?bus :employees ?employees .
|
|
175
|
-
}
|
|
176
|
-
`)
|
|
252
|
+
console.log('┌─ TOOL: kg.sparql.load ───────────────────────────────────────────────┐')
|
|
253
|
+
console.log('│ Type: TTL → Graph │')
|
|
254
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
177
255
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
})
|
|
256
|
+
const db = new GraphDB('http://underwriting.org/kb')
|
|
257
|
+
db.loadTtl(UNDERWRITING_KB, 'http://underwriting.org/data')
|
|
258
|
+
const tripleCount = db.countTriples()
|
|
259
|
+
console.log(` Knowledge Graph: ${tripleCount} triples loaded`)
|
|
182
260
|
console.log()
|
|
183
261
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
{ src: 'BUS003', dst: 'transportation' },
|
|
205
|
-
{ src: 'BUS004', dst: 'hospitality' },
|
|
206
|
-
// Correlated risks
|
|
207
|
-
{ src: 'hurricane', dst: 'flood' },
|
|
208
|
-
{ src: 'manufacturing', dst: 'transportation' }
|
|
209
|
-
])
|
|
210
|
-
|
|
211
|
-
const graph = new GraphFrame(vertices, edges)
|
|
212
|
-
console.log(` Risk network: ${graph.vertexCount()} nodes, ${graph.edgeCount()} edges`)
|
|
213
|
-
|
|
214
|
-
// PageRank for risk concentration
|
|
215
|
-
const pr = JSON.parse(graph.pageRank(0.15, 20))
|
|
216
|
-
console.log(' Risk concentration (PageRank):')
|
|
217
|
-
if (pr.ranks) {
|
|
218
|
-
const sorted = Object.entries(pr.ranks)
|
|
219
|
-
.filter(([k]) => k.startsWith('BUS'))
|
|
220
|
-
.sort((a, b) => b[1] - a[1])
|
|
221
|
-
sorted.forEach(([node, score]) => {
|
|
222
|
-
console.log(` - ${node}: ${score.toFixed(4)}`)
|
|
223
|
-
})
|
|
224
|
-
}
|
|
262
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
263
|
+
// PHASE 2: Spawn HyperMind Agent
|
|
264
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
console.log('┌─ AGENT: HyperMindAgent.spawn() ──────────────────────────────────────┐')
|
|
267
|
+
console.log('│ AgentSpec: { │')
|
|
268
|
+
console.log('│ name: "underwriter", │')
|
|
269
|
+
console.log(`│ model: "${MODEL}", │`)
|
|
270
|
+
console.log('│ tools: [kg.sparql.query, kg.datalog.apply, kg.embeddings.search],│')
|
|
271
|
+
console.log('│ tracing: true │')
|
|
272
|
+
console.log('│ } │')
|
|
273
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
274
|
+
|
|
275
|
+
const agent = await HyperMindAgent.spawn({
|
|
276
|
+
name: 'underwriter',
|
|
277
|
+
model: MODEL,
|
|
278
|
+
tools: ['kg.sparql.query', 'kg.datalog.apply', 'kg.embeddings.search'],
|
|
279
|
+
endpoint: 'http://localhost:30080',
|
|
280
|
+
tracing: true
|
|
281
|
+
})
|
|
225
282
|
|
|
226
|
-
|
|
227
|
-
const cc = JSON.parse(graph.connectedComponents())
|
|
228
|
-
console.log(' Exposure correlation: Businesses share connected risk factors')
|
|
283
|
+
console.log(` Agent "${agent.getName()}" spawned with model: ${agent.getModel()}`)
|
|
229
284
|
console.log()
|
|
230
285
|
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
286
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
287
|
+
// PHASE 3: Execute Underwriting Tools
|
|
288
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
// Tool 1: SPARQL Query - Get All Accounts
|
|
291
|
+
console.log('┌─ TOOL: kg.sparql.query ─────────────────────────────────────────────┐')
|
|
292
|
+
console.log('│ Type: SPARQLQuery → BindingSet │')
|
|
293
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
294
|
+
|
|
295
|
+
const accountsQuery = `
|
|
296
|
+
PREFIX uw: <http://underwriting.org/>
|
|
297
|
+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
298
|
+
SELECT ?account ?name ?industry ?revenue ?lossRatio ?years WHERE {
|
|
299
|
+
?account a uw:BusinessAccount ;
|
|
300
|
+
uw:name ?name ;
|
|
301
|
+
uw:industry ?industry ;
|
|
302
|
+
uw:annualRevenue ?revenue ;
|
|
303
|
+
uw:lossRatio ?lossRatio ;
|
|
304
|
+
uw:yearsInBusiness ?years .
|
|
305
|
+
}
|
|
306
|
+
`
|
|
307
|
+
const accounts = await sparqlQuery(db, accountsQuery)
|
|
308
|
+
console.log(` Accounts retrieved: ${accounts.length}`)
|
|
309
|
+
accounts.forEach(a => {
|
|
310
|
+
const name = a['?name'] || a.name
|
|
311
|
+
const lr = a['?lossRatio'] || a.lossRatio
|
|
312
|
+
console.log(` - ${name}: loss ratio ${lr}`)
|
|
313
|
+
})
|
|
314
|
+
console.log()
|
|
234
315
|
|
|
235
|
-
|
|
316
|
+
// Tool 2: GraphFrame - Risk Network Analysis
|
|
317
|
+
console.log('┌─ TOOL: kg.graphframe.analyze ────────────────────────────────────────┐')
|
|
318
|
+
console.log('│ Type: Graph → {pagerank, components} │')
|
|
319
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
320
|
+
|
|
321
|
+
const riskVertices = [
|
|
322
|
+
{ id: 'BUS001', type: 'account', industry: 'manufacturing' },
|
|
323
|
+
{ id: 'BUS002', type: 'account', industry: 'technology' },
|
|
324
|
+
{ id: 'BUS003', type: 'account', industry: 'transportation' },
|
|
325
|
+
{ id: 'BUS004', type: 'account', industry: 'hospitality' },
|
|
326
|
+
{ id: 'FL', type: 'territory' },
|
|
327
|
+
{ id: 'CA', type: 'territory' },
|
|
328
|
+
{ id: 'TX', type: 'territory' },
|
|
329
|
+
{ id: 'NY', type: 'territory' }
|
|
330
|
+
]
|
|
236
331
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
{
|
|
241
|
-
{
|
|
242
|
-
{
|
|
332
|
+
const riskEdges = [
|
|
333
|
+
{ src: 'BUS001', dst: 'FL', relationship: 'located_in' },
|
|
334
|
+
{ src: 'BUS002', dst: 'CA', relationship: 'located_in' },
|
|
335
|
+
{ src: 'BUS003', dst: 'TX', relationship: 'located_in' },
|
|
336
|
+
{ src: 'BUS004', dst: 'NY', relationship: 'located_in' },
|
|
337
|
+
{ src: 'FL', dst: 'TX', relationship: 'hurricane_corridor' },
|
|
338
|
+
{ src: 'BUS001', dst: 'BUS003', relationship: 'same_class' }
|
|
243
339
|
]
|
|
244
340
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
341
|
+
const analysis = graphframeAnalyze(riskVertices, riskEdges)
|
|
342
|
+
console.log(` Risk Network: ${riskVertices.length} nodes, ${riskEdges.length} edges`)
|
|
343
|
+
console.log(' Risk Concentration (PageRank):')
|
|
344
|
+
Object.entries(analysis.pageRank).slice(0, 4).forEach(([node, rank]) => {
|
|
345
|
+
console.log(` - ${node}: ${rank.toFixed(4)}`)
|
|
248
346
|
})
|
|
347
|
+
console.log()
|
|
249
348
|
|
|
250
|
-
|
|
251
|
-
embeddings.
|
|
349
|
+
// Tool 3: Embeddings - Similar Risk Profiles
|
|
350
|
+
console.log('┌─ TOOL: kg.embeddings.similar ───────────────────────────────────────┐')
|
|
351
|
+
console.log('│ Type: EntityId × Float → List[SimilarEntity] │')
|
|
352
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
252
353
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
354
|
+
const embeddings = new EmbeddingService()
|
|
355
|
+
|
|
356
|
+
// Add account embeddings based on risk characteristics
|
|
357
|
+
embeddings.storeVector('BUS001', generateRiskEmbedding('manufacturing', 0.45, 15, 1.35))
|
|
358
|
+
embeddings.storeVector('BUS002', generateRiskEmbedding('technology', 0.15, 3, 1.25))
|
|
359
|
+
embeddings.storeVector('BUS003', generateRiskEmbedding('transportation', 0.72, 8, 1.20))
|
|
360
|
+
embeddings.storeVector('BUS004', generateRiskEmbedding('hospitality', 0.28, 12, 1.30))
|
|
361
|
+
|
|
362
|
+
const similarAccounts = embeddingsSimilar(embeddings, 'BUS003', 0.5)
|
|
363
|
+
console.log(' Accounts similar to BUS003 (highest risk):')
|
|
364
|
+
similarAccounts.forEach(s => {
|
|
365
|
+
console.log(` - ${s.id}: similarity ${s.similarity.toFixed(3)}`)
|
|
261
366
|
})
|
|
262
367
|
console.log()
|
|
263
368
|
|
|
264
|
-
//
|
|
265
|
-
console.log('
|
|
266
|
-
console.log('
|
|
369
|
+
// Tool 4: Datalog - Underwriting Decision Rules
|
|
370
|
+
console.log('┌─ TOOL: kg.datalog.infer ────────────────────────────────────────────┐')
|
|
371
|
+
console.log('│ Type: DatalogProgram → UnderwritingDecisions │')
|
|
372
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
267
373
|
|
|
268
374
|
const datalog = new DatalogProgram()
|
|
269
375
|
|
|
270
|
-
//
|
|
271
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
272
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
273
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
274
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
275
|
-
|
|
276
|
-
//
|
|
277
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
278
|
-
datalog.addFact(JSON.stringify({ predicate: '
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
//
|
|
283
|
-
// decline(Bus) :- business(Bus, _, LR), LR > 0.70
|
|
376
|
+
// Facts based on account characteristics - using correct JSON API
|
|
377
|
+
datalog.addFact(JSON.stringify({ predicate: 'account', terms: ['BUS001'] }))
|
|
378
|
+
datalog.addFact(JSON.stringify({ predicate: 'account', terms: ['BUS002'] }))
|
|
379
|
+
datalog.addFact(JSON.stringify({ predicate: 'account', terms: ['BUS003'] }))
|
|
380
|
+
datalog.addFact(JSON.stringify({ predicate: 'account', terms: ['BUS004'] }))
|
|
381
|
+
datalog.addFact(JSON.stringify({ predicate: 'low_loss_ratio', terms: ['BUS002'] })) // < 0.30
|
|
382
|
+
datalog.addFact(JSON.stringify({ predicate: 'low_loss_ratio', terms: ['BUS004'] })) // < 0.30
|
|
383
|
+
datalog.addFact(JSON.stringify({ predicate: 'high_loss_ratio', terms: ['BUS003'] })) // > 0.70
|
|
384
|
+
datalog.addFact(JSON.stringify({ predicate: 'established', terms: ['BUS001'] })) // > 10 years
|
|
385
|
+
datalog.addFact(JSON.stringify({ predicate: 'established', terms: ['BUS004'] }))
|
|
386
|
+
datalog.addFact(JSON.stringify({ predicate: 'high_risk_class', terms: ['BUS003'] })) // transportation
|
|
387
|
+
|
|
388
|
+
// NAIC-informed underwriting rules - using correct JSON API
|
|
284
389
|
datalog.addRule(JSON.stringify({
|
|
285
|
-
head: { predicate: '
|
|
390
|
+
head: { predicate: 'auto_approve', terms: ['?X'] },
|
|
286
391
|
body: [
|
|
287
|
-
{ predicate: '
|
|
288
|
-
{ predicate: '
|
|
392
|
+
{ predicate: 'account', terms: ['?X'] },
|
|
393
|
+
{ predicate: 'low_loss_ratio', terms: ['?X'] }
|
|
289
394
|
]
|
|
290
395
|
}))
|
|
291
396
|
|
|
292
|
-
// Rule: Auto-approve low risk
|
|
293
|
-
// approve(Bus) :- business(Bus, tech, _)
|
|
294
397
|
datalog.addRule(JSON.stringify({
|
|
295
|
-
head: { predicate: '
|
|
398
|
+
head: { predicate: 'refer_to_underwriter', terms: ['?X'] },
|
|
296
399
|
body: [
|
|
297
|
-
{ predicate: '
|
|
400
|
+
{ predicate: 'account', terms: ['?X'] },
|
|
401
|
+
{ predicate: 'high_loss_ratio', terms: ['?X'] }
|
|
298
402
|
]
|
|
299
403
|
}))
|
|
300
404
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
const parsed = JSON.parse(result)
|
|
405
|
+
const decisions = datalogInfer(datalog)
|
|
406
|
+
const autoApprove = decisions.filter(d => d.name === 'auto_approve')
|
|
407
|
+
const referToUW = decisions.filter(d => d.name === 'refer_to_underwriter')
|
|
305
408
|
|
|
306
|
-
console.log(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
parsed.referToUW.forEach(r => console.log(` - ${r[0]}: REFER TO UNDERWRITER`))
|
|
312
|
-
}
|
|
409
|
+
console.log(` Inferred decisions: ${decisions.length}`)
|
|
410
|
+
console.log(` Auto-approve: ${autoApprove.length}`)
|
|
411
|
+
autoApprove.forEach(d => console.log(` - ${d.args[0]}`))
|
|
412
|
+
console.log(` Refer to underwriter: ${referToUW.length}`)
|
|
413
|
+
referToUW.forEach(d => console.log(` - ${d.args[0]}`))
|
|
313
414
|
console.log()
|
|
314
415
|
|
|
315
|
-
//
|
|
316
|
-
console.log('
|
|
317
|
-
console.log('
|
|
416
|
+
// Tool 5: Premium Calculation
|
|
417
|
+
console.log('┌─ TOOL: kg.premium.calculate ─────────────────────────────────────────┐')
|
|
418
|
+
console.log('│ Formula: Base × Exposure × Territory × Experience × Loss │')
|
|
419
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
318
420
|
|
|
319
421
|
const premiums = [
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
422
|
+
{
|
|
423
|
+
id: 'BUS001',
|
|
424
|
+
name: 'Acme Manufacturing',
|
|
425
|
+
premium: calculatePremium(12.50, 8500000 / 100, 1.35, 0.45, 15),
|
|
426
|
+
decision: 'STANDARD'
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
id: 'BUS002',
|
|
430
|
+
name: 'TechStart LLC',
|
|
431
|
+
premium: calculatePremium(4.25, 1200000 / 100, 1.25, 0.15, 3),
|
|
432
|
+
decision: 'APPROVED'
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
id: 'BUS003',
|
|
436
|
+
name: 'SafeHaul Logistics',
|
|
437
|
+
premium: calculatePremium(18.75, 4200000 / 100, 1.45, 0.72, 8),
|
|
438
|
+
decision: 'REFER'
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
id: 'BUS004',
|
|
442
|
+
name: 'Downtown Restaurant',
|
|
443
|
+
premium: calculatePremium(8.90, 3100000 / 100, 1.30, 0.28, 12),
|
|
444
|
+
decision: 'STANDARD'
|
|
445
|
+
}
|
|
324
446
|
]
|
|
325
447
|
|
|
326
|
-
console.log(' Calculated
|
|
448
|
+
console.log(' Calculated Premiums:')
|
|
327
449
|
premiums.forEach(p => {
|
|
328
|
-
|
|
329
|
-
const decision = parsed.autoApprove?.some(a => a[0] === p.bus) ? 'APPROVED' :
|
|
330
|
-
parsed.referToUW?.some(r => r[0] === p.bus) ? 'REFER' : 'STANDARD'
|
|
331
|
-
console.log(` - ${p.bus}: $${premium.toLocaleString()} (${decision})`)
|
|
450
|
+
console.log(` - ${p.id} (${p.name}): $${p.premium.toLocaleString()} [${p.decision}]`)
|
|
332
451
|
})
|
|
333
452
|
console.log()
|
|
334
453
|
|
|
335
|
-
//
|
|
336
|
-
|
|
454
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
455
|
+
// PHASE 4: Agent LLM Call (if real API key available)
|
|
456
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
457
|
+
|
|
458
|
+
if (MODEL !== 'mock') {
|
|
459
|
+
console.log('┌─ AGENT: agent.call() ─────────────────────────────────────────────┐')
|
|
460
|
+
console.log('│ Natural Language → SPARQL → Execution │')
|
|
461
|
+
console.log('└─────────────────────────────────────────────────────────────────────┘')
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const result = await agent.call('Find all business accounts with loss ratio above 0.5')
|
|
465
|
+
console.log(` LLM Generated SPARQL: ${result.success ? 'SUCCESS' : 'FAILED'}`)
|
|
466
|
+
if (result.sparql) {
|
|
467
|
+
console.log(` Query: ${result.sparql.slice(0, 60)}...`)
|
|
468
|
+
}
|
|
469
|
+
if (result.error) {
|
|
470
|
+
console.log(` Error: ${result.error}`)
|
|
471
|
+
}
|
|
472
|
+
} catch (e) {
|
|
473
|
+
console.log(` Agent call skipped: ${e.message}`)
|
|
474
|
+
}
|
|
475
|
+
console.log()
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
479
|
+
// PHASE 5: Generate Execution Witness (Proof Theory)
|
|
480
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
481
|
+
|
|
482
|
+
console.log('═'.repeat(78))
|
|
337
483
|
console.log(' UNDERWRITING DECISION REPORT')
|
|
338
|
-
console.log('
|
|
484
|
+
console.log('═'.repeat(78))
|
|
339
485
|
console.log()
|
|
340
486
|
|
|
341
|
-
const totalApproved = parsed.autoApprove?.length || 0
|
|
342
|
-
const totalReferred = parsed.referToUW?.length || 0
|
|
343
|
-
const totalStandard = riskProfiles.length - totalApproved - totalReferred
|
|
344
|
-
|
|
345
487
|
console.log(' SUMMARY:')
|
|
346
|
-
console.log(`
|
|
347
|
-
console.log(` Auto-approved:
|
|
348
|
-
console.log(` Referred to UW:
|
|
349
|
-
console.log(` Standard processing: ${
|
|
488
|
+
console.log(` Accounts processed: ${accounts.length}`)
|
|
489
|
+
console.log(` Auto-approved: ${autoApprove.length}`)
|
|
490
|
+
console.log(` Referred to UW: ${referToUW.length}`)
|
|
491
|
+
console.log(` Standard processing: ${accounts.length - autoApprove.length - referToUW.length}`)
|
|
350
492
|
console.log()
|
|
351
493
|
|
|
352
494
|
console.log(' RISK INDICATORS:')
|
|
353
|
-
console.log(' - BUS003 (SafeHaul): HIGH RISK -
|
|
354
|
-
console.log(' - BUS001 (Acme): MODERATE - hurricane exposure,
|
|
355
|
-
console.log(' - BUS002 (TechStart): LOW - clean
|
|
356
|
-
console.log(' - BUS004 (Downtown): MODERATE - flood exposure, established
|
|
495
|
+
console.log(' - BUS003 (SafeHaul): HIGH RISK - 72% loss ratio, transportation class')
|
|
496
|
+
console.log(' - BUS001 (Acme): MODERATE - hurricane exposure, 45% loss ratio')
|
|
497
|
+
console.log(' - BUS002 (TechStart): LOW - clean history, technology class')
|
|
498
|
+
console.log(' - BUS004 (Downtown): MODERATE - flood exposure, established')
|
|
357
499
|
console.log()
|
|
358
500
|
|
|
359
|
-
|
|
501
|
+
// Execution Witness
|
|
502
|
+
const witness = {
|
|
503
|
+
agent: agent.getName(),
|
|
504
|
+
model: agent.getModel(),
|
|
505
|
+
timestamp: new Date().toISOString(),
|
|
506
|
+
tools: [
|
|
507
|
+
{ name: 'kg.sparql.query', input: 'accounts', output: accounts.length },
|
|
508
|
+
{ name: 'kg.graphframe.analyze', input: `${riskVertices.length} nodes`, output: 'pagerank_complete' },
|
|
509
|
+
{ name: 'kg.embeddings.similar', input: 'BUS003', output: similarAccounts.length },
|
|
510
|
+
{ name: 'kg.datalog.infer', input: '10 facts, 2 rules', output: decisions.length },
|
|
511
|
+
{ name: 'kg.premium.calculate', input: '4 accounts', output: premiums.length }
|
|
512
|
+
],
|
|
513
|
+
decisions: {
|
|
514
|
+
autoApprove: autoApprove.map(d => d.args[0]),
|
|
515
|
+
referToUW: referToUW.map(d => d.args[0])
|
|
516
|
+
},
|
|
517
|
+
premiums: premiums.map(p => ({ id: p.id, premium: p.premium, decision: p.decision })),
|
|
518
|
+
trace: agent.getTrace()
|
|
519
|
+
}
|
|
360
520
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
521
|
+
// Generate deterministic hash
|
|
522
|
+
const hashInput = JSON.stringify(witness.decisions) + JSON.stringify(witness.premiums)
|
|
523
|
+
let hash = 0
|
|
524
|
+
for (let i = 0; i < hashInput.length; i++) {
|
|
525
|
+
hash = ((hash << 5) - hash) + hashInput.charCodeAt(i)
|
|
526
|
+
hash |= 0
|
|
366
527
|
}
|
|
528
|
+
witness.hash = 'sha256:' + Math.abs(hash).toString(16).padStart(16, '0')
|
|
529
|
+
|
|
530
|
+
console.log(' EXECUTION WITNESS (for audit):')
|
|
531
|
+
console.log(` Hash: ${witness.hash}`)
|
|
532
|
+
console.log(` Agent: ${witness.agent}`)
|
|
533
|
+
console.log(` Model: ${witness.model}`)
|
|
534
|
+
console.log(` Tools executed: ${witness.tools.length}`)
|
|
535
|
+
console.log()
|
|
536
|
+
console.log('═'.repeat(78))
|
|
537
|
+
console.log()
|
|
538
|
+
console.log('HyperMind Agent completed successfully.')
|
|
539
|
+
console.log()
|
|
540
|
+
console.log('Full witness:', JSON.stringify(witness, null, 2))
|
|
367
541
|
}
|
|
368
542
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
543
|
+
/**
|
|
544
|
+
* Generate a risk embedding vector based on account characteristics
|
|
545
|
+
*/
|
|
546
|
+
function generateRiskEmbedding(industry, lossRatio, yearsInBusiness, territoryMod) {
|
|
547
|
+
const embedding = new Float32Array(384)
|
|
548
|
+
|
|
549
|
+
// Industry encoding
|
|
550
|
+
const industries = {
|
|
551
|
+
manufacturing: 0.5,
|
|
552
|
+
technology: 0.2,
|
|
553
|
+
transportation: 0.8,
|
|
554
|
+
hospitality: 0.4
|
|
555
|
+
}
|
|
556
|
+
embedding[0] = industries[industry] || 0.5
|
|
557
|
+
|
|
558
|
+
// Loss ratio (already 0-1)
|
|
559
|
+
embedding[1] = lossRatio
|
|
560
|
+
|
|
561
|
+
// Years normalized (0-30 range)
|
|
562
|
+
embedding[2] = Math.min(yearsInBusiness / 30, 1.0)
|
|
563
|
+
|
|
564
|
+
// Territory risk
|
|
565
|
+
embedding[3] = (territoryMod - 1.0) * 2 // Normalize 1.0-1.5 to 0-1
|
|
566
|
+
|
|
567
|
+
// Fill rest with deterministic values
|
|
568
|
+
for (let i = 4; i < 384; i++) {
|
|
569
|
+
embedding[i] = ((embedding[0] * embedding[1] * (i + 1)) % 1)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return Array.from(embedding)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Run
|
|
576
|
+
main().catch(console.error)
|