Replies: 1 comment
-
|
Practical patterns from building agents that run across months on top of LlamaIndex - temporal validity is mostly application-layer in LlamaIndex today, but the index gives you enough primitives to build it cleanly without forking. 1. Treat each fact as a node with from llama_index.core.schema import TextNode
node = TextNode(
text="user budget is GBP 10k",
metadata={
"fact_key": "user.budget", # canonical fact identity
"valid_from": "2026-03-15",
"valid_to": None, # open-ended until superseded
"superseded_by": None,
"source_msg_id": "msg_4242",
},
)The 2. On write, run an explicit invalidation pass before insert. def upsert_fact(index, new_node):
key = new_node.metadata["fact_key"]
today = new_node.metadata["valid_from"]
# find any open-ended prior fact under the same key
prior = index.vector_store.query(MetadataFilters(filters=[
ExactMatchFilter(key="fact_key", value=key),
ExactMatchFilter(key="valid_to", value=None),
]))
for p in prior.nodes:
p.metadata["valid_to"] = today
p.metadata["superseded_by"] = new_node.node_id
index.update_ref_doc(p)
index.insert(new_node)Embedding-based "newer wins" alone is unreliable - embeddings of "GBP 5k" and "GBP 10k" sit in roughly the same neighbourhood, so retrieval ties on similarity and recency-bias has to break the tie. The explicit 3. Wrap retrieval in a from llama_index.core.postprocessor.types import BaseNodePostprocessor
class TemporalFilter(BaseNodePostprocessor):
as_of: str
def _postprocess_nodes(self, nodes, query_bundle):
return [n for n in nodes
if n.node.metadata.get("valid_from","0") <= self.as_of
and (n.node.metadata.get("valid_to") is None
or n.node.metadata["valid_to"] > self.as_of)]
retriever = index.as_retriever(similarity_top_k=20,
node_postprocessors=[TemporalFilter(as_of=today)])Filtering after retrieval (not before) keeps it cheap on small top-K and avoids fighting the vector-store filter API for ranged queries (which most stores expose unevenly). 4. For session-scoped facts ("preparing for a conference next week"), attach a TTL and a separate index you blow away on session-end. Mixing ephemeral context into the same 5. Recency bias is a tie-breaker, not a ranking strategy. If you weight Graph approach (your third bullet) works well for relational facts ("user.preferences.colour=blue"), less well for statement facts ("user said X on date Y"). PropertyGraphIndex is good if your facts naturally form a graph; KeywordTableIndex + the supersedes-chain above is simpler if they don't. Recipe: |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
A practical question about retrieval design for long-running agents.
Most RAG setups treat stored knowledge as static — you embed it once, retrieve by similarity, done. But agents that run across weeks or months accumulate facts that expire or contradict later information. A few scenarios:
User says "my budget is £5k" in January, then "budget increased to £10k" in March. Both are in the index. Which surfaces on retrieval?
A stored fact references a product version that no longer exists. No explicit signal it is outdated — the embedding still matches.
Seasonal or time-sensitive context ("I am preparing for a conference next week") that is meaningless three weeks later.
Curious how people are handling this in practice with LlamaIndex. A few approaches I have seen:
Is there a recommended pattern here, or is this mostly left to the application layer? Would be useful to understand what primitives LlamaIndex exposes that are relevant.
Beta Was this translation helpful? Give feedback.
All reactions