“Say Goodbye to Manual Prompting”

Getting Started: Install AdalFlow and Run Your First Query

pip install -U adalflow

LM apps often relys on other cloud or local model services and each of them often has their own Python SDKs. AdalFlow handles all of them as optional packages, so that developers only need to install the ones they need.

Set up OPENAI_API_KEY in your .env file or pass the api_key directly to the client.

import adalflow as adal
from adalflow.utils import setup_env

setup_env()

openai_llm = adal.Generator(
   model_client=adal.OpenAIClient(), model_kwargs={"model": "gpt-3.5-turbo"}
)
resopnse = openai_llm(prompt_kwargs={"input_str": "What is LLM?"})

Try AdalFlow experience end to end in 15 minutes with the Colab Notebook.

Build your LLM workflow with full control over Prompt, Model, and Output Data Parsing

  • Prompt is the new programming language. All LLM app patterns, from RAG to agents, are implemented via subprompts. AdalFlow leverages jinja2 template engine to help developers define the overall prompt structure for various applications.

  • DataClass helps developers define the input and output data structure for the LLM pipeline.

  • Component is where we define the LLM workflow, which supports both train and eval modes via forward and call methods.

With template, you know exactly what is passed to the LLM. You also have full control over the output parser by defining it yourself. system_prompt is claimed as a adal.Parameter to help training. You can also directly pass string.

template = r"""<START_OF_SYSTEM_PROMPT>
{{system_prompt}}
<END_OF_SYSTEM_PROMPT>
<START_OF_USER>
{{input_str}}
<END_OF_USER>"""

@adal.func_to_data_component
def parse_integer_answer(answer: str):
      numbers = re.findall(r"\d+", answer)
      return int(numbers[-1])

class ObjectCountTaskPipeline(adal.Component):
   def __init__(self, model_client: adal.ModelClient, model_kwargs: Dict):
      super().__init__()
      system_prompt = adal.Parameter(
            data="You will answer a reasoning question. Think step by step. The last line of your response should be of the following format: 'Answer: $VALUE' where VALUE is a numerical value.",
            role_desc="To give task instruction to the language model in the system prompt",
            requires_opt=True,
            param_type=ParameterType.PROMPT,
      )
      self.llm_counter = adal.Generator(
            model_client=model_client,
            model_kwargs=model_kwargs,
            template=template,
            prompt_kwargs={
               "system_prompt": system_prompt,
            },
            output_processors=parse_integer_answer,
      )

   def bicall(
      self, question: str, id: str = None
   ) -> Union[adal.GeneratorOutput, adal.Parameter]:
      output = self.llm_counter(prompt_kwargs={"input_str": question}, id=id)
      return output

Running the workflow:

object_count_pipeline = ObjectCountTaskPipeline(**model_config)

question = "I have a flute, a piano, a trombone, four stoves, a violin, an accordion, a clarinet, a drum, two lamps, and a trumpet. How many musical instruments do I have?"
response = object_count_pipeline(question)

Check out the Full Tutorial for more details.

Auto-optimize your LLM workflow with both Prompt Tuning and Few-shot Learning

  • When a Parameter is defined as PROMPT, AdalFlow will automatically optimize the prompt on your training dataset via LLM-AutoDiff.

  • When a Parameter is defined as DEMOS, Few-Shot Bootstrap Learning is applied.

  • When both are defined, AdalFlow will use both to find the best performing prompts.

The Trainer requires the following key elements from your workflow:

  1. Task Workflow – Defined as a Component from the previous step.

  2. Model Configuration – Includes settings for the optimizer and bootstrap teacher. It is recommended to use high-performing LLM models such as 4o, o1, o3-mini, or r1.

  3. Evaluation Metrics – The criteria used to assess model performance.

  4. LLM Workflow Execution – Instructions on how to invoke your LLM workflow in both evaluation and training modes.

Developers can organize the above information in AdalComponent, similar to how PyTorch LightningModule is structured.

class TrecClassifierAdal(adal.AdalComponent):
   def __init__(
      self,
      model_client: adal.ModelClient,
      model_kwargs: Dict,
      teacher_model_config: Dict,
      backward_engine_model_config: Dict,
      text_optimizer_model_config: Dict,
   ):
      task = TRECClassifierStructuredOutput(model_client, model_kwargs)
      eval_fn = AnswerMatchAcc(type="exact_match").compute_single_item
      loss_fn = adal.EvalFnToTextLoss(
            eval_fn=eval_fn,
            eval_fn_desc="exact_match: 1 if str(y) == str(y_gt) else 0. When the LLM prediction failed with format parsing which results with errors, we set y_pred = -1",
      )
      super().__init__(
            task=task,
            eval_fn=eval_fn,
            loss_fn=loss_fn,
            backward_engine_model_config=backward_engine_model_config,
            text_optimizer_model_config=text_optimizer_model_config,
            teacher_model_config=teacher_model_config,
      )

   def prepare_task(self, sample: TRECExtendedData):
      return self.task.bicall, {"question": sample.question, "id": sample.id}

   def prepare_eval(
      self, sample: TRECExtendedData, y_pred: adal.GeneratorOutput
   ) -> float:
      y_label = -1
      if y_pred and y_pred.data is not None and y_pred.data.class_name is not None:
            y_label = y_pred.data.class_name
      return self.eval_fn, {"y": y_label, "y_gt": sample.class_name}

   def prepare_loss(
      self, sample: TRECExtendedData, y_pred: adal.Parameter, *args, **kwargs
   ) -> Tuple[Callable[..., Any], Dict]:
      full_response = y_pred.data
      y_label = -1
      if (full_response and full_response.data is not None
            and full_response.data.class_name is not None):
            y_label = full_response.data.class_name

      y_pred.eval_input = y_label
      y_gt = adal.Parameter(
            name="y_gt",
            data=sample.class_name,
            eval_input=sample.class_name,
            requires_opt=False,
      )
      return self.loss_fn, {
            "kwargs": {"y": y_pred, "y_gt": y_gt},
            "id": sample.id,
      }

Check out the Full Tutorial for more details.

Unites Research and Production

Our team has experience in both AI research and production. We are building a library that unites the two worlds, forming a healthy LLM application ecosystem.

  • To resemble the PyTorch library makes it easier for LLM researchers to use the library.

  • Researchers building on AdalFlow enable production engineers to easily adopt, test, and iterate on their production data.

  • Our 100% control and clarity of the source code further make it easy for product teams to build on and for researchers to extend their new methods.

Community

Learn, share and collaborate with the AdalFlow AI community