Prompts conditionnels ({{#if}})

Un bloc {{#if}} n'ajoute un morceau de prompt – le texte que Marinara envoie à l'IA – que si une condition est vraie. Sers-t'en pour donner à un même preset partagé des instructions différentes selon les personnages, les personas ou les chats, ou pour laisser de côté un champ vide. Les conditionnels font partie du système de macros : ils fonctionnent donc partout où les macros fonctionnent, c'est-à-dire dans les fiches de personnage, les personas, les entrées de lorebook et les presets.

Marinara vérifie la condition à chaque construction du prompt, garde le texte qui correspond et écarte le reste. C'est une fonctionnalité avancée, et la plupart des chats n'en ont jamais besoin.

La syntaxe de base

Un bloc commence par {{#if condition}} et se termine par {{/if}}. Ajoute {{else}} pour le cas faux, et {{else if}} pour enchaîner d'autres conditions. Marinara examine chaque branche de haut en bas et garde la première qui est vraie. Si aucune ne l'est et qu'il n'y a pas de {{else}}, le bloc ne produit rien.

{{#if length == "short"}}
Keep your reply to one or two sentences.
{{else if length == "long"}}
Write a detailed, multi-paragraph reply.
{{else}}
Write a reply of normal length.
{{/if}}

Un bloc peut tenir sur une seule ligne ou sur plusieurs, et tu peux imbriquer des blocs.

Opérateurs

Une condition se compose en général d'une valeur de gauche, d'un opérateur et d'une valeur de droite, par exemple char == "Alice".

OpérateurSignification
==, =, isÉgal.
!=, is notDifférent.
>, <, >=, <=Supérieur, inférieur, supérieur ou égal, inférieur ou égal (nombres uniquement).
contains, includesLa valeur de gauche contient la valeur de droite en tant que texte.
not contains, not includesLa valeur de gauche ne contient pas la valeur de droite.

Règles de comparaison :

  1. Avec les opérateurs d'égalité, si les deux côtés ressemblent à des nombres, Marinara les compare comme des nombres : 5 est donc égal à 5.0. Sinon, il compare le texte sans tenir compte de la casse : Mari est donc égal à mari.
  2. Avec >, <, >= et <=, les deux côtés doivent être des nombres. Sinon, la condition est fausse.
  3. contains et includes ignorent la casse : contains "dr" correspond donc à Dr Smith.

Combiner des conditions

Utilise || pour OU et && pour ET. && est évalué avant ||. Ajoute des parenthèses pour fixer l'ordre.

{{#if character == "Maukie" || character == "Pantalone"}}
Use the shared instructions.
{{/if}}

{{#if (character == "Maukie" || character == "Pantalone") && scenario contains "lake"}}
Use the lakeside instructions for either character.
{{/if}}

Pour plusieurs choix d'égalité sur une même valeur, tu peux omettre la partie gauche répétée : character == "Maukie" || "Pantalone". Cela fonctionne avec ==, = et is. Écris des conditions complètes des deux côtés de &&.

Tests de contenu

Une condition sans opérateur est vraie quand la valeur a un vrai contenu. Elle est fausse quand la valeur est vide ou vaut false, 0, no, off, null ou undefined, quelle que soit la casse.

{{#if scenario}}
Current scene: {{scenario}}
{{else}}
No specific scene is set.
{{/if}}

Ce que tu peux comparer

Chaque côté d'une condition peut être :

  1. Un mot-clé de champ ou d'identité, comme char, user, group, persona, description, personality, scenario, input, model ou reasoning. Ces mots-clés lisent les mêmes valeurs que les macros correspondantes. group liste les autres personnages actifs, sans celui qui répond.
  2. Une valeur littérale entre guillemets, comme "Alice".
  3. Un nom de variable de preset, comme length. Voir Variables de preset.
  4. Une recherche de variable explicite, écrite var:name ou var.name.
  5. Une autre macro, résolue en premier.
  6. Une question pour ton modèle de décision : decision:"..." ou decision_choice:"...". Voir Interroger le modèle de décision.

Un mot isolé qui n'est pas un mot-clé est lu comme un nom de variable. Si aucune variable ne porte ce nom, il est utilisé comme simple texte. Mets les valeurs littérales entre guillemets pour éviter la confusion.

Mise entre guillemets

Mets le texte fixe entre guillemets droits, doubles ou simples. À l'intérieur d'une valeur entre guillemets, échappe un guillemet avec une barre oblique inverse, et écris \n pour un retour à la ligne. Mets toujours entre guillemets une valeur littérale qui contient une espace, comme "Dr Smith". Sans guillemets, une valeur de plusieurs mots est lue comme un seul nom de variable.

Blocs de groupe pour plusieurs personnages

Dans un chat de groupe avec deux personnages ou plus, un bloc de groupe répète son texte une fois par personnage. Place [ seul sur une ligne, puis ton texte, puis ] seul sur une ligne. Le bloc doit contenir une macro de personnage, comme {{char}} ou {{description}}, ou une condition portant sur le personnage.

[
{{char}}'s current attitude:
{{#if char == "Alice"}}cheerful and open{{else}}guarded and quiet{{/if}}
]

Avec Alice et Bob, le bloc s'exécute deux fois, une fois par personnage. Hors d'un bloc de groupe, une macro de personnage ne se résout que pour le personnage courant ou principal. Dans un chat avec un seul personnage, les lignes [ et ] restent du texte ordinaire.

Exemples

Un ton différent selon le personnage :

{{#if char == "Dottore"}}
Speak in a cold, clinical tone.
{{else}}
Speak warmly and casually.
{{/if}}

N'inclure un champ que s'il est rempli. Avec un champ Backstory (histoire personnelle) vide, le bloc ne produit rien : aucune étiquette vide n'est envoyée.

{{#if backstory}}
Backstory to remember: {{backstory}}
{{/if}}

Des instructions pour une famille de modèles. model est l'ID du modèle, comme claude-opus-5-5 : contains correspond donc à toutes les versions d'Opus, et == à un seul modèle précis.

{{#if model contains "opus"}}
Keep replies under 400 words.
{{/if}}

Des instructions de planification qui dépendent du raisonnement. Avec le raisonnement activé, quel que soit le niveau, le modèle reçoit la première ligne. Avec le raisonnement désactivé, {{reasoning}} vaut off, ce qui se lit comme faux. Pour agir sur un seul niveau, compare-le, comme dans {{#if reasoning == "max"}}.

{{#if reasoning}}
Plan the reply in your thinking before you write it.
{{else}}
Before replying, write your plan inside <plan></plan> tags.
{{/if}}

Interroger le modèle de décision

Une condition peut demander à ton modèle de décision ce qui se passe dans le chat. Le modèle de décision est un petit modèle qui lit les derniers messages et un énoncé que tu écris, puis indique si l'énoncé est vrai. Il n'écrit jamais dans le chat. Un preset, une fiche, une entrée de lorebook ou un prompt d'agent peut ainsi envoyer une instruction uniquement aux tours où elle s'applique.

Avant de commencer : choisis un modèle de décision sous Decision model (modèle de décision) dans le panneau Connections (Connexions). Modèles de décision explique ce que c'est et comment en choisir un. Pour décider si une entrée de lorebook entière s'active, utilise plutôt son champ Decision.

Quelques idées : décrire un nouveau lieu seulement si la scène a changé d'endroit, charger les règles de rythme du combat uniquement pendant un combat, retenir l'intensification dans un preset à progression lente, ou, dans un bloc de groupe, faire répondre directement le seul personnage interpellé.

Oui ou non : decision:

{{#if decision:"The latest message moves the scene to a new place"}}
Open your reply by describing the new location in one or two sentences.
{{/if}}

La condition est vraie quand le modèle de décision juge l'énoncé vrai. Elle fonctionne avec {{else}}, &&, ||, les parenthèses, l'imbrication et les blocs de groupe. Les macros de l'énoncé sont remplies d'abord : {{user}} et {{char}} fonctionnent donc. Dans un bloc de groupe, un énoncé qui nomme {{char}} est évalué une fois par personnage.

Une réponse parmi plusieurs : decision_choice:

decision_choice: demande au modèle de choisir une option. Les options sont les valeurs auxquelles tu le compares.

{{#if decision_choice:"Kaelen's mood in the latest message" == "angry"}}
Kaelen's lines are short and clipped.
{{else if decision_choice:"Kaelen's mood in the latest message" == "sad"}}
Kaelen speaks quietly and looks away.
{{else}}
Kaelen is his usual self.
{{/if}}

Le modèle choisit entre "angry", "sad" et "none of these". Écris l'énoncé comme un sujet, et les options comme de courtes réponses. La forme courte == "rain" || "snow" propose les deux options.

Pour dire ce qu'une option signifie dans ton histoire, ajoute des critères après elle, séparés par deux-points, avec le même type de guillemets sur les deux parties : == "angry":"Kaelen raises his voice, makes threats or breaks something". Le modèle répond toujours "angry". Si une option reçoit des critères différents à plusieurs endroits, ce sont les premiers qui sont utilisés.

Sticky et cooldown

Ajoute sticky: et cooldown: après un énoncé pour garder sa réponse pendant quelques tours au lieu de l'évaluer à chaque tour.

{{#if decision:"The latest message starts a fight" sticky:3 cooldown:5}}
Keep combat pacing rules in effect.
{{/if}}

Pour une entrée de lorebook activée par son champ Decision, utilise plutôt les réglages Sticky et Cooldown propres à l'entrée.

Until et while

Pour garder un bloc actif tant que dure une situation, ajoute until: ou while: avec un second énoncé.

{{#if decision:"A fight starts in the latest message" until:"The fight ends in the latest message"}}
Keep combat pacing rules in effect.
{{/if}}

Avec sticky, ajoute :and (aussi écrit :restrict) ou :or (aussi écrit :extend) après le second énoncé pour choisir comment les deux se combinent.

Ce que tu écrisAprès le oui, le bloc reste actif
until:"..." sticky:5 ou until:"...":and sticky:5Pendant 5 tours supplémentaires au maximum, et se désactive plus tôt si l'énoncé until est vrai.
until:"...":or sticky:5Pendant au moins 5 tours supplémentaires, puis jusqu'à ce que l'énoncé until soit vrai.
while:"..." sticky:5 ou while:"...":or sticky:5Pendant au moins 5 tours supplémentaires, puis tant que l'énoncé while est vrai.
while:"...":and sticky:5Pendant 5 tours supplémentaires au maximum, et se désactive plus tôt si l'énoncé while est faux.

Vérifier tous les quelques tours

Ajoute every:N pour n'évaluer un énoncé que tous les N tours.

{{#if decision:"The weather changes in the latest message" every:3}}
Describe the new weather in a sentence.
{{/if}}

Priorité

Quand un plan de prompt contient plus d'énoncés que le budget Decision statements per turn, priority:high ou priority:low décide lesquels sont évalués. Sans priorité, elle est moyenne.

{{#if decision:"In the latest message, a character is badly hurt" priority:high}}...{{/if}}
{{#if decision:"The latest message mentions food" priority:low}}...{{/if}}

Tu peux combiner tous les modificateurs dans n'importe quel ordre : decision:"..." priority:high sticky:3 cooldown:5 every:2.

Aucune réponse signifie non

Une condition de décision est fausse dès qu'il n'y a pas de réponse : aucun modèle de décision n'est défini, il n'a pas répondu à temps, ou il a échoué. Pour decision_choice:, toutes les comparaisons sont fausses. Conçois ton contenu en conséquence.

Tout modèle peut se tromper. Écris pour "un modèle de décision", pas pour un produit précis.

Rédiger les énoncés

Pour tester ta formulation :

  1. Sélectionne un modèle sous Decision model, puis clique sur Test (tester). Cela vérifie la connexion avec un exemple fixe. Cela ne teste pas ton énoncé.
  2. Ajoute l'énoncé à ton prompt, puis envoie des messages où il devrait être vrai et d'autres où il devrait être faux.
  3. Utilise Peek Prompt pour voir quelle branche a été envoyée. Ouvre Decision diagnostics (diagnostic des décisions), puis choisis Test decisions (tester les décisions) pour obtenir de nouvelles réponses sans lancer de génération. Les tests hébergés peuvent être payants. Voir Tester les énoncés de décision.
  4. Ajuste la formulation et teste de nouveau. Pour voir les réponses des générations réelles, active Debug Mode ou les logs de débogage du serveur.

Limites et coût

Budget d'énoncés

Decision statements per turn, sous Decision model, vaut 32 par défaut. Ce n'est pas un plafond unique sur toutes les requêtes Decision ni sur les dépenses. Marinara l'applique par étapes.

  1. Les énoncés du prompt principal du chat sont planifiés dans le budget. Les décisions de lorebook utilisent ce qui reste.
  2. Les agents exécutés avant ou pendant la réponse reçoivent un plan combiné avec le prompt principal, qui utilise de nouveau le budget. Cette étape ne soustrait pas ce qu'a utilisé le lorebook : le total peut donc dépasser le réglage.
  3. Les agents de post-traitement reçoivent un budget séparé après la réponse.

Les questions d'activation des agents et l'ordre de réponse Smart sont indépendants de ce réglage.

Seuls les énoncés qu'une étape peut utiliser entrent dans son plan : sections et groupes de preset activés, options de variables sélectionnées et contenu des entrées de lorebook activées. Une condition fixe peut exclure un énoncé, comme {{#if char == "Dottore" && decision:"..."}} quand le personnage est Mira. Une condition de variable ne le peut pas, car les variables peuvent changer pendant la construction du prompt. Les énoncés maintenus par sticky, cooldown ou every: ne prennent aucune place. Les énoncés écartés valent non.

Requêtes et durée

Sur un compte Decision hébergé, un tour peut produire plusieurs requêtes facturées. Les énoncés peuvent être regroupés, mais l'activation des lorebooks, le contenu des entrées nouvellement activées, les correspondances récursives et les phases des agents peuvent nécessiter d'autres lots. Un modèle de chat local ajoute du temps de calcul plutôt que des frais, et répond à un decision_choice: par une question oui/non par option.

Chaque requête a un délai : 1,5 seconde par défaut pour un compte Decision hébergé, ou 4 secondes pour un compte de modèle de chat compatible OpenAI ou pour un modèle local. Voir Délais. Un modèle local de raisonnement s'abstient avant la réponse, sauf si tu actives Also gate agents that run before the reply (évaluer aussi les agents exécutés avant la réponse).

Réutilisation des réponses

Marinara réutilise normalement les réponses réussies pour le même tour et le même modèle de décision : une régénération envoie donc souvent les mêmes branches sans nouvelle requête. Ce cache vit dans le serveur en cours d'exécution et conserve jusqu'à 200 tours. Un redémarrage, un retrait de la mémoire, un dernier message nouveau ou modifié, un autre modèle, ou un énoncé ou un ensemble d'options modifié peuvent nécessiter une nouvelle réponse.

Les réponses absentes ou en échec ne sont pas mises en cache : un nouvel essai peut donc interroger de nouveau le modèle et emprunter une autre branche. Les énoncés des prompts d'agents suivent les mêmes règles. Une relance manuelle d'un agent réutilise les réponses encore en cache pour ses entrées. Voir Énoncés de décision dans le prompt de l'agent.

Cache de prompts

Le cache de prompts du fournisseur est distinct du cache de réponses de Marinara. Il peut réutiliser un début inchangé du prompt envoyé à ton modèle de chat. Changer une branche de décision peut empêcher la réutilisation à partir de ce point, même si une partie antérieure inchangée peut rester admissible. La facturation dépend du fournisseur.

Place les blocs de décision qui changent souvent vers la fin du prompt, par exemple dans les instructions après l'historique ou une note d'auteur peu profonde. Garde une décision près du début uniquement si sa réponse change rarement. Les options de variables de preset fonctionnent de la même façon, car leur texte arrive à l'endroit où apparaît {{name}}.

Sur un compte Anthropic direct avec Enable prompt caching (activer le cache de prompts) actif, Marinara marque la fin du prompt système et un message du chat situé Cache depth (profondeur du cache) messages avant le plus récent (5 par défaut). Un changement avant l'historique peut invalider la frontière système et l'historique qui suit. Un changement après la frontière marquée de l'historique peut préserver la partie en cache. La longueur minimale du cache varie selon le modèle. Consulte le guide actuel du fournisseur : le guide du cache de prompts d'Anthropic ou le guide du cache de prompts d'OpenAI.

Quand une branche de décision n'apparaît jamais

Causes probables, dans l'ordre :

  1. Aucun modèle de décision n'est défini. Toutes les conditions de décision sont fausses. L'éditeur affiche un avertissement sous chaque champ qui en utilise une.
  2. Le modèle de décision ne répond pas : clé incorrecte, plus de crédits ou limite de requêtes sur un compte hébergé, modèle local arrêté ou trop lent, ou modèle de décision installé qui n'a pas démarré.
  3. C'est un modèle de raisonnement qui s'abstient avant la réponse.
  4. Trop d'énoncés à l'étape de planification, au-delà du budget.
  5. Il répond, mais sous son seuil. C'est généralement une question de formulation.

Regarde ce qu'indique Test, et utilise Peek Prompt pour voir les branches envoyées. Il liste les énoncés encore sans réponse, qui valent non. Avec le niveau de log sur debug, chaque énoncé, sa réponse et le fait qu'il se lise ou non comme un oui sont enregistrés dans le log. Voir Niveaux de log. La correction se trouve rarement dans le preset. Il s'agit généralement de la formulation, ou d'une branche qui porte quelque chose dont le prompt ne peut pas se passer.

Guides associés