Skip to content

Commit 5f78680

Browse files
committed
[AI Bundle] Restructure system_prompt configuration to nest include_tools
1 parent ca49b53 commit 5f78680

File tree

5 files changed

+277
-18
lines changed

5 files changed

+277
-18
lines changed

demo/config/packages/ai.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ ai:
3535
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
3636
options:
3737
temperature: 0.5
38-
system_prompt: 'Please answer the users question based on Wikipedia and provide a link to the article.'
39-
include_tools: true
38+
system_prompt:
39+
prompt: 'Please answer the users question based on Wikipedia and provide a link to the article.'
40+
include_tools: true
4041
tools:
4142
- 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia'
4243
audio:

src/ai-bundle/config/options.php

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,39 @@
130130
->end()
131131
->end()
132132
->booleanNode('structured_output')->defaultTrue()->end()
133-
->scalarNode('system_prompt')
133+
->arrayNode('system_prompt')
134+
->info('The system prompt configuration')
135+
->beforeNormalization()
136+
->ifString()
137+
->then(function (string $v) {
138+
return ['prompt' => $v];
139+
})
140+
->end()
141+
->beforeNormalization()
142+
->ifArray()
143+
->then(function (array $v) {
144+
if (!isset($v['prompt']) && !isset($v['include_tools'])) {
145+
throw new \InvalidArgumentException('Either "prompt" must be configured for system_prompt.');
146+
}
147+
148+
return $v;
149+
})
150+
->end()
134151
->validate()
135-
->ifTrue(fn ($v) => null !== $v && '' === trim($v))
136-
->thenInvalid('The default system prompt must not be an empty string')
152+
->ifTrue(function ($v) {
153+
return \is_array($v) && '' === trim($v['prompt'] ?? '');
154+
})
155+
->thenInvalid('The "prompt" cannot be empty.')
156+
->end()
157+
->children()
158+
->scalarNode('prompt')
159+
->info('The system prompt text')
160+
->end()
161+
->booleanNode('include_tools')
162+
->info('Include tool definitions at the end of the system prompt')
163+
->defaultFalse()
164+
->end()
137165
->end()
138-
->defaultNull()
139-
->info('The default system prompt of the agent')
140-
->end()
141-
->booleanNode('include_tools')
142-
->info('Include tool definitions at the end of the system prompt')
143-
->defaultFalse()
144166
->end()
145167
->arrayNode('tools')
146168
->addDefaultsIfNotSet()

src/ai-bundle/doc/index.rst

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ Configuration
7171
model:
7272
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
7373
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
74-
system_prompt: 'You are a helpful assistant that can answer questions.' # The default system prompt of the agent
75-
include_tools: true # Include tool definitions at the end of the system prompt
74+
system_prompt: # The system prompt configuration
75+
prompt: 'You are a helpful assistant that can answer questions.' # The prompt text
76+
include_tools: true # Include tool definitions at the end of the system prompt
7677
tools:
7778
# Referencing a service with #[AsTool] attribute
7879
- 'Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch'
@@ -144,6 +145,43 @@ Configuration
144145
vectorizer: 'ai.vectorizer.mistral_embeddings'
145146
store: 'ai.store.memory.research'
146147
148+
System Prompt Configuration
149+
---------------------------
150+
151+
For basic usage, specify the system prompt as a simple string:
152+
153+
.. code-block:: yaml
154+
155+
ai:
156+
agent:
157+
my_agent:
158+
model:
159+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
160+
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
161+
system_prompt: 'You are a helpful assistant.'
162+
163+
**Advanced Configuration**
164+
165+
For more control, such as including tool definitions in the system prompt, use the array format:
166+
167+
.. code-block:: yaml
168+
169+
ai:
170+
agent:
171+
my_agent:
172+
model:
173+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
174+
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
175+
system_prompt:
176+
prompt: 'You are a helpful assistant that can answer questions.'
177+
include_tools: true # Include tool definitions at the end of the system prompt
178+
179+
The array format supports these options:
180+
181+
* ``prompt`` (string, required): The system prompt text that will be sent to the AI model
182+
* ``include_tools`` (boolean, optional): When set to ``true``, tool definitions will be appended to the system prompt
183+
184+
147185
Usage
148186
-----
149187

