AlphaAvatar v0.6.6: event-driven multimodal memory, unified runtimes, and cleaner agent contracts

Hi. At a glance, this looks like a substantial step forward:


My short answers to the five design questions would now be roughly:

Question My current answer
1. One multimodal event representation, or keep modalities separate? I would unify identity, time, provenance, and episode/correlation semantics before trying to unify the actual modality payloads. Keep raw/audio/visual/conversation evidence independently inspectable, then derive a cross-modal episode or Memory representation when useful.
2. Put the full active capability set in context, or retrieve it on demand? I would separate runtime capability truth from what detail the model sees on each turn. For a small stable set, a compact full summary is simplest. For a large/dynamic set, keep a compact always-visible summary and retrieve detailed capability schemas on demand.
3. How much of Stream semantics should become public contract? Probably the observable semantics: consumer identity, position, retention boundary, missed/gap information, reconnect/clear behavior, and meaningful drop states. Buffering/scheduling internals can remain private.
4. Where should model reasoning stop and runtime commitment begin? Models can interpret, associate, summarize, rank, and produce candidates. The runtime should own decisions that become authoritative, durable, permissioned, delivered, or externally consequential. I would also distinguish reversible/retryable commitments from irreversible ones.
5. How much should Memory backends be standardized? Standardize the framework-visible outcomes and guarantees, not each backend’s internal storage mechanics. A common interface should not silently imply stronger consistency/durability than a backend actually provides.

The default route I would take from here is still fairly small:

make the existing semantics explicit
        ↓
preserve lightweight identity/provenance across the important boundaries
        ↓
cover those boundaries with a few deterministic scenarios
        ↓
only then decide which larger representation/protocol/backend abstractions
actually need to be standardized

I tried a couple of small CPU-only synthetic checks against the v0.6.6 code because the current implementation is now concrete enough that some of these questions can be tested without choosing the final architecture.

Two older concerns look materially better now:

  • Stream gaps are visible. A bounded PerceptionStream crossing its retention boundary now exposes first_available_seq, missed_count, and has_gap.
  • Runtime-only observations have a useful provenance receipt. A pathless EnvObservation can still expose an observation_id, source_id, time range, metadata, etc., without requiring the raw frame to be persisted.

That seems like exactly the kind of incremental contract hardening discussed in the v0.6.4 thread.

The two seams that now look most interesting to me are narrower:

  1. “captured / acknowledged” is not necessarily the same thing as “durably memorized”;
  2. Observation-level evidence exists, but the current ENV Memory write path does not yet appear to carry the same source identity all the way into the durable Memory item.

I do not think either observation implies a large redesign. They mostly look like places where a small amount of explicit state/identity could make the eventual contract much easier to test.

1. Multimodal representation: I would separate identity, association, evidence, and projection

I would be a little cautious about treating this as a binary choice between:

one unified multimodal representation

and

completely modality-specific representations

because there are several different things that can be unified independently.

A useful decomposition might be:

A. observation/event identity
B. temporal association / episode membership
C. modality-specific evidence
D. derived cross-modal episode
E. durable Memory representation
F. retrieval projection

I would be relatively aggressive about standardizing A, explicit about B, conservative about discarding C, and leave D–F comparatively flexible until actual retrieval/evaluation results make the trade-off clearer.

A. Common identity/time/provenance

A common envelope could stay very small:

observation_id:
source_id:
modality:
occurred_at:
recorded_at:
session_id:
participant_id:
correlation_id:
provenance:

This does not require audio, frames, transcripts, and annotations to use the same payload schema.

CloudEvents is useful here only as vocabulary: it deliberately separates common context attributes (id, source, type, time, etc.) from domain-specific event data. That is a useful precedent for “common event identity does not imply common payload semantics.”

I would not make AlphaAvatar a CloudEvents implementation just for this; the interesting part is the separation.

B. Association is a different problem from representation

There is also a question that comes before deciding how Memory should represent a multimodal event:

When do an audio segment, a video frame, a transcript, and a later identity annotation count as evidence for the same occurrence?

That is an association/synchronization policy.

The robotics/sensor side has a very mature version of this distinction. ROS message_filters keeps sensor messages in their native forms while TimeSynchronizer / ApproximateTimeSynchronizer decide which timestamped messages belong together. ApproximateTimeSynchronizer even makes the allowed temporal mismatch (slop) explicit.

