What do partial variables do in a LangChain PromptTemplate?
answer
- pre-bind some inputs, narrow the rest
- returns a new template, never mutates
- values may be zero-argument callables
- callable resolves at format time, not import
- fresh values break prompt-prefix caching
basics
~20 sPartial variables pre-bind some of a template's inputs, returning a new template that expects only the remaining ones. Values may be plain strings or zero-argument callables, which are evaluated each time the prompt is formatted.
solid answer
~40 sA prompt declares `input_variables`; `partial_variables` are the subset already supplied. You either pass `partial_variables={...}` to the constructor or call `prompt.partial(persona="an SRE")`, which returns a **new** template whose `input_variables` no longer include the bound names. That lets you configure a prompt once at wiring time and hand callers a narrower interface — the caller only supplies the genuinely per-request fields. A partial value can also be a callable taking no arguments: it is invoked at format time, so `partial(today=lambda: date.today().isoformat())` gives you a fresh timestamp per call rather than a value frozen at import. Two cautions: `.partial()` does not mutate the original, so you must keep the returned object, and a callable partial re-evaluates on every request, which means non-deterministic values silently break prompt-prefix caching upstream.
code
python · 16 linesfrom datetime import date
from langchain_core.prompts import ChatPromptTemplate
base = ChatPromptTemplate.from_messages([
("system", "Today is {today}. You answer as {persona}."),
("human", "{question}"),
])
configured = base.partial(
today=lambda: date.today().isoformat(), # evaluated per format call
persona="a terse SRE",
)
print(configured.input_variables) # ['question']
print(base.input_variables) # ['today', 'persona', 'question']
print(configured.format_messages(question="Is the API up?"))go deeper
Know that partial variables pre-fill some of a template's inputs and that .partial() hands you back a new template needing fewer arguments.
Explain that partial values may be zero-argument callables resolved at format time, that the original prompt is unchanged, and how input_variables shrinks after binding.
Show the operating consequences: a frozen timestamp in a long-lived process, and a volatile callable partial early in the system message wrecking provider prompt-prefix caching and trace grouping.
Own where configuration is bound across the codebase — one wiring layer that partials prompts for the app, a narrow per-request interface for callers, and CI assertions on prompt input_variables so renames fail loudly.
## The problem partials solve A prompt often has two kinds of inputs: things fixed by the application (a persona, an output format, a tenant name, a tool list) and things that vary per request (the user's question, retrieved context). Without partials you must thread the fixed values through every call site, which means every caller has to know about them and every one of them can get it wrong. `partial_variables` split the two. The template still declares all names, but the fixed ones are bound once and disappear from the interface the caller sees. ## The two ways to bind At construction: ``` PromptTemplate( template="You are {persona}. Answer: {question}", input_variables=["question"], partial_variables={"persona": "a terse SRE"}, ) ``` Or after the fact, with `.partial()`: ``` base = PromptTemplate.from_template("You are {persona}. Answer: {question}") configured = base.partial(persona="a terse SRE") configured.input_variables # ['question'] base.input_variables # ['persona', 'question'] — unchanged ``` `.partial()` returns a copy. This is the single most common bug: calling `prompt.partial(...)` for its side effect and then formatting the original, which still demands the variable. Treat prompts as immutable values. Both `PromptTemplate` and `ChatPromptTemplate` support this, because `partial()` lives on the shared base class. For a chat prompt the partial applies across every message template that mentions the name. ## Callable partials A partial value may be a zero-argument callable. At format time the template resolves partials by calling anything callable and using the return value: ``` prompt = PromptTemplate.from_template("Today is {today}. {question}").partial( today=lambda: date.today().isoformat() ) ``` This is the idiomatic way to inject a current timestamp, a request id, a feature-flag snapshot, or a lazily-computed tool catalogue. The alternative — `partial(today=date.today().isoformat())` — freezes the value at process start, which is a classic long-running-service bug: a server booted on Monday keeps telling the model it is Monday. Note the callable takes no arguments, so it cannot see the request's other variables. If the value depends on the request, it is not a partial; it is an input variable that some upstream step computes. ## What partials are not They are not a templating feature in the model's eyes — the rendered prompt is one flat text or message list either way. They are purely an interface-narrowing device on the Python side. They are also not a security boundary. A partial value is substituted into the template output like any other variable; it does not get escaped or sandboxed. Binding untrusted text as a partial is exactly as risky as binding it as an ordinary input. And they do not compose with defaults in the way people expect: there is no notion of "default that a caller may override at format time". If you supply a value at format time for a name that is also partialled, you get a duplicate-variable error rather than an override. If you want overridable defaults, build a fresh partial per call, or keep the name as a regular input variable and default it in your own wrapper function. ## Operational consequences The big one is caching. Many providers cache a stable prompt prefix, and observability tools group traces by prompt fingerprint. A callable partial that injects a second-resolution timestamp near the top of the system message changes the prefix on every request, quietly destroying both. If you need a timestamp, put it late in the prompt, coarsen it (date, not second), or reconsider whether the model needs it at all. The second is testability. Because the configured prompt is a plain object, a test can assert on `configured.input_variables` and on `configured.format(...)` output without a model in the loop. That is one of the strongest arguments for prompt objects over inline strings: the prompt's interface becomes something CI can check when someone renames a variable. ## In review Good signs: fixed configuration bound once at wiring time; callables for anything time-varying; the partialled object stored and reused. Bad signs: `.partial()` results discarded, timestamps frozen at import, and per-request data smuggled in through a closure so the prompt's declared interface no longer describes what actually varies.
- What happens if you also pass a partialled variable at format time?You get an error rather than an override — the partial and the supplied keyword collide as a duplicate value for the same name. Partials are bindings, not defaults. If you want a value the caller may override, keep it as a normal input variable and apply the default in your own wrapper, or build a new partial per request with the desired value.
- When is a callable partial the wrong tool?When the value depends on the request. A partial callable takes no arguments, so it cannot see the other inputs; anything derived from the user's question or retrieved context belongs upstream as a computed input variable. Callable partials are for ambient, request-independent facts like the current date or a config snapshot.
- Why can a partial hurt latency or cost in production?Providers commonly cache a stable prompt prefix, and a callable partial that injects a changing value — a precise timestamp, a request id — near the top of the system message invalidates that prefix on every call. Put volatile values late in the prompt, coarsen their granularity, or drop them if the model does not actually use them.
saying these in an interview costs you the question
- Thinking .partial() mutates the prompt in place
- Freezing a timestamp by calling the function when binding
- Treating partials as overridable defaults
- Passing request-specific data as a partial via a closure
- Assuming partial values are escaped or sanitised