آموزش Streamlit؛ ساخت اپلیکیشن و چت هوش مصنوعی با پایتون و API درواره

در این آموزش عملی یاد می‌گیرید با Streamlit و پایتون یک اپلیکیشن چت هوش مصنوعی بسازید، آن را به API درواره متصل کنید و قابلیت‌هایی مانند Streaming، تاریخچه گفتگو و انتخاب مدل را اضافه کنید.

Share
آموزش Streamlit؛ ساخت اپلیکیشن و چت هوش مصنوعی با پایتون و API درواره

برای تبدیل یک اسکریپت پایتون به اپلیکیشن تحت وب، همیشه لازم نیست HTML، CSS، JavaScript، React یا یک فریم‌ورک پیچیده بک‌اند یاد بگیرید. Streamlit یا استریم‌لیت به شما اجازه می‌دهد با کدنویسی پایتون، رابط‌های تعاملی برای پروژه‌های داده، یادگیری ماشین و هوش مصنوعی بسازید.

با Streamlit می‌توانید در مدت کوتاهی پروژه‌هایی مانند موارد زیر ایجاد کنید:

  • چت‌بات هوش مصنوعی
  • ابزار خلاصه‌سازی متن
  • رابط تحلیل فایل و داده
  • داشبورد تعاملی
  • ابزار تولید محتوا
  • اپلیکیشن استخراج اطلاعات
  • رابط آزمایش پرامپت
  • ابزار مقایسه مدل‌های هوش مصنوعی
  • نمونه اولیه محصول یا MVP
  • پنل داخلی برای تیم محتوا، پشتیبانی یا عملیات

در این مقاله، ابتدا Streamlit را نصب می‌کنیم و با مفاهیم اصلی آن آشنا می‌شویم. سپس یک اپلیکیشن کامل چت هوش مصنوعی با پایتون می‌سازیم و آن را به API درواره متصل می‌کنیم.

نسخه نهایی پروژه دارای این قابلیت‌ها خواهد بود:

  • اتصال به API سازگار با OpenAI درواره
  • انتخاب Model ID
  • تنظیم Temperature
  • نمایش پاسخ به‌صورت Streaming
  • نگهداری تاریخچه گفتگو
  • پاک‌کردن گفتگو
  • خروجی گرفتن از مکالمه
  • محدودکردن طول ورودی
  • مدیریت خطاهای API
  • نگهداری امن کلید API
  • رابط فارسی و راست‌به‌چپ
  • امکان استقرار با Docker یا Streamlit Community Cloud

Streamlit چیست؟

Streamlit یک فریم‌ورک متن‌باز پایتون برای ساخت اپلیکیشن‌های داده‌محور و رابط‌های تعاملی است. این فریم‌ورک بیشتر میان توسعه‌دهندگان پایتون، تحلیلگران داده، متخصصان یادگیری ماشین و سازندگان ابزارهای هوش مصنوعی استفاده می‌شود.

بر اساس وب‌سایت رسمی Streamlit، این فریم‌ورک می‌تواند اسکریپت‌های پایتون را بدون نیاز به توسعه جداگانه فرانت‌اند به اپلیکیشن وب تعاملی تبدیل کند.

برای مثال، کد زیر یک صفحه وب ساده می‌سازد:

import streamlit as st

st.title("اولین اپلیکیشن Streamlit")
st.write("این صفحه فقط با پایتون ساخته شده است.")

برای اجرای برنامه کافی است بنویسید:

streamlit run app.py

Streamlit یک سرور محلی اجرا و اپلیکیشن را در مرورگر باز می‌کند.

Streamlit برای چه پروژه‌هایی مناسب است؟

Streamlit برای پروژه‌هایی مناسب است که سرعت توسعه و امکان آزمایش ایده اهمیت زیادی دارد.

کاربردهای متداول آن عبارت‌اند از:

  • ساخت نمونه اولیه یک محصول هوش مصنوعی
  • طراحی رابط آزمایش مدل‌های زبانی
  • نمایش داده‌ها و نمودارها
  • ساخت ابزارهای داخلی سازمان
  • ساخت داشبوردهای تحلیلی
  • ساخت رابط برای مدل‌های یادگیری ماشین
  • دریافت فایل و پردازش آن
  • ساخت ابزارهای پژوهشی
  • نمایش نتایج پردازش داده
  • ارائه Demo به مشتری یا سرمایه‌گذار

Streamlit برای ساخت MVP، ابزار داخلی، داشبورد و Demo بسیار مناسب است. برای محصولات عمومی بزرگ با رابط بسیار اختصاصی، کنترل دقیق فرانت‌اند و تعداد زیاد کاربران هم‌زمان، معمولاً معماری‌هایی مانند React یا Next.js در کنار یک بک‌اند مستقل انعطاف بیشتری دارند.

تفاوت Streamlit با FastAPI و Django

Streamlit، FastAPI و Django هر سه در اکوسیستم پایتون استفاده می‌شوند، اما نقش یکسانی ندارند.

ابزارکاربرد اصلی
Streamlitساخت سریع رابط وب و اپلیکیشن تعاملی
FastAPIساخت Backend و REST API
Djangoساخت اپلیکیشن وب کامل با ORM، پنل مدیریت و سیستم کاربران
Flaskساخت اپلیکیشن وب یا API سبک
Gradioساخت رابط سریع برای مدل‌های یادگیری ماشین و هوش مصنوعی

Streamlit هم رابط کاربری و هم منطق اجرای برنامه را در یک اسکریپت پایتون قرار می‌دهد. این ویژگی توسعه را سریع می‌کند، اما برای معماری‌های پیچیده بهتر است منطق اصلی برنامه از رابط Streamlit جدا شود.

برای مثال، می‌توانید معماری زیر را داشته باشید:

Streamlit
→ Backend اختصاصی
→ API درواره
→ مدل هوش مصنوعی

در پروژه ساده این اتصال می‌تواند مستقیم باشد:

Streamlit
→ API درواره
→ مدل هوش مصنوعی

در این آموزش از معماری مستقیم استفاده می‌کنیم.

پیش‌نیازهای پروژه

برای اجرای پروژه به موارد زیر نیاز دارید:

  • Python نسخه 3.10 یا جدیدتر
  • pip
  • ویرایشگر کد مانند VS Code
  • حساب کاربری درواره
  • کلید API درواره
  • Model ID معتبر
  • آشنایی مقدماتی با پایتون

برای مشاهده مدل‌های موجود، قابلیت‌ها و قیمت آن‌ها به صفحه مدل‌های درواره مراجعه کنید.

در مثال‌ها از مقادیر جایگزین زیر استفاده می‌شود:

YOUR_DARVAREH_API_KEY
YOUR_MODEL_ID

این مقادیر را با اطلاعات واقعی حساب خود جایگزین کنید.

معماری اپلیکیشن

ساختار پروژه ما ساده است:

کاربر
→ رابط Streamlit
→ کتابخانه Python
→ API درواره
→ مدل هوش مصنوعی
→ پاسخ Streaming
→ نمایش در Streamlit

Streamlit پیام کاربر را دریافت می‌کند. برنامه پیام و تاریخچه مکالمه را به API درواره می‌فرستد. پاسخ مدل به‌صورت تدریجی دریافت و در رابط نمایش داده می‌شود.

مرحله اول: ساخت پوشه پروژه

یک پوشه جدید بسازید:

mkdir streamlit-darvareh-chat
cd streamlit-darvareh-chat

ساختار نهایی پروژه به این صورت خواهد بود:

streamlit-darvareh-chat/
├── app.py
├── requirements.txt
├── .env
├── .gitignore
└── .streamlit/
    └── secrets.toml

لازم نیست هم‌زمان از فایل .env و secrets.toml استفاده کنید. فایل .env برای توسعه معمول پایتون و secrets.toml برای مدیریت Secrets در Streamlit مناسب است.

مرحله دوم: ساخت محیط مجازی پایتون

ساخت محیط مجازی باعث می‌شود کتابخانه‌های پروژه از پکیج‌های سراسری سیستم جدا باشند.

در Linux و macOS:

python3 -m venv .venv
source .venv/bin/activate

در Windows PowerShell:

python -m venv .venv
.venv\Scripts\Activate.ps1

پس از فعال‌شدن محیط، نام .venv معمولاً در ابتدای خط ترمینال نمایش داده می‌شود.

مرحله سوم: نصب کتابخانه‌ها

دستور زیر را اجرا کنید:

pip install streamlit openai python-dotenv

فایل requirements.txt را با محتوای زیر بسازید:

streamlit
openai
python-dotenv

برای نصب مجدد وابستگی‌ها می‌توان از این دستور استفاده کرد:

pip install -r requirements.txt

برای اطمینان از نصب صحیح Streamlit، دستور زیر را اجرا کنید:

streamlit hello

طبق راهنمای رسمی نصب Streamlit، این دستور یک اپلیکیشن نمونه را اجرا می‌کند و برای آزمایش نصب مناسب است.

مرحله چهارم: آزمایش اولین برنامه

فایل app.py را بسازید:

import streamlit as st

st.set_page_config(
    page_title="اپلیکیشن هوش مصنوعی",
    page_icon="🤖",
    layout="centered",
)

st.title("اپلیکیشن هوش مصنوعی من")
st.write("این برنامه با Streamlit و پایتون ساخته شده است.")

برنامه را اجرا کنید:

streamlit run app.py

اپلیکیشن معمولاً در آدرس زیر در دسترس قرار می‌گیرد:

http://localhost:8501

اگر مرورگر خودکار باز نشد، همین آدرس را به‌صورت دستی باز کنید.

مدل اجرای Streamlit چگونه است؟

یکی از مهم‌ترین نکاتی که باید درباره Streamlit بدانید، مدل اجرای مجدد یا Rerun است.

هر بار که کاربر با یک Widget تعامل می‌کند، اسکریپت از ابتدا دوباره اجرا می‌شود. برای مثال، کلیک روی دکمه، تغییر Slider یا ارسال پیام می‌تواند باعث اجرای مجدد فایل app.py شود.

به همین دلیل، متغیرهای معمولی برای نگهداری دائمی تاریخچه گفتگو کافی نیستند:

messages = []

با اجرای مجدد برنامه، این فهرست دوباره خالی می‌شود.

برای حفظ وضعیت باید از st.session_state استفاده کنیم:

if "messages" not in st.session_state:
    st.session_state.messages = []

Session State داده را در طول نشست همان کاربر نگه می‌دارد.

مرحله پنجم: تنظیم کلید API در فایل env

در پوشه پروژه یک فایل .env ایجاد کنید:

DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID

فایل .gitignore را نیز بسازید:

.venv/
.env
.streamlit/secrets.toml
__pycache__/
*.pyc

کلید API نباید در GitHub، فایل عمومی، کد Frontend یا تصویر آموزشی منتشر شود.

آدرس پایه API درواره:

https://api.darvareh.ir/v1

Endpoint رایج مکالمه:

https://api.darvareh.ir/v1/chat/completions

مرحله ششم: آزمایش اتصال در یک فایل ساده

پیش از ساخت رابط کامل بهتر است اتصال API را به‌تنهایی آزمایش کنیم.

فایل موقت test_api.py را بسازید:

import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

api_key = os.getenv("DARVAREH_API_KEY")
model_id = os.getenv("DARVAREH_MODEL_ID")

if not api_key:
    raise RuntimeError("متغیر DARVAREH_API_KEY تنظیم نشده است.")

if not model_id:
    raise RuntimeError("متغیر DARVAREH_MODEL_ID تنظیم نشده است.")

client = OpenAI(
    api_key=api_key,
    base_url="https://api.darvareh.ir/v1",
)

