.NET API
CompiledAgentCache
Tracon.Core.dllCaches compiled agents keyed by
(tenant, name, definition fingerprint, dependency fingerprint, culture),
holding one entry per (tenant, name, definition fingerprint, culture).
public sealed class CompiledAgentCache- Inheritance
- object ← CompiledAgentCache
- Inherited Members
- object.GetType(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
Section titled “Remarks”The key measures CONTENT, not a version number. An earlier
design keyed the entry by the definition’s version and relied on “a save
increments the version, so the entry naturally goes stale”. That reasoning
does not hold for a DELETE followed by a CREATE of the same name: a store
that restarts numbering (the built-in ones did, until they kept the numbering) makes the
new definition version 1 again, and the
deleted definition’s compiled agent kept answering every run - the database
read correctly while the run did not. Measured on a real host.
The version component was therefore replaced by a fingerprint of the definition’s own serialized content (AgentDefinitionCompiler.CreateDefinitionFingerprint). Two entries share a compiled agent only when the definitions that produced them are byte-identical, which is exactly when sharing is correct. No explicit invalidation is needed, and none exists: an entry that can no longer be reached is simply never read again.
The same rule covers what the agent depends on outside its definition. The dependency fingerprint measures skills, callable sub-agents, a shared instructions block, the tenant’s egress policy, and the connection each remote (MCP) tool is bound to: a policy saved after an agent was cached changes the key, and the next resolve compiles again under that policy; a server definition changed or deleted after an agent was cached changes the key too, and the next resolve binds the live connection or fails the way an uncached compile does.
The cache is bounded by definitions, not by dependency changes.
The dependency fingerprint changes without any operator action: a remote MCP
server that edits a tool’s description or schema changes it at the next refresh.
Each (tenant, name, definition fingerprint, culture) therefore holds ONE
entry, and a compile under a new dependency fingerprint replaces it; the replaced
agent is never read again. The definition fingerprint stays a separate slot: two
versions of one agent served side by side (an experiment’s variants) must not
evict each other on every run. A new definition version still adds a slot, which
only an operator’s save can produce.
The tenant component of the key is REQUIRED. Agent names are unique
only within a tenant (see SqlAgentDefinitionStore) - it is common for
two different tenants to have a definition with the same name (e.g.
"support"), byte-identical content, and the same dependency
fingerprint (an empty string for both if they use no skills/callable
agents). If the tenant is not included in the key,
resolution for the second tenant returns the first tenant’s compiled agent
(instructions, tool bindings - including the tenant identity bound into
e.g. search_knowledge): tenant isolation broken outright.
Properties
Section titled “Properties”Number of entries in the cache: one per tenant, agent name, definition, and culture.
public int Count { get; }Property Value
Section titled “Property Value”Methods
Section titled “Methods”Clear()
Section titled “ Clear()”Empties the cache entirely.
public void Clear()CombineFingerprints(string, string)
Section titled “ CombineFingerprints(string, string)”Combines multiple dependency fingerprints into a single key component.
public static string CombineFingerprints(string first, string second)Parameters
Section titled “Parameters”first string
First fingerprint.
second string
Second fingerprint.
Returns
Section titled “Returns”The combined fingerprint.
Remarks
Section titled “Remarks”A separator is required: fingerprints may not have a fixed length, and a direct concatenation would let two different pairs produce the same string.
GetOrAdd(string, string, string, Func<AIAgent>)
Section titled “ GetOrAdd(string, string, string, Func<AIAgent>)”Retrieves the agent from the cache; if absent, produces it with
factory and adds it.
public AIAgent GetOrAdd(string tenantId, string name, string definitionFingerprint, Func<AIAgent> factory)Parameters
Section titled “Parameters”tenantId string
Identifier of the tenant requesting resolution.
name string
Agent name.
definitionFingerprint string
Fingerprint of the definition’s own content; see AgentDefinitionCompiler.CreateDefinitionFingerprint.
factory Func<AIAgent>
Producer called when absent from the cache.
Returns
Section titled “Returns”AIAgent
The compiled agent.
Exceptions
Section titled “Exceptions”One of the parameters is null.
GetOrAdd(string, string, string, string, Func<AIAgent>)
Section titled “ GetOrAdd(string, string, string, string, Func<AIAgent>)”Retrieves the agent from the cache together with its dependency
fingerprint; if absent, produces it with factory
and adds it.
public AIAgent GetOrAdd(string tenantId, string name, string definitionFingerprint, string dependencyFingerprint, Func<AIAgent> factory)Parameters
Section titled “Parameters”tenantId string
Identifier of the tenant requesting resolution.
name string
Agent name.
definitionFingerprint string
Fingerprint of the definition’s own content; see AgentDefinitionCompiler.CreateDefinitionFingerprint.
dependencyFingerprint string
Current fingerprint of what the compiled agent depends on outside its definition: skills, callable sub-agents, a shared instructions block, the tenant’s egress policy, and the connection each remote tool is bound to. These can change while the definition stays the same, so they need a separate key component.
factory Func<AIAgent>
Producer called when absent from the cache.
Returns
Section titled “Returns”AIAgent
The compiled agent.
GetOrAdd(string, string, string, string, string, Func<AIAgent>)
Section titled “ GetOrAdd(string, string, string, string, string, Func<AIAgent>)”Retrieves the agent from the cache together with its dependency fingerprint and
requested culture; if absent, produces it with factory and adds it.
public AIAgent GetOrAdd(string tenantId, string name, string definitionFingerprint, string dependencyFingerprint, string culture, Func<AIAgent> factory)Parameters
Section titled “Parameters”tenantId string
Identifier of the tenant requesting resolution.
name string
Agent name.
definitionFingerprint string
Fingerprint of the definition’s own content; see AgentDefinitionCompiler.CreateDefinitionFingerprint.
dependencyFingerprint string
See CompiledAgentCache.GetOrAdd.
culture string
The culture the definition was compiled with (resolved against
AgentDefinition.InstructionsByCulture).
An empty string when the run requested no culture. Part of the key: two runs of the
same definition in different cultures must not share a compiled agent - the requested
instructions text is baked into AI.ChatOptions at
compile time.
factory Func<AIAgent>
Producer called when absent from the cache.
Returns
Section titled “Returns”AIAgent
The compiled agent.
GetOrAddAsync(string, string, string, string, Func<ValueTask<AIAgent>>)
Section titled “ GetOrAddAsync(string, string, string, string, Func<ValueTask<AIAgent>>)”Retrieves the agent from the cache together with its dependency
fingerprint; if absent, produces it asynchronously with
factory and adds it.
public ValueTask<AIAgent> GetOrAddAsync(string tenantId, string name, string definitionFingerprint, string dependencyFingerprint, Func<ValueTask<AIAgent>> factory)Parameters
Section titled “Parameters”tenantId string
Identifier of the tenant requesting resolution.
name string
Agent name.
definitionFingerprint string
Fingerprint of the definition’s own content; see AgentDefinitionCompiler.CreateDefinitionFingerprint.
dependencyFingerprint string
See CompiledAgentCache.GetOrAdd.
factory Func<ValueTask<AIAgent>>
Producer called when absent from the cache.
Returns
Section titled “Returns”ValueTask<AIAgent>
The compiled agent.
Remarks
Section titled “Remarks”Compiling now needs an async credential lookup on a
cache miss. Concurrent.ConcurrentDictionary has no
native async GetOrAdd; the same “the factory may run more than
once, production is side-effect free, an extra instance is discarded”
guarantee as the sync overload applies here too.
GetOrAddAsync(string, string, string, string, string, Func<ValueTask<AIAgent>>)
Section titled “ GetOrAddAsync(string, string, string, string, string, Func<ValueTask<AIAgent>>)”Retrieves the agent from the cache together with its dependency fingerprint and
requested culture; if absent, produces it asynchronously with
factory and adds it.
public ValueTask<AIAgent> GetOrAddAsync(string tenantId, string name, string definitionFingerprint, string dependencyFingerprint, string culture, Func<ValueTask<AIAgent>> factory)Parameters
Section titled “Parameters”tenantId string
Identifier of the tenant requesting resolution.
name string
Agent name.
definitionFingerprint string
Fingerprint of the definition’s own content; see AgentDefinitionCompiler.CreateDefinitionFingerprint.
dependencyFingerprint string
See CompiledAgentCache.GetOrAdd.
culture string
See CompiledAgentCache.GetOrAdd.
factory Func<ValueTask<AIAgent>>
Producer called when absent from the cache.
Returns
Section titled “Returns”ValueTask<AIAgent>
The compiled agent.