Skills
L'agent ne voit que des skills. Une skill est une seule déclaration : à quoi elle sert
(cognitive), ce qu'elle appelle (invoke), et — quand ses lignes sont des choses qu'une
personne peut choisir — comment elles deviennent des items (kinds, present), sont réservées
(select), exécutées (submit) et dessinées (render). Les capabilities brutes ne sont jamais
exposées.
Une skill qui propose des items
Une skill qui déclare des kinds prend une opération du tenant et transforme ses lignes en
items qu'une personne peut choisir. Elle a une part dans la sélection, qui porte son nom.
skills:
- name: takeDeliveryOption # le nom de la skill ; c'est aussi le nom de sa part dans la sélection
cognitive: # rendu dans la description de l'outil
objective: "proposer le retrait ou la livraison"
diagnose: [l'adresse du client, l'heure souhaitée]
invoke: {method: getDeliveryOptions} # l'opération qui renvoie les lignes
kinds: # les kinds de ces items, chacun reconnu par son `when`
pickup:
when: "$fields.type == 'pickup'"
select: {} # ce qui change pour ce kind : {} veut dire « aucune garde »
submit: [{name: collect, mode: self}]
delivery:
when: "$fields.type == 'delivery'" # garde le select et le submit de la skill
present: # ce que portent les items : des champs typés (voir plus bas)
fields:
name: {from: name, type: text, role: title}
type: {from: type, type: text} # ce qui distingue les deux kinds
render: {component: cards} # comment les montrer
depends_on: takeMealSelection # pas de livraison pour une sélection vide…
on_loss: clear # …et elle part avec le dernier plat (ou `block`)
select:
max_items: 1 # un seul : en choisir un autre remplace le premier
guards: [...] # voir « Politiques »
submit: [...]
auto_trigger: take-meal-order # s'en servir ouvre ce playbook
cognitive.objectivedit à l'agent quand se servir de la skill ;diagnosece qu'il doit comprendre avant de proposer.invokenomme ce que la skill appelle : une opération des APIs de la station (invoke.method), ou une capability de Convoy (invoke.native, voir plus bas). Une skill àkindssansinvokeest refusée au chargement : ses items viennent d'un appel.kindsnomme les kinds que deviennent ses lignes, chacun reconnu par sonwhen(voir plus bas). Deux skills ne peuvent pas déclarer le même kind.auto_triggernomme le playbook que la skill ouvre quand on s'en sert (Playbooks).
invoke.native — les capabilities de Convoy
invoke.method ne nomme qu'une opération des APIs de la station. Les capabilities de Convoy
lui-même se nomment avec invoke.native :
native | ce que Convoy fait |
|---|---|
search | la recherche sémantique dans le catalogue |
routine | une routine composée par le moteur de bundles (bundles) |
set | un ensemble composé à la demande, un article par élément demandé |
- name: findProducts
invoke: {native: search}
kinds: {product: {}}
Une skill nomme une capability : method ou native, jamais les deux. method: search_products (ou build_routine, build_set) est refusé au chargement, avec l'écriture
native en remède.
params et invoke.args — les arguments fixés par la station
invoke.args fixe des arguments de l'appel. Chacun est une valeur littérale ou $params.<nom>, un
paramètre de la skill déclaré dans params :
- name: findDrinks
params:
category: {default: "149"}
invoke:
method: getMenu
args: {category: "$params.category"}
- La valeur d'un paramètre est son
default, une valeur littérale : il est lu avant l'appel, quand aucun item n'existe encore. Undefaultqui commence par$est refusé. Dans un playbook dont une tâche le lie (paramsde la tâche, voir Playbooks), sa valeur est cette liaison. - Le
defaultest facultatif. Sans lui, seule la liaison d'une tâche fixe le paramètre :invoke.argsne peut donc pas le lire (l'appel d'invoke a lieu avant tout submit), et un paramètre sansdefaultqu'aucune tâche ne lie est refusé au chargement. - Un argument fixé part à chaque appel de la capability de la skill — ses items, la recherche
live et l'amorçage du catalogue, une skill sans
kinds— par-dessus toute valeur envoyée par le modèle. Il est retiré du schéma d'entrée que les deux portes (MCP, agent) annoncent pour la skill : on ne le demande jamais au modèle. - Refusé au chargement : un
$params.xqui ne nomme aucun paramètre déclaré, un paramètre que rien ne lit, une autre référence$…dansinvoke.args, un paramètre sansdefaultlu parinvoke.args. Un argument que le schéma de la capability ne prend pas, ou une valeur d'un autre type que son paramètre (ou hors de sonenum), refuse la station à l'assemblage : l'API l'ignorerait, la convertirait ou la refuserait. $params.<nom>se lit aussi dans lesargsdes appels du submit de la skill (les siens et ceux de ses kinds) : Le submit. Une garde ne le lit pas ; le modèle ne fixe pas un paramètre (set_byn'existe pas encore).
kinds — reconnaître un kind
Un kind a trois clés, toutes facultatives :
| clé | effet |
|---|---|
when | la condition qui dit quelles lignes sont de ce kind |
select | les gardes de ce kind (seulement guards) ; {} veut dire « aucune garde » ; absent, celles de la skill |
submit | l'appel qui dépose les items de ce kind (un seul) ; absent, celui de la skill |
when se lit sur la ligne, avant qu'elle soit un item. C'est une condition du même langage que
les gardes (Politiques), mais elle ne lit que $fields.<nom>,
un champ déclaré dans le present.fields de la skill. Toute autre référence ($item…,
$params…, une clé de la ligne non présentée) est refusée au chargement. La clé kind ou type
d'une ligne n'est jamais lue d'elle-même : pour s'en servir, présentez-la.
- Une ligne est d'un kind quand son
whenest connu pour vrai. Une comparaison indéterminée (un>sur une valeur absente) ne l'est pas ;==et!=lisent une valeur absente commenull($fields.x != '149'est vrai pour une ligne sansx). - Chaque valeur se lit sous le type déclaré de son champ : un nombre dans un champ
textouidest son texte (149vaut'149'), un nombre écrit en texte dans un champ numérique est le nombre. Un littéral d'un autre type que son champ ($fields.category == 149sur un champtext) est refusé au chargement : il ne pourrait jamais être vrai. - Un seul kind est vrai : la ligne est de ce kind. Aucun, ou plusieurs : la ligne n'est pas proposée (c'est journalisé).
- Un kind seul sans
whenprend toutes les lignes. Avec plusieurs kinds, chacun déclare sonwhen, sinon la station est refusée.
Quand une même opération renvoie des items qui ne se traitent pas pareil : chez un restaurant, le
retrait ne demande pas d'adresse, ne coûte rien et n'envoie pas de coursier (l'exemple du début de
la page). Le when d'un kind seul filtre les lignes : une même opération peut nourrir une skill
de plats et une skill de boissons.
- name: takeMealSelection
invoke: {method: getMenu}
kinds: {dish: {when: "$fields.category != '149'"}}
present:
fields:
name: {from: name, type: text, role: title}
category: {from: category, type: text}
- name: takeDrinkSelection
invoke: {method: getMenu}
kinds: {drink: {when: "$fields.category == '149' and $fields.price > 0"}}
present:
fields:
name: {from: name, type: text, role: title}
price: {from: price, type: price, role: billed}
category: {from: category, type: text}
present — des champs typés
present.fields déclare ce que portent les items de la skill. Chaque champ a un nom choisi par la
station, une source (from, le champ de la ligne du tenant ; un chemin pointé, prix.ttc, lit
dans un objet), un type et parfois un rôle :
present:
fields:
name: {from: name, type: text, role: title}
price: {from: price, type: price, role: billed}
image: {from: image_url, type: image}
page: {from: slug, type: url, role: link,
template: "{tenant_url}/article/{value}"}
| rôle | type | ce que la carte en fait |
|---|---|---|
title | text ou id | le nom de la ligne. Sans title, la ligne montre sa référence |
billed | price | le prix de la ligne, dans la devise de la station (global.currency) |
link | url | la page de l'item sur le site du tenant (voir plus bas) |
- Un champ par rôle, au plus. Deux
titlerefusent la station au chargement. - Seul ce qui est déclaré est lu. Sans champ
billed, l'item n'a pas de prix affiché, même si ses lignes ont un champprice. L'image de la carte est le premier champ de typeimage. - Chaque champ est sur l'item sous son nom :
$item.fields.<nom>dans un submit, à côté de la ligne du tenant, et dans une condition (require,when). - Trois noms sont réservés :
namedoit être letitle,pricelebilled,image_urlla premièreimage. Tout autre usage de ces noms est refusé au chargement.code,slugetidsont refusés aussi : Core y écrit la référence de l'item quand il entre dans la sélection. - Une skill à
kindssanspresentne présente rien : ses lignes montrent leur référence. - La cadence d'un prix (
one_time,monthly…) se lit dans le champcadencede la ligne.
Le lien vers le site du tenant. Le champ role: link donne la page de l'item. Son template
n'a que deux marques : {value}, la valeur du champ (encodée pour rester dans son segment de
chemin), et {tenant_url}, le TENANT_URL du déploiement (un préfixe). Un template sans
{value}, ou avec une autre marque, est refusé au chargement ; un template qui ne commence ni par
{tenant_url} ni par http(s):// aussi (il se résoudrait sur l'hôte qui dessine la carte), comme un
template sur un autre champ que le lien. La valeur est lue sur l'item, sous le nom du champ lien. Sans template, la valeur elle-même est le lien, seulement si c'est une adresse
absolue http(s). Sans champ link, la carte n'a pas de lien : Convoy n'invente jamais une
adresse sur le domaine du tenant. La recherche dans le catalogue et la routine prennent le lien de
la skill du catalogue (catalog.from_skill).
Une ligne du catalogue — résolue par sa seule référence (l'article d'une routine, une référence
jamais montrée dans la room) ou lue par un hôte — se lit par le present de la skill qui lit le
catalogue : celle que nomme catalog.from_skill, sinon la skill de recherche native de la station
(invoke: {native: search}). Ses champs title, billed et sa première image donnent le nom, le
prix et l'image de la ligne. Sans une telle skill, une référence que la room n'a jamais vue est
inconnue : rien ne dit comment elle s'appelle ni ce qu'elle coûte. Une skill de catalogue sans champ
billed est signalée au chargement (ses lignes n'ont pas de prix). Deux skills de recherche native qui lisent le titre, le prix ou
l'image d'une ligne dans des champs différents sont refusées au chargement.
Les types de champ
Un champ de present et un champ de formulaire (la garde form de select.guards ou de
kinds.<k>.select.guards, submit_guard.form) prennent leur type dans la même table. Un type hors de la table refuse la
station au chargement.
| type | ce que fait Core |
|---|---|
text | l'affiche |
email, phone | l'écrit d'une seule façon avant qu'il identifie une room |
number, integer | vérifie qu'une réponse en est un (integer : un nombre entier) |
boolean | le vérifie ; dans le formulaire du submit, un boolean obligatoire est un consentement, qui n'accepte que oui |
price | dans la devise de la station ; le champ billed est le prix de la ligne |
quantity | un nombre entier |
id | un identifiant : du texte ou un nombre entier |
url | une adresse ; le champ link est la page de l'item chez le tenant |
image | l'adresse d'une image ; la première est l'image de la carte |
datetime | une date, ou une date et une heure, en texte ISO 8601 |
Dans un formulaire, une réponse est vérifiée selon son type, et un booléen n'est jamais un nombre.
Le modèle apprend qu'un price est « un nombre » et un quantity « un nombre entier » ; les
surfaces dessinent number, integer, price et quantity comme des champs numériques. Seuls
text, email et phone peuvent identifier une room (Auth).
render — comment le résultat est dessiné
render.component prend un nom d'un vocabulaire fermé ; un nom inconnu refuse la station. Sans
render, les items d'une skill à kinds sont des cartes ; pour une skill sans kinds, c'est le
résultat de sa capability qui décide.
| écrire | dessine |
|---|---|
carousel | une rangée de cartes qu'on fait défiler (des articles avec images) |
cards | des cartes à choisir, qui montrent le formulaire de la skill quand il y en a un |
checklist | une liste ordonnée à cocher |
report | un chiffre qui répond à une question (voir plus bas) |
La forme de la part
| clé | effet |
|---|---|
select.max_items: 1 | un seul item : le choix remplace. La remplaçante est annoncée à la personne. |
depends_on, on_loss | lié à la part d'une autre skill, nommée par son nom. Si elle se vide : clear vide celle-ci, block bloque le submit. |
countable: false | un ensemble, pas des quantités : chaque item une fois (des feuilles d'heures, des créneaux). Plus de bouton −/+, et toute demande de quantité est refusée. |
Une skill qui répond
Une skill sans kinds répond avec ce qu'elle appelle : une lecture, un rapport, une routine.
Ses lignes ne sont pas des items.
skills:
- name: rapport-heures-sup
cognitive:
objective: "répondre sur les heures supplémentaires d'une équipe pour un mois"
invoke: {method: getOvertimeReport} # déclarée access: read
render:
component: report
report:
headline: total_overtime_hours
headline_label: "heures supplémentaires"
unit: h
series: weeks
point_label: week_starting
point_value: overtime_hours
highlight: worst_week # marque le point ; ne retrie jamais
- Les clés d'item sont refusées sans
kinds.present,select,submit,countable,depends_oneton_lossfaçonnent des items ; écrites sur une skill sanskinds, rien ne les lirait, et Core refuse la station au chargement. render.reportfait correspondre les champs du tenant au cadre « rapport ». Il n'est lu que parcomponent: report: avec un autre composant, il est refusé au chargement.component: reportsansrender.reportest refusé aussi (Vérifier).yieldsdonne le kind que produit une skill sanskinds(les articles d'une routine) ;presents: selectionmontre le récapitulatif de la sélection au lieu d'appeler une capability ;authenticated: trueréserve la skill à une personne connectée (Auth).
Une skill qui ne fait que répondre
Elle ne se déclare pas : Core la déduit. Une skill est en lecture quand ce qu'elle appelle (son
invoke) est une opération déclarée access: read, et que rien de ce qu'elle montre ne peut être
déposé : pas de submit, pas de garde (select.guards vide), aucune autre skill qui tire son kind
dans un submit ou en dépend (depends_on), ni yields ni presents. Une capability native
(invoke: {native: …}) n'a pas d'accès déclaré : elle ne compte jamais comme une
lecture. La clé read_only n'existe pas ; écrite, elle est refusée.
C'est une garantie de Core : les lignes d'une skill en lecture ne sont jamais retenues comme
proposées, donc aucune porte ne peut les ajouter à la sélection. Core refuse aussi un invoke qui
nomme une opération access: write : l'écriture partirait sans submit ni confirmation.