I am not suggesting importing ROS semantics into AlphaAvatar. I mainly think it is a useful reminder that:

same episode
≠
same representation

For AlphaAvatar, a plausible route is:

audio observation ─────┐
video observation ─────┼─> shared episode/correlation identity
annotation ────────────┘
                              │
                              ├─ modality-specific evidence remains inspectable
                              │
                              └─ optional derived cross-modal Memory

That would preserve future choices.

C. There does not seem to be one settled multimodal-Memory architecture anyway

Recent systems are still exploring quite different points in the design space:

  • M3-Agent uses continuous visual/audio input with entity-centric multimodal memory.
  • EgoMem treats lifelong audiovisual memory with asynchronous retrieval/dialog/memory-management processes.
  • EventMemAgent detects event boundaries and archives event-level representations.
  • TaskMem moves some of the problem from “what representation?” to “what should be memorized at all?”

So I would not interpret the literature as saying that AlphaAvatar needs to settle on one universal Memory object now.

My default would be:

unify event identity + temporal/provenance semantics first
preserve modality evidence
derive Memory projections later

That seems to leave the most room for the system to evolve without losing inspectability.

2. Capability descriptions: separate runtime truth from model exposure

For capabilities, I think there are at least three separate layers:

1. capability truth
2. operational availability / authority
3. what is exposed to the model right now

For example:

installed
≠ enabled
≠ healthy
≠ authorized for this participant
≠ useful for this turn
≠ included in the current model context

That distinction matters more to me than whether the prompt uses “full list” or “retrieval.”

The current MCP Tools specification is a useful comparison here. Its tools/list operation returns tools currently available to the requesting client; the list may change, may depend on authorization, and a server can advertise listChanged.

That does not answer AlphaAvatar’s prompt-design question, but it does give useful vocabulary:

live capability state is runtime truth; prompt exposure is a separate policy.

For a small active set

If AlphaAvatar normally has something like:

Memory
Persona
RAG
MCP
DeepResearch
Interaction Router
Character

then putting a short, stable capability summary in the Avatar context is probably simpler than building retrieval machinery around seven tiny descriptions.

For a large or highly dynamic set

If the set expands into hundreds of MCP tools, skills, channel-specific actions, backend-specific operations, etc., then a hybrid seems more attractive:

always visible:
  compact capability/category summary
  current availability/authority state

retrieved when needed:
  detailed schema
  long usage instructions
  examples
  edge-case constraints

This also avoids conflating “the runtime knows this capability exists” with “the model must spend context tokens reading its entire schema on every turn.”

There is some recent work suggesting that tool descriptions themselves materially affect tool selection, but that adding more detail can also add steps/cost and sometimes regress behavior, so I would not assume “more descriptions in context” is monotonically better.

The low-cost thing to make explicit now is probably just:

capability identity
current availability
authority/scope if relevant
model-facing short description

while leaving the context-delivery strategy replaceable.

3. Stream semantics: public observable behavior, private implementation

This is the area where v0.6.6 looks most clearly improved to me.

A very small test of the current PerceptionStream gave the expected kind of result after retention overflow:

first_available_seq = 3
missed_count        = 2
has_gap             = true

So I would no longer frame the issue as “the consumer silently misses data.” The runtime now gives the consumer enough information to know a retention gap happened.

The remaining question is more precise:

What does a consumer identity mean across cleanup/reconnect/restart?

For example, in the same small test:

consumer reads
→ commits
→ continues normally

preserved the committed position.

But after:

clear_consumer("same-id")
→ read again as "same-id"

that consumer behaved like a fresh reader over the retained tail, with the missing range explicitly reported as a gap.

That can be a perfectly reasonable realtime contract. It just has different semantics from a durable-resume contract.

So I would make the behavior, rather than the implementation, public:

consumer identity
read/current position
committed position, if distinct
first available position
missed/gap information
what cleanup means
what reconnect means
what loss/drop states can occur

The queue implementation, locking, scheduling, buffer representation, etc. can stay internal.

Kafka’s consumer model is useful only as an established vocabulary example here: it explicitly distinguishes the consumer’s current position from its securely stored committed position, and manual offset control is useful when “consumed” should mean “processing completed.”

I would not import Kafka semantics wholesale into a realtime perception stream. In particular, old video frames are often correctly disposable.

The useful decision tree seems more like:

If this stream is freshness-oriented / best effort:
    retained tail + explicit gap may be sufficient.

