Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expression Language for LLM driven template replacements #298

Merged
merged 5 commits into from Aug 7, 2023

Conversation

raulraja
Copy link
Contributor

@raulraja raulraja commented Aug 4, 2023

The Expression class is a utility class designed to simplify the process of creating and running expressions for a chatbot or conversational AI system. It provides a fluent API for building expressions with different roles (SYSTEM, USER, and ASSISTANT) and generating dynamic prompts with variable keys.

Here's a breakdown of the key components and functionalities of the Expression class:

  1. Constructor:

    • The Expression class takes three parameters: scope, model, and a block function. scope is an instance of CoreAIScope, model is an instance of ChatWithFunctions, and block is a lambda function that takes a suspend Expression.() -> Unit as its parameter. The block function is used to define the sequence of messages and prompts within the expression.
  2. Logging:

    • The class uses the KotlinLogging library to create an instance of KLogger for logging purposes.
  3. Messages and Generation Keys:

    • The class has two mutable lists: messages and generationKeys. The messages list stores messages with different roles (SYSTEM, USER, ASSISTANT) that are added using the system, user, and assistant functions. The generationKeys list keeps track of variable keys used in the prompts, which are added using the prompt function.
  4. Message Builders:

    • The system, user, and assistant functions are used to add messages to the messages list with the corresponding roles.
  5. Prompt Generation:

    • The prompt function is used to generate prompts with variable keys (e.g., {{$key}}) and store the variable keys in the generationKeys list.
  6. run Function:

    • The run function executes the expression defined in the block function. It builds an instruction message using an ExpertSystem, adds it to the messages list, and then runs the prompt messages using the model.prompt function. The function also replaces the variable keys in the prompts with actual values obtained from the chatbot response.
  7. companion object run Function:

    • The run function within the companion object allows for a concise way of creating and executing an Expression instance. It is a convenience function that takes a scope, model, and a block lambda as arguments, and it automatically calls the run function on the Expression instance.
  8. New methods for the Chat interface that can consume a list of Message instead of a String or a Prompt

The code example below demonstrates how to use the Expression class to create a workout plan based on user input and the responses generated by the assistant.

package com.xebia.functional.xef.auto.expressions

import com.xebia.functional.xef.auto.CoreAIScope
import com.xebia.functional.xef.auto.ai
import com.xebia.functional.xef.auto.llm.openai.OpenAI
import com.xebia.functional.xef.auto.llm.openai.getOrThrow
import com.xebia.functional.xef.llm.ChatWithFunctions
import com.xebia.functional.xef.prompt.expressions.Expression
import com.xebia.functional.xef.prompt.expressions.ExpressionResult

suspend fun workoutPlan(
  scope: CoreAIScope,
  model: ChatWithFunctions,
  goal: String,
  experienceLevel: String,
  equipment: String,
  timeAvailable: Int
): ExpressionResult = Expression.run(scope = scope, model = model, block = {
  system { "You are a personal fitness trainer" }
  user {
    """
     |I want to achieve $goal.
     |My experience level is $experienceLevel, and I have access to the following equipment: $equipment.
     |I can dedicate $timeAvailable minutes per day.
     |Can you create a workout plan for me?
  """.trimMargin()
  }
  assistant {
    """
     |Sure! Based on your goal, experience level, equipment available, and time commitment, here's a customized workout plan:
     |${prompt("workout_plan")}
  """.trimMargin()
  }
})

suspend fun main() {
  val model = OpenAI.DEFAULT_SERIALIZATION
  ai {
    val plan = workoutPlan(
      scope = this,
      model = model,
      goal = "building muscle",
      experienceLevel = "intermediate",
      equipment = "dumbbells, bench, resistance bands",
      timeAvailable = 45
    )
    println("--------------------")
    println("Workout Plan")
    println("--------------------")
    println("🤖 replaced: ${plan.values.replacements.joinToString { it.key }}")
    println("--------------------")
    println(plan.result)
    println("--------------------")
  }.getOrThrow()
}
--------------------
Workout Plan
--------------------
🤖 replaced: workout_plan
--------------------
You are a personal fitness trainer
I want to achieve building muscle.
My experience level is intermediate, and I have access to the following equipment: dumbbells, bench, resistance bands.
I can dedicate 45 minutes per day.
Can you create a workout plan for me?
Sure! Based on your goal, experience level, equipment available, and time commitment, here's a customized workout plan:
Here's a sample workout plan for you:

Day 1: Upper Body
- Dumbbell bench press: 3 sets of 10 reps
- Bent-over rows with dumbbells: 3 sets of 10 reps
- Dumbbell shoulder press: 3 sets of 10 reps
- Bicep curls with resistance bands: 3 sets of 10 reps

Day 2: Lower Body
- Dumbbell squats: 3 sets of 10 reps
- Dumbbell lunges: 3 sets of 10 reps
- Romanian deadlifts with dumbbells: 3 sets of 10 reps
- Calf raises with resistance bands: 3 sets of 10 reps

Day 3: Full Body
- Dumbbell chest press: 3 sets of 10 reps
- Dumbbell rows: 3 sets of 10 reps
- Dumbbell shoulder press: 3 sets of 10 reps
- Dumbbell squats: 3 sets of 10 reps

Remember to warm up before each workout and cool down/stretch afterwards. Increase the weight/resistance as you get stronger and aim to progressively overload your muscles. Rest for 1-2 minutes between sets. Stay consistent and have fun with your workouts!

--------------------

Copy link
Contributor

@javipacheco javipacheco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to know your opinion about my message

Copy link
Contributor

@javipacheco javipacheco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was worried about creating different Expressions with the same conversationId, because we create several SYSTEM messages in the middle of the conversation

After my local tests, this is not really a problem. I have created 2 different plans with the same conversationId and got the expected results and did not mix the messages.

Anyway, Expression now has no control over the conversations, we are always using the conversation in the Scope. Maybe we should provide a way for doing that by passing a conversationId as a parameter in Expression.run. It's just a suggestion

🚀 LGTM!

@raulraja raulraja merged commit dbf500b into main Aug 7, 2023
5 checks passed
@raulraja raulraja deleted the expression-language branch August 7, 2023 14:17
raulraja added a commit that referenced this pull request Aug 9, 2023
* Add wikipedia search

* Add encodeURLQueryComponent()

* Format

* Expression Language for LLM driven template replacements (#298)

* Expression Language for LLM driven template replacements

* Prompt adjustments

* Prompt adjustments and better structure for building final prompt based on messages

* Spotless Apply (#304)

* add encodeURLQueryComponent()

* Add wikipedia search

* Add encodeURLQueryComponent()

* Format

* add encodeURLQueryComponent()

---------

Co-authored-by: Raúl Raja Martínez <raulraja@gmail.com>
Co-authored-by: Javi Pacheco <javi.pacheco@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants