Integrar facilmente modelos de linguagem grandes ao seu código Python. Basta usar os decoradores @prompt e @chatprompt para criar funções que retornam a saída estruturada do LLM. Misture consultas LLM e chamadas de função com código Python regular para criar lógica complexa.
FunctionCall e ParallelFunctionCall .async def ao definir uma função magentic.pip install magenticou usando UV
uv add magentic Configure sua chave de API do OpenAI, configurando a variável de ambiente OPENAI_API_KEY . Para configurar um provedor LLM diferente, consulte a configuração para mais.
O Decorador @prompt permite definir um modelo para um prompt de grande modelo de idioma (LLM) como uma função Python. Quando essa função é chamada, os argumentos são inseridos no modelo, esse prompt é enviado para um LLM que gera a saída da função.
from magentic import prompt
@ prompt ( 'Add more "dude"ness to: {phrase}' )
def dudeify ( phrase : str ) -> str : ... # No function body as this is never executed
dudeify ( "Hello, how are you?" )
# "Hey, dude! What's up? How's it going, my man?" O decorador @prompt respeitará a anotação do tipo de retorno da função decorada. Isso pode ser qualquer tipo suportado pela Pydantic, incluindo um modelo pydantic .
from magentic import prompt
from pydantic import BaseModel
class Superhero ( BaseModel ):
name : str
age : int
power : str
enemies : list [ str ]
@ prompt ( "Create a Superhero named {name}." )
def create_superhero ( name : str ) -> Superhero : ...
create_superhero ( "Garden Man" )
# Superhero(name='Garden Man', age=30, power='Control over plants', enemies=['Pollution Man', 'Concrete Woman'])Veja saídas estruturadas para mais.
O decorador @chatprompt funciona como @prompt mas permite que você passe as mensagens de bate -papo como um modelo em vez de um único prompt de texto. Isso pode ser usado para fornecer uma mensagem do sistema ou para solicitar poucos anos, onde você fornece respostas de exemplo para orientar a saída do modelo. Os campos de formato denotados pelos aparelhos encaracolados {example} serão preenchidos em todas as mensagens (exceto 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')Veja o bate -papo solicitando mais.
Um LLM também pode decidir chamar funções. Nesse caso, a função decorada @prompt retorna um objeto FunctionCall que pode ser chamado para executar a função usando os argumentos fornecidos pelo LLM.
from typing import Literal
from magentic import prompt , FunctionCall
def search_twitter ( query : str , category : Literal [ "latest" , "people" ]) -> str :
"""Searches Twitter for a query."""
print ( f"Searching Twitter for { query !r } in category { category !r } " )
return "<twitter results>"
def search_youtube ( query : str , channel : str = "all" ) -> str :
"""Searches YouTube for a query."""
print ( f"Searching YouTube for { query !r } in channel { channel !r } " )
return "<youtube results>"
@ prompt (
"Use the appropriate search function to answer: {question}" ,
functions = [ search_twitter , search_youtube ],
)
def perform_search ( question : str ) -> FunctionCall [ str ]: ...
output = perform_search ( "What is the latest news on LLMs?" )
print ( output )
# > FunctionCall(<function search_twitter at 0x10c367d00>, 'LLMs', 'latest')
output ()
# > Searching Twitter for 'Large Language Models news' in category 'latest'
# '<twitter results>'Veja a função chamando para mais.
Às vezes, o LLM exige fazer uma ou mais chamadas de função para gerar uma resposta final. O decorador @prompt_chain resolverá objetos FunctionCall automaticamente e passará a saída de volta ao LLM para continuar até que a resposta final seja atingida.
No exemplo a seguir, quando o describe_weather é chamado de LLM chama primeiro a função get_current_weather , usa o resultado disso para formular sua resposta final que é retornada.
from magentic import prompt_chain
def get_current_weather ( location , unit = "fahrenheit" ):
"""Get the current weather in a given location"""
# Pretend to query an API
return {
"location" : location ,
"temperature" : "72" ,
"unit" : unit ,
"forecast" : [ "sunny" , "windy" ],
}
@ prompt_chain (
"What's the weather like in {city}?" ,
functions = [ get_current_weather ],
)
def describe_weather ( city : str ) -> str : ...
describe_weather ( "Boston" )
# 'The current weather in Boston is 72°F and it is sunny and windy.' As funções movidas a LLM criadas usando @prompt , @chatprompt e @prompt_chain podem ser fornecidas como functions a outros decoradores @prompt / @prompt_chain , assim como as funções regulares do Python. Isso permite a funcionalidade cada vez mais complexa de LLM, permitindo que os componentes individuais sejam testados e melhorados isoladamente.
A classe StreamedStr (e AsyncStreamedStr ) pode ser usada para transmitir a saída do LLM. Isso permite que você processe o texto enquanto ele está sendo gerado, em vez de receber toda a saída de uma só vez.
from magentic import prompt , StreamedStr
@ prompt ( "Tell me about {country}" )
def describe_country ( country : str ) -> StreamedStr : ...
# Print the chunks while they are being received
for chunk in describe_country ( "Brazil" ):
print ( chunk , end = "" )
# 'Brazil, officially known as the Federative Republic of Brazil, is ...' Vários StreamedStr podem ser criados ao mesmo tempo para transmitir saídas LLM simultaneamente. No exemplo abaixo, a geração da descrição para vários países leva aproximadamente a mesma quantidade de tempo que para um único país.
from time import time
countries = [ "Australia" , "Brazil" , "Chile" ]
# Generate the descriptions one at a time
start_time = time ()
for country in countries :
# Converting `StreamedStr` to `str` blocks until the LLM output is fully generated
description = str ( describe_country ( country ))
print ( f" { time () - start_time :.2f } s : { country } - { len ( description ) } chars" )
# 22.72s : Australia - 2130 chars
# 41.63s : Brazil - 1884 chars
# 74.31s : Chile - 2968 chars
# Generate the descriptions concurrently by creating the StreamedStrs at the same time
start_time = time ()
streamed_strs = [ describe_country ( country ) for country in countries ]
for country , streamed_str in zip ( countries , streamed_strs ):
description = str ( streamed_str )
print ( f" { time () - start_time :.2f } s : { country } - { len ( description ) } chars" )
# 22.79s : Australia - 2147 chars
# 23.64s : Brazil - 2202 chars
# 24.67s : Chile - 2186 chars As saídas estruturadas também podem ser transmitidas pelo LLM usando o tipo de anotação de retorno Iterable (ou AsyncIterable ). Isso permite que cada item seja processado enquanto o próximo está sendo gerado.
from collections . abc import Iterable
from time import time
from magentic import prompt
from pydantic import BaseModel
class Superhero ( BaseModel ):
name : str
age : int
power : str
enemies : list [ str ]
@ prompt ( "Create a Superhero team named {name}." )
def create_superhero_team ( name : str ) -> Iterable [ Superhero ]: ...
start_time = time ()
for hero in create_superhero_team ( "The Food Dudes" ):
print ( f" { time () - start_time :.2f } s : { hero } " )
# 2.23s : name='Pizza Man' age=30 power='Can shoot pizza slices from his hands' enemies=['The Hungry Horde', 'The Junk Food Gang']
# 4.03s : name='Captain Carrot' age=35 power='Super strength and agility from eating carrots' enemies=['The Sugar Squad', 'The Greasy Gang']
# 6.05s : name='Ice Cream Girl' age=25 power='Can create ice cream out of thin air' enemies=['The Hot Sauce Squad', 'The Healthy Eaters']Veja o streaming para mais.
Funções / coroutinas assíncronos podem ser usados para consultar simultaneamente o LLM. Isso pode aumentar bastante a velocidade geral da geração e também permitir que outros códigos assíncronos sejam executados enquanto aguardam a saída do LLM. No exemplo abaixo, o LLM gera uma descrição para cada presidente dos EUA enquanto aguarda o próximo da lista. Medir os caracteres gerados por segundo mostra que este exemplo atinge uma aceleração de 7x sobre o processamento serial.
import asyncio
from time import time
from typing import AsyncIterable
from magentic import prompt
@ prompt ( "List ten presidents of the United States" )
async def iter_presidents () -> AsyncIterable [ str ]: ...
@ prompt ( "Tell me more about {topic}" )
async def tell_me_more_about ( topic : str ) -> str : ...
# For each president listed, generate a description concurrently
start_time = time ()
tasks = []
async for president in await iter_presidents ():
# Use asyncio.create_task to schedule the coroutine for execution before awaiting it
# This way descriptions will start being generated while the list of presidents is still being generated
task = asyncio . create_task ( tell_me_more_about ( president ))
tasks . append ( task )
descriptions = await asyncio . gather ( * tasks )
# Measure the characters per second
total_chars = sum ( len ( desc ) for desc in descriptions )
time_elapsed = time () - start_time
print ( total_chars , time_elapsed , total_chars / time_elapsed )
# 24575 28.70 856.07
# Measure the characters per second to describe a single president
start_time = time ()
out = await tell_me_more_about ( "George Washington" )
time_elapsed = time () - start_time
print ( len ( out ), time_elapsed , len ( out ) / time_elapsed )
# 2206 18.72 117.78Veja Asyncio para mais.
functions para @prompt pode conter funções assíncronas/coroutines. Quando os objetos FunctionCall correspondentes são chamados, o resultado deve ser aguardado.Annotated pode ser usada para fornecer descrições e outros metadados para parâmetros de função. Consulte a documentação pydantic sobre o uso Field para descrever argumentos de função.@prompt e @prompt_chain também aceitam um argumento model . Você pode passar em uma instância do OpenaiChatModel para usar o GPT4 ou configurar uma temperatura diferente. Veja abaixo.@prompt , seguindo o notebook Exemplo para um quadro de dados de pandas. O Magentic suporta múltiplos "back -end" (provedores de LLM). Estes são
openai : o back -end padrão que usa o pacote Python openai . Suporta todas as características de Magentic. from magentic import OpenaiChatModelanthropic : usa o pacote anthropic Python. Suporta todos os recursos do Magentic, no entanto, as respostas de streaming são atualmente recebidas de uma só vez. pip install " magentic[anthropic] " from magentic . chat_model . anthropic_chat_model import AnthropicChatModellitellm : usa o pacote litellm Python para permitir a consulta LLMS de muitos fornecedores diferentes. NOTA: Alguns modelos podem não suportar todos os recursos da chamada de função magentic /saída estruturada e saída e streaming. pip install " magentic[litellm] " from magentic . chat_model . litellm_chat_model import LitellmChatModelmistral : usa o pacote Python openai com algumas pequenas modificações para tornar as consultas da API compatíveis com a API Mistral. Suporta todos os recursos do Magentic, no entanto, as chamadas de ferramentas (incluindo saídas estruturadas) não são transmitidas, portanto são recebidas de uma só vez. Nota: Uma versão futura do Magentic pode mudar para usar o pacote mistral Python. from magentic . chat_model . mistral_chat_model import MistralChatModel O back -end e o LLM ( ChatModel ) usado por magentic podem ser configurados de várias maneiras. Quando uma função magentica é chamada, o ChatModel a ser usado segue esta ordem de preferência
ChatModel fornecida como argumento do model para o decorador magentewith MyChatModel:ChatModel global criado a partir de variáveis de ambiente e as configurações padrão no SRC/Magentic/Settings.py from magentic import OpenaiChatModel , prompt
from magentic . chat_model . litellm_chat_model import LitellmChatModel
@ prompt ( "Say hello" )
def say_hello () -> str : ...
@ prompt (
"Say hello" ,
model = LitellmChatModel ( "ollama_chat/llama3" ),
)
def say_hello_litellm () -> str : ...
say_hello () # Uses env vars or default settings
with OpenaiChatModel ( "gpt-3.5-turbo" , temperature = 1 ):
say_hello () # Uses openai with gpt-3.5-turbo and temperature=1 due to context manager
say_hello_litellm () # Uses litellm with ollama_chat/llama3 because explicitly configuredAs seguintes variáveis de ambiente podem ser definidas.
| Variável de ambiente | Descrição | Exemplo |
|---|---|---|
| Magentic_backend | O pacote a ser usado como back -end LLM | Antrópico / Openai / Litellm |
| Magentic_antrópico_model | Modelo Antrópico | Claude-3-Haiku-20240307 |
| Magentic_antrópico_api_key | Chave da API antrópica a ser usada por Magentic | sk -... |
| Magentic_antrópica_base_url | URL base para uma API antrópica compatível | http: // localhost: 8080 |
| Magentic_antrópica_max_tokens | Número máximo de tokens gerados | 1024 |
| Magentic_antrópico_temperature | Temperatura | 0,5 |
| Magentic_litellm_model | Modelo Litellm | Claude-2 |
| Magentic_litellm_api_base | O URL base para consultar | http: // localhost: 11434 |
| Magentic_litellm_max_tokens | Litellm Max Número de tokens gerados | 1024 |
| Magentic_litellm_temperature | Litellm Temperature | 0,5 |
| Magentic_mistral_model | Modelo Mistral | Mistral-Large-Latest |
| Magentic_mistral_api_key | Chave da API Mistral a ser usada por Magentic | XEG ... |
| Magentic_mistral_base_url | URL base para uma API compatível com Mistral | http: // localhost: 8080 |
| Magentic_mistral_max_tokens | Número máximo de tokens gerados | 1024 |
| Magentic_mistral_seed | Semente para amostragem determinística | 42 |
| Magentic_mistral_temperature | Temperatura | 0,5 |
| Magentic_openai_model | Modelo Openai | GPT-4 |
| Magentic_openai_api_key | Chave da API OpenAI a ser usada por Magentic | sk -... |
| Magentic_openai_api_type | Opções permitidas: "Openai", "Azure" | Azure |
| Magentic_openai_base_url | URL base para uma API compatível com o OpenAi | http: // localhost: 8080 |
| Magentic_openai_max_tokens | Openai Max Número de tokens gerados | 1024 |
| Magentic_openai_seed | Semente para amostragem determinística | 42 |
| Magentic_openai_temperature | Temperatura aberta | 0,5 |
Ao usar o back-end openai , definir a variável de ambiente MAGENTIC_OPENAI_BASE_URL ou usar OpenaiChatModel(..., base_url="http://localhost:8080") em código permite que você use magentic com qualquer serviço de AZURE API com o OpenAi Compatível OpenIAI Proxy Server, Localai. Observe que, se a API não suportar chamadas de ferramentas, você não poderá criar funções rápidas que retornem objetos Python, mas outros recursos do magentic ainda funcionarão.
Para usar o Azure com o back -end do OpenAI, você precisará definir a variável de ambiente MAGENTIC_OPENAI_API_TYPE para "azure" ou usar OpenaiChatModel(..., api_type="azure") e também defina as variáveis de ambiente necessárias para o pacote OpenAi para acessar o Azure. Veja https://github.com/openai/openai-python#microsoft-azure-openai
Muitos damas de tipo levantarão avisos ou erros para funções com o decorador @prompt devido à função que não tem valor ou valor de retorno. Existem várias maneiras de lidar com elas.
empty-body . # pyproject.toml
[ tool . mypy ]
disable_error_code = [ " empty-body " ]... (isso não satisfaz mypy) ou raise . @ prompt ( "Choose a color" )
def random_color () -> str : ...# type: ignore[empty-body] em cada função. Nesse caso, você pode adicionar um docstring em vez de ... @ prompt ( "Choose a color" )
def random_color () -> str : # type: ignore[empty-body]
"""Returns a random color."""