adachsoft / ai-agent
Stateless AI Agent Orchestrator Library for tool-calling chats.
Installs: 7
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/ai-agent
Requires
- php: ^8.3
- adachsoft/ai-integration: v0.3.0
- adachsoft/ai-tool-call: ^1.0.0
- adachsoft/collection: ^3.0
- adachsoft/console-io: ^0.1.0
Requires (Dev)
- adachsoft/changelog-linter: ^0.3.0
- adachsoft/php-code-style: ^0.1.0
- friendsofphp/php-cs-fixer: ^3.89
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.4
- twig/twig: ^3.0
- vlucas/phpdotenv: ^5.6
README
Stateless AI Agent Orchestrator Library for tool-calling chats.
- PHP: >= 8.3
- Namespace: AdachSoft\AiAgent\
Overview
This library orchestrates a single tool-calling chat turn in stateless mode. The client passes a full, pre-trimmed conversation and the current user turn; the library appends the user message, runs orchestration using external PublicApi integrations, and returns the updated full conversation plus token usage.
See detailed docs:
- docs/PROJECT_OVERVIEW_AI_AGENT.md (EN)
- docs/PRZEGLAD_PROJEKTU_AI_AGENT.md (PL)
Installation
Install via Composer:
composer require adachsoft/ai-agent
This library relies on external PublicApi facades for chat and tools (installed by you):
- adachsoft/ai-integration (ToolCallingChatFacade)
- adachsoft/ai-tool-call (AiToolCallFacade)
Quick Start (Stateless)
1) Create ports (PublicApi facades from external libs):
- ToolCallingChatFacade (e.g. Deepseek/OpenAI provider via their builders)
- AiToolCallFacade (provides tool catalog and execution)
2) Build the AiAgent facade via builder with config and policies. 3) Prepare conversation (ChatMessageDtoCollection) and current user turn (AskUserTurnDto). 4) Call ask($conversation, $turn) and persist returned fullConversation on your side.
Minimal Example
use AdachSoft\AiAgent\PublicApi\Builder\AiAgentBuilder;
use AdachSoft\AiAgent\PublicApi\Dto\AgentConfigDto;
use AdachSoft\AiAgent\PublicApi\Dto\AskUserTurnDto;
use AdachSoft\AiAgent\PublicApi\Dto\Collection\ChatMessageDtoCollection;
use AdachSoft\AiAgent\PublicApi\Dto\Collection\ToolIdCollection;
use AdachSoft\AiAgent\PublicApi\Dto\PoliciesConfigDto;
use AdachSoft\AiAgent\PublicApi\Dto\PortsConfigDto;
use AdachSoft\AiAgent\PublicApi\Vo\ToolId;
// 1) Create external facades (using your provider credentials)
$toolCalling = /* ToolCallingChatFacadeInterface from adachsoft/ai-integration */;
$aiTools = /* AiToolCallFacadeInterface from adachsoft/ai-tool-call */;
$ports = new PortsConfigDto(
toolCallingChatFacade: $toolCalling,
aiToolCallFacade: $aiTools,
);
// 2) Build AiAgent facade
$facade = (new AiAgentBuilder())
->withPorts($ports)
->withAgentConfig(new AgentConfigDto(
name: 'MyAgent',
description: 'Demo agent',
providerId: 'deepseek',
modelId: 'deepseek-chat',
temperature: 0.2,
maxTokens: 1024,
timeoutSeconds: 60,
tools: new ToolIdCollection([new ToolId('current_datetime')]),
))
->withPolicies(new PoliciesConfigDto(
maxSteps: 6,
maxToolCallsPerTurn: 2,
maxDurationSeconds: 60,
))
->build();
// 3) Prepare conversation and user turn
$conversation = new ChatMessageDtoCollection([]); // pre-trimmed history on caller side
$turn = new AskUserTurnDto('What time is it in Warsaw? Reply as HH:MM.');
// 4) Execute one stateless turn
$response = $facade->ask($conversation, $turn);
// 5) Persist the updated history and inspect final answer
$conversation = $response->fullConversation; // store externally
$finalText = $response->finalText; // assistant final message
Examples
Runnable examples are provided in the examples/ directory. Each example has its own folder with run.php and README.md.
- Quickstart (single turn):
- Run from repo: php examples/agent-publicapi-quickstart/run.php "Your prompt"
- Run from installed package: php vendor/adachsoft/ai-agent/examples/agent-publicapi-quickstart/run.php "Your prompt"
- Interactive chat:
- Run from repo: php examples/agent-publicapi-chat/run.php
- Run from installed package: php vendor/adachsoft/ai-agent/examples/agent-publicapi-chat/run.php
Note: bin/ scripts are thin wrappers that redirect to example entrypoints.
Environment variables:
- DEEPSEEK_API_KEY
Versioning
- This library is versioned exclusively via Git tags (no version in composer.json).
- Current version: v0.3.3 (see changelog.json for details).
API Highlights
- AiAgentFacadeInterface::ask(ChatMessageDtoCollection, AskUserTurnDto): AskResponseDto
- AskResponseDto contains:
- fullConversation (ChatMessageDtoCollection)
- tokensUsed (TokensUsageDto: promptTokens, completionTokens, totalTokens)
- status (enum: e.g. Completed)
- finalText (string|null)
SPI: Chat turn resolution
When the underlying provider returns an ambiguous chat turn (both finalText and at least one toolCall), the library uses a strategy to decide how to interpret the result.
Built-in strategies
You can select one of the built-in domain strategies via AiAgentBuilder::withChatTurnResolutionMode(string $mode):
prefer_final_text– usefinalText, ignoretoolCalls.prefer_tool_calls– ignorefinalText, continue with tool execution.error_on_conflict– treat the situation as a domain error.
Example:
$facade = (new AiAgentBuilder())
->withPorts($ports)
->withAgentConfig($config)
->withPolicies($policies)
->withChatTurnResolutionMode('prefer_final_text')
->build();
Custom SPI strategy
For full control you can implement the SPI:
- Interface:
AdachSoft\AiAgent\Spi\Conversation\ChatTurnResolutionStrategyInterface - Method:
public function decide(ChatTurnResult $turn): string - Expected return values:
'use_final_text''use_tool_calls''error'
Register your SPI strategy via the builder:
use AdachSoft\AiAgent\Spi\Conversation\ChatTurnResolutionStrategyInterface as SpiStrategy;
final class MyResolutionStrategy implements SpiStrategy
{
public function decide(ChatTurnResult $turn): string
{
// your custom policy here
return 'use_tool_calls';
}
}
$facade = (new AiAgentBuilder())
->withPorts($ports)
->withAgentConfig($config)
->withPolicies($policies)
->withSpiChatTurnResolutionStrategy(new MyResolutionStrategy())
->build();
Internally the library adapts the SPI decision to a domain enum and uses it inside the orchestrator; the public API of AiAgentFacadeInterface remains unchanged.
Design Notes
- Stateless: the library does not store history or perform trimming. Provide a pre-trimmed conversation on each call.
- Ports only: integrates via PublicApi facades (no HTTP details in this lib).
- Collections: uses adachsoft/collection; no public arrays in DTOs.
Changelog
See CHANGELOG.md. Source of truth is changelog.json (generated via adachsoft/changelog-linter).
License
MIT (see composer.json).