<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های هادی</title>
        <link>https://virgool.io/feed/@hadi_hnp</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-06-16 13:29:22</pubDate>
        <image>
            <url>https://static.virgool.io/images/default-avatar.jpg</url>
            <title>هادی</title>
            <link>https://virgool.io/@hadi_hnp</link>
        </image>

                    <item>
                <title>بررسی روش های اجرای مدل های زبانی به صورت Local</title>
                <link>https://virgool.io/@hadi_hnp/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B1%D9%88%D8%B4-%D9%87%D8%A7%DB%8C-%D8%A7%D8%AC%D8%B1%D8%A7%DB%8C-%D9%85%D8%AF%D9%84-%D9%87%D8%A7%DB%8C-%D8%B2%D8%A8%D8%A7%D9%86%DB%8C-%D8%A8%D9%87-%D8%B5%D9%88%D8%B1%D8%AA-local-r0ott6wa1qfi</link>
                <description>در این مقاله قصد داریم روش های های مختلف اجرای LLM بصورت Local را بطور کلی بررسی کنیم.لازم به ذکر است در این مقاله برای پیاده سازی از ویندوز استفاده شده است.در ابتدا به بررسی برخی اصطلاحات و ابزارها میپردازیم و سپس در ادامه چند نمونه را اجرا میکنیم. مدل های زبانی یا LLMLLM یا Large Language Model یک نوع مدل از هوش مصنوعی است که با استفاده از اطلاعات بسیار زیاد آموزش دیده و قابلیت درک و تولید زبان انسان را دارد. معروف ترین فرمت های مدل های زبانی فرمت GGUF: این فرمت یکی از رایج ترین فرمت های حال حاضر است که برای دستگاه ها با توان پایین و همچنین اجرا بر روی CPU بهینه شده است. در واقع این فرمت کوانتیزه شده و برای اجرای سریع تر میباشد. این مدل بر روی GPU نیز قابل اجراست اما برای بهره وری کامل و استفاده کامل از GPU بهتر است سراغ فرمت های دیگر برویم.فرمت pt: فرمت خروجی مدل ساخته شده با استفاده از PyTorch میباشد ولی برای اجرای عملیاتی مناسب نیست و شاید امن نباشد.فرمت pb و ckpt: فرمت خروجی مدل ساخته شده با استفاده از TensorFlow میابشد که ckpt برای آموزش مدل و .pb برای خروجی نهایی مدل در محیط عملیاتی استفاده میشود.فرمت safetensors: یک فرمت مدرن برای مدل های زبانی میباشد که قابلیت اجرای مستقیم و امن بر روی GPU را دارد.  این فرمت معمولا از روی فایل های pt ساخته میشود.فرمت onnx: بهترین فرمت برای اجرا در محیط های عملیاتی میباشد. این فرمت مستقل از ابزار توسعه بوده و سایر فرمت های PyTorch و TensorFlow قابل تبدیل به این فرمت هستند. اما باید توجه کرد که TensorFlow یک اکوسیستم کامل دارد و راه بهینه ای برای اجرا در محیط عملیاتی در دسترس قرار میدهد. ابزارها و برنامه هاCUDA:CUDA یا Compute Unified Device Architecture یک پلتفرم توسعه داده شده توسط انودیا برای اجرای موازی پردازش ها بر روی GPU های این شرکت میباشد. با استفاده از این قابلیت میتوان پردازش های مورد نیاز را بطور مستقیم بر روی GPU اجرا کرد. معادل این ابزار برای GPU های AMD با نام ROCm عرضه شده اما در حال حاضر نسبت به CUDA قابلیت های محدودتری دارد و در حال پیشرفت است. برای اجرای مدل زبانی بر روی GPU انودیا ضروری است که GPU از این امکان پشتیبانی کند و ورژن مناسب CUDA و همچنین نسخه درایور متناظر با ورژن CUDA نصب گردد. llama.cpp:این ابزار یکی از مهم ترین و پرکاربردترین ابزارهای حال حاضر برای اجرای مدل های زبانی با فرمت GGUF است. این ابزار قابلیت اجرای فرمت ها GGUF را با کمترین منابع بر روی CPU یا GPU فراهم میکند.  Llama.cpp با زبان c++ نوشته شده و برای اجرا باید بر روی سیستم مورد نظر کامپایل شود. همچنین در صورت نیاز به اجرا بر روی GPU باید حین کامپایل پارامتر مورد نظر فعال گردد. Ollama:یکی دیگر از ابزارها Ollama نام دارد که در قالب یک برنامه کامل به کاربر امکان دانلود و اجرای مدل های زبانی را بدون پیچیدگی میدهد. این ابزار در پس زمینه از llama.cpp برای اجرای مدل های زبانی استفاده میکند به همین دلیل قابلیت اجرای مدل های کوانتیزه شده GGUF را دارد. در صورت نصب ورژن مناسب CUDA و پشتیبانی GPU، این ابزار مدل را بر روی GPU اجرا میکند(باید آخرین نسخه Ollama بر روی سیستم نصب باشد و همچنین از مدل مناسب برای GPU استفاده گردد تا این ابزار برای اجرا از GPU استفاده کند). PyTorch:یک Framework محبوب برای توسعه و اجرای مدل های زبانی است و از فرمت های .bin , .pt, .safetensors پیشتبانی میکند. PyTorch یک ابزار قدرتمند میباشد که در کنار Transformers بهترین گزینه برای توسعه و آموزش(Train) مدل های زبانی است. از PyTorch برای اجرا در محیط های عملیاتی نیز استفاده میشود اما به دلیل احتمال استفاده غیربهینه از منابع و همچنین در صورت نیاز به پردازش های توزیع شده، ابزارهای مناسب تری وجود دارد یا اینکه باید در کنار ابزارهای دیگر مورد استفاده قرار گیرد.TorchServe:یک ابزار برای استقرار و استفاده از مدل های PyTorch در محیط عملیاتی میباشد. TensorFlow:TensorFlow نیاز همانند PyTorch یک Framework محبوب برای توسعه و اجرای مدل های زبانی است. نسبت به PyTorch پیچیدگی بیشتری دارد و برای مقیاس پذیری برای مثال در محیط کلود قابلیت های بیشتری ارائه میدهد. به دلیل وجود یک اکوسیستم بزرگ و بهینه برای اجرا در محیط های عملیاتی بیشتر مورد توجه است.TensorFlow Serving:یک ابزار برای استفاده از مدل های TensorFlow در محیط عملیاتی میباشد. ONNX Runtime:یک ابزار قدرتمند و cross-platform برای اجرای مدل های زبانی با فرمت onxx میباشد. همانطور که گفته شد میتوان مدل های آموزش داده شده PyTorch و TensorFlow را به این فرمت تبدیل کرد و بعد در محیط عملیاتی بطور بهینه بر روی سخت افزارهای مختلف اجرا کرد. علاوه بر ابزارهای گفته شده، برنامه ها و ابزارهای مختلف دیگری نیز وجود دارد که در آینده در این مقاله قرار خواهند گرفت.اجرای مدل های زبانیدر این مثال برای اجرای مدل بر روی GPU از سیستم عامل ویندوز و ورژن 11.8 CUDA استفاده شده و حداقل نسخه درایور نصب شده باید نسخه 522.05 باشد. علاوه بر این کارت گرافیک مورد نظر نیز باید پشتیبانی گردد. همچنین نسخه پایتونی که از CUDA 11.8 پیشتیبانی میکند نسخه 3.11.1 میباشد. توجه به نسخه بسیار حائز اهمیت است.برای مشاهده کارت گرافیک در ویندوز میتوان از دستور زیر استفاده کرد:wmic path win32_VideoController get name, driverversionهمچنین با استفاده از WIN+R و دستور dxdiag میتوان کارت گرافیک و ورژن درایور را مشاهده کرد.برای مشاهده ورژن و بررسی نصب CUDA میتوان از دستور زیر استفاده کرد:nvcc --versionمدل های مختلف را نیز میتوان از HuggingFace دانلود کرد.1-اجرا مدل های GGUF با استفاده از llama.cpp بطور مستقیم:با توجه به نیاز به کامپایل llama.cpp نیاز است Visual Studio را برای استفاده از Desktop Development c++ نصب کنیم.ابتدا llama.cpp را از لینک زیر دانلود میکنیمhttps://github.com/ggml-org/llama.cppسپس با استفاده از دستور زیر عملیات بیلد را انجام میدهیم:cd llama
cmake -B build
cmake --build build --config Releaseبرای استفاده از GPU در حین کامپایل باید فلگ DLLAMA_CUBLAS ست شود در غیر این صورت مدل بر روی CPU اجرا خواهد شد:cmake -DLLAMA_CUBLAS=onدر خروجی یک فایل با نام main.exe ایجاد میشود و براحتی میتوان مدل GGUF را بصورت زیر اجرا کرد:.\main.exe -m .\models\mistral-7b-instruct-v0.2.Q4_K_M.gguf -p &quot;Hello, How are you?&quot;2-اجرای مدل های GGUF در پایتون:برای اجرای آفلاین ابتدا باید پکیج های مورد نیاز را دانلود کرد:mkdir offline_for_llama

