Rekhu Assistant
Actions, the scan and knowledge
Decide what the assistant may do for your users, let Rekhu propose the actions of a Laravel app, and give the assistant the knowledge of your trade.
What actions are for
An action is something the assistant may do for the user: list their orders, reorder, change an address, export invoices. The assistant decides when to use one; your code does the work, as the signed-in user, with your own checks. The assistant can never do more than the user can do in your app.
This page also covers pages, help and the project's knowledge.
Define an action
In Laravel, an action is a route you already have: put #[Tool] on its controller method. The assistant calls the route as when the user submits the form, so its middleware, form request, route model binding and policies all apply. In Node.js, an action is a function: defineAction checks the input against its schema, then calls run with the user.
use Rekhu\Assistant\Attributes\Tool;
#[Tool('List the user\'s orders, newest first: number, date, status, total. "search" matches an order number.',
name: 'list_orders',
inputs: ['search' => 'An order number or product name'],
fields: ['id', 'number', 'status', 'total'],
label: ['en' => 'Looking up your orders', 'nl' => 'Ik zoek je bestellingen'],
)]
public function index(Request $request) { … }
#[Tool('Cancel an order that has not shipped yet', confirm: true)]
public function cancel(Order $order) { … }
import { defineAction } from '@rekhu/assistant-node';
defineAction({
name: 'list_orders',
description: 'List the user\'s orders, newest first: number, date, status, total. "search" matches an order number.',
input: { search: 'string?' },
inputs: { search: 'An order number or product name' },
fields: ['id', 'number', 'status', 'total'],
label: { en: 'Looking up your orders', nl: 'Ik zoek je bestellingen' },
readonly: true,
run: ({ search }, { user }) => orders.forUser(user, search),
});
defineAction({
name: 'cancel_order',
description: 'Cancel an order that has not shipped yet',
input: { orderNumber: 'string' },
danger: true,
run: ({ orderNumber }, { user }) => orders.cancelFor(user, orderNumber), // throws Forbidden or ActionError
});
Laravel #[Tool] |
Node.js defineAction |
|
|---|---|---|
| What it does, for the assistant | description (first argument) |
description |
| Its name | name, else the route name |
name |
| The input | the form request's rules, or rules |
input: a short form or a JSON Schema |
| What an input means | inputs |
inputs |
| Keep only these fields of a result | fields |
fields |
| What the user reads while it runs | label |
label |
| Ask for a click first | confirm: true; every DELETE route by default |
confirm: true, or danger: true for a red button |
An action that changes something waits for a click on its confirm card; only the click runs it.
Actions without a page
Some questions have no page: which invoices are overdue, the revenue of one shop this month. In Laravel, write a Tool class (php artisan make:assistant-tool OverdueInvoices); it is offered to the users its authorize() allows. In Node.js, any defineAction can be written only for the assistant.
Files
An action can hand the user a file. In Laravel, return a download as you do today (response()->download(), Excel::download()). In Node.js, return file(body, name, type). The user gets a Download button behind a signed link that expires.
#[Tool('Export the transactions as Excel, with the same filters as list_transactions', rules: TransactionFilters::class)]
public function export(TransactionFilters $request)
{
return Excel::download(new TransactionsExport($request->validated()), 'transactions.xlsx');
}
import { defineAction, file } from '@rekhu/assistant-node';
defineAction({
name: 'export_orders',
description: 'The user\'s orders of a period as a CSV file: number, date, status, total',
input: { from: 'string', to: 'string' },
readonly: true,
run: async ({ from, to }, { user }) => file(await orders.csv(user, from, to), 'orders.csv', 'text/csv'),
});
Let Rekhu propose the actions
In a Laravel app, the scan writes the first version of your actions for you:
- Run the scan. Rekhu reads your routes, the inputs they validate and the code of their controller methods. It never reads your records.
- Open
assistant/actions.php. Every route is there, with a description for the assistant, whether it needs the user's click, and whether Rekhu would switch it on. Everything starts off. - Switch on what the assistant may do. Set
'on' => trueon those entries, and commit the file like any other code. - Approve the knowledge. The scan also drafts the project's knowledge; it waits on the project's Assistant tab.
- Test with real questions. The scan writes
assistant/questions.php, a question per action.rekhu:assistant:testasks them as a user and shows which used the right action.
php artisan rekhu:assistant:scan
php artisan rekhu:assistant:test --user=12
An entry in the file has the same shape as #[Tool]: 'on', 'description', and optionally 'confirm', 'fields', 'label', with 'why_off' on what Rekhu proposes to keep off.
- The file is yours. A new scan only adds routes the file does not have yet, switched off. It never changes an entry that is there, and marks entries whose route is gone with
'state' => 'gone'. - The code wins. A route with
#[Tool]is never put in the file. - It grants nothing. Every action still runs through your middleware, policies and validation.
- Kept in Rekhu. Each scan is kept in your account per environment;
--restorewrites the last one back.
--only="orders.*" scans part of the app, --no-code leaves the controller code out, and --dry-run shows what would change. A scan is a few model calls, charged to the project like the assistant's own.
Note: the scan reads Laravel routes. In Node.js the actions are the functions you define, so there is nothing to scan.
Pages and help
Pages give the user a button; the assistant never navigates on its own. In Laravel, every named GET route the user may open is a page; #[Page] adds a description. In Node.js, list them with definePage.
use Rekhu\Assistant\Attributes\Page;
#[Page('Invoices', help: 'All invoices of the account, with download and payment status')]
public function index() { … }
import { definePage } from '@rekhu/assistant-node';
definePage({ name: 'invoices', label: 'Invoices', path: '/invoices', help: 'All invoices of the account, with download and payment status' });
Help is markdown, one topic per file; the first # heading is the title. Laravel reads resources/rekhu/help; Node.js reads the folder in help. The assistant answers how-to questions from it, in the user's language, and says so when the help does not cover a question.
Knowledge
How the assistant behaves for your users belongs in the project's Knowledge in your Rekhu account: what the app is, the words of your trade, the tone, what it never does, when it hands over to a person. It reaches the assistant on every answer, within a minute, without a deploy.
| What | Where |
|---|---|
| How the app works, step by step | help files |
| How the assistant behaves, the words of your trade | the project's Knowledge |
| Rules that belong with the code | instructions in the config |
| What users can click in a fresh chat | quick in the config |
Writing good actions
- The description is for the assistant. Say what the action does, what comes back, what each input means, and when to use it. It is never shown to users.
- Read first, then change. Give the assistant a way to find a record before every action that changes one.
- Say when it does not apply ("of an order that has not shipped yet"), and still refuse in your code when it does not.
rekhu:assistant:check (Laravel) and npx rekhu-assistant check (Node.js) find unclear actions before your users do.
Next
- Laravel or Node.js: the setup around the actions.
- Conversation history: earlier conversations in a list.