You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1. Continue the conversation, without `message_history` the model would not know who "he" was referring to.
116
116
117
+
_(This example is complete, it can be run "as is")_
118
+
117
119
## System Prompts
118
120
119
121
System prompts might seem simple at first glance since they're just strings (or sequences of strings that are concatenated), but crafting the right system prompt is key to getting the model to behave as you want.
120
122
121
123
Generally, system prompts fall into two categories:
122
124
123
-
1.**Static system prompts**: These are known when writing the code and can be defined via the `system_prompt` parameter of the `Agent` constructor.
124
-
2.**Dynamic system prompts**: These aren't known until runtime and should be defined via functions decorated with `@agent.system_prompt`.
125
+
1.**Static system prompts**: These are known when writing the code and can be defined via the `system_prompt` parameter of the [`Agent` constructor][pydantic_ai.Agent.__init__].
126
+
2.**Dynamic system prompts**: These aren't known until runtime and should be defined via functions decorated with [`@agent.system_prompt`][pydantic_ai.Agent.system_prompt].
125
127
126
128
You can add both to a single agent; they're concatenated in the order they're defined at runtime.
127
129
@@ -159,28 +161,323 @@ print(result.data)
159
161
3. Dynamic system prompt defined via a decorator.
160
162
4. Another dynamic system prompt, system prompts don't have to have the `CallContext` parameter.
161
163
164
+
_(This example is complete, it can be run "as is")_
165
+
162
166
## Retrievers
163
167
164
-
* two different retriever decorators (`retriver_plain` and `retriever_context`) depending on whether you want to use the context or not, show an example using both
165
-
* retriever parameters are extracted and used to build the schema for the tool, then validated with pydantic
166
-
* if a retriever has a single "model like" parameter (e.g. pydantic mode, dataclass, typed dict), the schema for the tool will but just that type
167
-
* docstrings are parsed to get the tool description, thanks to griffe docs for each parameter are extracting using Google, numpy or sphinx docstring styling
168
-
* You can raise `ModelRetry` from within a retriever to suggest to the model it should retry
169
-
* the return type of retriever can either be `str` or a JSON object typed as `dict[str, Any]` as some models (e.g. Gemini) support structured return values, some expect text (OpenAI) but seem to be just as good at extracting meaning from the data
168
+
Retrievers provide a mechanism for models to request extra information to help them generate a response.
169
+
170
+
They're useful when it is impractical or impossible to put all the context an agent might need into the system prompt, or when you want to make agents' behavior more deterministic by deferring some of the logic required to generate a response to another tool.
171
+
172
+
!!! info "Retrievers vs. RAG"
173
+
Retrievers are basically the "R" of RAG (Retrieval-Augmented Generation) — they augment what the model can do by letting it request extra information.
174
+
175
+
The main semantic difference between PydanticAI Retreivers and RAG is RAG is synonymous with vector search, while PydanticAI retrievers are more general purpose. (Note: we might add support for some vector search functionality in the future, particuarly an API for generating embeddings, see [#58](https://github.com/pydantic/pydantic-ai/issues/58))
176
+
177
+
There are two different decorator functions to register retrievers:
178
+
179
+
1.[`@agent.retriever_plain`][pydantic_ai.Agent.retriever_plain] — for retrievers that don't need access to the agent [context][pydantic_ai.dependencies.CallContext]
180
+
2.[`@agent.retriever_context`][pydantic_ai.Agent.retriever_context] — for retrievers that do need access to the agent [context][pydantic_ai.dependencies.CallContext]
181
+
182
+
Here's an example using both:
183
+
184
+
```py title="dice_game.py"
185
+
import random
186
+
187
+
from pydantic_ai import Agent, CallContext
188
+
189
+
agent = Agent(
190
+
'gemini-1.5-flash', # (1)!
191
+
deps_type=str, # (2)!
192
+
system_prompt=(
193
+
"You're a dice game, you should roll the dice and see if the number "
194
+
"you got back matches the user's guess, if so tell them they're a winner. "
195
+
"Use the player's name in the response."
196
+
),
197
+
)
198
+
199
+
200
+
@agent.retriever_plain# (3)!
201
+
defroll_dice() -> str:
202
+
"""Roll a six-sided dice and return the result."""
203
+
returnstr(random.randint(1, 6))
204
+
205
+
206
+
@agent.retriever_context# (4)!
207
+
defget_player_name(ctx: CallContext[str]) -> str:
208
+
"""Get the player's name."""
209
+
return ctx.deps
210
+
211
+
212
+
dice_result = agent.run_sync('My guess is 4', deps='Adam') # (5)!
213
+
print(dice_result.data)
214
+
#> Congratulations Adam, you guessed correctly! You're a winner!
215
+
```
216
+
217
+
1. This is a pretty simple task, so we can use the fast and cheap Gemini flash model.
218
+
2. We pass the user's name as the dependency, to keep things simple we use just the name as a string as the dependency.
219
+
3. This retriever doesn't need any context, it just returns a random number. You could probably use a dynamic system prompt in this case.
220
+
4. This retriever needs the player's name, so it uses `CallContext` to access dependencies which are just the player's name.
221
+
5. Run the agent, passing the player's name as the dependency.
222
+
223
+
_(This example is complete, it can be run "as is")_
224
+
225
+
Let's print the messages from that game to see what happened:
226
+
227
+
```python title="dice_game_messages.py"
228
+
from dice_game import dice_result
229
+
230
+
print(dice_result.all_messages())
231
+
"""
232
+
[
233
+
SystemPrompt(
234
+
content="You're a dice game, you should roll the dice and see if the number you got back matches the user's guess, if so tell them they're a winner. Use the player's name in the response.",
content="Congratulations Adam, you guessed correctly! You're a winner!",
278
+
timestamp=datetime.datetime(...),
279
+
role='model-text-response',
280
+
),
281
+
]
282
+
"""
283
+
```
284
+
285
+
We can represent that as a flow diagram, thus:
286
+
287
+

288
+

289
+
290
+
### Retrievers, tools, and schema
291
+
292
+
Under the hood, retrievers use the model's "tools" or "functions" API to let the model know what retrievers are available to call. Tools or functions are also used to define the schema(s) for structured responses, thus a model might have access to many tools, some of which call retrievers while others end the run and return a result.
293
+
294
+
Function parameters are extracted from the function signature, and all parameters except `CallContext` are used to build the schema for that tool call.
295
+
296
+
Even better, PydanticAI extracts the docstring from retriever functions and (thanks to [griffe](https://mkdocstrings.github.io/griffe/)) extracts parameter descriptions from the docstring and add them to the schema.
297
+
298
+
[Griffe supports](https://mkdocstrings.github.io/griffe/reference/docstrings/#docstrings) extracting parameter descriptions from `google`, `numpy` and `sphinx` style docstrings, PydanticAI will infer the format to use based on the docstring. We'll add support in future to explicitly set the style to use, and warn/error if not all parameters are documented, see [#59](https://github.com/pydantic/pydantic-ai/issues/59).
299
+
300
+
To demonstrate retriever schema, here we use [`FunctionModel`][pydantic_ai.models.function.FunctionModel] to print the schema a model would receive:
301
+
302
+
```py title="retriever_schema.py"
303
+
from pydantic_ai import Agent
304
+
from pydantic_ai.messages import Message, ModelAnyResponse, ModelTextResponse
305
+
from pydantic_ai.models.function import AgentInfo, FunctionModel
_(This example is complete, it can be run "as is")_
352
+
353
+
The return type of retriever can any valid JSON object ([`JsonData`][pydantic_ai.dependencies.JsonData]) as some models (e.g. Gemini) support semi-structured return values, some expect text (OpenAI) but seem to be just as good at extracting meaning from the data, if a Python is returned and the model expects a string, the value will be serialized to JSON
170
354
171
355
## Reflection and self-correction
172
356
173
-
* validation errors from both retrievers parameter validation and structured result validation can be passed back to the with a request to retry
174
-
* as described above, you can also raise `ModelRetry` from within a retriever or result validator to tell the model it should retry
175
-
* the default retry count is 1, but can be altered both on a whole agent, or on a per-retriever basis and result validator basis
176
-
* you can access the current retry count from within a retriever or result validator via `ctx.retry`
357
+
Validation errors from both retriever parameter validation and [structured result validation](results.md#structured-result-validation) can be passed back to the model with a request to retry.
358
+
359
+
You can also raise [`ModelRetry`][pydantic_ai.exceptions.ModelRetry] from within a [retriever](#retrievers) or [result validator functions](results.md#result-validators-functions) to tell the model it should retry.
360
+
361
+
- The default retry count is **1** but can be altered for the [entire agent][pydantic_ai.Agent.__init__], a [specific retriever][pydantic_ai.Agent.retriever_context], or a [result validator][pydantic_ai.Agent.__init__].
362
+
- You can access the current retry count from within a retriever or result validator via [`ctx.retry`][pydantic_ai.dependencies.CallContext].
363
+
364
+
Here's an example:
365
+
366
+
```py title="retriever_retry.py"
367
+
from fake_database import DatabaseConn
368
+
from pydantic import BaseModel
369
+
370
+
from pydantic_ai import Agent, CallContext, ModelRetry
f'No user found with name {name!r}, remember to provide their full name'
395
+
)
396
+
return user_id
397
+
398
+
399
+
result = agent.run_sync(
400
+
'Send a message to John Doe asking for coffee next week', deps=DatabaseConn()
401
+
)
402
+
print(result.data)
403
+
"""
404
+
user_id=123 message='Hello John, would you be free for coffee sometime next week? Let me know what works for you!'
405
+
"""
406
+
```
177
407
178
408
## Model errors
179
409
180
-
* If models behave unexpectedly, e.g. the retry limit is exceed, agent runs will raise `UnexpectedModelBehaviour` exceptions
181
-
* If you use PydanticAI in correctly, we try to raise a `UserError` with a helpful message
182
-
* show an except of a `UnexpectedModelBehaviour` being raised
183
-
* if a `UnexpectedModelBehaviour` is raised, you may want to access the [`.last_run_messages`][pydantic_ai.Agent.last_run_messages] attribute of an agent to see the messages exchanged that led to the error, show an example of accessing `.last_run_messages` in an except block to get more details
410
+
If models behave unexpectedly (e.g., the retry limit is exceeded, or their API returns `503`), agent runs will raise [`UnexpectedModelBehaviour`][pydantic_ai.exceptions.UnexpectedModelBehaviour].
411
+
412
+
In these cases, [`agent.last_run_messages`][pydantic_ai.Agent.last_run_messages] can be used to access the messages exchanged during the run to help diagnose the issue.
413
+
414
+
```python
415
+
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehaviour
416
+
417
+
agent = Agent('openai:gpt-4o')
418
+
419
+
420
+
@agent.retriever_plain
421
+
defcalc_volume(size: int) -> int: # (1)!
422
+
if size ==42:
423
+
return size**3
424
+
else:
425
+
raise ModelRetry('Please try again.')
426
+
427
+
428
+
try:
429
+
result = agent.run_sync('Please get me the volume of a box with size 6.')
430
+
except UnexpectedModelBehaviour as e:
431
+
print('An error occurred:', e)
432
+
#> An error occurred: Retriever exceeded max retries count of 1
433
+
print('cause:', repr(e.__cause__))
434
+
#> cause: ModelRetry('Please try again.')
435
+
print('messages:', agent.last_run_messages)
436
+
"""
437
+
messages:
438
+
[
439
+
UserPrompt(
440
+
content='Please get me the volume of a box with size 6.',
441
+
timestamp=datetime.datetime(...),
442
+
role='user',
443
+
),
444
+
ModelStructuredResponse(
445
+
calls=[
446
+
ToolCall(
447
+
tool_name='calc_volume',
448
+
args=ArgsObject(args_object={'size': 6}),
449
+
tool_id=None,
450
+
)
451
+
],
452
+
timestamp=datetime.datetime(...),
453
+
role='model-structured-response',
454
+
),
455
+
RetryPrompt(
456
+
content='Please try again.',
457
+
tool_name='calc_volume',
458
+
tool_id=None,
459
+
timestamp=datetime.datetime(...),
460
+
role='retry-prompt',
461
+
),
462
+
ModelStructuredResponse(
463
+
calls=[
464
+
ToolCall(
465
+
tool_name='calc_volume',
466
+
args=ArgsObject(args_object={'size': 6}),
467
+
tool_id=None,
468
+
)
469
+
],
470
+
timestamp=datetime.datetime(...),
471
+
role='model-structured-response',
472
+
),
473
+
]
474
+
"""
475
+
else:
476
+
print(result.data)
477
+
```
478
+
1. Define a retriever that will raise `ModelRetry` repeatedly in this case.
479
+
480
+
_(This example is complete, it can be run "as is")_
Copy file name to clipboardExpand all lines: docs/dependencies.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Dependencies
2
2
3
-
PydanticAI uses a dependency injection system to provide data and services to your agent's [system prompts](agents.md#system-prompts), [retrievers](agents.md#retrievers) and [result validators](results.md#result-validators).
3
+
PydanticAI uses a dependency injection system to provide data and services to your agent's [system prompts](agents.md#system-prompts), [retrievers](agents.md#retrievers) and [result validators](results.md#result-validators-functions).
4
4
5
5
Matching PydanticAI's design philosophy, our dependency system tries to use existing best practice in Python development rather than inventing esoteric "magic", this should make dependencies type-safe, understandable easier to test and ultimately easier to deploy in production.
6
6
@@ -159,7 +159,7 @@ _(This example is complete, it can be run "as is")_
159
159
160
160
## Full Example
161
161
162
-
As well as system prompts, dependencies can be used in [retrievers](agents.md#retrievers) and [result validators](results.md#result-validators).
162
+
As well as system prompts, dependencies can be used in [retrievers](agents.md#retrievers) and [result validators](results.md#result-validators-functions).
0 commit comments