Skip to content

Chat Prompting

@chatprompt

The @chatprompt decorator works just like @prompt but allows you to pass chat messages as a template rather than a single text prompt. This can be used to provide a system message or for few-shot prompting where you provide example responses to guide the model's output. Format fields denoted by curly braces {example} will be filled in all messages (except FunctionResultMessage).

from magentic import chatprompt, AssistantMessage, SystemMessage, UserMessage
from pydantic import BaseModel


class Quote(BaseModel):
    quote: str
    character: str


@chatprompt(
    SystemMessage("You are a movie buff."),
    UserMessage("What is your favorite quote from Harry Potter?"),
    AssistantMessage(
        Quote(
            quote="It does not do to dwell on dreams and forget to live.",
            character="Albus Dumbledore",
        )
    ),
    UserMessage("What is your favorite quote from {movie}?"),
)
def get_movie_quote(movie: str) -> Quote: ...


get_movie_quote("Iron Man")
# Quote(quote='I am Iron Man.', character='Tony Stark')

escape_braces

To prevent curly braces from being interpreted as format fields, use the escape_braces function to escape them in strings.

from magentic.chatprompt import escape_braces

string_with_braces = "Curly braces like {example} will be filled in!"
escaped_string = escape_braces(string_with_braces)
# 'Curly braces {{example}} will be filled in!'
escaped_string.format(example="test")
# 'Curly braces {example} will be filled in!'

Placeholder

The Placeholder class enables templating of AssistantMessage content within the @chatprompt decorator. This allows dynamic changing of the messages used to prompt the model based on the arguments provided when the function is called.

from magentic import chatprompt, AssistantMessage, Placeholder, UserMessage
from pydantic import BaseModel


class Quote(BaseModel):
    quote: str
    character: str


@chatprompt(
    UserMessage("Tell me a quote from {movie}"),
    AssistantMessage(Placeholder(Quote, "quote")),
    UserMessage("What is a similar quote from the same movie?"),
)
def get_similar_quote(movie: str, quote: Quote) -> Quote: ...


get_similar_quote(
    movie="Star Wars",
    quote=Quote(quote="I am your father", character="Darth Vader"),
)
# Quote(quote='The Force will be with you, always.', character='Obi-Wan Kenobi')

Placeholder can also be utilized in the format method of custom Message subclasses to provide an explicit way of inserting values from the function arguments. For example, see UserImageMessage in (TODO: link to GPT-vision page).

FunctionCall

The content of an AssistantMessage can be a FunctionCall. This can be used to demonstrate to the LLM when/how it should call a function.

from magentic import (
    chatprompt,
    AssistantMessage,
    FunctionCall,
    UserMessage,
    SystemMessage,
)


def change_music_volume(increment: int) -> int:
    """Change music volume level. Min 1, max 10."""
    print(f"Music volume change: {increment}")


def order_food(food: str, amount: int):
    """Order food."""
    print(f"Ordered {amount} {food}")


@chatprompt(
    SystemMessage(
        "You are hosting a party and must keep the guests happy."
        "Call functions as needed. Do not respond directly."
    ),
    UserMessage("It's pretty loud in here!"),
    AssistantMessage(FunctionCall(change_music_volume, -2)),
    UserMessage("{request}"),
    functions=[change_music_volume, order_food],
)
def adjust_for_guest(request: str) -> FunctionCall[None]: ...


func = adjust_for_guest("Do you have any more food?")
func()
# Ordered 3 pizza

FunctionResultMessage

To include the result of calling the function in the messages use a FunctionResultMessage. This takes a FunctionCall instance as its second argument. The same FunctionCall instance must be passed to an AssistantMessage and the corresponding FunctionResultMessage so that the result can be correctly linked back to the function call that created it.

from magentic import (
    chatprompt,
    AssistantMessage,
    FunctionCall,
    FunctionResultMessage,
    UserMessage,
)


def plus(a: int, b: int) -> int:
    return a + b


plus_1_2 = FunctionCall(plus, 1, 2)


@chatprompt(
    UserMessage("Use the plus function to add 1 and 2."),
    AssistantMessage(plus_1_2),
    FunctionResultMessage(3, plus_1_2),
    UserMessage("Now add 4 to the result."),
    functions=[plus],
)
def do_math() -> FunctionCall[int]: ...


do_math()
# FunctionCall(<function plus at 0x10a0829e0>, 3, 4)

AnyMessage

The AnyMessage type can be used for (de)serialization of Message objects, or as a return type in prompt-functions. This allows you to create prompt-functions to do things like summarize a chat history into fewer messages, or even to create a set of messages that you can use in a chatprompt-function.

from magentic import AnyMessage, prompt


@prompt("Create an example of few-shot prompting for a chatbot")
def make_few_shot_prompt() -> list[AnyMessage]: ...


make_few_shot_prompt()
# [SystemMessage('You are a helpful and knowledgeable assistant. You answer questions promptly and accurately. Always be polite and concise.'),
#  UserMessage('What’s the weather like today?'),
#  AssistantMessage[Any]('The weather today is sunny with a high of 75°F (24°C) and a low of 55°F (13°C). No chance of rain.'),
#  UserMessage('Can you explain the theory of relativity in simple terms?'),
#  AssistantMessage[Any]('Sure! The theory of relativity, developed by Albert Einstein, has two main parts: Special Relativity and General Relativity. Special Relativity is about how time and space are linked for objects moving at a consistent speed in a straight line. It shows that time can slow down or speed up depending on how fast you are moving compared to something else. General Relativity adds gravity into the mix and shows that massive objects cause space to bend and warp, which we feel as gravity.'),
#  UserMessage('How do I bake a chocolate cake?'),
#  AssistantMessage[Any]("Here's a simple recipe for a chocolate cake:\n\nIngredients:\n- 1 and 3/4 cups all-purpose flour\n- 1 and 1/2 cups granulated sugar\n- 3/4 cup cocoa powder\n- 1 and 1/2 teaspoons baking powder\n- 1 and 1/2 teaspoons baking soda\n- 1 teaspoon salt\n- 2 large eggs\n- 1 cup whole milk\n- 1/2 cup vegetable oil\n- 2 teaspoons vanilla extract\n- 1 cup boiling water\n\nInstructions:\n1. Preheat your oven to 350°F (175°C). Grease and flour two 9-inch round baking pans.\n2. In a large bowl, whisk together the flour, sugar, cocoa powder, baking powder, baking soda, and salt.\n3. Add the eggs, milk, oil, and vanilla. Beat on medium speed for 2 minutes.\n4. Stir in the boiling water (batter will be thin).\n5. Pour the batter evenly into the prepared pans.\n6. Bake for 30 to 35 minutes or until a toothpick inserted into the center comes out clean.\n7. Cool the cakes in the pans for 10 minutes, then remove them from the pans and cool completely on a wire rack.\n8. Frost with your favorite chocolate frosting and enjoy!")]

For (de)serialization, check out TypeAdapter from pydantic. See more on the pydantic Type Adapter docs page.

from magentic import AnyMessage
from pydantic import TypeAdapter


messages = [
    {"role": "system", "content": "Hello"},
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hello"},
    {"role": "tool", "content": 3, "tool_call_id": "unique_id"},
]
TypeAdapter(list[AnyMessage]).validate_python(messages)
# [SystemMessage('Hello'),
#  UserMessage('Hello'),
#  AssistantMessage[Any]('Hello'),
#  ToolResultMessage[Any](3, self.tool_call_id='unique_id')]