Prompt Manager — Cliente Python
prompt-manager-client é a biblioteca que as aplicações de IA importam para buscar seus prompts na API do Prompt Manager em tempo de execução. Os prompts se atualizam ao vivo: quando alguém commita e publica uma nova versão na UI, a aplicação passa a usá-la em segundos, sem redeploy.
Ela é deliberadamente pequena — três coisas públicas:
PromptManager— cliente síncrono (requests)AsyncPromptManager— cliente assíncrono (httpx), mesma interface comawaitPrompt— o prompt imutável e totalmente resolvido que uma busca retorna
Instalação
O pacote está publicado no PyPI como prompt-manager-client-dl:
uv add prompt-manager-client-dl
# ou: pip install prompt-manager-client-dl
O nome de import continua sendo prompt_manager. O código-fonte vive neste repositório, em client/ — para desenvolver contra ele, instale como dependência de path/git:
uv add "prompt-manager-client-dl @ git+ssh://git@bitbucket.org/avisourgente/prompt-manager.git#subdirectory=client"
Requer Python ≥ 3.10. Dependências: requests, httpx, cachetools.
Começo rápido
Crie um cliente por processo na inicialização (ele guarda o cache e a sessão HTTP) e reutilize-o:
from prompt_manager import PromptManager
pm = PromptManager(
application="superchat", # o nome da sua aplicação no manager
environment="prod", # qual deployment seguir
fallback_dir="prompts_fallback", # opcional: snapshots offline (veja abaixo)
)
# Uma chamada: busca (com cache) + preenche variáveis + relata o uso
text = pm.render(
"resposta_juridica",
variables={"pergunta": pergunta, "contexto": contexto},
request_id=request_id, # campos opcionais de observabilidade —
user=user_email, # eles vinculam o caso real ao prompt
metadata={"tenant": tenant}, # na tela de "Registros" do manager
)
render() retorna a string final pronta para enviar ao modelo — e levanta PromptValidationError se o prompt define mensagens de chat. Para esses (ou para padronizar tudo em mensagens), use render_messages(), que aceita os mesmos argumentos e retorna uma lista [{"role", "content"}] no formato OpenAI:
messages = pm.render_messages(
"resposta_juridica",
variables={"pergunta": pergunta, "contexto": contexto},
)
# → [{"role": "system", "content": "..."}, {"role": "user", "content": "..."}]
# pronto para qualquer SDK no formato OpenAI (openai, litellm, ...):
resposta = client.chat.completions.create(model=..., messages=messages)
# LangChain aceita esses dicts diretamente:
resposta = chat_model.invoke(messages)
Um prompt sem marcadores de papel vira uma única mensagem system — então render_messages() pode ser o único caminho da sua aplicação, sem ramificar pelo formato do prompt.
Onde o cliente se conecta (base_url)
O endereço da API é resolvido nesta ordem:
- o argumento
base_urldo construtor, se informado; - a variável de ambiente
PROMPT_MANAGER_URL; - o padrão de produção:
http://promptmanager-prod.datalawyer.local.
Ou seja: em produção você não configura nada, e deployments de staging/dev redirecionam todos os clientes com uma única variável de ambiente:
export PROMPT_MANAGER_URL=http://localhost:8000 # ex.: stack de dev local
Async
from prompt_manager import AsyncPromptManager
pm = AsyncPromptManager(application="superchat", environment="prod")
async def handle(question: str) -> str:
return await pm.render("resposta_juridica", variables={"pergunta": question})
Use-o como async context manager (async with AsyncPromptManager(...) as pm:) ou mantenha-o pela vida do processo; ao sair, ele descarrega os relatos de uso pendentes e fecha seu cliente httpx (a menos que você tenha passado o seu próprio).
Trabalhando com o objeto Prompt
get_prompt() retorna o prompt resolvido sem renderizá-lo:
prompt = pm.get_prompt("resposta_juridica")
prompt.template # texto resolvido completo (fragmentos e exemplos já expandidos)
prompt.variables # as variáveis de runtime que o template ainda espera
prompt.version # número da versão commitada que esta resolução usou
prompt.hash # hash do snapshot — registre-o para rastrear qualquer chamada de LLM até o prompt exato
prompt.suggested_model # modelo + parâmetros sugeridos pelo autor do prompt
prompt.suggested_params
prompt.fragment_versions # quais versões de fragmentos foram embutidas
prompt.fewshots # quais bancos de exemplos foram injetados, e quantos exemplos cada um contribuiu
prompt.messages # o template já dividido em mensagens de chat pelo servidor
prompt.is_chat # True se o template usa marcadores de papel
rendered = prompt.render(pergunta="...", contexto="...") # prompts de texto
messages = prompt.render_messages(pergunta="...", contexto="...") # qualquer prompt
render e render_messages (tanto no cliente quanto no Prompt) validam os valores: nomes de variáveis desconhecidos, variáveis obrigatórias faltando e {{placeholders}} restantes levantam PromptValidationError. Valores dict/list são codificados em JSON automaticamente.
Fragmentos
Fragmentos (blocos reutilizáveis: modelos de documento, seções de instrução, personas) são servidos pelo mesmo /fetch que os prompts — get_prompt() funciona para ambos e prompt.kind distingue ("prompt" ou "fragment"). Para o caso comum — a aplicação quer só o texto do fragmento, mantendo os {{placeholders}} para preencher depois — use get_fragment_text():
modelo = pm.get_fragment_text("modelo-contestacao") # placeholders intactos
modelo = pm.get_fragment_text("modelo-contestacao", values={"esfera": "civel"})
Sub-fragmentos e bancos de exemplos chegam expandidos pelo servidor; ramos {% if %} são resolvidos localmente com os values informados (os defaults registrados preenchem os omitidos); marcadores de papel são descartados e o texto vem inteiro. Diferente de render(), nada é obrigatório — placeholders restantes são devolvidos como estão. Cache, revalidação por ETag e fallback offline funcionam exatamente como em get_prompt(); fragmentos compartilhados vivem na pseudo-aplicação shared (PromptManager(application="shared", ...)). Disponível também no AsyncPromptManager (await pm.get_fragment_text(...)).
Mensagens de chat
Um template pode conter linhas com marcadores de papel {% system %} / {% user %} / {% assistant %} — uma nova mensagem começa em cada marcador, e o conteúdo antes do primeiro pertence a system. O servidor já entrega o template dividido (prompt.messages), e render_messages() preenche as variáveis mantendo os papéis. render() recusa prompts de chat (PromptValidationError) em vez de devolver texto com marcadores embutidos; se você realmente quiser o texto plano, junte os conteúdos de render_messages() deliberadamente. Snapshots de fallback antigos (sem o campo messages) continuam funcionando: o cliente divide o template localmente com a mesma regra do servidor.
Ambientes
O environment informado na construção é o padrão; qualquer chamada pode sobrescrevê-lo:
pm.get_prompt("resposta_juridica", environment="staging")
"default" é especial: ele sempre segue a versão commitada mais recente, sem precisar de deployment explícito. Ambientes nomeados (prod, staging, …) servem a versão que foi fixada neles pela UI — e caem para a versão mais recente se o prompt nunca foi fixado ali.
Fluxo ({% if %} no template)
Não existe uma classe separada de variáveis de "controle": as booleanas e categóricas que decidem os ramos {% if %} são variáveis comuns, passadas em variables junto com as de texto. O servidor entrega o template com as tags {% if %} intactas e a biblioteca resolve os ramos localmente a cada render; a mesma variável também pode ser impressa como {{placeholder}} (booleanas viram true/false). Uma busca e uma entrada de cache cobrem todas as combinações.
messages = pm.render_messages(
"chitchat",
variables={
"agente_customizado": True, # booleana → decide o ramo
"agent_description": descricao, # variável de texto → preenche {{...}}
"query": pergunta,
...
},
)
Uma booleana/categórica omitida usa o valor padrão registrado no manager — a mesma regra do servidor, então o texto resultante é byte a byte o que o playground mostra para os mesmos valores. Variáveis cujo único uso está em um ramo desligado deixam de ser obrigatórias (continuam aceitas, apenas não são usadas). Snapshots de fallback gerados pelo prompt-manager pull preservam as tags, então o fallback offline também cobre todas as combinações.
Ablação de few-shots (A/B sem exemplos)
fewshots=False resolve o mesmo prompt com todos os bancos de exemplos desligados — é assim que uma aplicação mede o que os exemplos realmente valem:
com_exemplos = pm.render("classificador", variables=v) # normal
sem_exemplos = pm.render("classificador", variables=v, fewshots=False) # ablação
As duas resoluções têm cache e ETag separados.
Cache, resiliência e fallback offline
O cliente é construído para que a API de prompts nunca seja um ponto único de falha:
- Cache com TTL (padrão 45 s,
ttl=no construtor): buscas repetidas dentro da janela não custam nada. - Revalidação por ETag: expirado o TTL, o cliente revalida com
If-None-Match; um prompt inalterado custa um 304, não um novo download. - Último valor conhecido: se a API estiver inacessível, o cliente registra um warning e continua servindo o último prompt buscado com sucesso (por chave nome/ambiente/fewshots), pela vida do processo.
- Arquivos de fallback locais: se a API estiver inacessível e nada foi buscado ainda (ex.: logo após um cold start), o cliente carrega
<fallback_dir>/<nome>.<ambiente>.json, depois<fallback_dir>/<nome>.jsone, em último caso,<fallback_dir>/<nome>.default.json— assim um bundle com apenas snapshotsdefaultcontinua útil emprod/staging.
Só quando os quatro falham ele levanta PromptUnavailableError. O resultado obsoleto/de fallback também entra no cache com TTL, então uma indisponibilidade custa no máximo um timeout por janela de TTL, não um por chamada.
Gere os snapshots de fallback com a CLI incluída (commite-os no seu repositório ou embuta-os na sua imagem, atualizando a cada deploy):
uv run prompt-manager pull --app superchat --env prod --out prompts_fallback/
(--url sobrescreve o endereço da API; caso contrário valem PROMPT_MANAGER_URL / o padrão de produção, igual à biblioteca.)
Relato de uso
Cada render()/render_messages() dispara (fire-and-forget) um relato para POST /api/v1/usage com a identidade do prompt (nome, versão, hash do snapshot) e os valores de variáveis com que foi preenchido. A UI do manager os lê de volta para que os prompts possam ser testados contra casos reais de produção em vez de casos inventados.
- Nunca bloqueia nem quebra um render — falhas são logadas em nível debug e descartadas.
- Passe
request_id,useremetadatapara tornar os casos gravados filtráveis. - Desabilite completamente com
report_usage=False(ex.: em testes), ou no servidor deixandoOPENSEARCH_HOSTvazio.
Referência do construtor
| Parâmetro | Padrão | Significado |
|---|---|---|
application |
(obrigatório) | Nome da aplicação registrada no manager |
base_url |
$PROMPT_MANAGER_URL, senão http://promptmanager-prod.datalawyer.local |
Raiz da API do Prompt Manager |
environment |
"default" |
Ambiente padrão de todas as chamadas |
ttl |
45 |
Segundos que um prompt buscado é servido sem revalidação |
fallback_dir |
None |
Diretório com snapshots offline do prompt-manager pull |
timeout |
10 |
Timeout HTTP em segundos |
session / client |
None |
Traga seu próprio requests.Session / httpx.AsyncClient |
report_usage |
True |
Relata as variáveis de cada render para a tela de Registros |
Release files for prompt-manager-client-dl 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| prompt_manager_client_dl-0.6.0.tar.gz | 27.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prompt_manager_client_dl-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.0 kB
Release files / prompt_manager_client_dl-0.6.0.tar.gz
| Download URL | prompt_manager_client_dl-0.6.0.tar.gz |
|---|---|
| Size | 27.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
defd259b22dfe1b0b0300b3d878bc94dcb96fa6d5207072fc0f6405562845233
|
|
BLAKE2b-256 checksum How to use checksums |
dc0421b9e7f301f8e6c421bbb21de9db75e0552247adc878f9c74ec46de07869
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / prompt_manager_client_dl-0.6.0-py3-none-any.whl
| Download URL | prompt_manager_client_dl-0.6.0-py3-none-any.whl |
|---|---|
| Size | 19.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d66f43e8bc22a423b550645e8f49fb90c1cb481800442ab8e178d5175528c1ca
|
|
BLAKE2b-256 checksum How to use checksums |
b6a366a3fd66d73201620a2bb0c1efcce264f7e0bfde4a076e95aab5cfba2ea4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|