ساخت Regex با هوش مصنوعی؛ آموزش تولید، تست و توضیح عبارت منظم با AI

در این آموزش یاد می‌گیرید Regex دقیق را با AI تولید و آزمایش کنید و یک AI Regex Generator بسازید که نیاز متنی، نمونه‌های مثبت و منفی را به الگوی تست‌شده و قابل توضیح تبدیل می‌کند.

Share
ساخت Regex با هوش مصنوعی؛ آموزش تولید، تست و توضیح عبارت منظم با AI

ساخت Regex با هوش مصنوعی؛ از توضیح نیاز تا الگوی تست‌شده

Regex یا Regular Expression ابزاری قدرتمند برای جست‌وجو، استخراج و اعتبارسنجی متن است. با Regex می‌توان فعالیت‌هایی مانند این‌ها را انجام داد:

  • استخراج شماره سفارش از متن
  • پیدا کردن تاریخ‌ها
  • جداسازی کد محصول
  • اعتبارسنجی قالب یک شناسه داخلی
  • حذف فاصله‌های تکراری
  • استخراج مقدار از لاگ
  • پیدا کردن تگ‌ها و Placeholderها
  • تغییر قالب متن
  • پردازش فایل‌های متنی
  • ساخت Rule برای Search و Replace

مشکل این است که Regexهای پیچیده به‌سرعت ناخوانا می‌شوند:

^(?:[A-Z]{2}-)?\d{4,8}(?:-[a-z0-9]{2,6})?$

حتی اگر این Pattern در نگاه اول درست به نظر برسد، پرسش‌های زیادی باقی می‌ماند:

  • پیشوند اجباری است یا اختیاری؟
  • حروف کوچک مجازند؟
  • عدد فارسی پذیرفته می‌شود؟
  • فاصله ابتدا و انتهای ورودی چه می‌شود؟
  • Match باید تمام رشته را پوشش دهد یا بخشی از آن را؟
  • Dialect مربوط به Python است یا JavaScript؟
  • رفتار \d برای Unicode چیست؟
  • چه ورودی‌هایی نباید Match شوند؟
  • Pattern روی متن طولانی چه عملکردی دارد؟

هوش مصنوعی می‌تواند نیاز زبانی را به Regex تبدیل و الگو را توضیح دهد، اما یک Regex تولیدشده نباید فقط به‌دلیل ظاهر حرفه‌ای پذیرفته شود. هر Pattern باید با مجموعه‌ای از نمونه‌های مثبت، منفی و مرزی آزمایش شود.

در این مقاله یک AI Regex Generator واقعی با پایتون و API درواره می‌سازیم که:

  1. توضیح فارسی نیاز را دریافت می‌کند.
  2. Dialect و نوع Match را مشخص می‌کند.
  3. نمونه‌های مثبت و منفی را می‌گیرد.
  4. Regex و توضیح ساختاریافته تولید می‌کند.
  5. Pattern را Compile می‌کند.
  6. تمام نمونه‌ها را اجرا می‌کند.
  7. خطاهای False Positive و False Negative را گزارش می‌دهد.
  8. حداکثر در چند مرحله محدود Pattern را اصلاح می‌کند.
  9. کد آماده Python و JavaScript می‌سازد.

Regex چیست؟

Regex زبانی برای تعریف الگوی متنی است.

یک Pattern ساده:

\d+

این الگو یک یا چند رقم متوالی را پیدا می‌کند.

نمونه:

شماره سفارش 1258 ثبت شد.

نتیجه:

1258

الگوی دیگری برای شناسه سفارش:

^ORD-\d{6}$

این Pattern فقط رشته‌هایی با ساختار زیر را قبول می‌کند:

ORD-123456

توضیح:

  • ^ ابتدای رشته
  • ORD- متن ثابت
  • \d{6} دقیقاً شش رقم
  • $ انتهای رشته

هوش مصنوعی چگونه به ساخت Regex کمک می‌کند؟

تبدیل توضیح فارسی به Pattern

درخواست:

شناسه باید با PRD- شروع شود و بعد از آن دقیقاً 8 رقم لاتین بیاید.

خروجی مناسب:

^PRD-[0-9]{8}$

توضیح Regex موجود

ورودی:

^(?:[A-Z]{2}-)?[0-9]{4,8}$

توضیح:

  • پیشوند دو حرف بزرگ انگلیسی و - اختیاری است.
  • بخش عددی بین ۴ تا ۸ رقم لاتین دارد.
  • تمام رشته باید با Pattern سازگار باشد.

تولید Test Case

مدل می‌تواند نمونه‌های مثبت، منفی و مرزی پیشنهاد دهد.

تبدیل میان Dialectها

برخی قابلیت‌های Regex در Python، JavaScript، Java، .NET و PCRE متفاوت‌اند. مدل می‌تواند تبدیل اولیه انجام دهد، اما Pattern باید در Runtime مقصد Compile و تست شود.

بهینه‌کردن خوانایی

یک Pattern طولانی می‌تواند به چند بخش نام‌گذاری‌شده یا حالت Verbose تبدیل شود.

استخراج داده با Named Group

^(?P<prefix>[A-Z]{3})-(?P<number>[0-9]{6})$

در Python می‌توان prefix و number را جداگانه دریافت کرد.

مهم‌ترین اصل: Regex بدون مثال ناقص است

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

یک Regex برای شماره سفارش بنویس.

اطلاعات لازم:

  • پیشوند چیست؟
  • تعداد رقم‌ها چقدر است؟
  • رقم فارسی مجاز است؟
  • حروف کوچک پذیرفته می‌شوند؟
  • فاصله مجاز است؟
  • Match روی تمام رشته انجام می‌شود؟
  • نمونه معتبر چیست؟
  • نمونه نامعتبر چیست؟
  • Runtime مقصد چیست؟

درخواست بهتر:

برای Python یک Regex بساز.