response = client.chat.completions.create(
    model=model_id,
    messages=[
        {
            "role": "system",
            "content": "شما یک دستیار فارسی دقیق و مختصر هستید.",
        },
        {
            "role": "user",
            "content": "Streamlit را در یک پاراگراف توضیح بده.",
        },
    ],
    temperature=0.2,
    max_tokens=500,
)

print(response.choices[0].message.content)

فایل را اجرا کنید:

python test_api.py

اگر اتصال درست باشد، پاسخ مدل در ترمینال نمایش داده می‌شود.

مرحله هفتم: ساخت نسخه ساده چت‌بات

اکنون فایل app.py را با نسخه زیر جایگزین کنید:

import os

import streamlit as st
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

st.set_page_config(
    page_title="چت هوش مصنوعی",
    page_icon="💬",
    layout="centered",
)

api_key = os.getenv("DARVAREH_API_KEY")
model_id = os.getenv("DARVAREH_MODEL_ID")

if not api_key or not model_id:
    st.error("کلید API یا Model ID تنظیم نشده است.")
    st.stop()

client = OpenAI(
    api_key=api_key,
    base_url="https://api.darvareh.ir/v1",
)

st.title("چت هوش مصنوعی")
st.caption("ساخته‌شده با Streamlit و API درواره")

user_prompt = st.chat_input("پیام خود را بنویسید...")

if user_prompt:
    with st.chat_message("user"):
        st.write(user_prompt)

    response = client.chat.completions.create(
        model=model_id,
        messages=[
            {
                "role": "system",
                "content": "شما یک دستیار فارسی دقیق و کاربردی هستید.",
            },
            {
                "role": "user",
                "content": user_prompt,
            },
        ],
        temperature=0.3,
        max_tokens=1000,
    )

    assistant_text = response.choices[0].message.content

    with st.chat_message("assistant"):
        st.write(assistant_text)

این نسخه پیام را دریافت و پاسخ را نمایش می‌دهد، اما هنوز تاریخچه گفتگو را نگه نمی‌دارد.

مرحله هشتم: افزودن تاریخچه گفتگو

برای نگهداری پیام‌ها از Session State استفاده می‌کنیم:

if "messages" not in st.session_state:
    st.session_state.messages = []

هر پیام دارای دو فیلد است:

{
    "role": "user",
    "content": "پیام کاربر"
}

یا:

{
    "role": "assistant",
    "content": "پاسخ مدل"
}

برای نمایش پیام‌های قبلی:

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

پس از دریافت پیام کاربر، آن را ذخیره می‌کنیم:

st.session_state.messages.append(
    {
        "role": "user",
        "content": user_prompt,
    }
)

بعد از دریافت پاسخ نیز پیام Assistant ذخیره می‌شود:

st.session_state.messages.append(
    {
        "role": "assistant",
        "content": assistant_text,
    }
)

مرحله نهم: نمایش Streaming پاسخ

در حالت عادی کاربر باید تا تولید کامل پاسخ منتظر بماند. در Streaming، بخش‌های پاسخ هم‌زمان با تولید نمایش داده می‌شوند.

تابع زیر جریان پاسخ را از API دریافت می‌کند:

def stream_model_response(client, model_id, messages, temperature):
    stream = client.chat.completions.create(
        model=model_id,
        messages=messages,
        temperature=temperature,
        max_tokens=1500,
        stream=True,
    )

    for chunk in stream:
        if not chunk.choices:
            continue

        content = chunk.choices[0].delta.content

        if content:
            yield content

Streamlit دارای st.write_stream است که داده‌های Generator را به‌تدریج نمایش می‌دهد:

with st.chat_message("assistant"):
    assistant_text = st.write_stream(
        stream_model_response(
            client=client,
            model_id=model_id,
            messages=api_messages,
            temperature=temperature,
        )
    )

برای آشنایی بیشتر با اجزای Streamlit و APIهای رابط کاربری می‌توانید مستندات رسمی Streamlit را مطالعه کنید.

کد کامل اپلیکیشن Streamlit

نسخه زیر یک چت‌بات کامل و قابل اجرا می‌سازد:

import json
import os
from typing import Generator

import streamlit as st
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

DARVAREH_BASE_URL = "https://api.darvareh.ir/v1"
DEFAULT_SYSTEM_PROMPT = """
شما یک دستیار فارسی دقیق، شفاف و کاربردی هستید.

قواعد:
- پاسخ را متناسب با سؤال کاربر ارائه کنید.
- اگر درباره موضوعی مطمئن نیستید، عدم قطعیت را بیان کنید.
- اطلاعات، عدد، منبع یا قابلیت ساختگی تولید نکنید.
- برای موضوع‌های فنی، در صورت نیاز مثال ارائه کنید.
- پاسخ را به زبان فارسی بنویسید؛ مگر اینکه کاربر زبان دیگری بخواهد.
""".strip()

MAX_USER_CHARACTERS = 12000
MAX_HISTORY_MESSAGES = 20

st.set_page_config(
    page_title="چت هوش مصنوعی درواره",
    page_icon="🤖",
    layout="centered",
)

st.markdown(
    """
    <style>
    .stApp {
        direction: rtl;
    }

    [data-testid="stChatInput"] textarea {
        direction: rtl;
        text-align: right;
    }

    [data-testid="stSidebar"] {
        direction: rtl;
    }

    [data-testid="stMarkdownContainer"] {
        text-align: right;
    }

    pre, code {
        direction: ltr;
        text-align: left;
    }
    </style>
    """,
    unsafe_allow_html=True,
)


def read_secret(name: str, default: str | None = None) -> str | None:
    try:
        value = st.secrets.get(name)
        if value:
            return str(value)
    except Exception:
        pass

    return os.getenv(name, default)


@st.cache_resource
def create_client(api_key: str) -> OpenAI:
    return OpenAI(
        api_key=api_key,
        base_url=DARVAREH_BASE_URL,
        timeout=60.0,
        max_retries=2,
    )


