PDL: Lenguaje de Programación Declarativa por Palabras

resúmenes

Los grandes modelos lingüísticos (LLM) han despertado un gran interés en todo el mundo y han hecho posibles muchas aplicaciones de inteligencia artificial que antes resultaban difíciles de conseguir. Sin embargo, este texto no estructurado de entrada y salida hace que las aplicaciones basadas en LLM sean vulnerables. Esto ha impulsado el surgimiento de marcos de insinuación que pretenden regular la interacción de los LLM con el mundo externo. Sin embargo, los marcos de sugerencias existentes tienen una curva de aprendizaje elevada o privan a los desarrolladores del control sobre las sugerencias precisas. Para abordar este dilema, este artículo presenta el Lenguaje Declarativo de Indicaciones (PDL). PDL es un sencillo lenguaje declarativo dirigido a datos, basado en YAML, que pone las indicaciones en su núcleo. PDL funciona bien con múltiples plataformas LLM y LLMs, soporta la escritura de aplicaciones interactivas que invocan LLMs y herramientas, y es fácil de implementar por ejemplo, chatbots, RAGs o agentes, entre casos de uso comunes como chatbots, RAGs o proxies. Esperamos que el PDL haga que la programación rápida sea más fácil, más robusta y más agradable.

 

1. Introducción

Los grandes modelos lingüísticos (LLM) han progresado mucho y han demostrado su capacidad para realizar diversas tareas útiles. Dado que los LLM se controlan mediante pistas del lenguaje natural, la ingeniería de pistas se ha convertido en un método ad hoc para mejorar la precisión (White et al.2023). El aprendizaje a través de patrones de señalización, como el aprendizaje contextual (Brown et al.2020), múltiples cadenas de llamadas LLM (Chase et al.2022), generación mejorada (RAG) (Lewis et al.2020), el uso de herramientas (Schick et al.2023), el modelo de lenguaje auxiliar procedimental (PAL) (Gao et al.2023) y agentes (Yao et al.2023) puede desbloquear más funciones. Sin embargo, a pesar de su potencia, LLM sigue siendo frágil: a veces alucina o incluso no se ciñe a la sintaxis y los tipos esperados.

El marco de cueing (Liu et al.2023) permite a los desarrolladores utilizar LLM y patrones de sugerencias relacionados más fácilmente, al tiempo que reduce su vulnerabilidad. Algunos marcos, como LangChain (Chase et al.2022) y AutoGen (Wu et al.2023), proporcionando características específicas para patrones populares como RAGs o proxies. Sin embargo, estas características pueden privar a los usuarios del control sobre los avisos básicos y obligarles a aprender muchas características complejas del marco. En cambio, los marcos de avisos de bajo nivel, como Guidance (Microsoft.2023) y LMQL (Beurer-Kellner et al.2023) que proporcionan un mayor control a través de la sintaxis y los tipos. Sin embargo, requieren que los usuarios programen en lenguajes imperativos como Python o TypeScript. Frameworks en el otro extremo del espectro, como DSPy (Khattab et al.2023) y Vieira (Li et al.2024), evitando por completo las indicaciones escritas a mano mediante la generación automática de indicaciones. Desafortunadamente, esto le quita aún más el control al desarrollador. Por tanto, la cuestión es cómo hacer que la programación LLM sea más robusta y mantener al desarrollador en el asiento del conductor, al tiempo que se mantiene la simplicidad.

Para resolver este problema, nos basamos en ideas de diseño de lenguajes de programación de eficacia probada. El principio de ortogonalidad defiende el uso de un conjunto pequeño y sencillo de funciones que se combinan para lograr una funcionalidad potente (van Wijngaarden et al.1977). En este contexto, ortogonalidad significa evitar en lo posible los casos especiales. Para los marcos de sugerencias, la ortogonalidad es una forma de evitar características específicas. A continuación, si el lenguaje puede pasar la comprobación de tipos y funciones (Hugging Face, el2023) reforzado estructuralmente, los desarrolladores tendrán menos problemas con la vulnerabilidad. Sigue existiendo una tensión irresoluble: por un lado, queremos que los desarrolladores sean capaces de controlar pistas precisas y, por otro, necesitamos un lenguaje declarativo sencillo. Por este motivo, hemos optado por un lenguaje orientado a los datos que difumina deliberadamente la línea entre procedimientos (por ejemplo, para cadenas y herramientas) y datos (para pistas). Esta inspiración procede de la antigua noción de código como datos (McCarthy.1960), y un trabajo seminal sobre la programación sin capas (Cooper et al.2006).