قالب معتبر:
- کل رشته باید Match شود.
- با ORD- شروع شود.
- بعد از آن دقیقاً 6 رقم لاتین باشد.
- فاصله مجاز نیست.
- حروف کوچک مجاز نیست.

نمونه‌های معتبر:
ORD-123456
ORD-000001

نمونه‌های نامعتبر:
ord-123456
ORD-12345
ORD-۱۲۳۴۵۶
XORD-123456
ORD-123456-7

Positive Example و Negative Example

نمونه مثبت

ورودی‌ای که باید Match شود:

INV-2026-000125

نمونه منفی

ورودی‌ای که نباید Match شود:

INV-26-125

نمونه مرزی

ورودی‌ای که دقیقاً در حداقل یا حداکثر محدودیت است:

AB-0001
AB-99999999

کیفیت Regex بیشتر از توضیح طولانی، به تنوع Test Caseها وابسته است.

تفاوت Search، Match و Full Match

این سه هدف یکسان نیستند.

الگو در هر بخش متن پیدا شود:

شماره سفارش ORD-123456 ثبت شد.

Prefix Match

رشته از ابتدای خود با الگو آغاز شود، اما ممکن است پس از Match متن دیگری داشته باشد.

Full Match

تمام رشته باید با Pattern سازگار باشد:

ORD-123456

برای اعتبارسنجی یک فیلد معمولاً Full Match مناسب‌تر است.

در Python:

re.search(pattern, text)
re.match(pattern, text)
re.fullmatch(pattern, text)

در JavaScript متد test بر اساس Anchorهای Pattern عمل می‌کند:

pattern.test(value)

برای Full Match در JavaScript معمولاً از ^ و $ استفاده می‌شود.

تفاوت Dialectهای Regex

قابلیتPythonJavaScript
Named Group(?P<name>...)(?<name>...)
Verbose Modere.VERBOSEپشتیبانی مستقیم ندارد
Full Matchre.fullmatchAnchorهای ^ و $
Lookbehindپشتیبانی می‌شوددر Runtimeهای مدرن پشتیبانی می‌شود
Unicode Flagرفتار داخلی Pythonمعمولاً Flag u
Global Matchfindall یا finditerFlag g

Pattern باید برای Runtime مقصد تولید شود.

\d یا [0-9]؟

در Python، \d می‌تواند ارقام Unicode را نیز Match کند؛ از جمله در بسیاری از شرایط ارقام فارسی:

۱۲۳۴۵۶

اگر فقط رقم لاتین می‌خواهید، صریح بنویسید:

[0-9]

اگر هر رقم Unicode مورد قبول است:

\d

در Requirement باید این تفاوت مشخص شود.

ساخت Regex برای کد محصول

Requirement:

کد محصول:
- با سه حرف بزرگ انگلیسی شروع شود.
- سپس خط تیره قرار گیرد.
- بعد از آن بین 4 تا 8 رقم لاتین باشد.
- کل رشته باید Match شود.

Pattern:

^[A-Z]{3}-[0-9]{4,8}$

نمونه‌های معتبر:

PRD-1234
ABC-00001234

نمونه‌های نامعتبر:

prd-1234
AB-1234
ABCD-1234
PRD-۱۲۳۴
PRD-123
PRD-123456789

ساخت Regex برای استخراج تاریخ ISO

Requirement:

تاریخ‌هایی با قالب YYYY-MM-DD را از متن استخراج کن.

Pattern اولیه:

\b[0-9]{4}-[0-9]{2}-[0-9]{2}\b

این Pattern قالب را تشخیص می‌دهد، اما معتبر بودن تقویمی را تضمین نمی‌کند:

2026-99-45

روش مناسب:

  1. Regex قالب را استخراج کند.
  2. کتابخانه تاریخ مقدار را Parse و اعتبارسنجی کند.
from datetime import date


value = "2026-07-20"
parsed = date.fromisoformat(value)

Regex نباید وظیفه Parser تخصصی تاریخ را به‌طور کامل بر عهده بگیرد.

ساخت Regex برای استخراج Placeholder

متن:

سلام {{customer_name}}، سفارش {{order_id}} آماده است.

Pattern:

\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}

نتایج:

customer_name
order_id

در Python:

import re


pattern = re.compile(
    r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}"
)

values = pattern.findall(
    "سلام {{customer_name}}، "
    "سفارش {{order_id}} آماده است."
)

print(values)

ساخت Regex برای شماره موبایل ایران

قبل از تولید Pattern باید Scope مشخص شود.

فرمت‌های احتمالی:

09121234567
989121234567
+989121234567
00989121234567

آیا همه این قالب‌ها معتبرند یا فقط قالب داخلی؟

اگر Requirement فقط این باشد:

شماره با 09 شروع شود و در مجموع 11 رقم لاتین داشته باشد.

Pattern:

^09[0-9]{9}$

این Pattern فقط قالب را بررسی می‌کند. تعریف کامل‌تر باید بر اساس نیاز واقعی سامانه انجام شود و نباید قالب‌های جدید بدون تصمیم محصول اضافه شوند.

ساخت Regex برای ایمیل

اعتبارسنجی کامل تمام حالت‌های استاندارد ایمیل با یک Regex ساده مناسب نیست. برای بسیاری از فرم‌ها یک کنترل قالب عمومی کافی است:

^[^@\s]+@[^@\s]+\.[^@\s]+$

اما برای سیستم واقعی بهتر است:

  • Trim انجام شود.
  • طول کنترل شود.
  • آدرس Normalize شود.
  • Parser یا کتابخانه مناسب استفاده شود.
  • تأیید عملی آدرس با جریان محصول انجام شود.

هدف Regex باید «بررسی قالب پایه» باشد، نه اثبات وجود یا قابل استفاده بودن آدرس.

ساخت پروژه AI Regex Generator

