-
Notifications
You must be signed in to change notification settings - Fork 421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds dynamic
to system_prompt
decorator, allowing reevaluation
#560
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM in general, but you need to format code and fix linting issues.
Co-authored-by: Samuel Colvin <[email protected]>
@samuelcolvin I'm reviewing this, since I found a bug, when using dynamic=True on one decorator it reevaluates (builds) all other sys_parts. I will have to make a discrimination or annotate the message once is built. |
Hey @josead |
I will commit Tomorrow, but yes I have an approach
El mar, 31 de dic de 2024, 7:59 p. m., Hamza Farhan <
***@***.***> escribió:
… Hey @josead <https://github.com/josead>
Were you able to solve the issue?
—
Reply to this email directly, view it on GitHub
<#560 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABM4XWN43HEPZTA3E7AFMDT2IMOUZAVCNFSM6AAAAABUKEBRB2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDKNRWG42DIOBXHA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@HamzaFarhan @samuelcolvin Please review this approach. Now each DynamicSystemPromptPart has a reference to the system prompt runner. Which makes the lookup easier for reevaluation. It uses id(), the python memory index. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise looking good.
Co-authored-by: Samuel Colvin <[email protected]>
This ref will be __qualname__ Co-authored-by: Samuel Colvin <[email protected]>
Co-authored-by: Samuel Colvin <[email protected]>
Co-authored-by: Samuel Colvin <[email protected]>
Co-authored-by: Samuel Colvin <[email protected]>
Same as `SystemPromptPart`, but its content is regenerated on each run. | ||
""" | ||
|
||
ref: str = '' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samuelcolvin I had to make a default value here because of:
Fields without default values cannot appear after fields with default values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you need a new dataclass, also with this setup part_kind
can't act as a proper disciminator. Set better to have a new class with
part_kind: Literal['system-prompt-dynamic'] = 'system-prompt-dynamic'
Or probably simpler make this dynamic_ref: str | None = None
and just add it to SystemPromptPart
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the latter is better, since I wanted to keep the type-checks of existing code. I will make the changes.
@@ -535,16 +539,24 @@ def system_prompt(self, func: Callable[[], str], /) -> Callable[[], str]: ... | |||
@overload | |||
def system_prompt(self, func: Callable[[], Awaitable[str]], /) -> Callable[[], Awaitable[str]]: ... | |||
|
|||
@overload | |||
def system_prompt(self, /, *, dynamic: bool = False): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def system_prompt(self, /, *, dynamic: bool = False): ... | |
def system_prompt(self, /, *, dynamic: bool = False) -> Callable[..., Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samuelcolvin is this what is expected here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no that's not right, I think it should be
-> Callable[[_system_prompt.SystemPromptFunc[AgentDeps]], _system_prompt.SystemPromptFunc[AgentDeps]]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but you should be able to work it out from looking at the other cases and checking with pyright.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now is fixed!
dynamic
to system_prompt
decorator, allowing reevaluation
thanks so much. |
Quality Gate failedFailed conditions |
@josead what if I have all_messages from a run of agent1 and I want to pass it as history to a run of agent2? Both agents have their own system prompts but this would overwrite agent2's system prompt even if both are dynamic because of the dynamic_ref not matching. How can we do this? |
I'm also confused about how dynamic system prompt works here. |
@HamzaFarhan Can you add a code example of your case so I can reproduce your approach? |
@tranhoangnguyen03 is your use case same as Hamza? I'd love to help you if you show me what you are trying to achieve. What I'm thinking here is that the messages needs to be managed with some utility functions, this way we can operate in the conversation doing some filtering and insertion of system prompts, etc.. |
from copy import deepcopy
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext, models
from pydantic_ai import messages as _messages
from pydantic_ai import usage as _usage
from pydantic_ai.result import ResultData
from pydantic_ai.tools import AgentDeps
@dataclass
class User:
name: str
anime_agent = Agent(
name="anime_fan",
model="google-gla:gemini-1.5-flash",
system_prompt="You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
deps_type=User,
result_type=str,
)
summary_agent = Agent(
name="summary_agent",
model="google-gla:gemini-1.5-flash",
system_prompt="Your job is to summarize the conversation into bullet points when asked.",
result_type=str,
)
@anime_agent.system_prompt(dynamic=True)
def system_prompt(ctx: RunContext[User]) -> str:
return f"The user's name is {ctx.deps.name}. And the last anime they watched is Solo Leveling."
deps = User(name="Hamza")
message_history = None
user_prompt = "Hey"
while user_prompt.lower() not in ["q", "quit", "exit"]:
res = await anime_agent.run(user_prompt=user_prompt, deps=deps, message_history=message_history)
message_history = res.all_messages()
user_prompt = input(f"{res.data} (q to quit)> ")
res.all_messages()
"""
[
{
"parts": [
{
"content": "You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
"dynamic_ref": null,
"part_kind": "system-prompt"
},
{
"content": "The user's name is Hamza. And the last anime they watched is Solo Leveling.",
"dynamic_ref": "system_prompt",
"part_kind": "system-prompt"
},
{
"content": "Hey",
"timestamp": "2025-01-16T13:02:44.164903Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Yo Hamza! What's up, dude? Just finished re-watching a few episodes of *Mob Psycho 100*, myself. Been meaning to check out some other stuff, but man, that animation style is just *chef's kiss*. So, whatcha been up to? Last I heard you were diving into *Solo Leveling*. How'd you like it? I'm planning on watching it soon, heard the action sequences are insane.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:02:46.184918Z",
"kind": "response"
},
{
"parts": [
{
"content": "I liked it. a bit childish but the action was good. got any recs?",
"timestamp": "2025-01-16T13:03:18.104051Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Ah, yeah, I get the \"childish\" vibe sometimes with *Solo Leveling*'s power fantasy aspect. It's definitely more shonen than anything else. \n\nFor recs... depends what you're in the mood for! If you liked the action, but want something a bit more mature, maybe *Jujutsu Kaisen*? The animation is gorgeous, the fights are creative and brutal, and the characters are complex. Or, if you want something with a similar power-leveling progression, but a darker tone, *That Time I Got Reincarnated as a Slime* is a solid choice. It starts a bit lighter, but gets pretty dark and strategic later on.\n\nIf you want something completely different, maybe something more character-driven like *A Place Further Than the Universe*? That's a slice-of-life anime, but it's beautifully done and very heartwarming.\n\n\nSo, tell me more about what aspects of *Solo Leveling* you enjoyed the most. That'll help me narrow down the recommendations! Were you a fan of the dungeon crawling, the character development, or just the sheer power displays?\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:20.688696Z",
"kind": "response"
},
{
"parts": [
{
"content": "The action was nice",
"timestamp": "2025-01-16T13:03:35.965734Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Right on! Action's always a good time. Okay, sticking to that, here are a few more tailored recommendations based on a love for awesome action sequences:\n\n* **Demon Slayer:** Absolutely stunning animation, incredibly creative fight choreography, and a compelling story. If *Solo Leveling* scratched that action itch, *Demon Slayer* will probably leave you breathless.\n\n* **My Hero Academia:** Another shonen classic, but with a fantastic cast of characters and increasingly epic battles. The animation quality gets better and better each season.\n\n* **Attack on Titan:** Darker and more mature than *Solo Leveling*, but the action sequences, especially the Titan battles, are legendary and incredibly well-done. (Just be warned, it gets *intense*.)\n\nAny of those sound appealing, or are you thinking something perhaps a bit less... \"mainstream\"? Let me know, and we can explore some more under-the-radar gems!\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:38.699198Z",
"kind": "response"
}
]
"""
summary_res = await summary_agent.run(
user_prompt="Summarize the conversation", result_type=str, message_history=message_history
)
print(summary_res.data) # NO BULLET POINTS
"""
We started by catching up, and you told me you recently finished *Solo Leveling*, finding the action good but the overall tone a bit childish. I asked for your preferences to give better recommendations. You mentioned enjoying the action scenes the most. I then gave you several recommendations catering to that preference, including *Jujutsu Kaisen*, *That Time I Got Reincarnated as a Slime*, *Demon Slayer*, *My Hero Academia*, and *Attack on Titan*, adjusting the suggestions based on your feedback and asking what kind of vibe you were after (mainstream or something more niche).
"""
summary_res.all_messages() # summary_agent's system prompt is not being used
"""
[
{
"parts": [
{
"content": "You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
"dynamic_ref": null,
"part_kind": "system-prompt"
},
{
"content": "The user's name is Hamza. And the last anime they watched is Solo Leveling.",
"dynamic_ref": "system_prompt",
"part_kind": "system-prompt"
},
{
"content": "Hey",
"timestamp": "2025-01-16T13:02:44.164903Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Yo Hamza! What's up, dude? Just finished re-watching a few episodes of *Mob Psycho 100*, myself. Been meaning to check out some other stuff, but man, that animation style is just *chef's kiss*. So, whatcha been up to? Last I heard you were diving into *Solo Leveling*. How'd you like it? I'm planning on watching it soon, heard the action sequences are insane.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:02:46.184918Z",
"kind": "response"
},
{
"parts": [
{
"content": "I liked it. a bit childish but the action was good. got any recs?",
"timestamp": "2025-01-16T13:03:18.104051Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Ah, yeah, I get the \"childish\" vibe sometimes with *Solo Leveling*'s power fantasy aspect. It's definitely more shonen than anything else. \n\nFor recs... depends what you're in the mood for! If you liked the action, but want something a bit more mature, maybe *Jujutsu Kaisen*? The animation is gorgeous, the fights are creative and brutal, and the characters are complex. Or, if you want something with a similar power-leveling progression, but a darker tone, *That Time I Got Reincarnated as a Slime* is a solid choice. It starts a bit lighter, but gets pretty dark and strategic later on.\n\nIf you want something completely different, maybe something more character-driven like *A Place Further Than the Universe*? That's a slice-of-life anime, but it's beautifully done and very heartwarming.\n\n\nSo, tell me more about what aspects of *Solo Leveling* you enjoyed the most. That'll help me narrow down the recommendations! Were you a fan of the dungeon crawling, the character development, or just the sheer power displays?\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:20.688696Z",
"kind": "response"
},
{
"parts": [
{
"content": "The action was nice",
"timestamp": "2025-01-16T13:03:35.965734Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Right on! Action's always a good time. Okay, sticking to that, here are a few more tailored recommendations based on a love for awesome action sequences:\n\n* **Demon Slayer:** Absolutely stunning animation, incredibly creative fight choreography, and a compelling story. If *Solo Leveling* scratched that action itch, *Demon Slayer* will probably leave you breathless.\n\n* **My Hero Academia:** Another shonen classic, but with a fantastic cast of characters and increasingly epic battles. The animation quality gets better and better each season.\n\n* **Attack on Titan:** Darker and more mature than *Solo Leveling*, but the action sequences, especially the Titan battles, are legendary and incredibly well-done. (Just be warned, it gets *intense*.)\n\nAny of those sound appealing, or are you thinking something perhaps a bit less... \"mainstream\"? Let me know, and we can explore some more under-the-radar gems!\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:38.699198Z",
"kind": "response"
},
{
"parts": [
{
"content": "Summarize the conversation",
"timestamp": "2025-01-16T13:06:40.215983Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "We started by catching up, and you told me you recently finished *Solo Leveling*, finding the action good but the overall tone a bit childish. I asked for your preferences to give better recommendations. You mentioned enjoying the action scenes the most. I then gave you several recommendations catering to that preference, including *Jujutsu Kaisen*, *That Time I Got Reincarnated as a Slime*, *Demon Slayer*, *My Hero Academia*, and *Attack on Titan*, adjusting the suggestions based on your feedback and asking what kind of vibe you were after (mainstream or something more niche).\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:06:41.966401Z",
"kind": "response"
}
]
"""
# My basic utils
def replace_system_parts(
message: _messages.ModelRequest, new_parts: list[_messages.ModelRequestPart] | None = None
) -> _messages.ModelRequest | None:
new_parts = new_parts or []
non_system_parts = [p for p in message.parts if not isinstance(p, _messages.SystemPromptPart)]
if final_parts := new_parts + non_system_parts:
return _messages.ModelRequest(parts=final_parts)
return None
async def get_messages_for_agent_tool(
agent: Agent[AgentDeps, ResultData],
user_prompt: str,
message_history: list[_messages.ModelMessage] | None,
model: models.KnownModelName | models.Model | None = None,
) -> list[_messages.ModelMessage]:
model_used = await agent._get_model(model)
ctx = RunContext(deps=None, model=model_used, usage=_usage.Usage(), prompt=user_prompt)
new_parts = await agent._sys_parts(ctx)
if message_history is None:
return [_messages.ModelRequest(parts=new_parts)]
messages = deepcopy(message_history)
if isinstance(messages[0], _messages.ModelRequest):
new_message = replace_system_parts(message=messages[0], new_parts=new_parts)
if new_message is not None:
messages[0] = new_message
return messages
summary_agent_messages = await get_messages_for_agent_tool(
agent=summary_agent, user_prompt="Summarize the conversation", message_history=message_history
)
summary_res_2 = await summary_agent.run(
user_prompt="Summarize the conversation", result_type=str, message_history=summary_agent_messages
)
print(summary_res_2.data) # WE GOT BULLET POINTS
"""
* **Initial Discussion:** We began by discussing *Mob Psycho 100* and transitioned to the user's experience with *Solo Leveling*, which they found enjoyable despite some perceived childishness.
* **Recommendations Request:** The user asked for anime recommendations.
* **Recommendation Phase 1:** Based on their enjoyment of *Solo Leveling*'s action, I initially suggested *Jujutsu Kaisen*, *That Time I Got Reincarnated as a Slime*, and *A Place Further Than the Universe* (as a contrasting, character-driven option).
* **Recommendation Phase 2:** Following clarification that the user prioritized action, I offered further recommendations: *Demon Slayer*, *My Hero Academia*, and *Attack on Titan*. The conversation ended with an offer to explore lesser-known shows if the user preferred.
"""
summary_res_2.all_messages() # summary_agent's system prompt is being used
"""
[
{
"parts": [
{
"content": "Your job is to summarize the conversation into bullet points when asked.",
"dynamic_ref": null,
"part_kind": "system-prompt"
},
{
"content": "Hey",
"timestamp": "2025-01-16T13:02:44.164903Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Yo Hamza! What's up, dude? Just finished re-watching a few episodes of *Mob Psycho 100*, myself. Been meaning to check out some other stuff, but man, that animation style is just *chef's kiss*. So, whatcha been up to? Last I heard you were diving into *Solo Leveling*. How'd you like it? I'm planning on watching it soon, heard the action sequences are insane.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:02:46.184918Z",
"kind": "response"
},
{
"parts": [
{
"content": "I liked it. a bit childish but the action was good. got any recs?",
"timestamp": "2025-01-16T13:03:18.104051Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Ah, yeah, I get the \"childish\" vibe sometimes with *Solo Leveling*'s power fantasy aspect. It's definitely more shonen than anything else. \n\nFor recs... depends what you're in the mood for! If you liked the action, but want something a bit more mature, maybe *Jujutsu Kaisen*? The animation is gorgeous, the fights are creative and brutal, and the characters are complex. Or, if you want something with a similar power-leveling progression, but a darker tone, *That Time I Got Reincarnated as a Slime* is a solid choice. It starts a bit lighter, but gets pretty dark and strategic later on.\n\nIf you want something completely different, maybe something more character-driven like *A Place Further Than the Universe*? That's a slice-of-life anime, but it's beautifully done and very heartwarming.\n\n\nSo, tell me more about what aspects of *Solo Leveling* you enjoyed the most. That'll help me narrow down the recommendations! Were you a fan of the dungeon crawling, the character development, or just the sheer power displays?\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:20.688696Z",
"kind": "response"
},
{
"parts": [
{
"content": "The action was nice",
"timestamp": "2025-01-16T13:03:35.965734Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Right on! Action's always a good time. Okay, sticking to that, here are a few more tailored recommendations based on a love for awesome action sequences:\n\n* **Demon Slayer:** Absolutely stunning animation, incredibly creative fight choreography, and a compelling story. If *Solo Leveling* scratched that action itch, *Demon Slayer* will probably leave you breathless.\n\n* **My Hero Academia:** Another shonen classic, but with a fantastic cast of characters and increasingly epic battles. The animation quality gets better and better each season.\n\n* **Attack on Titan:** Darker and more mature than *Solo Leveling*, but the action sequences, especially the Titan battles, are legendary and incredibly well-done. (Just be warned, it gets *intense*.)\n\nAny of those sound appealing, or are you thinking something perhaps a bit less... \"mainstream\"? Let me know, and we can explore some more under-the-radar gems!\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:03:38.699198Z",
"kind": "response"
},
{
"parts": [
{
"content": "Summarize the conversation",
"timestamp": "2025-01-16T13:28:43.086239Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "* **Initial Discussion:** We began by discussing *Mob Psycho 100* and transitioned to the user's experience with *Solo Leveling*, which they found enjoyable despite some perceived childishness.\n\n* **Recommendations Request:** The user asked for anime recommendations.\n\n* **Recommendation Phase 1:** Based on their enjoyment of *Solo Leveling*'s action, I initially suggested *Jujutsu Kaisen*, *That Time I Got Reincarnated as a Slime*, and *A Place Further Than the Universe* (as a contrasting, character-driven option).\n\n* **Recommendation Phase 2:** Following clarification that the user prioritized action, I offered further recommendations: *Demon Slayer*, *My Hero Academia*, and *Attack on Titan*. The conversation ended with an offer to explore lesser-known shows if the user preferred.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:28:45.160165Z",
"kind": "response"
}
]
""" |
@josead I copy pasted code from a notebook so that's why I'm using await on agent.run. |
Ah I should mention that even if the sumamry_agent has a dynamic system_prompt, it does not get used. |
from copy import deepcopy
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext, models
from pydantic_ai import messages as _messages
from pydantic_ai import usage as _usage
from pydantic_ai.result import ResultData
from pydantic_ai.tools import AgentDeps
@dataclass
class User:
name: str
anime_agent = Agent(
name="anime_fan",
model="google-gla:gemini-1.5-flash",
system_prompt="You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
deps_type=User,
result_type=str,
)
summary_agent = Agent(
name="summary_agent",
model="google-gla:gemini-1.5-flash",
system_prompt="Your job is to summarize the conversation into bullet points when asked.",
deps_type=User,
result_type=str,
)
@anime_agent.system_prompt(dynamic=True)
def system_prompt(ctx: RunContext[User]) -> str:
return f"The user's name is {ctx.deps.name}. And the last anime they watched is Solo Leveling."
@summary_agent.system_prompt(dynamic=True)
def system_prompt2(ctx: RunContext[User]) -> str:
return f"The user's name is {ctx.deps.name}. And the last anime they watched is Attack on Titan."
deps = User(name="Hamza")
message_history = None
user_prompt = "Hey"
while user_prompt.lower() not in ["q", "quit", "exit"]:
res = await anime_agent.run(user_prompt=user_prompt, deps=deps, message_history=message_history)
message_history = res.all_messages()
user_prompt = input(f"{res.data} (q to quit)> ")
summary_res = await summary_agent.run(
user_prompt="Summarize the conversation", result_type=str, message_history=message_history, deps=deps
)
print(summary_res.data) # NO BULLET POINTS
"""
We started by catching up, then Hamza mentioned finishing *Solo Leveling* and asking for recommendations of similar anime. I praised the action in *Solo Leveling* and suggested *Tower of God* and *The God of High School* as shows with similar action-packed fight scenes and power scaling. I also mentioned *That Time I Got Reincarnated as a Slime* as a possibility, but pointed out it had a slightly different tone and power progression. Finally, I asked Hamza to specify what aspects of *Solo Leveling* he enjoyed most to give him more tailored recommendations.
"""
summary_res.all_messages()
"""
[
{
"parts": [
{
"content": "You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
"dynamic_ref": null,
"part_kind": "system-prompt"
},
{
"content": "The user's name is Hamza. And the last anime they watched is Solo Leveling.",
"dynamic_ref": "system_prompt",
"part_kind": "system-prompt"
},
{
"content": "Hey",
"timestamp": "2025-01-16T13:44:55.982524Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Yo Hamza! What's up, dude? Just finished rewatching a few episodes of *Mob Psycho 100* myself. Still the best fight choreography I've seen in ages. So, what'd you think of *Solo Leveling*? I've been meaning to check it out – heard the animation style is pretty slick.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:44:57.666334Z",
"kind": "response"
},
{
"parts": [
{
"content": "action was nice. any similar recs?",
"timestamp": "2025-01-16T13:45:08.392019Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "Dude, *Solo Leveling*'s action is *insane*. That power scaling is next level. For similar action, you gotta check out *Tower of God*. It's got that same kinda \"overpowered protagonist slowly revealing his full strength\" vibe, plus the animation's pretty solid. Also, *The God of High School* is another good one, though the art style is a bit different – more vibrant and almost comedic in parts, but the fights are still epic. If you're digging the darker, more serious tone of *Solo Leveling*, then maybe *That Time I Got Reincarnated as a Slime* (though it's got a different power progression). What are you looking for specifically – more isekai, more straight-up action, something with a similar \"underdog becomes OP\" plotline?\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:45:10.632385Z",
"kind": "response"
},
{
"parts": [
{
"content": "Summarize the conversation",
"timestamp": "2025-01-16T13:45:12.096573Z",
"part_kind": "user-prompt"
}
],
"kind": "request"
},
{
"parts": [
{
"content": "We started by catching up, then Hamza mentioned finishing *Solo Leveling* and asking for recommendations of similar anime. I praised the action in *Solo Leveling* and suggested *Tower of God* and *The God of High School* as shows with similar action-packed fight scenes and power scaling. I also mentioned *That Time I Got Reincarnated as a Slime* as a possibility, but pointed out it had a slightly different tone and power progression. Finally, I asked Hamza to specify what aspects of *Solo Leveling* he enjoyed most to give him more tailored recommendations.\n",
"part_kind": "text"
}
],
"timestamp": "2025-01-16T13:45:13.639345Z",
"kind": "response"
}
]
"""
|
Notice how the dynamic_ref is still "system_prompt" and not "system_prompt2" |
I think this is not how you are supposed to use history_messages, but I see what you are trying to achieve. import random
from copy import deepcopy
from dataclasses import dataclass
from pydantic_ai import Agent, RunContext, models
from pydantic_ai import messages as _messages
from pydantic_ai import usage as _usage
from pydantic_ai.result import ResultData
from pydantic_ai.tools import AgentDeps
@dataclass
class User:
name: str
anime_agent = Agent(
name="anime_fan",
model="google-gla:gemini-1.5-flash",
system_prompt="You are an anime fan. The user will also be an anime fan. Just bros chilling talking about anime.",
deps_type=User,
result_type=str,
)
# Name is not supposed to change, so we don't need to reevaluate.
@anime_agent.system_prompt()
def system_prompt(ctx: RunContext[User]) -> str:
return f"The user's name is {ctx.deps.name}. And the last anime they watched is Solo Leveling."
# But, adding a system prompt that enables reevaluation we can do things like
@anime_agent.system_prompt(dynamic=True)
def system_prompt(ctx: RunContext[User]) -> str:
charisma = random.randint(0,10) # suppose we get this from an API (external environment)
shyness = random.randint(0,10) # this needs to be evaluated on every run
curiosity = random.randint(0,10)
neuroticism = random.randint(0,10)
return f"""You are allowed to change radically the tone and voice on conversation based on this attributes, 0 means none, and 10 means a lot:
[charisma: {charisma}]
[shyness: {shyness}]
[curiosity: {curiosity]
[neuroticism: {neuroticism}]
"""
deps = User(name="Hamza")
message_history = None
user_prompt = "Hey"
while user_prompt.lower() not in ["q", "quit", "exit"]:
res = await anime_agent.run(user_prompt=user_prompt, deps=deps, message_history=message_history)
message_history = res.all_messages()
user_prompt = input(f"{res.data} (q to quit)> ")
# ----------- Having this code, you should be able to reevaluate the charisma, etc on each run with the usage of message_history. Without dynamic, and adding message_history, you would end up with a fixed charisma, shyness etc.. based on the first run (the one without message_history). Now, to your point of summarizing, I'd suggest you to lower down to a simple input, and not share the message_history. AFAIU the simple usecase of summarizing, does not require you to send the message_histrory, you should add the history of conversation as just a simple message of the user like this:
This is a simple case of a chain or single purposed agent, you can use Agent because it is simple, but I think you are overusing the Agents here. @samuelcolvin correct me if I'm wrong here with the Agent's usage intention. |
Yeah this is just an example. |
Adding this functionality of "anchoring" would be interesting perhaps we can submit an issue and implementing this would be something very similar to what we did with dynamic, but dynamic won't be able to help here. |
Adds optional parameter "dynamic" to decorator "system_prompt".
Implements the reevaluation of system prompts.
Allows new prompts to be added only if they are dynamic.
Hello Pydantic Team,
In response to this issue I've created #531
Let me know if this is a good approach for the enhancement.
CC: @samuelcolvin @sydney-runkle
Regards!