Este artículo presenta el Prompt Declaration Language (PDL), un lenguaje ortogonal y tipado orientado a datos. A diferencia de otros lenguajes de avisos incrustados en lenguajes imperativos, PDL se basa en YAML (Ben-Kiki et al.2004YAML es un formato de serialización de datos que es legible (al facilitar una sintaxis sencilla para cadenas no estructuradas) y estructurado (compatible con JSON). Las variables en PDL también contienen valores JSON y opcionalmente utilizan JSON Schema (Pezoa et al.2016PDL se implementa actualmente mediante un intérprete que realiza una comprobación de tipos dinámica. Una de las ventajas de representar los programas como datos es la facilidad de conversión de programas (Mernik et al.2005), por ejemplo para la optimización. La renderización de programas en un formato de representación de datos puede incluso facilitar que los programas PDL generen programas PDL a través de un gran modelo de lenguaje, similar a PAL (Gao et al.2023).

Los programas PDL se componen de bloques (objetos YAML), cada uno de los cuales añade datos al contexto del prompt. Esta forma de pensar es ideal para su uso con tecnologías de avisos como chatbots o agentes: la ejecución del programa construye implícitamente el diálogo o la trayectoria sin pipelining explícito. PDL admite LLM nativos, así como LLM alojados por múltiples proveedores, incluidos, entre otros, el modelo de código abierto Granite en IBM Watsonx1 y Replicate2 (Abdelaziz et al.2024Equipo Granite, IBM.2024PDL proporciona bucles y estructuras de control condicionales, así como la introducción de funciones y archivos para la modularidad.PDL utiliza Jinja2 (Ronacher.2008) para crear plantillas no sólo de sugerencias, sino de programas enteros.

Este artículo ofrece una visión general de PDL a través de un ejemplo introductorio (Sección 2), seguida de una descripción detallada del lenguaje (Sección 3). Describe las herramientas para ejecutar y editar programas PDL (Sección 4), y proporciona estudios de casos para demostrar otras aplicaciones del PDL (Sección 5). Por último, se analizan trabajos relacionados (Sección 6), y enSección 7PDL es de código abierto y puede encontrarse en la página https://github.com/IBM/prompt-declaration-language Cómo conseguirlo. En general, PDL es un nuevo lenguaje de programación de LLM sencillo pero potente.

 

2. Panorama general

Esta sección ofrece una visión general de la funcionalidad de PDL a través de un ejemplo de chatbot. Un programa PDL ejecuta una serie de terrón (de tierra)Los datos generados por cada bloque se aportan al contexto de fondo. Hay varios tipos de bloques que pueden generar datos de diferentes maneras: llamadas a modelos, lectura de datos de stdin o archivos, creación directa de varios datos JSON y ejecución de código. Además, hay una variedad de bloques de control (if-then-else, for y repeat) que permiten a los usuarios de PDL expresar ricas canalizaciones de datos y aplicaciones de IA.

busque 1(a) muestra el código PDL para un chatbot sencillo. El bloque read: de las líneas 1-4 imprime un mensaje pidiendo al usuario que introduzca una consulta y la lee de stdin. Fig. 1(b) Se muestra una traza de la ejecución del mismo programa. Por ejemplo, un usuario podría preguntar "¿Qué es una ensalada de idiomas?". . Para evitar repeticiones, la cláusula "atributo: [contexto]" pone la respuesta del usuario en el contexto de fondo, pero no el resultado (lo que se imprime en stdout).

El bloque repeat:until: de las líneas 5-16 contiene un bloque text: anidado, que a su vez contiene una secuencia de dos bloques anidados. Los bloques text: convierten los resultados de sus bloques anidados en cadenas y las concatenan. El bloque model: de las líneas 7-9 llama a un gran modelo de lenguaje (LLM) que utiliza el contexto acumulado actual como pista. En la primera iteración del bucle, el contexto consta de sólo dos líneas: "¿Cuál es su consulta?" y "¿Qué es una ensalada de lenguaje?". El parámetro del modelo 'stop: [\n\n]' hace que el LLM deje de generar tokens después de que se hayan generado dos saltos de línea consecutivos. el intérprete de LLM imprime la salida del LLM en verde; Fig. 1(b) muestra que en este ejemplo, LLM generó "Una ensalada de lenguaje es [...]". El bloque read: en las líneas 10-15 imprime el mensaje usando la sintaxis de cadena multilínea de YAML (comenzando con una línea vertical (|)). Este ejemplo muestra cómo PDL pone el prompt en primer lugar, a la vez que lo hace fácil de leer y da al desarrollador un control preciso. La traza del intérprete a la derecha muestra que el usuario ha escrito "¡Dilo como un poema!", que se define como la variable question a la izquierda en la línea 10 y se añade al contexto en la línea 12. La cláusula until: de la línea 16 especifica el contexto de la pregunta. La cláusula until: de la línea 16 especifica que la expresión Jinja2 '${question == "quit"}’ PDL utiliza la sintaxis '${...}' para incrustar plantillas Jinja2 en lugar de '{{...}}' en lugar de '{{...}}', porque esta última es incompatible con los caracteres especiales de YAML (llaves).

En la segunda iteración del bucle, el contexto contiene los efectos de la primera iteración del bucle. Así, la segunda ejecución del bloque model: ve la salida de la primera ejecución y puede parafrasearla como un poema, "En un mundo donde muchas lenguas [...]" como se muestra aquí 1(b). Finalmente, durante la ejecución del segundo bloque read: de este ejemplo, el usuario teclea "quit", haciendo que el bucle termine. Ahora que hemos visto algunos bloques PDL comunes (read:, repeat:, text: y model:) en acción, podemos pasar al segundo bloque read:. 3 que describe el resto de bloques y características del lenguaje.

PDL:声明式提示词编程语言

(a) Código

- 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) Localización de intérpretes

您的查询是什么?
什么是语言沙拉?
语言沙拉是一个术语,用于描述在单一对话或文本中混合不同语言和方言。它可以被看作是[…]
输入查询或说“quit”退出。
用诗的形式表达!
在语言众多的世界中,
语言沙拉诞生,喜悦中成长。
词语交织,和谐中流动,
五彩缤纷的语言,活力绽放。
输入查询或说“quit”退出。
quit

Figura 1. Chatbot sencillo en PDL

 

3. Lengua

PDL:声明式提示词编程语言

Figura 2. Referencia rápida del PDL

PDL es un lenguaje incrustado en YAML, por lo que cada programa PDL es un documento YAML válido que se ajusta a la arquitectura PDL. Figura 2 es una referencia rápida a la PDL y se explica en esta sección utilizando las reglas sintácticas. Un programa es un bloque o una lista de bloques, donde un bloque puede ser una expresión o un bloque estructurado, como se muestra en las siguientes reglas sintácticas:

pdl ::= bloque | [bloque, . . . ,bloque]bloque ::= expresión | bloque_estructurado

Todas las reglas sintácticas de esta sección utilizan la sintaxis de estilo flujo de YAML (por ejemplo, [bloque, ...,bloque]. ...,bloque]). El mismo código PDL también se puede representar como sintaxis de estilo bloque de YAML, por ejemplo:

  • bloque
    ...
  • bloque

Cada bloque contiene un cuerpo de bloque con una palabra clave que indica el tipo de bloque (por ejemplo, modelo o lectura). Hay 15 tipos de cuerpos de bloque (los campos opcionales están marcados con signos de interrogación):

PDL:声明式提示词编程语言 cuerpo_bloque ::=

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

Ya hemos visto los bloques model: y read: en la sección anterior. Los bloques model: invocan el gran modelo de lenguaje. Las entradas se toman del contexto actual a menos que se especifique el campo opcional input:. El campo opcional parameters: se utiliza para configurar el comportamiento de inferencia del modelo. El bloque read: lee la entrada de un archivo, o de la entrada estándar si no se especifica un nombre de archivo. El campo opcional message: se utiliza para mostrar un mensaje al usuario, el campo opcional multiline: determina si se detiene en los saltos de línea.

Los cinco tipos de bloque para crear datos son: text:, lastOf:, array:, object: y data:. Figura 2 Se muestran en un ejemplo sencillo. La lista de bloques sin la palabra clave se muestra como lastOf:. La diferencia entre el bloque object: y el bloque data: es que el intérprete PDL ignora la palabra clave PDL en el bloque data: y lo trata como un campo JSON normal.

Para modularidad, PDL soporta bloques include: y funciones. Los bloques include: abren programas PDL en una ruta relativa especificada y añaden su salida a la ubicación donde aparece. La sintaxis de los argumentos de las funciones es la siguiente:

args::={x:exp resión,... ,x:expres sión}

Cada x:expressi on asigna nombres de parámetros a especificaciones de tipos (en la definición function:) o valores (en la llamada a función:). return: keyword proporciona el cuerpo de la función, que puede contener bloques anidados; figura 2 Se muestra un sencillo ejemplo de expresión Jinja2. La palabra clave opcional pdl_context: restablece el contexto durante una llamada, por ejemplo, al contexto vacío [].

Existen tres tipos de bloques de control: if:, for: y varias formas repeat:. Pueden contener bloques anidados o expresiones simples; si contienen una lista de bloques, la lista se comporta por defecto como lastOf:. Si no desea el comportamiento lastOf:, un enfoque común es encapsular el cuerpo del bucle en un bloque text:, o fusionar los resultados de las iteraciones del bucle utilizando la palabra clave join::

join::=as:? (text∣array∣lastOf),with:? cadena

Los 15 bloques anteriores pueden utilizarse en combinación con cero o más palabras clave opcionales que se aplican a cualquier bloque:

estructurado bloque::=

{ block_body,
description:?
string,
def:?
x,
defs:?
defs,
role:?
string,
contribute:?
contribute,
parser:?parser,
spec:?
type }

description: es un comentario especial. def: asigna el resultado de un bloque a una variable; figura 1 Ya existe un ejemplo en la línea 10 de la PDL. Por el contrario, defs: crea múltiples definiciones de variables, cada una con su propio nombre, x, y asigna valores a través de un procedimiento PDL anidado:

defs::={x:pdl ,...,x:pdl}

rol: Asigna un rol específico, como "usuario", "asistente" o "sistema", a los datos generados por el bloque.Las llamadas PDL al modelo de chat siguen la práctica común de las API de chat modernas al pasar pares de secuencias {content:str, role:str}, en lugar de texto plano, como prompts. A continuación, la API del modelo aplica la plantilla de chat específica del modelo y aplana la secuencia insertando las etiquetas de control adecuadas, lo que proporciona al programa PDL cierta independencia del modelo. Si el bloque no especifica explícitamente el rol:, el bloque modelo adopta por defecto el rol de "asistente" y los demás bloques el de "usuario". Los roles de los bloques incrustados son coherentes con los de los bloques externos. En futuras investigaciones, también planeamos implementar mecanismos de seguridad basados en permisos utilizando roles.

La palabra clave contribute: puede utilizarse para especificar un subconjunto (posiblemente vacío) a dos destinos 'resultado' o 'contexto'. Por defecto, cada módulo contribuye a su propio resultado y al contexto de fondo utilizado para las siguientes llamadas al Large Language Model (LLM). Figura 1 La línea 2 muestra un ejemplo de restricción de las contribuciones de los módulos sólo al contexto de fondo para simplificar el resultado.

La palabra clave parser: permite que un módulo que normalmente sólo genera cadenas planas (por ejemplo, una llamada LLM) genere datos estructurados. Los analizadores compatibles son json, yaml, regex y jsonl. La palabra clave spec: especifica el tipo. Los tipos para PDL son un subconjunto de JSON Schema (Pezoa et al. 2016), Fig. 2 Una breve demostración de la sintaxis abreviada común se muestra en. Por ejemplo, el tipo '{preguntas: [cadena], respuestas: [cadena]}' es un objeto que contiene dos campos para preguntas y respuestas, ambos con matrices de cadenas. El primer campo 5 La sección mostrará cómo funcionan conjuntamente parser: y spec:. El trabajo futuro también utilizará estas palabras clave para la decodificación de restricciones (Scholak et al. 2021).

Un bloque atómico es una expresión:

expresión ::= bool | número | cadena | ${𝑗𝑖𝑛𝑗𝑎_𝑒𝑥𝑝𝑟𝑒 𝑠𝑠𝑖𝑜𝑛} | cadena_expresión

Las expresiones pueden ser valores básicos, expresiones Jinja2 (Ronacher. 2008Jinja2 es una forma práctica de especificar plantillas para sugerencias, donde partes de la sugerencia están codificadas y otras partes se rellenan con expresiones. Sin embargo, la PDL amplía aún más el uso de Jinja2 al permitir a los desarrolladores crear plantillas no sólo para sugerencias individuales, sino para cadenas completas de llamadas a modelos y otros módulos. Aunque recomendamos a los lectores que consulten la documentación de Jinja2 para obtener una lista completa de las expresiones posibles, la figura 2 La PDL sólo utiliza expresiones Jinja2, excluyendo sentencias Jinja2 como {% if .. %} tal vez {% for .. %}porque ya se solapan con las funciones if: y for: de PDL.

Por último, pero no por ello menos importante, PDL tiene un módulo code: que permite la ejecución de código en un lenguaje de programación determinado (en el momento de escribir este artículo, sólo se admite Python). En la siguiente sección se describen las herramientas de PDL, incluido el intérprete, que proporciona una función de sandboxing para reducir el riesgo de ejecutar código arbitrario. Para más información, consulta el enlace al tutorial en el repositorio GitHub de PDL.

 

4. Herramientas

La PDL proporciona herramientas para que los programas PDL sean fáciles de escribir, ejecutar y comprender.

En primer lugar, el PDL intérprete es un motor de ejecución con una interfaz de línea de comandos, como cabría esperar de un lenguaje de scripting. El intérprete soporta el modo streaming, en el que la salida LLM es visible progresivamente a medida que se genera, proporcionando así una experiencia de chat más interactiva. El intérprete también soporta sandboxing, lo que permite lanzarlo en un contenedor y se recomienda cuando se ejecutan acciones o código generados por LLM.

PDL Soporte IDE VSCode se ha mejorado para facilitar la escritura de código PDL mediante el resaltado de sintaxis, el autocompletado, la información sobre herramientas para palabras clave PDL y la comprobación de errores. Estas características son impulsadas en parte por el meta-modo PDL, el esquema JSON que define un PDL válido.

%%pdl unidad de magia Mejorado con Jupyter Notebooks, los desarrolladores pueden escribir unidades de código directamente en PDL. De este modo, la plataforma de cuadernos alojada puede utilizarse como un simple patio de recreo para la exploración interactiva de indicaciones. Dadas varias unidades de código PDL en el mismo cuaderno, las unidades posteriores pueden utilizar variables definidas en unidades anteriores. Además, el contexto de fondo de la unidad posterior se transfiere de la unidad anterior; cuando no se desea, los desarrolladores pueden utilizar la función %%pdl --reset-context para anular este comportamiento.

PDL Visualizador de documentos en tiempo real Se muestra una traza específica de la ejecución del programa PDL en forma de cuadros anidados de colores, similar al gráfico típico que se encuentra en una disertación o en una entrada de blog sobre consejos LLM. A continuación, el usuario puede seleccionar uno de los recuadros para visualizar el código PDL correspondiente, de forma similar a una celda de una hoja de cálculo que muestra datos, pero el usuario puede seleccionarlos para examinar la fórmula que generó esos datos. Esta vista en tiempo real permite al usuario comprender rápidamente los datos específicos y luego pasar a comprender el código que generó esos datos.

Por último, el PDL tiene un SDK(Kit de desarrollo de software), una pequeña biblioteca de Python para llamar a PDL desde Python, lo que resulta útil para ampliar aplicaciones Python de mayor tamaño y utilizar procedimientos basados en sugerencias, como los agentes. Como se describe en la sección 3 Como se discutió en la Sección , los archivos PDL pueden contener Python en bloques de código. Cuando desarrollamos aplicaciones más grandes usando PDL, encontramos útil mantener este código en unas pocas líneas, definiendo la función en un archivo Python separado y luego llamándola desde PDL. Una buena práctica es pasar datos entre PDL y Python en forma de objetos JSON. Opcionalmente, puedes usar la función spec: y TypedDict o Pydantic en el lado Python para la comprobación de tipos, como se muestra en la siguiente sección. 3 Se muestra.

PDL:声明式提示词编程语言 (a) Códigos 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) Código 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 ]