ابزار ما این ورودی را دریافت می‌کند:

{
  "description": "شناسه با ORD- شروع و بعد از آن شش رقم لاتین باشد",
  "dialect": "python",
  "match_mode": "fullmatch",
  "positive_examples": [
    "ORD-123456",
    "ORD-000001"
  ],
  "negative_examples": [
    "ord-123456",
    "ORD-12345",
    "ORD-۱۲۳۴۵۶"
  ]
}

خروجی:

{
  "pattern": "^ORD-[0-9]{6}$",
  "flags": [],
  "explanation": [],
  "test_results": [],
  "all_tests_passed": true
}

ایجاد پروژه

mkdir ai-regex-generator
cd ai-regex-generator

python -m venv .venv

فعال‌سازی در Linux و macOS:

source .venv/bin/activate

فعال‌سازی در Windows:

.venv\Scripts\Activate.ps1

نصب وابستگی‌ها:

pip install \
  openai \
  python-dotenv \
  pydantic \
  fastapi \
  uvicorn \
  regex

ساخت پوشه‌ها:

mkdir regex_generator output

فایل‌های زیر را ایجاد کنید:

regex_generator/__init__.py
regex_generator/schemas.py
regex_generator/generator.py
regex_generator/tester.py
regex_generator/repair.py
regex_generator/examples.py
main.py

تنظیم API درواره

فایل .env:

DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH

فایل .gitignore:

.env
.venv/
__pycache__/
output/

برای دریافت API Key در درواره ثبت‌نام کنید. مدل مناسب را از صفحه مدل‌های درواره انتخاب کنید.

تعریف Schemaها

فایل regex_generator/schemas.py:

from typing import Literal

from pydantic import BaseModel, Field


RegexDialect = Literal[
    "python",
    "javascript",
]

MatchMode = Literal[
    "search",
    "match",
    "fullmatch",
]

RegexFlag = Literal[
    "IGNORECASE",
    "MULTILINE",
    "DOTALL",
    "VERBOSE",
]


class RegexRequest(BaseModel):
    description: str = Field(
        min_length=5,
        max_length=3000,
    )
    dialect: RegexDialect
    match_mode: MatchMode
    positive_examples: list[str] = Field(
        min_length=1,
        max_length=100,
    )
    negative_examples: list[str] = Field(
        min_length=1,
        max_length=100,
    )
    extraction_groups: list[str] = Field(
        default_factory=list
    )
    constraints: list[str] = Field(
        default_factory=list
    )


class RegexPartExplanation(BaseModel):
    part: str
    meaning: str


class GeneratedRegex(BaseModel):
    dialect: RegexDialect
    pattern: str
    flags: list[RegexFlag] = Field(
        default_factory=list
    )
    match_mode: MatchMode
    explanation: list[
        RegexPartExplanation
    ]
    assumptions: list[str] = Field(
        default_factory=list
    )
    limitations: list[str] = Field(
        default_factory=list
    )
    python_example: str
    javascript_example: str


class RegexTestResult(BaseModel):
    value: str
    expected_match: bool
    actual_match: bool
    passed: bool
    matched_text: str | None = None
    groups: dict[str, str | None] = Field(
        default_factory=dict
    )
    error: str | None = None


class RegexResponse(BaseModel):
    generated: GeneratedRegex
    test_results: list[
        RegexTestResult
    ]
    all_tests_passed: bool
    repair_attempts: int
    warnings: list[str] = Field(
        default_factory=list
    )

تولید Regex با API درواره

فایل regex_generator/generator.py:

import json
import os

from dotenv import load_dotenv
from openai import OpenAI
from pydantic import ValidationError

from regex_generator.schemas import (
    GeneratedRegex,
    RegexRequest,
)


load_dotenv()

api_key = os.getenv("DARVAREH_API_KEY")
model = os.getenv("DARVAREH_MODEL")

if not api_key:
    raise RuntimeError(
        "DARVAREH_API_KEY is not configured."
    )

if not model:
    raise RuntimeError(
        "DARVAREH_MODEL is not configured."
    )

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


SYSTEM_PROMPT = """
تو یک متخصص Regular Expression هستی.

وظیفه:
بر اساس Requirement و Test Caseها یک Regex تولید کن.

قواعد:
- Pattern باید برای Dialect درخواستی معتبر باشد.
- Match Mode را دقیق رعایت کن.
- تمام نمونه‌های مثبت باید Match شوند.
- تمام نمونه‌های منفی نباید Match شوند.
- اگر فقط ارقام لاتین لازم‌اند از [0-9] استفاده کن.
- قابلیت خارج از Dialect مقصد استفاده نکن.
- Pattern را بیش از حد عمومی نساز.
- Requirement جدید اختراع نکن.
- گروه‌های استخراجی فقط در صورت درخواست ساخته شوند.
- مثال‌های Python و JavaScript را تولید کن.
- خروجی فقط JSON معتبر باشد.
"""


OUTPUT_TEMPLATE = {
    "dialect": "python",
    "pattern": "^ORD-[0-9]{6}$",
    "flags": [],
    "match_mode": "fullmatch",
    "explanation": [
        {
            "part": "^",
            "meaning": "ابتدای رشته",
        },
        {
            "part": "ORD-",
            "meaning": "پیشوند ثابت",
        },
        {
            "part": "[0-9]{6}",
            "meaning": "دقیقاً شش رقم لاتین",
        },
        {
            "part": "$",
            "meaning": "انتهای رشته",
        },
    ],
    "assumptions": [],
    "limitations": [],
    "python_example": "string",
    "javascript_example": "string",
}


