adachsoft/ai-agent

Stateless AI Agent Orchestrator Library for tool-calling chats.

Installs: 13

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Forks: 0

pkg:composer/adachsoft/ai-agent

v0.5.1 2025-12-15 09:41 UTC

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',
        systemPrompt: 'You are a helpful assistant.',
        parameters: [
            'temperature' => 0.2,
            'max_tokens' => 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

Configuring system prompt and generation parameters

Agent behaviour is configured via two fields on AgentConfigDto:

  • systemPrompt  high level, stable instruction injected as the first system role message in every request to adachsoft/ai-integration.
  • parameters  free-form array of provider-specific generation knobs forwarded 1:1 to ToolCallingChatRequestDto::$parameters (no renaming or filtering).

Examples of parameters values:

  • OpenAI-style:
    • ['temperature' => 0.7, 'max_tokens' => 200, 'top_p' => 0.9, 'presence_penalty' => 0.6, 'frequency_penalty' => 0.3]
  • Gemini-style:
    • `[
      'temperature' => 0.5,
      'thinkingConfig' => [
          'includeThoughts' => false,
          'thinkingLevel' => 'high',
      ],
      'maxOutputTokens' => 8192,
      

      ]`

You are responsible for choosing parameter names that match the selected provider (providerId, modelId). The agent library treats this array as opaque data and passes it straight to adachsoft/ai-integration.

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

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  use finalText, ignore toolCalls.
  • prefer_tool_calls  ignore finalText, 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).