To Every Agent Its Own Database
A Working Reference Architecture for Agent-Native Analytical Exchange
Everyone is trying to make the data warehouse ready for agents. I think that starts from the wrong end of the architecture, so I inverted the problem and built the opposite in working code. I’ve been sitting on this for a bit, as I’ve been hyper-focused on my book (and course) on Mixed Model Arts. But I figured I’ll give a taste of a side quest I’m actively working on. More to come soon.
A couple of months ago at AI Council, my friend Hannes Mühleisen, co-creator of DuckDB, unveiled Quack, DuckDB’s client-server protocol. A week or so later, he gave a lunch-and-learn to my community and went deeper into what Quack could do.
The next morning, I woke up at 5 a.m. with an idea I had probably been working through in my sleep. I recall a hazy dream of me and my friends Bill Inmon, Zhamak Dehghani, and Hannes hanging out. Their influence on my idea will become more obvious as you read on.
Most companies are preparing their data platforms for agents by adding larger clusters, more replicas, faster semantic layers, stronger workload controls, and new interfaces around the same central analytical systems. That may work, especially because modern warehouses already provide admission control, workload isolation, resource groups, and quotas. But it still assumes that an agent should behave like a very fast analyst connecting to a shared platform.
I wanted to test a different model. Instead of routing every agent through the same analytical plane, I gave each agent its own embedded analytical engine and allowed agents to exchange data products directly.
What started as a prototype using DuckDB, DuckLake, Quack, and Malloy led to a broader question: what kinds of contracts are required when agents exchange analytical data without relying on one central semantic and computational authority?
The Warehouse Was Designed Around Different Workload Assumptions
My good friend Bill Inmon, widely known as the father of the data warehouse, helped formalize and popularize the architecture when organizations needed to separate operational workloads from integrated analytical workloads. The architecture obviously worked, with data warehouses becoming the central place where organizations could integrate, historize, govern, and query data from many operational systems. The term is now so commonplace that it is easy to forget that the design was once controversial (Bill has some real war stories here).
Much of the traditional warehouse (and lakehouse) operating model has been organized around human-paced analytical consumption: scheduled reports, dashboards, exploratory queries, and downstream processes that people eventually inspect or govern.
A human may run a query, inspect the result, and notice that something looks wrong. If revenue drops 40% overnight, someone will often stop and ask whether the business changed or whether the data is broken. Humans also carry implicit context from meetings, documents, Slack conversations, spreadsheets, and prior analysis.
Agents create a different workload. They can be numerous, highly concurrent, and prone to fan-out. One request may trigger several agents, each of which creates additional work. Their outputs can be passed into downstream computations within milliseconds, leaving little time for anyone to inspect intermediate results.
Agents also cannot be expected to infer freshness, semantic intent, or abnormality unless those requirements are represented explicitly. A human analyst may know that “revenue” means revenue net of refunds because someone from accounting clarified it during a meeting. An agent may have access to portions of that context, but it cannot be relied upon to recover and apply the same institutional knowledge consistently.
This creates several architectural pressures:
higher query concurrency,
speculative and redundant work,
rapid chaining of intermediate results,
faster propagation of errors,
and stronger requirements for machine-checkable semantics, provenance, freshness, and trust.
A centralized system can be scaled to accommodate much of this. However, without strict workload isolation, speculative agent traffic can interfere with other users. Semantic definitions can drift from their implementations, and governance processes designed around human review may not operate at the same speed as autonomous data exchanges.
Last year’s Supporting our AI Overlords paper describes several of these workload characteristics, including high throughput, heterogeneity, redundancy, and steerability. My architecture is not the design proposed in that paper, but it responds to a similar change in workload assumptions.
Instead of concentrating more agent activity around a shared analytical system, I tested what happens when analytical state and computation move toward the agents.
Every Agent Gets an Embedded Database
In the prototype (to be posted soon), each agent gets its own embedded DuckDB instance. I call an agent in this system a peer.
A peer has its own analytical engine, a local cache, access to the mesh, and optionally a direct connection to a system of record. Most peers do not communicate directly with the warehouse. They retrieve immutable analytical slices from other peers.
Here’s a Peer in Python:
class Peer:
“”“An agent with a local analytical engine, a slice cache, and access
to the mesh. Only designated origin peers can access a source system
directly.
“”“
def __init__(self, peer_id, mesh, warehouse=None, token=None, ...):
self.id = peer_id
self.mesh = mesh
self.warehouse = warehouse
self.cache = {}
# Each peer owns an embedded DuckDB instance. Quack exposes that
# instance over a loopback TCP port for peer-to-peer communication.
self.con = duckdb.connect()Under the prototype transport, every active peer exposes its DuckDB instance over a real loopback TCP port using Quack. You can watch the ports appear with:
lsof -i -P -n | grep pythonWhen one peer requests data from another, the request travels over a real socket between separate DuckDB processes. There is no mandatory central data server through which every exchange must pass. Each holder serves the slices it owns.
The system still has a control plane. A directory records which peer currently holds each slice. Decentralizing the data path does not eliminate the need for discovery, routing, authorization, or freshness coordination.
This changes the role of the agent. It is no longer only a client of a data platform. It can hold analytical data, serve it to peers, and derive new products from what it holds.
In Data Mesh terms, ownership becomes more concrete. A domain does not merely “own” a schema inside a shared warehouse. It can operate an engine that physically holds and serves its products.
The agents themselves can remain disposable. They spawn, perform work, hand off a durable result, and get reaped. Their DuckDB processes and ports disappear, while the products they created remain in DuckLake, Parquet, or another durable holder.
This raises an immediate consistency problem. If no central platform enforces one canonical representation, many agents can end up holding subtly different versions of the same business concept. The prototype addresses this by making the unit of exchange immutable and semantically identified.
Agents Exchange Immutable Slices
Agents do not exchange an unqualified notion of “the latest.” The term is mutable: it may resolve to one snapshot now and another a moment later. Any system that supports “newest” still needs freshness rules, registry consistency, monotonicity guarantees, and mechanisms for detecting stale discovery results.
The exchanged data, however, can be immutable.
The mesh exchanges analytical slices pinned to a particular data state. A useful analogy is the distinction between a live dashboard and a screenshot. A live dashboard can change while it is being read. A screenshot represents one fixed state.
The prototype identifies each slice using a reference containing:
the catalog,
the dataset,
the DuckLake snapshot,
the semantic-contract identity,
and a digest of the materialized slice or its canonical manifest.
@dataclass(frozen=True)
class SliceRef:
“”“Identity for a frozen analytical slice and the contract under
which it is interpreted.
“”“
catalog_id: str
dataset: str
snapshot_id: int
contract_digest: str
slice_digest: strThese fields identify different parts of the product.
catalog_id, dataset, and snapshot_id identify the DuckLake database state from which the slice was produced. A DuckLake snapshot ID is a commit identifier within a particular catalog. It is not a content hash, and one snapshot may contain changes to several tables.
slice_digest identifies the materialized content or canonical manifest of the slice. This is what allows the system to identify exact content rather than only a database state.
contract_digest identifies the semantic artifact under which the data should be interpreted.
The reference therefore identifies a dataset from a particular catalog state, materialized as a particular piece of content, under a particular semantic contract.
When the underlying data changes, the system creates a new reference rather than mutating the existing slice. Historical references remain resolvable only while the corresponding DuckLake metadata and physical files are retained. Immutability does not eliminate retention policies, garbage collection, corruption, or object-store failure.
It does remove one important source of ambiguity. Once a peer receives a pinned SliceRef, the content associated with that reference should not change. The peer does not have to wonder whether a cached copy of that particular slice has become stale.
This does not eliminate all cache-coherence problems. Mutable aliases such as newest, routing records, authorization state, revocations, and holder availability remain control-plane concerns. The narrower benefit is that pinned analytical content no longer requires invalidation or reconciliation.
The contract digest handles a different problem: identifying the meaning attached to the content.
The Contract Has Three Layers
Most data-contract work has focused on producers. A producer publishes a schema, describes the columns, and promises not to change them silently.
That is necessary, but it leaves the consumer’s assumptions largely implicit. Those assumptions often live in queries, dashboards, documentation, or the memory of the analyst who originally built the metric.
An agent needs those assumptions represented as machine-checkable artifacts. The prototype therefore uses three contract layers:
a semantic contract,
a structural compatibility relation,
and a consumer intent contract.
1. The Semantic Contract
The producer’s semantic contract is represented as a Malloy model. It defines typed sources, measures, and governed queries:
source: orders is duckdb.table(’orders.parquet’) extend {
primary_key: order_id
measure: order_count is count()
measure: gross_revenue is sum(qty * unit_price)
}
query: total_revenue is orders -> {
aggregate: gross_revenue
}Malloy compiles semantic definitions and queries into SQL that DuckDB can execute.
The contract is hashed and included in the slice reference:
def digest_of(content: bytes) -> str:
return hashlib.sha256(content).hexdigest()The full digest is the canonical identity.
An earlier version of the prototype also derived a signed 63-bit integer from the SHA-256 digest for compact indexing. That is acceptable as a convenience identifier, but it should not be treated as the cryptographic identity because truncated hashes eventually collide at sufficient scale.
Including the semantic artifact in the reference means that two agents using different contract files refer to differently named slices.
For example, changing:
sum(qty * unit_price)to:
sum(qty * unit_price) * 0.9changes the contract digest and therefore changes the slice reference. The semantic disagreement becomes visible when the agents attempt to exchange or resolve the slice instead of remaining hidden inside downstream computations.
A hash does not prove that every relevant semantic difference has been captured. Two agents can still interpret the same model differently if they run different Malloy or DuckDB versions, use different runtime settings, or depend on different external artifacts.
A production semantic identity should therefore include execution-relevant context:
semantic_identity = hash(
canonical_contract,
malloy_version,
duckdb_version,
dialect,
runtime_settings,
dependency_digests,
)The claim is not that a hash proves meaning. The claim is that the explicit semantic artifact becomes part of the product’s identity.
For governed Malloy queries, the same model can define both the semantic meaning and the executable computation. This reduces the risk of documentation drifting away from a separately maintained SQL implementation.
That guarantee does not automatically extend to arbitrary SQL sent directly to a peer. Such computations sit outside the governed Malloy contract unless they receive their own contract identity.
2. The Compatibility Relation
A content hash only tells the system whether two artifacts are byte-for-byte identical. It cannot determine whether two different artifacts are compatible.
A formatting change can alter a hash without altering the meaning:
sum(qty * unit_price)and:
sum(qty*unit_price)may be equivalent despite producing different file hashes.
More importantly, an additive change and a breaking redefinition both appear merely as different hashes.
The prototype therefore parses each Malloy model into a normalized ContractSpec. The specification records the measures, queries, sources, dependencies, and execution context on which consumers may rely.
Two specifications can then be compared:
class Relation(Enum):
IDENTICAL = “identical”
ADDITIVE = “additive”
BREAKING = “breaking”
UNRELATED = “unrelated”The relations have the following meanings:
IDENTICAL: the normalized public definitions and relevant dependencies match.
ADDITIVE: the second contract adds declarations while preserving the transitive definitions and execution context required by the first.
BREAKING: a depended-upon definition, source assumption, dependency, or execution behavior has changed.
UNRELATED: the contracts do not describe the same source or cannot be compared meaningfully.
BREAKING is the most important relation.
Suppose two agents use a measure called gross_revenue, but one defines it before refunds and the other defines it after refunds. Both computations may be internally valid, but their outputs should not be treated as equivalent.
The structural comparison can surface the conflict directly:
BREAKING: redefined measure [’gross_revenue’]This does not mean that compatible contracts automatically make two results safe to combine. The underlying slices may overlap, use different grains, cover different time periods, use different currencies, or represent different populations.
Contract compatibility answers whether one contract preserves the definitions required by another. Separate composition rules must determine whether two particular slices or results can be combined. Those rules need to account for grain, partitions, time coverage, units, dimensional domains, overlap, and aggregation behavior.
3. The Intent Contract
The third layer represents the consumer’s requirements.
An agent should not have to request a snapshot ID directly because it may not know which snapshots are available. Instead, it states the dataset it wants, the semantic definitions it requires, and its freshness preference.
@dataclass(frozen=True)
class Intent:
“”“The dataset and semantic assumptions the consumer is prepared
to operate under.
“”“
dataset: str
require: ContractSpec
freshness: str = “newest”The resolver converts this mutable request into one concrete immutable reference:
Intent
-> resolve against catalog
-> pinned SliceRef
For example, resolve(intent, catalog) may choose the newest available snapshot whose contract preserves all definitions required by the consumer.
If every available contract changes a measure or dependency on which the consumer relies, resolution fails:
if not candidates:
if saw_breaking:
raise Unresolvable(
f”{intent.dataset!r} has snapshots, but every candidate “
“changes a definition required by the consumer”
)The consumer therefore declares what it is prepared to accept, and the exchange checks available products against that declaration.
The word newest remains a mutable control-plane concept. The catalog and resolver must define freshness ordering, replay protection, monotonic reads, and how signed entries are compared.
Once resolution returns a SliceRef, however, the consumer operates against a pinned product rather than an alias whose meaning may change during execution.
Derived Data Cannot Promote Itself to Source Authority
A decentralized system must distinguish between authoritative source data and data derived by an agent.
The prototype assigns every slice a provenance tier:
class Tier(Enum):
WAREHOUSE = “warehouse”
DERIVED = “derived”In a multi-domain implementation, WAREHOUSE would probably be renamed AUTHORITATIVE_ORIGIN, because the authoritative source does not have to be a warehouse.
The distinction is between a slice attested by a designated system-of-record principal and a slice computed by an ordinary peer.
A derived slice receives DERIVED tier when it is created. It cannot satisfy a request that explicitly requires authoritative source data:
def find(self, peer_id, ref, require=None):
derived_seen = False
for holder in self.directory.get(ref.dataset, []):
s = self.transport.fetch(holder, ref)
if require is Tier.WAREHOUSE and s.tier is not Tier.WAREHOUSE:
derived_seen = True
continue
return s
if derived_seen:
raise PermissionError(”refusing to launder provenance”)The tier travels with the signed artifact. Keeping it only in a mutable side table would make it easier to lose or rewrite.
A single tier flag is not enough to explain where a result came from, especially after several derivation steps. Each slice therefore carries a lineage DAG.
Every lineage node contains:
its inputs,
the declared operation,
the producing principal,
the tier,
a digest,
and a signature or attestation.
Each node’s digest covers its metadata and the digests of its inputs. Altering an earlier node changes the digests above it.
Verification walks the graph:
def verify(node, ring) -> Proof:
def check(n, depth):
if not ring.verify_sig(n.principal, n.digest(), n.sig):
return False, depth, f”bad signature on {n.op}”
if not n.inputs:
if n.tier is not Tier.WAREHOUSE:
return False, depth, (
f”root {n.ref.dataset} is not authoritative”
)
if not ring.is_warehouse(n.principal):
return False, depth, (
f”root {n.ref.dataset} signed by “
f”{n.principal!r}, not an authorized origin”
)This verifies chain integrity and checks whether the lineage roots satisfy the configured trust policy. It does not prove that the underlying data is correct, that the declared operation was executed faithfully, or that no relevant inputs were omitted.
A signature proves that a principal made an attestation. It does not prove the truth of the attested statement. The current prototype signs lineage using HMAC and an in-process keyring. That demonstrates the mechanics but does not establish production-grade principal identity. If verifiers possess the same symmetric signing keys as producers, they may also be able to forge attestations.
A production implementation would require asymmetric signatures, protected signing identities, rotation, revocation, and independently verifiable trust roots. The prototype therefore demonstrates how a lineage and trust policy can be represented and checked. It does not yet provide a complete production identity system.
There is also a fair Data Mesh objection: if one warehouse is the only origin of trusted data, then the architecture may appear to preserve central authority under a peer-to-peer transport. The authority in this design is scoped by product. One warehouse may be authoritative for an orders dataset, while another domain system is authoritative for customer support or product telemetry.
Peers may be symmetric in their ability to hold and serve data, but they are not equal in provenance authority. Only designated principals can originate authoritative roots for particular products. Derived products remain usable and shareable. They simply cannot claim a higher provenance tier without a valid attestation from an authorized origin.
Push Computation Toward the Data
Agent swarms cannot afford to transfer a large slice when the required result is a single aggregate.
The prototype therefore supports several consumption patterns.
A bulk fetch transfers the slice to the requester. This is useful when the requester intends to perform substantial local derivation.
A query operation pushes computation to the holder:
def query(self, ref: SliceRef, sql: str):
for holder in self.mesh.directory.get(ref.dataset, []):
result = self.mesh.transport.query(holder, ref, sql)
if result is not None:
return resultAn aggregate query may return one row, while a filtering query returns only the matching rows.
The transport also supports metadata-only operations:
prove returns the lineage DAG,
contract returns the normalized contract specification.
The same slice can therefore be consumed as a full dataset, a filtered result, an aggregate, a contract, or a provenance proof.
Quack provides the remote DuckDB transport and execution channel. The slice, discovery, contract, lineage, and trust operations are protocols implemented by the prototype on top of Quack.
Remote query pushdown introduces a serious security boundary. A production system should not give an untrusted peer unrestricted SQL access to another peer’s DuckDB connection.
Arbitrary SQL may be able to access unrelated tables, inspect local files, invoke extensions, mutate state, or consume unbounded CPU and memory.
A production implementation would need:
a read-only isolated process or connection,
a capability-scoped view exposing only the referenced slice,
a restricted query grammar or validated logical plan,
CPU and memory limits,
statement timeouts,
row limits,
extension restrictions,
and controls over file and network access.
There is also a verification gap. A signed input slice does not prove that a remote peer returned the correct aggregate. A malicious or compromised holder can return an incorrect result even when the input provenance is valid.
Addressing that requires another mechanism, such as reproducible local execution, replicated computation, trusted execution environments, signed runtime attestations, or cryptographic proof systems.
The current prototype authenticates inputs and declared lineage. It does not yet provide verifiable remote computation. I plan to add these to the project over time. If I open it up to the public, happy to accept pull requests.
Where the LLM Belongs
The architecture is designed for agents, but the core trust decisions are deterministic.
Contract hashing, structural comparison, intent resolution, snapshot pinning, digest verification, signature checking, and trust-policy enforcement must produce the same result for every peer given the same inputs.
Using an LLM to decide whether a contract change is IDENTICAL, ADDITIVE, or BREAKING would make the compatibility boundary probabilistic. Two models could reach different conclusions about whether two metrics are safe to use together.
An LLM is better suited to translating an ambiguous user request into a structured intent.
A user might ask for:
The current revenue picture, net of refunds.
A model can propose a dataset, required measure definitions, and freshness constraint:
The model proposes the intent. The deterministic core validates and resolves it. If the model invents a measure or asks for a contract that does not exist, resolution should fail. The model cannot declare a slice trustworthy merely by producing a plausible explanation.
The boundary is straightforward: an LLM may propose intent and semantics, but trust decisions must be expressed as deterministic artifacts and checked mechanically before other systems depend on them.
What Is Real and What Is Stubbed
The prototype contains real implementations of the core mechanisms:
DuckLake is the system of record.
Snapshot metadata comes from DuckLake.
Data is stored in Parquet files on disk.
Quack provides real DuckDB-to-DuckDB communication over TCP.
Peers bind real loopback ports.
Malloy models compile governed queries into SQL.
Contract identities come from real content hashes.
Lineage graphs contain real digests.
Verification recomputes those digests.
The system checks root trust and provenance tiers.
Peers exchange data and metadata over real sockets.
Several components remain deliberately simplified:
peer tokens are generated in-process rather than issued through OIDC or a production identity proxy,
lineage uses HMAC rather than asymmetric identities,
the holder directory is a Python dictionary rather than a replicated registry,
remote SQL is not yet fully sandboxed,
and remote query results are not cryptographically verifiable.
The prototype is sufficient to test the architecture and expose its difficult boundaries. It is not a production-grade distributed trust system.
Most of the Mechanisms Already Exist
The individual mechanisms have substantial prior art.
Immutable snapshots exist in Iceberg, Delta Lake, DuckLake, and other lakehouse systems. Content-addressed artifacts appear in IPFS, Git-like systems, package managers, reproducible-build systems, lakeFS, Nessie, and Dolt.
Compatibility classes are common in schema registries and API versioning. Signed provenance graphs appear in in-toto, SLSA, and software supply-chain tooling. Semantic models exist in Malloy, dbt, Cube, and other semantic-layer systems. Compute pushdown is a basic feature of distributed databases and query engines.
Systems have also combined artifact identity with schema, code, execution environment, and dependency metadata in several contexts.
The contribution here is the combination:
An immutable analytical slice jointly identified by a pinned data state, a materialized-content digest, and an executable semantic contract; exchanged peer-to-peer; checked for structural compatibility; and accompanied by authenticated lineage that prevents ordinary derived peers from claiming source authority.
I have not personally encountered this exact combination implemented as a peer-to-peer analytical exchange model. That is different from claiming that none of the ideas, or any similar assembly, has existed before.
There is also a separate body of work around mesh memory and collaborative memory for agents. Those systems exchange reasoning traces, observations, plans, and cognitive state. This system exchanges analytical data and the artifacts required to interpret it. The vocabulary overlaps, but the systems exchange different things.
Where I Would Challenge My Design
I’m sure there are plenty of objections. Here are three anticipated objections that deserve particular attention.
“You Moved the Bottleneck to the Registry”
The prototype decentralizes data and compute but retains a directory recording which peer holds each slice. That directory is a centralized or partially centralized control-plane component. It does not serve analytical bytes or execute analytical workloads. Its job is to resolve immutable references to holders, which makes it closer to a nameserver than a warehouse. That does not make the registry trivial. DNS scales through hierarchy, delegation, replication, caching, TTLs, and a large amount of operational machinery.
A compromised registry can also do more than withhold the newest entry. Unless signed entries bind the complete object identity, holder identity, validity interval, sequence information, and trust policy, the registry may replay old references, suppress revocations, equivocate between clients, return different valid histories, or route requests to malicious or unavailable holders.
Signatures can prevent undetected substitution, but they do not automatically provide freshness or availability. A production registry would require some combination of signed sequence numbers, validity windows, replicated logs, consistency checks, quorum reads, transparency mechanisms, and holder-identity binding.
The registry is not a second analytical platform, but it remains a distributed-systems component that must be designed and operated carefully.
“This Requires Your Exact Stack”
The reference implementation uses Malloy, DuckDB, DuckLake, and Quack, but the architecture does not depend on those particular products.
Equivalent components could include Iceberg or Delta for versioned storage, dbt or Cube for semantic definitions, Arrow Flight or another database protocol for exchange, and in-toto or SLSA-style attestations for provenance.
I used this stack because it made the entire system small enough to build and inspect end-to-end. It is a reference implementation rather than a required platform.
“A Five-Hop Agent Failure Will Be Impossible to Debug”
The design makes one important debugging question more tractable: where did a particular artifact come from?
Each slice carries its lineage rather than depending entirely on runtime logs reconstructed after the fact. That lineage remains available after the peer that produced the slice has been removed.
A debugger can inspect the input references, declared operations, producing principals, contract identities, snapshots, and provenance attestations.
That does not eliminate the need for operational observability. A production deployment still needs distributed tracing, metrics, resource accounting, admission control, revocation, failure handling, audit logs, anomaly detection, query isolation, and fleet management. The artifact-level provenance is useful groundwork, but the operational layer remains substantial.
The Architectural Claim
The central warehouse remains useful, and many organizations will continue extending it to support agent workloads. This prototype tests a different option: moving some analytical state and computation into agent-local engines and allowing those engines to exchange governed, immutable products directly.
The design gives every active agent an embedded analytical engine. Agents exchange pinned slices rather than relying only on mutable aliases such as “latest.” The semantic contract becomes part of the product identity, consumers declare the meanings they are prepared to accept, and provenance travels with the artifact.
This approach does not eliminate consistency, identity, security, freshness, or observability. It changes where those concerns are handled. Content identity belongs in immutable references. Semantic compatibility belongs in deterministic contract comparison. Source authority belongs in product-scoped trust policies and signatures. Discovery and freshness remain control-plane responsibilities.
The next step is to test the model under more realistic execution patterns: agents recruiting specialists, exchanging SliceRef objects over real sockets, deriving new products, and terminating while their outputs and lineage remain available.
Lastly, I did this to explore the feasibility of this idea. It’s a personal side quest. The prototype works, but it’s far from enterprise-ready. I think there’s some validity to it, but there’s obviously still more to do, so stay tuned.
The code and video demo will be released soon, coinciding with my upcoming work. This architecture is an exploration, but I suspect the industry’s shift toward agent-native data will make peer-to-peer analytical exchange a necessity rather than an alternative. Stay tuned.
*AI was used to help write this article, specifically the demo code and notes related to the code and gory technical details. The rest is written by me. - Joe Reis




Good discussion about this article over at LinkedIn: https://www.linkedin.com/posts/josephreis_to-every-agent-its-own-database-share-7485394639223480320-gGkj/?utm_source=share&utm_medium=member_ios&rcm=ACoAAAajovEBZaTvKT0qIqHq9ItYb5C1EMVsVSY
Giving each agent its own embedded engine instead of routing through one shared warehouse is a clever inversion of the usual approach.
I've been mapping out agent architecture patterns like this in a repo of runnable examples, worth a look.
https://github.com/hardness1020/awesome-agent-architecture