def generate_regex(
    request: RegexRequest,
) -> GeneratedRegex:
    response = client.chat.completions.create(
        model=model,
        temperature=0,
        messages=[
            {
                "role": "system",
                "content": SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": (
                    "درخواست:\n\n"
                    + request.model_dump_json(
                        indent=2
                    )
                    + "\n\nقالب خروجی:\n"
                    + json.dumps(
                        OUTPUT_TEMPLATE,
                        ensure_ascii=False,
                        indent=2,
                    )
                ),
            },
        ],
    )

    raw_output = (
        response.choices[0]
        .message.content
    )

    if not raw_output:
        raise RuntimeError(
            "The model returned an "
            "empty regex."
        )

    try:
        parsed = json.loads(raw_output)
    except json.JSONDecodeError as error:
        raise RuntimeError(
            f"Invalid JSON from model: "
            f"{error}"
        ) from error

    try:
        generated = (
            GeneratedRegex
            .model_validate(parsed)
        )
    except ValidationError as error:
        raise RuntimeError(
            f"Generated regex failed "
            f"validation: {error}"
        ) from error

    if generated.dialect != request.dialect:
        raise RuntimeError(
            "Generated dialect does not "
            "match requested dialect."
        )

    if (
        generated.match_mode
        != request.match_mode
    ):
        raise RuntimeError(
            "Generated match mode does not "
            "match requested mode."
        )

    return generated

تبدیل Flagها به Python Regex Flags

فایل regex_generator/tester.py:

import regex

from regex_generator.schemas import (
    GeneratedRegex,
    RegexRequest,
    RegexTestResult,
)


REGEX_TIMEOUT_SECONDS = 0.05


def build_flags(
    flag_names: list[str],
) -> int:
    flags = 0

    mapping = {
        "IGNORECASE": regex.IGNORECASE,
        "MULTILINE": regex.MULTILINE,
        "DOTALL": regex.DOTALL,
        "VERBOSE": regex.VERBOSE,
    }

    for flag_name in flag_names:
        flags |= mapping[flag_name]

    return flags


def execute_match(
    compiled,
    value: str,
    match_mode: str,
):
    if match_mode == "search":
        return compiled.search(
            value,
            timeout=REGEX_TIMEOUT_SECONDS,
        )

    if match_mode == "match":
        return compiled.match(
            value,
            timeout=REGEX_TIMEOUT_SECONDS,
        )

    if match_mode == "fullmatch":
        return compiled.fullmatch(
            value,
            timeout=REGEX_TIMEOUT_SECONDS,
        )

    raise ValueError(
        f"Unsupported match mode: "
        f"{match_mode}"
    )


def test_generated_regex(
    request: RegexRequest,
    generated: GeneratedRegex,
) -> list[RegexTestResult]:
    flags = build_flags(
        generated.flags
    )

    try:
        compiled = regex.compile(
            generated.pattern,
            flags,
        )
    except regex.error as error:
        return [
            RegexTestResult(
                value="",
                expected_match=True,
                actual_match=False,
                passed=False,
                error=(
                    f"Regex compile error: "
                    f"{error}"
                ),
            )
        ]

    cases = [
        (value, True)
        for value in (
            request.positive_examples
        )
    ]

    cases.extend(
        (value, False)
        for value in (
            request.negative_examples
        )
    )

    results: list[
        RegexTestResult
    ] = []

    for value, expected_match in cases:
        try:
            match = execute_match(
                compiled,
                value,
                request.match_mode,
            )

            actual_match = (
                match is not None
            )

            groups = {}

            if match is not None:
                groups = {
                    key: group_value
                    for key, group_value
                    in match.groupdict().items()
                }

            results.append(
                RegexTestResult(
                    value=value,
                    expected_match=(
                        expected_match
                    ),
                    actual_match=actual_match,
                    passed=(
                        actual_match
                        == expected_match
                    ),
                    matched_text=(
                        match.group(0)
                        if match
                        else None
                    ),
                    groups=groups,
                )
            )

        except TimeoutError:
            results.append(
                RegexTestResult(
                    value=value,
                    expected_match=(
                        expected_match
                    ),
                    actual_match=False,
                    passed=False,
                    error=(
                        "Regex evaluation "
                        "exceeded timeout."
                    ),
                )
            )

    return results

کتابخانه regex امکان Timeout برای Match را فراهم می‌کند. این محدودیت برای جلوگیری از مصرف طولانی CPU روی Pattern یا ورودی نامناسب مفید است.

چرا Pattern را با Runtime واقعی تست می‌کنیم؟

ممکن است مدل Pattern مربوط به JavaScript بسازد:

(?<code>[A-Z]+)

و آن را برای Python پیشنهاد دهد. در Python، Named Group متداول به شکل زیر است:

(?P<code>[A-Z]+)

Compile کردن Pattern در Runtime مقصد، خطاهای Dialect را پیش از استفاده آشکار می‌کند.

در نمونه آموزشی، اجرای تست با Python انجام می‌شود. برای اطمینان از Regex جاوااسکریپت باید Pattern در Node.js نیز Compile و تست شود.

اصلاح Regex شکست‌خورده

اگر نمونه‌ها Pass نشوند، نتیجه واقعی را به مدل برمی‌گردانیم.

فایل regex_generator/repair.py:

import json

from regex_generator.generator import (
    OUTPUT_TEMPLATE,
    SYSTEM_PROMPT,
    client,
    model,
)
from regex_generator.schemas import (
    GeneratedRegex,
    RegexRequest,
    RegexTestResult,
)


REPAIR_PROMPT = """
Regex قبلی برخی Test Caseها را پاس نکرده است.

فقط بر اساس Requirement و نتایج واقعی تست، Pattern را اصلاح کن.

قواعد:
- نمونه مثبت شکست‌خورده باید Match شود.
- نمونه منفی شکست‌خورده نباید Match شود.
- نمونه‌های قبلاً موفق را خراب نکن.
- Requirement جدید اضافه نکن.
- Pattern را بیش از حد عمومی نکن.
- Dialect و Match Mode را تغییر نده.
- خروجی فقط JSON معتبر باشد.
"""