Figura 3. RAG Ejemplo en PDL

5. Casos prácticos

Estamos en la primera 2 En la sección se ha visto un ejemplo sencillo de chatbot PDL. Esta sección mostrará algunos casos de uso ligeramente más complejos para PDL: RAGs, proxies y generación de PDLs a partir de PDLs.

5.1 Recuperar la generación de mejoras

Generación mejorada por recuperación, o RAGque primero recupera el contexto pertinente y luego lo añade a las preguntas del modelo para generar respuestas (Lewis et al. 2020). Fig. 3(a) Muestra un programa PDL que utiliza RAG para recuperar un pequeño número de ejemplos para una tarea de generación de código. Código: Las líneas 2-6 utilizan Python para inicializar una base de datos vectorial de divisiones de entrenamiento para el conjunto de datos MBPP de "programas Python mayoritariamente básicos" (Austin et al., 2008). 2021). Utiliza el gráfico 3Python definida en (b), y una variable especial PDL_SESSION que permite pasar estado a bloques de código posteriores. Figura 3Las líneas 8-11 de (a) inicializan la variable test_query con la petición en lenguaje natural que generó el código Python. Las líneas 12-19 inicializan la variable recuperada con los cinco ejemplos más similares de los datos de entrenamiento.

Las líneas 20-24 añaden instrucciones al contexto, las líneas 25-29 añaden un puñado de ejemplos al contexto, y las líneas 30-32 añaden consultas de prueba al contexto. El bucle for: de la línea 25 es una forma habitual de generar datos con PDL, en este caso para el aprendizaje contextual. Por último, las líneas 33-35 llaman a un modelo Granite 3 (Granite Team, IBM 2024), utilizando el contexto acumulativo que hace que genere funciones Python que comprueban las peticiones de consulta. Aunque se trata de un ejemplo sencillo, también utilizamos PDL con Codellm-Devkit (Krishna et al. 2024) se utiliza junto con una herramienta que analiza estáticamente el código fuente de diversos lenguajes de programación para recuperar otros contextos relevantes cuando se pide al LLM que realice tareas de codificación.

