{"course":{"slug":"context-engineering","title":"Context Engineering","url":"https://addaly.com/learn/context-engineering","level":"advanced","track":"build"},"lesson":{"slug":"freshness-and-as-of-lines","title":"Telling the model when things were true","url":"https://addaly.com/learn/context-engineering/freshness-and-as-of-lines","position":16,"of":87,"minutes":8,"answer":"Inject today's date at the stable–variable boundary, attach each passage's date to the passage itself rather than a distant header, treat recency as a score adjustment rather than a filter.","keyIdea":"Inject today's date at the stable–variable boundary, attach each passage's date to the passage itself rather than a distant header, treat recency as a score adjustment rather than a filter, and monitor how stale your index is as a separate number from how old your documents are."},"body":{"format":"markdown","text":"## The model does not know what day it is\n\nA model's weights are frozen at training time. Whatever it believes about dates comes from the training distribution, and it will happily reason about \"last year\" using a year that is not the current one. Everything time-dependent in your system has to arrive through the context.\n\nThe minimum viable version is one line near the top of the instructions:\n\n```\nToday is 2026-09-08. Treat any date after this as a future date.\n```\n\nThat single line prevents a family of bugs: relative date arithmetic against the wrong anchor, \"the latest version\" meaning whatever was latest in training, and a model politely correcting a user's correct date because it looks implausible from where it is standing.\n\nInject it, do not hardcode it, and remember the earlier lesson: putting a timestamp near the top of a cached prefix destroys the cache below it. Put the date at the boundary between stable and variable material (after the cached instructions, before the variable content) and you get both.\n\n## Every passage carries its date\n\nThe second rule follows from the previous lesson. If several retrieved passages can disagree, each needs its date attached to it, immediately.\n\n```\n[7] Refund policy — updated 2026-03-14 — source: help/refunds\nCustomers may request a refund within 30 days...\n```\n\nNot in a metadata block at the top of the request. Adjacent. A model resolving a conflict between passage 3 and passage 7 has to have both dates within reading distance of both texts, and a header 4,000 tokens earlier is not within reading distance in any practical sense.\n\n## Recency as a ranking signal, not a filter\n\nThe earlier lesson on filters applies here in its sharpest form. Recency is almost always a preference and almost never a constraint. A hard `updated_after` filter is how a system becomes unable to answer questions about anything that has not changed recently, and stable documentation is stable precisely because it is correct.\n\nA soft version, applied to the score after retrieval:\n\n```python\nage_days = (now - doc.updated_at).days\nscore = sim * math.exp(-age_days / HALF_LIFE_DAYS)\n```\n\nChoose the half-life from the domain, not from taste. Prices and availability might use 30 days. Engineering documentation might use 700. Legislation might use none at all, because a 1998 statute is not less applicable for being old.\n\nThe honest caveat: exponential decay on a similarity score mixes two incomparable quantities, and the result is a heuristic rather than a principled ranking. It works well enough in practice, and you should verify on your evaluation set that it does not push out old documents that were the right answer.\n\n## Freshness of the index, not just of the document\n\nThree different clocks, and teams usually track one:\n\n1. **Document age**: when the content was last changed by its author.\n2. **Index age**: when your pipeline last read it. A document updated an hour ago and indexed nightly is stale in your system regardless of its own timestamp.\n3. **Cache age**: when this particular retrieval result or generated summary was computed. A semantic cache serving yesterday's answer to today's question is an index-freshness failure hiding behind a latency optimisation.\n\nMonitor the second: the median and 99th percentile of `now - indexed_at` across the corpus. It is the number that answers \"why does the assistant not know about the change we shipped this morning\", and almost nobody has it on a dashboard until the first time somebody asks.\n\n## Live data does not belong in the index\n\nA balance, an order status, an inventory count, today's price. These change faster than any indexing pipeline and should be fetched at request time through a tool or a direct query, never embedded and retrieved.\n\nThe symptom of getting this wrong is distinctive and worth recognising: an assistant that is confidently, specifically wrong about a number, and whose wrongness has a consistent lag. If the answer is always right as of some hours ago, the value is coming from an index that should have been a lookup.\n\n## The instruction that helps, and its limit\n\nWhen conflicting dated passages must both be present, an explicit rule helps:\n\n```\nWhen sources disagree, prefer the one with the most recent date and say which\nyou used. If the dates are the same, say that the sources conflict.\n```\n\nIt improves the outcome and it does not solve it. Models are unreliable at comparing many dates scattered through a long context, and the failure is silent. Where correctness matters, resolve the conflict in code before assembly (keep the newest per document family) and use the instruction only as a second line of defence."},"check":{"question":"An assistant's answers about stock levels are always correct as of roughly six hours earlier. What does this pattern indicate?","options":[{"text":"The model's training cutoff is being used for the numbers, so no live data is reaching the context at all.","correct":false,"tempting":"A training cutoff would produce answers years out of date or entirely invented, not a consistent six-hour lag."},{"text":"The date line is missing from the system prompt, so the model anchors relative dates incorrectly.","correct":false,"tempting":"A missing date anchor causes relative-date errors, which would be erratic rather than a fixed offset in a specific value."},{"text":"A fast-changing value is being served from an index refreshed on a schedule instead of fetched at request time.","correct":true},{"text":"Recency decay is weighting older documents too heavily, so newer stock records lose to older ones.","correct":false,"tempting":"Misreads decay, which favours new documents; and a ranking preference would not produce a uniform lag across every answer."}]},"prev":{"slug":"near-duplicates-and-boilerplate","title":"The same passage three times","url":"https://addaly.com/learn/context-engineering/near-duplicates-and-boilerplate"},"next":{"slug":"metadata-that-earns-its-tokens","title":"The header on every chunk","url":"https://addaly.com/learn/context-engineering/metadata-that-earns-its-tokens"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Telling the model when things were true', in Context Engineering. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/context-engineering/freshness-and-as-of-lines (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Telling the model when things were true","courseTitle":"Context Engineering","module":{"index":2,"slug":"what-earns-a-place","title":"Selection: what earns a place"},"answer":null,"keyIdea":"Inject today’s date at the stable–variable boundary, attach each passage’s date to the passage itself rather than a distant header, treat recency as a score adjustment rather than a filter, and monitor how stale your index is as a separate number from how old your documents are.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-model-does-not-know-what-day-it-is","runs":[{"text":"The model does not know what day it is"}]},{"t":"p","runs":[{"text":"A model’s weights are frozen at training time. Whatever it believes about dates comes from the training distribution, and it will happily reason about “last year” using a year that is not the current one. Everything time-dependent in your system has to arrive through the context."}]},{"t":"p","runs":[{"text":"The minimum viable version is one line near the top of the instructions:"}]},{"t":"code","lang":"","label":"Text","text":"Today is 2026-09-08. Treat any date after this as a future date."},{"t":"p","runs":[{"text":"That single line prevents a family of bugs: relative date arithmetic against the wrong anchor, “the latest version” meaning whatever was latest in training, and a model politely correcting a user’s correct date because it looks implausible from where it is standing."}]},{"t":"p","runs":[{"text":"Inject it, do not hardcode it, and remember the earlier lesson: putting a timestamp near the top of a cached prefix destroys the cache below it. Put the date at the boundary between stable and variable material (after the cached instructions, before the variable content) and you get both."}]},{"t":"h2","id":"every-passage-carries-its-date","runs":[{"text":"Every passage carries its date"}]},{"t":"p","runs":[{"text":"The second rule follows from the previous lesson. If several retrieved passages can disagree, each needs its date attached to it, immediately."}]},{"t":"code","lang":"","label":"Text","text":"[7] Refund policy — updated 2026-03-14 — source: help/refunds\nCustomers may request a refund within 30 days..."},{"t":"p","runs":[{"text":"Not in a metadata block at the top of the request. Adjacent. A model resolving a conflict between passage 3 and passage 7 has to have both dates within reading distance of both texts, and a header 4,000 tokens earlier is not within reading distance in any practical sense."}]},{"t":"h2","id":"recency-as-a-ranking-signal-not-a-filter","runs":[{"text":"Recency as a ranking signal, not a filter"}]},{"t":"p","runs":[{"text":"The earlier lesson on filters applies here in its sharpest form. Recency is almost always a preference and almost never a constraint. A hard "},{"text":"updated_after","code":true},{"text":" filter is how a system becomes unable to answer questions about anything that has not changed recently, and stable documentation is stable precisely because it is correct."}]},{"t":"p","runs":[{"text":"A soft version, applied to the score after retrieval:"}]},{"t":"code","lang":"python","label":"Python","text":"age_days = (now - doc.updated_at).days\nscore = sim * math.exp(-age_days / HALF_LIFE_DAYS)"},{"t":"p","runs":[{"text":"Choose the half-life from the domain, not from taste. Prices and availability might use 30 days. Engineering documentation might use 700. Legislation might use none at all, because a 1998 statute is not less applicable for being old."}]},{"t":"p","runs":[{"text":"The honest caveat: exponential decay on a similarity score mixes two incomparable quantities, and the result is a heuristic rather than a principled ranking. It works well enough in practice, and you should verify on your evaluation set that it does not push out old documents that were the right answer."}]},{"t":"h2","id":"freshness-of-the-index-not-just-of-the-document","runs":[{"text":"Freshness of the index, not just of the document"}]},{"t":"p","runs":[{"text":"Three different clocks, and teams usually track one:"}]},{"t":"ol","start":1,"items":[[{"text":"Document age","b":true},{"text":": when the content was last changed by its author."}],[{"text":"Index age","b":true},{"text":": when your pipeline last read it. A document updated an hour ago and indexed nightly is stale in your system regardless of its own timestamp."}],[{"text":"Cache age","b":true},{"text":": when this particular retrieval result or generated summary was computed. A semantic cache serving yesterday’s answer to today’s question is an index-freshness failure hiding behind a latency optimisation."}]]},{"t":"p","runs":[{"text":"Monitor the second: the median and 99th percentile of "},{"text":"now - indexed_at","code":true},{"text":" across the corpus. It is the number that answers “why does the assistant not know about the change we shipped this morning”, and almost nobody has it on a dashboard until the first time somebody asks."}]},{"t":"h2","id":"live-data-does-not-belong-in-the-index","runs":[{"text":"Live data does not belong in the index"}]},{"t":"p","runs":[{"text":"A balance, an order status, an inventory count, today’s price. These change faster than any indexing pipeline and should be fetched at request time through a tool or a direct query, never embedded and retrieved."}]},{"t":"p","runs":[{"text":"The symptom of getting this wrong is distinctive and worth recognising: an assistant that is confidently, specifically wrong about a number, and whose wrongness has a consistent lag. If the answer is always right as of some hours ago, the value is coming from an index that should have been a lookup."}]},{"t":"h2","id":"the-instruction-that-helps-and-its-limit","runs":[{"text":"The instruction that helps, and its limit"}]},{"t":"p","runs":[{"text":"When conflicting dated passages must both be present, an explicit rule helps:"}]},{"t":"code","lang":"","label":"Text","text":"When sources disagree, prefer the one with the most recent date and say which\nyou used. If the dates are the same, say that the sources conflict."},{"t":"p","runs":[{"text":"It improves the outcome and it does not solve it. Models are unreliable at comparing many dates scattered through a long context, and the failure is silent. Where correctness matters, resolve the conflict in code before assembly (keep the newest per document family) and use the instruction only as a second line of defence."}]}],"check":{"question":[{"text":"An assistant’s answers about stock levels are always correct as of roughly six hours earlier. What does this pattern indicate?"}],"options":[{"text":[{"text":"The model’s training cutoff is being used for the numbers, so no live data is reaching the context at all."}],"correct":false,"tempting":[{"text":"A training cutoff would produce answers years out of date or entirely invented, not a consistent six-hour lag."}]},{"text":[{"text":"The date line is missing from the system prompt, so the model anchors relative dates incorrectly."}],"correct":false,"tempting":[{"text":"A missing date anchor causes relative-date errors, which would be erratic rather than a fixed offset in a specific value."}]},{"text":[{"text":"A fast-changing value is being served from an index refreshed on a schedule instead of fetched at request time."}],"correct":true,"tempting":null},{"text":[{"text":"Recency decay is weighting older documents too heavily, so newer stock records lose to older ones."}],"correct":false,"tempting":[{"text":"Misreads decay, which favours new documents; and a ranking preference would not produce a uniform lag across every answer."}]}],"order":[1,2,3,0],"why":null},"prev":{"slug":"near-duplicates-and-boilerplate","title":"The same passage three times","minutes":8},"next":{"slug":"metadata-that-earns-its-tokens","title":"The header on every chunk","minutes":8}}}