def repair_regex(
    request: RegexRequest,
    generated: GeneratedRegex,
    results: list[RegexTestResult],
) -> GeneratedRegex:
    failed_results = [
        result.model_dump()
        for result in results
        if not result.passed
    ]

    payload = {
        "request": request.model_dump(),
        "previous_regex": (
            generated.model_dump()
        ),
        "failed_tests": failed_results,
    }

    response = client.chat.completions.create(
        model=model,
        temperature=0,
        messages=[
            {
                "role": "system",
                "content": (
                    SYSTEM_PROMPT
                    + "\n"
                    + REPAIR_PROMPT
                ),
            },
            {
                "role": "user",
                "content": (
                    json.dumps(
                        payload,
                        ensure_ascii=False,
                        indent=2,
                    )
                    + "\n\nقالب خروجی:\n"
                    + json.dumps(
                        OUTPUT_TEMPLATE,
                        ensure_ascii=False,
                        indent=2,
                    )
                ),
            },
        ],
    )

    raw_output = (
        response.choices[0]
        .message.content
    )

    if not raw_output:
        raise RuntimeError(
            "The model returned an "
            "empty repaired regex."
        )

    repaired = (
        GeneratedRegex.model_validate(
            json.loads(raw_output)
        )
    )

    if repaired.dialect != request.dialect:
        raise RuntimeError(
            "Repair changed the dialect."
        )

    if (
        repaired.match_mode
        != request.match_mode
    ):
        raise RuntimeError(
            "Repair changed match mode."
        )

    return repaired

ساخت Orchestrator

فایل regex_generator/service.py:

from regex_generator.generator import (
    generate_regex,
)
from regex_generator.repair import (
    repair_regex,
)
from regex_generator.schemas import (
    RegexRequest,
    RegexResponse,
)
from regex_generator.tester import (
    test_generated_regex,
)


MAX_REPAIR_ATTEMPTS = 2


def build_and_test_regex(
    request: RegexRequest,
) -> RegexResponse:
    generated = generate_regex(
        request
    )

    repair_attempts = 0

    while True:
        test_results = (
            test_generated_regex(
                request,
                generated,
            )
        )

        all_tests_passed = all(
            result.passed
            for result in test_results
        )

        if all_tests_passed:
            break

        if (
            repair_attempts
            >= MAX_REPAIR_ATTEMPTS
        ):
            break

        generated = repair_regex(
            request,
            generated,
            test_results,
        )

        repair_attempts += 1

    warnings = []

    if not all_tests_passed:
        warnings.append(
            "Regex هنوز تمام Test Caseها "
            "را پاس نکرده است و نباید بدون "
            "بررسی استفاده شود."
        )

    if (
        len(request.positive_examples)
        < 3
    ):
        warnings.append(
            "تعداد نمونه‌های مثبت برای "
            "ارزیابی قابل اتکا کم است."
        )

    if (
        len(request.negative_examples)
        < 3
    ):
        warnings.append(
            "تعداد نمونه‌های منفی برای "
            "ارزیابی قابل اتکا کم است."
        )

    return RegexResponse(
        generated=generated,
        test_results=test_results,
        all_tests_passed=(
            all_tests_passed
        ),
        repair_attempts=(
            repair_attempts
        ),
        warnings=warnings,
    )

تعداد تلاش اصلاح محدود است. اگر بعد از دو مرحله Regex درست نشد، سیستم باید از کاربر نمونه یا توضیح بیشتری بخواهد.

ساخت API با FastAPI

فایل main.py:

from fastapi import (
    FastAPI,
    HTTPException,
)

from regex_generator.schemas import (
    RegexRequest,
    RegexResponse,
)
from regex_generator.service import (
    build_and_test_regex,
)


app = FastAPI(
    title="Darvareh AI Regex Generator",
    version="1.0.0",
)


@app.get("/health")
def health_check():
    return {
        "status": "ok",
    }


@app.post(
    "/generate",
    response_model=RegexResponse,
)
def generate_regex_endpoint(
    request: RegexRequest,
):
    overlapping_examples = (
        set(request.positive_examples)
        & set(request.negative_examples)
    )

    if overlapping_examples:
        raise HTTPException(
            status_code=422,
            detail=(
                "The same value cannot be both "
                "positive and negative: "
                + ", ".join(
                    sorted(
                        overlapping_examples
                    )
                )
            ),
        )

    try:
        return build_and_test_regex(
            request
        )

    except Exception as error:
        raise HTTPException(
            status_code=500,
            detail=(
                "Regex generation failed."
            ),
        ) from error

اجرای API

uvicorn main:app --reload

مستندات تعاملی:

http://127.0.0.1:8000/docs

آزمایش با curl

curl -X POST \
  http://127.0.0.1:8000/generate \
  -H "Content-Type: application/json" \
  -d '{
    "description": "کل رشته باید با ORD- شروع شود و پس از آن دقیقاً شش رقم لاتین باشد",
    "dialect": "python",
    "match_mode": "fullmatch",
    "positive_examples": [
      "ORD-123456",
      "ORD-000001",
      "ORD-999999"
    ],
    "negative_examples": [
      "ord-123456",
      "ORD-12345",
      "ORD-1234567",
      "ORD-۱۲۳۴۵۶",
      "XORD-123456"
    ],
    "extraction_groups": [],
    "constraints": [
      "فاصله مجاز نیست",
      "فقط رقم لاتین مجاز است"
    ]
  }'

پاسخ مورد انتظار:

