Entity mapping and relationships
Entities and relationships live in the playbook. Physical tables, columns, and foreign keys live in bindings. This page walks through both, step by step.
Step 1 — Name your business objects (entities)
Start from how people talk about the domain, not from raw schema names.
| Business concept | Playbook entity | Identifier field | Attributes |
|---|---|---|---|
| Sales rep | crm_user |
user_id |
full_name |
| Customer account | crm_account |
account_name |
industry |
In playbook JSON:
"entities": {
"crm_user": {
"identifier": "user_id",
"attributes": ["full_name"]
},
"crm_account": {
"identifier": "account_name",
"attributes": ["industry"]
}
}
identifier— the stable id agents use to resolve a subject or join rows.attributes— fields agents may read or display. Names here are playbook vocabulary, not necessarily database column names.
Step 2 — Define how entities connect (relationships)
A relationship is a named edge from one entity to another.
| Relationship | From | To | Meaning |
|---|---|---|---|
owns_account |
crm_user |
crm_account |
A user owns one or more accounts |
In playbook JSON:
"relationships": {
"owns_account": { "from": "crm_user", "to": "crm_account" }
}
Agents query relationships in plain language: "How many accounts does Alex own?" maps to counting owns_account where the subject is Alex's user_id.
Relationships are directional. from is usually the subject side; to is the object being accessed or counted.
Step 3 — Route entities to data sources
The sources map tells the runtime which binding file to use for each entity.
"sources": {
"crm_user": "postgres",
"crm_account": "postgres"
}
Both entities use the postgres source key → one binding file: bindings/simple-crm-access.postgres.yaml.
If payroll lives in a CSV while CRM stays in Postgres:
"sources": {
"crm_user": "postgres",
"crm_account": "postgres",
"crm_payroll_record": "csv"
}
That playbook needs two binding files: .postgres.yaml and .csv.yaml.
Step 4 — Map entities to physical schema (binding)
Bindings translate playbook names to real tables and columns.
Postgres binding (bindings/simple-crm-access.postgres.yaml):
source_id: warehouse_pg
entities:
crm_user:
from: users
id: user_id
fields: [full_name]
crm_account:
from: accounts
id: account_name
fields: [industry]
relationships:
owns_account:
object: crm_account
link_column: owner_user_id
| Binding field | Meaning |
|---|---|
source_id |
Profile key in profiles/local.yaml (credentials) |
entities.<name>.from |
Table, collection, file, or API path |
entities.<name>.id |
Physical id column (maps to playbook identifier) |
entities.<name>.fields |
Playbook attribute → physical column (omit when names match) |
relationships.<name>.object |
The to entity in playbook terms |
relationships.<name>.link_column |
Foreign key on the object side linking back to the subject |
The runtime compiles lookups and counts from this YAML — do not write raw SQL or SOQL in bindings.
Step 5 — Map columns when names differ
CSV columns often do not match playbook field names. Use fields to map left (playbook) → right (physical):
entities:
crm_user:
from: payroll.csv
id: user_id
fields:
user_id: user
full_name: full_name
Here the CSV column user stores the value for playbook field user_id.
Step 6 — Validate the mapping
- Introspect the source via MCP to confirm table and column names.
- Propose binding YAML with
propose_bindingbefore saving. - Test with
test_bindingor aquery_graphcall:
curl -s http://127.0.0.1:8787/query \
-H 'Content-Type: application/json' \
-d '{
"playbook_id": "simple-crm-access",
"resolve": { "entity": "crm_user", "by_name": "Alex Anderson" },
"count": { "relationship": "owns_account", "object_entity": "crm_account" }
}'
A successful response proves entity mapping and the owns_account link column are correct.
Mental model
Playbook Binding
──────── ───────
crm_user ──────────────► users table, user_id column
crm_account ──────────────► accounts table, account_name column
owns_account ──────────────► accounts.owner_user_id → users.user_id
Keep business language in the playbook. Keep schema details in bindings. When IT renames owner_user_id, update one binding file — not every agent prompt.