def build_api_messages(
    history: list[dict[str, str]],
    system_prompt: str,
) -> list[dict[str, str]]:
    recent_history = history[-MAX_HISTORY_MESSAGES:]

    return [
        {
            "role": "system",
            "content": system_prompt,
        },
        *recent_history,
    ]


def stream_model_response(
    client: OpenAI,
    model_id: str,
    messages: list[dict[str, str]],
    temperature: float,
    max_tokens: int,
) -> Generator[str, None, None]:
    stream = client.chat.completions.create(
        model=model_id,
        messages=messages,
        temperature=temperature,
        max_tokens=max_tokens,
        stream=True,
    )

    for chunk in stream:
        if not chunk.choices:
            continue

        delta = chunk.choices[0].delta

        if delta.content:
            yield delta.content


api_key = read_secret("DARVAREH_API_KEY")
default_model_id = read_secret("DARVAREH_MODEL_ID", "")

st.title("چت هوش مصنوعی")
st.caption("اپلیکیشن آزمایشی ساخته‌شده با Streamlit و API درواره")

if not api_key:
    st.error(
        "کلید API تنظیم نشده است. متغیر DARVAREH_API_KEY را "
        "در فایل .env یا secrets.toml قرار دهید."
    )
    st.stop()

client = create_client(api_key)

if "messages" not in st.session_state:
    st.session_state.messages = []

with st.sidebar:
    st.header("تنظیمات")

    model_id = st.text_input(
        "Model ID",
        value=default_model_id,
        placeholder="YOUR_MODEL_ID",
        help="شناسه مدل را از صفحه مدل‌های درواره دریافت کنید.",
    )

    temperature = st.slider(
        "Temperature",
        min_value=0.0,
        max_value=1.5,
        value=0.3,
        step=0.1,
    )

    max_tokens = st.slider(
        "حداکثر توکن خروجی",
        min_value=100,
        max_value=4000,
        value=1500,
        step=100,
    )

    system_prompt = st.text_area(
        "System Prompt",
        value=DEFAULT_SYSTEM_PROMPT,
        height=220,
    )

    st.caption(
        f"برای هر درخواست، حداکثر {MAX_HISTORY_MESSAGES} "
        "پیام اخیر ارسال می‌شود."
    )

    if st.button("پاک‌کردن گفتگو", use_container_width=True):
        st.session_state.messages = []
        st.rerun()

    conversation_json = json.dumps(
        st.session_state.messages,
        ensure_ascii=False,
        indent=2,
    )

    st.download_button(
        label="دانلود تاریخچه JSON",
        data=conversation_json,
        file_name="conversation.json",
        mime="application/json",
        use_container_width=True,
    )

if not model_id.strip():
    st.warning("ابتدا Model ID را در نوار کناری وارد کنید.")
    st.stop()

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

user_prompt = st.chat_input("پیام خود را بنویسید...")

if user_prompt:
    normalized_prompt = user_prompt.strip()

    if not normalized_prompt:
        st.warning("پیام نمی‌تواند خالی باشد.")
        st.stop()

    if len(normalized_prompt) > MAX_USER_CHARACTERS:
        st.error(
            f"طول پیام بیشتر از حد مجاز است. "
            f"حداکثر {MAX_USER_CHARACTERS} کاراکتر ارسال کنید."
        )
        st.stop()

    user_message = {
        "role": "user",
        "content": normalized_prompt,
    }

    st.session_state.messages.append(user_message)

    with st.chat_message("user"):
        st.markdown(normalized_prompt)

    api_messages = build_api_messages(
        history=st.session_state.messages,
        system_prompt=system_prompt.strip() or DEFAULT_SYSTEM_PROMPT,
    )

    try:
        with st.chat_message("assistant"):
            assistant_text = st.write_stream(
                stream_model_response(
                    client=client,
                    model_id=model_id.strip(),
                    messages=api_messages,
                    temperature=temperature,
                    max_tokens=max_tokens,
                )
            )

        if not isinstance(assistant_text, str):
            assistant_text = str(assistant_text)

        st.session_state.messages.append(
            {
                "role": "assistant",
                "content": assistant_text,
            }
        )

    except Exception as exc:
        if (
            st.session_state.messages
            and st.session_state.messages[-1]["role"] == "user"
        ):
            st.session_state.messages.pop()

        st.error(
            "ارسال درخواست با خطا مواجه شد. "
            "کلید API، Model ID، موجودی حساب و اتصال اینترنت را بررسی کنید."
        )

        with st.expander("جزئیات فنی خطا"):
            st.code(str(exc))

اجرای نسخه کامل

در ترمینال اجرا کنید:

streamlit run app.py

سپس آدرس زیر را باز کنید:

http://localhost:8501

Model ID را در نوار کناری وارد و یک پیام آزمایشی ارسال کنید.

چرا از Cache Resource استفاده کردیم؟

در کد از این Decorator استفاده شده است:

@st.cache_resource
def create_client(api_key: str) -> OpenAI:
    return OpenAI(
        api_key=api_key,
        base_url=DARVAREH_BASE_URL,
    )

Streamlit با هر تعامل اسکریپت را دوباره اجرا می‌کند. بدون Cache ممکن است Client در هر Rerun دوباره ساخته شود.

st.cache_resource برای منابعی مناسب است که می‌توانند میان اجراهای مجدد همان برنامه استفاده شوند؛ مانند:

  • Client اتصال به API
  • اتصال پایگاه داده
  • مدل یادگیری ماشین
  • منابع سنگین
  • Sessionهای HTTP قابل استفاده مجدد

کلید API را در خروجی تابع، رابط یا Log نمایش ندهید.

مدیریت تاریخچه و پنجره زمینه

در این پروژه تنها ۲۰ پیام اخیر ارسال می‌شود:

MAX_HISTORY_MESSAGES = 20