src/ai-bundle/src/AiBundle.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,11 +604,13 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
604604
}
605605

606606
// SYSTEM PROMPT
607-
if (\is_string($config['system_prompt'])) {
607+
if (isset($config['system_prompt'])) {
608+
$includeTools = isset($config['system_prompt']['include_tools']) && $config['system_prompt']['include_tools'];
609+
608610
$systemPromptInputProcessorDefinition = (new Definition(SystemPromptInputProcessor::class))
609611
->setArguments([
610-
$config['system_prompt'],
611-
$config['include_tools'] ? new Reference('ai.toolbox.'.$name) : null,
612+
$config['system_prompt']['prompt'],
613+
$includeTools ? new Reference('ai.toolbox.'.$name) : null,
612614
new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
613615
])
614616
->addTag('ai.agent.input_processor', ['agent' => $agentId, 'priority' => -30]);

src/ai-bundle/tests/DependencyInjection/AiBundleTest.php

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,200 @@ public function testPerplexityPlatformConfiguration()
648648
$this->assertSame('ai.platform.contract.perplexity', (string) $arguments[2]);
649649
}
650650

651+
#[TestDox('System prompt with array structure works correctly')]
652+
public function testSystemPromptWithArrayStructure()
653+
{
654+
$container = $this->buildContainer([
655+
'ai' => [
656+
'agent' => [
657+
'test_agent' => [
658+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
659+
'system_prompt' => [
660+
'prompt' => 'You are a helpful assistant.',
661+
],
662+
'tools' => [
663+
['service' => 'some_tool', 'description' => 'Test tool'],
664+
],
665+
],
666+
],
667+
],
668+
]);
669+
670+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
671+
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
672+
$arguments = $definition->getArguments();
673+
674+
$this->assertSame('You are a helpful assistant.', $arguments[0]);
675+
$this->assertNull($arguments[1]); // include_tools is false, so null reference
676+
}
677+
678+
#[TestDox('System prompt with include_tools enabled works correctly')]
679+
public function testSystemPromptWithIncludeToolsEnabled()
680+
{
681+
$container = $this->buildContainer([
682+
'ai' => [
683+
'agent' => [
684+
'test_agent' => [
685+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
686+
'system_prompt' => [
687+
'prompt' => 'You are a helpful assistant.',
688+
'include_tools' => true,
689+
],
690+
'tools' => [
691+
['service' => 'some_tool', 'description' => 'Test tool'],
692+
],
693+
],
694+
],
695+
],
696+
]);
697+
698+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
699+
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
700+
$arguments = $definition->getArguments();
701+
702+
$this->assertSame('You are a helpful assistant.', $arguments[0]);
703+
$this->assertInstanceOf(Reference::class, $arguments[1]);
704+
$this->assertSame('ai.toolbox.test_agent', (string) $arguments[1]);
705+
}
706+
707+
#[TestDox('System prompt with only prompt key defaults include_tools to false')]
708+
public function testSystemPromptWithOnlyPromptKey()
709+
{
710+
$container = $this->buildContainer([
711+
'ai' => [
712+
'agent' => [
713+
'test_agent' => [
714+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
715+
'system_prompt' => [
716+
'prompt' => 'You are a helpful assistant.',
717+
],
718+
'tools' => [
719+
['service' => 'some_tool', 'description' => 'Test tool'],
720+
],
721+
],
722+
],
723+
],
724+
]);
725+
726+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
727+
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
728+
$arguments = $definition->getArguments();
729+
730+
$this->assertSame('You are a helpful assistant.', $arguments[0]);
731+
$this->assertNull($arguments[1]); // include_tools defaults to false
732+
}
733+
734+
#[TestDox('Agent without system prompt does not create processor')]
735+
public function testAgentWithoutSystemPrompt()
736+
{
737+
$container = $this->buildContainer([
738+
'ai' => [
739+
'agent' => [
740+
'test_agent' => [
741+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
742+
],
743+
],
744+
],
745+
]);
746+
747+
$this->assertFalse($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
748+
}
749+
750+
#[TestDox('Valid system prompt creates processor correctly')]
751+
public function testValidSystemPromptCreatesProcessor()
752+
{
753+
// This test verifies that valid system prompts work correctly with new structure
754+
$container = $this->buildContainer([
755+
'ai' => [
756+
'agent' => [
757+
'test_agent' => [
758+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
759+
'system_prompt' => [
760+
'prompt' => 'Valid prompt',
761+
'include_tools' => true,
762+
],
763+
'tools' => [
764+
['service' => 'some_tool', 'description' => 'Test tool'],
765+
],
766+
],
767+
],
768+
],
769+
]);
770+
771+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
772+
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
773+
$arguments = $definition->getArguments();
774+
775+
$this->assertSame('Valid prompt', $arguments[0]);
776+
$this->assertInstanceOf(Reference::class, $arguments[1]);
777+
$this->assertSame('ai.toolbox.test_agent', (string) $arguments[1]);
778+
}
779+
780+
#[TestDox('Empty prompt in array structure throws configuration exception')]
781+
public function testEmptyPromptInArrayThrowsException()
782+
{
783+
$this->expectException(InvalidConfigurationException::class);
784+
$this->expectExceptionMessage('The "prompt" cannot be empty.');
785+
786+
$this->buildContainer([
787+
'ai' => [
788+
'agent' => [
789+
'test_agent' => [
790+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
791+
'system_prompt' => [
792+
'prompt' => '',
793+
],
794+
],
795+
],
796+
],
797+
]);
798+
}
799+
800+
#[TestDox('System prompt array without prompt key throws configuration exception')]
801+
public function testSystemPromptArrayWithoutPromptKeyThrowsException()
802+
{
803+
$this->expectException(InvalidConfigurationException::class);
804+
$this->expectExceptionMessage('The "prompt" cannot be empty.');
805+
806+
$this->buildContainer([
807+
'ai' => [
808+
'agent' => [
809+
'test_agent' => [
810+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
811+
'system_prompt' => [
812+
'include_tools' => true,
813+
],
814+
],
815+
],
816+
],
817+
]);
818+
}
819+
820+
#[TestDox('System prompt with string format works correctly')]
821+
public function testSystemPromptWithStringFormat()
822+
{
823+
$container = $this->buildContainer([
824+
'ai' => [
825+
'agent' => [
826+
'test_agent' => [
827+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
828+
'system_prompt' => 'You are a helpful assistant.',
829+
'tools' => [
830+
['service' => 'some_tool', 'description' => 'Test tool'],
831+
],
832+
],
833+
],
834+
],
835+
]);
836+
837+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.system_prompt_processor'));
838+
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
839+
$arguments = $definition->getArguments();
840+
841+
$this->assertSame('You are a helpful assistant.', $arguments[0]);
842+
$this->assertNull($arguments[1]); // include_tools not enabled with string format
843+
}
844+
651845
public function testVectorizerConfiguration()
652846
{
653847
$container = $this->buildContainer([
@@ -1076,8 +1270,10 @@ private function getFullConfig(): array
10761270
],
10771271
'structured_output' => false,
10781272
'track_token_usage' => true,
1079-
'system_prompt' => 'You are a helpful assistant.',
1080-
'include_tools' => true,
1273+
'system_prompt' => [
1274+
'prompt' => 'You are a helpful assistant.',
1275+
'include_tools' => true,
1276+
],
10811277
'tools' => [
10821278
'enabled' => true,
10831279
'services' => [

0 commit comments

Comments
 (0)