PDL:声明式提示词编程语言 (a) Código

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) Localización de intérpretes

科罗拉多造山运动东部区域的海拔范围是多少?
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”}}

Figura 4. ReAct actuar en nombre de algn. en un puesto de responsabilidad

5.2.Agente ReAct

Modelización de grandes lenguajes basada en actuar en nombre de algn. en un puesto de responsabilidad Permite seleccionar y configurar grandes modelos lingüísticos movimientoen matriz en el que se ejecutan estas acciones, y la salida de las acciones se devuelve al modelo de lenguaje grande como el atención. Existen diferentes modelos para este tipo de agentes, como ReAct (Yao et al. 2023) y ReWOO (Xu et al. 2023). Las acciones se basan en un amplio modelo lingüístico Llamada a la herramienta (Schick et al. 2023), mientras que los agentes encadenan múltiples llamadas a herramientas en una secuencia dinámica de arranque de grandes modelos lingüísticos. El objetivo es que las aplicaciones basadas en IA sean menos prescriptivas y más orientadas a objetivos. Además, el agente puede utilizar las observaciones como retroalimentación para recuperarse cuando las acciones van mal.

busque 4 En el corazón de ReAct hay un bucle pensar-actuar-observar, que se representa en el código como definiciones de variables para pensar (línea 8), actuar (línea 13) y observar (línea 19). Pensar es el lenguaje natural de la generación de modelos, por ejemplo, en la figura 4Necesito buscar al descubridor del río Hudson para saber cuándo nació' en la traza del intérprete de (b). La acción es el JSON generado por el modelo para que coincida con las herramientas del modelo Granite utilizando los datos de entrenamiento (Abdelaziz et al. 2024). Las líneas 17 y 18 del lado izquierdo garantizan que la salida del modelo de lenguaje grande se analiza como JSON y se ajusta al esquema {nombre, argumentos}, mientras que la traza del intérprete del lado derecho muestra que el modelo genera efectivamente dichos objetos. Esto permite utilizar Jinja2 para acceder a los campos del objeto, como ${ action.arguments.topic } en la línea 27. Las observaciones son generadas por el entorno, en este caso el código Python que llama a Wikipedia. Como se muestra en la línea 27, el 4 Como se discutió en la Sección , para los casos que implican ejecutar código generado (parcialmente) a partir de un modelo de lenguaje grande, recomendamos utilizar las capacidades de sandboxing de PDL.

