yao/agent/docs/models.md
Max b7fc91a312 Update Agent Documentation with MCP Directory Structure and Model Integration
- Added a new section in the MCP documentation detailing the directory structure for assistants, enhancing clarity on how to define namespaced MCP servers.
- Included examples of using assistant models within MCP tools, providing guidance on referencing models in tool configurations.
- Updated the README to include a link to the new Models documentation, improving navigation and resource accessibility for users.
2026-01-01 11:46:12 +08:00

9.1 KiB

Assistant Models

Assistants can define their own namespaced data models in the models/ directory. These models are automatically loaded with the agents.<assistant-id>. prefix and use isolated database tables.

Directory Structure

assistants/
└── my-assistant/
    ├── package.yao
    └── models/
        ├── order.mod.yao       # → agents.my-assistant.order
        ├── item.mod.yao        # → agents.my-assistant.item
        └── nested/
            └── log.mod.yao     # → agents.my-assistant.nested.log

Model Definition

Standard Yao model definition with automatic table prefixing.

models/order.mod.yao

{
  "name": "Order",
  "label": "Order Record",
  "description": "Customer orders",
  "table": {
    "name": "order",
    "comment": "Order records"
  },
  "columns": [
    {
      "name": "id",
      "type": "ID",
      "label": "ID",
      "primary": true
    },
    {
      "name": "order_no",
      "type": "string",
      "label": "Order Number",
      "length": 100,
      "nullable": false,
      "unique": true,
      "index": true
    },
    {
      "name": "customer_id",
      "type": "string",
      "label": "Customer ID",
      "length": 255,
      "nullable": false,
      "index": true
    },
    {
      "name": "total_amount",
      "type": "decimal",
      "label": "Total Amount",
      "precision": 15,
      "scale": 2,
      "nullable": false
    },
    {
      "name": "status",
      "type": "enum",
      "label": "Status",
      "option": ["pending", "confirmed", "shipped", "completed", "cancelled"],
      "default": "pending",
      "nullable": false,
      "index": true
    },
    {
      "name": "metadata",
      "type": "json",
      "label": "Metadata",
      "nullable": true
    }
  ],
  "relations": {
    "items": {
      "type": "hasMany",
      "model": "item",
      "key": "order_id",
      "foreign": "id"
    }
  },
  "indexes": [
    {
      "name": "idx_customer_status",
      "columns": ["customer_id", "status"],
      "type": "index"
    }
  ],
  "option": {
    "timestamps": true,
    "soft_deletes": true
  }
}

Table Naming

Tables are automatically prefixed with agents_<assistant-id>_:

Assistant ID Model File Model ID Table Name
expense models/order.mod.yao agents.expense.order agents_expense_order
tests.demo models/user.mod.yao agents.tests.demo.user agents_tests_demo_user

Using Models

In Hooks

import { Process } from "@yao/runtime";

function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
  // Query assistant's own model
  const orders = Process("models.agents.my-assistant.order.Paginate", {
    wheres: [{ column: "status", value: "pending" }],
    limit: 10,
  });

  return { messages };
}

In MCP Tools

{
  "transport": "process",
  "tools": {
    "list_orders": "models.agents.my-assistant.order.Paginate",
    "get_order": "models.agents.my-assistant.order.Find",
    "create_order": "models.agents.my-assistant.order.Create",
    "update_order": "models.agents.my-assistant.order.Update"
  }
}

In Scripts

src/orders.ts

import { Process } from "@yao/runtime";

export function ListPending(): any[] {
  return Process("models.agents.my-assistant.order.Get", {
    wheres: [{ column: "status", value: "pending" }],
    orders: [{ column: "created_at", option: "desc" }],
  });
}

export function CreateOrder(data: any): any {
  return Process("models.agents.my-assistant.order.Create", data);
}

export function UpdateStatus(id: number, status: string): any {
  return Process("models.agents.my-assistant.order.Update", id, { status });
}

Column Types

Type Description Options
ID Auto-increment primary key primary: true
string VARCHAR length (default: 255)
text TEXT -
integer INT -
bigInteger BIGINT -
float FLOAT precision, scale
decimal DECIMAL precision, scale
boolean BOOLEAN -
date DATE -
datetime DATETIME -
timestamp TIMESTAMP -
json JSON/JSONB -
enum ENUM option: [...]

Column Options

Option Type Description
nullable boolean Allow NULL values
default any Default value
unique boolean Unique constraint
index boolean Create index
primary boolean Primary key
length integer String length
precision integer Decimal precision
scale integer Decimal scale
comment string Column comment

Relations

{
  "relations": {
    "items": {
      "type": "hasMany",
      "model": "item",
      "key": "order_id",
      "foreign": "id"
    },
    "customer": {
      "type": "hasOne",
      "model": "customer",
      "key": "id",
      "foreign": "customer_id"
    }
  }
}
Type Description
hasOne One-to-one relationship
hasMany One-to-many relationship
hasOneThrough Has one through intermediate
hasManyThrough Has many through intermediate

Model Options

{
  "option": {
    "timestamps": true,
    "soft_deletes": true,
    "permission": true
  }
}
Option Description
timestamps Add created_at, updated_at columns
soft_deletes Add deleted_at for soft delete
permission Enable permission checks

Process Reference

Common model processes:

Process Description Arguments
Find Get by ID id, query?
Get Get records query
Paginate Paginated list query, page, pagesize
Create Create record data
Update Update record id, data
Save Create or update data
Delete Delete record id
Destroy Hard delete id
Insert Batch insert columns, rows
UpdateWhere Batch update query, data
DeleteWhere Batch delete query

Migration

Models are automatically migrated when Yao starts. The migration:

  1. Creates tables if not exist
  2. Adds new columns
  3. Creates indexes
  4. Does NOT drop columns (safe migration)

To force schema sync:

yao migrate --reset  # Warning: drops and recreates tables

Example: Complete Assistant with Models

assistants/inventory/package.yao

{
  "name": "Inventory Assistant",
  "connector": "gpt-4o",
  "mcp": {
    "servers": [{ "server_id": "inventory" }]
  }
}

assistants/inventory/models/product.mod.yao

{
  "name": "Product",
  "table": { "name": "product" },
  "columns": [
    { "name": "id", "type": "ID", "primary": true },
    { "name": "sku", "type": "string", "length": 50, "unique": true },
    { "name": "name", "type": "string", "length": 200 },
    { "name": "quantity", "type": "integer", "default": 0 },
    { "name": "price", "type": "decimal", "precision": 10, "scale": 2 }
  ],
  "option": { "timestamps": true }
}

assistants/inventory/mcps/inventory.mcp.yao

{
  "label": "Inventory",
  "transport": "process",
  "tools": {
    "list_products": "models.agents.inventory.product.Paginate",
    "get_product": "models.agents.inventory.product.Find",
    "update_stock": "agents.inventory.stock.Update"
  }
}

assistants/inventory/src/stock.ts

import { Process } from "@yao/runtime";

export function Update(args: { sku: string; quantity: number }): any {
  const product = Process("models.agents.inventory.product.Get", {
    wheres: [{ column: "sku", value: args.sku }],
    limit: 1,
  });

  if (!product || product.length === 0) {
    throw new Error(`Product not found: ${args.sku}`);
  }

  return Process("models.agents.inventory.product.Update", product[0].id, {
    quantity: args.quantity,
  });
}