Skip to content

Commit a364388

Browse files
KPJoshicopybara-github
authored andcommitted
feat: Add custom instructions support to LlmBackedUserSimulator
Details: - Allows users to provide custom instructions for the LLM-backed user simulator via the `custom_instructions` field in `LlmBackedUserSimulatorConfig`. - The custom instructions must include placeholders for the stop signal, conversation plan, and conversation history. A pydantic validator ensures these placeholders are present. - If no custom instructions are provided, the current default template is used. Co-authored-by: Keyur Joshi <keyurj@google.com> PiperOrigin-RevId: 850471448
1 parent 0918b64 commit a364388

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

src/google/adk/evaluation/simulation/llm_backed_user_simulator.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from google.genai import types as genai_types
2222
from pydantic import Field
23+
from pydantic import field_validator
2324
from typing_extensions import override
2425

2526
from ...events.event import Event
@@ -40,7 +41,7 @@
4041
_AUTHOR_USER = "user"
4142
_STOP_SIGNAL = "</finished>"
4243

43-
_USER_AGENT_INSTRUCTIONS_TEMPLATE = """You are a Simulated User designed to test an AI Agent.
44+
_DEFAULT_USER_AGENT_INSTRUCTIONS = """You are a Simulated User designed to test an AI Agent.
4445
4546
Your single most important job is to react logically to the Agent's last message.
4647
The Conversation Plan is your canonical grounding, not a script; your response MUST be dictated by what the Agent just said.
@@ -126,6 +127,38 @@ class LlmBackedUserSimulatorConfig(BaseUserSimulatorConfig):
126127
(Not recommended) If you don't want a limit, you can set the value to -1.""",
127128
)
128129

130+
custom_instructions: Optional[str] = Field(
131+
default=None,
132+
description="""Custom instructions for the LlmBackedUserSimulator. The
133+
instructions must contain the following formatting placeholders:
134+
* {stop_signal} : text to be generated when the user simulator decides that the
135+
conversation is over.
136+
* {conversation_plan} : the overall plan for the conversation that the user
137+
simulator must follow.
138+
* {conversation_history} : the conversation between the user and the agent so
139+
far.""",
140+
)
141+
142+
@field_validator("custom_instructions")
143+
@classmethod
144+
def validate_custom_instructions(cls, value: Optional[str]) -> Optional[str]:
145+
if value is None:
146+
return value
147+
if not all(
148+
placeholder in value
149+
for placeholder in [
150+
"{stop_signal}",
151+
"{conversation_plan}",
152+
"{conversation_history}",
153+
]
154+
):
155+
raise ValueError(
156+
"custom_instructions must contain each of the following formatting"
157+
" placeholders:"
158+
" {stop_signal}, {conversation_plan}, {conversation_history}"
159+
)
160+
return value
161+
129162

130163
@experimental
131164
class LlmBackedUserSimulator(UserSimulator):
@@ -147,6 +180,11 @@ def __init__(
147180
llm_registry = LLMRegistry()
148181
llm_class = llm_registry.resolve(self._config.model)
149182
self._llm = llm_class(model=self._config.model)
183+
self._instructions = (
184+
self._config.custom_instructions
185+
if self._config.custom_instructions
186+
else _DEFAULT_USER_AGENT_INSTRUCTIONS
187+
)
150188

151189
@classmethod
152190
def _summarize_conversation(
@@ -183,7 +221,7 @@ async def _get_llm_response(
183221
# first invocation - send the static starting prompt
184222
return self._conversation_scenario.starting_prompt
185223

186-
user_agent_instructions = _USER_AGENT_INSTRUCTIONS_TEMPLATE.format(
224+
user_agent_instructions = self._instructions.format(
187225
stop_signal=_STOP_SIGNAL,
188226
conversation_plan=self._conversation_scenario.conversation_plan,
189227
conversation_history=rewritten_dialogue,

tests/unittests/evaluation/simulation/test_llm_backed_user_simulator.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from google.adk.evaluation.simulation.user_simulator import Status
2121
from google.adk.events.event import Event
2222
from google.genai import types
23+
from pydantic import ValidationError
2324
import pytest
2425

2526
_INPUT_EVENTS = [
@@ -88,6 +89,20 @@
8889
helpful_assistant: Sure, what is your departure date and destination?"""
8990

9091

92+
def test_llm_backed_user_simulator_config_validation():
93+
"""Tests for LlmBackedUserSimulatorConfig."""
94+
config = LlmBackedUserSimulatorConfig(custom_instructions=None)
95+
assert config.custom_instructions is None
96+
valid_instructions = (
97+
"{stop_signal} {conversation_plan} {conversation_history}"
98+
)
99+
config = LlmBackedUserSimulatorConfig(custom_instructions=valid_instructions)
100+
assert config.custom_instructions == valid_instructions
101+
invalid_instructions = "Instructions with missing formatting placeholders"
102+
with pytest.raises(ValidationError):
103+
LlmBackedUserSimulatorConfig(custom_instructions=invalid_instructions)
104+
105+
91106
class TestHelperMethods:
92107
"""Test cases for LlmBackedUserSimulator helper methods."""
93108

0 commit comments

Comments
 (0)