Overview

Chains in PromptL allow you to break complex workflows into smaller, manageable steps. Each step generates a response, which can then be used in subsequent steps. This approach improves the model’s ability to perform complex tasks and provides greater control over dynamic conversations.

With Chains, you can:

  • Process tasks incrementally to guide the model step-by-step.
  • Store and reuse intermediate results dynamically.
  • Customize step-specific configurations for more efficient execution.
  • Isolate steps to minimize context overhead or confusion.

Syntax

Define steps in your prompt using the <step> tag. The engine pauses after each step, waits for the model’s response, and adds it as an assistant message before continuing.

Basic Syntax

<step>
  Analyze the following text and identify the main idea:

  <user>
    "The quick brown fox jumps over the lazy dog."
  </user>
</step>

<step>
  Now, summarize the identified main idea in one sentence.
</step>

Step with Custom Configuration

Override the default configuration by adding attributes to the <step> tag:

<step model="gpt-3.5-turbo" temperature={{0.5}}>
  Rewrite the following paragraph in simpler language:

  <user>
    "Quantum mechanics explains the behavior of particles on a microscopic scale."
  </user>
</step>

Advanced Features

Storing Step Responses

You can store the text response of a step in a variable using the as attribute. This allows you to reuse the response in later steps or logic blocks.

<step as="analysis">
  Is this statement correct? {{ statement }}
  Respond only with "correct" or "incorrect".
</step>

{{ if analysis == "correct" }}
  Provide additional details about why the statement is correct.
{{ else }}
  Explain why the statement is incorrect and provide the correct answer.
{{ endif }}

Storing Raw Messages

Some providers will return additional metadata along with the response. To store the entire message object, instead of just the text response (e.g., role, content, and additional metadata), use the raw attribute:

<step raw="detailedMessage">
  Summarize the following text:
  {{ text }}
</step>

The raw response will return an object with the full message details, which contains the role, content, and other metadata provided by the model. The content attribute will always be defined as an array of content objects, which can include text, images, or other types of content.


Isolating Steps

Use the isolated attribute to prevent a step from inheriting context from previous steps. This can reduce unnecessary costs or confusion for the model.

<step isolated as="summary1">
  Summarize the following text:
  {{ text1 }}
</step>

<step isolated as="summary2">
  Summarize the following text:
  {{ text2 }}
</step>

<step>
  Compare these summaries and provide a conclusion:
  {{ summary1 }}
  {{ summary2 }}
</step>

In this example, the final step does not need to conside the full texts used in previous steps, so isolating the first two steps can help reduce context overhead, resulting in cheaper and more efficient processing.


Real-World Use Cases

Multi-Step Workflow

Chains are ideal for breaking down tasks like:

  1. Analyzing data.
  2. Generating intermediate results.
  3. Combining results for a final output.
<step as="analysis">
  Analyze the following data and provide key insights:
  {{ data }}
</step>

<step>
  Based on the insights:
  {{ analysis }}
  Provide recommendations for improvement.
</step>

Decision Trees

Use logic to adapt workflows based on intermediate results:

<step as="classification">
  Classify this document into one of the following categories: A, B, or C.
</step>

{{ if classification == "A" }}
<step>
  Generate detailed content for Category A.
</step>
{{ else if classification == "B" }}
<step>
  Generate detailed content for Category B.
</step>
{{ else }}
<step>
  Generate detailed content for Category C.
</step>
{{ endif }}

Implementation

To execute chains, use the Chain class. The chain evaluates the prompt step-by-step, waiting for the model’s response at each step.

To run a step, execute the step method of the chain instance. The first time step is called, it should not include any arguments. Subsequent calls must always pass the model response message from the previous step.

Example: Using the Chain Class

import { Chain } from 'promptl-ai';
import OpenAI from 'openai';

// Initialize the OpenAI client
const client = new OpenAI();

// Generate a response based on step messages and configuration
async function generateResponse({ config, messages }) {
  const response = await client.chat.completions.create({
    ...config,
    messages,
  });
  return response.choices[0].message;
}

// Create a new chain
const chain = new Chain({
  prompt: '...', // Your PromptL prompt as a string
  parameters: {...} // Your prompt parameters
});

// Process the chain step-by-step
let result = chain.step(); // The first step does not require any arguments
let lastResponse;

while (!result.completed) {
  lastResponse = await generateResponse(result);
  result = chain.step(lastResponse); // Pass the model response to the next step
}

console.log('Final Output:', lastResponse.content);

Debugging Chains

  1. Log Intermediate Steps:
    • Use the raw attribute to inspect full responses for debugging.
  2. Handle Errors Gracefully:
    • Implement fallback logic for unexpected responses or failures.
  3. Test Edge Cases:
    • Ensure your chains handle empty inputs, invalid configurations, or incomplete data.

Summary

Chains and Steps provide powerful tools for breaking complex tasks into manageable parts. With features like custom configurations, variable storage, and step isolation, you can design robust, dynamic workflows tailored to any use case.