این محدودیت چند مزیت دارد:

  • کاهش مصرف توکن
  • کاهش زمان پاسخ
  • جلوگیری از بزرگ‌شدن نامحدود درخواست
  • کاهش احتمال عبور از Context Window مدل

با این حال، محدودکردن بر اساس تعداد پیام دقیق نیست. ممکن است یک پیام بسیار طولانی بیشتر از چندین پیام کوتاه توکن مصرف کند.

در پروژه پیشرفته‌تر بهتر است:

  1. توکن پیام‌ها را تخمین بزنید.
  2. یک بودجه مشخص برای تاریخچه تعیین کنید.
  3. قدیمی‌ترین پیام‌ها را حذف کنید.
  4. در صورت نیاز، بخش قدیمی گفتگو را خلاصه کنید.
  5. خلاصه را همراه پیام‌های جدید نگه دارید.

ذخیره کلید با Streamlit Secrets

برای استفاده از Streamlit Secrets، پوشه زیر را ایجاد کنید:

.streamlit/

سپس فایل .streamlit/secrets.toml را بسازید:

DARVAREH_API_KEY = "YOUR_DARVAREH_API_KEY"
DARVAREH_MODEL_ID = "YOUR_MODEL_ID"

این فایل را به Git اضافه نکنید:

.streamlit/secrets.toml

کدی که نوشتیم ابتدا Streamlit Secrets را بررسی می‌کند و اگر مقدار پیدا نشود، سراغ متغیر محیطی می‌رود.

انتخاب مدل مناسب

نوع مدل باید با کاربرد اپلیکیشن هماهنگ باشد.

کاربردویژگی موردنیاز
چت عمومیکیفیت مناسب در مکالمه و زبان فارسی
خلاصه‌سازیدقت، سرعت و هزینه متعادل
تولید محتواتوانایی نوشتن و پیروی از دستور
برنامه‌نویسیمدل مناسب Coding
تحلیل متنخروجی پایدار و دقیق
پاسخ کوتاه پرتعدادسرعت بالا و هزینه کمتر
استدلال چندمرحله‌ایتوانایی Reasoning

مدل قوی‌تر همیشه بهترین انتخاب نیست. برای یک ابزار پرتعداد خلاصه‌سازی، مدل سریع و اقتصادی ممکن است انتخاب مناسب‌تری از یک مدل سنگین استدلالی باشد.

برای مشاهده آخرین مدل‌ها، Model ID و قیمت، صفحه مدل‌های هوش مصنوعی درواره را بررسی کنید.

افزودن حالت خلاصه‌سازی به اپلیکیشن

می‌توانید در Sidebar یک Selectbox برای انتخاب وظیفه اضافه کنید:

task_type = st.selectbox(
    "نوع پردازش",
    options=[
        "گفتگو",
        "خلاصه‌سازی",
        "بازنویسی",
        "استخراج نکات مهم",
    ],
)

سپس بر اساس نوع پردازش، System Prompt را تغییر دهید:

SYSTEM_PROMPTS = {
    "گفتگو": """
    شما یک دستیار فارسی دقیق و کاربردی هستید.
    """,
    "خلاصه‌سازی": """
    متن کاربر را به زبان فارسی خلاصه کن.
    اطلاعات خارج از متن اضافه نکن.
    نام‌ها، اعداد و نکات اصلی را حفظ کن.
    """,
    "بازنویسی": """
    متن کاربر را با لحن رسمی، روان و شفاف بازنویسی کن.
    مفهوم و اطلاعات متن را تغییر نده.
    """,
    "استخراج نکات مهم": """
    نکات اصلی متن را به‌صورت فهرست استخراج کن.
    فقط به اطلاعات موجود در متن متکی باش.
    """,
}

system_prompt = SYSTEM_PROMPTS[task_type].strip()

با این روش یک اپلیکیشن می‌تواند چند ابزار متنی داشته باشد.

دریافت فایل متنی

Streamlit با st.file_uploader امکان دریافت فایل دارد:

uploaded_file = st.file_uploader(
    "فایل متنی را انتخاب کنید",
    type=["txt", "md"],
)

if uploaded_file is not None:
    file_text = uploaded_file.read().decode("utf-8")

    st.text_area(
        "محتوای فایل",
        value=file_text,
        height=300,
    )

برای ارسال فایل به مدل باید محتوای آن را در پیام User قرار دهید:

file_prompt = f"""
متن زیر را خلاصه کن:

{file_text}
"""

برای فایل‌های بزرگ نباید تمام محتوا را بدون محدودیت در یک درخواست قرار دهید. ابتدا اندازه فایل را کنترل کنید و در صورت نیاز متن را به بخش‌های کوچک‌تر تقسیم کنید.

افزودن شمارنده کاراکتر

برای کمک به کنترل اندازه درخواست:

text = st.text_area(
    "متن ورودی",
    height=250,
)

st.caption(f"تعداد کاراکتر: {len(text):,}")

سپس پیش از ارسال:

if len(text) > 12000:
    st.error("متن برای این ابزار بیش از حد طولانی است.")
    st.stop()

حد مناسب باید بر اساس مدل، نوع کاربرد و Context Window تعیین شود.

ساخت رابط دو ستونه

برای ابزارهایی مانند بازنویسی متن می‌توانید ورودی و خروجی را کنار یکدیگر نمایش دهید:

left_column, right_column = st.columns(2)

with left_column:
    source_text = st.text_area(
        "متن اصلی",
        height=350,
    )

with right_column:
    st.text_area(
        "متن بازنویسی‌شده",
        value=st.session_state.get("rewritten_text", ""),
        height=350,
    )

در رابط فارسی می‌توانید ترتیب ستون‌ها را متناسب با تجربه کاربری آزمایش کنید.

مدیریت خطاهای رایج API

در محیط واقعی بهتر است خطاها بر اساس نوع آن‌ها مدیریت شوند.