# download llama-cpp for python
pip download llama-cpp-python -d offline_for_llama
pip download &quot;scikit-build-core[pyproject]&gt;=0.9.2&quot; -d offline_for_llama
pip download &quot;cmake&gt;=3.21&quot; -d offline_for_llama
سپس در سیستم مقصد نصب میکنیم(در صورت عدم نیاز به انتقال فایل به سیستم افلاین میتوانیم مستقیم بدون دانلود با دسترسی به اینترنت پکیج ها را نصب کنیم):pip install --no-index --find-links=offline_for_llama llama-cpp-pythonپس از نصب با یک نمونه کد ساده پایتون میتوان مدل را اجرا کرد:from llama_cpp import Llama
def test_llama():
    
    model_path = &quot;.\mistral-7b-instruct-v0.2.Q4_K_M.gguf&quot;

   
    llama = Llama(model_path=model_path)


    prompt = &quot;Hello, how are you?&quot;
    
    output = llama(prompt=prompt, max_tokens=50)

    print(prompt)
    print(&quot;Output:\n&quot;, output[&#039;choices&#039;][0][&#039;text&#039;])

if __name__ == &quot;__main__&quot;:
    test_llama()3-اجرای مدل های safetensor با استفاده از PyTorch:ابتدا باید مدل مورد نظر را دانلود کرد. برای راحتی میتوان با اسکریپت زیر و وارد کردن نام مدل، پکیج کامل مدل را دانلود کرد:import os
os.environ[&quot;TRANSFORMERS_NO_FLASH_ATTN&quot;] = &quot;1&quot;

from transformers import GPT2LMHeadModel, GPT2Tokenizer

model_name = &quot;gpt2&quot;
save_dir = &quot;./offline_package_gpt2&quot;


model = GPT2LMHeadModel.from_pretrained(model_name)
model.save_pretrained(save_dir)


tokenizer = GPT2Tokenizer.from_pretrained(model_name)
tokenizer.save_pretrained(save_dir)سپس باید کتابخانه های مورد نیاز برای استفاده از PyTorch را دانلود و نصب کرد(ورژن torch باید با ورژن CUDA مطابقت داشته باشد همچنین مانند قبل در صورت عدم نیاز به دانلود و انتقال به سیستم افلاین، میتوان مستقیم پکیج ها را نصب کرد):mkdir offline_packages
pip download torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 -d offline_packages
pip download transformers tokenizers accelerate --no-deps -d offline_packages
pip download regex tqdm requests numpy --dest offline_packages
pip download &quot;huggingface-hub&lt;1.0,&gt;=0.16.4&quot; -d offline_packages
pip download &quot;safetensors&gt;=0.4.3&quot; -d offline_packages
pip download psutil -d offline_packages


pip install --no-index --find-links=. torch torchvision torchaudio
pip install --no-index --find-links=offline_packages regex tqdm requests numpy
pip install --no-index --find-links=offline_packages tokenizers
pip install --no-index --find-links=offline_packages transformers accelerateبرای اطمینان از نصب PyTorch و همچنین دسترسی به CUDA برای اجرا بر روی GPU باید از اسکریپت زیر استفاده کرد:import torch
# test -&gt; pytorch installed
print(&quot; PyTorch version:&quot;, torch.__version__)
# test -&gt; cuda installed
print(&quot; CUDA available:&quot;, torch.cuda.is_available())

# test -&gt; pytorch detect gpu and can access to gput
if torch.cuda.is_available():
    print(&quot; GPU name:&quot;, torch.cuda.get_device_name(0))
    print(&quot; Current device:&quot;, torch.cuda.current_device())
    
    # run a simple task o gpu
    x = torch.rand(3, 3).cuda()
    print(&quot; Tensor on GPU:\n&quot;, x)
else:
    print(&quot; CUDA is NOT available. Using CPU.&quot;)
پس از اطمینان از نصب، برای اجرای مدل دانلود شده میتوان از کد زیر استفاده کرد:import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer


if not torch.cuda.is_available():
    raise RuntimeError(&quot;GPU not available.&quot;)

device = torch.device(&quot;cuda&quot;)
model_path = &quot;.\offline_package_gpt2&quot;


tokenizer = GPT2Tokenizer.from_pretrained(model_path)
model = GPT2LMHeadModel.from_pretrained(model_path).to(device)
model.eval&#40;&#41;


input_text = &quot;The meaning of life is&quot;
inputs = tokenizer(input_text, return_tensors=&quot;pt&quot;)
inputs = {k: v.to(device) for k, v in inputs.items()}

with torch.no_grad():
    output_ids = model.generate(
        **inputs,
        max_length=50,
        do_sample=True,
        temperature=0.8,
        top_p=0.95,
        num_return_sequences=1
    )

generated_text = tokenizer.decode(output_ids[0], skip_special_tokens=True)
print(generated_text)در این مقاله سعی شد بطور ساده برخی از مفاهیم و ابزارهای مورد نیاز برای اجرای مدل های زبانی بصورت لوکال توضیح داده شود و همچنین چند مثال کوتاه در این خصوص زده شد. در آینده این مقاله با روش ها و توضیحات بیشتر بروز خواهد شد!</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Mon, 07 Jul 2025 14:06:29 +0330</pubDate>
            </item>
                    <item>
                <title>هوش مصنوعی تولیدی(Generative AI)</title>
                <link>https://virgool.io/@hadi_hnp/%D9%87%D9%88%D8%B4-%D9%85%D8%B5%D9%86%D9%88%D8%B9%DB%8C-%D8%AA%D9%88%D9%84%DB%8C%D8%AF%DB%8Cgenerative-ai-afdy7jjquczm</link>
                <description>Generative AIدر این مقاله قصد داریم در مورد GenAI و قابلیت ها و محدودیت هاش صحبت کنیم.در ابتدا باید با مفهوم GenAI آشنا شویم. Generative AI یا هوش مصنوعی تولیدی، نوعی از هوش مصنوعی است که میتواند محتوای جدیدی را تولید کند. نمونه بارز این هوش مصنوعی ها، چت بات های هوشمند میباشند که با استفاده از LLM ها یک خروجی برای پرسش و درخواست کاربر آماده میکنند.با توجه به اینکه این مفهوم در دنیای نرم افزار جدید است و سرعت پیشرفت بالایی دارد و همچنین از مرحله Concept وارد مرحله عملیاتی شده، تکنیک ها و تکنولوژی های موجود به سرعت در حال تغییر و پیشرفت مطابق با آن میباشند.در ابتدا از حالت های ساده یک GenAI شروع میکنیم، مشکلات آن را بررسی میکنیم و راهکار مناسب را نیز شرح خواهیم داد.ساده ترین حالت ممکن ارسال مستقیم prompt به LLM و دریافت پاسخ از آن میباشد. در این روش هیچ کنترلی انجام نمیشود و LLM با توجه به اطلاعاتی که آموزش داده شده است پاسخ میدهد. پایگاه دانش LLM یک پایگاه دانش محدود است و قطعا تمام موضوعات را در بر نمیگیرد. همچنین میتوان گفت LLM یک پاسخ دهنده مغرور است و اگر موضوعی را نداند به آن اقرار نمیکند و ممکن است پاسخ اشتباهی را تولید کند.در نتیجه استفاده از LLM به تنهایی برای موارد خاص یا پایگاه دانش خصوصی نتیجه رضایت بخشی ندارد و باید با سایر روش ها ترکیب شود. قبل از شروع بیان راهکارها ابتدا باید چند مفهوم از جمله Eval ها(ارزیاب های LLM) و Embedding را توضیح داد.مفهوم Eval یا ارزیاب LLM: برای ارزیابی خروجی یک LLM و اینکه چه میزان این خروجی صحیح میباشد میتوان از راهکارهای زیر استفاده کرد:1- ارزیابی یک LLM توسط خود LLM: میتوان پرسش و پاسخ تولید شده را مجدد به صورت یکجا به خود LLM داد و در یک prompt از آن درخواست کرد صحت این پرسش و پاسخ را بررسی کند. این یک روش ساده است اما به شدت دارای ریسک است. مدل میتواند پاسخ اشتباه خود را تصدیق کند و یک پاسخ کاملا اشتباه تولید کند.2- بررسی خروجی توسط یک اپراتور: یک راهکار سنتی و همیشگی، بررسی خروجی توسط یک فرد مسلط به آن حیطه میباشد. برای حجم دیتا بالا قطعا این روش پاسخگو نیست اما میتوان با روش های دیگر آن را ترکیب کرد تا از خروجی اطمینان حاصل شود3- استفاده از یک LLM تصدیق گر: روش دیگر استفاده از یک LLM دیگر به عنوان تصدیق گر یا ارزیاب میباشد. برخی LLM ها به همین منظور trainمیشوند و مناسب این کار میباشند.مفهوم Embedding: در واقع LLM ها شبکه های عصبی ای هستند که شامل لایه های ورودی، لایه های پنهان و لایه های خروجی میباشند. یکی از لایه های ورودی Embedding نام دارد. در این بخش داده های ورودی به بردارهای عددی تبدیل میشوند و بعد به لایه های پنهان ارسال میشوند. در واقع LLM از داده های عددی برای بررسی ورودی و تولید پاسخ استفاده میکند. از Embedding برای یافتن و جستجو میان شباهت های داده های مختلف میتوان استفاده کرد. اما باید توجه داشت این روش برای داده های غیرساختار یافته مانند متن ها و فایل ها و تصاویر مناسب میباشد. برای داده های ساختار یافته مانند دیتابیس های SQL این روش به این صورت قابل استفاده نیست. مفهوم Vector Store  نیز به یک Storage برای داده های برداری اشاره دارد که امکان ذخیره سازی و جستجو بر طبق این نوع داده را فراهم میکند. با توجه به اینکه خود LLM به صورت ذاتی از Embedding استفاده میکند، برای جستجو نیز میتواند این موضوع یک روش مناسب باشد.تا اینجا با موضوع GenAI و Direct prompt و مفاهیم eval, embedding و vector store آشنا شدیم. حال به سراغ بهبود روش Direct Promptمیرویم.میتوانیم از دو روش RAG یا Fine tuning برای این موضوع استفاده کنیم.ابتدا میرسیم به RAG یا Retrieve augmented document به مفهومی اشاره دارد که همراه با Prompt کاربر مجموعه ای از داده های مرتبط دیگر نیز به LLM ارسال گردد تا به کمک آن پاسخ را تولید کند. متداول ترین روش این است که مجموعه ای از فایل ها برای مثال فایل های PDF را با استفاده از Embedding model به بردارهای عددی تبدیل کرده و در یک Vector storeذخیره کرد. زمان دریافت ورودی کاربر، این ورودی را نیز به یک بردار عددی تبدیل کرده و با استفاده از الگوریتم های ANN similarity search بر روی vector store، محتوای مرتبط را استخراج کرده و همراه ورودی کاربر به LLM ارسال کنیم. این روش بدون نیاز به دستکاری LLM قابلیت تولید پاسخ بر اساس پایگاه دانش خصوصی را دارد و یک روش به صرفه و بهینه میباشد. اما این روش محدودیت ها و مشکلاتی نیز همراه خود دارد.1- بازیابی اطلاعات نامناسب و غیربهینه از vector database: همانطور که گفته شد اطلاعات ابتدا تبدیل به داده های عددی شده و در vector database ذخیره میشود. شیوه انجام این عملیات به پارامترهای مختلفی به خصوص سایز chunk ها در embedding بستگی دارد. با جدا سازی تکه های مختلف یک محتوا شاید مفهوم معنایی آن نیز از بین رود. یک مثال خیلی ساده، شاید پاسخ یک پرسش در دو بخش جدا از هم embedding و ذخیره شود و از نظر معنایی و برداری یکی از این بخش ها ارتباط کمتری با موضوع داشته باشند و در نتیجه در خروجی ظاهر نشود. اما اگر این دو بخش بصورت یکجا و در یک بخش ذخیره میشدند نتیجه دقیقتری تولید میشد. یک راهکار برای حل این مشکل استفاده از روش های جستجوی دیگر در کنار این روش میباشد. برای مثال استفاده از روش مرسوم جستجوی keyword ها برای نمونه در Elasticsearch ، که داده ها در این حالت نیز باید در storage یا دیتابیس مخصوص ذخیره شوند. میتوان علاوه بر جستجوی برداری، جستجوی keyword را نیز انجام داد و مجموعه بزرگتری از اطلاعات را به LLM برای تولید پاسخ بهتر ارسال کرد. اما مشکلی که به وجود می آورد ارسال اطلاعات خیلی بیشتر به LLM میباشد که ممکن است چالش هایی ایجاد کند.2- پرسش غیر دقیق کاربر: هیچگاه کاربر به طور دقیق نمیتواند پرسش خود را مطرح کند به گونه ای که پرسش به بهترین شیوه بیان شده باشد. این موضوع در خروجی LLM تاثیر بسزایی دارد. یکی از راهکار استفاده از خود LLM برای بهبود پرسش کاربر است. میتوان از همان LLM یا یک LLM دیگر استفاده کرد و پرسش کاربر را به آن ارسال کرد و درخواست داد تا معادل های کامل تری برای این پرسش تولید کند، سپس آنها را برای تولید خروجی نهایی به LLM ارسال کرد. در این روش نیز دوبار از LLM استفاده شده است که موضوع performance باید مورد توجه قرار گیرد.3- عدم توانایی LLM در پردازش مناسب حجم زیاد دیتا ورودی: در صورتی که حجم دیتا ورودی به LLM برای پرسش زیاد باشد نتیجه ممکن است رضایت بخش نباشد. معمولا LLM ها اگر پاسخ در ابتدای محتوا یا در انتهای محتوا باشد پاسخ خوبی تولید میکنند اما اگر پاسخ اصلی در میانه محتوا باشد و ورودی نیز به شدت زیاد باشد، پاسخ رضایت بخشی تولید نخواهند کرد. برای حل این مشکل یکی از راه حل ها استفاده از Reranker ها میباشد. وظیفه Rerankerارزیابی و امتیازدهی به محتوای بازیابی شده از Vector database ها یا سایر سورس ها میباشد. Reranker از شبکه های عصبی عمیق و الگوریتم های خاص خود استفاده میکند و هر محتوای بازیابی شده و ارتباط آن با پرسش کاربر را امتیازدهی میکند. سپس میتوانیم محتوا با امتیاز پایینتر را از لیست حذف کنیم تا محتوای ارسال شده به LLMکاهش یابد. موضوع performance را نیز باید مورد توجه قرار داد.4- امنیت: یکی از مهم ترین مشکلات LLM ها بحث محدودسازی ها و امنیت و تعیین سطوح دسترسی به اطلاعات میباشد. میتوان به راحتی LLM را دور زد تا نکات امنیتی را مورد توجه قرار ندهد و هر نوع پرسشی را پاسخ دهد. یکی از راهکارها استفاده از Guardrail ها میباشد که در واقع یک LLM است که مخصوص این کار train داده شده و جلوی LLM اصلی قرار میگرد و ابتدا ورودی کاربر را بررسی میکند که آیا با قوانین داده شده مطابقت دارد یا خیر. در این روش نیز با توجه به استفاده از یک LLM دیگر باید performance را مورد توجه قرار داد.تا اینجا RAG و محدودیت ها و راهکارهای مقابله با محدودیت ها شرح داده شد. گاهی اوقات  پرسش کاربر در مورد یک موضوع خیلی خاص و پیشرفته است و ممکن است با استفاده از RAG نیز پاسخ مطلوب ایجاد نشود. در اینجا موضوع Fine Tune کردن LLM های از پیش آموزش داده شده مطرح میشود. برخی از LLM ها با در اختیار قرارداد یک API امکان fine tune کردن مدل یا تغییر پارامترهای محاسباتی شبکه عصبی را فراهم میکنند. اما باید دقت داشت این کار به توان سخت افزاری بالایی نیاز دارد و در خیلی موارد شاید به صرفه نباشد.و در آخر ساخت یک LLM اختصاصی! در اکثر موارد روش های بالا پاسخگو هستند و نیازی حتی به fine tune کردن نیز نمیباشد اما اگر تمام راهکار را امتحان کردید و باز هم پاسخ مطلوب تولید نشد باید سراغ یک LLM اختصاصی بروید!</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Tue, 22 Apr 2025 11:36:12 +0330</pubDate>
            </item>
                    <item>
                <title>ایجاد ارتباط بین SqlServer و Oracle</title>
                <link>https://virgool.io/@hadi_hnp/%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D8%A8%DB%8C%D9%86-sqlserver-%D9%88-oracle-uuvtuvvymstp</link>
                <description>تو این پست قصد داریم شیوه ارتباط بین دیتابیس های Oracle و SqlServer که رو ویندوز نصب هستن رو شرح بدیم.برقراری ارتباط از SqlServer به Oracleابتدا باید Oracle Database client رو از سایت اوراکل برای ویندوز دانلود کنیم. بعد از دانلود یک مسیر برای نصب مشخص میکنیم. برای مثال یک فولدر بنام Oracle در مسیر ProgramFile ایجاد میکنیم و فایل دانلودی رو اونجا extract میکنیم و یک فولدر خروجی به نام client ایجاد میشه.ساختار و مسیر فولدر(فولدرهای product و response پس از نصب و در مراحل بعدی ایجاد خواهد شد)فایل setup.exe رو به صورت run as admin اجرا میکنیم. نوع نصب Administrator انتخاب میکنیم تا تمامی ابزارها و سرویس های مورد نیاز نصب بشه.در مرحله بعد، برای راه اندازی سرویس و کانفیگ های مورد نیاز تعیین میکنیم از چه کاربری استفاده شه. گزینه Use Windows Built-in Account رو انتخاب میکنیم.در مرحله باید مسیر قرارگیری کانفیگ ها و همچنین Oracle Home رو برای نصب مشخص کنیم. از فولدر client که قبلا ساختیم استفاده میکنیم و مسیر رو مشخص میکنیم.در مرحله بعد خلاصه ای از اطلاعات نصب رو میبینم و گزینه install رو میزنیم تا پروسه نصب کامل بشه.نصب client اوراکل به پایان رسید. برای اطمینان از نصب ODBC Data Source  رو اجرا میکنیم و در بخش درایورها چک میکنیم درایور مربوط به اوراکل مطابق با شکل زیر اضافه شده باشه: حالا باید تنظیمات مربوط به سرویس برای ارتباط با دیتابیس اوراکل رو انجام بدیم. فرض میکنیم روی دیتابیس اوراکل یک سرویس بنام orclpdb ایجاد کردیم و میخوایم به اون متصل شیم.برای مثال تو تنظیمات فایل tnsnames.ora در دیتابیس اصلی اوراکل اینجور سرویسی تعریف شده:کانفیگ tnsnames بر روی سرور اصلی اوراکلباید مطابق با تنظیمات بالا، کلاینت نصب رو نیز تنظیم کنیم و tns مورد نیاز رو تعریف کنیم. به مسیر زیر میریم و یک فایل با عنوان tnsnames.ora ایجاد میکنیم:C:\Program Files\Oracle\client\product\21.0.0\client_1\network\adminمطابق با تنظیمات tns سرور اصلی اوراکل، تنظیمات رو وارد میکنیم و فایل رو ذخیره میکنیم(ipو port و service name مربوط به دیتابیس اوراکل رو وارد میکنیم).orclpdb =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.220.129)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orclpdb)
    )
  )تا اینجا تنظیمات مربوط به کلاینت اوراکل به اتمام رسید. SSMS رو اجرا میکنیم و به دیتابیس متصل میشیم.تو قسمت Server Objects -&gt; Linked Servers -&gt; Provider چک میکنیم Ora.OLEDB.Oracle وجود داشته باشه و با کلیک راست روش از قسمت Properties تیک گزینه Allow inprocess رو میزنیم.حالا باید یک لینک سرور ایجاد کنیم. روی Linked Servers کلیک راست میکنیم و گزینه New Linked Server رو میزنیم. تو بخش General با توجه به تنظیماتی که برای tns وارد کنیم موارد رو بصورت زیر پر میکنیم:در بخش Security نیز گزینه Be Made using this security context رو میزنیم و یوزر و پسورد رو برای اتصال وارد میکنیم.نکته: اگه حین ایجاد Linked Server به خطا خوردید یا با مشکل کرش کردن مواجه شدید یک دور سرویس SQL Server یا ویندوز باید restart شه. در صورتی که ارتباط شبکه برقرار باشه و tns مربوطه به درستی وارد شده باشه اما مجدد با خطای ORA 12638 مواجه شدید به مسیر زیر برید و کانفیگ SQLNET.AUTHENTICATION_SERVICES رو None قرار بدید:C:\Program Files\Oracle\client\product\21.0.0\client_1\network\admin\sqlnet.ora برقراری ارتباط از Oracle به SQLServerبا فرض اینکه دیتابیس اوراکل بر روی ویندوز نصب شده است، برای برقراری این ارتباط ابتدا باید یک ODBC Data Source بر روی این سرور ایجاد کنیم. ODBC Data Source رواجرا میکنیم و از قسمت System DSN بر روی Add کلیک میکنیم.درایور مربوط به SQL Server رو انتخاب میکنیم.در قسمت بعد علاوه بر وارد کردن نام دلخواه، IP مربوط به سروری که SQL Server روش اجرا میشه رو وارد میکنیم.در قسمت بعد برای Authentication گزینه With SQL Server authentication رو انتخاب میکنیم و یوزر و پسورد مورد نظر برای اتصال رو هم وارد میکنیم.در قسمت بعد default database رو انتخاب میکنیم و بعد Finish رو میزنیم.تو قسمت آخر Test Data Source رو میزنیم تا از صحیح بودن اتصال اطیمنان حاصل کنیم.تا اینجا Data Source آماده شده و باید از این Data Source برای اتصال اوراکل به SQL Server استفاده کنیم. اگه اوراکل روی لینوکس نصب شده باید یک ODBC Data Source روی لینوکس ایجاد کنیم.حالا باید تنظیمات مربوط به listener و tns رو برای ارتباط با Data Source انجام بدیم. ابتدا تو مسیر زیر(Oracle Home) یک فایل با عنوان initSQLSERVER1.ora ایجاد میکنیمC:\OracleDB\Base\homes\OraDB21Home1\hs\admin\initSQLSERVER1.ora و تنظیمات زیر رو اضافه میکنیم:HS_FDS_CONNECT_INFO = SQLSERVER1
