adachsoft/ai-agent
最新稳定版本:v0.6.0
Composer 安装命令:
composer require adachsoft/ai-agent
包简介
Stateless AI Agent Orchestrator Library for tool-calling chats.
README 文档
README
Stateful AI Agent Orchestrator Library for tool-calling chats.
- PHP: >= 8.3
- Namespace: AdachSoft\AiAgent\
Overview
This library orchestrates tool-calling chats in stateful mode. The client
identifies a conversation by conversationId, the library owns and persists
full conversation history behind a ConversationRepositoryInterface, and each
ask() call executes a single agent turn over the stored history.
High-level responsibilities:
- maintain conversation history and correlate messages with tool calls,
- control the tool-calling loop under retry and budget policies,
- integrate exclusively with external PublicApi facades for chat and tools (no HTTP transport in this library),
- expose a small, stable PublicApi facade for starting conversations and executing turns.
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 (Stateful)
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
AiAgentBuilderwith config and policies. 3) CallstartConversation($conversationId)once to create an empty conversation in the configured repository. 4) For each user turn callask($conversationId, AskRequestDto)and persist nothing yourself - the library appends new messages internally.
Minimal Example (stateful)
use AdachSoft\\AiAgent\\PublicApi\\Builder\\AiAgentBuilder;
use AdachSoft\\AiAgent\\PublicApi\\Dto\\AgentConfigDto;
use AdachSoft\\AiAgent\\PublicApi\\Dto\\AskRequestDto;
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 in stateful mode
$facade = (new AiAgentBuilder())
->withPorts($ports)
->withAgentConfig(new AgentConfigDto(
name: 'MyAgent',
description: 'Demo stateful 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();
$conversationId = 'example-conversation-1';
// 3) Start a new empty conversation once
try {
$facade->startConversation($conversationId);
} catch (\\AdachSoft\\AiAgent\\PublicApi\\Exception\\ConversationAlreadyExistsPublicApiException $e) {
// conversation already exists - reuse it
}
// 4) Ask a question (one stateful turn)
$request = new AskRequestDto(prompt: 'What time is it in Warsaw? Reply as HH:MM.');
$response = $facade->ask($conversationId, $request);
$finalText = $response->finalText; // assistant final message
$history = $response->fullConversation; // full conversation as seen by the agent
Configuring system prompt and generation parameters
Agent behaviour is configured via two fields on AgentConfigDto:
systemPrompt- high level, stable instruction injected as the firstsystemrole message in every request toadachsoft/ai-integration.parameters- free-form array of provider-specific generation knobs forwarded 1:1 toToolCallingChatRequestDto::$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 stateful 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"
- Run from repo:
- Interactive chat (stateful over a single conversationId):
- 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
- Run from repo:
Note: bin/ scripts are thin wrappers that redirect to example entrypoints.
Environment variables:
DEEPSEEK_API_KEY
API Highlights
AiAgentFacadeInterface::startConversation(string $conversationId): voidAiAgentFacadeInterface::ask(string $conversationId, AskRequestDto $request): AskResponseDto
AskResponseDto contains:
fullConversation(ChatMessageDtoCollection) - full conversation as seen by the agent after the turn,tokensUsed(TokensUsageDto:promptTokens,completionTokens,totalTokens),status(enum value describing orchestration outcome),finalText(string|null) - final assistant text for this turn.
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
- Stateful: the library owns conversation history keyed by
conversationIdand appends only new messages viaConversationRepositoryInterface. - 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).
统计信息
- 总下载量: 17
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-11-14