from openai import (
    APIConnectionError,
    APIStatusError,
    APITimeoutError,
    AuthenticationError,
    BadRequestError,
    RateLimitError,
)

نمونه مدیریت خطا:

try:
    response = client.chat.completions.create(
        model=model_id,
        messages=messages,
    )

except AuthenticationError:
    st.error("کلید API نامعتبر است.")

except BadRequestError:
    st.error("ساختار درخواست یا Model ID را بررسی کنید.")

except RateLimitError:
    st.error("تعداد درخواست‌ها زیاد است. کمی بعد دوباره تلاش کنید.")

except APITimeoutError:
    st.error("زمان پاسخ‌گویی بیش از حد انتظار شد.")

except APIConnectionError:
    st.error("اتصال به سرویس برقرار نشد.")

except APIStatusError as exc:
    st.error(f"API با وضعیت {exc.status_code} پاسخ داد.")

except Exception:
    st.error("خطای پیش‌بینی‌نشده‌ای رخ داد.")

در رابط عمومی، متن کامل خطا را به همه کاربران نمایش ندهید؛ زیرا ممکن است جزئیات فنی یا اطلاعات داخلی در آن وجود داشته باشد. جزئیات کامل را فقط در محیط توسعه یا سامانه ثبت خطا نگه دارید.

خطاهای متداول Streamlit

دستور streamlit پیدا نمی‌شود

مطمئن شوید محیط مجازی فعال است:

source .venv/bin/activate

در Windows:

.venv\Scripts\Activate.ps1

سپس نصب را بررسی کنید:

pip show streamlit

صفحه با هر کلیک دوباره اجرا می‌شود

این رفتار اصلی Streamlit است. برای حفظ اطلاعات از st.session_state و برای منابع قابل استفاده مجدد از Cache استفاده کنید.

تاریخچه گفتگو حذف می‌شود

تاریخچه نباید در یک متغیر محلی ساده نگهداری شود. از این ساختار استفاده کنید:

if "messages" not in st.session_state:
    st.session_state.messages = []

پاسخ Streaming نمایش داده نمی‌شود

بررسی کنید:

  • stream=True تنظیم شده باشد.
  • تابع یک Generator ایجاد کند.
  • از yield استفاده شده باشد.
  • مدل انتخابی از Streaming در Endpoint مورد استفاده پشتیبانی کند.
  • محتوای Delta قبل از نمایش بررسی شود.

رابط فارسی به‌هم‌ریخته است

جهت کلی صفحه را با CSS روی RTL قرار دهید، اما جهت کدها را LTR نگه دارید:

.stApp {
    direction: rtl;
}

pre, code {
    direction: ltr;
    text-align: left;
}

تغییر فایل در صفحه دیده نمی‌شود

Streamlit معمولاً تغییر را شناسایی و صفحه را به‌روزرسانی می‌کند. اگر این اتفاق نیفتاد، برنامه را متوقف و دوباره اجرا کنید:

streamlit run app.py

کنترل هزینه در اپلیکیشن Streamlit

اگر برنامه عمومی باشد، هر کاربر می‌تواند باعث مصرف API شود. بنابراین محدودیت‌گذاری اهمیت زیادی دارد.

اقدامات پیشنهادی:

  • محدودکردن طول ورودی
  • محدودکردن max_tokens
  • ارسال تعداد محدودی از پیام‌های قبلی
  • تعیین سقف درخواست برای هر کاربر
  • استفاده از مدل متناسب با وظیفه
  • ثبت تعداد درخواست‌ها
  • محدودکردن آپلود فایل
  • افزودن احراز هویت برای اپلیکیشن خصوصی
  • جلوگیری از ارسال هم‌زمان چند درخواست
  • غیرفعال‌کردن دکمه در زمان پردازش
  • ثبت مصرف روزانه

در برنامه نمونه، این دو محدودیت را داریم:

MAX_USER_CHARACTERS = 12000
MAX_HISTORY_MESSAGES = 20

برای برنامه عمومی واقعی، این محدودیت‌ها کافی نیستند و باید احراز هویت، Rate Limit و سقف مصرف نیز اضافه شود.

آیا کلید API را از کاربر دریافت کنیم؟

دو معماری رایج وجود دارد.

کلید مشترک سمت سرور

کلید متعلق به سازنده اپلیکیشن است و در Secret سرور نگهداری می‌شود.

مزایا:

  • تجربه کاربری ساده‌تر
  • کاربر نیازی به واردکردن کلید ندارد

محدودیت‌ها:

  • تمام هزینه بر عهده مالک برنامه است.
  • استفاده عمومی می‌تواند مصرف را افزایش دهد.
  • به احراز هویت و محدودیت نرخ نیاز دارد.

کلید اختصاصی هر کاربر

کاربر کلید API خود را وارد می‌کند.

مزایا:

  • مصرف هر کاربر از حساب خودش انجام می‌شود.
  • هزینه روی حساب سازنده متمرکز نمی‌شود.

محدودیت‌ها:

  • باید درباره نحوه نگهداری کلید شفاف باشید.
  • کلید نباید در Log یا پایگاه داده بدون حفاظت ذخیره شود.
  • تجربه کاربری پیچیده‌تر است.

برای ابزار داخلی تیم، کلید سمت سرور معمولاً ساده‌تر است. برای اپلیکیشن عمومی، بهتر است معماری مصرف، احراز هویت و محدودیت‌ها پیش از انتشار طراحی شود.

استقرار در Streamlit Community Cloud

Streamlit Community Cloud می‌تواند اپلیکیشن متصل به مخزن GitHub را اجرا کند. روند کلی:

  1. پروژه را در یک Repository قرار دهید.
  2. فایل .env و secrets.toml را Commit نکنید.
  3. وارد Streamlit Community Cloud شوید.
  4. Repository و فایل app.py را انتخاب کنید.
  5. Secrets را در تنظیمات برنامه وارد کنید.
  6. اپلیکیشن را Deploy کنید.