{
  "generated": {
    "dialect": "python",
    "pattern": "^ORD-[0-9]{6}$",
    "flags": [],
    "match_mode": "fullmatch",
    "explanation": [
      {
        "part": "^",
        "meaning": "ابتدای رشته"
      },
      {
        "part": "ORD-",
        "meaning": "پیشوند ثابت"
      },
      {
        "part": "[0-9]{6}",
        "meaning": "دقیقاً شش رقم لاتین"
      },
      {
        "part": "$",
        "meaning": "انتهای رشته"
      }
    ],
    "assumptions": [],
    "limitations": [],
    "python_example": "import re\n...",
    "javascript_example": "const pattern = /^ORD-[0-9]{6}$/;"
  },
  "all_tests_passed": true,
  "repair_attempts": 0,
  "warnings": []
}

استفاده در Python

import re


ORDER_ID_PATTERN = re.compile(
    r"^ORD-[0-9]{6}$"
)


def is_valid_order_id(
    value: str,
) -> bool:
    return (
        ORDER_ID_PATTERN.fullmatch(
            value
        )
        is not None
    )

آزمایش:

assert is_valid_order_id(
    "ORD-123456"
)

assert not is_valid_order_id(
    "ORD-۱۲۳۴۵۶"
)

استفاده در JavaScript

const orderIdPattern = /^ORD-[0-9]{6}$/;

function isValidOrderId(value) {
  return orderIdPattern.test(value);
}

console.log(isValidOrderId("ORD-123456"));
console.log(isValidOrderId("ORD-۱۲۳۴۵۶"));

اگر Pattern دارای Flag g باشد، استفاده مکرر از test می‌تواند به‌دلیل تغییر lastIndex رفتار متفاوتی داشته باشد. برای Validation معمولاً Flag سراسری لازم نیست.

ساخت Named Group

Requirement:

شناسه فاکتور با قالب INV-YYYY-NNNNNN است.
سال و شماره را جداگانه استخراج کن.

Python:

^INV-(?P<year>[0-9]{4})-(?P<number>[0-9]{6})$

استفاده:

import re


pattern = re.compile(
    r"^INV-(?P<year>[0-9]{4})-"
    r"(?P<number>[0-9]{6})$"
)

match = pattern.fullmatch(
    "INV-2026-000125"
)

if match:
    print(match.groupdict())

خروجی:

{
    "year": "2026",
    "number": "000125",
}

JavaScript:

^INV-(?<year>[0-9]{4})-(?<number>[0-9]{6})$

استفاده از Raw String در Python

بهتر است Patternها با Raw String نوشته شوند:

pattern = r"\d+\.\d+"

به‌جای:

pattern = "\\d+\\.\\d+"

Raw String خوانایی را بهتر می‌کند و تداخل Escapeهای Python با Regex را کاهش می‌دهد.

Regex Verbose برای خوانایی

Pattern پیچیده:

import re


pattern = re.compile(
    r"""
    ^
    (?P<prefix>[A-Z]{3})
    -
    (?P<year>[0-9]{4})
    -
    (?P<number>[0-9]{6})
    $
    """,
    re.VERBOSE,
)

در حالت Verbose فاصله و Commentهای بیرون Character Class معمولاً نادیده گرفته می‌شوند. این قابلیت برای نگهداری Patternهای پیچیده مناسب است.

Test Suite مستقل برای Regex

import re

import pytest


PATTERN = re.compile(
    r"^ORD-[0-9]{6}$"
)


@pytest.mark.parametrize(
    "value",
    [
        "ORD-123456",
        "ORD-000001",
        "ORD-999999",
    ],
)
def test_valid_order_ids(value):
    assert PATTERN.fullmatch(value)


@pytest.mark.parametrize(
    "value",
    [
        "ord-123456",
        "ORD-12345",
        "ORD-1234567",
        "ORD-۱۲۳۴۵۶",
        "XORD-123456",
        " ORD-123456",
        "ORD-123456 ",
        "",
    ],
)
def test_invalid_order_ids(value):
    assert not PATTERN.fullmatch(
        value
    )

Regex باید مانند هر منطق دیگری در Test Suite پروژه قرار گیرد.

تست Property-based برای Regex

با Hypothesis می‌توان داده بیشتری تولید کرد:

pip install hypothesis

نمونه:

import re

from hypothesis import (
    given,
    strategies as st,
)


PATTERN = re.compile(
    r"^ORD-[0-9]{6}$"
)


@given(
    number=st.integers(
        min_value=0,
        max_value=999999,
    )
)
def test_accepts_all_six_digit_order_ids(
    number,
):
    value = f"ORD-{number:06d}"

    assert PATTERN.fullmatch(value)

این Test تضمین می‌کند تمام اعداد شش‌رقمی قالب‌بندی‌شده پذیرفته شوند.

ساخت Regex برای Search and Replace

هدف:

فاصله‌های چندگانه را به یک فاصله تبدیل کن.

Pattern:

[ \t]+

جایگزین:

یک فاصله

Python:

import re


cleaned = re.sub(
    r"[ \t]+",
    " ",
    text,
)

اگر Line Break باید حفظ شود، استفاده از \s+ مناسب نیست؛ زیرا \s می‌تواند Newline را نیز Match کند.

Greedy و Lazy

متن:

<b>اول</b><b>دوم</b>

Greedy:

<b>.*</b>

ممکن است کل بخش زیر را Match کند:

<b>اول</b><b>دوم</b>

Lazy:

<b>.*?</b>

Matchهای جداگانه تولید می‌کند.

بااین‌حال برای پردازش HTML پیچیده بهتر است از Parser استفاده شود، نه Regex.

Lookahead و Lookbehind

مثال: عددی که بعد از price= آمده است:

(?<=price=)[0-9]+

اما Lookbehind در Dialectها و Runtimeهای مختلف محدودیت‌هایی دارد. راه ساده‌تر با Capture Group:

price=([0-9]+)

تا جای ممکن Pattern قابل‌حمل و قابل‌فهم را ترجیح دهید.

تشخیص Pattern بیش از حد عمومی

Pattern:

.*123.*

ممکن است نمونه مثبت را Match کند، اما احتمالاً نمونه‌های منفی زیادی نیز می‌پذیرد.

