progress in AGI
This commit is contained in:
parent
3047c62f5d
commit
a095dd530f
44
hesabixCore/src/AiTool/PersonService.php
Normal file
44
hesabixCore/src/AiTool/PersonService.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\AiTool;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use App\Entity\Person;
|
||||
use App\Service\Explore;
|
||||
|
||||
class PersonService
|
||||
{
|
||||
private EntityManagerInterface $em;
|
||||
private \App\Cog\PersonService $cogPersonService;
|
||||
public function __construct(EntityManagerInterface $em, \App\Cog\PersonService $cogPersonService)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->cogPersonService = $cogPersonService;
|
||||
}
|
||||
|
||||
/**
|
||||
* دریافت اطلاعات یک شخص بر اساس کد و اطلاعات دسترسی
|
||||
*/
|
||||
public function getPersonInfoByCode($code, $acc): array
|
||||
{
|
||||
if (!$code) {
|
||||
return [
|
||||
'error' => 'کد شخص الزامی است'
|
||||
];
|
||||
}
|
||||
if (!$acc) {
|
||||
return [
|
||||
'error' => 'اطلاعات دسترسی (acc) الزامی است'
|
||||
];
|
||||
}
|
||||
try {
|
||||
return $this->cogPersonService->getPersonInfo($code, $acc);
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
'error' => 'خطا در دریافت اطلاعات شخص: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -7,7 +7,6 @@ use App\Entity\Person;
|
|||
use App\Entity\PersonType;
|
||||
use App\Entity\HesabdariRow;
|
||||
use App\Service\Explore;
|
||||
use App\Service\Access;
|
||||
|
||||
/**
|
||||
* سرویس مدیریت اشخاص
|
||||
|
@ -17,12 +16,12 @@ use App\Service\Access;
|
|||
class PersonService
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private Access $access;
|
||||
private array $access;
|
||||
|
||||
/**
|
||||
* سازنده سرویس
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entityManager, Access $access)
|
||||
public function __construct(EntityManagerInterface $entityManager, array $access)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->access = $access;
|
||||
|
|
|
@ -32,7 +32,8 @@ class wizardController extends AbstractController
|
|||
Log $log
|
||||
): JsonResponse
|
||||
{
|
||||
try {
|
||||
|
||||
|
||||
$acc = $access->hasRole('join');
|
||||
if (!$acc) {
|
||||
return $this->json([
|
||||
|
@ -45,7 +46,8 @@ class wizardController extends AbstractController
|
|||
}
|
||||
|
||||
// بررسی دسترسی هوش مصنوعی
|
||||
if (!$acc['ai']) {
|
||||
$acc = $access->hasRole('ai');
|
||||
if (!$acc) {
|
||||
return $this->json([
|
||||
'success' => false,
|
||||
'error' => 'شما دسترسی استفاده از هوش مصنوعی را ندارید',
|
||||
|
@ -96,7 +98,11 @@ class wizardController extends AbstractController
|
|||
'showChargeButton' => true,
|
||||
'debug_info' => [
|
||||
'balance' => $currentBalance,
|
||||
'required' => $estimatedCost
|
||||
'required' => $estimatedCost,
|
||||
'business' => [
|
||||
'id' => $business->getId(),
|
||||
'name' => $business->getName(),
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -142,15 +148,27 @@ class wizardController extends AbstractController
|
|||
}
|
||||
}
|
||||
|
||||
// پاکسازی خروجی از اشیای Doctrine (Business, User و ...)
|
||||
array_walk_recursive($response, function (&$item) {
|
||||
if (is_object($item) && method_exists($item, 'getId')) {
|
||||
$item = $item->getId();
|
||||
}
|
||||
});
|
||||
return $this->json($response);
|
||||
} else {
|
||||
return $this->json([
|
||||
$errorResponse = [
|
||||
'success' => false,
|
||||
'error' => $result['error'] ?? 'خطای نامشخص در سرویس هوش مصنوعی',
|
||||
'debug_info' => $result['debug_info'] ?? ['fallback' => 'no debug info from service', 'result' => $result]
|
||||
]);
|
||||
];
|
||||
array_walk_recursive($errorResponse, function (&$item) {
|
||||
if (is_object($item) && method_exists($item, 'getId')) {
|
||||
$item = $item->getId();
|
||||
}
|
||||
|
||||
});
|
||||
return $this->json($errorResponse);
|
||||
}
|
||||
try {
|
||||
} catch (\Exception $e) {
|
||||
return $this->json([
|
||||
'success' => false,
|
||||
|
|
|
@ -64,15 +64,13 @@ class AGIService
|
|||
'service_status' => $status,
|
||||
'inputs' => [
|
||||
'message' => $message,
|
||||
'business' => $business,
|
||||
'user' => $user,
|
||||
'conversationId' => $conversationId
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// مدیریت گفتگو و تاریخچه
|
||||
$conversation = $this->manageConversation($conversationId, $business, $user, $message);
|
||||
$conversationHistory = $this->getConversationHistory($conversation);
|
||||
|
@ -80,8 +78,8 @@ class AGIService
|
|||
// ذخیره پیام کاربر
|
||||
$this->saveUserMessage($conversation, $message);
|
||||
|
||||
// ساخت پرامپ هوشمند
|
||||
$prompt = $this->buildSmartPrompt($message, $business, $conversationHistory);
|
||||
// فقط سوال کاربر به عنوان prompt
|
||||
$userPrompt = $message;
|
||||
$service = $this->getAIAgentSource();
|
||||
$apiKey = $this->getAIApiKey($service);
|
||||
|
||||
|
@ -94,16 +92,14 @@ class AGIService
|
|||
'service' => $service,
|
||||
'inputs' => [
|
||||
'message' => $message,
|
||||
'business' => $business,
|
||||
'user' => $user,
|
||||
'conversationId' => $conversationId
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// ارسال درخواست با function calling
|
||||
$result = $this->sendToAIServiceWithFunctionCalling($prompt, $apiKey, $service, $conversationHistory, $acc);
|
||||
// ارسال درخواست با function calling (فقط سوال کاربر)
|
||||
$result = $this->sendToAIServiceWithFunctionCalling($userPrompt, $apiKey, $service, $conversationHistory, $acc);
|
||||
|
||||
if (!$result['success']) {
|
||||
return $result;
|
||||
|
@ -129,18 +125,8 @@ class AGIService
|
|||
'tool_results' => $result['tool_results'] ?? []
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
} catch (\Exception $e) {
|
||||
$this->log->error('خطا در ارسال درخواست به هوش مصنوعی: ' . $e->getMessage(), [
|
||||
'context' => 'AGIService::sendRequest',
|
||||
'message' => $message,
|
||||
'business' => $business,
|
||||
'user' => $user,
|
||||
'conversationId' => $conversationId,
|
||||
'exception' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'خطا در پردازش درخواست: ' . $e->getMessage(),
|
||||
|
@ -149,8 +135,6 @@ class AGIService
|
|||
'exception' => $e->getMessage(),
|
||||
'inputs' => [
|
||||
'message' => $message,
|
||||
'business' => $business,
|
||||
'user' => $user,
|
||||
'conversationId' => $conversationId
|
||||
]
|
||||
]
|
||||
|
@ -166,33 +150,36 @@ class AGIService
|
|||
$urls = $this->getServiceUrls($service);
|
||||
$model = $this->getAIModel();
|
||||
|
||||
// ساخت messages با تاریخچه
|
||||
$messages = [];
|
||||
|
||||
// اضافه کردن تاریخچه گفتگو
|
||||
// پیام system شامل قوانین خروجی و مثال دقیق
|
||||
$systemPrompt = "شما دستیار هوشمند حسابیکس هستید. فقط پاسخ را به صورت JSON مطابق مثال خروجی بده. اگر نیاز به ابزار داشتی، از function calling استفاده کن."
|
||||
. $this->promptService->getOutputFormatPrompt();
|
||||
$messages = [
|
||||
[
|
||||
'role' => 'system',
|
||||
'content' => $systemPrompt
|
||||
]
|
||||
];
|
||||
// تاریخچه گفتگو
|
||||
foreach ($conversationHistory as $historyItem) {
|
||||
$messages[] = [
|
||||
'role' => $historyItem['role'],
|
||||
'content' => $historyItem['content']
|
||||
];
|
||||
}
|
||||
|
||||
// اضافه کردن پیام فعلی
|
||||
// پیام user فقط سوال فعلی
|
||||
$messages[] = [
|
||||
'role' => 'user',
|
||||
'content' => $prompt
|
||||
];
|
||||
|
||||
// تعریف ابزارهای موجود
|
||||
$tools = $this->buildToolsFromPromptServices();
|
||||
|
||||
$data = [
|
||||
'model' => $model,
|
||||
'messages' => $messages,
|
||||
'tools' => $tools,
|
||||
'tool_choice' => 'auto', // اجازه انتخاب ابزار به مدل
|
||||
'tool_choice' => 'auto',
|
||||
'max_tokens' => 12000,
|
||||
'temperature' => 0.1
|
||||
'temperature' => 0.9
|
||||
];
|
||||
|
||||
$maxIterations = 5; // حداکثر تعداد تکرار برای جلوگیری از حلقه بینهایت
|
||||
|
@ -220,7 +207,12 @@ class AGIService
|
|||
'context' => 'sendToAIServiceWithFunctionCalling',
|
||||
'url_list' => $urls,
|
||||
'data' => $data,
|
||||
'iteration' => $iteration
|
||||
'iteration' => $iteration,
|
||||
'result' => $result,
|
||||
'apiKey' => $apiKey,
|
||||
'service' => $service,
|
||||
'conversationHistory' => $conversationHistory,
|
||||
|
||||
]
|
||||
];
|
||||
}
|
||||
|
@ -313,63 +305,22 @@ class AGIService
|
|||
try {
|
||||
switch ($tool) {
|
||||
case 'getPersonInfo':
|
||||
// استفاده مستقیم از سرویس Cog\PersonService
|
||||
return $this->callGetPersonInfoFromCog($params);
|
||||
// استفاده مستقیم از سرویس جدید
|
||||
$cogPersonService = new \App\Cog\PersonService($this->em, $params['acc'] ?? null);
|
||||
$personService = new \App\AiTool\PersonService($this->em, $cogPersonService);
|
||||
return $personService->getPersonInfoByCode($params['code'] ?? null, $params['acc'] ?? null);
|
||||
default:
|
||||
return [
|
||||
'error' => 'ابزار ناشناخته: ' . $tool
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->log->error('خطا در اجرای ابزار: ' . $e->getMessage(), [
|
||||
'context' => 'AGIService::callTool',
|
||||
'tool' => $tool,
|
||||
'params' => $params,
|
||||
'exception' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'error' => 'خطا در اجرای ابزار: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* اجرای ابزار getPersonInfo با استفاده از سرویس Cog\PersonService
|
||||
*/
|
||||
private function callGetPersonInfoFromCog(array $params)
|
||||
{
|
||||
$code = $params['code'] ?? null;
|
||||
if (!$code) {
|
||||
return [
|
||||
'error' => 'کد شخص الزامی است'
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// دریافت اطلاعات دسترسی (acc) از context یا پارامترها
|
||||
$acc = $params['acc'] ?? null;
|
||||
if (!$acc) {
|
||||
return [
|
||||
'error' => 'اطلاعات دسترسی (acc) الزامی است'
|
||||
];
|
||||
}
|
||||
// استفاده از سرویس Cog\PersonService
|
||||
$personService = new \App\Cog\PersonService($this->em, $this->provider->getAccessService());
|
||||
$result = $personService->getPersonInfo($code, $acc);
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$this->log->error('خطا در دریافت اطلاعات شخص از Cog: ' . $e->getMessage(), [
|
||||
'context' => 'AGIService::callGetPersonInfoFromCog',
|
||||
'code' => $code,
|
||||
'exception' => $e->getMessage()
|
||||
]);
|
||||
return [
|
||||
'error' => 'خطا در دریافت اطلاعات شخص: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ساخت پرامپ هوشمند
|
||||
*/
|
||||
|
@ -420,9 +371,19 @@ class AGIService
|
|||
*/
|
||||
private function makeHttpRequest(string $url, array $data, string $apiKey): array
|
||||
{
|
||||
$requestJson = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
$debugInfo = [
|
||||
'request_url' => $url,
|
||||
'request_data' => $data,
|
||||
'request_headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $apiKey,
|
||||
],
|
||||
'api_key' => $apiKey,
|
||||
'model' => $data['model'] ?? null,
|
||||
'request_json' => $requestJson,
|
||||
'request_time' => date('Y-m-d H:i:s'),
|
||||
'request_size_bytes' => strlen($requestJson),
|
||||
];
|
||||
|
||||
try {
|
||||
|
@ -440,6 +401,10 @@ class AGIService
|
|||
$content = $response->getContent(false); // false: throw exception on 4xx/5xx نمیدهد
|
||||
$debugInfo['http_code'] = $statusCode;
|
||||
$debugInfo['raw_response'] = $content;
|
||||
$debugInfo['response_headers'] = $response->getHeaders(false);
|
||||
$debugInfo['response_time'] = date('Y-m-d H:i:s');
|
||||
$debugInfo['response_size_bytes'] = strlen($content);
|
||||
$debugInfo['response_sample'] = mb_substr($content, 0, 500, 'UTF-8');
|
||||
|
||||
if ($statusCode !== 200) {
|
||||
$debugInfo['http_error_message'] = $this->getHttpErrorMessage($statusCode);
|
||||
|
@ -461,6 +426,7 @@ class AGIService
|
|||
];
|
||||
}
|
||||
|
||||
$debugInfo['response_data'] = $responseData;
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => $responseData,
|
||||
|
@ -629,13 +595,6 @@ class AGIService
|
|||
return (bool) ($this->registryMGR->get('system', 'aiEnabled') ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* دریافت پرامپ هوش مصنوعی
|
||||
*/
|
||||
public function getAIPrompt(): string
|
||||
{
|
||||
return $this->registryMGR->get('system', 'aiPrompt') ?? 'شما یک دستیار هوشمند برای سیستم حسابداری هستید.';
|
||||
}
|
||||
|
||||
/**
|
||||
* مدیریت گفتگو - ایجاد یا بازیابی گفتگوی موجود
|
||||
|
@ -833,6 +792,19 @@ class AGIService
|
|||
*/
|
||||
private function buildToolsFromPromptServices(): array
|
||||
{
|
||||
return $this->promptService->getAllTools();
|
||||
// بر اساس آخرین مستندات OpenAI و OpenRouter، باید هر ابزار به صورت زیر باشد:
|
||||
// [
|
||||
// 'type' => 'function',
|
||||
// 'function' => [
|
||||
// 'name' => ..., 'description' => ..., 'parameters' => [...]
|
||||
// ]
|
||||
// ]
|
||||
// این ساختار در PromptService رعایت شده اما اینجا یکبار دیگر چک و لاگ میکنیم
|
||||
$tools = $this->promptService->getAllTools();
|
||||
// ابزارها را در لاگ دیباگ ذخیره کن
|
||||
if (method_exists($this->log, 'debug')) {
|
||||
$this->log->debug('AIService tools', ['tools' => $tools]);
|
||||
}
|
||||
return $tools;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,13 @@ class BankPromptService
|
|||
$bankAccountInfoData = json_decode($bankAccountInfoPrompt, true);
|
||||
|
||||
if ($bankAccountInfoData) {
|
||||
// اصلاح ساختار properties
|
||||
$properties = [
|
||||
'code' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Bank account code (e.g., 1001, 1002)'
|
||||
]
|
||||
];
|
||||
$tools[] = [
|
||||
'type' => 'function',
|
||||
'function' => [
|
||||
|
@ -33,8 +40,8 @@ class BankPromptService
|
|||
'description' => $bankAccountInfoData['description'],
|
||||
'parameters' => [
|
||||
'type' => 'object',
|
||||
'properties' => $bankAccountInfoData['input'],
|
||||
'required' => array_keys($bankAccountInfoData['input'])
|
||||
'properties' => $properties,
|
||||
'required' => ['code']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
@ -26,6 +26,13 @@ class InventoryPromptService
|
|||
$itemInfoData = json_decode($itemInfoPrompt, true);
|
||||
|
||||
if ($itemInfoData) {
|
||||
// اصلاح ساختار properties
|
||||
$properties = [
|
||||
'code' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Item code (e.g., 1001, 1002)'
|
||||
]
|
||||
];
|
||||
$tools[] = [
|
||||
'type' => 'function',
|
||||
'function' => [
|
||||
|
@ -33,8 +40,8 @@ class InventoryPromptService
|
|||
'description' => $itemInfoData['description'],
|
||||
'parameters' => [
|
||||
'type' => 'object',
|
||||
'properties' => $itemInfoData['input'],
|
||||
'required' => array_keys($itemInfoData['input'])
|
||||
'properties' => $properties,
|
||||
'required' => ['code']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
@ -26,6 +26,13 @@ class PersonPromptService
|
|||
$personInfoData = json_decode($personInfoPrompt, true);
|
||||
|
||||
if ($personInfoData) {
|
||||
// اصلاح ساختار properties
|
||||
$properties = [
|
||||
'code' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Person code (e.g., 1001, 1002)'
|
||||
]
|
||||
];
|
||||
$tools[] = [
|
||||
'type' => 'function',
|
||||
'function' => [
|
||||
|
@ -33,8 +40,8 @@ class PersonPromptService
|
|||
'description' => $personInfoData['description'],
|
||||
'parameters' => [
|
||||
'type' => 'object',
|
||||
'properties' => $personInfoData['input'],
|
||||
'required' => array_keys($personInfoData['input'])
|
||||
'properties' => $properties,
|
||||
'required' => ['code']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue