Calls to a language model¶
A call to a language model takes its prompt from a file of the package and returns a validated
pydantic model, through a pydantic_ai.Agent.
What to read¶
- The prompt is
app/prompts/extract_parties.md, a Markdown file next to the code. A change to it is reviewed and versioned like a change to the code. load_promptreads it withimportlib.resources.files, which finds the file in an installed wheel as well as in a checkout, and caches it for the process.parties_agentbuilds the agent with the prompt asinstructionsandPartiesasoutput_type. pydantic-ai validates the answer of the model againstPartiesand, when the validation fails, sends the error back to the model, up toretriestimes.extract_partiesruns the agent and returnsresult.output, already aParties.- The tests drive the agent with a
FunctionModel, which stands for the provider: no key and no network.
What to adapt¶
- The prompt,
PartyandPartiesare one example: one prompt file and one output model per task. - The model is a setting (
APP_LLM_MODEL, read by the settings module), passed to the agent as a name such as"openai:gpt-5.5"or as aModel. - Do not name a prompt file
*.prompt.yml: that is the format of GitHub Models, andgh modelsreads such files with its own schema. - Check that the build of the package includes the prompt files.
References¶
code/app/prompts/extract_parties.md¶
a prompt: a Markdown file of the package, reviewed and versioned like code
You read a contract and list its parties.
A party is a person or an organization that signs the contract or is bound by it. For each party,
give its name as the contract writes it and its role in the contract, such as "buyer", "seller",
"lessor" or "lessee".
Leave out witnesses, notaries and people who are only mentioned. If the contract names no party,
return an empty list.
code/app/core/prompts.py¶
the prompt is read with importlib.resources, so it is found in an installed wheel too
"""The prompts of the package, read from the Markdown files of `app/prompts/`."""
from __future__ import annotations
from functools import cache
from importlib.resources import files
@cache
def load_prompt(name: str) -> str:
"""Return the text of the prompt `app/prompts/<name>.md`, read once per process."""
return files("app.prompts").joinpath(f"{name}.md").read_text(encoding="utf-8")
code/app/core/parties.py¶
the agent: the prompt as instructions, a pydantic model as output_type, and the model passed in
"""The parties of a contract, extracted by a model into a validated structure."""
from __future__ import annotations
from typing import TYPE_CHECKING
from pydantic import BaseModel, Field
from pydantic_ai import Agent
from app.core.prompts import load_prompt
if TYPE_CHECKING:
from pydantic_ai.models import Model
class Party(BaseModel):
"""A person or an organization bound by the contract."""
name: str = Field(min_length=1)
role: str = Field(min_length=1)
class Parties(BaseModel):
"""The parties of one contract."""
parties: list[Party]
def parties_agent(model: Model | str) -> Agent[None, Parties]:
"""Return the agent that extracts the parties, with the model the settings name."""
return Agent(
model,
instructions=load_prompt("extract_parties"),
output_type=Parties,
retries=2,
)
async def extract_parties(agent: Agent[None, Parties], contract: str) -> Parties:
"""Return the parties of the text of a contract."""
result = await agent.run(contract)
return result.output
https://pydantic.dev/docs/ai/core-concepts/output/#structured-output¶
how pydantic-ai turns the output of the model into the output_type, and sends a validation error back to it
https://pydantic.dev/docs/ai/core-concepts/agent/#instructions¶
instructions and not system_prompt
In general, we recommend using instructions instead of system_prompt unless you have a specific reason to use system_prompt.
https://docs.python.org/3/library/importlib.resources.html#importlib.resources.files¶
reading a file of a package, wherever the package is installed