busque 4La traza del intérprete en (b) muestra que esta ejecución tiene dos iteraciones del bucle del agente. Aunque se trata de un ejemplo sencillo, también hemos implementado un agente de edición de código utilizando la PDL, que se utilizó en la tabla de clasificación de SWE-bench Lite como parte del commit4 (Jiménez et al. 2024). La presentación resuelve la primera instancia de 23,7% utilizando sólo el modelo de código abierto, que es superior a cualquier resultado anterior utilizando el modelo de código abierto y comparable a los resultados del modelo de frontera.

5.3.Generación de PDL a partir de PDL utilizando el modelo Big Language

En las secciones anteriores se ha mostrado cómo los desarrolladores humanos pueden utilizar PDL para codificar distintos patrones de cueing. Esta generación de meta-PDL es útil cuando los grandes modelos lingüísticos necesitan crear planes de resolución de problemas, por ejemplo, como parte del flujo de trabajo de un agente. Tradicionalmente, estos planes han sido sólo texto, JSON o código Python. Con PDL, estos planes pueden ser una combinación de modelos totalmente ejecutables y llamadas a código. Esta sección explora el uso de la meta-generación PDL en el conjunto de datos GSMHard5 .

GSMHard es una versión más difícil de GSM8k que contiene problemas matemáticos escolares que requieren aritmética simple o razonamiento simbólico. GSMHard contiene una entrada, el enunciado del problema matemático, y una salida, el código Python que resuelve el problema. Hemos implementado PAL (Gao et al. 2023), pero en lugar de generar código Python, se pide al gran modelo de lenguaje que genere PDL. El pensamiento textual en cadena se representa como bloques de texto PDL, mientras que la aritmética se realiza utilizando bloques de código PDL.