برای کاهش Overfitting و Underfitting:

  • نمونه‌های منفی مشابه نمونه مثبت بسازید.
  • یک کاراکتر را در هر نمونه تغییر دهید.
  • طول کمتر و بیشتر را آزمایش کنید.
  • فاصله ابتدا و انتها را آزمایش کنید.
  • Unicode را آزمایش کنید.
  • حروف کوچک و بزرگ را آزمایش کنید.
  • متن اضافه قبل و بعد را آزمایش کنید.

تولید خودکار Test Case با AI

پرامپت:

برای Requirement و Regex زیر Test Case تولید کن.

دسته‌ها:
- Valid Normal
- Minimum Boundary
- Maximum Boundary
- One Character Too Short
- One Character Too Long
- Wrong Prefix
- Wrong Case
- Leading Whitespace
- Trailing Whitespace
- Unicode Digit
- Extra Suffix
- Empty String

قواعد:
- نتیجه مورد انتظار هر مورد را مشخص کن.
- Test Case تکراری تولید نکن.
- Requirement جدید اختراع نکن.

Testهای تولیدشده نیز باید بازبینی شوند؛ زیرا ممکن است مدل نتیجه مورد انتظار را اشتباه تعیین کند.

استفاده از Regex برای استخراج، نه Parse کامل

Regex برای الگوهای محلی مناسب است:

  • شناسه
  • Token
  • Placeholder
  • بخش مشخص Log
  • قالب ساده تاریخ
  • Search and Replace

برای ساختارهای پیچیده بهتر است Parser استفاده شود:

  • JSON
  • XML
  • HTML پیچیده
  • کد برنامه‌نویسی
  • SQL
  • آدرس کامل
  • تاریخ معتبر تقویمی
  • فرمت‌های دارای Grammar تو در تو

بررسی عملکرد Regex

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

Patternهای دارای تکرار تو در تو نیازمند توجه‌اند:

(a+)+$

روش‌های کاهش مشکل عملکردی:

  • Pattern را ساده کنید.
  • Anchor اضافه کنید.
  • ورودی را محدود کنید.
  • از تکرار تو در تو اجتناب کنید.
  • Timeout بگذارید.
  • روی ورودی نزدیک به حالت شکست Benchmark بگیرید.
  • Regex را یک‌بار Compile و دوباره استفاده کنید.

Benchmark ساده

import time
import regex


pattern = regex.compile(
    r"^ORD-[0-9]{6}$"
)

samples = [
    "ORD-123456",
    "ORD-999999",
    "ORD-12345X",
] * 10_000

started_at = time.perf_counter()

for sample in samples:
    pattern.fullmatch(
        sample,
        timeout=0.05,
    )

elapsed = (
    time.perf_counter()
    - started_at
)

print(f"Elapsed: {elapsed:.4f}s")

Benchmark باید روی داده شبیه ورودی واقعی انجام شود.

پشتیبانی از JavaScript در ابزار

برای تست واقعی Pattern جاوااسکریپت، می‌توان یک Runner Node.js ساخت:

const fs = require("fs");

const input = JSON.parse(
  fs.readFileSync(0, "utf8")
);

const regex = new RegExp(
  input.pattern,
  input.flags
);

const results = input.cases.map((item) => ({
  value: item.value,
  expected: item.expected,
  actual: regex.test(item.value),
}));

process.stdout.write(
  JSON.stringify(results)
);

Backend داده تست را از stdin ارسال و خروجی JSON را دریافت می‌کند. Runner باید Timeout داشته باشد و در محیط محدود اجرا شود.

ساخت کتابخانه Patternهای تأییدشده

به‌جای تولید دوباره Regexهای پرتکرار، Patternهای تأییدشده را ذخیره کنید:

{
  "name": "internal_order_id",
  "version": 2,
  "dialect": "python",
  "pattern": "^ORD-[0-9]{6}$",
  "match_mode": "fullmatch",
  "flags": [],
  "test_suite": {
    "positive": [],
    "negative": []
  },
  "status": "approved"
}

مزایا:

  • نسخه‌بندی
  • استفاده مجدد
  • تست Regression
  • ثبت Requirement
  • جلوگیری از تولید Pattern متفاوت
  • امکان مهاجرت Dialect

ارزیابی AI Regex Generator

Dataset مرجع:

  • شناسه ساده
  • Prefix اختیاری
  • طول متغیر
  • Named Group
  • Unicode
  • Multiline
  • Search
  • Full Match
  • Search and Replace
  • Pattern ناممکن با Requirement متناقض
  • تفاوت Python و JavaScript
  • نمونه‌های مثبت و منفی مشابه

معیارها:

معیارتوضیح
Compile Rateدرصد Patternهای قابل Compile
Positive Recallنمونه‌های مثبت پذیرفته‌شده
Negative Rejectionنمونه‌های منفی ردشده
Dialect Accuracyسازگاری با Runtime مقصد
Group Accuracyاستخراج صحیح گروه‌ها
Requirement Alignmentتطابق با توضیح
Repair Successموفقیت چرخه اصلاح
Performanceزمان Match
Explanation Accuracyتوضیح درست Pattern
Human Acceptanceدرصد Patternهای پذیرفته‌شده

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

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

  • پیروی دقیق از Requirement
  • شناخت Regex Dialect
  • تولید JSON معتبر
  • درک نمونه‌های مثبت و منفی
  • توضیح Pattern
  • اصلاح بر اساس نتیجه تست
  • حفظ Constraintها

برای Patternهای ساده، مدل سریع و اقتصادی کافی است. برای الگوهای چندگروهی و تبدیل میان Dialectها، مدل قوی‌تر ممکن است نتیجه بهتری بدهد.

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

اشتباهات رایج

درخواست Regex بدون مثال

حداقل چند نمونه مثبت و منفی ارائه دهید.

مشخص‌نکردن Dialect

