progress in AGI

This commit is contained in:
Hesabix 2025-07-24 12:55:44 +00:00
parent 3047c62f5d
commit a095dd530f
7 changed files with 155 additions and 101 deletions

View 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()
];
}
}
}

View file

@ -7,7 +7,6 @@ use App\Entity\Person;
use App\Entity\PersonType; use App\Entity\PersonType;
use App\Entity\HesabdariRow; use App\Entity\HesabdariRow;
use App\Service\Explore; use App\Service\Explore;
use App\Service\Access;
/** /**
* سرویس مدیریت اشخاص * سرویس مدیریت اشخاص
@ -17,12 +16,12 @@ use App\Service\Access;
class PersonService class PersonService
{ {
private EntityManagerInterface $entityManager; 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->entityManager = $entityManager;
$this->access = $access; $this->access = $access;

View file

@ -32,7 +32,8 @@ class wizardController extends AbstractController
Log $log Log $log
): JsonResponse ): JsonResponse
{ {
try {
$acc = $access->hasRole('join'); $acc = $access->hasRole('join');
if (!$acc) { if (!$acc) {
return $this->json([ return $this->json([
@ -45,7 +46,8 @@ class wizardController extends AbstractController
} }
// بررسی دسترسی هوش مصنوعی // بررسی دسترسی هوش مصنوعی
if (!$acc['ai']) { $acc = $access->hasRole('ai');
if (!$acc) {
return $this->json([ return $this->json([
'success' => false, 'success' => false,
'error' => 'شما دسترسی استفاده از هوش مصنوعی را ندارید', 'error' => 'شما دسترسی استفاده از هوش مصنوعی را ندارید',
@ -96,7 +98,11 @@ class wizardController extends AbstractController
'showChargeButton' => true, 'showChargeButton' => true,
'debug_info' => [ 'debug_info' => [
'balance' => $currentBalance, '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); return $this->json($response);
} else { } else {
return $this->json([ $errorResponse = [
'success' => false, 'success' => false,
'error' => $result['error'] ?? 'خطای نامشخص در سرویس هوش مصنوعی', 'error' => $result['error'] ?? 'خطای نامشخص در سرویس هوش مصنوعی',
'debug_info' => $result['debug_info'] ?? ['fallback' => 'no debug info from service', 'result' => $result] '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) { } catch (\Exception $e) {
return $this->json([ return $this->json([
'success' => false, 'success' => false,

View file

@ -64,15 +64,13 @@ class AGIService
'service_status' => $status, 'service_status' => $status,
'inputs' => [ 'inputs' => [
'message' => $message, 'message' => $message,
'business' => $business,
'user' => $user,
'conversationId' => $conversationId 'conversationId' => $conversationId
] ]
] ]
]; ];
} }
try {
// مدیریت گفتگو و تاریخچه // مدیریت گفتگو و تاریخچه
$conversation = $this->manageConversation($conversationId, $business, $user, $message); $conversation = $this->manageConversation($conversationId, $business, $user, $message);
$conversationHistory = $this->getConversationHistory($conversation); $conversationHistory = $this->getConversationHistory($conversation);
@ -80,8 +78,8 @@ class AGIService
// ذخیره پیام کاربر // ذخیره پیام کاربر
$this->saveUserMessage($conversation, $message); $this->saveUserMessage($conversation, $message);
// ساخت پرامپ هوشمند // فقط سوال کاربر به عنوان prompt
$prompt = $this->buildSmartPrompt($message, $business, $conversationHistory); $userPrompt = $message;
$service = $this->getAIAgentSource(); $service = $this->getAIAgentSource();
$apiKey = $this->getAIApiKey($service); $apiKey = $this->getAIApiKey($service);
@ -94,16 +92,14 @@ class AGIService
'service' => $service, 'service' => $service,
'inputs' => [ 'inputs' => [
'message' => $message, 'message' => $message,
'business' => $business,
'user' => $user,
'conversationId' => $conversationId 'conversationId' => $conversationId
] ]
] ]
]; ];
} }
// ارسال درخواست با function calling // ارسال درخواست با function calling (فقط سوال کاربر)
$result = $this->sendToAIServiceWithFunctionCalling($prompt, $apiKey, $service, $conversationHistory, $acc); $result = $this->sendToAIServiceWithFunctionCalling($userPrompt, $apiKey, $service, $conversationHistory, $acc);
if (!$result['success']) { if (!$result['success']) {
return $result; return $result;
@ -129,18 +125,8 @@ class AGIService
'tool_results' => $result['tool_results'] ?? [] 'tool_results' => $result['tool_results'] ?? []
] ]
]; ];
try {
} catch (\Exception $e) { } 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 [ return [
'success' => false, 'success' => false,
'error' => 'خطا در پردازش درخواست: ' . $e->getMessage(), 'error' => 'خطا در پردازش درخواست: ' . $e->getMessage(),
@ -149,8 +135,6 @@ class AGIService
'exception' => $e->getMessage(), 'exception' => $e->getMessage(),
'inputs' => [ 'inputs' => [
'message' => $message, 'message' => $message,
'business' => $business,
'user' => $user,
'conversationId' => $conversationId 'conversationId' => $conversationId
] ]
] ]
@ -166,33 +150,36 @@ class AGIService
$urls = $this->getServiceUrls($service); $urls = $this->getServiceUrls($service);
$model = $this->getAIModel(); $model = $this->getAIModel();
// ساخت messages با تاریخچه // پیام system شامل قوانین خروجی و مثال دقیق
$messages = []; $systemPrompt = "شما دستیار هوشمند حسابیکس هستید. فقط پاسخ را به صورت JSON مطابق مثال خروجی بده. اگر نیاز به ابزار داشتی، از function calling استفاده کن."
. $this->promptService->getOutputFormatPrompt();
// اضافه کردن تاریخچه گفتگو $messages = [
[
'role' => 'system',
'content' => $systemPrompt
]
];
// تاریخچه گفتگو
foreach ($conversationHistory as $historyItem) { foreach ($conversationHistory as $historyItem) {
$messages[] = [ $messages[] = [
'role' => $historyItem['role'], 'role' => $historyItem['role'],
'content' => $historyItem['content'] 'content' => $historyItem['content']
]; ];
} }
// پیام user فقط سوال فعلی
// اضافه کردن پیام فعلی
$messages[] = [ $messages[] = [
'role' => 'user', 'role' => 'user',
'content' => $prompt 'content' => $prompt
]; ];
// تعریف ابزارهای موجود
$tools = $this->buildToolsFromPromptServices(); $tools = $this->buildToolsFromPromptServices();
$data = [ $data = [
'model' => $model, 'model' => $model,
'messages' => $messages, 'messages' => $messages,
'tools' => $tools, 'tools' => $tools,
'tool_choice' => 'auto', // اجازه انتخاب ابزار به مدل 'tool_choice' => 'auto',
'max_tokens' => 12000, 'max_tokens' => 12000,
'temperature' => 0.1 'temperature' => 0.9
]; ];
$maxIterations = 5; // حداکثر تعداد تکرار برای جلوگیری از حلقه بی‌نهایت $maxIterations = 5; // حداکثر تعداد تکرار برای جلوگیری از حلقه بی‌نهایت
@ -220,7 +207,12 @@ class AGIService
'context' => 'sendToAIServiceWithFunctionCalling', 'context' => 'sendToAIServiceWithFunctionCalling',
'url_list' => $urls, 'url_list' => $urls,
'data' => $data, 'data' => $data,
'iteration' => $iteration 'iteration' => $iteration,
'result' => $result,
'apiKey' => $apiKey,
'service' => $service,
'conversationHistory' => $conversationHistory,
] ]
]; ];
} }
@ -313,63 +305,22 @@ class AGIService
try { try {
switch ($tool) { switch ($tool) {
case 'getPersonInfo': 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: default:
return [ return [
'error' => 'ابزار ناشناخته: ' . $tool 'error' => 'ابزار ناشناخته: ' . $tool
]; ];
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log->error('خطا در اجرای ابزار: ' . $e->getMessage(), [
'context' => 'AGIService::callTool',
'tool' => $tool,
'params' => $params,
'exception' => $e->getMessage()
]);
return [ return [
'error' => 'خطا در اجرای ابزار: ' . $e->getMessage() '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 private function makeHttpRequest(string $url, array $data, string $apiKey): array
{ {
$requestJson = json_encode($data, JSON_UNESCAPED_UNICODE);
$debugInfo = [ $debugInfo = [
'request_url' => $url, 'request_url' => $url,
'request_data' => $data, '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 { try {
@ -440,6 +401,10 @@ class AGIService
$content = $response->getContent(false); // false: throw exception on 4xx/5xx نمی‌دهد $content = $response->getContent(false); // false: throw exception on 4xx/5xx نمی‌دهد
$debugInfo['http_code'] = $statusCode; $debugInfo['http_code'] = $statusCode;
$debugInfo['raw_response'] = $content; $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) { if ($statusCode !== 200) {
$debugInfo['http_error_message'] = $this->getHttpErrorMessage($statusCode); $debugInfo['http_error_message'] = $this->getHttpErrorMessage($statusCode);
@ -461,6 +426,7 @@ class AGIService
]; ];
} }
$debugInfo['response_data'] = $responseData;
return [ return [
'success' => true, 'success' => true,
'data' => $responseData, 'data' => $responseData,
@ -629,13 +595,6 @@ class AGIService
return (bool) ($this->registryMGR->get('system', 'aiEnabled') ?? false); 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 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;
} }
} }

View file

@ -26,6 +26,13 @@ class BankPromptService
$bankAccountInfoData = json_decode($bankAccountInfoPrompt, true); $bankAccountInfoData = json_decode($bankAccountInfoPrompt, true);
if ($bankAccountInfoData) { if ($bankAccountInfoData) {
// اصلاح ساختار properties
$properties = [
'code' => [
'type' => 'string',
'description' => 'Bank account code (e.g., 1001, 1002)'
]
];
$tools[] = [ $tools[] = [
'type' => 'function', 'type' => 'function',
'function' => [ 'function' => [
@ -33,8 +40,8 @@ class BankPromptService
'description' => $bankAccountInfoData['description'], 'description' => $bankAccountInfoData['description'],
'parameters' => [ 'parameters' => [
'type' => 'object', 'type' => 'object',
'properties' => $bankAccountInfoData['input'], 'properties' => $properties,
'required' => array_keys($bankAccountInfoData['input']) 'required' => ['code']
] ]
] ]
]; ];

View file

@ -26,6 +26,13 @@ class InventoryPromptService
$itemInfoData = json_decode($itemInfoPrompt, true); $itemInfoData = json_decode($itemInfoPrompt, true);
if ($itemInfoData) { if ($itemInfoData) {
// اصلاح ساختار properties
$properties = [
'code' => [
'type' => 'string',
'description' => 'Item code (e.g., 1001, 1002)'
]
];
$tools[] = [ $tools[] = [
'type' => 'function', 'type' => 'function',
'function' => [ 'function' => [
@ -33,8 +40,8 @@ class InventoryPromptService
'description' => $itemInfoData['description'], 'description' => $itemInfoData['description'],
'parameters' => [ 'parameters' => [
'type' => 'object', 'type' => 'object',
'properties' => $itemInfoData['input'], 'properties' => $properties,
'required' => array_keys($itemInfoData['input']) 'required' => ['code']
] ]
] ]
]; ];

View file

@ -26,6 +26,13 @@ class PersonPromptService
$personInfoData = json_decode($personInfoPrompt, true); $personInfoData = json_decode($personInfoPrompt, true);
if ($personInfoData) { if ($personInfoData) {
// اصلاح ساختار properties
$properties = [
'code' => [
'type' => 'string',
'description' => 'Person code (e.g., 1001, 1002)'
]
];
$tools[] = [ $tools[] = [
'type' => 'function', 'type' => 'function',
'function' => [ 'function' => [
@ -33,8 +40,8 @@ class PersonPromptService
'description' => $personInfoData['description'], 'description' => $personInfoData['description'],
'parameters' => [ 'parameters' => [
'type' => 'object', 'type' => 'object',
'properties' => $personInfoData['input'], 'properties' => $properties,
'required' => array_keys($personInfoData['input']) 'required' => ['code']
] ]
] ]
]; ];