Probabilistic Resolution
When two agent instances have different DIDs, cryptographic resolution cannot help. But they might still be the same logical agent - deployed independently by different teams, or registered before a shared keypair policy was in place.
This is the same problem the resolution engine solves for customer records. "John Doe" in CRM and "Jonathan Doe" in billing have different primary keys, but the engine matches them on name, email, phone, and behavioral signals. Agent resolution works the same way.
Matching signals
The resolution engine evaluates agent records against each other using these signals:
| Signal | Type | Weight | Description |
|---|---|---|---|
agent_name | Fuzzy (Jaro-Winkler) | Medium | "support-agent" vs "support-bot" |
capabilities | Set overlap (Jaccard) | Medium | ["resolve","memorize"] vs ["resolve","memorize","search"] |
root_authority_did | Exact | Strong | Same human delegated authority to both |
description | Fuzzy (Cosine) | Weak | Free-text description similarity |
behavioral_pattern | Custom | Medium | Same types of actions on same entity types |
platform_metadata | Composite | Weak | Shared configuration, environment, org ID |
The strongest single signal is root_authority_did. If the same human (or root agent) delegated authority to two different agent instances, they are very likely the same logical agent deployed across platforms.
How blocking works for agents
Blocking narrows the comparison space - just like with customer records. Instead of comparing every agent against every other agent, the engine groups agents into candidate blocks:
| Blocking Key | Groups by | Example |
|---|---|---|
[root_authority_did] | Same delegating authority | All agents delegated by did:agent:root1... |
[agent_name] | Exact name match | All agents named "support-agent" |
[capabilities] | First 3 sorted capabilities | All agents with ["memorize","resolve","search"] |
Only agents within the same block are scored against each other. An SDR agent is never compared against a data-sync agent because they share no blocking keys.
Configuring an agent identity spec
Agent resolution uses the same YAML spec format as customer resolution. The entity type is agent, and the fields map to agent registry attributes:
api_version: kanoniv/v1
identity_version: "1.0"
entity:
name: agent
sources:
web-registry:
adapter: api
primary_key: agent_did
schema:
agent_did: { type: string }
agent_name: { type: string }
capabilities: { type: array }
root_authority_did: { type: string }
description: { type: string }
platform: { type: string }
slack-registry:
adapter: api
primary_key: agent_did
schema:
agent_did: { type: string }
agent_name: { type: string }
capabilities: { type: array }
root_authority_did: { type: string }
description: { type: string }
platform: { type: string }
rules:
- name: did_exact
type: exact
field: agent_did
weight: 1.0
- name: root_authority_exact
type: exact
field: root_authority_did
weight: 0.4
- name: name_fuzzy
type: similarity
field: agent_name
algorithm: jaro_winkler
threshold: 0.80
weight: 0.3
- name: capabilities_overlap
type: similarity
field: capabilities
algorithm: cosine
threshold: 0.70
weight: 0.2
- name: description_fuzzy
type: similarity
field: description
algorithm: cosine
threshold: 0.60
weight: 0.1
decision:
thresholds:
match: 0.85
review: 0.65
survivorship:
strategy: source_priority
source_order: [web-registry, slack-registry]This spec says: exact DID match is the strongest signal (weight 1.0), followed by shared root authority (0.4), fuzzy name match (0.3), capabilities overlap (0.2), and description similarity (0.1). Pairs scoring above 0.85 merge automatically. Pairs between 0.65 and 0.85 are flagged for human review.
Scoring walkthrough
Consider two agent instances:
Web agent (registered via web channel):
{
"agent_did": "did:agent:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"agent_name": "support-agent",
"capabilities": ["resolve", "memorize", "search"],
"root_authority_did": "did:agent:root_9f8e7d6c5b4a3f2e1d0c9b8a",
"description": "Handles customer support queries across channels"
}Slack agent (registered via Slack channel):
{
"agent_did": "did:agent:f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
"agent_name": "support-bot",
"capabilities": ["resolve", "memorize", "search", "create_task"],
"root_authority_did": "did:agent:root_9f8e7d6c5b4a3f2e1d0c9b8a",
"description": "Customer support bot for Slack workspace"
}Different DIDs, so did_exact scores 0.0. But:
| Rule | Score | Why |
|---|---|---|
did_exact | 0.0 | Different DIDs |
root_authority_exact | 1.0 | Same root authority DID |
name_fuzzy | 0.82 | "support-agent" vs "support-bot" (Jaro-Winkler) |
capabilities_overlap | 0.87 | 3 of 4 capabilities shared (Jaccard-like via cosine) |
description_fuzzy | 0.71 | Both mention "support" and "customer" |
Weighted score:
(0.0 * 1.0 + 1.0 * 0.4 + 0.82 * 0.3 + 0.87 * 0.2 + 0.71 * 0.1)
/ (1.0 + 0.4 + 0.3 + 0.2 + 0.1)
= (0 + 0.4 + 0.246 + 0.174 + 0.071) / 2.0
= 0.891 / 2.0
= 0.446This score is below the match threshold. The DID mismatch pulls it down heavily. But adjust the spec to down-weight did_exact when you expect different DIDs:
rules:
- name: did_exact
type: exact
field: agent_did
weight: 0.0 # DIDs are expected to differ across platforms
- name: root_authority_exact
type: exact
field: root_authority_did
weight: 0.5 # Strongest signal when DIDs differNew weighted score (excluding DID):
(1.0 * 0.5 + 0.82 * 0.3 + 0.87 * 0.2 + 0.71 * 0.1) / (0.5 + 0.3 + 0.2 + 0.1)
= (0.5 + 0.246 + 0.174 + 0.071) / 1.1
= 0.991 / 1.1
= 0.901Score 0.901. Above the 0.85 threshold. Auto-merge.
Resolving agents via the API
Use the standard resolve endpoint. The engine treats agent records identically to customer records:
POST /v1/resolve/realtime
Content-Type: application/json
X-API-Key: kn_live_...
{
"source_name": "slack-registry",
"external_id": "support-bot-slack",
"data": {
"agent_did": "did:agent:f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
"agent_name": "support-bot",
"capabilities": ["resolve", "memorize", "search", "create_task"],
"root_authority_did": "did:agent:root_9f8e7d6c5b4a3f2e1d0c9b8a",
"description": "Customer support bot for Slack workspace"
}
}If the web agent was previously registered, the response links the Slack agent to the same canonical entity:
{
"entity_id": "agt_7f82a1b2-...",
"canonical_data": {
"agent_name": "support-agent",
"capabilities": ["resolve", "memorize", "search", "create_task"],
"root_authority_did": "did:agent:root_9f8e7d6c5b4a3f2e1d0c9b8a",
"platforms": ["web", "slack"]
},
"is_new": false,
"confidence": 0.90
}The canonical name is "support-agent" (from the web source, which has higher survivorship priority). Capabilities are the union of both instances. The platforms array shows both channels.
Match explanations
Every merge decision includes a full explanation, just like customer resolution:
{
"left": "web-registry:support-agent-web",
"right": "slack-registry:support-bot-slack",
"score": 0.90,
"outcome": "match",
"rules": [
{ "name": "root_authority_exact", "score": 1.0, "field": "root_authority_did" },
{ "name": "name_fuzzy", "score": 0.82, "field": "agent_name" },
{ "name": "capabilities_overlap", "score": 0.87, "field": "capabilities" },
{ "name": "description_fuzzy", "score": 0.71, "field": "description" }
]
}You always know why two agents were resolved to the same entity and which signals contributed the most.
Behavioral matching
Beyond static metadata, the engine can match agents based on behavioral patterns:
- Action types: Both agents primarily call
resolveandmemorize- notmergeorsplit - Entity interactions: Both agents interact with customer-type entities in the same tenant
- Temporal patterns: Both agents are active during US business hours
- Decision patterns: Both agents produce similar confidence scores on similar entity types
Behavioral signals are weaker individually but powerful in combination. An agent that resolves customer entities, stores memory entries about lead qualification, and operates during US business hours has a distinctive behavioral fingerprint.
When to use probabilistic vs cryptographic
| Situation | Resolution path |
|---|---|
| You deployed the same agent to multiple channels | Cryptographic - share the keypair |
| Different teams deployed similar agents independently | Probabilistic - score on metadata + behavior |
| Third-party agent needs to merge with your internal agent | Probabilistic - no shared keypair possible |
| Acquired company's agents need to join your identity graph | Probabilistic - different key infrastructure |
| Agent keypair was rotated, old instances still registered | Probabilistic - old DID will not match new DID |
The two paths are complementary. Cryptographic resolution handles the easy cases instantly. Probabilistic resolution handles the hard cases with confidence scores and full auditability.