Regex Python و JavaScript در برخی قابلیت‌ها متفاوت‌اند.

استفاده از \d برای رقم لاتین

اگر فقط رقم لاتین لازم است، [0-9] بنویسید.

استفاده از Search برای Validation

برای اعتبارسنجی معمولاً Full Match مناسب‌تر است.

اعتماد به ظاهر Pattern

Regex باید Compile و روی Test Suite اجرا شود.

تلاش برای Parse ساختار پیچیده

برای HTML، JSON یا کد از Parser استفاده کنید.

نبود محدودیت طول ورودی

حتی Pattern ساده نیز باید روی ورودی کنترل‌شده استفاده شود.

اصلاح نامحدود با مدل

تعداد تلاش‌ها باید محدود و Failureها به کاربر گزارش شوند.

ذخیره‌نکردن Requirement

Pattern بدون توضیح و تست در آینده قابل نگهداری نیست.

نقشه راه Production

مرحله اول: Generator دستی

Requirement و Test Case وارد و Pattern نمایش داده شود.

مرحله دوم: اجرای Test

تمام نمونه‌ها در Runtime مقصد اجرا شوند.

مرحله سوم: Repair محدود

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

مرحله چهارم: Approval

Pattern پس از بازبینی ذخیره شود.

مرحله پنجم: Versioning

هر تغییر Pattern نسخه جدید داشته باشد.

مرحله ششم: CI

Test Suite Patternهای تأییدشده در CI اجرا شود.

چک‌لیست Regex تولیدشده

  • Dialect مشخص است.
  • Match Mode مشخص است.
  • Requirement دقیق است.
  • نمونه مثبت کافی وجود دارد.
  • نمونه منفی مشابه وجود دارد.
  • مرزهای طول آزمایش شده‌اند.
  • فاصله ابتدا و انتها بررسی شده است.
  • رفتار Unicode مشخص است.
  • Pattern در Runtime مقصد Compile می‌شود.
  • تمام Test Caseها Pass شده‌اند.
  • Groupهای استخراجی بررسی شده‌اند.
  • عملکرد روی ورودی بلند آزمایش شده است.
  • Pattern و Test Suite نسخه‌بندی شده‌اند.
  • توضیح Pattern ذخیره شده است.
  • API Key فقط در Backend قرار دارد.

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

Regex چیست؟

Regex یا Regular Expression زبانی برای تعریف الگوهای متنی است و برای جست‌وجو، استخراج، جایگزینی و اعتبارسنجی قالب متن استفاده می‌شود.

آیا هوش مصنوعی می‌تواند Regex بسازد؟

بله. اگر Requirement، Dialect و نمونه‌های مثبت و منفی را ارائه دهید، مدل می‌تواند Pattern تولید کند. نتیجه باید در Runtime مقصد آزمایش شود.

چگونه Regex تولیدشده را تست کنیم؟

Pattern را Compile و روی مجموعه‌ای از ورودی‌های معتبر، نامعتبر و مرزی اجرا کنید. نتیجه واقعی باید با انتظار هر Test Case مقایسه شود.

تفاوت Regex در Python و JavaScript چیست؟

Named Group، Flagها، Full Match و برخی قابلیت‌ها در این دو محیط متفاوت‌اند. Dialect باید هنگام تولید مشخص شود.

چرا \d ارقام فارسی را Match می‌کند؟

در برخی Runtimeها از جمله Python، \d می‌تواند ارقام Unicode را بپذیرد. برای فقط رقم لاتین از [0-9] استفاده کنید.

آیا می‌توان با Regex ایمیل را کاملاً اعتبارسنجی کرد؟

Regex ساده برای بررسی قالب پایه مناسب است، اما برای اعتبارسنجی کامل بهتر است از Parser و جریان تأیید مناسب محصول استفاده شود.

آیا می‌توان Pattern تولیدشده را مستقیم در Production استفاده کرد؟

خیر. ابتدا Compile، Test Case، ورودی طولانی، Dialect و رفتار مرزی را بررسی و Pattern را نسخه‌بندی کنید.

بهترین مدل برای تولید Regex چیست؟

مدل باید در پیروی از Constraint، شناخت Dialect و تولید خروجی ساختاریافته مناسب باشد. مدل‌های موجود را در صفحه مدل‌های درواره مقایسه کنید.

API درواره چگونه متصل می‌شود؟

در Backend، base_url را روی https://api.darvareh.ir/v1 قرار دهید و Requirement و Test Caseها را برای مدل ارسال کنید. Pattern در Backend Compile و آزمایش می‌شود.

جمع‌بندی

هوش مصنوعی می‌تواند ساخت و توضیح Regex را بسیار سریع‌تر کند، اما Pattern تولیدشده فقط زمانی قابل استفاده است که با Requirement روشن و Test Caseهای کافی همراه باشد.

روش مناسب این است که ابتدا Dialect، Match Mode، نمونه‌های مثبت، نمونه‌های منفی و Constraintها مشخص شوند. مدل Pattern و توضیح آن را تولید کند، سپس Backend آن را در Runtime واقعی Compile و آزمایش کند. اگر تستی شکست خورد، فقط نتیجه واقعی همان تست‌ها برای اصلاح محدود به مدل برگردانده شود.

در پروژه این مقاله یک AI Regex Generator با Python، FastAPI، Pydantic، کتابخانه regex و API درواره ساختیم. این ابزار Regex را تولید، با Timeout اجرا، نتیجه نمونه‌ها را مقایسه و حداکثر در دو مرحله اصلاح می‌کند.

برای شروع، در درواره ثبت‌نام و API Key دریافت کنید. سپس مدل مناسب را از صفحه مدل‌های درواره انتخاب کرده و ابزار را ابتدا با Patternهای ساده و Test Caseهای دقیق آزمایش کنید.

مقالات مرتبط

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

Read more

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

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

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

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

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

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