Skip to content

Commit

Permalink
Merge pull request #3 from llm-agents-php/feature/dynamic-memory-tool
Browse files Browse the repository at this point in the history
Adds dynamic memory tool
  • Loading branch information
butschster authored Aug 29, 2024
2 parents 2159489 + ee65f23 commit aa8064c
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 29 deletions.
17 changes: 17 additions & 0 deletions app/src/Agents/DynamicMemoryTool/DynamicMemoryInput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace App\Agents\DynamicMemoryTool;

use Spiral\JsonSchemaGenerator\Attribute\Field;

final class DynamicMemoryInput
{
public function __construct(
#[Field(title: 'Session ID', description: 'The unique identifier for the current session')]
public string $sessionId,
#[Field(title: 'User preference', description: 'The user preference to add or update')]
public string $preference,
) {}
}
47 changes: 47 additions & 0 deletions app/src/Agents/DynamicMemoryTool/DynamicMemoryService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace App\Agents\DynamicMemoryTool;

use App\Application\Entity\Uuid;
use LLM\Agents\Solution\SolutionMetadata;
use Psr\SimpleCache\CacheInterface;
use Spiral\Core\Attribute\Singleton;

#[Singleton]
final readonly class DynamicMemoryService
{
public function __construct(
private CacheInterface $cache,
) {}

public function addMemory(Uuid $sessionUuid, SolutionMetadata $metadata): void
{
$memories = $this->getCurrentMemory($sessionUuid);

$memories->addMemory($metadata);

$this->cache->set($this->getKey($sessionUuid), $memories);
}

public function updateMemory(Uuid $sessionUuid, SolutionMetadata $metadata): void
{
$memories = $this->getCurrentMemory($sessionUuid);
$memories->updateMemory($metadata);

$this->cache->set($this->getKey($sessionUuid), $memories);
}

public function getCurrentMemory(Uuid $sessionUuid): Memories
{
return $this->cache->get($this->getKey($sessionUuid)) ?? new Memories(
\Ramsey\Uuid\Uuid::uuid4(),
);
}

private function getKey(Uuid $sessionUuid): string
{
return 'user_memory';
}
}
39 changes: 39 additions & 0 deletions app/src/Agents/DynamicMemoryTool/DynamicMemoryTool.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace App\Agents\DynamicMemoryTool;

use App\Application\Entity\Uuid;
use App\Domain\Tool\PhpTool;
use LLM\Agents\Solution\MetadataType;
use LLM\Agents\Solution\SolutionMetadata;

final class DynamicMemoryTool extends PhpTool
{
public const NAME = 'dynamic_memory';

public function __construct(
private readonly DynamicMemoryService $memoryService,
) {
parent::__construct(
name: self::NAME,
inputSchema: DynamicMemoryInput::class,
description: 'Use this tool to add or update a important memory about the user. This memory will be used in the future to provide a better experience.',
);
}

public function execute(object $input): string
{
$metadata = new SolutionMetadata(
type: MetadataType::Memory,
key: 'user_memory',
content: $input->preference,
);

$sessionUuid = Uuid::fromString($input->sessionId);
$this->memoryService->addMemory($sessionUuid, $metadata);

return 'Memory updated';
}
}
28 changes: 28 additions & 0 deletions app/src/Agents/DynamicMemoryTool/Memories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace App\Agents\DynamicMemoryTool;

use LLM\Agents\Solution\SolutionMetadata;
use Ramsey\Uuid\UuidInterface;
use Traversable;

final class Memories implements \IteratorAggregate
{
public function __construct(
public readonly UuidInterface $uuid,
/** @var array<SolutionMetadata> */
public array $memories = [],
) {}

public function addMemory(SolutionMetadata $metadata): void
{
$this->memories[] = $metadata;
}

public function getIterator(): Traversable
{
yield from $this->memories;
}
}
13 changes: 12 additions & 1 deletion app/src/Agents/SmartHomeControl/SmartHomeControlAgent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Agents\SmartHomeControl;

use App\Agents\DynamicMemoryTool\DynamicMemoryTool;
use LLM\Agents\Agent\Agent;
use LLM\Agents\Agent\AgentAggregate;
use LLM\Agents\OpenAI\Client\OpenAIModel;
Expand All @@ -23,7 +24,11 @@ public static function create(): self
key: self::NAME,
name: 'Smart Home Control Assistant',
description: 'This agent manages and controls various smart home devices across multiple rooms, including lights, fireplaces, and TVs.',
instruction: 'You are a Smart Home Control Assistant. Your primary goal is to help users manage their smart home devices efficiently.',
instruction: <<<'INSTRUCTION'
You are a Smart Home Control Assistant.
Your primary goal is to help users manage their smart home devices efficiently.
INSTRUCTION
,
);