busque 6 Se muestra un programa PDL que genera código PDL y lo ejecuta en el mismo programa. La variable demos contiene un pequeño número de ejemplos diseñados para enseñar al modelo a generar código PDL. En la línea 32, un bloque de llamada al modelo utiliza estos ejemplos y un problema con variables libres como entrada. El resultado es un programa PDL para resolver el problema. La línea 38 extrae el programa PDL y lo ejecuta en Python. El programa se aplica al conjunto de datos GSMHard, donde el problema rellena el problema de entrada.

Este experimento descubrió que 10% para el conjunto de datos GSMHard era en realidad erróneo porque la verdad sobre el terreno era incoherente con las preguntas formuladas. Fig. 6 Se muestran ejemplos de estas incoherencias. El uso de PDL ayudó a descubrirlo, ya que el código PDL generado era legible para los humanos, por lo que pudimos comprobar fácilmente los puntos de datos que no coincidían con la verdad básica y descubrimos que ésta era errónea en algunos casos. Utilizamos un gran modelo lingüístico para cubrir todo el conjunto de datos y seleccionamos sistemáticamente los ejemplos que parecían incoherentes. A continuación, examinamos manualmente los resultados para eliminar los falsos positivos e identificamos puntos de datos de 10% con este problema.

PDL:声明式提示词编程语言

 

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

