An agent can have its own private database tables. Define them as `.mod.yao` files inside the agent's `models/` directory — Yao migrates the tables automatically on startup.
## Directory Structure
```
assistants/myns/myagent/
├── package.yao
├── models/
│ ├── record.mod.yao
│ └── label.mod.yao
```
### Model ID and Table Name
The framework derives the **model ID** from the file path:
```
assistants/<namespace>/<agent>/models/<name>.mod.yao
↓
agents.<namespace>.<agent>.<name>
```
For `assistants/myns/myagent/models/record.mod.yao` → model ID is `agents.myns.myagent.record`.
The **actual database table name** is auto-prefixed:
```
prefix = "agents_<namespace>_<agent>_"
table = prefix + table.name (from .mod.yao)
```
So `"table": {"name": "record"}` → actual DB table `agents_myns_myagent_record`.
You never need to reference the prefix directly — always use the model ID.
## Define a Model
```json
{
"name": "Record",
"table": { "name": "record" },
"columns": [
{ "name": "id", "type": "ID", "primary": true },
{ "name": "team_id", "type": "string", "length": 100, "nullable": false, "index": true },
{ "name": "title", "type": "string", "length": 500, "nullable": false },
{ "name": "content", "type": "text", "nullable": true },
{
"name": "status",
"type": "enum",
"option": ["active", "archived"],
"default": "active",
"nullable": false,
"index": true
}
],
"indexes": [
{ "name": "idx_team_status", "columns": ["team_id", "status"], "type": "index" }
],
"option": {
"timestamps": true,
"soft_deletes": true
}
}
```
### Column Types
| Type | Description |
|------|-------------|
| `ID` | Auto-increment primary key |
| `string` | VARCHAR — use `length` to set size |
| `text` / `mediumText` / `longText` | TEXT variants |
| `integer` / `bigInteger` | Integer types |
| `decimal` / `float` | Numeric types |
| `boolean` | Boolean |
| `enum` | Enumeration — requires `option: [...]` |
| `json` | JSON column |
| `date` / `datetime` / `timestamp` | Date/time types |
### Common Options
```json
"option": {
"timestamps": true, // adds created_at, updated_at columns
"soft_deletes": true // adds deleted_at; Delete() soft-deletes instead of removing the row
}
```
## Query from a Hook
The process name format is `models.<model-id>.<Method>`:
```typescript
import { Process } from "@yao/runtime";
const MODEL = "agents.myns.myagent";
export function Create(ctx, messages) {
// Get a list of records
const records = Process(`models.${MODEL}.record.Get`, {
wheres: [
{ column: "team_id", value: ctx.authorized.team_id },
{ column: "status", value: "active" }
],
orders: [{ column: "created_at", option: "desc" }],
limit: 20
});
// Save a record
const id = Process(`models.${MODEL}.record.Create`, {
team_id: ctx.authorized.team_id,
title: "...",
content: "...",
status: "active"
});
return { messages };
}
```
### Built-in Model Processes
| Process | Description |
|---------|-------------|
| `models.<id>.Find(id, params)` | Get one record by primary key |
| `models.<id>.Get(params)` | Get a list of records |
| `models.<id>.Paginate(params, page, pageSize)` | Paginated query |
| `models.<id>.Create(row)` | Insert a record, return ID |
| `models.<id>.Save(row)` | Upsert (create or update) |
| `models.<id>.Update(id, row)` | Update by primary key |
| `models.<id>.Delete(id)` | Delete (soft-delete if enabled) |
| `models.<id>.Destroy(id)` | Hard delete |
## What's Next
Write the business logic that operates on these models:
- **[Scripts & Processes →](./scripts)** — export TypeScript functions as Yao Processes