I asked our agent's memory the same question twice: who approves discounts at TransPacific Haulage? The first time it said the CFO. The second time I added "as of July 2025", and it said the Commercial Lead.
Both answers were correct. That second answer is the part I want to talk about, because most agent memory I've seen only knows "now", and an agent that only knows now gives confident, outdated advice.
What we built
Precedent is a buyer-side negotiation copilot for procurement teams. Before a vendor renewal it writes a brief: which tactic to lead with, what to avoid, who really decides on discounts, and what to ask for. Every claim cites the memory it came from. We named it after legal precedent: no advice without a past case to cite.
The memory lives in Hindsight, running as a local server. One bank holds every negotiation, tagged per vendor. An agent on gpt-4.1 reads that memory through tools, and Hindsight uses gpt-4.1-mini for its own background work: extracting facts, consolidating them into beliefs, and rewriting a playbook per vendor.
I owned the data, the evaluation and the question of time. This post is about all three.
Time matters in two different ways
What we knew grows. By July 2025 we had negotiated with TransPacific once, a volume commitment that lost, and the only approver we had seen was the Commercial Lead. In September 2025 their account manager took our payment-terms offer to the CFO, Ananya Rao, who approved 9% off. So the July answer was not wrong. It was everything we knew in July.
The world changes. Northwind Cloudworks is the sharper case. Through early 2026, discounts were approved by Raj Menon, their Regional Sales Director. In April 2026 Northwind was acquired by Stratus Group, Raj left, and a new VP Revenue Operations, Alan Pierce, now signs. The tactic that had won for us twice, a competing quote, got only 10% the first time we tried it with him.
An agent that only knows now handles neither case well. In the first, you can't audit why it once believed something. In the second, it might happily tell you to call Raj.
How time gets into the memory
Three things make this work, and none of them needed custom infrastructure on top of Hindsight.
Every memory knows when it happened. Each negotiation is retained with its own date as the timestamp, not the date we loaded it, plus tags and structured metadata:
client().retain(bank_id=bank_id, content=episode_text(ep, vendor), timestamp=ep["date"], context="Buyer-side negotiation record", tags=[vtag(vendor["id"]), f"category:{vendor['category']}"], document_id=f"ep-{vendor['id']}-{ep['date']}-{ep['tactic_used']}", metadata={"tactic": ep["tactic_used"], "outcome": ep["outcome"], "final_discount": f"{ep['final_discount']:.3f}", "ask_discount": f"{ep['ask_discount']:.3f}", "date": ep["date"]}) Hindsight extracts facts from each note and keeps that occurred date on every one. In the screenshots above, every result carries its date.
Newer evidence wins, and the history stays. We tell Hindsight how to consolidate. The observations mission says: "When newer evidence contradicts an older belief, record the change rather than discarding the history." We also store a standing directive in the bank itself, so it applies whenever Hindsight reasons over the memory, not just in our prompt:
("Newest evidence wins", "When memories conflict (new owner, departed contact, changed pricing policy), prefer " "the most recent evidence and say what changed and when.", 8), Hindsight also keeps every earlier version of each vendor's playbook, with the memories each version was built from. When a belief changes, I can see when and why.
You can ask what we knew on a date. The Memory explorer runs Hindsight's recall with a query timestamp and keeps only what had happened by then:
if as_of: kw["query_timestamp"] = f"{as_of}T23:59:59Z" res = client().recall(bank_id=bank_id, query=query, budget="mid", max_tokens=4000, trace=True, include_entities=True, types=["observation", "world", "experience"], **kw).to_dict() Observations are dropped from as-of queries, because Hindsight re-derives them later from all the evidence, so they can carry hindsight from the future. The whole call needs no LLM; the TransPacific queries took around 60 ms. The trace also shows which retrieval channel found each memory (semantic, keyword, graph or time) and the people and companies involved.
Where the data comes from
That Northwind story only works if the history contains changes like that. No public dataset contains negotiation notes. Procurement registers record awards, not what was said on a call or what the buyer asked for first. The shortcut would be writing plausible notes about real companies, and I refused: invented call notes about real firms are fabrication.
So the vendors are fictional: 12 of them and 61 negotiations, written in data/generate.py. Each vendor has a hidden profile that the notes are consistent with but never state:
dict(list_markup=0.24, floor_discount=0.22, works="competing_quote", fails="escalate_to_exec", decision_maker="Regional Sales Director", quirk="Opens with a 'special' 5% discount that expires in 48 hours; ...") The agent has to infer "competing quotes work here" from several episodes. I added deliberate contradictions over time, like the Northwind acquisition, because a memory that only counts wins would miss them.
The real-world input is market context: 19,133 contract awards from Estonia's public procurement register. For SaaS licences that gives 1,193 awards, a median of EUR 96,097, and 53% of contracts with a single bidder. That tells the agent something useful: in a market with one bidder, a competing quote is a weaker threat than it sounds.
Does the memory actually help?
Reading memory is not the same as improving outcomes. To measure that I needed something to negotiate against, so I wrote a simulator. Each simulated vendor has hidden rules the agent never sees: one tactic that works, one that backfires, and a maximum discount.
Two arms run ten negotiations per vendor, starting from an empty bank. Same model, same prompt, same loop; the only difference is whether the memory tools exist. After each round, the memory arm's outcome is retained into Hindsight, and the next round can recall it.
| Measure | Memory ON | Memory OFF |
|---|---|---|
| Mean % of max discount, last 5 negotiations | 75.6 | 39.9 |
| Vendors "won" (at least 85% of max) | 2 of 3, from negotiation 4 | 0 of 3, never |
The first negotiation is a cold start for both, so they start close. The gap opens as outcomes accumulate.
Lessons
- Store when something happened, not when you loaded it. The timestamp on retain is what makes every later question about time possible.
- Decide how conflicts resolve, and write it down. "Newest evidence wins, keep the history" is a policy. Put it where the memory system can apply it, not only in your agent's prompt.
- "As of" is an audit tool. When a brief looks wrong, being able to ask what we knew on a date separates "the memory was stale" from "the world changed".
- Refuse to fake real data. Fictional vendors with hidden rules gave us a test the agent could visibly fail, which was worth more than realistic-looking notes about real companies.
Limits
The negotiation history is synthetic by design, and the Estonian benchmarks describe award sizes and bidder counts, not discounts. The simulator, its rules and the 85% win threshold are mine, so the curve shows that the memory loop works, not real-world savings. It is one run on three vendors with no confidence intervals, and one vendor was never solved by either arm. Next, I want to run several seeds and change a vendor's rules mid-run to test whether the agent notices.
If you're thinking about memory for your own agents, Vectorize has a good explainer on what agent memory is, and the Hindsight documentation covers retain, recall, tags and mental models.