Figura 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

Figura 6. Puntos de datos del problema de muestra GSMHard

 

6. Trabajos relacionados

Un estudio reciente sitúa Marcos de referencia Definida como una capa que gestiona, simplifica y facilita la interacción entre el LLM y los usuarios, herramientas u otros modelos (Liu et al.2023). El estudio pone de relieve que una de las principales deficiencias del marco de la señalización es la pronunciada curva de aprendizaje.

Probablemente, el marco de prompting más popular en la actualidad es LangChain (Chase et al.2022), cuya rica funcionalidad lo hace a la vez potente y complejo.La principal motivación de MiniChain es precisamente evitar esta complejidad (Rush.2023), que ofrece menos funciones y más sencillas que pueden combinarse para crear aplicaciones avanzadas. Sin embargo, tanto LangChain como MiniChain son frameworks basados en Python, lo que los hace más no declarativos porque los desarrolladores necesitan escribir código imperativo.PDL tiene una motivación similar a MiniChain, pero da un paso más allá al utilizar YAML en lugar de Python como base.

Al igual que con otros marcos de incitación, el objetivo del PDL es hacer que el LLM sea más robusto.Guidance (Microsoft.2023) es un marco basado en Python que proporciona un diseño más estructurado, pero es de más bajo nivel que LangChain. Del mismo modo, LMQL (Beurer-Kellner et al.2023) es un lenguaje específico del dominio incrustado en Python que hace uso de tipos y decodificación restringida.PDL se inspira en cierta medida en LMQL en su entrelazamiento de pistas y programación, pero a diferencia de LMQL, depende menos del código imperativo de Python.Crouse et al. utilizan máquinas de estados finitos para especificar formalmente el flujo interno de varios bucles inteligentes (Crouse et al.2024); aunque se trata de un esfuerzo fascinante, no introduce un lenguaje de señales sofisticado.