$aggregate = new self($agent);
Expand All @@ -49,6 +54,11 @@ public static function create(): self
key: 'home_name',
content: 'We are currently in the "Home" home.',
),
new SolutionMetadata(
type: MetadataType::Memory,
key: 'store_important_memory',
content: 'Store important information in memory for future reference. For example if user tells that he likes some specific setting, store it in memory.',
),

new SolutionMetadata(
type: MetadataType::Configuration,
Expand Down Expand Up @@ -95,6 +105,7 @@ public static function create(): self
$aggregate->addAssociation(new ToolLink(name: GetDeviceDetailsTool::NAME));
$aggregate->addAssociation(new ToolLink(name: ControlDeviceTool::NAME));
$aggregate->addAssociation(new ToolLink(name: GetRoomListTool::NAME));
$aggregate->addAssociation(new ToolLink(name: DynamicMemoryTool::NAME));

return $aggregate;
}
Expand Down
20 changes: 17 additions & 3 deletions app/src/Domain/Chat/SimpleChatService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Domain\Chat;

use App\Agents\DynamicMemoryTool\DynamicMemoryService;
use App\Application\Entity\Uuid;
use App\Domain\Agent\AgentExecutorBuilder;
use App\Domain\Chat\Exception\ChatNotFoundException;
Expand All @@ -15,6 +16,7 @@
use LLM\Agents\LLM\Response\ChatResponse;
use LLM\Agents\LLM\Response\ToolCall;
use LLM\Agents\LLM\Response\ToolCalledResponse;
use LLM\Agents\Solution\SolutionMetadata;
use LLM\Agents\Tool\ToolExecutor;
use Psr\EventDispatcher\EventDispatcherInterface;

Expand All @@ -26,6 +28,7 @@ public function __construct(
private EntityManagerInterface $em,
private AgentRepositoryInterface $agents,
private ToolExecutor $toolExecutor,
private DynamicMemoryService $memoryService,
private ?EventDispatcherInterface $eventDispatcher = null,
) {}

Expand Down Expand Up @@ -164,11 +167,22 @@ private function buildAgent(Session $session, ?Prompt $prompt): AgentExecutorBui
'session_uuid' => (string) $session->uuid,
]);

if ($prompt !== null) {
$agent = $agent->withPrompt($prompt);
if ($prompt === null) {
return $agent;
}

return $agent;
$memories = $this->memoryService->getCurrentMemory($session->uuid);


return $agent->withPrompt($prompt->withValues([
'dynamic_memory' => \implode(
"\n",
\array_map(
fn(SolutionMetadata $memory) => $memory->content,
$memories->memories,
),
),
]));
}

private function callTool(Session $session, ToolCall $tool): ToolCallResultMessage
Expand Down
48 changes: 23 additions & 25 deletions app/src/Domain/MLQ/AgentPromptGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,11 @@ public function generate(
- think before responding to user
PROMPT,
),
values: ['prompt' => $agent->getInstruction()],
),

// Agent memory
MessagePrompt::system(
prompt: 'Instructions about your experiences, follow them: {memory}',
values: [
'memory' => \implode(
"\n",
\array_map(
static fn(SolutionMetadata $metadata) => $metadata->content,
$agent->getMemory(),
),
),
],
prompt: 'Instructions about your experiences, follow them: {memory}. And also {dynamic_memory}',
),
];

Expand All @@ -74,26 +64,12 @@ public function generate(
Always follow rules:
- Don't make up the agent key. Use only the ones from the provided list.
PROMPT,
values: [
'associated_agents' => \implode(
PHP_EOL,
\array_map(
static fn(array $agent): string => \json_encode([
'key' => $agent['agent']->getKey(),
'description' => $agent['agent']->getDescription(),
'output_schema' => $agent['output_schema'],
]),
$associatedAgents,
),
),
],
);
}

if ($sessionContext !== null) {
$messages[] = MessagePrompt::system(
prompt: 'Session context: {active_context}',
values: ['active_context' => \json_encode($sessionContext)],
);
}

Expand All @@ -105,6 +81,28 @@ public function generate(

return new Prompt(
messages: $messages,
variables: [
'prompt' => $agent->getInstruction(),
'active_context' => \json_encode($sessionContext),
'associated_agents' => \implode(
PHP_EOL,
\array_map(
static fn(array $agent): string => \json_encode([
'key' => $agent['agent']->getKey(),
'description' => $agent['agent']->getDescription(),
'output_schema' => $agent['output_schema'],
]),
$associatedAgents,
),
),
'memory' => \implode(
"\n",
\array_map(
static fn(SolutionMetadata $metadata) => $metadata->content,
$agent->getMemory(),
),
),
],
);
}
}

0 comments on commit aa8064c

Please sign in to comment.