PDL : Langage de programmation déclaratif à mots guidés
résumés
Les grands modèles de langage (LLM) ont suscité un grand intérêt dans le monde entier, permettant de nombreuses applications d'intelligence artificielle jusqu'alors insaisissables.Les LLM sont contrôlés par des invites textuelles très expressives et renvoient des réponses textuelles. Les LLM sont contrôlés par des invites textuelles très expressives et renvoient des réponses textuelles. Toutefois, ce texte non structuré d'entrée et de sortie rend les applications basées sur les LLM vulnérables. Cette situation a favorisé l'émergence de cadres d'incitation visant à réguler l'interaction des LLM avec le monde extérieur. Cependant, les cadres d'incitation existants ont une courbe d'apprentissage élevée ou privent les développeurs du contrôle sur les incitations précises. Pour résoudre ce dilemme, cet article présente le Prompting Declarative Language (PDL), un langage déclaratif simple orienté vers les données, basé sur YAML, qui met l'incitation au centre de ses préoccupations. Le PDL fonctionne bien avec plusieurs plateformes LLM et LLM, prend en charge l'écriture d'applications interactives qui invoquent les LLM et les outils, et est facile à mettre en œuvre - par exemple, les chatbots, les RAG ou les agents, parmi les cas d'utilisation courants tels que les chatbots, les RAG ou les agents. des cas d'utilisation courants tels que les chatbots, les RAG ou les proxies. Nous espérons que le PDL rendra la programmation rapide plus facile, plus robuste et plus agréable.
1. introduction
Les grands modèles de langage (LLM) ont fait de grands progrès, démontrant leur capacité à effectuer une variété de tâches utiles. Étant donné que les LLM sont contrôlés par des indices de langage naturel, l'ingénierie des indices est devenue une méthode ad hoc pour améliorer la précision (White et al.2023). L'apprentissage par le biais de modèles de repères, tels que l'apprentissage contextuel (Brown et al.2020), de multiples chaînes d'appel LLM (Chase et al.2022), la génération améliorée (RAG) (Lewis et al.2020), l'utilisation d'outils (Schick et al.2023), le modèle de langage auxiliaire procédural (PAL) (Gao et al.2023) et des agents (Yao et al.2023) peut débloquer davantage de fonctionnalités. Cependant, malgré sa puissance, LLM reste fragile : il lui arrive d'halluciner ou même de ne pas respecter la syntaxe et les types attendus.
Le cadre de repérage (Liu et al.2023) permet aux développeurs d'utiliser plus facilement LLM et les hint patterns associés tout en réduisant leur vulnérabilité. Certains cadres, tels que LangChain (Chase et al.2022) et AutoGen (Wu et al.2023), en fournissant des fonctionnalités spécifiques pour des modèles populaires tels que les RAG ou les proxies. Toutefois, ces fonctions peuvent priver les utilisateurs du contrôle des messages-guides de base et les obliger à apprendre de nombreuses fonctions complexes du cadre. En revanche, les cadres d'assistance de bas niveau tels que Guidance (Microsoft.2023) et LMQL (Beurer-Kellner et al.2023) qui offrent plus de contrôle grâce à la syntaxe et aux types. Cependant, ils exigent que les utilisateurs programment dans des langages impératifs tels que Python ou TypeScript. Les frameworks situés à l'autre extrémité du spectre, tels que DSPy (Khattab et al.2023) et Vieira (Li et al.2024), ce qui permet d'éviter les invites manuscrites en les générant automatiquement. Malheureusement, cela enlève encore plus de contrôle au développeur. La question est donc de savoir comment rendre la programmation LLM plus robuste et maintenir le développeur dans le siège du conducteur tout en conservant la simplicité.
Pour résoudre ce problème, nous nous appuyons sur des idées éprouvées en matière de conception de langages de programmation. Le principe d'orthogonalité préconise l'utilisation d'un ensemble restreint et simple de fonctions qui sont combinées pour obtenir une fonctionnalité puissante (van Wijngaarden et al.1977). Dans ce contexte, l'orthogonalité consiste à éviter autant que possible les cas particuliers. Pour les cadres d'indices, l'orthogonalité est un moyen d'éviter des caractéristiques spécifiques. Ensuite, si le langage peut passer la vérification de type et de rôle (Hugging Face, le2023), les développeurs auront moins de mal à gérer la vulnérabilité. Une tension insoluble subsiste : d'une part, nous voulons que les développeurs puissent contrôler des indices précis et, d'autre part, nous avons besoin d'un langage déclaratif simple. C'est pourquoi nous avons choisi un langage orienté données qui brouille délibérément la frontière entre les procédures (par exemple pour les chaînes et les outils) et les données (pour les indices). Cette inspiration est venue de la vieille notion de code en tant que données (McCarthy.1960), et des travaux fondamentaux sur la programmation sans couche (Cooper et al.2006).
Cet article présente le Prompt Declaration Language (PDL), un langage orthogonal et typé orienté données. Contrairement à d'autres langages d'invite intégrés dans des langages impératifs, le PDL est basé sur YAML (Ben-Kiki et al.2004YAML est un format de sérialisation des données qui est à la fois lisible par l'homme (en facilitant une syntaxe simple pour les chaînes non structurées) et structuré (compatible avec JSON). Les variables dans PDL contiennent également des valeurs JSON et utilisent éventuellement JSON Schema (Pezoa et al.2016PDL est actuellement mis en œuvre par un interprète qui effectue une vérification dynamique des types. L'un des avantages de la représentation des programmes sous forme de données est la facilité de transformation des programmes (Mernik et al.2005), par exemple pour l'optimisation. Le rendu des programmes dans un format de représentation des données peut même permettre aux programmes PDL de générer des programmes PDL à l'aide d'un grand modèle de langage, similaire à PAL (Gao et al.2023).
Les programmes PDL sont constitués de blocs (objets YAML), dont chacun ajoute des données au contexte de l'invite. Cet état d'esprit est idéal pour une utilisation avec des technologies d'incitation telles que les chatbots ou les agents : l'exécution du programme construit implicitement le dialogue ou la trajectoire sans pipelining explicite. PDL prend en charge les LLM natifs, ainsi que les LLM hébergés par de multiples fournisseurs, y compris, mais sans s'y limiter, le modèle open-source Granite sur IBM Watsonx1 et Replicate2 (Abdelaziz et al.2024; Granite Team, IBM.2024PDL fournit des boucles et des structures de contrôle conditionnelles, ainsi que l'introduction de fonctions et de fichiers pour la modularité. PDL utilise Jinja2 (Ronacher.2008) pour modéliser non seulement des indices, mais aussi des programmes entiers.
Cet article présente une vue d'ensemble de PDL à l'aide d'un exemple introductif (Section 2), suivie d'une description détaillée de la langue (Section 3). Il décrit les outils permettant d'exécuter et d'éditer des programmes PDL (Section 4), et fournit des études de cas pour démontrer d'autres applications de la PDL (Section 5). Enfin, le document aborde les travaux connexes (Section 6), et enSection 7La PDL est une source ouverte et peut être consultée sur le site Web de la Commission européenne. https://github.com/IBM/prompt-declaration-language Comment l'utiliser. Dans l'ensemble, PDL est un nouveau langage de programmation rapide LLM simple mais puissant.
2. vue d'ensemble
Cette section présente une vue d'ensemble des fonctionnalités de la PDL à travers un exemple de chatbot. Un programme PDL exécute une série de motte (de terre)Les données générées par chaque bloc sont intégrées au contexte d'arrière-plan. Il existe plusieurs types de blocs qui peuvent générer des données de différentes manières : appels de modèle, lecture de données à partir de stdin ou de fichiers, création directe de diverses données JSON et exécution de code. En outre, il existe une variété de blocs de contrôle (if-then-else, for et repeat) qui permettent aux utilisateurs de PDL d'exprimer des pipelines de données riches et des applications d'intelligence artificielle.
carte 1(a) montre le code PDL d'un simple chatbot. Le bloc read : des lignes 1 à 4 imprime un message demandant à l'utilisateur d'entrer une requête et la lit à partir de stdin. Fig. 1(b) Une trace de l'exécution du même programme est montrée. Par exemple, un utilisateur peut demander "Qu'est-ce qu'une salade de langue ?" . Pour éviter les répétitions, la clause "attribut : [contexte]" place la réponse de l'utilisateur dans le contexte d'arrière-plan, mais pas le résultat (ce qui est imprimé sur stdout).
Le bloc repeat:until : des lignes 5 à 16 contient un bloc text : imbriqué, qui contient à son tour une séquence de deux blocs imbriqués. Les blocs text : convertissent les résultats de leurs blocs imbriqués en chaînes de caractères et les concaténent. Le bloc model : des lignes 7 à 9 appelle un grand modèle de langage (LLM) qui utilise le contexte accumulé actuel comme indice. Lors de la première itération de la boucle, le contexte ne comprend que deux lignes : "Quelle est votre requête ?" et "Qu'est-ce qu'une salade de langue ?". Le paramètre de modèle "stop : [\n\n]" fait en sorte que le LLM arrête de générer des jetons après que deux sauts de ligne consécutifs aient été générés. l'interpréteur LLM imprime la sortie LLM en vert ; Fig. 1(b) montre que dans cet exemple, LLM a généré "A language salad is [...]". Le bloc read : des lignes 10 à 15 imprime le message à l'aide de la syntaxe de chaîne multiligne de YAML (commençant par une ligne verticale (|)). Cet exemple montre comment PDL fait passer l'invite en premier, tout en la rendant facile à lire et en donnant au développeur un contrôle précis. La trace de l'interpréteur à droite montre que l'utilisateur a tapé "Say it as a poem !", qui est défini comme la variable question à gauche à la ligne 10 et ajouté au contexte à la ligne 12. La clause until : à la ligne 16 spécifie que l'expression Jinja2 '${question == "quit"}’
PDL utilise la syntaxe '${...}' pour intégrer les modèles Jinja2 au lieu de '{{...}'. pour intégrer les modèles Jinja2 au lieu de "{{...}} au lieu de "{{...}}", car cette dernière est incompatible avec les caractères spéciaux de YAML (accolades).
Dans la deuxième itération de la boucle, le contexte contient les effets de la première itération de la boucle. Ainsi, la deuxième exécution du bloc model : voit la sortie de la première exécution et peut la paraphraser comme un poème, "In a world where many tongues [...]", comme le montre le schéma suivant 1(b). Finalement, au cours de l'exécution du deuxième bloc read : de cet exemple, l'utilisateur tape "quit", ce qui met fin à la boucle. Maintenant que nous avons vu certains blocs PDL courants (read :, repeat :, text : et model :) en action, nous pouvons passer au deuxième bloc read :. 3 qui décrit les blocs restants et les caractéristiques de la langue.

(a) Code
- read:
contribute: [context]
message: |
您的查询是什么?
- repeat:
text:
- model: watsonx/ibm/granite-13b-chat-v2
parameters:
stop: ["\n\n"]
- def: question
read:
contribute: [context]
message: |
输入查询或说“quit”退出。
until: ${question == "quit"}
(b) Recherche d'interprètes
您的查询是什么?
什么是语言沙拉?
语言沙拉是一个术语,用于描述在单一对话或文本中混合不同语言和方言。它可以被看作是[…]
输入查询或说“quit”退出。
用诗的形式表达!
在语言众多的世界中,
语言沙拉诞生,喜悦中成长。
词语交织,和谐中流动,
五彩缤纷的语言,活力绽放。
输入查询或说“quit”退出。
quit
Figure 1 : Chatbot simple en PDL
3. la langue

Figure 2 : Référence rapide PDL
PDL est un langage intégré à YAML, ce qui fait de chaque programme PDL un document YAML valide conforme à l'architecture PDL. Figure 2 est une référence rapide à la PDL et est expliquée dans cette section à l'aide des règles de syntaxe. Un programme est un bloc ou une liste de blocs, un bloc pouvant être une expression ou un bloc structuré, comme le montrent les règles syntaxiques suivantes :
pdl ::= bloc | [bloc, . . . ,bloc]bloc ::= expression | bloc_structuré
Toutes les règles syntaxiques de cette section utilisent la syntaxe de style YAML (par exemple, [block, ...,block]). ...,block]). Le même code PDL peut également être rendu par la syntaxe de style bloc de YAML, par exemple :
- bloc
... - bloc
Chaque bloc contient un corps de bloc avec un mot-clé indiquant le type de bloc (par exemple, modèle ou lecture). Il existe 15 types de corps de bloc (les champs facultatifs sont annotés de points d'interrogation) :
corps_du_bloc ::=
model:expression,input:?pdl,parameters:?
expression
| read:file,message:?
string,message:?
bool
| text:pdl
| lastOf:pdl
| array:pdl
| object:pdl
| data:json
| include:file
| function:args,return:pdl
| call:𝑓,args:args
| if:expression,then:pdl,else:?pdl
| for:args,repeat:𝑝𝑑𝑙,join:?
join
| repeat:pdl,num_iterations:n,join:?
join
| repeat:pdl,until:expression,join:?
join
| code:pdl,lang:string
Nous avons déjà vu les blocs model : et read : dans la section précédente. Les blocs model : invoquent le grand modèle de langage. Les invites sont tirées du contexte actuel, sauf si le champ facultatif input : est spécifié. Le champ optionnel parameters : est utilisé pour configurer le comportement d'inférence du modèle. Le bloc read : lit les données d'entrée depuis un fichier, ou depuis l'entrée standard si aucun nom de fichier n'est spécifié. Le champ optionnel message : est utilisé pour afficher un message à l'utilisateur, le champ optionnel multiline : détermine s'il faut s'arrêter aux sauts de ligne.
Les cinq types de blocs permettant de créer des données sont les suivants : text :, lastOf :, array :, object : et data :. Figure 2 Elles sont illustrées dans un exemple simple. La différence entre le bloc object : et le bloc data : est que l'interpréteur PDL ignore le mot-clé PDL dans le bloc data : et le traite comme un champ JSON normal.
Pour des raisons de modularité, PDL prend en charge les blocs include : et les fonctions. Les blocs include : ouvrent les programmes PDL à un chemin relatif spécifié et ajoutent leur sortie à l'endroit où elle apparaît. La syntaxe des arguments des fonctions est la suivante :
args::={x:exp ression,... ,x:expres sion}
Chaque x:expressi on associe les noms des paramètres à des spécifications de type (dans la définition de la fonction) ou à des valeurs (dans l'appel de la fonction). Le mot-clé return : fournit le corps de la fonction, qui peut contenir des blocs imbriqués ; figure 2 Un exemple d'expression Jinja2 simple est présenté. Le mot-clé facultatif pdl_context : réinitialise le contexte lors d'un appel, par exemple au contexte vide [].
Il existe trois types de blocs de contrôle : if :, for : et diverses formes de répétition. Ils peuvent contenir des blocs imbriqués ou de simples expressions ; s'ils contiennent une liste de blocs, la liste se comporte comme le dernier : par défaut. Si vous ne souhaitez pas le comportement lastOf :, une approche courante consiste à encapsuler le corps de la boucle dans un bloc text :, ou à fusionner les résultats des itérations de la boucle à l'aide du mot-clé join: :
join::=comme: ? (text∣array∣lastOf),with: ? chaîne
Les 15 blocs ci-dessus peuvent être utilisés en combinaison avec zéro ou plusieurs mots-clés facultatifs qui s'appliquent à n'importe quel bloc :
structuré bloc::=
{ block_body,
description:?
string,
def:?
x,
defs:?
defs,
role:?
string,
contribute:?
contribute,
parser:?parser,
spec:?
type }
description : est un commentaire spécial. def : assigne le résultat d'un bloc à une variable ; figure 1 Il existe déjà un exemple à la ligne 10 du PDL. En revanche, defs : crée plusieurs définitions de variables, chacune avec son propre nom, x, et attribue des valeurs par le biais d'une procédure PDL imbriquée :
defs::={x:pdl ,...,x:pdl}
Les appels PDL au modèle de dialogue en ligne suivent la pratique courante des API de dialogue en ligne modernes en transmettant des paires de séquences {contenu:str, rôle:str}, plutôt que du texte brut, en tant qu'invites. L'API de modèle applique alors le modèle de chat spécifique au modèle et aplatit la séquence en insérant les balises de contrôle appropriées, ce qui confère au programme PDL une certaine indépendance par rapport au modèle. Si le bloc ne spécifie pas explicitement le rôle :, le bloc de modèle prend par défaut le rôle d'"assistant" et les autres blocs celui d'"utilisateur". Les rôles des blocs intégrés sont cohérents avec ceux des blocs extérieurs. Dans de futures recherches, nous prévoyons également de mettre en œuvre des mécanismes de sécurité basés sur les permissions et utilisant les rôles.
Le mot-clé contribute : peut être utilisé pour spécifier un sous-ensemble (éventuellement vide) pour deux destinations "résultat" ou "contexte". Par défaut, chaque module contribue à son propre résultat et au contexte d'arrière-plan utilisé pour les appels ultérieurs au Grand Modèle de Langage (LLM). Figure 1 La ligne 2 montre un exemple de restriction des contributions des modules au contexte général afin de simplifier le résultat.
Le mot-clé parser : permet à un module qui ne génère normalement que des chaînes plates (par exemple, un appel LLM) de générer des données structurées. Les analyseurs supportés sont json, yaml, regex et jsonl. spec : mot-clé spécifiant le type. Les types pour PDL sont un sous-ensemble de JSON Schema (Pezoa et al. 2016), Fig. 2 Une brève démonstration de la syntaxe abrégée courante est présentée dans le document suivant. Par exemple, le type "{questions : [str], answers : [str]}" est un objet contenant deux champs pour les questions et les réponses, qui contiennent tous deux des tableaux de chaînes de caractères. Le premier champ 5 Cette section montrera comment les mots-clés parser : et spec : fonctionnent ensemble. Les travaux futurs utiliseront également ces mots-clés pour le décodage des contraintes (Scholak et al. 2021).
Un bloc atomique est une expression :
expression ::= bool | number | string | ${𝑗𝑖𝑛𝑗𝑎_𝑒𝑥𝑝𝑟𝑒 𝑠𝑠𝑖𝑜𝑛} | string_expression
Les expressions peuvent être des valeurs de base, des expressions Jinja2 (Ronacher. 2008Jinja2 est un moyen pratique de spécifier des modèles pour les indices, où certaines parties de l'indice sont codées en dur et d'autres sont complétées par des expressions. Cependant, la PDL étend encore l'utilisation de Jinja2 en permettant aux développeurs de créer des modèles non seulement pour des indications individuelles, mais aussi pour des chaînes d'appel de modèle entières et d'autres modules. Bien que nous recommandions aux lecteurs de se référer à la documentation de Jinja2 pour obtenir une liste complète des expressions possibles, la Figure 2 La PDL n'utilise que des expressions Jinja2, à l'exclusion des déclarations Jinja2 telles que {% if .. %}
peut-être {% for .. %}
parce qu'elles se chevauchent déjà avec les fonctions if : et for : de PDL.
Enfin, PDL dispose d'un module code : qui permet d'exécuter du code dans un langage de programmation donné (à ce jour, seul Python est pris en charge). La section suivante décrit les outils PDL, y compris l'interpréteur, qui offre une fonctionnalité de bac à sable pour réduire le risque d'exécution de code arbitraire. Pour plus d'informations, voir le lien vers le tutoriel dans le dépôt GitHub de PDL.
4. les outils
Le PDL fournit des outils qui facilitent l'écriture, l'exécution et la compréhension des programmes PDL.
Premièrement, le PDL interprète est un moteur d'exécution avec une interface de ligne de commande, comme on peut l'attendre d'un langage de script. L'interpréteur supporte le mode streaming, où la sortie LLM est progressivement visible au fur et à mesure qu'elle est générée, offrant ainsi une expérience de chat plus interactive. L'interpréteur prend également en charge le sandboxing, ce qui lui permet d'être lancé dans un conteneur et est recommandé lors de l'exécution d'actions ou de code générés par LLM.
PDL Support IDE VSCode a été amélioré pour faciliter l'écriture du code PDL grâce à la mise en évidence de la syntaxe, à l'auto-complétion, aux infobulles pour les mots-clés PDL et à la vérification des erreurs. Ces fonctionnalités s'appuient en partie sur le méta-mode PDL, le schéma JSON qui définit un PDL valide.
%%pdl
unité de magie Grâce aux carnets Jupyter, les développeurs peuvent écrire des unités de code directement dans PDL. De cette manière, la plateforme de carnets hébergés peut être utilisée comme un simple terrain de jeu pour l'exploration interactive des messages-guides. Étant donné que plusieurs unités de code PDL se trouvent dans le même carnet, les unités ultérieures peuvent utiliser des variables définies dans les unités précédentes. En outre, le contexte de l'unité ultérieure est repris de l'unité précédente ; lorsque cela n'est pas souhaité, les développeurs peuvent utiliser la fonction %%pdl --reset-context
pour ignorer ce comportement.
PDL Visualisateur de documents en temps réel Une trace spécifique de l'exécution du programme PDL est affichée sous la forme de boîtes colorées imbriquées, semblables au graphique typique que l'on trouve dans une dissertation ou un article de blog sur les conseils LLM. L'utilisateur peut ensuite sélectionner l'une des boîtes pour afficher le code PDL correspondant, de la même manière qu'une cellule de tableur affiche des données, mais l'utilisateur peut les sélectionner pour examiner la formule qui a généré ces données. Cette vue en temps réel permet à l'utilisateur de comprendre rapidement les données spécifiques et de passer ensuite à la compréhension du code qui a généré ces données.
Enfin, le PDL dispose d'une SDK(Software Development Kit), une petite bibliothèque Python permettant d'appeler des PDL à partir de Python, ce qui est utile pour étendre des applications Python plus importantes afin d'utiliser des procédures basées sur des indices, telles que les agents. Comme décrit dans la section 3 Comme nous l'avons vu à la section , les fichiers PDL peuvent contenir des blocs de code Python. Lors du développement d'applications plus importantes à l'aide de PDL, nous avons trouvé utile de limiter ce code à quelques lignes, en définissant la fonction dans un fichier Python distinct et en l'appelant ensuite à partir de PDL. Une bonne pratique consiste à transmettre les données entre PDL et Python sous la forme d'objets JSON. En option, vous pouvez utiliser la fonction spec:
et TypedDict ou Pydantic du côté de Python pour la vérification des types, comme le montre la section suivante. 3 Représenté.
(a) Codes PDL
1text:
2- lang: python
3 code: |
4 import rag_mbpp
5 PDL_SESSION.mbpp = rag_mbpp.initialize()
6 result = ""
7- defs:
8 test_query: >-
9 编写一个 Python 函数,从字符串中删除给定字符的第一个和最后一个出现。
12 retrieved:
13 lang: python
14 spec: [{query: str, answer: str}]
15 code: |
16 import rag_mbpp
17 result = rag_mbpp.retrieve(
18 PDL_SESSION.mbpp, "${test_query}", 5
19 )
20 text: >
21 给定文本在 "Q:" 之后,生成一个 Python 函数在 "A:" 之后。
24 这里有一些示例,请完成最后一个:
25- for:
26 few_shot_sample: ${retrieved}
27 repeat: |
28 Q: ${few_shot_sample.query}
29 A: ‘‘‘${few_shot_sample.answer}‘‘‘
30- |-
31 Q: ${test_query}
32 A:
33- model: watsonx/ibm/granite-3-8b-instruct
34 parameters:
35 stop: ["Q:", "A:"]
(b) Code Python
1from typing import TypedDict
2import datasets
3from sklearn.feature_extraction.text \
4 import TfidfVectorizer
5
6def initialize():
7 train_in = datasets.load_dataset(
8 "mbpp", "sanitized", split="train"
9 )
10 corpus = [row["prompt"] for row in train_in]
11 tfidf = TfidfVectorizer().fit(corpus)
12 def embed(text):
13 sparse_result = tfidf.transform(
14 raw_documents=[text]
15 )
16 return sparse_result.toarray().flatten()
17 train_em = train_in.map(
18 lambda row: {"em": embed(row["prompt"])}
19 )
20 vec_db = train_em.add_faiss_index("em")
21 return vec_db, embed
22
23QA = TypedDict("QA", {"query":str,"answer":str})
24def retrieve(mbpp, query, n: int) -> list[QA]:
25 vec_db, embed = mbpp
26 key = embed(query)
27 nearest = vec_db.get_nearest_examples(
28 "em", key, n
29 )
30 queries = nearest.examples["prompt"]
31 answers = nearest.examples["code"]
32 return [
33 {"query": q, "answer": a}
34 for q, a in zip(queries, answers)
35 ]
Figure 3. RAG Exemple en PDL
5. études de cas
Nous sommes dans la première 2 Un exemple simple de chatbot PDL a été présenté dans cette section. Cette section présente des cas d'utilisation un peu plus complexes de la PDL : les RAG, les mandataires et la génération de PDL à partir de PDL.
5.1 Génération de l'amélioration de la récupération
Génération améliorée par la recherche, ou RAGqui permet d'abord d'extraire le contexte pertinent, puis de l'ajouter aux messages-guides du modèle pour générer des réponses (Lewis et al. 2020). Fig. 3(a) Montre un programme PDL utilisant RAG pour récupérer un petit nombre d'exemples pour une tâche de génération de code. Code : Les lignes 2 à 6 utilisent Python pour initialiser une base de données vectorielle de séquences d'entraînement pour l'ensemble de données MBPP de "programmes Python essentiellement basiques" (Austin et al., 2008). 2021). Il utilise le graphique 3Python définie en (b), et une variable spéciale PDL_SESSION qui permet de transmettre l'état à des blocs de code ultérieurs. La figure 3Les lignes 8 à 11 de (a) initialisent la variable test_query à la requête en langage naturel qui a généré le code Python. Les lignes 12 à 19 initialisent la variable retrieved aux cinq exemples les plus similaires des données d'apprentissage.
Les lignes 20-24 ajoutent des instructions au contexte, les lignes 25-29 ajoutent une poignée d'exemples au contexte, et les lignes 30-32 ajoutent des requêtes de test au contexte. La boucle for : de la ligne 25 est un moyen courant de générer des données à l'aide de PDL, dans ce cas pour l'apprentissage contextuel. Enfin, les lignes 33-35 appellent un modèle Granite 3 (Granite Team, IBM 2024), en utilisant le contexte cumulatif qui lui permet de générer des fonctions Python qui testent les requêtes. Bien qu'il s'agisse d'un exemple simple, nous utilisons également PDL avec Codellm-Devkit (Krishna et al. 2024) est utilisé en conjonction avec un outil qui analyse statiquement le code source à partir d'une variété de langages de programmation afin de récupérer d'autres contextes pertinents lorsque le LLM est invité à effectuer des tâches de codage.
(a) Code
1text:
2- read: react_few_shot_samples.txt
3- |
4
5 Hudson River 的发现者是什么时候出生的?
6- repeat:
7 text:
8 - def: thought
9 model: watsonx/ibm/granite-34b-code-instruct
10 parameters:
11 stop: ["Act:"]
12 include_stop_sequence: true
13 - def: action
14 model: watsonx/ibm/granite-34b-code-instruct
15 parameters:
16 stop: ["\n"]
17 parser: json
18 spec: {name: str, arguments: {topic: str}}
19 - def: observation
20 if: ${ action.name == "Search" }
21 then:
22 text:
23 - "Obs: "
24 - lang: python
25 code: |
26 import wikipedia
27 query = "${ action.arguments.topic }"
28 result = wikipedia.summary(query)
29 until: ${ action.name != "Search" }
(b) Recherche d'interprètes
科罗拉多造山运动东部区域的海拔范围是多少?
Tho: 我需要搜索科罗拉多造山运动,找出东部区域的范围。
Act: {”name”: ”Search”, ”arguments”: {”topic”: ”科罗拉多造山运动”}}
Obs: 科罗拉多造山运动是一个事件 […]
[…]
Hudson River 的发现者是什么时候出生的?
Tho: 我需要搜索 Hudson River 的发现者,找出他是什么时候出生的。
Act: {”name”: ”Search”, ”arguments”: {”topic”: ”Hudson River 的发现者”}}
Obs: Hudson River 是一条 315 英里长的 […]
Tho: Hudson River 的发现者是 Henry Hudson。我需要搜索 Henry Hudson,找出他是什么时候出生的。
Act: {”name”: ”Search”, ”arguments”: {”topic”: ”Henry Hudson”}}
Obs: Henry Hudson (约 1565 年 – 消失 […]
Tho: Henry Hudson 于 1565 年出生。Act: {”name”: ”Finish”, ”arguments”: {”topic”: ”1565”}}
Figure 4. ReAct agir au nom de qqn. dans une position de responsabilité
5.2 Agent ReAct
Basé sur un grand modèle linguistique agir au nom de qqn. dans une position de responsabilité Permet la sélection et la configuration de modèles linguistiques étendus mouvementsen matrice dans lequel ces actions sont exécutées, et la sortie de ces actions est renvoyée au grand modèle linguistique en tant que tenir compte. Il existe différents modèles pour ces agents, tels que ReAct (Yao et al. 2023) et ReWOO (Xu et al. 2023). Les actions sont basées sur un grand modèle linguistique Appel d'outil (Schick et al. 2023), tandis que les agents enchaînent plusieurs appels d'outils dans une séquence dynamique d'amorçage d'un grand modèle linguistique. L'objectif est de rendre les applications basées sur l'IA moins prescriptives et plus orientées vers les objectifs. En outre, l'agent peut utiliser les observations comme un retour d'information pour se remettre d'une action qui a mal tourné.
carte 4 Au cœur du ReAct se trouve une boucle penser-agir-observer, qui est représentée dans le code par des définitions de variables pour penser (ligne 8), agir (ligne 13) et observer (ligne 19). La pensée est le langage naturel de la génération de modèles, par exemple, dans la figure 4Je dois rechercher le découvreur de la rivière Hudson pour savoir quand il est né" dans la trace de l'interprète de (b). L'action est le JSON généré par le modèle pour correspondre aux outils du modèle Granite utilisant les données d'apprentissage (Abdelaziz et al. 2024). Les lignes 17 et 18 du côté gauche garantissent que la sortie du grand modèle de langage est analysée en JSON et qu'elle est conforme au schéma {nom, arguments}, tandis que la trace de l'interpréteur du côté droit montre que le modèle génère effectivement de tels objets. Cela permet d'utiliser Jinja2 pour accéder aux champs de l'objet, comme ${ action.arguments.topic } à la ligne 27. Les observations sont générées par l'environnement, dans ce cas le code Python qui appelle Wikipedia. Comme le montre la ligne 27, l'objet 4 Comme indiqué dans la section , pour les cas impliquant l'exécution d'un code généré (partiellement) à partir d'un grand modèle de langage, nous recommandons d'utiliser les capacités de sandboxing de PDL.
carte 4La trace de l'interpréteur en (b) montre que cette exécution comporte deux itérations de la boucle de l'agent. Bien qu'il s'agisse d'un exemple simple, nous avons également mis en œuvre un agent d'édition de code utilisant la PDL, qui a été utilisé dans le classement SWE-bench Lite dans le cadre du commit4 (Jimenez et al. 2024). La soumission résout la première instance de 23,7% en utilisant uniquement le modèle open source, ce qui est plus élevé que tous les résultats précédents utilisant le modèle open source et comparable aux résultats du modèle frontière.
5.3 Générer des PDL à partir de PDL à l'aide du Big Language Model
Les sections précédentes ont montré comment les développeurs humains peuvent utiliser les PDL pour coder différents modèles de repères. Cette génération de méta-PDL est utile lorsque de grands modèles de langage doivent créer des plans de résolution de problèmes, par exemple dans le cadre du flux de travail d'un agent. Traditionnellement, ces plans sont uniquement constitués de texte, de JSON ou de code Python. Avec PDL, ces plans peuvent être une combinaison de modèles entièrement exécutables et d'appels de code. Cette section explore l'utilisation de la méta-génération PDL sur l'ensemble de données GSMHard5 .
GSMHard est une version plus difficile de GSM8k qui contient des problèmes de mathématiques de l'année scolaire nécessitant un raisonnement arithmétique ou symbolique simple. GSMHard contient une entrée, l'énoncé du problème de mathématiques, et une sortie, le code Python qui résout le problème. Nous avons implémenté PAL (Gao et al. 2023), mais au lieu de générer du code Python, il est demandé au grand modèle linguistique de générer du PDL. La pensée textuelle en chaîne est représentée sous forme de blocs de texte PDL, tandis que l'arithmétique est effectuée à l'aide de blocs de code PDL.
carte 6 Un programme PDL qui génère du code PDL et l'exécute dans le même programme est présenté. La variable demos contient un petit nombre d'exemples destinés à apprendre au modèle comment générer du code PDL. À la ligne 32, un bloc d'appel de modèle utilise ces exemples et un problème avec des variables libres comme entrée. Le résultat est un programme PDL permettant de résoudre le problème. La ligne 38 extrait le programme PDL et l'exécute en Python. Le programme est appliqué à l'ensemble de données GSMHard, où le problème alimente le problème d'entrée.
Cette expérience a révélé que 10% pour l'ensemble de données GSMHard était en fait erroné car la vérité de terrain n'était pas cohérente avec les questions posées. Fig. 6 Des exemples de ces incohérences sont présentés. L'utilisation de PDL a permis de mettre en évidence ces incohérences, car le code PDL généré est lisible par l'homme, ce qui nous a permis de vérifier facilement les points de données qui ne correspondaient pas à la vérité de terrain et de constater que la vérité de terrain était erronée dans certains cas. Nous avons utilisé un grand modèle linguistique pour couvrir l'ensemble de la base de données et avons systématiquement sélectionné les exemples qui semblaient incohérents. Nous avons ensuite vérifié manuellement les résultats pour éliminer les faux positifs et avons identifié les points de données de 10% présentant ce problème.

1 定义:
2 示例:
3 数据:
4 文本:
5 |
6 ...
7
8 问题: Roger 有 5 个网球。
9 他又购买了 2 罐网球。
10 每罐有 3 个网球。
11 现在他总共有多少个网球?
12
13 答案:
14 ```
15 文本:
16 - "Roger 起初有\n"
17 - 定义: tennis_balls
18 数据: 5
19 - "\n个网球。\n"
20 - "2 罐,每罐有 3 个网球,总共是\n"
21 - 语言: python
22 定义: bought_balls
23 代码: result = 2 * 3
24 - "\n个网球。\n"
25 - "结果是:\n"
26 - 语言: python
27 定义: RESULT
28 代码: result = ${ tennis_balls } + ${ bought_balls }
29 ```
30 原始: true
31 文本:
32 - 模型: watsonx/meta-llama/llama-3-70b-instruct
33 定义: PDL
34 输入:
35 文本:
36 - ${ demos.text }
37 - "问题: ${ question }"
38 - 语言: python
39 代码: |
40 from pdl.pdl import exec_str
41 s = """${ PDL }"""
42 pdl = s.split("```")[1]
43 result = exec_str(pdl)
44 定义: RESULT
Figure 5.
James 决定每周跑 1793815 次冲刺
每次冲刺 60 米。
他每周总共跑多少米?
def solution():
sprints_per_day = 1793815
days_per_week = 3
meters_per_sprint = 60
total_sprints = sprints_per_day * days_per_week
total_meters = total_sprints * meters_per_sprint
result = total_meters
return result
Figure 6 : Points de données de l'échantillon de problèmes GSMHard
6. travaux connexes
Une étude récente estime que Cadres de repérage Défini comme une couche qui gère, simplifie et facilite l'interaction entre le LLM et les utilisateurs, les outils ou d'autres modèles (Liu et al.).2023). L'étude souligne que la courbe d'apprentissage abrupte constitue l'une des principales faiblesses du cadre de repérage.
Le cadre d'incitation le plus populaire aujourd'hui est probablement LangChain (Chase et al.2022La principale motivation de MiniChain est précisément d'éviter cette complexité (Rush.2023), qui offre moins de fonctionnalités simples pouvant être combinées pour des applications avancées. Cependant, LangChain et MiniChain sont des frameworks basés sur Python, ce qui les rend plus non déclaratifs car les développeurs doivent écrire du code impératif. PDL a une motivation similaire à MiniChain, mais va plus loin en utilisant YAML plutôt que Python comme base.
Comme pour d'autres cadres d'incitation, l'objectif de PDL est de rendre LLM plus robuste.2023) est un cadre basé sur Python qui offre une conception plus structurée, mais qui est plus primitif que LangChain. De même, LMQL (Beurer-Kellner et al.2023PDL s'inspire quelque peu de LMQL dans son entrelacement d'indices et de programmation, mais contrairement à LMQL, il repose moins sur le code impératif de Python.Crouse et al. utilisent des machines à états finis pour spécifier formellement le flux interne de diverses boucles intelligentes (Crouse et al.2024) ; bien qu'il s'agisse d'une entreprise fascinante, elle n'introduit pas un langage de repères sophistiqué.
L'un des avantages des langages spécifiques à un domaine est qu'ils peuvent mettre en œuvre des transformations de programme, par exemple à des fins d'optimisation (Mernik et al.2005Le cadre de repérage DSPy (Khattab et al.2023) a pour devise "Programming, not hints" (programmation, pas de conseils) : il génère des conseils automatiquement, de sorte que les développeurs n'ont pas besoin de les écrire manuellement. De même, Vieira (Li et al.2024) étend Prolog pour utiliser le LLM comme une relation probabiliste et génère automatiquement des invites.DSPy et Vieira sont tous deux des cadres très avancés, mais contrairement à PDL, ils affaiblissent tous deux le contrôle du développeur sur les invites spécifiques.Lale (Baudart et al.2021) est un langage qui permet aux utilisateurs d'ajuster progressivement le compromis entre l'automatisation et le contrôle dans le pipeline de l'IA, mais il ne se concentre pas sur les indices LLM.DSPy, Vieira, et Lale optimisent la performance prédictive, tandis que l'autre optimisation cible la performance de calcul.SGLang (Zheng et al.2023) y parvient en utilisant mieux le cache des préfixes, ce qui se traduit par un plus grand nombre d'occurrences dans le cache KV (Kwon et al.2023). Les travaux futurs examineront si la nature déclarative de PDL peut permettre des optimisations similaires des performances de calcul.
Récemment, un certain nombre de cadres d'incitation basés sur la modélisation du langage étendu (LLM) ont vu le jour, en se concentrant sur les agents LLM, AutoGen (Wu et al.2023) est un cadre multi-agents dans lequel tout le contenu est constitué d'agents et de dialogues. D'autres cadres multi-agents incluent CrewAI (Moura.2023) et GPTSwarm (Zhuge et al.2024). PDL, tout en supportant également les procurations, adopte une position plus équilibrée, traitant les procurations comme une technique de repérage parmi d'autres.
7. conclusion
PDL est un langage déclaratif axé sur les données : les programmes sont constitués de blocs YAML, chacun d'entre eux étant constitué de données littérales ou génératives. Le modèle de pensée consiste à exécuter un bloc en ajoutant ses données à un contexte d'arrière-plan, qui est utilisé comme indice par les appels ultérieurs au modèle de langage plus large. Cette thèse présente le langage à l'aide d'exemples de programmes et d'une visite guidée de la syntaxe et des outils. La nature déclarative du langage facilite également la mise en œuvre d'optimisations automatiques pour la vitesse, la précision et la sécurité, qui seront mises en œuvre progressivement dans les travaux futurs :https://github.com/IBM/prompt-declaration-language.
© déclaration de droits d'auteur
Article copyright Cercle de partage de l'IA Tous, prière de ne pas reproduire sans autorisation.
Articles connexes
Pas de commentaires...