Una de las ventajas de los lenguajes de dominio específico es que pueden aplicar transformaciones de programas, por ejemplo para su optimización (Mernik et al.2005El marco DSPy cueing (Khattab et al.2023) tiene el lema "Programación, no pistas": genera pistas automáticamente, de modo que los desarrolladores no necesitan escribirlas manualmente. Del mismo modo, Vieira (Li et al.2024) extiende Prolog para utilizar el LLM como una relación probabilística y genera automáticamente prompts.DSPy y Vieira son marcos muy avanzados, pero a diferencia de PDL, ambos debilitan el control del desarrollador sobre prompts específicos.Lale (Baudart et al.2021) es un lenguaje que permite a los usuarios ajustar progresivamente el equilibrio entre automatización y control en el proceso de IA, pero no se centra en las pistas LLM.DSPy, Vieira y Lale optimizan el rendimiento predictivo, mientras que la otra optimización se centra en el rendimiento computacional.SGLang (Zheng et al.2023) lo consigue haciendo un mejor uso de la caché de prefijos, lo que se traduce en más aciertos en la caché KV (Kwon et al.2023). El trabajo futuro explorará si la naturaleza declarativa de PDL puede permitir optimizaciones de rendimiento computacional similares.

Recientemente, han surgido varios marcos de prompting basados en Large Language Modelling (LLM), centrados en agentes LLM.AutoGen (Wu et al.2023) es un marco multiagente en el que todo el contenido consiste en agentes y diálogos. Otros marcos multiagente son CrewAI (Moura.2023) y GPTSwarm (Zhuge et al.2024). PDL, aunque también admite proxies, adopta una postura más equilibrada, tratando los proxies como una de las diversas técnicas de cueing.

 

7. Conclusión

PDL es un lenguaje declarativo orientado a datos: los programas constan de bloques YAML, cada uno de los cuales es un dato literal o generativo. El modelo de pensamiento consiste en ejecutar un bloque añadiendo sus datos a un contexto de fondo, que es utilizado como pista por las llamadas posteriores al modelo de lenguaje más amplio. Esta tesis presenta el lenguaje con programas de ejemplo y una visita guiada por la sintaxis y las herramientas. La naturaleza declarativa del lenguaje también facilita la implementación de optimizaciones automáticas de velocidad, precisión y seguridad, que se implementarán de forma incremental en futuros trabajos.PDL ya está listo para su uso y es de código abierto en la siguiente URL:https://github.com/IBM/prompt-declaration-language.

© declaración de copyright

Artículos relacionados

Sin comentarios

Debe iniciar sesión para participar en los comentarios.
Acceder ahora
ninguno
Sin comentarios...