If downstream work is recoverable:
    preserve a stable recovery identity or cursor.

If downstream work has a durable-delivery guarantee:
    acknowledge that guarantee at the later durable boundary,
    not merely when the observation was read.

That last branch connects directly to the next question.

4. Model reasoning vs runtime commitment: the code now exposes a very concrete boundary

I still like the boundary from the previous discussion:

Observation
    ↓
model interpretation
    ↓
candidate
    ↓
runtime policy
    ↓
commit / merge / reject / defer
    ↓
durable or authoritative state

The interesting thing in v0.6.6 is that this is no longer just an abstract diagram. There are now concrete implementation seams where the distinction matters.

A small fault check: capture acknowledgement can precede Memory-processing success

I ran the real v0.6.6 EnvMemoryScheduler control flow with synthetic dependencies and forced the processing callback to fail.

The sequence was:

capture ENV batch
→ perception event cursor committed
→ processing attempt fails
→ retry
→ processing attempt fails again
→ retry limit exhausted
→ batch leaves pending state

During both processing attempts, the perception cursor was already advanced.

I would not call this a data-loss bug from that test. The provider/cache/perception dependencies were synthetic, and I did not reproduce a full process crash/restart/backend-recovery path.

What it does demonstrate is a legitimate intermediate state:

captured / acknowledged
but
not successfully processed into Memory

That might be completely intentional if ENV Memory is best-effort.

The design choice can then stay small:

If best-effort ENV Memory is intended:
    exposing "processing failed/dropped" in trace/metrics may be enough.

If failed processing should be recoverable:
    keep a retryable batch identity / recovery point.

If durable Memory delivery is promised:
    distinguish capture acknowledgement from durable-memory acknowledgement.

No large abstraction is required just to make the distinction explicit.

Not all “commitments” have the same recovery semantics

I would also avoid making commit one universal binary concept.

These have very different failure/recovery properties:

write a Memory candidate
update Persona
mark a fact as current state
send an email
call a device action
speak audio to the user

A useful classification may be:

reversible
compensatable
idempotently retryable
externally irreversible

Distributed-workflow systems have similar vocabulary. For example, the Saga pattern distinguishes compensable operations, a “pivot”/point of no return, and retryable operations after that point.

Again, I would not add a Saga engine to AlphaAvatar. I only think the vocabulary helps prevent:

memory persisted

and

email sent

from accidentally receiving identical “commit” semantics just because both were proposed by the model.

My preferred boundary remains:

model owns:
    interpretation
    association
    summarization
    candidate generation
    ranking / suggestion

runtime owns:
    authority checks
    policy decision
    durable state transitions
    external side effects
    delivered-output accounting

The runtime does not need to understand the model’s entire reasoning process. It only needs enough structured information to know what proposed change it is being asked to make and what eventually happened to it.

5. Provenance: the Observation layer looks fixed; the remaining seam is the Memory write path

This was probably the clearest result of the small checks.

A pathless runtime observation now produces a useful evidence receipt. So the older issue:

no persisted media path
→ empty provenance

does not seem to describe v0.6.6 anymore.

I then tried a narrower round trip:

Observation ID
→ evidence receipt
→ fake ENV Memory delta
→ MemoryItem
→ VDB serialization helpers
→ reconstructed MemoryItem
→ Markdown backup

and compared two cases.

Surface Current ENV-path mirror contains source Observation ID Positive control: evidence explicitly attached
MemoryItem no yes
flattened VDB payload no yes
rebuilt item no yes
Markdown backup no yes

The important part is the positive control.

It suggests that the existing serialization surfaces are capable of carrying the Observation provenance when it is supplied. The narrower seam appears to be the current ENV MemoryItem construction path: evidence is constructed/cached, while the direct evidence attachment in that path is still marked as a TODO.

I would phrase that very narrowly:

Observation-level provenance exists, and the serializers can preserve it; the remaining question is how much of that source identity should cross into durable Memory.

That is quite different from saying “AlphaAvatar loses provenance.”

There may be other runtime/cache/graph paths from which provenance can be recovered, and storing a complete evidence blob may be unnecessary or undesirable.

Given the privacy/self-hosted goals, I would probably start with the smallest useful durable link, for example:

source_observation_ids:
  - ...
source_event_ids:
  - ...

or perhaps a compact receipt/correlation ID, rather than copying raw frames or large evidence objects into every Memory item.

