last ...
This commit is contained in:
@@ -0,0 +1,977 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>تحلیل معماری پروژه xAiApi - فن آوران ساحر علم</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e3a8a;
|
||||
--secondary: #3b82f6;
|
||||
--accent: #f59e0b;
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--bg-light: #f8fafc;
|
||||
--bg-code: #1e293b;
|
||||
--text-dark: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: 'Tahoma', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
|
||||
color: var(--text-dark);
|
||||
line-height: 1.8;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: white;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="none" stroke="white" stroke-width="0.5" opacity="0.1"/></svg>');
|
||||
opacity: 0.3;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 2.2em;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 1.1em;
|
||||
opacity: 0.95;
|
||||
position: relative;
|
||||
}
|
||||
.meta-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: var(--bg-light);
|
||||
padding: 15px 30px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.9em;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.meta-item strong { color: var(--primary); }
|
||||
.content { padding: 40px; }
|
||||
.section {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
background: var(--bg-light);
|
||||
border-radius: 12px;
|
||||
border-right: 5px solid var(--secondary);
|
||||
}
|
||||
.section h2 {
|
||||
color: var(--primary);
|
||||
font-size: 1.6em;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.section h3 {
|
||||
color: var(--secondary);
|
||||
font-size: 1.2em;
|
||||
margin: 20px 0 12px;
|
||||
}
|
||||
.step {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
.step-number {
|
||||
display: inline-block;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.arch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.arch-card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
border-top: 4px solid var(--secondary);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.arch-card:hover { transform: translateY(-5px); }
|
||||
.arch-card h4 {
|
||||
color: var(--primary);
|
||||
margin-bottom: 12px;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
.arch-card ul {
|
||||
list-style: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
.arch-card li {
|
||||
padding: 6px 0;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
.arch-card li::before {
|
||||
content: '▸';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
color: var(--accent);
|
||||
font-weight: bold;
|
||||
}
|
||||
.tech-badge {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85em;
|
||||
margin: 3px;
|
||||
}
|
||||
.tech-badge.primary { background: var(--primary); }
|
||||
.tech-badge.success { background: var(--success); }
|
||||
.tech-badge.accent { background: var(--accent); }
|
||||
.tech-badge.danger { background: var(--danger); }
|
||||
pre {
|
||||
background: var(--bg-code);
|
||||
color: #e2e8f0;
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
font-size: 0.88em;
|
||||
margin: 15px 0;
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
code {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.9em;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
th {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 12px;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
tr:hover { background: var(--bg-light); }
|
||||
.flow-diagram {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.flow-step {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.flow-arrow {
|
||||
display: inline-block;
|
||||
color: var(--accent);
|
||||
font-size: 1.5em;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.alert {
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 15px 0;
|
||||
border-right: 4px solid;
|
||||
}
|
||||
.alert-info {
|
||||
background: #dbeafe;
|
||||
border-color: var(--secondary);
|
||||
color: #1e40af;
|
||||
}
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
border-color: var(--success);
|
||||
color: #065f46;
|
||||
}
|
||||
.alert-warning {
|
||||
background: #fef3c7;
|
||||
border-color: var(--accent);
|
||||
color: #92400e;
|
||||
}
|
||||
.footer {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 25px;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.footer p { margin: 5px 0; }
|
||||
.highlight {
|
||||
background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.toc {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 25px;
|
||||
border: 2px solid var(--border);
|
||||
}
|
||||
.toc h3 { color: var(--primary); margin-bottom: 15px; }
|
||||
.toc ol { padding-right: 25px; }
|
||||
.toc li { padding: 6px 0; }
|
||||
.toc a {
|
||||
color: var(--secondary);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.toc a:hover { color: var(--primary); text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>🧠 تحلیل جامع معماری پروژه xAiApi</h1>
|
||||
<div class="subtitle">پلتفرم یکپارچه ارائه خدمات هوش مصنوعی مبتنی بر ASP.NET Core</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-bar">
|
||||
<div class="meta-item">👨💻 <strong>توسعهدهنده:</strong> هادی خزاعی اصل</div>
|
||||
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
|
||||
<div class="meta-item">📅 <strong>تاریخ تحلیل:</strong> چهارشنبه ۸ مهر ۱۴۰۵</div>
|
||||
<div class="meta-item">📦 <strong>نسخه بررسی شده:</strong> 2026.09.30</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="toc">
|
||||
<h3>📑 فهرست مطالب</h3>
|
||||
<ol>
|
||||
<li><a href="#overview">مرور کلی و چشمانداز پروژه</a></li>
|
||||
<li><a href="#architecture">معماری کلان و ساختار ماژولار</a></li>
|
||||
<li><a href="#layers">لایهبندی و مسئولیتها</a></li>
|
||||
<li><a href="#ai-providers">ارائهدهندگان هوش مصنوعی پشتیبانی شده</a></li>
|
||||
<li><a href="#data-model">مدل داده و پایگاه داده</a></li>
|
||||
<li><a href="#api-endpoints">نقاط پایانی API</a></li>
|
||||
<li><a href="#features">ویژگیهای کلیدی پیادهسازی شده</a></li>
|
||||
<li><a href="#flow">جریان پردازش درخواست</a></li>
|
||||
<li><a href="#di">مدیریت وابستگیها (DI)</a></li>
|
||||
<li><a href="#strengths">نقاط قوت و پیشنهادات بهبود</a></li>
|
||||
<li><a href="#memory">خلاصه ذخیره شده در حافظه</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Section 1: Overview -->
|
||||
<div class="section" id="overview">
|
||||
<h2>🎯 گام ۱: مرور کلی و چشمانداز پروژه</h2>
|
||||
<p>
|
||||
پروژه <span class="highlight">xAiApi</span> یک پلتفرم جامع و مقیاسپذیر برای ارائه خدمات هوش مصنوعی است که بر بستر
|
||||
<code>ASP.NET Core</code> توسعه یافته است. این پروژه با رویکرد <strong>Modular Architecture</strong> طراحی شده
|
||||
و قابلیت اتصال به چندین ارائهدهنده LLM را به صورت یکپارچه فراهم میکند.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>💡 هدف اصلی:</strong> ایجاد یک لایه انتزاعی (Abstraction Layer) بر روی ارائهدهندگان مختلف AI
|
||||
تا توسعهدهندگان بتوانند بدون وابستگی به یک ارائهدهنده خاص، از قابلیتهای هوش مصنوعی در محصولات خود استفاده کنند.
|
||||
</div>
|
||||
|
||||
<h3>سه ماژول اصلی پروژه:</h3>
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>📡 xAiApi (لایه ارائه)</h4>
|
||||
<ul>
|
||||
<li>Controllers و API Endpoints</li>
|
||||
<li>Database Context و Migrations</li>
|
||||
<li>Providers و Service Implementations</li>
|
||||
<li>Configuration و DI Extensions</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🧩 xAiModels (لایه مدل)</h4>
|
||||
<ul>
|
||||
<li>Entities و DTOs</li>
|
||||
<li>Repository Pattern</li>
|
||||
<li>Enrichers و Data Providers</li>
|
||||
<li>GraphQL Support</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>⚙️ xAiService (لایه سرویس)</h4>
|
||||
<ul>
|
||||
<li>Vector Helper (Embedding)</li>
|
||||
<li>Cosine Similarity</li>
|
||||
<li>User Validation Extensions</li>
|
||||
<li>Configuration Node Names</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Architecture -->
|
||||
<div class="section" id="architecture">
|
||||
<h2>🏗️ گام ۲: معماری کلان و ساختار ماژولار</h2>
|
||||
<p>
|
||||
معماری پروژه بر پایه الگوی <strong>Clean Architecture</strong> همراه با <strong>Repository Pattern</strong>
|
||||
و <strong>Dependency Injection</strong> گسترده طراحی شده است.
|
||||
</p>
|
||||
|
||||
<h3>سلسله مراتب لایهها:</h3>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">🌐 Client</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">📡 Controllers</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🧠 AI Services</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">💾 Data Provider</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🗄️ Repository</span>
|
||||
</div>
|
||||
|
||||
<h3>ساختار پوشهای پروژه xAiApi:</h3>
|
||||
<pre>xAiApi/
|
||||
├── 📂 Controllers/
|
||||
│ ├── DefaultAiController.cs
|
||||
│ ├── DefaultEmbeddingController.cs
|
||||
│ ├── DefaultThinkingAiController.cs
|
||||
│ ├── StartupController.cs
|
||||
│ ├── XAiServiceControllerBase.cs
|
||||
│ └── XAiEmbeddingServiceControllerBase.cs
|
||||
├── 📂 Providers/
|
||||
│ ├── XAIServiceBase.cs (هسته اصلی)
|
||||
│ ├── XAiEmbeddingServiceBase.cs
|
||||
│ ├── XDefaultAiService.cs
|
||||
│ ├── XDefaultEmbeddingService.cs
|
||||
│ └── XDefaultThinkingAiService.cs
|
||||
├── 📂 Database/
|
||||
│ ├── XAiApiDbContext.cs
|
||||
│ └── XAiApiDatabaseDescriptor.cs
|
||||
├── 📂 Interfaces/
|
||||
│ ├── IXAiServiceBase.cs
|
||||
│ ├── IXAiEmbeddingServiceBase.cs
|
||||
│ └── IXDefault*.cs
|
||||
├── 📂 Configurations/
|
||||
│ └── XAiApiConfiguration.cs
|
||||
├── 📂 Extensions/
|
||||
│ ├── XModelsExtensions.cs
|
||||
│ └── XProgramExtensions.cs
|
||||
├── 📂 DI/
|
||||
│ └── XDIHelperExtension.cs
|
||||
├── 📂 Constants/
|
||||
│ ├── XAiApiConstants.cs
|
||||
│ └── ConfigurationNodeNames.cs
|
||||
└── 📂 Migrations/
|
||||
└── InitialMigrationAiApi.cs</pre>
|
||||
</div>
|
||||
|
||||
<!-- Section 3: Layers -->
|
||||
<div class="section" id="layers">
|
||||
<h2>🧱 گام ۳: لایهبندی و مسئولیتها</h2>
|
||||
|
||||
<h3>۳.۱ لایه Controllers (لایه ارائه)</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Controller</th>
|
||||
<th>مسئولیت</th>
|
||||
<th>ویژگی کلیدی</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DefaultAiController</code></td>
|
||||
<td>پاسخ به سوالات عمومی</td>
|
||||
<td>استفاده از مدل Gemma</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DefaultEmbeddingController</code></td>
|
||||
<td>تبدیل متن به بردار</td>
|
||||
<td>پشتیبانی از Batch</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DefaultThinkingAiController</code></td>
|
||||
<td>استدلال عمیق (Reasoning)</td>
|
||||
<td>مدل Qwen با Reasoning</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>StartupController</code></td>
|
||||
<td>پیام خوشآمدگویی</td>
|
||||
<td>AllowAnonymous</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ نکته مهم:</strong> استفاده از <code>XBaseIdentityApiV1Controller</code> به عنوان کلاس پایه،
|
||||
مدیریت هویت و مجوزها را به صورت یکپارچه فراهم میکند.
|
||||
</div>
|
||||
|
||||
<h3>۳.۲ لایه Providers (هسته منطقی)</h3>
|
||||
<p>کلاس <code>XAIServiceBase</code> به عنوان <strong>قلب تپنده</strong> پروژه عمل میکند و مسئولیتهای زیر را بر عهده دارد:</p>
|
||||
<ul style="padding-right: 25px; margin: 15px 0;">
|
||||
<li>مدیریت ارتباط با LLM از طریق <code>IChatClient</code></li>
|
||||
<li>پیادهسازی Memory Management برای حفظ Context مکالمات</li>
|
||||
<li>پشتیبانی از Streaming Response (SSE)</li>
|
||||
<li>مدیریت پروژهها، مکالمات و پیامها</li>
|
||||
<li>تزریق Introduction Prompt به صورت خودکار</li>
|
||||
</ul>
|
||||
|
||||
<h3>۳.۳ لایه Data Provider</h3>
|
||||
<p>کلاس <code>XAiDataProvider</code> مسئولیتهای زیر را مدیریت میکند:</p>
|
||||
<ul style="padding-right: 25px; margin: 15px 0;">
|
||||
<li>ایجاد خودکار <strong>Default Project</strong> و <strong>Default Conversation</strong> برای هر کاربر</li>
|
||||
<li>مدیریت Multi-language Resources</li>
|
||||
<li>ارسال SignalR Notifications</li>
|
||||
<li>Enrichment اشیاء با اطلاعات مرتبط</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Section 4: AI Providers -->
|
||||
<div class="section" id="ai-providers">
|
||||
<h2>🤖 گام ۴: ارائهدهندگان هوش مصنوعی پشتیبانی شده</h2>
|
||||
<p>
|
||||
پروژه از طریق <code>XAiModelProviderType</code> از چندین ارائهدهنده پشتیبانی میکند:
|
||||
</p>
|
||||
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>🟢 OpenAI</h4>
|
||||
<ul>
|
||||
<li>استفاده از <code>OpenAIClient</code></li>
|
||||
<li>پشتیبانی از ApiKey</li>
|
||||
<li>Endpoint قابل تنظیم</li>
|
||||
<li>Chat & Embedding</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🔵 Ollama</h4>
|
||||
<ul>
|
||||
<li>اجرای Local Models</li>
|
||||
<li>بدون نیاز به ApiKey</li>
|
||||
<li>مناسب برای توسعه</li>
|
||||
<li>مدلهای Gemma, Qwen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🟡 DeepSeek</h4>
|
||||
<ul>
|
||||
<li>پشتیبانی تعریف شده</li>
|
||||
<li>برای Reasoning Models</li>
|
||||
<li>در حال توسعه</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🟣 HuggingFace</h4>
|
||||
<ul>
|
||||
<li>دسترسی به هزاران مدل</li>
|
||||
<li>پشتیبانی تعریف شده</li>
|
||||
<li>در حال توسعه</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>مدلهای پیشفرض پیکربندی شده:</h3>
|
||||
<pre>// از XAiApiConstants.cs
|
||||
XAiDefaultModelName = "Gemma" // مدل پیشفرض چت
|
||||
XAiEmbeddingModelName = "Embed" // مدل Embedding
|
||||
XAiDefaultThinkingModelName = "Qwen" // مدل Reasoning</pre>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>⚠️ نکته طراحی:</strong> استفاده از <code>Microsoft.Extensions.AI</code> به عنوان لایه انتزاعی استاندارد،
|
||||
امکان تعویض ارائهدهنده بدون تغییر کد را فراهم میکند.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 5: Data Model -->
|
||||
<div class="section" id="data-model">
|
||||
<h2>🗄️ گام ۵: مدل داده و پایگاه داده</h2>
|
||||
<p>پایگاه داده شامل ۶ جدول اصلی است:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>جدول</th>
|
||||
<th>کلید</th>
|
||||
<th>فیلدهای کلیدی</th>
|
||||
<th>رابطه</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AiProjects</code></td>
|
||||
<td>Guid</td>
|
||||
<td>OwnerId, Title, Description, Prompt</td>
|
||||
<td>→ Conversations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AiConversations</code></td>
|
||||
<td>Guid</td>
|
||||
<td>OwnerId, Title, ProjectId</td>
|
||||
<td>→ Messages, Project</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AiMessages</code></td>
|
||||
<td>Guid</td>
|
||||
<td>Content, Role, MetaDatas, ConversationId</td>
|
||||
<td>→ Conversation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Files</code></td>
|
||||
<td>Guid</td>
|
||||
<td>Name, Path, Thumb, References</td>
|
||||
<td>مستقل</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Strings</code></td>
|
||||
<td>Int (Auto)</td>
|
||||
<td>Language, ResourceTitle, TranslatedValue</td>
|
||||
<td>Multi-language</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Tags</code></td>
|
||||
<td>Int (Auto)</td>
|
||||
<td>Tag, References</td>
|
||||
<td>مستقل</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>سلسله مراتب دادهها:</h3>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">👤 User (OwnerId)</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">📁 Project</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">💬 Conversation</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">📝 Message</span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>💡 ویژگی مهم:</strong> هر کاربر به صورت خودکار یک <strong>Default Project</strong> و یک
|
||||
<strong>Default Conversation</strong> دارد که از طریق <code>XAiDataProvider</code> مدیریت میشود.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 6: API Endpoints -->
|
||||
<div class="section" id="api-endpoints">
|
||||
<h2>🔌 گام ۶: نقاط پایانی API</h2>
|
||||
|
||||
<h3>۶.۱ Endpoints عمومی (از XAiServiceControllerBase):</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>متد</th>
|
||||
<th>مسیر</th>
|
||||
<th>توضیح</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/AskText?question=</code></td>
|
||||
<td>پرسش ساده و دریافت پاسخ</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/AskTextStream?question=</code></td>
|
||||
<td>پرسش با پاسخ Streaming (SSE)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge primary">POST</span></td>
|
||||
<td><code>/Ask</code></td>
|
||||
<td>پرسش با Context (Project/Conversation)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge primary">POST</span></td>
|
||||
<td><code>/AskStream</code></td>
|
||||
<td>پرسش Context دار با Streaming</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/Projects</code></td>
|
||||
<td>دریافت لیست پروژهها</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge primary">POST</span></td>
|
||||
<td><code>/Projects</code></td>
|
||||
<td>ایجاد پروژه جدید</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/Projects/{id}/Conversations</code></td>
|
||||
<td>مکالمات یک پروژه</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/Conversations/{id}/Messages</code></td>
|
||||
<td>پیامهای یک مکالمه</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>*/Query</code></td>
|
||||
<td>جستجوی پیشرفته با XQuery</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>۶.۲ Endpoints Embedding:</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/Embedding?content=</code></td>
|
||||
<td>تبدیل یک متن به بردار</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="tech-badge success">GET</span></td>
|
||||
<td><code>/Embeddings?batch=</code></td>
|
||||
<td>تبدیل دستهای متون به بردار</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Section 7: Features -->
|
||||
<div class="section" id="features">
|
||||
<h2>✨ گام ۷: ویژگیهای کلیدی پیادهسازی شده</h2>
|
||||
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>🔄 Streaming Responses</h4>
|
||||
<ul>
|
||||
<li>پیادهسازی SSE (Server-Sent Events)</li>
|
||||
<li>غیرفعالسازی Buffering</li>
|
||||
<li>پاسخ بلادرنگ به کاربر</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🧠 Memory Management</h4>
|
||||
<ul>
|
||||
<li>حفظ Context مکالمات</li>
|
||||
<li>Introduction Prompt</li>
|
||||
<li>Project-specific Prompts</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🌐 Multi-language Support</h4>
|
||||
<ul>
|
||||
<li>Resource-based Localization</li>
|
||||
<li>Owned Item Resources</li>
|
||||
<li>Default Language Management</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>📡 Real-time Notifications</h4>
|
||||
<ul>
|
||||
<li>SignalR Hub</li>
|
||||
<li>Project/Conversation/Message Events</li>
|
||||
<li>Connection-aware Broadcasting</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🔐 Authorization</h4>
|
||||
<ul>
|
||||
<li>OAuth2 Introspection</li>
|
||||
<li>Policy-based Authorization</li>
|
||||
<li>Role-based Access Control</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>📚 Swagger Documentation</h4>
|
||||
<ul>
|
||||
<li>XML Comments</li>
|
||||
<li>API V1 Versioning</li>
|
||||
<li>Auto-generated Docs</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🎯 Reasoning Models</h4>
|
||||
<ul>
|
||||
<li>ReasoningEffort Configuration</li>
|
||||
<li>ReasoningOutput Options</li>
|
||||
<li>Thinking AI Service</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>📊 Vector Operations</h4>
|
||||
<ul>
|
||||
<li>Vector Normalization</li>
|
||||
<li>Cosine Similarity</li>
|
||||
<li>Batch Embedding</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 8: Flow -->
|
||||
<div class="section" id="flow">
|
||||
<h2>🔄 گام ۸: جریان پردازش درخواست</h2>
|
||||
|
||||
<h3>۸.۱ جریان پرسش ساده (AskText):</h3>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">Client Request</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Controller</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Validation</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">XAIServiceBase</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">GetHistory()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">GetClient()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">LLM API</span>
|
||||
</div>
|
||||
|
||||
<h3>۸.۲ جریان پرسش با Context (Ask):</h3>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">Request + ProjectId + ConversationId</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Load Project</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Load Conversation</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Load Messages</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">PrepareMemory()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Save User Message</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">AskLLM()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">Save AI Response</span>
|
||||
</div>
|
||||
|
||||
<h3>۸.۳ جریان Embedding:</h3>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">Text Input</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">GetEmbeddingGenerator()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">GenerateAsync()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">VectorHelper.Normalize()</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">float[] Result</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 9: DI -->
|
||||
<div class="section" id="di">
|
||||
<h2>🔗 گام ۹: مدیریت وابستگیها (DI)</h2>
|
||||
|
||||
<h3>سرویسهای ثبت شده در <code>Startup.ConfigureServices</code>:</h3>
|
||||
<pre>// سرویسهای پایه
|
||||
services.AddXCommons();
|
||||
services.AddXAppConfiguration(Configuration);
|
||||
services.AddXCors(appConfiguration.AllowedOrigins);
|
||||
services.AddXSwagger(Configuration, xmlPath);
|
||||
services.AddXHttpService(Configuration);
|
||||
services.AddXApiV1Versioning();
|
||||
services.AddXAuthorization();
|
||||
|
||||
// سرویسهای هویت و ذخیرهسازی
|
||||
services.AddXIdentityService(lifeTime, Configuration);
|
||||
services.AddXStorageService(Configuration);
|
||||
services.AddXPushService(lifeTime, Configuration);
|
||||
|
||||
// سرویسهای داده
|
||||
services.AddXDatabase(lifeTime, descriptor, Configuration);
|
||||
services.AddXStringService<XAiApiDbContext>(lifeTime, repositoryType);
|
||||
services.AddXTagService<XAiApiDbContext>(lifeTime, repositoryType);
|
||||
services.AddXFileService<XAiApiDbContext>(lifeTime, repositoryType);
|
||||
services.AddXAiDataService<XAiApiDbContext>(lifeTime, repositoryType);
|
||||
|
||||
// سرویسهای AI اختصاصی
|
||||
services.AddScoped<IXDefaultAiService, XDefaultAiService>();
|
||||
services.AddScoped<IXDefaultEmbeddingService, XDefaultEmbeddingService>();
|
||||
services.AddScoped<IXDefaultThinkingAiService, XDefaultThinkingAiService>();</pre>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ الگوی طراحی:</strong> استفاده از <strong>Extension Methods</strong> برای ثبت سرویسها
|
||||
کد را تمیز، قابل نگهداری و ماژولار نگه میدارد.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 10: Strengths -->
|
||||
<div class="section" id="strengths">
|
||||
<h2>💎 گام ۱۰: نقاط قوت و پیشنهادات بهبود</h2>
|
||||
|
||||
<h3>✅ نقاط قوت:</h3>
|
||||
<ul style="padding-right: 25px; margin: 15px 0;">
|
||||
<li><strong>معماری ماژولار:</strong> جداسازی واضح لایهها و مسئولیتها</li>
|
||||
<li><strong>Provider Agnostic:</strong> قابلیت تعویض ارائهدهنده AI بدون تغییر کد</li>
|
||||
<li><strong>Repository Pattern:</strong> پشتیبانی از EF, MongoDB, InMemory</li>
|
||||
<li><strong>Streaming Support:</strong> پیادهسازی کامل SSE برای UX بهتر</li>
|
||||
<li><strong>Multi-language:</strong> سیستم Resource-based برای چندزبانگی</li>
|
||||
<li><strong>Real-time:</strong> یکپارچگی SignalR برای Notifications</li>
|
||||
<li><strong>GraphQL Support:</strong> امکان پرسوجوی انعطافپذیر</li>
|
||||
<li><strong>Enrichment Pattern:</strong> جداسازی منطق غنیسازی دادهها</li>
|
||||
</ul>
|
||||
|
||||
<h3>🔧 پیشنهادات بهبود:</h3>
|
||||
<ul style="padding-right: 25px; margin: 15px 0;">
|
||||
<li>پیادهسازی کامل DeepSeek و HuggingFace providers</li>
|
||||
<li>افزودن Rate Limiting برای کنترل مصرف API</li>
|
||||
<li>پیادهسازی Caching برای پاسخهای پرتکرار</li>
|
||||
<li>افزودن Telemetry و Monitoring (OpenTelemetry)</li>
|
||||
<li>پیادهسازی Unit of Work Pattern به صورت کاملتر</li>
|
||||
<li>افزودن Health Checks برای پایش سرویسها</li>
|
||||
<li>پیادهسازی Circuit Breaker برای resilience</li>
|
||||
<li>افزودن Unit Tests و Integration Tests</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Section 11: Memory Summary -->
|
||||
<div class="section" id="memory">
|
||||
<h2>🧠 گام ۱۱: خلاصه ذخیره شده در حافظه</h2>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ تأیید ذخیرهسازی:</strong> تحلیل کامل پروژه در حافظه من ذخیره شد و برای دستورات بعدی شما آماده است.
|
||||
</div>
|
||||
|
||||
<h3>📌 نکات کلیدی که به خاطر سپردهام:</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>موضوع</th>
|
||||
<th>جزئیات</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>معماری</strong></td>
|
||||
<td>Clean Architecture + Modular + Repository Pattern</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>فناوری پایه</strong></td>
|
||||
<td>ASP.NET Core, Entity Framework, SignalR, GraphQL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ارائهدهندگان AI</strong></td>
|
||||
<td>OpenAI, Ollama, DeepSeek, HuggingFace</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>مدلهای پیشفرض</strong></td>
|
||||
<td>Gemma (Chat), Embed (Embedding), Qwen (Reasoning)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ساختار داده</strong></td>
|
||||
<td>Project → Conversation → Message</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>ویژگیهای کلیدی</strong></td>
|
||||
<td>Streaming, Memory, Multi-lang, Real-time, Reasoning</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>الگوهای طراحی</strong></td>
|
||||
<td>DI, Repository, Provider, Enricher, Hub</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>فایلهای کلیدی</strong></td>
|
||||
<td>XAIServiceBase.cs, XAiDataProvider.cs, Startup.cs</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>🎯 آماده برای دستورات بعدی:</h3>
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>🔧 توسعه ویژگی جدید</h4>
|
||||
<ul>
|
||||
<li>افزودن Provider جدید</li>
|
||||
<li>پیادهسازی Endpoint جدید</li>
|
||||
<li>افزودن Entity جدید</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🐛 رفع اشکال</h4>
|
||||
<ul>
|
||||
<li>تحلیل خطاهای runtime</li>
|
||||
<li>بهینهسازی performance</li>
|
||||
<li>بررسی memory leaks</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>📚 مستندسازی</h4>
|
||||
<ul>
|
||||
<li>API Documentation</li>
|
||||
<li>Architecture Diagrams</li>
|
||||
<li>User Guides</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🚀 استقرار</h4>
|
||||
<ul>
|
||||
<li>Configuration Management</li>
|
||||
<li>Deployment Strategies</li>
|
||||
<li>Scaling Considerations</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>💬 پیام به استاد:</strong> تحلیل کامل پروژه با موفقیت انجام شد و تمامی جزئیات معماری،
|
||||
ساختار کد، الگوهای طراحی و ویژگیهای پیادهسازی شده در حافظه من ثبت گردید. اکنون آماده دریافت
|
||||
دستورات بعدی شما برای توسعه، بهبود، یا هرگونه تغییر در پروژه هستم.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>👨💻 توسعهدهنده:</strong> هادی خزاعی اصل</p>
|
||||
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
|
||||
<p><strong>📅 تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</p>
|
||||
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
|
||||
🧠 تحلیل جامع معماری پروژه xAiApi - تمامی حقوق محفوظ است
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,640 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>طراحی سیستم استخراج محتوا از فایل با مدل Qwen3.5 GGUF - فن آوران ساحر علم</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e3a8a;
|
||||
--secondary: #3b82f6;
|
||||
--accent: #f59e0b;
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--warning: #f97316;
|
||||
--bg-light: #f8fafc;
|
||||
--bg-code: #1e293b;
|
||||
--text-dark: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: 'Tahoma', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
|
||||
color: var(--text-dark);
|
||||
line-height: 1.8;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: white;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.header h1 { font-size: 2.2em; margin-bottom: 10px; position: relative; }
|
||||
.header .subtitle { font-size: 1.1em; opacity: 0.95; position: relative; }
|
||||
.meta-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: var(--bg-light);
|
||||
padding: 15px 30px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
|
||||
.meta-item strong { color: var(--primary); }
|
||||
.content { padding: 40px; }
|
||||
.section {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
background: var(--bg-light);
|
||||
border-radius: 12px;
|
||||
border-right: 5px solid var(--secondary);
|
||||
}
|
||||
.section h2 {
|
||||
color: var(--primary);
|
||||
font-size: 1.6em;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
|
||||
.step {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
.step-number {
|
||||
display: inline-block;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.arch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.arch-card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
border-top: 4px solid var(--secondary);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.arch-card:hover { transform: translateY(-5px); }
|
||||
.arch-card h4 { color: var(--primary); margin-bottom: 12px; font-size: 1.15em; }
|
||||
.arch-card ul { list-style: none; padding-right: 0; }
|
||||
.arch-card li { padding: 6px 0; padding-right: 20px; position: relative; font-size: 0.95em; }
|
||||
.arch-card li::before { content: '▸'; position: absolute; right: 0; color: var(--accent); font-weight: bold; }
|
||||
.tech-badge {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85em;
|
||||
margin: 3px;
|
||||
}
|
||||
.tech-badge.primary { background: var(--primary); }
|
||||
.tech-badge.success { background: var(--success); }
|
||||
.tech-badge.accent { background: var(--accent); }
|
||||
pre {
|
||||
background: var(--bg-code);
|
||||
color: #e2e8f0;
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
font-size: 0.85em;
|
||||
margin: 15px 0;
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
code {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.9em;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
th { background: var(--primary); color: white; padding: 12px; text-align: right; font-weight: bold; }
|
||||
td { padding: 12px; border-bottom: 1px solid var(--border); }
|
||||
tr:hover { background: var(--bg-light); }
|
||||
.flow-diagram {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.flow-step {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.flow-arrow { display: inline-block; color: var(--accent); font-size: 1.5em; margin: 0 8px; vertical-align: middle; }
|
||||
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
|
||||
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
|
||||
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
|
||||
.alert-warning { background: #fef3c7; border-color: var(--accent); color: #92400e; }
|
||||
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; margin-top: 40px; }
|
||||
.footer p { margin: 5px 0; }
|
||||
.highlight { background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%); padding: 2px 6px; border-radius: 4px; font-weight: bold; }
|
||||
.toc { background: white; padding: 20px; border-radius: 10px; margin-bottom: 25px; border: 2px solid var(--border); }
|
||||
.toc h3 { color: var(--primary); margin-bottom: 15px; }
|
||||
.toc ol { padding-right: 25px; }
|
||||
.toc li { padding: 6px 0; }
|
||||
.toc a { color: var(--secondary); text-decoration: none; transition: color 0.2s; }
|
||||
.toc a:hover { color: var(--primary); text-decoration: underline; }
|
||||
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
|
||||
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
|
||||
.badge-new { background: var(--success); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>🧠 استخراج هوشمند محتوا از فایل با مدل Qwen3.5 GGUF</h1>
|
||||
<div class="subtitle">راهنمای گام به گام یکپارچهسازی مدلهای محلی GGUF در معماری xAiApi</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-bar">
|
||||
<div class="meta-item">👨💻 <strong>توسعهدهنده:</strong> هادی خزاعی اصل</div>
|
||||
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
|
||||
<div class="meta-item">📅 <strong>تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</div>
|
||||
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="toc">
|
||||
<h3>📑 فهرست مطالب</h3>
|
||||
<ol>
|
||||
<li><a href="#overview">تحلیل رویکرد و استراتژی</a></li>
|
||||
<li><a href="#step1">گام ۱: آمادهسازی مدل GGUF در Ollama</a></li>
|
||||
<li><a href="#step2">گام ۲: پیکربندی مدل در appsettings.json</a></li>
|
||||
<li><a href="#step3">گام ۳: ایجاد سرویس استخراج محتوا (Extraction Service)</a></li>
|
||||
<li><a href="#step4">گام ۴: ایجاد Controller اختصاصی</a></li>
|
||||
<li><a href="#step5">گام ۵: ثبت وابستگیها (DI)</a></li>
|
||||
<li><a href="#flow">جریان کامل پردازش</a></li>
|
||||
<li><a href="#tips">نکات کلیدی و مهندسی پرامپت</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Section 1: Overview -->
|
||||
<div class="section" id="overview">
|
||||
<h2>🎯 گام ۱: تحلیل رویکرد و استراتژی</h2>
|
||||
<p>
|
||||
مدل <span class="highlight">Qwen3.5-9B-The-Defiant-Fable-Uncensored-Heretic-NEO-IMATRIX-MAX-MTP-GGUF</span>
|
||||
یک مدل زبانی بزرگ (LLM) در فرمت <strong>GGUF</strong> است. برای اجرای این مدل و استفاده از آن جهت استخراج محتوا،
|
||||
بهترین و سازگارترین راه با معماری فعلی پروژه شما، استفاده از <strong>Ollama</strong> به عنوان موتور اجرای محلی (Local Inference Engine) است.
|
||||
</p>
|
||||
<div class="alert alert-info">
|
||||
<strong>💡 استراتژی دو مرحلهای استخراج محتوا:</strong><br>
|
||||
۱. <strong>استخراج خام (Raw Extraction):</strong> استفاده از <code>IFileContentExtractor</code> (که قبلاً طراحی کردیم) برای خواندن متن خام از PDF/DOCX/TXT.<br>
|
||||
۲. <strong>پردازش هوشمند (LLM Processing):</strong> ارسال متن خام به مدل Qwen3.5 با یک <strong>System Prompt</strong> دقیق برای خلاصهسازی، استخراج موجودیتها (NER)، یا تبدیل به JSON ساختاریافته.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Ollama Setup -->
|
||||
<div class="section" id="step1">
|
||||
<h2>⚙️ گام ۲: آمادهسازی مدل GGUF در Ollama</h2>
|
||||
<p>از آنجا که Ollama به صورت بومی از فرمت GGUF پشتیبانی میکند، باید مدل را دانلود و در Ollama ایمپورت کنیم.</p>
|
||||
|
||||
<div class="step">
|
||||
<span class="step-number">۱</span>
|
||||
<strong>دانلود فایل GGUF:</strong>
|
||||
<p>فایل <code>.gguf</code> را از لینک Hugging Face ارائه شده دانلود کرده و در مسیری مانند <code>C:\Models\qwen3.5-defiant.gguf</code> ذخیره کنید.</p>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="step-number">۲</span>
|
||||
<strong>ایجاد فایل Modelfile:</strong>
|
||||
<p>یک فایل متنی بدون پسوند به نام <code>Modelfile</code> در کنار فایل GGUF ایجاد کنید و محتوای زیر را در آن قرار دهید:</p>
|
||||
<pre>FROM C:/Models/qwen3.5-defiant.gguf
|
||||
|
||||
# تنظیم پارامترهای بهینه برای استخراج محتوا
|
||||
PARAMETER temperature 0.2
|
||||
PARAMETER top_p 0.9
|
||||
PARAMETER num_ctx 8192
|
||||
|
||||
# تنظیم پرامپت سیستمی پیشفرض برای استخراج ساختاریافته
|
||||
SYSTEM """
|
||||
تو یک دستیار هوشمند و دقیق برای استخراج و تحلیل محتوای اسناد هستی.
|
||||
وظیفه تو خواندن متن ورودی، درک عمیق آن، و استخراج اطلاعات کلیدی به صورت ساختاریافته و دقیق است.
|
||||
همیشه به زبان فارسی روان و حرفهای پاسخ بده، مگر اینکه خلاف آن درخواست شود.
|
||||
"""</pre>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="step-number">۳</span>
|
||||
<strong>ایجاد مدل در Ollama:</strong>
|
||||
<p>ترمینال یا CMD را باز کرده و دستور زیر را اجرا کنید تا مدل با یک نام مستعار (Alias) کوتاه ثبت شود:</p>
|
||||
<pre>ollama create qwen3.5-defiant:9b -f Modelfile</pre>
|
||||
<p>سپس برای اطمینان از صحت نصب، دستور <code>ollama list</code> را اجرا کنید.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 3: Configuration -->
|
||||
<div class="section" id="step2">
|
||||
<h2>📝 گام ۳: پیکربندی مدل در appsettings.json</h2>
|
||||
<p>اکنون باید این مدل جدید را به لیست مدلهای مجاز در پیکربندی پروژه اضافه کنیم تا سرویسها بتوانند از آن استفاده کنند.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/appsettings.json</span>
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
"AiApiConfiguration": {
|
||||
"Models": [
|
||||
{
|
||||
"Name": "Gemma",
|
||||
"Url": "http://localhost:11434",
|
||||
"LLM": "gemma:2b",
|
||||
"Provider": "Ollama"
|
||||
},
|
||||
{
|
||||
"Name": "QwenDefiant",
|
||||
"Url": "http://localhost:11434",
|
||||
"LLM": "qwen3.5-defiant:9b",
|
||||
"Provider": "Ollama"
|
||||
}
|
||||
],
|
||||
"Prompts": [
|
||||
{
|
||||
"Name": "Introduction",
|
||||
"Template": "تو یک دستیار هوشمند مفید هستی."
|
||||
},
|
||||
{
|
||||
"Name": "DocumentExtraction",
|
||||
"Template": "متن زیر از یک فایل استخراج شده است. لطفاً آن را تحلیل کن و خروجی را دقیقاً در قالب JSON با کلیدهای زیر برگردان: {\"summary\": \"خلاصه ۳ خطی\", \"key_points\": [\"نکته ۱\", \"نکته ۲\"], \"entities\": {\"نام_اشخاص\": [], \"تاریخ_ها\": []}}. متن: {0}"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ نکته:</strong> استفاده از نام مستعار <code>qwen3.5-defiant:9b</code> در فیلد <code>LLM</code> باعث میشود کد شما تمیز و خوانا بماند.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 4: Extraction Service -->
|
||||
<div class="section" id="step3">
|
||||
<h2>🔧 گام ۴: ایجاد سرویس استخراج محتوا (Extraction Service)</h2>
|
||||
<p>ما یک سرویس اختصاصی ایجاد میکنیم که ترکیبی از <code>IFileContentExtractor</code> (برای خواندن فایل) و <code>IChatClient</code> (برای پردازش با Qwen) باشد.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-new">NEW</span>
|
||||
<span class="path">xAiApi/Interfaces/IDocumentExtractionService.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace xAiApi.Interfaces
|
||||
{
|
||||
public interface IDocumentExtractionService
|
||||
{
|
||||
/// <summary>
|
||||
/// استخراج و تحلیل هوشمند محتوای یک فایل
|
||||
/// </summary>
|
||||
Task<string> ExtractAndAnalyzeAsync(
|
||||
IFormFile file,
|
||||
string extractionPromptTemplate,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
}</pre>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-new">NEW</span>
|
||||
<span class="path">xAiApi/Providers/XDocumentExtractionService.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using xAiApi.Configurations;
|
||||
using xAiApi.Constants;
|
||||
using xAiApi.Extensions;
|
||||
using xAiApi.Interfaces;
|
||||
using xAiModels.Constants;
|
||||
using xAiModels.Extensions;
|
||||
using xAiService.Interfaces;
|
||||
using xExceptions.Constants;
|
||||
|
||||
namespace xAiApi.Providers
|
||||
{
|
||||
public class XDocumentExtractionService : IDocumentExtractionService
|
||||
{
|
||||
private readonly IFileContentExtractor _fileExtractor;
|
||||
private readonly XAiApiConfiguration _configuration;
|
||||
private readonly ILogger<XDocumentExtractionService> _logger;
|
||||
|
||||
public XDocumentExtractionService(
|
||||
IFileContentExtractor fileExtractor,
|
||||
XAiApiConfiguration configuration,
|
||||
ILogger<XDocumentExtractionService> logger)
|
||||
{
|
||||
_fileExtractor = fileExtractor;
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<string> ExtractAndAnalyzeAsync(
|
||||
IFormFile file,
|
||||
string extractionPromptTemplate,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// ۱. اعتبارسنجی فایل
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
XException.InvalidArgs.Throw("فایل نامعتبر است.");
|
||||
}
|
||||
|
||||
// ۲. استخراج متن خام از فایل
|
||||
string rawText;
|
||||
using (var stream = file.OpenReadStream())
|
||||
{
|
||||
if (_fileExtractor.CanExtract(file.ContentType))
|
||||
{
|
||||
rawText = await _fileExtractor.ExtractAsync(stream, file.ContentType, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
XException.InvalidData.Throw($"فرمت فایل {file.ContentType} پشتیبانی نمیشود.");
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rawText))
|
||||
{
|
||||
XException.InvalidData.Throw("محتوای استخراج شده از فایل خالی است.");
|
||||
}
|
||||
|
||||
// ۳. آمادهسازی پرامپت نهایی
|
||||
var finalPrompt = string.Format(extractionPromptTemplate, rawText);
|
||||
|
||||
// ۴. دریافت کلاینت مدل QwenDefiant از پیکربندی
|
||||
var modelDescriptor = _configuration.GetModel("QwenDefiant");
|
||||
if (!modelDescriptor.IsValid())
|
||||
{
|
||||
XException.InvalidConfiguration.Throw("مدل QwenDefiant در پیکربندی یافت نشد.");
|
||||
}
|
||||
|
||||
// ۵. ساخت ChatClient (با استفاده از منطق موجود در XAIServiceBase یا مستقیم)
|
||||
using var client = CreateChatClient(modelDescriptor);
|
||||
|
||||
// ۶. ارسال درخواست به مدل
|
||||
var history = new[]
|
||||
{
|
||||
new ChatMessage(ChatRole.System, "تو یک متخصص استخراج داده از اسناد هستی. فقط خروجی درخواست شده را تولید کن."),
|
||||
new ChatMessage(ChatRole.User, finalPrompt)
|
||||
};
|
||||
|
||||
var response = await client.GetResponseAsync(history, cancellationToken: cancellationToken);
|
||||
|
||||
if (!response.IsValid())
|
||||
{
|
||||
XException.ActionFailed.Throw("مدل هوش مصنوعی پاسخی معتبر تولید نکرد.");
|
||||
}
|
||||
|
||||
return response.Text;
|
||||
}
|
||||
|
||||
private IChatClient CreateChatClient(XAiModels.Models.XAiModelDescriptor descriptor)
|
||||
{
|
||||
// بازنویسی منطق ساخت کلاینت Ollama بر اساس معماری پروژه
|
||||
var httpClient = new HttpClient { BaseAddress = new Uri(descriptor.Url), Timeout = Timeout.InfiniteTimeSpan };
|
||||
var ollamaClient = new OllamaSharp.OllamaApiClient(httpClient, descriptor.LLM);
|
||||
|
||||
return new ChatClientBuilder(ollamaClient)
|
||||
.UseFunctionInvocation()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Section 5: Controller -->
|
||||
<div class="section" id="step4">
|
||||
<h2>🎮 گام ۵: ایجاد Controller اختصاصی</h2>
|
||||
<p>یک endpoint جدید برای دریافت فایل و بازگرداندن محتوای تحلیلشده ایجاد میکنیم.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-new">NEW</span>
|
||||
<span class="path">xAiApi/Controllers/DocumentExtractionController.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using xCommons.Configurations;
|
||||
using xCommons.Providers;
|
||||
using xIdentityService.Interfaces;
|
||||
using xAiApi.Interfaces;
|
||||
using xAiApi.Extensions;
|
||||
|
||||
namespace xAiApi.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
public class DocumentExtractionController : XBaseIdentityApiV1Controller
|
||||
{
|
||||
private readonly IDocumentExtractionService _extractionService;
|
||||
private readonly XAiApiConfiguration _configuration;
|
||||
|
||||
public DocumentExtractionController(
|
||||
ILogger<DocumentExtractionController> logger,
|
||||
XAppConfiguration appConfiguration,
|
||||
XValidationProvider validationProvider,
|
||||
IXIdentityProvider identityProvider,
|
||||
IDocumentExtractionService extractionService,
|
||||
XAiApiConfiguration configuration)
|
||||
: base(logger, appConfiguration, validationProvider, identityProvider)
|
||||
{
|
||||
_extractionService = extractionService;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// آپلود فایل و استخراج هوشمند محتوا با مدل Qwen3.5
|
||||
/// </summary>
|
||||
[HttpPost("Extract")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<ActionResult<object>> Extract(
|
||||
[FromForm] IFormFile file,
|
||||
[FromForm] string promptName = "DocumentExtraction",
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
ValidationProvider.NotNull(file, nameof(file));
|
||||
|
||||
// دریافت الگوی پرامپت از پیکربندی
|
||||
var promptTemplate = _configuration.GetPrompt(promptName);
|
||||
if (string.IsNullOrWhiteSpace(promptTemplate))
|
||||
{
|
||||
promptTemplate = "متن زیر را تحلیل و خلاصه کن: {0}";
|
||||
}
|
||||
|
||||
// فراخوانی سرویس استخراج
|
||||
var result = await _extractionService.ExtractAndAnalyzeAsync(
|
||||
file: file,
|
||||
extractionPromptTemplate: promptTemplate,
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
return Ok(new { success = true, data = result });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return GetExceptionActionResult(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Section 6: DI -->
|
||||
<div class="section" id="step5">
|
||||
<h2>🔗 گام ۶: ثبت وابستگیها (Dependency Injection)</h2>
|
||||
<p>سرویس جدید را در متد <code>ConfigureServices</code> فایل <code>Startup.cs</code> ثبت کنید.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/Startup.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// ... (ثبتهای قبلی)
|
||||
|
||||
// ثبت Extractor های فایل (اگر قبلاً ثبت نشدهاند)
|
||||
services.AddSingleton<IFileContentExtractor, PlainTextContentExtractor>();
|
||||
services.AddSingleton<IFileContentExtractor, PdfContentExtractor>();
|
||||
services.AddSingleton<IFileContentExtractor, CompositeFileContentExtractor>();
|
||||
|
||||
// ✅ ثبت سرویس جدید استخراج محتوا
|
||||
services.AddScoped<IDocumentExtractionService, XDocumentExtractionService>();
|
||||
|
||||
// ... (بقیه کدها)
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Section 7: Flow -->
|
||||
<div class="section" id="flow">
|
||||
<h2>🔄 گام ۷: جریان کامل پردازش</h2>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">📤 کلاینت: آپلود فایل</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🎮 DocumentExtractionController</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">📄 IFileContentExtractor (استخراج متن خام)</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🧠 Qwen3.5 GGUF (تحلیل و ساختارسازی)</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">✅ بازگرداندن JSON/متن تحلیلشده</span>
|
||||
</div>
|
||||
|
||||
<h3>نمونه درخواست (cURL):</h3>
|
||||
<pre>curl -X POST "http://localhost:5000/api/DocumentExtraction/Extract" \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-F "file=@/path/to/document.pdf" \
|
||||
-F "promptName=DocumentExtraction"</pre>
|
||||
</div>
|
||||
|
||||
<!-- Section 8: Tips -->
|
||||
<div class="section" id="tips">
|
||||
<h2>💡 گام ۸: نکات کلیدی و مهندسی پرامپت برای مدلهای GGUF</h2>
|
||||
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>🎯 پرامپتنویسی دقیق</h4>
|
||||
<p>مدلهای GGUF محلی به دستورالعملهای شفاف بسیار خوب پاسخ میدهند. در <code>appsettings.json</code> حتماً قالب خروجی (مثلاً JSON) را به صراحت مشخص کنید.</p>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>⚡ مدیریت Context Window</h4>
|
||||
<p>در <code>Modelfile</code> مقدار <code>num_ctx</code> را بر اساس حجم فایلهای شما تنظیم کنید (مثلاً 8192 یا 16384). اگر فایل بزرگ است، آن را به قطعات (Chunks) تقسیم کنید.</p>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🛡️ مدیریت خطا</h4>
|
||||
<p>همیشه احتمال خطای OOM (کمبود حافظه RAM/VRAM) در مدلهای 9B را در نظر بگیرید. لاگهای Ollama را برای پایش مصرف حافظه بررسی کنید.</p>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>🖼️ پشتیبانی از تصویر (Vision)</h4>
|
||||
<p>اگر این نسخه خاص از Qwen از ورودی تصویر پشتیبانی کند، میتوانید در <code>XDocumentExtractionService</code> به جای متن خام، فایل تصویر را به <code>DataContent</code> تبدیل و ارسال کنید.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ جمعبندی:</strong> با این طراحی، شما بدون تغییر در هسته اصلی <code>XAIServiceBase</code>، یک ماژول کاملاً ایزوله و قدرتمند برای استخراج محتوا با مدلهای محلی GGUF ایجاد کردهاید که کاملاً با معماری ماژولار شرکت <strong>فن آوران ساحر علم</strong> همخوانی دارد.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>👨💻 توسعهدهنده:</strong> هادی خزاعی اصل</p>
|
||||
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
|
||||
<p><strong>📅 تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</p>
|
||||
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
|
||||
🧠 طراحی سیستم استخراج محتوا با Qwen3.5 GGUF - تمامی حقوق محفوظ است
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,648 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>پیادهسازی استخراج محتوا با استفاده از xFileService در xAiApi - فن آوران ساحر علم</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e3a8a;
|
||||
--secondary: #3b82f6;
|
||||
--accent: #f59e0b;
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--warning: #f97316;
|
||||
--bg-light: #f8fafc;
|
||||
--bg-code: #1e293b;
|
||||
--text-dark: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: 'Tahoma', 'Segoe UI', sans-serif;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
|
||||
color: var(--text-dark);
|
||||
line-height: 1.8;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: white;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.header h1 { font-size: 2.2em; margin-bottom: 10px; position: relative; }
|
||||
.header .subtitle { font-size: 1.1em; opacity: 0.95; position: relative; }
|
||||
.meta-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: var(--bg-light);
|
||||
padding: 15px 30px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
|
||||
.meta-item strong { color: var(--primary); }
|
||||
.content { padding: 40px; }
|
||||
.section {
|
||||
margin-bottom: 35px;
|
||||
padding: 25px;
|
||||
background: var(--bg-light);
|
||||
border-radius: 12px;
|
||||
border-right: 5px solid var(--secondary);
|
||||
}
|
||||
.section h2 {
|
||||
color: var(--primary);
|
||||
font-size: 1.6em;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
|
||||
.step {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
.step-number {
|
||||
display: inline-block;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.arch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.arch-card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
border-top: 4px solid var(--secondary);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.arch-card:hover { transform: translateY(-5px); }
|
||||
.arch-card h4 { color: var(--primary); margin-bottom: 12px; font-size: 1.15em; }
|
||||
.arch-card ul { list-style: none; padding-right: 0; }
|
||||
.arch-card li { padding: 6px 0; padding-right: 20px; position: relative; font-size: 0.95em; }
|
||||
.arch-card li::before { content: '▸'; position: absolute; right: 0; color: var(--accent); font-weight: bold; }
|
||||
.tech-badge {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85em;
|
||||
margin: 3px;
|
||||
}
|
||||
.tech-badge.primary { background: var(--primary); }
|
||||
.tech-badge.success { background: var(--success); }
|
||||
.tech-badge.accent { background: var(--accent); }
|
||||
pre {
|
||||
background: var(--bg-code);
|
||||
color: #e2e8f0;
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
font-size: 0.85em;
|
||||
margin: 15px 0;
|
||||
border-right: 4px solid var(--accent);
|
||||
}
|
||||
code {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 0.9em;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
th { background: var(--primary); color: white; padding: 12px; text-align: right; font-weight: bold; }
|
||||
td { padding: 12px; border-bottom: 1px solid var(--border); }
|
||||
tr:hover { background: var(--bg-light); }
|
||||
.flow-diagram {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 10px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.flow-step {
|
||||
display: inline-block;
|
||||
background: var(--secondary);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.flow-arrow { display: inline-block; color: var(--accent); font-size: 1.5em; margin: 0 8px; vertical-align: middle; }
|
||||
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
|
||||
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
|
||||
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
|
||||
.alert-warning { background: #fef3c7; border-color: var(--accent); color: #92400e; }
|
||||
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; margin-top: 40px; }
|
||||
.footer p { margin: 5px 0; }
|
||||
.highlight { background: linear-gradient(120deg, #fef3c7 0%, #fef3c7 100%); padding: 2px 6px; border-radius: 4px; font-weight: bold; }
|
||||
.toc { background: white; padding: 20px; border-radius: 10px; margin-bottom: 25px; border: 2px solid var(--border); }
|
||||
.toc h3 { color: var(--primary); margin-bottom: 15px; }
|
||||
.toc ol { padding-right: 25px; }
|
||||
.toc li { padding: 6px 0; }
|
||||
.toc a { color: var(--secondary); text-decoration: none; transition: color 0.2s; }
|
||||
.toc a:hover { color: var(--primary); text-decoration: underline; }
|
||||
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
|
||||
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
|
||||
.badge-new { background: var(--success); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>📎 پیادهسازی استخراج محتوا با ماژول xFileService</h1>
|
||||
<div class="subtitle">یکپارچهسازی XFileProvider در XAiServiceBase برای پردازش فایلهای ضمیمه</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-bar">
|
||||
<div class="meta-item">👨💻 <strong>توسعهدهنده:</strong> هادی خزاعی اصل</div>
|
||||
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
|
||||
<div class="meta-item">📅 <strong>تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</div>
|
||||
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="toc">
|
||||
<h3>📑 فهرست مطالب</h3>
|
||||
<ol>
|
||||
<li><a href="#strategy">استراتژی یکپارچهسازی</a></li>
|
||||
<li><a href="#step1">گام ۱: بهروزرسانی Controller برای آپلود فایل</a></li>
|
||||
<li><a href="#step2">گام ۲: تزریق IXFileProvider به XAiServiceBase</a></li>
|
||||
<li><a href="#step3">گام ۳: پیادهسازی منطق استخراج محتوا از فایل</a></li>
|
||||
<li><a href="#step4">گام ۴: الحاق محتوا به ChatMessage و ذخیره Metadata</a></li>
|
||||
<li><a href="#step5">گام ۵: بهروزرسانی Extension مدلها</a></li>
|
||||
<li><a href="#flow">جریان کامل دادهها</a></li>
|
||||
<li><a href="#summary">خلاصه تغییرات</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Section 1: Strategy -->
|
||||
<div class="section" id="strategy">
|
||||
<h2>🎯 گام ۱: استراتژی یکپارچهسازی</h2>
|
||||
<p>
|
||||
به جای مدیریت مستقیم <code>IFormFile</code> در لایه سرویس هوش مصنوعی، از معماری تمیز (Clean Architecture) پیروی میکنیم:
|
||||
</p>
|
||||
<div class="arch-grid">
|
||||
<div class="arch-card">
|
||||
<h4>۱. لایه ارائه (Controller)</h4>
|
||||
<ul>
|
||||
<li>دریافت <code>IFormFileCollection</code></li>
|
||||
<li>فراخوانی <code>IXFileProvider.Upload</code></li>
|
||||
<li>دریافت لیست <code>XFileDto</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>۲. لایه سرویس (XAiServiceBase)</h4>
|
||||
<ul>
|
||||
<li>دریافت <code>IEnumerable<XFileDto></code></li>
|
||||
<li>فراخوانی <code>IXFileProvider.GetFileDescriptor</code></li>
|
||||
<li>خواندن Stream و تبدیل به <code>AIContent</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="arch-card">
|
||||
<h4>۳. لایه داده (Metadata)</h4>
|
||||
<ul>
|
||||
<li>ذخیره <code>FileId</code> در فیلد <code>MetaDatas</code> پیام</li>
|
||||
<li>حفظ رابطه بدون نیاز به جدول Join جدید</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<div class="section" id="step1">
|
||||
<h2>🎮 گام ۲: بهروزرسانی Controller برای آپلود فایل</h2>
|
||||
<p>ابتدا باید فایلها را از طریق <code>XFileProvider</code> آپلود کنیم تا <code>XFileDto</code> دریافت شود.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/Controllers/XAiServiceControllerBase.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>// ۱. افزودن وابستگی به Constructor
|
||||
private readonly IXFileProvider _fileProvider;
|
||||
|
||||
protected XAiServiceControllerBase(
|
||||
// ... پارامترهای قبلی
|
||||
IXFileProvider fileProvider // ✅ جدید
|
||||
) : base(...)
|
||||
{
|
||||
// ...
|
||||
_fileProvider = fileProvider;
|
||||
}
|
||||
|
||||
// ۲. اصلاح متد Ask
|
||||
[HttpPost("Ask")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<ActionResult<string>> Ask(
|
||||
[FromForm] XAiResponseRequest request,
|
||||
[FromForm] IFormFileCollection files,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!request.IsValid()) XException.InvalidArgs.Throw();
|
||||
|
||||
var userInfo = await GetUserInfo();
|
||||
var connectionId = GetConnectionId();
|
||||
|
||||
// ✅ آپلود فایلها از طریق ماژول xFileService
|
||||
IEnumerable<XFileDto> uploadedFiles = null;
|
||||
if (files != null && files.Any())
|
||||
{
|
||||
uploadedFiles = await _fileProvider.Upload(
|
||||
files: files,
|
||||
userInfo: userInfo,
|
||||
connectionId: connectionId,
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ ارسال XFileDto به سرویس هوش مصنوعی
|
||||
var result = await aiService.AskAsync(
|
||||
prompt: request.Prompt,
|
||||
ownerId: userInfo.UserId,
|
||||
projectId: request.ProjectId,
|
||||
conversationId: request.ConversationId,
|
||||
connectionId: connectionId,
|
||||
attachedFiles: uploadedFiles, // ✅ تغییر نوع پارامتر
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return GetExceptionActionResult(ex);
|
||||
}
|
||||
}</pre>
|
||||
<div class="alert alert-info">
|
||||
<strong>💡 نکته:</strong> همین تغییر باید برای متد <code>AskStream</code> نیز اعمال شود.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<div class="section" id="step2">
|
||||
<h2>⚙️ گام ۳: تزریق IXFileProvider به XAiServiceBase</h2>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/Providers/XAIServiceBase.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>// ۱. افزودن فیلد و تزریق در Constructor
|
||||
private readonly IXFileProvider _fileProvider;
|
||||
|
||||
protected XAIServiceBase(
|
||||
IXAiDataProvider dataProvider,
|
||||
ILogger<XAIServiceBase> logger,
|
||||
XAiApiConfiguration configuration,
|
||||
XValidationProvider validationProvider,
|
||||
IXFileProvider fileProvider, // ✅ جدید
|
||||
string model = null
|
||||
)
|
||||
{
|
||||
this.dataProvider = dataProvider;
|
||||
this.logger = logger;
|
||||
this.configuration = configuration;
|
||||
this.validationProvider = validationProvider;
|
||||
this._fileProvider = fileProvider; // ✅ مقداردهی
|
||||
|
||||
Descriptor = configuration.GetModel(model);
|
||||
Options = new ChatOptions();
|
||||
}
|
||||
|
||||
// ۲. بهروزرسانی امضای متد AskAsync در Interface و Implementation
|
||||
public async Task<XAiMessageDto> AskAsync(
|
||||
string prompt,
|
||||
string ownerId,
|
||||
Guid projectId,
|
||||
Guid conversationId,
|
||||
string connectionId = null,
|
||||
IEnumerable<XFileDto> attachedFiles = null, // ✅ تغییر از IFormFileCollection
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// ... (کدهای اعتبارسنجی و دریافت Project/Conversation)
|
||||
|
||||
// ✅ پردازش فایلهای ضمیمه
|
||||
var fileContents = await ProcessAttachedFilesAsync(attachedFiles, cancellationToken);
|
||||
|
||||
// ... (ساخت promptMessage)
|
||||
|
||||
// ✅ ذخیره ارجاع فایلها در MetaDatas پیام
|
||||
if (attachedFiles != null && attachedFiles.Any())
|
||||
{
|
||||
var fileIds = attachedFiles.Select(f => f.Id).ToList();
|
||||
promptMessage.MetaDatas = new Dictionary<string, object>
|
||||
{
|
||||
{ "AttachedFileIds", fileIds }
|
||||
}.ToJSON();
|
||||
}
|
||||
|
||||
promptMessage = await dataProvider.AddMessage(...);
|
||||
|
||||
// ✅ الحاق محتوا به ChatMessage
|
||||
var promptChatMessage = promptMessage.ToChatMessages(fileContents);
|
||||
|
||||
var answer = await AskLLMAsync(history: history, prompt: promptChatMessage, cancellationToken: cancellationToken);
|
||||
|
||||
// ... (ذخیره پاسخ و بازگشت نتیجه)
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<div class="section" id="step3">
|
||||
<h2>🔍 گام ۴: پیادهسازی منطق استخراج محتوا از فایل</h2>
|
||||
<p>این متد کمکی درون <code>XAiServiceBase</code> مسئول خواندن فایل از طریق <code>XFileProvider</code> و تبدیل آن به فرمت قابل فهم برای LLM است.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-new">NEW METHOD</span>
|
||||
<span class="path">xAiApi/Providers/XAIServiceBase.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>/// <summary>
|
||||
/// پردازش فایلهای ضمیمه و تبدیل به AIContent
|
||||
/// </summary>
|
||||
private async Task<IList<AIContent>> ProcessAttachedFilesAsync(
|
||||
IEnumerable<XFileDto> files,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var contents = new List<AIContent>();
|
||||
|
||||
if (files == null || !files.Any())
|
||||
{
|
||||
return contents;
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ دریافت استریم فایل از ماژول xFileService
|
||||
var descriptor = await _fileProvider.GetFileDescriptor(
|
||||
id: file.Id,
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
if (descriptor == null || descriptor.Stream == null)
|
||||
{
|
||||
logger.LogWarning("فایل {FileName} یافت نشد یا قابل خواندن نیست.", file.FileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ✅ تشخیص نوع فایل و پردازش مناسب
|
||||
if (file.Type == XFileType.Image || descriptor.MIMEType.StartsWith("image/"))
|
||||
{
|
||||
// برای مدلهای Vision: ارسال به صورت DataContent
|
||||
using var memoryStream = new MemoryStream();
|
||||
await descriptor.Stream.CopyToAsync(memoryStream, cancellationToken);
|
||||
contents.Add(new DataContent(memoryStream.ToArray(), descriptor.MIMEType));
|
||||
}
|
||||
else
|
||||
{
|
||||
// برای فایلهای متنی: خواندن محتوا و الحاق به Prompt
|
||||
using var reader = new StreamReader(descriptor.Stream);
|
||||
var textContent = await reader.ReadToEndAsync(cancellationToken);
|
||||
|
||||
// قالببندی برای درک بهتر مدل از منبع متن
|
||||
var formattedText = $"[File: {file.FileName} (Type: {descriptor.MIMEType})]\n{textContent}\n[/File]";
|
||||
contents.Add(new TextContent(formattedText));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "خطا در پردازش فایل ضمیمه: {FileName}", file.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}</pre>
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ مزیت:</strong> با استفاده از <code>GetFileDescriptor</code>، ماژول هوش مصنوعی نیازی به دانستن جزئیات سیستم فایل (File System) ندارد و کاملاً از <code>xFileService</code> انتزاع یافته است.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4 -->
|
||||
<div class="section" id="step4">
|
||||
<h2>🔌 گام ۵: بهروزرسانی Extension مدلها</h2>
|
||||
<p>متد <code>ToChatMessages</code> باید بتواند محتوای استخراج شده از فایلها را در کنار متن اصلی پیام قرار دهد.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiModels/Extensions/XAiModelsExtensions.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>public static ChatMessage ToChatMessages(
|
||||
this XAiMessageDto source,
|
||||
IList<AIContent> additionalContents = null // ✅ پارامتر جدید
|
||||
)
|
||||
{
|
||||
ChatMessage result = null;
|
||||
|
||||
if (!source.IsNullOrDefault())
|
||||
{
|
||||
var contents = new List<AIContent>();
|
||||
|
||||
// ۱. افزودن متن اصلی پیام (Prompt کاربر)
|
||||
if (!string.IsNullOrWhiteSpace(source.Content))
|
||||
{
|
||||
contents.Add(new TextContent(source.Content));
|
||||
}
|
||||
|
||||
// ۲. ✅ افزودن محتوای استخراج شده از فایلها
|
||||
if (additionalContents != null && additionalContents.Any())
|
||||
{
|
||||
contents.AddRange(additionalContents);
|
||||
}
|
||||
|
||||
result = new ChatMessage
|
||||
{
|
||||
AuthorName = source.Role == XAiChatRole.User && !source.Owner.IsNullOrDefault()
|
||||
? source.Owner.GetFullname()
|
||||
: string.Empty,
|
||||
Role = source.Role.ToChatRole(),
|
||||
MessageId = source.Id.ToString(),
|
||||
Contents = contents // ✅ لیست ترکیبی از متن و فایل
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<!-- Step 5 -->
|
||||
<div class="section" id="step5">
|
||||
<h2>🔗 گام ۶: ثبت وابستگیها (Dependency Injection)</h2>
|
||||
<p>اطمینان حاصل کنید که <code>IXFileProvider</code> در کانتینر DI ثبت شده است (که بر اساس فایلهای ارائه شده، قبلاً در <code>xFileService.DI.XDIHelperExtension</code> انجام شده است). فقط باید اطمینان حاصل کنیم که در <code>xAiApi</code> قابل تزریق است.</p>
|
||||
|
||||
<div class="file-change">
|
||||
<span class="badge-modify">MODIFY</span>
|
||||
<span class="path">xAiApi/Startup.cs</span>
|
||||
</div>
|
||||
|
||||
<pre>public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// ... (سایر ثبتها)
|
||||
|
||||
// ✅ اطمینان از ثبت سرویس فایل (اگر قبلاً در ماژول xFileService ثبت نشده، اینجا فراخوانی شود)
|
||||
// services.AddXFileService<XAiApiDbContext>(xDataService.Constants.XRepositoryType.EF);
|
||||
|
||||
// ✅ بهروزرسانی ثبت سرویسهای AI برای تزریق IXFileProvider
|
||||
// نکته: چون XAIServiceBase کلاس پایه است، باید در کلاسهای مشتق شده (مثل XDefaultAiService) تزریق شود.
|
||||
|
||||
// مثال برای XDefaultAiService:
|
||||
// services.AddScoped<IXDefaultAiService>(sp => new XDefaultAiService(
|
||||
// sp.GetRequiredService<IXAiDataProvider>(),
|
||||
// sp.GetRequiredService<ILogger<XDefaultAiService>>(),
|
||||
// sp.GetRequiredService<XAiApiConfiguration>(),
|
||||
// sp.GetRequiredService<XValidationProvider>(),
|
||||
// sp.GetRequiredService<IXFileProvider>() // ✅ تزریق جدید
|
||||
// ));
|
||||
}</pre>
|
||||
<div class="alert alert-warning">
|
||||
<strong>⚠️ توجه:</strong> اگر <code>XAiServiceBase</code> را مستقیماً ثبت نمیکنید و از کلاسهای مشتق شده استفاده میکنید، باید Constructor آن کلاسها را نیز برای پذیرش <code>IXFileProvider</code> و پاس دادن آن به <code>base(...)</code> بهروزرسانی کنید.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flow -->
|
||||
<div class="section" id="flow">
|
||||
<h2>🔄 گام ۷: جریان کامل پردازش</h2>
|
||||
<div class="flow-diagram">
|
||||
<span class="flow-step">📤 کلاینت: ارسال Form (Prompt + Files)</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🎮 Controller: فراخوانی IXFileProvider.Upload</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">💾 xFileService: ذخیره فیزیکی و ثبت در DB</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">⚙️ XAiServiceBase: دریافت List<XFileDto></span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🔍 XAiServiceBase: فراخوانی GetFileDescriptor</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">📝 تبدیل Stream به TextContent/DataContent</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">🤖 ارسال ChatMessage (Text + Files) به LLM</span>
|
||||
<span class="flow-arrow">→</span>
|
||||
<span class="flow-step">💾 ذخیره Message با FileIds در MetaDatas</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="section" id="summary">
|
||||
<h2>📋 گام ۸: خلاصه تغییرات</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ردیف</th>
|
||||
<th>فایل / ماژول</th>
|
||||
<th>نوع تغییر</th>
|
||||
<th>توضیح</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>۱</td>
|
||||
<td><code>xAiApi/Controllers/XAiServiceControllerBase.cs</code></td>
|
||||
<td><span class="badge-modify">MODIFY</span></td>
|
||||
<td>افزودن <code>IXFileProvider</code> و فراخوانی <code>Upload</code> قبل از سرویس AI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>۲</td>
|
||||
<td><code>xAiApi/Interfaces/IXAiServiceBase.cs</code></td>
|
||||
<td><span class="badge-modify">MODIFY</span></td>
|
||||
<td>تغییر پارامتر <code>files</code> از <code>IFormFileCollection</code> به <code>IEnumerable<XFileDto></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>۳</td>
|
||||
<td><code>xAiApi/Providers/XAIServiceBase.cs</code></td>
|
||||
<td><span class="badge-modify">MODIFY</span></td>
|
||||
<td>تزریق <code>IXFileProvider</code> و افزودن متد <code>ProcessAttachedFilesAsync</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>۴</td>
|
||||
<td><code>xAiModels/Extensions/XAiModelsExtensions.cs</code></td>
|
||||
<td><span class="badge-modify">MODIFY</span></td>
|
||||
<td>پشتیبانی <code>ToChatMessages</code> از <code>additionalContents</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>۵</td>
|
||||
<td><code>xAiApi/Providers/XDefaultAiService.cs</code> (و سایر مشتقها)</td>
|
||||
<td><span class="badge-modify">MODIFY</span></td>
|
||||
<td>بهروزرسانی Constructor برای پاس دادن <code>IXFileProvider</code> به کلاس پایه</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<strong>✅ دستاوردهای این طراحی:</strong>
|
||||
<ul style="padding-right: 25px; margin-top: 10px;">
|
||||
<li>🛡️ <strong>جداسازی مسئولیتها:</strong> ماژول AI دیگر درگیر آپلود یا مدیریت فایل فیزیکی نیست.</li>
|
||||
<li>♻️ <strong>استفاده مجدد:</strong> از تمام قابلیتهای <code>xFileService</code> (مانند Thumbnail، References، و Storage) بهره میبریم.</li>
|
||||
<li>🔗 <strong>ردیابیپذیری:</strong> با ذخیره <code>FileId</code> در <code>MetaDatas</code>، همیشه میتوانیم بفهمیم کدام فایلها به کدام پیام متصل بودهاند.</li>
|
||||
<li>🎨 <strong>پشتیبانی چندوجهی (Multi-modal):</strong> آمادهسازی برای ارسال تصاویر به صورت <code>DataContent</code> به مدلهای Vision.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>👨💻 توسعهدهنده:</strong> هادی خزاعی اصل</p>
|
||||
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
|
||||
<p><strong>📅 تاریخ تهیه مستند:</strong> چهارشنبه ۸ مهر ۱۴۰۵</p>
|
||||
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
|
||||
📎 پیادهسازی استخراج محتوا با ماژول xFileService - تمامی حقوق محفوظ است
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user