HS_FDS_TRACE_LEVEL = 0بعد از میریم سراغ فایل tnsname.ora تو مسیر زیر:C:\OracleDB\Base\homes\OraDB21Home1\network\adminو tns مربوط به DataSource که تعریف کردیم رو به انتهای فایل اضافه میکنیم:SQLSERVER1 =
  (DESCRIPTION=
    (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
      (CONNECT_DATA=(SID=SQLSERVER1))
      (HS=OK)
    )سپس به فایل listener.ora یک SID جدید اضافه میکنیم.(SID_DESC=
  (SID_NAME=SQLSERVER1)
  (ORACLE_HOME=C:\OracleDB\Source)
  (PROGRAM=dg4odbc)
)فایل بعد از اضافه کردن تنظیمات بالا مطابق زیر خواهد بود:# listener.ora Network Configuration File: C:\OracleDB\Base\homes\OraDB21Home1\NETWORK\ADMIN\listener.ora
# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = CLRExtProc)
      (ORACLE_HOME = C:\OracleDB\Source)
      (PROGRAM = extproc)
      (ENVS = &amp;quotEXTPROC_DLLS=ONLY:C:\OracleDB\Source\bin\oraclr.dll&amp;quot)
    )


     (SID_DESC=
  	(SID_NAME=SQLSERVER1)
  	(ORACLE_HOME=C:\OracleDB\Source)
  	(PROGRAM=dg4odbc)
     )

  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.220.129)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
	(DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
      
    )
  )حال باید سرویس مربوط به listener را restart کنیم.در آخر از طریق Oracle Sql Developer بعد از لاگین از قسمت Database Link یک لینک جدید ایجاد کرده و علاوه بر یوزر و پسورد در قسمت Service Name نام سرویس یا همان SQLSERVER1 را وارد میکنیم و تیک Public را میزنیم و در آخر گزینه اتصال را میزنیم.با دستور زیر نیز میتوان یک لینک اتصال ایجاد کرد:CREATE DATABASE LINK SQLSERVER1 CONNECT TO &amp;quotMyUser&amp;quot IDENTIFIED BY &amp;quotMyPassword&amp;quot USING &#039;SQLSERVER1&#039;;به صورت زیر نیز میتوانیم QUERY های مربوطه را اجرا کنیم:select * from dbo.DimCustomer@&amp;quotDATABASE_LINK1&amp;quot