Why I think this link is worth preserving

It becomes useful for several future operations without choosing the final Memory architecture:

Why does the assistant believe this?
Which observations produced this Memory?
Was this state inferred from stale evidence?
What should be reconsidered after a correction?
Can an eval distinguish a bad interpretation from missing evidence?

Graphiti is an interesting comparison, not a prescription. Its current implementation distinguishes event/reference time from ingestion time and exposes provenance from episode UUIDs to derived graph elements. It also preserves valid_at / invalid_at style temporal semantics for facts.

The part I find relevant to AlphaAvatar is not “use a temporal graph.” It is simply that:

raw/source episode identity
and
derived/current state

can remain connected without being the same object.

6. Memory backend standardization: normalize guarantees, not internals

For Memory backends I would avoid defining one interface that accidentally makes LanceDB, Qdrant, Markdown backup, graph persistence, etc. appear more semantically equivalent than they are.

The storage systems themselves expose different consistency controls.

For example, LanceDB’s consistency docs expose read_consistency_interval:

default       → no automatic cross-process refresh
0             → check for updates on every read
non-zero      → eventual refresh after an interval

while Qdrant separately exposes concepts such as:

write_consistency_factor
read consistency
write ordering

So a backend-neutral method like:

await memory.save(item)

cannot by itself tell the rest of AlphaAvatar:

Has the write merely been accepted?
Is it durable?
Will an immediate retrieval see it?
Is it visible on every replica?
Can this operation be retried safely?

I would therefore standardize the framework-visible result more strongly than the storage implementation.

Something conceptually like:

item_id:
write_status:
durable:
query_visible:
retryable:
revision:

does not all have to ship at once; even two or three of those fields could remove ambiguity.

The main rule I would want is:

A common backend interface should not silently promise stronger consistency or durability than the selected backend actually provides.

That also makes backend-specific optimization easier, because the contract describes the result AlphaAvatar needs rather than prescribing how LanceDB/Qdrant/etc. must achieve it.

7. A tiny deterministic scenario suite looks higher-value than a larger abstraction right now

At this point I think a surprisingly small scenario suite would buy a lot.

I would start with only three core cases.

Scenario A — capture succeeds, processing fails

observations arrive
→ batch captured
→ processing fails
→ retry limit reached

Record only:

source_observation_ids:
capture_cursor:
capture_status:
memory_processing_status:
retry_count:
terminal_reason:
durable_memory_ids:

The purpose is not to require replay. It is simply to make the current semantics undeniable.

Scenario B — provenance round trip

Observation
→ evidence
→ Memory candidate/item
→ persistence
→ reload

Assertion:

Can the durable Memory record identify the source observation(s),
if the configured contract says it should?

Scenario C — retention + reconnect

consumer reads
→ commits
→ retention advances
→ consumer is cleared/disconnected
→ reconnect

Record:

previous_committed_cursor:
first_available_cursor:
resumed_cursor:
missed_count:
duplicate_count:

These three cases together cover a large part of questions 3 and 4 without any model benchmark, GPU, or external API.

Later, if useful:

D. multimodal late arrival
E. correction / old-current-state invalidation
F. backend partial failure
G. capability becomes unavailable during session

can be added.

A nearby project that recently went through a similar maturation is DeerFlow.

Its run-event-stream analysis started from the observation that one internal event stream had become the source for frontend history, debugging, token accounting, and evaluation, while its semantics still mostly lived in implementation details. That issue has since been closed by work that introduced an explicit run-event contract/documentation/conformance path.

Its separate eval RFC also takes a useful low-cost order:

deterministic replay
→ trajectory assertions
→ outcome evaluation
→ optional live/judge layers

I do not think AlphaAvatar should copy DeerFlow’s event schema. The useful lesson is smaller:

once an internal runtime record has multiple consumers, specifying and deterministically testing the existing semantics can be more valuable than designing a more general protocol first.

That seems very close to where AlphaAvatar is now.

8. If you introduce a trace schema, I would keep the first version deliberately boring

Given the earlier discussion about an internal trace contract before an out-of-process protocol, I think that still looks like the right order.

An illustrative internal shape could be:

identity:
  event_id:
  session_id:
  correlation_id:
  causation_id:
  source_observation_ids:

time:
  occurred_at:
  recorded_at:

stream:
  consumer_id:
  read_cursor:
  committed_cursor:
  first_available:
  missed_count:

memory:
  candidate_id:
  policy_decision:
  persistence_status:
  durable_memory_ids:
  backend_revision:

failure:
  stage:
  retry_count:
  terminal_reason:

I would not treat that as a schema proposal so much as a checklist of distinctions the deterministic scenarios might need.

Two fields I would seriously consider keeping separate from the beginning are:

occurred_at
recorded_at

because late-arriving perception/correction data eventually makes one timestamp ambiguous.

This is a very old distinction in stream/temporal systems, and Graphiti is also a nearby agent-memory example that now explicitly distinguishes event/reference time from ingestion time.

Likewise:

read_cursor
committed_cursor

may or may not both be required in AlphaAvatar, but the current ENV scheduler behavior shows why it is worth deciding rather than letting “cursor” acquire several meanings later.

The public/wire protocol can stay much smaller than this internal diagnostic representation.

9. A few nearby references I found useful as maps, not prescriptions

These are useful mostly because each supplies vocabulary for one narrow part of the problem.

Event/runtime contracts

  • DeerFlow run-event-stream issue — a de facto internal event stream becoming an explicit/versioned contract once frontend/debug/eval/accounting all depend on it.
  • DeerFlow eval RFC — deterministic replay and trajectory checks before live/judge-based evaluation.
  • CloudEvents specification — common event context separated from opaque/domain-specific event data.

Streaming / resumption

  • Kafka consumer documentation — mature vocabulary separating current consumer position from committed recovery position. Not a recommendation to give perception streams Kafka semantics.

Multimodal association

  • ROS message_filters — timestamp-based association of multiple sensor streams without requiring one common sensor payload representation.

Temporal/provenance Memory

  • Graphiti — episodes, temporal validity, and provenance from source episodes to derived graph elements; useful as vocabulary for current-vs-historical and evidence-vs-derived-state distinctions.

Capability availability

  • MCP Tools specification — the available tool set can be dynamic and authorization-dependent, while listChanged makes changes explicit. Useful for separating runtime availability from model-context policy.

Storage semantics

10. Limits of the small checks

For clarity, I would not over-read the synthetic results.

Stream / Observation checks

These exercised the real v0.6.6 core data structures directly, but they were intentionally small synthetic cases rather than long-running production sessions.

ENV scheduler fault check

The scheduler/control flow was the pinned v0.6.6 implementation, but the external perception/cache/processing dependencies were replaced with deterministic stubs so processing could be forced to fail.

So the supported statement is:

this intermediate control-flow state exists

not:

a production data-loss incident has been demonstrated

A real process crash/restart/backend recovery path could add semantics that the synthetic test did not exercise.

Provenance round trip

The test deliberately removed model/provider variability by supplying a fake EnvMemoryDelta.

It used the current MemoryItem field shape plus the real flatten/rebuild helpers and Markdown writer.

The supported statement is therefore narrow:

the checked serialization paths can preserve Observation provenance
when it is supplied, while the current mirrored ENV MemoryItem construction
does not directly carry that Observation ID

It does not establish that no other transient/runtime/graph path can recover source provenance.

That distinction is why I think this is mainly a useful contract seam rather than a bug report.

Overall, I would resist standardizing more of the system than necessary.

The places where standardization seems to pay for itself are the places where ambiguity becomes expensive:

What observation/event is this?
When did it occur, and when did the runtime learn about it?
Was anything missed?
Was it only captured, or also successfully processed?
Is this interpretation still a candidate?
Did runtime policy accept/reject/defer it?
Did it become durable/current state?
Can the durable result still point back to its evidence?
What guarantee did the selected backend actually provide?

Everything else can stay surprisingly flexible.

So if ENV Memory is intentionally best-effort, I think visibility of the intermediate/drop state may be enough.

If failed processing should be recoverable, the same seam could instead become a retry/recovery identity.

If durable Memory delivery becomes an explicit guarantee, the acknowledgement boundary can move later without changing what “perception capture” means.

Likewise, for multimodal Memory I would settle identity + temporal association + provenance before settling one universal representation; for capabilities I would keep runtime truth separate from context-delivery policy; and for Memory backends I would standardize observable guarantees rather than internal consistency mechanisms.

That seems to preserve the direction of the project: the model can keep getting smarter and more model-native over time, while the runtime stays useful because the consequential boundaries remain inspectable, testable, and replaceable.