Secrets موردنیاز:

DARVAREH_API_KEY = "YOUR_DARVAREH_API_KEY"
DARVAREH_MODEL_ID = "YOUR_MODEL_ID"

توجه داشته باشید که اپلیکیشن عمومی می‌تواند توسط افراد مختلف استفاده شود و هر درخواست از اعتبار API شما مصرف کند. قراردادن کلید در Secrets مانع مشاهده مستقیم آن می‌شود، اما مانع استفاده زیاد از خود رابط عمومی نیست.

برای اپلیکیشن دارای کلید مشترک، دسترسی کاربران و سقف مصرف را کنترل کنید.

استقرار با Docker

فایل Dockerfile را ایجاد کنید:

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

EXPOSE 8501

CMD [
  "streamlit",
  "run",
  "app.py",
  "--server.address=0.0.0.0",
  "--server.port=8501"
]

Image را بسازید:

docker build -t streamlit-darvareh-chat .

Container را اجرا کنید:

docker run \
  --name streamlit-darvareh-chat \
  -p 8501:8501 \
  -e DARVAREH_API_KEY="YOUR_DARVAREH_API_KEY" \
  -e DARVAREH_MODEL_ID="YOUR_MODEL_ID" \
  streamlit-darvareh-chat

اپلیکیشن در این آدرس در دسترس خواهد بود:

http://localhost:8501

کلید API را داخل Dockerfile یا Image قرار ندهید. آن را هنگام اجرا با متغیر محیطی یا سامانه مدیریت Secrets وارد کنید.

آیا Streamlit برای Production مناسب است؟

پاسخ به نوع پروژه بستگی دارد.

Streamlit برای این موارد انتخاب مناسبی است:

  • ابزار داخلی
  • داشبورد
  • اپلیکیشن پژوهشی
  • نمونه اولیه
  • پنل آزمایش مدل
  • Demo
  • ابزار تیم محتوا یا تحلیل داده
  • اپلیکیشن با تعداد محدود کاربر

برای یک سرویس عمومی بزرگ ممکن است به این اجزا نیاز داشته باشید:

  • احراز هویت مستقل
  • پایگاه داده
  • Rate Limit
  • Backend جداگانه
  • صف پردازش
  • ثبت مصرف
  • کنترل دسترسی
  • Load Balancer
  • چند Instance
  • ذخیره Session خارج از حافظه
  • مانیتورینگ
  • مدیریت خطا و Log مرکزی

در چنین معماری‌ای می‌توان Streamlit را به‌عنوان رابط داخلی نگه داشت و منطق اصلی را در یک Backend مستقل قرار داد.

بهبود معماری پروژه

هنگامی که پروژه بزرگ‌تر شد، تمام کد را در app.py نگه ندارید.

ساختار بهتر:

streamlit-darvareh-chat/
├── app.py
├── config.py
├── services/
│   └── ai_client.py
├── utils/
│   ├── prompts.py
│   └── validation.py
├── requirements.txt
└── .streamlit/
    └── secrets.toml

فایل services/ai_client.py:

from typing import Generator

from openai import OpenAI


class DarvarehAIClient:
    def __init__(self, api_key: str):
        self.client = OpenAI(
            api_key=api_key,
            base_url="https://api.darvareh.ir/v1",
            timeout=60.0,
            max_retries=2,
        )

    def stream_chat(
        self,
        model: str,
        messages: list[dict[str, str]],
        temperature: float = 0.3,
        max_tokens: int = 1500,
    ) -> Generator[str, None, None]:
        stream = self.client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens,
            stream=True,
        )

        for chunk in stream:
            if not chunk.choices:
                continue

            content = chunk.choices[0].delta.content

            if content:
                yield content

این جداسازی باعث می‌شود:

  • کد رابط ساده‌تر شود.
  • Client در پروژه‌های دیگر قابل استفاده باشد.
  • نوشتن تست آسان‌تر شود.
  • تغییر Provider یا تنظیمات API ساده‌تر شود.
  • منطق کسب‌وکار با رابط کاربری مخلوط نشود.

ایده‌های توسعه پروژه

پس از اجرای نسخه پایه می‌توانید قابلیت‌های زیر را اضافه کنید:

انتخاب چند مدل

امکان انتخاب Model IDهای مختلف و مقایسه پاسخ آن‌ها را فراهم کنید.

ذخیره گفتگو

گفتگوها را با شناسه کاربر در PostgreSQL یا یک پایگاه داده دیگر ذخیره کنید.

پردازش PDF

متن PDF را استخراج، بخش‌بندی و برای خلاصه‌سازی ارسال کنید.

خروجی ساختاریافته

از مدل بخواهید اطلاعات را در قالب JSON برگرداند و نتیجه را در جدول نمایش دهید.

داشبورد مصرف

تعداد درخواست، زمان پاسخ، مدل استفاده‌شده و توکن مصرفی را ثبت و نمایش دهید.

کتابخانه پرامپت

چند پرامپت آماده برای خلاصه‌سازی، بازنویسی، تحلیل و استخراج داده بسازید.

بازخورد کاربر

دکمه‌های پسندیدن و نپسندیدن پاسخ را اضافه و نتیجه را برای ارزیابی کیفیت ذخیره کنید.

RAG

اسناد داخلی را Embedding کنید، بخش‌های مرتبط را بازیابی کنید و همراه سؤال برای مدل بفرستید.

احراز هویت

برای اپلیکیشن داخلی، ورود کاربران و سطح دسترسی اضافه کنید.

چک‌لیست نهایی پروژه

