Skip to content

Commit 743aadb

Browse files
committed
Record instructions on the agent run span even when an instructions function is used.
1 parent db93a9c commit 743aadb

File tree

2 files changed

+796
-8
lines changed

2 files changed

+796
-8
lines changed

pydantic_ai_slim/pydantic_ai/agent/__init__.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -685,15 +685,21 @@ async def get_instructions(run_context: RunContext[AgentDepsT]) -> str | None:
685685
finally:
686686
try:
687687
if instrumentation_settings and run_span.is_recording():
688-
run_span.set_attributes(self._run_span_end_attributes(state, usage, instrumentation_settings))
688+
run_span.set_attributes(
689+
self._run_span_end_attributes(
690+
state, usage, instrumentation_settings, graph_deps.new_message_index
691+
)
692+
)
689693
finally:
690694
run_span.end()
691695

692696
def _run_span_end_attributes(
693-
self, state: _agent_graph.GraphAgentState, usage: _usage.RunUsage, settings: InstrumentationSettings
697+
self,
698+
state: _agent_graph.GraphAgentState,
699+
usage: _usage.RunUsage,
700+
settings: InstrumentationSettings,
701+
new_message_index: int,
694702
):
695-
literal_instructions, _ = self._get_instructions()
696-
697703
if settings.version == 1:
698704
attrs = {
699705
'all_messages_events': json.dumps(
@@ -704,19 +710,32 @@ def _run_span_end_attributes(
704710
)
705711
}
706712
else:
707-
attrs = {
713+
# Store the last instructions here for convenience
714+
last_instructions = InstrumentedModel._get_instructions(state.message_history) # pyright: ignore[reportPrivateUsage]
715+
attrs: dict[str, Any] = {
708716
'pydantic_ai.all_messages': json.dumps(settings.messages_to_otel_messages(list(state.message_history))),
709-
**settings.system_instructions_attributes(literal_instructions),
717+
**settings.system_instructions_attributes(last_instructions),
710718
}
711719

720+
# Store an attribute that indicates that the instructions from this agent run were not always the same
721+
# This can signal to an observability UI that different steps in the agent run had different instructions
722+
# Note: We purposely only look at "new" messages because they are the only ones produced by this agent run.
723+
for m in state.message_history[new_message_index:]:
724+
if (
725+
isinstance(m, _messages.ModelRequest)
726+
and m.instructions is not None
727+
and m.instructions != last_instructions
728+
):
729+
attrs['pydantic_ai.variable_instructions'] = True
730+
712731
return {
713732
**usage.opentelemetry_attributes(),
714733
**attrs,
715734
'logfire.json_schema': json.dumps(
716735
{
717736
'type': 'object',
718737
'properties': {
719-
**{attr: {'type': 'array'} for attr in attrs.keys()},
738+
**{k: {'type': 'array'} if isinstance(v, str) else {} for k, v in attrs.items()},
720739
'final_result': {'type': 'object'},
721740
},
722741
}

0 commit comments

Comments
 (0)