</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Mon, 17 Feb 2025 12:36:04 +0330</pubDate>
            </item>
                    <item>
                <title>استفاده از NFC Reader در صفحه وب(Chrome Android)</title>
                <link>https://virgool.io/@hadi_hnp/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-nfc-reader-%D8%AF%D8%B1-%D8%B5%D9%81%D8%AD%D9%87-%D9%88%D8%A8chrome-android-wg1cbutaiiqb</link>
                <description>NFC Readerتو این پست قصد داریم نحوه استفاده از NFC Reader در نسخه اندروید مرورگر کروم  با استفاده از Javascript رو شرح بدیم.ابتدا یک صفحه وب ساده رو بستر nginx ارائه میدیم و با استفاده از مرورگر گوگل کروم در اندروید اطلاعات یک تگ nfc رو میخونیم.نسخه اندروید گوگل کروم از ورژن 89 به بعد در صورتی که صفحه وب رو بستر https ارائه بشه امکان استفاده از ابجکت NDEFReader برای کار با NFC رو به ما میده.پس ابتدا باید یک SSL/TLS Certificate برای استفاده در nginx بسازیم. با استفاده از openssl و دستور زیر یک Public Key و Private Key میسازیمopenssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout domain.key -out domain.crtیک سری پارامتر ورودی میخواد که جهت تست میتونیم از همون مقادیر پیش فرض استفاده کنیم. بعد از ساخت Certificate ها باید کانفیگ مربوط به nginx که شامل دو پارامتر ssl_certificate و ssl_certificate_key میشن رو ست کنیم. برای اینکار فایل nginx.conf رو باز میکینم و کانفیگ زیر رو اضافه میکنیم:# HTTPS server    
    server {
       listen       443 ssl;
       server_name  localhost;
       ssl_certificate      localhost.crt;
       ssl_certificate_key  localhost.key;
       location / {
           root   html;
           index  index.html index.htm;
       }
    }بعد از اون میریم سراغ ایجاد یک فایل html ساده که اطلاعات یک NFC Tag رو بخونیم و نمایش بدیم. تو html صرفا دو بخش برای وارد کردن متن ایجاد میکنیم و با استفاده از id بهشون مقدار بدیم.برای اینکار از ابجکت NDEFReader استفاده میکنیم. اول چک میکنیم که امکان استفاده از NDEFReader فعال باشه یعنی بستر HTTPS فراهم باشه و رو مرورگر کروم ورژن 89+ اجرا بشه. با استفاده از کد زیر و چک کردن NDEFReader اینکار انجام میشه:if (!(&amp;quotNDEFReader&amp;quot in window)) {
     document.getElementById(&#039;content1&#039;).innerHTML=&#039;can not access to nfc!&#039; + &#039;\n&#039;;
} else {
     document.getElementById(&#039;content1&#039;).innerHTML=&#039;nfc is ok!&#039; + &#039;\n&#039;;
}برای شروع خواندن NFC از یک دکمه با id=scanBt در صفحه استفاده میکنیم و بعد کلیک روی این دکمه یک EventListener به ابجکت NDEFReader  اضافه میکنیم که در صورت خوندن شدن NFC توسط گوشی یا گجت مربوطه این اطلاعات رو دریافت کنه و تو صفحه نمایش بده. کد به صورت زیر میشه:scanBt.addEventListener(&amp;quotclick&amp;quot, async () =&gt; {
     document.querySelector(&#039;#contentOut&#039;).textContent += &amp;quotscan button clicked, start scan..&amp;quot + &#039;\n&#039;;
     try {
          const ndef = new NDEFReader();
          await ndef.scan();
          document.querySelector(&#039;#contentOut&#039;).textContent += &amp;quotScan started&amp;quot + &#039;\n&#039;;
          ndef.addEventListener(&amp;quotreadingerror&amp;quot, () =&gt; {
               document.querySelector(&#039;#contentOut&#039;).textContent += &amp;quotcan not read data&amp;quot + &#039;\n&#039;;
          });
          ndef.addEventListener(&amp;quotreading&amp;quot, ({ message, serialNumber }) =&gt; {
                 document.querySelector(&#039;#contentOut&#039;).textContent += `&gt; Serial Number: ${serialNumber}` + &#039;\n&#039;;
                 document.querySelector(&#039;#contentOut&#039;).textContent += `&gt; Records: (${message.records.length})` + &#039;\n&#039;;
                 for(const record of message.records) {
                        switch(record.recordType) {
                                 case &#039;text&#039;:
                                           const textDecoder = new TextDecoder(record.encoding)
                                           document.querySelector(&#039;#contentOut&#039;).textContent += `&gt; content: ${textDecoder.decode(record.data)}` + &#039;\n&#039;;
                                              break;
                          }
                }
       });
       } catch (error) {
                document.querySelector(&#039;#contentOut&#039;).textContent += &amp;quoterror: &amp;quot + error + &#039;\n&#039;;
       }
});کد کامل رو تو این لینک میتونید ببینید.</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Wed, 12 Feb 2025 14:59:19 +0330</pubDate>
            </item>
                    <item>
                <title>نصب Oracle Apex بر روی ویندوز</title>
                <link>https://virgool.io/@hadi_hnp/%D9%86%D8%B5%D8%A8-oracle-apex-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-%D9%88%DB%8C%D9%86%D8%AF%D9%88%D8%B2-fb2ig6jrelh0</link>
                <description>تو این پست قصد داریم Oracle Apex رو بر روی ویندوز نصب کنیم. برای نصب از نسخه های زیر استفاده میکنیم:Oracle Database 23.1Apex 23.2Ords 23.4Java 21Apache Tomcat 9با یه سرچ ساده میتونید فایل دانلودی نسخه های بالا رو پیدا کنید و Oracle Database رو نصب کنید.نکته: هنگام نصب تو انتخاب پسورد از @ استفاده نکنید. اوراکل این کاراکتر رو به عنوان جدا کننده میشناسه و بعدها تو ورود احتمال داره به مشکل بر میخورید. اگه بعد از نصب با استفاده از SQL PLus با یوزر sys خواستید وارد بشید و با خطای ERROR: ORA-12154 مواجه شدید میتونید رو همون سیستمی که دیتابیس رو نصب کردید بصورت زیر وارد شید/ as sysdbaبا دستور بالا به عنوان sys وارد شدید و با دستور زیر میتونید پسورد یوزر sys رو تغییر بدیدALTER USER sys IDENTIFIED BY &quot;new_pass&quot;بعد از نصب دیتابیس ابتدا باید یک PDB رو برای نصب Apex مشخص کنید. هنگام نصب دیتابیس اوراکل، بطور پیش فرض یک PDB ساخته میشه(برای مثال orclpdb یا هر اسمی که حین نصب انتخاب کنید). هم میتونید از اون استفاده کنید هم میتونید یک PDB جدید ایجاد کنید. برای ایجاد یک PDB جدید دستور زیر رو اجرا میکنیمCreate pluggable database apexpdb 
Admin user apexpdb identified by apexpdb#123
Roles=(dba)
DEFAULT TABLESPACE apexpdbts
Datafile &#039;C:\OracleDB213\Base\oradata\ORCL\apexpdb\apexpdb01.dbf&#039; size 250M
autoextend on
FILE_NAME_CONVERT=(&#039;C:\OracleDB213\Base\oradata\ORCL\pdbseed&#039;,
 &#039;C:\OracleDB213\Base\oradata\ORCL\apexpdb&#039;);تو دستور بالا ابتدا نام PDB رو مشخص کردیم(apexpdb) بعد یوزر با دسترسی dba براش ساختیم(یوزر apexpdb با پسورد apexpdb#123) بعد از اون Table Space و مسیر ذخیره سازیش رو مشخص کردیم.در آخر با دستور زیر PDB رو باز میکنیمalter pluggable database apexpdb open;و با دستور زیر اتصالمون رو به PDB تغییر میدیمalter session set container=apexpdb;برای اطمینان از اتصال به PDB خروجی دستور زیر رو چک میکنیمshow con_name;تا اینجا کار مراحل ساخت PDB به پایان رسید حالا باید Apex رو بر روی PDB که ساخته شده نصب کنیم. برای نصب Apex نیازه یک Table Space برای APEX application user و یکی برای APEX files user و یکی هم برای Temp مشخص کنیم. میتونید با دستور زیر Table Space جدید بسازید:create tablespace apexdatafile datafile  &#039;C:\OracleDB213\Base\oradata\ORCL\apexpdb\apexdatafile.dbf&#039; size 200m autoextend onیا اینکه از همون Table Space های قبلی استفاده کنیم. من اینجا Table Space جدید ساختم. بعد از extract کردن فایل apex باید فایل apexins.sql رو اجرا کنید پارامترهای Table Space رو بهش بدیم:@apexins.sql apexdatafile apexdatafile TEMP /i/نصب Apex یه مقدار طول میکشه.بعد از نصب Apex چندتا اکانت ساخته میشه:APEX_230200:مربوط به Schema و متادیتا اپکسFLOWS_FILE:مربوط به فایل های اپلودی اپکسAPEX_PUBLIC_USER:برای Oracle Rest Data Service استفاده میشه که در ادامه باهاش کار داریم.حالا باید اسکریپت apxchpwd.sql رو برای ست کردن یوزرنیم و پسورد مربوط به internal workspace اجرا کنیم:@apxchpwd.sqlیوزرنیم و ایمیل رو میپرسه که دیفالتش Admin هست و بعد یک پسورد با پیچیدگی بالا میخواد.Enter the administrator&#039;s username [ADMIN]
User &quot;ADMIN&quot; does not yet exist and will be created.
Enter ADMIN&#039;s email [ADMIN]
Enter ADMIN&#039;s password []
Created instance administrator ADMIN.بعد از این مرحله باید یوزر زیر رو Unlock کنیم و یک پسورد براش ست کنیمALTER USER APEX_PUBLIC_USER ACCOUNT UNLOCK;
ALTER USER APEX_PUBLIC_USER IDENTIFIED BY Apex#123;بطور پیشفرض پسورد یوزر بالا بعد از 180 روز expire میشه. اگه میخوایم کلا expire نشه باید پارامتر PASSWORD_LIFE_TIME رو برای یوزر مربوطه روی unlimited بزاریم.create profile no_expire limit password_life_time unlimited;
alter user apex_public_user profile no_expire;حالا باید اسکریپت apex_rest_config.sql رو برای کانفیگ Restful WebServices اجرا کنیم. بعد از اجرا برای دو یوزر APEX_LISTENER و APEX_REST_PUBLIC_USER از ما پسورد میخواد که باید یک پسورد با پیچیدگی مناسب وارد کنیم. میتونیم برای این دو هم expire رو unlimit کنیمalter user apex_rest_public_user profile no_expire;
alter user apex_listener profile no_expire;پروفایل no_expre را برای یوزر زیر نیز ست میکنیم(این یوزر بعد از نصب ords ایجاد میشود):alter user ORDS_PUBLIC_USER profile no_expire;نصب Apex و کانفیگ های مربوط به دیتابیس تا اینجا به اتمام رسید. حالا باید بریم سراغ کانفیگ و راه اندازی Ords برای ارتباط با Apex.ابتدا جاوا رو نصب میکنیم. حین نصب باید تیک مربوط به ست کردن JAVA_HOME رو بزنیم. بعد از نصب CMD رو باز میکنیم و با اجرای دستور زیر و مشاهده ورژن نصب شده جاوا مطمئن میشیم که به درستی نصب شده.java --versionبرای نصب ords تو ورژن های قبلی با دستور java --jar فایل ords.war رو اجرا میکردیم. تو این نسخه هم میشه این کار رو انجام ولی اوراکل پیشنهاد داده از راه جدید نصب رو انجام بدیم.بعد از خارج کردن ords از حالت فشرده، داخل فولدر bin یک فایل بنام ords وجود داره که ما نصب رو باهاش انجام میدیم. داخل فولدر ords یک فولدر بنام config ایجاد میکنیم. این فولدر رو هر جای دیگه هم میتونیم ایجاد کنیم و در نهایت کانفیگ های ords اینجا ذخیره میشه. cmd رو در مسیر فولدر ords باز میکنیم و دستور زیر رو اجرا میکنیمbin\ords --config C:\ords\config installتوی حالت اول میایم ords رو بصورت standalone اجرا میکنیم. در این حالت نیازی به وب سرور نیست و بصورت داخلی از وب سرور jetty برای اجرا استفاده میکنه. بعد از اجرای دستور بالا یکسری اطلاعات از ما میخواد که با توجه به مراحل قبلی وارد میکنیم:Type of installation &gt; [2] Enter 
Database connection type &gt; [1] Enter
Host name &gt; [localhost] Enter
Listen port &gt; [1521] Enter
Service name &gt; apexpdb
Administrator username &gt; sys
Password &gt; 
Default tablespace &gt; [SYSAUX] Enter
Temp tablespace &gt; [TEMP] Enter
Additional features &gt; [1] Enter
Start ORDS in standalone mode &gt; [1] Enter
Protocol &gt; [HTTP] Enter
Port &gt; [8080] Enter
Static Resources &gt; C:\apex\images
آخرین پارامتری که از ما میخواد مسیر Static Resource های مربوط به apex هست که در فولدر images قرار داره و باید آدرسش رو بدیم(برای تامکت تو مرحله بعد میتونیم این فولدر رو داخل webapps قرار بدیم و عنوانش رو i بزاریم). در آخر ords روی پورت 8080 میاد بالا و میتونیم با وارد کردن آدرس http://localhost:8080 واردش بشیم. برای ورود به workspace عنوان رو internal میزنیم و یوزر admin و پسورد هم همون پسوردیه که با اجرای فایل apxchpwd.sql کردیم. با زدن crtl+c یا بستن cmd اجرای ords هم متوقف میشه. برای اجرای مجددش دستور زیر رو میتونیم استفاده کنیمbin\ords --config C:\ords\config serveتو محیط عملیاتی برای اینکه کنترل کاملتری داشته باشیم بهتره که ords رو روی یک وب سرور مثل tomcat اجرا کنیم. برای اینکار ابتدا tomcat رو نصب میکنیم(ولی اجرا نمیکنیم). فایل ords.war که فولدر ords وجود داره رو انتقال میدیم به مسیر نصب tomcat و در فولدر webapps کپی میکنیم. ords برای اجرا نیاز به دسترسی به فولدر کانفیگش داره. برای اینکه این دسترسی فراهم بشه باید یک سیستم Variable با مشخصات زیر بسازیم:Variable Name: JAVA_TOOL_OPTIONS
Variable Value: -Dconfig.url=C:\ords\configحالا فولدر images رو کپی میکنیم داخل فولدر webapps تاکمکت و عنوانش رو به i تغییر میدیم.بعد از این مرحله تامکت رو اجرا میکنیم و آدرس http://localhost:8080/ords رو توی مرورگر میزنیم و به ords دسترسی خواهیم داشت.نکته: اگه هنگام ورود به ords با خطایی مبنی نبودن تنظیمات database connection مواجه شدید به احتمال زیاد ords به فولدر کانفیگش دسترسی نداره. دقت کنید که JAVA_TOOL_OPTIONS به درستی ست شده باشه و در تنظیمات سطح دسترسی فولدر config به یوزر LOCAL SERVICE دسترسی فول بدید.اگه هنگام ایجاد و استفاده از Rest Service با خطای ACL مواجه شدید، ابتدا با اجرای دستور زیر یوزر Apex رو پیدا کنید:SELECT TABLE_OWNER FROM all_synonyms WHERE SYNONYM_NAME = &#039;WWV_FLOW&#039; and OWNER = &#039;PUBLIC&#039;و بعد اسکریپت زیر رو اجرا کنید(یوزر رو جایگزین کنید):DECLARE
  l_acl       VARCHAR2(100) := &#039;aclname.xml&#039;;
  l_desc      VARCHAR2(100) := &#039;description&#039;;
  l_principal VARCHAR2(30)  := &#039;APEX_23...&#039;;
  l_host      VARCHAR2(100) := &#039;localhost&#039;; --hostname
BEGIN
  dbms_network_acl_admin.create_acl(l_acl, l_desc, l_principal, TRUE, &#039;connect&#039;);
  dbms_network_acl_admin.add_privilege(l_acl, l_principal, TRUE, &#039;resolve&#039;);
  dbms_network_acl_admin.assign_acl(l_acl, l_host);
  COMMIT;
END;با اینکار محدودیت اتصال به Rest Web Service برای Apex برداشته میشه.نکته: اگه بعد از Restart کردن ویندوز Apex بالا نیومد، وضعیت PDB رو چک کنید اگه close بود اول با دستور زیر Open کنید:Alter pluggable database apexpdb open;و بعد با دستور زیر State رو ذخیره کنید:Alter pluggable database apexpdb save state;با اجرای دستور بالا هر بار بعد از Restart تو حالت Open ست میشه.نکته: در صورتی که پس از مدتی با خطای expire شدن یا نادرست بودن پسورد هنگام ورود به ords مواجه شدید ، باید ابتدا پسورد یوزر ORDS_PUBLIC_USER را آنلاک کرده و پسورد آن راتغییر دهید:ALTER USER ORDS_PUBLIC_USER ACCOUNT UNLOCK;
alter user ORDS_PUBLIC_USER identified by pass1;سپس در تنظیمات ords با روش زیر پسورد جدید را ست کنید:bin\ords --config C:\ords\config config secret db.passwordپس از اجرای کد بالا باید پسورد جدید را وارد کنید. در صورتی که چند pool برای دیتابیس داشتید(برای مثال با نام orclpool1) دستور به صورت زیر خواهد بود:bin\ords --config C:\ords\config config --db-pool orclpool1 secret db.passwordدر صورت حل نشدن مشکل و در صورتی که از نسخه های قدیمی تر ORDS استفاده میکنید با دستور زیر(با استفاده از کاربر sys در اسکیما خودتان) وضعیت یوزرهای APEX_PUBLIC_USER , APEX_LISTENER , APEX_REST_PUBLIC_USER را نیز بررسی کنید:
SELECT username, account_status FROM dba_users WHERE account_status LIKE &#039;%LOCK%&#039;;
معمولا یوزرهای زیر شاید قفل باشن که باید انلاک شه:
alter user APEX_LISTENER identified by &quot;abc123&quot; account unlock;
alter user APEX_PUBLIC_USER identified by &quot;abc123&quot; account unlock;
alter user APEX_REST_PUBLIC_USER identified by &quot;abc123&quot; account unlock;
alter user ORDS_PUBLIC_USER identified by &quot;abc123&quot; account unlock;نکته اگر بعد از نصب با خطایهایی مشابه عدم شناسایی Listener یا Open نبودن PDB مواجه شدید در صورتی که Listener درست است، باید از طریق SQL Plus به عنوان sys وارد شوید و در صورت idle بودن دیتابیس باید دیتابیس را startup کنید و بعد در صورت بسته بودن PDB ها آنها را باز کنید: / as sysdba
startup
SELECT pdb_name, status FROM dba_pdbs;
show pdbs;
 ALTER PLUGGABLE DATABASE ALL OPEN;نکته: شیوه حذف ACL ها در صورت نیاز:SELECT * FROM dba_network_acls;
SELECT * FROM dba_network_acl_privileges;

BEGIN
  FOR r IN (SELECT acl FROM dba_network_acls) LOOP
    BEGIN
      DBMS_OUTPUT.PUT_LINE(&#039;Deleting ACL: &#039; || r.acl);
      DBMS_NETWORK_ACL_ADMIN.drop_acl(acl =&gt; r.acl);
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE(&#039;Error deleting ACL &#039; || r.acl || &#039;: &#039; || SQLERRM);
    END;
  END LOOP;
END;
/
COMMIT;</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Thu, 22 Feb 2024 20:21:09 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی Spring RestClient</title>
                <link>https://virgool.io/@hadi_hnp/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-spring-restclient-i4us8rhcs4wz</link>
                <description>حتما تا حالا شده بخواید با API دیگه ای تو برنامتون ارتباط برقرار کنید و دنبال بهترین روش یا بهترین Library موجود بودید. اگه از Spring MVC استفاده میکنید با اولین جستجو به RestTemplate میرسید. ولی  میبینید که deprecate شده! بعد از معرفی Spring Webflux و رویکرد Reactive Programming دیگه RestTemplate به دلیل synchronous و بلوکه کردن Thread پاسخگو این بخش نبود. همین باعث شد که WebClient  با رویکرد fluent API توسعه پیدا کنه و بصورت non-blocking امکان ارتباط با API های دیگه رو بده. همچنین اگه از Spring MVC هم استفاده میکنید، میشه با اضافه کردن Spring Webflux به dependency ها و فراخونی تابع block() در انتها، بصورت synchronous از این کلاینت هم استفاده کرد. ولی خب اینکه یک dependency جدا به برنامه تون اضافه شه و با مفاهیم Reactive Programming مواجه بشید اون هم تو Spring MVC  شاید خیلی خوشایند نباشه.تو ورژن 6.1 Spring Framework کلاینت جدیدی با نام RestClient به عنوان synchronous HTTP client معرفی شد. RestClient هم مثل WebClient از fluent API استفاده میکنه و با فراخونی بصورت method chaining کار رو خیلی ساده میکنه و خوانایی کد رو بالا میبره. RestClient از HTTP interfaces هم پشتیبانی میکنه و کار رو به شدت راحت تر و خوانا تر کرده.بریم یه نمونه ساده استفاده از RestClient رو ببینیم.اول با استفاده از Spring Boot یک api ساده برای تست ایجاد میکنیم. این api شامل متودهای GET,POST,PUT,UPDATE,DELETE بر روی موجودیت Customer است که بصورت زیر تعریف شده:public class Customer {
    private Integer id;
    private String firstName;
    private String lastName;
    private List&lt;Address&gt; addressList;
}کلاس CustomerController هم بصورت زیر تعریف میکنیم:@RestController
@RequestMapping(&amp;quot/customers&amp;quot)
public class CustomerRestController {
    private final CustomerService customerService;
    public CustomerRestController(CustomerService customerService) {
        this.customerService = customerService;
    }
    @GetMapping()
    public List&lt;Customer&gt; getAllCustomers() {
        return this.customerService.getAllCustomers();
    }
    @GetMapping(&amp;quot{id}&amp;quot)
    public Customer getCustomerById(@PathVariable(&amp;quotid&amp;quot) int id) {
        return this.customerService.getCustomerById(id);
    }
    @PostMapping()
    public void addNewCustomer(@RequestBody Customer customer) {
        this.customerService.createCustomer(customer);
    }
    @PutMapping
    public void updateCustomer(@RequestBody Customer customer) {
        this.customerService.updateCustomer(customer);
    }
    @DeleteMapping(&amp;quot{id}&amp;quot)
    public void deleteCustomer(@PathVariable(&amp;quotid&amp;quot) int id) {
        this.customerService.deleteCustomer(id);
    }
}برای ایجاد احراز هویت از Spring Security استفاده میکنیم و بصورت ساده با استفاده http basic تنظیمات رو اعمال میکنیم:@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.withUsername(&amp;quotuser&amp;quot)
                .password(passwordEncoder.encode(&amp;quotuser&amp;quot))
                .roles(&amp;quotuser&amp;quot)
                .build();
        return new InMemoryUserDetailsManager(user);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -&gt; {
                    csrf.disable();
                })
                .cors(cors -&gt; cors.disable())
                .authorizeHttpRequests(auth -&gt; {
                    auth.requestMatchers(&amp;quot/sample/**&amp;quot).permitAll();
                    auth.anyRequest().authenticated();
                })
                .httpBasic(Customizer.withDefaults())
                .build();
    }
}کد کامل رو میتونید اینجا ببینید.یک سرویس جدید برای ارتباط با API میسازیم. برای ارتباط با API از RestClient استفاده میکنیم که با اضافه کردن Spring-Web-MVC به پروژه در دسترس قرار میگیره. تو مرحله قبل برای secure کردن endpoint ها از یورز و پسورد user استفاده کردیم و پورت هم روی 8081 گذاشتیم. برای راحتی و externalize کردن مقادیر میایم اون ها رو داخل فایل application.properties قرار میدیم و بعد در کدمون استفاده میکنیم:server.port=8083
application.api.url=http://localhost:8081/api
application.api.username=user
application.api.password=userبرای ساخت Bean از RestClient راه های مختلفی وجود داره که یکی از اون ها استفاده از builder هست. یک کلاس بنام RestClientConfig میسازیم و مقادیر رو از فایل application.properties میخونیم. همونطور که گفتیم RestClient از fluent api پشتیبانی میکنه و میتونیم توابع رو بصورت زنجیره ای فراخونی کنیم که خوانایی کد رو به شدت بالا میبره:@Configuration
public class RestClientConfig {

    @Value(&amp;quot${application.api.url}&amp;quot)
    private String url;
    @Value(&amp;quot${application.api.username}&amp;quot)
    private String username;
    @Value(&amp;quot${application.api.password}&amp;quot)
    private String password;

    @Bean
    public RestClient restClient() {
        String auth = &amp;quotBasic &amp;quot + Base64
                .getEncoder()
                .encodeToString((username + &amp;quot:&amp;quot + password).getBytes());
        return RestClient.builder()
                .baseUrl(url)
                .defaultHeader(
                        HttpHeaders.AUTHORIZATION,
                        auth
                ).build();
    }
}تا اینجا RestClient ما تنظیماتش انجام شده و  برای ارتباط با api آمادست.با استفاده GET method میتونیم دیتا رو از api بگیریم و به راحتی به entity مورد نظرمون تبدیلش کنیم. برای گرفتن لیستی از Customer ها بصورت زیر عمل میکنیم:List&lt;Customer&gt; customers = restClient.get()
                .uri(&amp;quot/customers&amp;quot)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .body(new ParameterizedTypeReference&lt;List&lt;Customer&gt;&gt;() {});میتونیم تو بخش uri پارامتر هم جایگذاری کنیم و همچنین برای هندل کردن خطاها هم میتونیم از onStatus استفاده کنیم. برای مثال برای دریافت یک Customer از کد زیر رو مینویسیم:         int id = 1;
        Customer customer = restClient.get()
                .uri(&amp;quot/customers/{id}&amp;quot, id)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .onStatus(status -&gt; status.value() == 404, (request, response) -&gt;
                    logger.info(&amp;quot404 not found&amp;quot))
                .body(Customer.class);برای ارسال اطلاعات و درخواست ایجاد یک Customer جدید از post استفاده میکنیم:Customer customer = new Customer();
        customer.setFirstName(&amp;quotnew customer&amp;quot);
        customer.setLastName(&amp;quotnew customer&amp;quot);
        ResponseEntity responseEntity = restClient.post()
                .uri(&amp;quot/customers&amp;quot)
                .contentType(MediaType.APPLICATION_JSON)
                .body(customer)
                .retrieve()
                .toBodilessEntity();
        logger.info(&amp;quotresponse code=&amp;quot + responseEntity.getStatusCode());با استفاده از put میتونیم درخواست آپدیت یک entity رو ارسال کنیم و با استفاده از delete هم میتونیم درخواست حذف یک entity رو ایجاد کنیم.کد کامل رو میتونید اینجا ببینید.تو این مقاله سعی کردیم یک معرفی خیلی کوتاه از RestClient داشته باشیم که به عنوان جایگزینی برای RestTemplate معرفی شده و با استفاده از fluent API کار رو خیلی ساده و خوانا تر کرده.ممنون که مقاله رو خوندید. امیدوارم مفید بوده باشه.</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Sat, 23 Dec 2023 22:11:36 +0330</pubDate>
            </item>
                    <item>
                <title>نصب Java(Eclipse Temurin) در Ubuntu</title>
                <link>https://virgool.io/@hadi_hnp/%D9%86%D8%B5%D8%A8-javaeclipse-temurin-%D8%AF%D8%B1-ubuntu-wgy5p0mubtbj</link>
                <description>اگه دنبال یک build کاملا رایگان از Open JDK میگردید که پشتیبانی بشه و بطور منظم براش patch منتشر شه تا مشکلات رو برطرف کنه Eclipse Temurin یکی از بهترین گزینه هاست.Temurin by Adoptiumنصب بدون اینترنتاگر سرور Ubuntu به اینترنت دسترسی نداره اول باید فایل مربوط به Temurin رو دانلود کنید و به سرورتون انتقال بدید.برای مثال از طریق لینک زیر با دستور wget میتونید نسخه 17 رو دانلود کنید:wget https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.8_7.tar.gzدستور wget یک ابزاره که برای دانلود فایل استفاده میشه و از پروتکل های HTTP, HTTPS, FTP, FTPS پشتیبانی میکنه. این دستور فایل رو دانلود میکنه و در همون مسیری که اجرا شده ذخیره میکنه.اگه با ویندوز هم دانلود کردید در نهایت باید از طریق راه هایی که وجود داره فایل رو به سرورتون انتقال بدید.فرمت فایل tar.gz یک فرمت برای فشرده سازی فایل هاست که خودش از دو فرمت تشکیل شده. فرمت tar برای آرشیو کردن چندین فایل در یک فایل استفاده میشه و فرمت gz(gzip) هم برای فشرده سازی استفاده میشه.بعد از انتقال فایل به سرور، برای خارج کردن فایل از حالت فشرده باید از دستور tar با پارامترهای زیر استفاده کرد:tar -xvzf OpenJDK17U-jdk_x64_linux_hotspot_17.0.8_7.tar.gzپارامترهای استفاده شده در این دستور:پارامترx: برای خارج کردن فایل از حالت آرشیوپارامترz: برای خارج کردن فایل از حالت فشرده با استفاده از gzipپارامترf: برای مشخص کردن مسیر و نام فایلپارامترv: برای نمایش دادن مراحل انجام این کار در ترمینال(در واقع میگه چه فایل هایی به چه صورت داره extract میشه، صرفا جهت نمایش دادنه)در نهایت با انجام دستور بالا یک دایرکتوری ایجاد میشه. برای مثال دایرکتوری با نام زیر:jdk-17.0.8+7حالا باید دایرکتوری رو به مسیر مناسب مطابق با ساختار فایل بندی لینوکس انتقال داد. یکی از مسیرهای مناسب برای نصب بسته های یوزر، مسیر /opt هست(یکی دیگه از مسیرهایی که استفاده میشه usr/local هست). برای انتقال اول یک دایرکتوری با نام jdk با دستور mkdir ایجاد میکنیم و بعد با دستور mv انتقال میدیم:mkdir /opt/jdk
mv jdk-17.0.8+7 /opt/jdkدستور mkdir برای ساختن دایرکتوری انجام میشه و دستور mv هم فایل وارد شده در بخش اول رو به مسیر وارد شده در بخش دوم انتقال میده.یکار دیگه هم میشه کرد که در همون زمان که با دستور tar فایل رو extract میکنیم مسیر extract هم بهش بدیم. برای اینکار باز باید با mkdir دایرکتوری مربوطه رو ایجاد کرد و بعد از دستور tar با پارامتر -C استفاده کرد که مسیر extract کردن رو مشخص میکنه:mkdir /opt/jdk
tar -xvzf OpenJDK17U-jdk_x64_linux_hotspot_17.0.8_7.tar.gz -C /opt/jdkحالا باید Environment Variable ها رو ست کرد تا بشه از دستور java در همه جای لینوکس استفاده کرد. باید دستورات زیر رو وارد کنیم(متغیر JAVA_HOME رو چون خیلی از برنامه های جاوا بیس ممکنه ازش استفاده کنن باید ست کنیم):export JAVA_HOME=&amp;quot/opt/java/jdk-17.0.8+7&amp;quot
export PATH=$PATH:$JAVA_HOME/binدستور export برای ست کردن variable بطور موقت استفاده میشه یعنی بعد از ریبوت شدن لینوکس دیگه این variable ها وجود ندارد. برای اینکه این مشکل برطرف بشه باید دستورات بالا در در فایل etc/profile وارد کرد.برای اینکار میشه از ادیتورهای vim یا nano استفاده کرد و دو خط دستور بالا رو انتهای این فایل اضافه و فایل رو ذخیره کرد. حالا با دستور زیر باید مطمئن شد جاوا بطور صحیح نصب شده:java --version
Outptu:
openjdk 17.0.8 2023-07-18
OpenJDK Runtime Environment Temurin-17.0.8+7 (build 17.0.8+7)
OpenJDK 64-Bit Server VM Temurin-17.0.8+7 (build 17.0.8+7, mixed mode, sharing)تا اینجا کار نصب تموم شده و میتونید از دستور java استفاده کنید. حالا اگه چندین نسخه از جاوا نصب کردید و میخواید دستور جاوا به یکی از اون نسخه ها اشاره کنه یک راه اینه که مقدار متغیر JAVA_HOME رو به نسخه مورد نظر تغییر بدید یا اینکه از دستور update-alternative کمک بگیرید. این دستور برای مدیریت نسخه های مختلف یک برنامه استفاده میشه. این دستور میاد برای یک دستور دیگه symbolic link میسازه و بهش اولویت میده که بر اساس اولویت داده شده از اون دستور استفاده میشه. باید برای هر دو دستور java و javac این کار انجام شه:update-alternatives --install /usr/bin/java java /opt/jdk/jdk-17.0.8+7/bin/java 10
update-alternatives --install /usr/bin/javac javac /opt/jdk/jdk-17.0.8+7/bin/javac 10با پارامتر install در مسیر usr/bin یک symbolic link برای دستور java و javac ساخته میشه و به مسیر نصب جاوا اشاره میکنه. عدد آخر هم مربوط به اولویته این دستور هست. برای مثال اگه نسخه 21 جاوا رو نصب کردید و خواستید جایگزین بشه، باید دستورهای بالا رو با مسیر نصب نسخه 21 جایگزین کنید و عدد اولویت هم بالاتر از 10 بزارید.نصب با استفاده از اینترنت و پکیج منیجردر صورتی که بخواید با استفاده از اینترنت و پکیج منیجر نصب رو انجام بدید ابتدا باید repository مربوط به Temurin رو به Ubuntu اضافه کنید چون تو ریپوهای دیفالتش این پکیج وجود نداره. برای اینکار اول باید GPG Key مربوط به ریپو رو دانلود و اضافه کنیم:mkdir /etc/apt/keyrings
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | tee /etc/apt/keyrings/adoptium.ascبا دستور mkdir یک دایرکتوری برای ذخیره GPG Key میسازیم. با استفاده از pipe (|) خروجی دستور قبل رو به ورودی دستور بعدی میدیم. اینجا GPG Key مربوط به ریپو رو با wget دانلود کردیم و به ورودی دستور tee دادیم که میاد در مسیر keyrings یک فایل با نام adoptium.asc اضافه میکنه و GPG Key دانلود شده رو اینجا ذخیره میکنه(کاربرد دستور tee خوندن اطلاعات از ورودی و نوشتن اونها در خروجی مشخص شده است)بعد از اینکار نوبت به اضافه کردن ریپو میرسه. در مسیر etc/apt/sources.list.d/ یک فایل با نام adoptium.list میسازیم(با استفاده از دستور touch):touch /etc/apt/sources/list.d/adoptium.listبا استفاده از vim یا nano بازش میکنیم و محتویات زیر رو بهش اضافه میکنیم:deb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb jammy mainمحتویات بالا فرمت استاندارد اضافه کردن ریپو هست که با توجه به اینکه ریپو deb base هست باید اول deb نوشته بشه و بعد چون GPG Key داره باید مسیر فایلش رو بهش بدیم و بعد آدرس ریپور و در آخر هم code name لینوکس خودمون(که در اینجا jammy main هست) .code name رو تو فایل زیر میتونید پیدا کنید:cat /etc/os-releaseدستور cat محتویات فایل رو نمایش میده. بجای ایجاد فایل و پیدا کردن codename میتونید از دستورات زیر هم اضافه کنید که از فایل مربوطه codename رو برمیداره و محتویات رو تو فایل جدید مورد نظر میریزه:echo &amp;quotdeb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= &#039;/^VERSION_CODENAME/{print$2}&#039; /etc/os-release) main&amp;quot | tee /etc/apt/sources.list.d/adoptium.listتا اینجای کار ریپو رو اضافه کردیم. برای نصب Temurin کافیه لیست پکیج ها رو اپدیت کنیم و از پکیج منیجر apt برای نصب استفاده کنیم:apt update
apt install temurin-17-jdkبا دستور بالا پکیج temurin 17 دانلود میشه و نصب میشه و در انتها با دستور java --version میتونیم اطلاعات نسخه نصب شده رو مشاهده کنیم.</description>
                <category>هادی</category>
                <author>هادی</author>
                <pubDate>Wed, 09 Aug 2023 09:47:57 +0330</pubDate>
            </item>
            </channel>
</rss>