پیش از انتشار اپلیکیشن بررسی کنید:

  • کلید API داخل کد قرار نگرفته است.
  • فایل .env در Git ذخیره نمی‌شود.
  • Model ID معتبر است.
  • Base URL دقیقاً تنظیم شده است.
  • تاریخچه گفتگو محدود شده است.
  • طول ورودی کنترل می‌شود.
  • max_tokens محدود است.
  • خطاهای API مدیریت می‌شوند.
  • متن کامل خطا برای کاربران عمومی نمایش داده نمی‌شود.
  • اپلیکیشن عمومی دارای کنترل مصرف است.
  • فایل‌های بزرگ بدون بررسی ارسال نمی‌شوند.
  • پاسخ‌های مهم پیش از استفاده نهایی بررسی می‌شوند.
  • مدل متناسب با وظیفه انتخاب شده است.
  • قیمت و مصرف مدل بررسی شده است.
  • Secrets در محیط استقرار تنظیم شده‌اند.
  • Docker Image شامل کلید API نیست.
  • برای استفاده چندکاربره، معماری Session بررسی شده است.

پرسش‌های متداول

Streamlit چیست؟

Streamlit یک فریم‌ورک پایتون برای ساخت سریع اپلیکیشن‌های وب تعاملی، داشبوردها و رابط‌های مدل‌های هوش مصنوعی است.

آیا Streamlit به HTML و JavaScript نیاز دارد؟

برای ساخت رابط‌های معمول Streamlit نیازی به HTML و JavaScript ندارید. بیشتر اجزای رابط با توابع پایتون ساخته می‌شوند. برای شخصی‌سازی بسیار پیشرفته ممکن است به CSS یا Componentهای اختصاصی نیاز داشته باشید.

آیا Streamlit رایگان است؟

خود فریم‌ورک Streamlit متن‌باز است. هزینه استقرار، سرور، سرویس‌های جانبی و API هوش مصنوعی به روش استفاده شما بستگی دارد.

آیا می‌توان Streamlit را به API درواره متصل کرد؟

بله. با تنظیم Base URL، API Key و Model ID می‌توانید از کتابخانه پایتون سازگار یا درخواست مستقیم HTTP استفاده کنید.

Base URL:

https://api.darvareh.ir/v1

آیا Streamlit برای ساخت چت‌بات مناسب است؟

بله. اجزایی مانند st.chat_input، st.chat_message، st.session_state و st.write_stream ساخت رابط چت را ساده می‌کنند.

چگونه تاریخچه چت را حفظ کنیم؟

برای نگهداری تاریخچه در طول نشست کاربر از st.session_state استفاده کنید. برای نگهداری دائمی و چنددستگاهی باید اطلاعات را در پایگاه داده ذخیره کنید.

آیا کاربران مختلف تاریخچه یکدیگر را می‌بینند؟

Session State برای نشست‌های مختلف جدا نگه داشته می‌شود، اما داده دائمی نیست. طراحی دقیق اپلیکیشن چندکاربره، Cacheهای مشترک و پایگاه داده باید با دقت انجام شود.

آیا می‌توان اپلیکیشن Streamlit را روی سرور شخصی اجرا کرد؟

بله. می‌توانید برنامه را مستقیماً با Python، داخل Docker یا روی سرویس‌های میزبانی سازگار اجرا کنید.

آیا قراردادن API Key در Streamlit Secrets کافی است؟

Secrets مانع قراردادن مستقیم کلید در کد و Repository می‌شود، اما برای یک اپلیکیشن عمومی همچنان به احراز هویت، محدودیت نرخ و کنترل مصرف نیاز دارید.

چگونه هزینه استفاده را کاهش دهیم؟

تاریخچه را محدود کنید، max_tokens را کاهش دهید، ورودی‌های تکراری را حذف کنید، مدل مناسب انتخاب کنید و برای هر کاربر سقف مصرف تعیین کنید.

Streamlit بهتر است یا Gradio؟

هر دو برای ساخت رابط پایتونی مناسب‌اند. Gradio معمولاً برای Demo سریع مدل‌ها و ورودی‌های چندرسانه‌ای ساده است. Streamlit برای داشبورد، ابزار داده، اپلیکیشن چندبخشی و رابط‌های قابل ترکیب انعطاف بیشتری ارائه می‌دهد.

جمع‌بندی

Streamlit یکی از ساده‌ترین راه‌ها برای تبدیل کد پایتون به یک اپلیکیشن وب تعاملی است. این فریم‌ورک به‌ویژه برای ساخت Demo، داشبورد، ابزارهای داخلی و نمونه اولیه محصولات هوش مصنوعی کاربرد دارد.

در این آموزش یک اپلیکیشن چت کامل ساختیم که:

  • با پایتون و Streamlit اجرا می‌شود.
  • به API درواره متصل است.
  • پاسخ مدل را به‌صورت Streaming نمایش می‌دهد.
  • تاریخچه گفتگو را نگه می‌دارد.
  • Model ID و پارامترهای تولید را قابل تنظیم می‌کند.
  • کلید API را خارج از کد نگه می‌دارد.
  • امکان پاک‌کردن و دانلود مکالمه دارد.
  • خطاها و طول ورودی را کنترل می‌کند.
  • قابلیت استقرار با Docker و Streamlit Community Cloud دارد.

برای شروع ساخت اپلیکیشن خود، در درواره ثبت‌نام کنید، کلید API بسازید و Model ID موردنظر را از صفحه مدل‌ها دریافت کنید.

مقالات مرتبط

برای مطالعه شرایط استفاده و محدودیت‌های مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.

Read more

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی با ترکیب گردش‌کارهای خودکار و مدل‌های هوش مصنوعی، پردازش متن، دسته‌بندی، استخراج اطلاعات و تصمیم‌های پیشنهادی را خودکار می‌کند. در این راهنما، معماری و ساخت نمونه عملی آن با API درواره را می‌آموزید.

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce شیوه‌ای جدید برای خرید اینترنتی است که در آن ایجنت هوش مصنوعی می‌تواند نیاز کاربر را بفهمد، محصولات را جست‌وجو و مقایسه کند و فرایند خرید را پیش ببرد. در این راهنما با معماری، UCP، ACP و پیاده‌سازی آن با API درواره آشنا می‌شوید.