Two read paths, one item
The same items answer "what happened last" and "what is relevant", through two different reads.
Why
An assistant needs two different things from its history. It needs the last few messages, in order, every single turn. And occasionally it needs the one thing the user said three weeks ago that bears on what they just asked. Those are different questions, and they want different reads.
The first is a Query on the session partition, read backwards with a Limit. It is the cheapest read in the course: the key decides what to read, nothing is scanned and thrown away, and the order is exact.
The second is a vector search, and the decision that makes it work is what the index is partitioned by. The SearchSchema here declares userId as its HASH, not sessionId. Partition by session and relevance can only ever reach inside the conversation you are already in, which is the one place recency has already covered. Partition by user and it reaches across every conversation they have ever had. That is the entire reason for keeping this in DynamoDB beside the items rather than in a separate vector database.
There is no join and no synchronisation between the two. The index is maintained as part of the base write, so a message is searchable the moment it is written. Merging the two result sets is your application's job, and there is no ranking that combines a sort order with a similarity score for you. That is a judgement about your product, and DynamoDB is right not to guess at it.
A vector index is a second read path over the same items; partition it by the axis relevance needs to cross, which is usually not the one recency already covers.
These rows are real and already here. Running an operation against them downloads a DynamoDB-compatible engine, about 850 KB, and executes it in this tab.
The recency window: one session's partition, read backwards, capped at six. Cheap, exactly ordered, and completely blind to anything said in an earlier session.
Run an operation to see the raw engine response.