> Full SurrealDB documentation index: https://surrealdb.com/docs/llms.txt

# Rome, 753 BC to AD 1453

Every year of Roman history from the founding of the city to the fall of Constantinople, written into SurrealDB Agent Memory as structured facts, with rulers and control of places read back as value histories.

This demo follows Rome as a political entity, and the people who lived in it, through every year from the traditional founding of the city in 753 BC to the fall of Constantinople in AD 1453. About 9,200 events from Wikipedia went into SurrealDB Agent Memory as structured facts, and the page shows what the memory holds: where and when each event happened, who held each office, and who controlled each place at any date.

## What went in

The sources are Wikipedia's page for each year from 753 BC to AD 1453, or the page for its decade where a year has no page of its own, and Wikipedia's [Timeline of Roman history](https://en.wikipedia.org/wiki/Timeline_of_Roman_history). Each line of a page became one event. Where the timeline and a year page describe the same event, the more informative description was kept and the other was dropped.

The demo keeps the events that involve the following:

* Events in Rome itself,
* The events of peoples Rome dealt with at the time.

This demo focuses on Rome the political entity, so no events in the former West Roman Empire are kept after AD 476 when it fell. At that point Rome is only the Eastern Roman (Byzantine) Empire. Events in the former West Roman Empire do occasionally show up after this date if they are related to the Eastern Roman Empire.

A language model (Gemini 3.8 Flash) read each event before it was written, and returned:

- who acted, what they did, and to what
- where the event happened
- any value the event changed, such as a new tyrant of Syracuse or a city passing to another power
- whether the event involves Rome, one of its contacts, or neither

Where the source names no place, the model inferred one from the event. The map draws those places as hollow, dashed dots, and the export marks them as inferred. The model also made the decisions about scope and duplicates, so some borderline cases could go either way.

## The interactive map

<DemoEmbed title="Rome, 753 BC to AD 1453" height={760} pages={[{"label": "Map", "src": "/docs/showcase/rome/map.html"}]} />

A link can open the demo at a chosen view. These three show what the page does with a war, a life and a single year:

- [Hannibal in the Second Punic War, 218 to 201 BC](/docs/showcase/rome/map.html?from=218%20BC&to=201%20BC&entity=Hannibal&at=39,6,5)
- [Julius Caesar, from his birth to his death](/docs/showcase/rome/map.html?from=100%20BC&to=44%20BC&entity=Julius%20Caesar&at=44,6,4)
- [AD 1204, the Fourth Crusade takes Constantinople](/docs/showcase/rome/map.html?year=AD%201204&at=40,26,5)

## As you go through the demo, note the following

- **Every fact has a known time.** Each event was written with the year it happened as its known time, so a value read as of 216 BC is the value the memory held for that year. Use **Year** to step through single years, or **Period** for a span such as a war or a reign.
- **Rulers and control are value histories.** Who ruled Syracuse or controlled Milan is a value that a later fact supersedes. Choose an entity, and its panel shows its values as of the selected time. **Compare two years** shows what changed between two dates.
- **The page shows what the memory returned.** The value histories were read from Agent Memory's history endpoint and saved with the page, so the page makes no calls to Agent Memory while you use it.
- **One place can have several names.** Mediolanum and Milan, or Byzantium and Constantinople, are separate entities in the memory. The page joins 149 such groups, and the memory holds a `same_as` relation for each pair where both names are entities.
- **You can follow several entities at once.** Add more than one name in the search, then show the events that involve **any of these** or **all of these**.
- **Story and Life view show one subject in order.** **Story** plays the main events of the selected period one after another. **Life view** puts one entity's offices, events, places and the people it met most on one time axis. Raise **Detail** to see more.
- **Export writes a prompt, not an article.** **Export** collects the facts that match the period and filters into a prompt for the LLM of your choice. Agent Memory stores and returns facts, and the article is written by the model you give the prompt to.

## Writing the facts

The facts were written with `POST /facts` and `infer: "triples"`, which stores structured entries as they are sent, with no extraction model in Agent Memory. Each request holds one year, and the request's `observed_at` is that year's known time. This is part of the request for 216 BC:

```json
{
  "infer": "triples",
  "observed_at": "0538-01-01T00:00:00Z",
  "labels": ["year=216 BC"],
  "triples": [
    {
      "entity": { "type": "person", "name": "Hannibal" },
      "verb": "captured",
      "object": "Cannae",
      "summary": "Hannibal marched south through Italy and captured the Roman army supply depot at Cannae.",
      "occurred_at": "0538-01-01T00:00:00Z",
      "source_clause": "The Carthaginian general, Hannibal, moves his forces southward through Italy and seizes the large army supply depot at Cannae on the Aufidus River."
    },
    {
      "entity": { "type": "person", "name": "Hannibal" },
      "key": "active_in",
      "target": { "type": "place", "name": "Cannae" },
      "occurred_at": "0538-01-01T00:00:00Z"
    },
    {
      "entity": { "type": "place", "name": "Cannae" },
      "key": "controlled_by",
      "value": "Ancient Carthage",
      "occurred_at": "0538-01-01T00:00:00Z"
    }
  ]
}
```

The three entries show the three kinds of fact the demo uses:

| Entry | Kind | What it gives the page |
| --- | --- | --- |
| Hannibal `captured` Cannae | Action, with a summary and the source wording | An event on the map and in the lists |
| Hannibal `active_in` Cannae | Relation | The places an entity was involved in, for the life view |
| Cannae `controlled_by` Ancient Carthage | Attribute | A value that the next `controlled_by` for Cannae supersedes |

The 1,745 years with events took 1,745 requests and about 22,000 entries.

Agent Memory identifies an entity by its type and its normalised name, so one name needs one type everywhere. Rome is a place where events happen and also the state that acts in them, and the demo gives it the type `place` in every entry. Supplied types are also normalised into a closed vocabulary, so `place` is stored as `location`. [Ontology grounding](/docs/agent-memory/tuning/ontology-grounding.md) lists the types.

## Dates before AD 1

`observed_at` and the other time fields take RFC 3339 timestamps, whose years run from 0000 to 9999, so a date before AD 1 cannot be sent as it is. The demo counts years *ab urbe condita*, "from the founding of the city", which is a Roman way of counting years: 753 BC is year 1 and AD 1453 is year 2206. Every date in the demo is then a valid timestamp, and the page converts each year back to BC or AD to show it.

A date with a known day keeps the day, so the Battle of Cannae on 2 August 216 BC is written as `0538-08-02`. A leap day needs care: 29 February is a valid day in the Julian year AD 468 but not in the timestamp year 1221, so such a date moves to 28 February.

The same approach works for any data that starts before AD 1. Choose a starting year for your own data, count from it when you write, and convert when you show a date. [Historical and archaeological data](/docs/agent-memory/cookbooks/patterns/historical-and-archaeological-data.md) covers other decisions that this kind of data needs.

## Values that change over time

Each `controlled_by`, `king`, `tyrant`, `consul` or `emperor` entry supersedes the previous value for the same entity and key. The history endpoint returns the whole chain, with the time each value started and ended:

```http
GET /api/v1/{context_id}/entities/location/Syracuse,%20Sicily/history/tyrant
```

For Syracuse, the chain runs from Gelon to Hieronymus, with the known time of each change. The page reads the value of each chain at the selected year, which is the value that was current then. **Compare two years** in the demo puts two of these readings side by side.

Agent Memory keeps a value until a later fact supersedes it, and in data this sparse a later fact may never come. Publius Quinctilius Varus became governor of Germania in AD 7 and died in AD 9, and no later governor is recorded, so the memory still holds him as governor. The page treats a value as supported until the death of the person it names, or for 60 years after it was set when nothing later is recorded, apart from slow-changing values such as who controls a place. The life view draws the value as a bar for that time and as a dashed line after it, and the entity panel marks it as having no later record.

Some offices are held by two people at once. Two consuls in one year are written as one value, `Julius Caesar and Mark Antony`, because a second `consul` entry for the same year would supersede the first.

## One place, several names

Many places in the data appear under an ancient and a modern name, such as Mediolanum and Milan, or Singidunum and Belgrade. The demo found 149 groups of names for the same place, people or state. Places within a few kilometres of each other, and names that share a core such as Carthage and Ancient Carthage, were proposed as pairs, and a model decided for each pair whether the two names refer to one thing. A part of a place, such as the Roman Forum in Rome, and a successor state, such as the Roman Republic after the Roman Kingdom, were kept apart.

For each pair where both names are entities in the memory, the demo wrote a `same_as` relation, 72 in total.

How aggressively to link up similar entities depends on each user's case. For more details on this subject, see the [Entities with several names](/docs/agent-memory/mental-model/entities-with-several-names.md) page which describes when to link names in this way and when to keep them apart. The page also merges each group under one name, so a search for Mediolanum finds the events recorded under Milan.

## Export the data to make your own history paper or book

**Export** turns the current period and filters into a prompt with the matching facts. It lists the events in order, with their dates, places and source links, and the rulers and offices of each place as they stood when its events happened. The prompt asks the model to keep the uncertainty a fact carries, to add nothing that is not in the list, and to say where the facts leave a gap.

Agent Memory does not write the article. Its part is to store the facts and return the ones that match, with their times and their history. A language model writes the text, so two models, or two runs of one model, give different articles from the same facts.

### An example: Plato

Plato appears in only six events of the demo, so his export is short enough to show in full. To make it, search for Plato, choose **Period**, select **span of the chosen entities**, then select **Export** and set the length to 200 words. The facts are where Plato meets Roman history, with his visits to Syracuse and later events that involve his work, so the prompt asks for an article about those rather than about his whole life.

```text title="Prompt"
Write an article of about 200 words for general readers.

Subject: Plato, as recorded in the history of Rome
Period: 388 BC to AD 529

Use only the facts listed below. They were exported from SurrealDB Agent Memory, which holds events taken from Wikipedia's year pages and its "Timeline of Roman history" for every year from the founding of Rome (753 BC) to the fall of Constantinople (AD 1453). Agent Memory stores and retrieves facts; it did not write any of this text.

- Keep the uncertainty a fact carries, in natural wording: an "approximate date" or one "from a decade summary" is not exact ("around 600 BC"), a place "inferred from context" was worked out rather than stated ("probably at Como"), and many early Roman dates are traditional rather than recorded. Do not repeat these labels in the article.
- Do not add events, people or numbers that are not in the list. Where the facts leave a gap the subject needs, say briefly that little is recorded, without discussing the data itself.
- Some events appear twice from different source pages; treat them as one.
- Stay close to the requested length.
- Give the sources you relied on at the end, using the links provided.
- The facts are the points where Plato appears in the history of Rome. Write about those, not a full account of everything else known about the subject.

Filters applied to the facts: only entries involving Plato.

## Rulers, control and offices (from Agent Memory's value histories)

Values for a place are as they stood when the event at that place happened.

- Syracuse, Sicily in 388 BC: controlled by Ancient Corinth (from 734 BC); tyrant Dionysius I of Syracuse (from 405 BC); government democracy (from 466 BC)
- Syracuse, Sicily in 367 BC: controlled by Ancient Corinth (from 734 BC); tyrant Dionysius II of Syracuse (from 367 BC); government democracy (from 466 BC)
- Rome in AD 321: controlled by Constantine the Great (from AD 312); government principate (from 27 BC); bishop Pope Sylvester I (from AD 314)

## Events (6)

- 388 BC: Plato traveled to Syracuse at the invitation of Dion. Place: Syracuse, Sicily. Source: https://en.wikipedia.org/wiki/388_BC
- 387 BC: Dionysius I expelled Plato from Syracuse, after which Plato returned to Athens. Place: Syracuse, Sicily. Source: https://en.wikipedia.org/wiki/387_BC
- 367 BC: Dionysius II succeeded his father Dionysius I as tyrant of Syracuse under the guidance of Dion and Plato. Place: Syracuse, Sicily (inferred from context). Source: https://en.wikipedia.org/wiki/367_BC
- 361 BC: Plato failed to reconcile Dionysius II with Dion and was forced to flee Syracuse. Place: Syracuse, Sicily. Source: https://en.wikipedia.org/wiki/361_BC
- AD 321: Calcidius translated Plato's Timaeus into Latin. Place: Rome (inferred from context). Source: https://en.wikipedia.org/wiki/321
- AD 529: Emperor Justinian I ordered the closure of the Platonic Academy in Athens, prompting its pagan philosophers to emigrate eastward. Place: Platonic Academy. Source: https://en.wikipedia.org/wiki/529
```

Possible output:

> In the historical records intersecting Roman annals, the Greek philosopher Plato first appears through his turbulent involvement with Sicily. In 388 BC, Plato traveled to Syracuse at the invitation of Dion. The visit was cut short when the tyrant Dionysius I expelled him in 387 BC, prompting Plato to return to Athens. He was drawn back in 367 BC when Dionysius II succeeded his father as tyrant of Syracuse, assuming rule under the guidance of Plato and Dion, probably in Syracuse itself. However, court tensions persisted, and in 361 BC Plato failed to reconcile Dionysius II with Dion, forcing the philosopher to flee the city once more.
>
> Little is recorded concerning Plato across the centuries that followed. He reemerged in AD 321, when Calcidius translated Plato’s dialogue *Timaeus* into Latin, an event probably taking place in Rome. The final entry arrived in AD 529, when the Christian emperor Justinian I ordered the closure of the Platonic Academy in Athens. This imperial suppression prompted the academy's pagan philosophers to emigrate eastward, bringing an end to the ancient institution.
>
> **Sources**
> - https://en.wikipedia.org/wiki/388_BC
> - https://en.wikipedia.org/wiki/387_BC
> - https://en.wikipedia.org/wiki/367_BC
> - https://en.wikipedia.org/wiki/361_BC
> - https://en.wikipedia.org/wiki/321
> - https://en.wikipedia.org/wiki/529

This article came from Gemini 3.8 Flash. It uses all six events, keeps the inferred places as "probably", and says that little is recorded between 361 BC and AD 321. One detail comes from the model rather than the facts: no fact calls Justinian I a Christian emperor. Check any article in this way, because models frequently add what they already know unless the facts leave them no room.

## Sources and licences

The events come from Wikipedia, under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) licence. The map's land, rivers and lakes are from [Natural Earth](https://www.naturalearthdata.com/), which is in the public domain, and the map is drawn with [Leaflet](https://leafletjs.com/).
