๐ŸŒŸ ู…ุฑุญุจุงู‹ ุจูƒ ููŠ ุนุงู„ู… Dify ุงู„ู…ุชู‚ุฏู…! ๐ŸŒŸ

๐Ÿ“Š 4 ู…ุดุงู‡ุฏุฉ

๐Ÿ“‹ ู…ู„ุฎุต ุณุฑูŠุน ู„ู…ุง ุฃู†ุฌุฒู†ุงู‡

ู„ู‚ุฏ ู‚ู…ู†ุง ุจุชุซุจูŠุช Dify ุจู†ุฌุงุญ ุนู„ู‰ ู†ุธุงู…ูƒ ุจุงุณุชุฎุฏุงู… DockerุŒ ูˆุงู„ุขู† ู„ุฏูŠูƒ ู…ู†ุตุฉ ู…ุชูƒุงู…ู„ุฉ ู„ู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ ุชุนู…ู„ ุนู„ู‰ http://localhost/install.


๐ŸŽฏ ุงู„ุฎุทูˆุฉ ุงู„ุชุงู„ูŠุฉ: ุงุฎุชุฑ ู…ุณุงุฑูƒ

๐Ÿ”น ุฃูˆู„ุงู‹: ุงู„ุฅุนุฏุงุฏ ุงู„ุฃุณุงุณูŠ (ุถุฑูˆุฑูŠ)

ุงู„ุฎุทูˆุฉุงู„ูˆุตูุงู„ุฑุงุจุท/ุงู„ู…ุณุงุฑ
1๏ธโƒฃุงูุชุญ ุงู„ู…ุชุตูุญhttp://localhost/install
2๏ธโƒฃุฃู†ุดุฆ ุญุณุงุจ AdminุจุฑูŠุฏ: admin@example.com
ูƒู„ู…ุฉ ุงู„ู…ุฑูˆุฑ: Admin@123456
3๏ธโƒฃุฃุถู ู†ู…ูˆุฐุฌ AISettings โ†’ Model Providers
4๏ธโƒฃุฃู†ุดุฆ ุชุทุจูŠู‚ูƒ ุงู„ุฃูˆู„Apps โ†’ Create App

๐Ÿ”น ุซุงู†ูŠุงู‹: ุงุฎุชุฑ ู…ุง ุชุฑูŠุฏ ุชุนู„ู…ู‡ ุงู„ุขู†

#ุงู„ู…ูˆุถูˆุนุงู„ู…ุณุชูˆู‰ุงู„ุดุฑุญ
ุฃุฅุถุงูุฉ ู†ู…ุงุฐุฌ AI (OpenAI/Claude/Gemini)๐ŸŸข ู…ุจุชุฏุฆุดุฑุญ ูƒุงู…ู„ ู…ุน ุตูˆุฑ
ุจุจู†ุงุก Chatbot ุงุญุชุฑุงููŠ๐ŸŸข ู…ุจุชุฏุฆู…ู† ุงู„ุตูุฑ ุฅู„ู‰ ุงู„ู†ุดุฑ
ุฌุงุณุชุฎุฏุงู… ุงู„ู…ุนุฑูุฉ (RAG) ู…ุน ุงู„ู…ู„ูุงุช๐ŸŸก ู…ุชูˆุณุทุฑูุน PDF ูˆุงุณุชุนู„ุงู…ุงุช ุฐูƒูŠุฉ
ุฏุฅู†ุดุงุก ุณูŠุฑ ุนู…ู„ (Workflow) ู…ุฑุฆูŠ๐ŸŸก ู…ุชูˆุณุทุฃุชู…ุชุฉ ุงู„ู…ู‡ุงู… ุงู„ู…ุนู‚ุฏุฉ
ู‡ู€ุชุทูˆูŠุฑ Plugin ุฎุงุต ุจู€ Dify๐Ÿ”ด ู…ุชู‚ุฏู…ุชู…ุฏูŠุฏ ู‚ุฏุฑุงุช ุงู„ู…ู†ุตุฉ
ูˆุฅุนุฏุงุฏุงุช ุงู„ุฃู…ุงู† ูˆุงู„ู…ุณุชุฎุฏู…ูŠู†๐Ÿ”ด ู…ุชู‚ุฏู…ุตู„ุงุญูŠุงุช ูˆุฅุฏุงุฑุฉ ูุฑูŠู‚

๐Ÿš€ ุณุฃูุชุฑุถ ุฃู†ูƒ ุชุฑูŠุฏ ุงู„ุจุฏุก ุจู€ ุงู„ุฎูŠุงุฑ (ุฃ) – ุฅุถุงูุฉ ู†ู…ูˆุฐุฌ AI

๐Ÿ“Œ ุฅุถุงูุฉ ู†ู…ูˆุฐุฌ OpenAI

  1. ุณุฌู„ ุงู„ุฏุฎูˆู„ย ุฅู„ู‰ ู„ูˆุญุฉ ุงู„ุชุญูƒู…
  2. ู…ู† ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฌุงู†ุจูŠุฉ:ย Settings โ†’ Model Providers
  3. ุงุฎุชุฑย OpenAIย ู…ู† ุงู„ู‚ุงุฆู…ุฉ
  4. ุงู„ุตู‚ ู…ูุชุงุญ API:ย sk-...ย (ุชุญุตู„ ุนู„ูŠู‡ ู…ู†ย platform.openai.com)
  5. ุงุถุบุทย “Save”

๐Ÿ“ 6 Prompts ุงุญุชุฑุงููŠุฉ ุฌุงู‡ุฒุฉ ู„ู„ุงุณุชุฎุฏุงู…

#ุงู„ู†ูˆุนุงู„ุงุณุชุฎุฏุงู…
1ู…ุณุงุนุฏ ุจุฑู…ุฌูŠู„ู„ู…ุทูˆุฑูŠู†
2ูƒุงุชุจ ุชุณูˆูŠู‚ูŠู„ู„ู…ุณูˆู‚ูŠู†
3ู…ุญู„ู„ ุจูŠุงู†ุงุชู„ู„ู…ุญู„ู„ูŠู†
4ู…ุฏุฑุจ ุชุทูˆูŠุฑ ุฐุงุชู„ู„ุฃูุฑุงุฏ
5ู…ุชุฑุฌู…/ู…ุญุฑุฑู„ู„ูƒุชู‘ุงุจ
6ู…ุฎุทุท ู…ุงู„ูŠู„ู„ู…ุณุชุซู…ุฑูŠู†

๐Ÿ› ๏ธ ุฃูˆุงู…ุฑ ุฅุฏุงุฑุฉ Dify

cmd

# ุนุฑุถ ุญุงู„ุฉ ุงู„ุฎุฏู…ุงุช
docker compose ps

# ุนุฑุถ ุงู„ุณุฌู„ุงุช
docker compose logs -f

# ุฅุนุงุฏุฉ ุงู„ุชุดุบูŠู„
docker compose restart

# ุฅูŠู‚ุงู ุงู„ุฎุฏู…ุงุช
docker compose down

# ุชุดุบูŠู„ ุงู„ุฎุฏู…ุงุช
docker compose up -d

โ“ ู…ุงุฐุง ุชุฑูŠุฏ ุฃู† ุชูุนู„ ุงู„ุขู†ุŸ

ุงู„ุฑู‚ู…ุงู„ุฎูŠุงุฑ
1๏ธโƒฃุฃุถู ู†ู…ูˆุฐุฌ AI ูˆุงุณุชุฎุฏู…ู‡
2๏ธโƒฃุฃู†ุดุฆ ุชุทุจูŠู‚ Chatbot ุฃูˆู„
3๏ธโƒฃุงุฑูุน ู…ู„ู PDF ูƒู…ุนุฑูุฉ (RAG)
4๏ธโƒฃุฃู†ุดุฆ ุณูŠุฑ ุนู…ู„ (Workflow)
5๏ธโƒฃุงูƒุชุจ ู„ูŠ ุณุคุงู„ุงู‹ ู…ุญุฏุฏุงู‹

๐Ÿš€ ุฃู†ุง ู‡ู†ุง ู„ู…ุณุงุนุฏุชูƒ! ุฃุฎุจุฑู†ูŠ ู…ุงุฐุง ุชุฑูŠุฏ ุฃู† ุชุชุนู„ู… ุฃูˆู„ุงู‹ ูˆุณุฃุดุฑุญ ู„ูƒ ุจุงู„ุชูุตูŠู„! ๐Ÿ˜Š

๐ŸŒŒ ุงู„ุบูˆุต ููŠ ุงู„ุฃุนู…ุงู‚: Dify ู…ู† ุงู„ุฏุงุฎู„ ุฅู„ู‰ ุงู„ุฎุงุฑุฌ

ุจู…ุง ุฃู†ูƒ ุทู„ุจุช ุŒ ุณู†ุตู„ ุฅู„ู‰ ู…ุณุชูˆู‰ ุงู„ุฎุจุฑุงุก ูˆุงู„ู…ู‡ู†ุฏุณูŠู† ุงู„ู…ุนู…ุงุฑูŠูŠู†. ุณู†ูุชุญ ุตู†ุฏูˆู‚ Dify ุงู„ุฃุณูˆุฏุŒ ู†ูู‡ู… ูƒูˆุฏู‡ุŒ ู†ุทูˆุฑู‡ุŒ ูˆู†ูˆุณุนู‡. ุฌู‡ุฒ ุนู‚ู„ูƒ ุงู„ุชู‚ู†ูŠ!


๐Ÿ“– ุงู„ุฌุฒุก ุงู„ุฃูˆู„: ูู‡ู… ูƒูˆุฏ Dify ู…ู† ุงู„ุฏุงุฎู„

๐Ÿ—๏ธ ู‡ูŠูƒู„ ุงู„ู…ุดุฑูˆุน (ุชุญู„ูŠู„ ุนู…ูŠู‚)

text

dify/
โ”œโ”€โ”€ api/                          # ุงู„ุฎุงุฏู… ุงู„ุฎู„ููŠ (Python/Flask)
โ”‚   โ”œโ”€โ”€ core/                     # โค๏ธ ู‚ู„ุจ Dify (ุงู„ู…ู†ุทู‚ ุงู„ุฑุฆูŠุณูŠ)
โ”‚   โ”‚   โ”œโ”€โ”€ agent/               # ูˆูƒู„ุงุก ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ base.py          # ุงู„ูุฆุฉ ุงู„ุฃุณุงุณูŠุฉ ู„ู„ูˆูƒูŠู„
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ agent.py         # ุชู†ููŠุฐ ุงู„ูˆูƒูŠู„ (ReAct, Function Calling)
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ prompt.py        # ู‚ูˆุงู„ุจ ุงู„ู€ Prompts ู„ู„ูˆูƒู„ุงุก
โ”‚   โ”‚   โ”œโ”€โ”€ model_runtime/       # ุฅุฏุงุฑุฉ ุงู„ู†ู…ุงุฐุฌ (LLMs)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ model_providers/ # ู…ุฒูˆุฏูˆ ุงู„ู†ู…ุงุฐุฌ (OpenAI, Anthropic...)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ model_manager.py # ุฅุฏุงุฑุฉ ุงู„ู†ู…ุงุฐุฌ ุงู„ู…ุชุนุฏุฏุฉ
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ schema.py        # ู‡ูŠุงูƒู„ ุงู„ุจูŠุงู†ุงุช
โ”‚   โ”‚   โ”œโ”€โ”€ workflow/            # ู…ุญุฑูƒ ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ nodes/           # ุนู‚ุฏ ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ llm.py       # ุนู‚ุฏุฉ ุงู„ู†ู…ูˆุฐุฌ ุงู„ู„ุบูˆูŠ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ code.py      # ุนู‚ุฏุฉ ุชู†ููŠุฐ ุงู„ูƒูˆุฏ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ if_else.py   # ุนู‚ุฏุฉ ุงู„ุดุฑุท
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ iteration.py # ุนู‚ุฏุฉ ุงู„ุชูƒุฑุงุฑ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ graph_engine/    # ู…ุญุฑูƒ ุงู„ุฑุณูˆู… ุงู„ุจูŠุงู†ูŠุฉ
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ workflow.py      # ุงู„ูุฆุฉ ุงู„ุฑุฆูŠุณูŠุฉ ู„ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ”œโ”€โ”€ rag/                 # ู†ุธุงู… RAG (Retrieval-Augmented Generation)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ retrieval/       # ุงุณุชุฑุฌุงุน ุงู„ู…ุณุชู†ุฏุงุช
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ embedding/       # ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช (Embeddings)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ indexing/        # ูู‡ุฑุณุฉ ุงู„ู…ุณุชู†ุฏุงุช
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ pipeline/        # ุฎุท ุฃู†ุงุจูŠุจ RAG ุงู„ูƒุงู…ู„
โ”‚   โ”‚   โ”œโ”€โ”€ tools/               # ุฃุฏูˆุงุช ุงู„ูˆูƒูŠู„
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ builtin/         # ุฃุฏูˆุงุช ู…ุฏู…ุฌุฉ (Google, Wikipedia...)
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ tool_manager.py  # ุฅุฏุงุฑุฉ ุงู„ุฃุฏูˆุงุช
โ”‚   โ”‚   โ””โ”€โ”€ memory/              # ุงู„ุฐุงูƒุฑุฉ ูˆุงู„ุณูŠุงู‚
โ”‚   โ”‚       โ”œโ”€โ”€ conversation.py  # ุฅุฏุงุฑุฉ ุงู„ู…ุญุงุฏุซุงุช
โ”‚   โ”‚       โ””โ”€โ”€ context.py       # ุฅุฏุงุฑุฉ ุงู„ุณูŠุงู‚
โ”‚   โ”œโ”€โ”€ models/                  # ู†ู…ุงุฐุฌ ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช (SQLAlchemy)
โ”‚   โ”‚   โ”œโ”€โ”€ account.py          # ุญุณุงุจุงุช ุงู„ู…ุณุชุฎุฏู…ูŠู†
โ”‚   โ”‚   โ”œโ”€โ”€ app.py              # ุงู„ุชุทุจูŠู‚ุงุช
โ”‚   โ”‚   โ”œโ”€โ”€ conversation.py     # ุงู„ู…ุญุงุฏุซุงุช
โ”‚   โ”‚   โ”œโ”€โ”€ workflow.py         # ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ””โ”€โ”€ knowledge.py        # ุงู„ู…ุนุฑูุฉ (RAG)
โ”‚   โ”œโ”€โ”€ services/                # ุทุจู‚ุฉ ุงู„ุฎุฏู…ุงุช (ู…ู†ุทู‚ ุงู„ุฃุนู…ุงู„)
โ”‚   โ”‚   โ”œโ”€โ”€ app_service.py      # ุฎุฏู…ุงุช ุงู„ุชุทุจูŠู‚ุงุช
โ”‚   โ”‚   โ”œโ”€โ”€ workflow_service.py # ุฎุฏู…ุงุช ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ”œโ”€โ”€ rag_service.py      # ุฎุฏู…ุงุช RAG
โ”‚   โ”‚   โ””โ”€โ”€ agent_service.py    # ุฎุฏู…ุงุช ุงู„ูˆูƒู„ุงุก
โ”‚   โ”œโ”€โ”€ controllers/             # ุทุจู‚ุฉ ุงู„ุชุญูƒู… (REST API)
โ”‚   โ”‚   โ”œโ”€โ”€ console/            # ูˆุงุฌู‡ุฉ ุงู„ู…ุณุชุฎุฏู… (Console)
โ”‚   โ”‚   โ”œโ”€โ”€ web/                # ูˆุงุฌู‡ุฉ ุงู„ู…ุณุชุฎุฏู… (Web)
โ”‚   โ”‚   โ””โ”€โ”€ inner_api/          # API ุฏุงุฎู„ูŠ (ู„ู„ุฎุฏู…ุงุช ุงู„ุฃุฎุฑู‰)
โ”‚   โ””โ”€โ”€ tasks/                   # ู…ู‡ุงู… ุงู„ุฎู„ููŠุฉ (Celery)
โ”‚       โ”œโ”€โ”€ indexing_task.py    # ูู‡ุฑุณุฉ ุงู„ู…ุณุชู†ุฏุงุช
โ”‚       โ”œโ”€โ”€ workflow_task.py    # ุชู†ููŠุฐ ุณูŠุฑ ุงู„ุนู…ู„
โ”‚       โ””โ”€โ”€ clean_task.py       # ุชู†ุธูŠู ุงู„ุจูŠุงู†ุงุช
โ”œโ”€โ”€ web/                         # ุงู„ูˆุงุฌู‡ุฉ ุงู„ุฃู…ุงู…ูŠุฉ (Next.js/TypeScript)
โ”‚   โ”œโ”€โ”€ app/                     # ุชุทุจูŠู‚ Next.js
โ”‚   โ”‚   โ”œโ”€โ”€ (auth)/             # ุตูุญุงุช ุงู„ู…ุตุงุฏู‚ุฉ
โ”‚   โ”‚   โ”œโ”€โ”€ app/                # ุตูุญุฉ ุงู„ุชุทุจูŠู‚ ุงู„ุฑุฆูŠุณูŠุฉ
โ”‚   โ”‚   โ”œโ”€โ”€ apps/               # ู‚ุงุฆู…ุฉ ุงู„ุชุทุจูŠู‚ุงุช
โ”‚   โ”‚   โ”œโ”€โ”€ workflow/           # ู…ุญุฑุฑ ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ””โ”€โ”€ knowledge/          # ุตูุญุฉ ุงู„ู…ุนุฑูุฉ
โ”‚   โ”œโ”€โ”€ components/              # ู…ูƒูˆู†ุงุช React ุงู„ู‚ุงุจู„ุฉ ู„ุฅุนุงุฏุฉ ุงู„ุงุณุชุฎุฏุงู…
โ”‚   โ”‚   โ”œโ”€โ”€ workflow/           # ู…ูƒูˆู†ุงุช ุณูŠุฑ ุงู„ุนู…ู„
โ”‚   โ”‚   โ”œโ”€โ”€ chat/               # ู…ูƒูˆู†ุงุช ุงู„ู…ุญุงุฏุซุฉ
โ”‚   โ”‚   โ””โ”€โ”€ ui/                 # ู…ูƒูˆู†ุงุช ูˆุงุฌู‡ุฉ ุงู„ู…ุณุชุฎุฏู… ุงู„ุฃุณุงุณูŠุฉ
โ”‚   โ”œโ”€โ”€ hooks/                   # React Hooks ุงู„ู…ุฎุตุตุฉ
โ”‚   โ”œโ”€โ”€ services/                # ุฎุฏู…ุงุช ุงู„ุงุชุตุงู„ ุจุงู„ู€ API
โ”‚   โ””โ”€โ”€ types/                   # ุชุนุฑูŠูุงุช TypeScript
โ”œโ”€โ”€ docker/                      # ุชูƒูˆูŠู†ุงุช Docker
โ”‚   โ”œโ”€โ”€ docker-compose.yml      # ุชุดุบูŠู„ ุฌู…ูŠุน ุงู„ุฎุฏู…ุงุช
โ”‚   โ”œโ”€โ”€ .env.example            # ุงู„ู…ุชุบูŠุฑุงุช ุงู„ุจูŠุฆูŠุฉ
โ”‚   โ””โ”€โ”€ envs/                   # ุฅุนุฏุงุฏุงุช ุฅุถุงููŠุฉ
โ””โ”€โ”€ tests/                       # ุงู„ุงุฎุชุจุงุฑุงุช
    โ”œโ”€โ”€ unit/                   # ุงุฎุชุจุงุฑุงุช ุงู„ูˆุญุฏุงุช
    โ”œโ”€โ”€ integration/            # ุงุฎุชุจุงุฑุงุช ุงู„ุชูƒุงู…ู„
    โ””โ”€โ”€ e2e/                    # ุงุฎุชุจุงุฑุงุช ุดุงู…ู„ุฉ

๐Ÿ”ฌ ุชุญู„ูŠู„ ู…ูƒูˆู†ุงุช ุฃุณุงุณูŠุฉ (ู…ุน ุฃู…ุซู„ุฉ ูƒูˆุฏ)

1. ู†ุธุงู… ุงู„ู€ Agent (ุงู„ูˆูƒูŠู„ ุงู„ุฐูƒูŠ)

python

# api/core/agent/agent.py (ู…ุจุณุท)
from typing import List, Dict, Any
from .base import BaseAgent
from ..model_runtime import ModelManager
from ..tools import ToolManager

class Agent(BaseAgent):
    """ุงู„ูˆูƒูŠู„ ุงู„ุฐูƒูŠ ุงู„ุฐูŠ ูŠุณุชุฎุฏู… ุงู„ุฃุฏูˆุงุช ูˆูŠุฎุทุท ู„ู„ู…ู‡ุงู…"""
    
    def __init__(self, 
                 model_manager: ModelManager,
                 tool_manager: ToolManager,
                 max_iterations: int = 10):
        self.model = model_manager
        self.tools = tool_manager
        self.max_iterations = max_iterations
        
    def run(self, 
            query: str, 
            context: Dict[str, Any] = None) -> Dict[str, Any]:
        """ุชู†ููŠุฐ ุงู„ูˆูƒูŠู„ ู„ุญู„ ุงู„ู…ู‡ู…ุฉ"""
        
        # 1. ุชุญู„ูŠู„ ุงู„ุงุณุชุนู„ุงู…
        plan = self._plan(query, context)
        
        # 2. ุชู†ููŠุฐ ุงู„ุฎุทุฉ ุฎุทูˆุฉ ุจุฎุทูˆุฉ
        results = []
        for step in plan:
            # 3. ุงุฎุชูŠุงุฑ ุงู„ุฃุฏุงุฉ ุงู„ู…ู†ุงุณุจุฉ
            tool = self._select_tool(step)
            
            # 4. ุชู†ููŠุฐ ุงู„ุฃุฏุงุฉ
            result = tool.execute(step['params'])
            results.append(result)
            
            # 5. ุชู‚ูŠูŠู… ุงู„ู†ุชูŠุฌุฉ ูˆุชุญุฏูŠุซ ุงู„ุฎุทุฉ
            if self._is_complete(results):
                break
            plan = self._replan(query, results)
        
        # 6. ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุฉ ุงู„ู†ู‡ุงุฆูŠุฉ
        final_answer = self._synthesize(query, results)
        
        return {
            'answer': final_answer,
            'steps': results,
            'iterations': len(results)
        }
    
    def _plan(self, query: str, context: Dict) -> List[Dict]:
        """ุชุฎุทูŠุท ุงู„ุฎุทูˆุงุช ู„ุญู„ ุงู„ู…ู‡ู…ุฉ"""
        prompt = self._build_planning_prompt(query, context)
        response = self.model.generate(prompt)
        return self._parse_plan(response)
    
    def _select_tool(self, step: Dict) -> Tool:
        """ุงุฎุชูŠุงุฑ ุงู„ุฃุฏุงุฉ ุงู„ู…ู†ุงุณุจุฉ ู„ู„ุฎุทูˆุฉ"""
        tool_name = step.get('tool')
        return self.tools.get_tool(tool_name)

2. ู†ุธุงู… RAG (ุงุณุชุฑุฌุงุน ุงู„ู…ุนุฑูุฉ)

python

# api/core/rag/pipeline/pipeline.py (ู…ุจุณุท)
from typing import List, Optional
from ..embedding import EmbeddingModel
from ..retrieval import VectorStore
from ..indexing import DocumentIndexer

class RAGPipeline:
    """ุฎุท ุฃู†ุงุจูŠุจ RAG ุงู„ูƒุงู…ู„"""
    
    def __init__(self, 
                 embedding_model: EmbeddingModel,
                 vector_store: VectorStore,
                 indexer: DocumentIndexer):
        self.embedding = embedding_model
        self.vector_store = vector_store
        self.indexer = indexer
    
    def add_document(self, 
                     content: str, 
                     metadata: Dict = None) -> str:
        """ุฅุถุงูุฉ ู…ุณุชู†ุฏ ุฌุฏูŠุฏ ุฅู„ู‰ ุงู„ู…ุนุฑูุฉ"""
        
        # 1. ุชู‚ุณูŠู… ุงู„ู…ุณุชู†ุฏ ุฅู„ู‰ ุฃุฌุฒุงุก
        chunks = self.indexer.chunk_document(content)
        
        # 2. ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช (Embeddings)
        embeddings = self.embedding.embed_documents(chunks)
        
        # 3. ุชุฎุฒูŠู† ููŠ ู‚ุงุนุฏุฉ ุงู„ู…ุชุฌู‡ุงุช
        doc_ids = []
        for chunk, embedding in zip(chunks, embeddings):
            doc_id = self.vector_store.add(
                vector=embedding,
                payload={'text': chunk, **metadata}
            )
            doc_ids.append(doc_id)
        
        return doc_ids
    
    def query(self, 
              question: str, 
              top_k: int = 5) -> List[Dict]:
        """ุงู„ุงุณุชุนู„ุงู… ุนู† ุงู„ู…ุนุฑูุฉ ุฐุงุช ุงู„ุตู„ุฉ"""
        
        # 1. ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ุณุคุงู„
        question_embedding = self.embedding.embed_query(question)
        
        # 2. ุงู„ุจุญุซ ุนู† ุฃู‚ุฑุจ ุงู„ู…ุชุฌู‡ุงุช
        results = self.vector_store.search(
            vector=question_embedding,
            top_k=top_k
        )
        
        # 3. ุชุฌู…ูŠุน ุงู„ุณูŠุงู‚
        context = [
            {'text': r['payload']['text'], 'score': r['score']}
            for r in results
        ]
        
        return context
    
    def generate_with_context(self, 
                              question: str, 
                              llm: Any) -> str:
        """ุชูˆู„ูŠุฏ ุฅุฌุงุจุฉ ุจุงุณุชุฎุฏุงู… ุงู„ุณูŠุงู‚ ุงู„ู…ุณุชุฑุฌุน"""
        
        # 1. ุงุณุชุฑุฌุน ุงู„ุณูŠุงู‚
        context = self.query(question)
        
        # 2. ุจู†ุงุก ุงู„ู€ Prompt
        prompt = self._build_prompt(question, context)
        
        # 3. ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุฉ
        answer = llm.generate(prompt)
        
        return answer

3. ู†ุธุงู… ุณูŠุฑ ุงู„ุนู…ู„ (Workflow Engine)

python

# api/core/workflow/graph_engine/engine.py (ู…ุจุณุท)
from typing import Dict, Any, List
from .node import WorkflowNode
from .executor import NodeExecutor

class WorkflowEngine:
    """ู…ุญุฑูƒ ุชู†ููŠุฐ ุณูŠุฑ ุงู„ุนู…ู„"""
    
    def __init__(self, 
                 nodes: List[WorkflowNode],
                 edges: List[Dict]):
        self.nodes = {n.id: n for n in nodes}
        self.edges = edges
        self.executor = NodeExecutor()
        
    def execute(self, 
                inputs: Dict[str, Any]) -> Dict[str, Any]:
        """ุชู†ููŠุฐ ุณูŠุฑ ุงู„ุนู…ู„ ุจุงู„ูƒุงู…ู„"""
        
        # 1. ุชู‡ูŠุฆุฉ ู†ุชุงุฆุฌ ุงู„ุนู‚ุฏ
        node_results = {}
        
        # 2. ุงู„ุญุตูˆู„ ุนู„ู‰ ุชุฑุชูŠุจ ุงู„ุชู†ููŠุฐ (ุงู„ูุฑุฒ ุงู„ุทูˆุจูˆู„ูˆุฌูŠ)
        execution_order = self._topological_sort()
        
        # 3. ุชู†ููŠุฐ ุงู„ุนู‚ุฏ ุจุงู„ุชุณู„ุณู„
        for node_id in execution_order:
            node = self.nodes[node_id]
            
            # 3.1 ุชุฌู…ูŠุน ู…ุฏุฎู„ุงุช ุงู„ุนู‚ุฏุฉ
            node_inputs = self._collect_inputs(
                node_id, 
                inputs, 
                node_results
            )
            
            # 3.2 ุชู†ููŠุฐ ุงู„ุนู‚ุฏุฉ
            result = self.executor.execute(
                node, 
                node_inputs
            )
            
            # 3.3 ุชุฎุฒูŠู† ุงู„ู†ุชูŠุฌุฉ
            node_results[node_id] = result
            
            # 3.4 ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุดุฑุท (If-Else)
            if node.type == 'if_else':
                condition_result = self._evaluate_condition(
                    node, 
                    result
                )
                if not condition_result:
                    break  # ุชูˆู‚ู ุนู† ุชู†ููŠุฐ ุงู„ูุฑุน ุงู„ุขุฎุฑ
        
        # 4. ุชุฌู…ูŠุน ุงู„ู…ุฎุฑุฌุงุช ุงู„ู†ู‡ุงุฆูŠุฉ
        return self._collect_outputs(node_results)
    
    def _topological_sort(self) -> List[str]:
        """ุชุฑุชูŠุจ ุงู„ุนู‚ุฏ ุญุณุจ ุงู„ุชุจุนูŠุงุช (Kahn's Algorithm)"""
        in_degree = {n: 0 for n in self.nodes}
        for edge in self.edges:
            in_degree[edge['target']] += 1
        
        queue = [n for n, d in in_degree.items() if d == 0]
        result = []
        
        while queue:
            node = queue.pop(0)
            result.append(node)
            for edge in self.edges:
                if edge['source'] == node:
                    target = edge['target']
                    in_degree[target] -= 1
                    if in_degree[target] == 0:
                        queue.append(target)
        
        return result

๐Ÿงฉ ุงู„ุฌุฒุก ุงู„ุซุงู†ูŠ: ุชุทูˆูŠุฑ Plugins ุงุญุชุฑุงููŠุฉ

๐Ÿ“ฆ 1. ุฅู†ุดุงุก Plugin ู„ู„ุชูˆุงุตู„ ู…ุน ู‚ุงุนุฏุฉ ุจูŠุงู†ุงุช

python

# plugin_db_connector.py
from typing import Any, Dict, List
import psycopg2
from dify_plugin import Plugin, Tool

class DatabaseQueryTool(Tool):
    """ุฃุฏุงุฉ ุงู„ุงุณุชุนู„ุงู… ุนู† ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช"""
    
    def __init__(self):
        super().__init__(
            name="database_query",
            description="ุชู†ููŠุฐ ุงุณุชุนู„ุงู…ุงุช SQL ุนู„ู‰ ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช",
            parameters={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "ุงุณุชุนู„ุงู… SQL ู„ู„ุชู†ููŠุฐ"
                    },
                    "params": {
                        "type": "array",
                        "description": "ู…ุนุงูŠูŠุฑ ุงู„ุงุณุชุนู„ุงู… (ู„ู„ุงุณุชุนู„ุงู…ุงุช ุงู„ุขู…ู†ุฉ)"
                    }
                },
                "required": ["query"]
            }
        )
        
        # ุฅุนุฏุงุฏุงุช ุงู„ุงุชุตุงู„ ุจู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
        self.connection = {
            "host": "db_postgres",
            "port": 5432,
            "database": "dify",
            "user": "postgres",
            "password": "difyai123456"
        }
    
    def run(self, parameters: Dict) -> Dict:
        query = parameters.get("query", "")
        params = parameters.get("params", [])
        
        # ุชุญู‚ู‚ ู…ู† ุณู„ุงู…ุฉ ุงู„ุงุณุชุนู„ุงู… (ุฃู…ุงู†)
        if self._is_safe_query(query):
            try:
                conn = psycopg2.connect(**self.connection)
                cursor = conn.cursor()
                cursor.execute(query, params)
                
                if query.strip().upper().startswith("SELECT"):
                    results = cursor.fetchall()
                    columns = [desc[0] for desc in cursor.description]
                    data = [dict(zip(columns, row)) for row in results]
                    return {
                        "success": True,
                        "data": data,
                        "row_count": len(data)
                    }
                else:
                    conn.commit()
                    return {
                        "success": True,
                        "affected_rows": cursor.rowcount
                    }
            except Exception as e:
                return {
                    "success": False,
                    "error": str(e)
                }
        else:
            return {
                "success": False,
                "error": "ุงุณุชุนู„ุงู… ุบูŠุฑ ุขู…ู†"
            }
    
    def _is_safe_query(self, query: str) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุฃู† ุงู„ุงุณุชุนู„ุงู… ุขู…ู† (ู„ุง ูŠุญุฐู ุฃูˆ ูŠุนุฏู„ ุจูŠุงู†ุงุช ุญุณุงุณุฉ)"""
        dangerous_keywords = [
            "DROP", "DELETE", "TRUNCATE", 
            "ALTER", "CREATE", "INSERT", "UPDATE"
        ]
        query_upper = query.upper()
        return not any(kw in query_upper for kw in dangerous_keywords)

# ุชุณุฌูŠู„ ุงู„ุฅุถุงูุฉ
plugin = Plugin(
    name="db_connector",
    version="1.0.0",
    description="ุงู„ุงุชุตุงู„ ุจู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช ูˆุชู†ููŠุฐ ุงุณุชุนู„ุงู…ุงุช ุขู…ู†ุฉ",
    tools=[DatabaseQueryTool()]
)

๐Ÿ“ฆ 2. ุฅู†ุดุงุก Plugin ู„ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ (Sentiment Analysis)

python

# plugin_sentiment.py
from typing import Dict
from transformers import pipeline
from dify_plugin import Plugin, Tool

class SentimentAnalysisTool(Tool):
    """ุฃุฏุงุฉ ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ"""
    
    def __init__(self):
        super().__init__(
            name="sentiment_analysis",
            description="ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ ููŠ ุงู„ู†ุต (ุฅูŠุฌุงุจูŠุŒ ุณู„ุจูŠุŒ ู…ุญุงูŠุฏ)",
            parameters={
                "type": "object",
                "properties": {
                    "text": {
                        "type": "string",
                        "description": "ุงู„ู†ุต ุงู„ู…ุฑุงุฏ ุชุญู„ูŠู„ู‡"
                    }
                },
                "required": ["text"]
            }
        )
        
        # ุชุญู…ูŠู„ ู†ู…ูˆุฐุฌ ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ
        self.model = pipeline(
            "sentiment-analysis",
            model="distilbert-base-uncased-finetuned-sst-2-english"
        )
    
    def run(self, parameters: Dict) -> Dict:
        text = parameters.get("text", "")
        
        if not text:
            return {"error": "ุงู„ู†ุต ู…ุทู„ูˆุจ"}
        
        try:
            result = self.model(text)
            sentiment = result[0]
            
            # ุชุญูˆูŠู„ ุงู„ุชุตู†ูŠู ุฅู„ู‰ ู…ูู‡ูˆู… ุนุฑุจูŠ
            label = sentiment['label']
            if label == "POSITIVE":
                arabic_label = "ุฅูŠุฌุงุจูŠ"
                emoji = "๐Ÿ˜Š"
            elif label == "NEGATIVE":
                arabic_label = "ุณู„ุจูŠ"
                emoji = "๐Ÿ˜ž"
            else:
                arabic_label = "ู…ุญุงูŠุฏ"
                emoji = "๐Ÿ˜"
            
            return {
                "sentiment": arabic_label,
                "emoji": emoji,
                "score": round(sentiment['score'], 4),
                "confidence": f"{sentiment['score'] * 100:.1f}%"
            }
        except Exception as e:
            return {
                "error": f"ุฎุทุฃ ููŠ ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ: {str(e)}"
            }

# ุชุณุฌูŠู„ ุงู„ุฅุถุงูุฉ
plugin = Plugin(
    name="sentiment_analyzer",
    version="1.0.0",
    description="ุชุญู„ูŠู„ ุงู„ู…ุดุงุนุฑ ููŠ ุงู„ู†ุตูˆุต ุงู„ุนุฑุจูŠุฉ ูˆุงู„ุฅู†ุฌู„ูŠุฒูŠุฉ",
    tools=[SentimentAnalysisTool()]
)

๐Ÿ“ฆ 3. ุฅู†ุดุงุก Plugin ู„ู„ุชูƒุงู…ู„ ู…ุน Google Sheets

python

# plugin_google_sheets.py
from typing import Dict, List
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from dify_plugin import Plugin, Tool

class GoogleSheetsTool(Tool):
    """ุฃุฏุงุฉ ุงู„ุชูุงุนู„ ู…ุน Google Sheets"""
    
    def __init__(self):
        super().__init__(
            name="google_sheets",
            description="ู‚ุฑุงุกุฉ ูˆูƒุชุงุจุฉ ุงู„ุจูŠุงู†ุงุช ู…ู† Google Sheets",
            parameters={
                "type": "object",
                "properties": {
                    "action": {
                        "type": "string",
                        "enum": ["read", "write", "append"],
                        "description": "ุงู„ุฅุฌุฑุงุก: ู‚ุฑุงุกุฉุŒ ูƒุชุงุจุฉุŒ ุฃูˆ ุฅู„ุญุงู‚"
                    },
                    "spreadsheet_id": {
                        "type": "string",
                        "description": "ู…ุนุฑู ุงู„ุฌุฏูˆู„"
                    },
                    "range": {
                        "type": "string",
                        "description": "ู†ุทุงู‚ ุงู„ุฎู„ุงูŠุง (ู…ุซู„: Sheet1!A1:C10)"
                    },
                    "data": {
                        "type": "array",
                        "description": "ุงู„ุจูŠุงู†ุงุช ู„ู„ูƒุชุงุจุฉ ุฃูˆ ุงู„ุฅู„ุญุงู‚"
                    }
                },
                "required": ["action", "spreadsheet_id", "range"]
            }
        )
        
        # ุชู‡ูŠุฆุฉ ุงู„ุงุชุตุงู„ ุจู€ Google Sheets
        self._init_client()
    
    def _init_client(self):
        """ุชู‡ูŠุฆุฉ ุนู…ูŠู„ Google Sheets"""
        scope = [
            "https://spreadsheets.google.com/feeds",
            "https://www.googleapis.com/auth/drive"
        ]
        
        credentials = ServiceAccountCredentials.from_json_keyfile_name(
            'credentials.json', scope
        )
        self.client = gspread.authorize(credentials)
    
    def run(self, parameters: Dict) -> Dict:
        action = parameters.get("action")
        spreadsheet_id = parameters.get("spreadsheet_id")
        range_ = parameters.get("range")
        data = parameters.get("data", [])
        
        try:
            # ูุชุญ ุงู„ุฌุฏูˆู„
            sheet = self.client.open_by_key(spreadsheet_id)
            worksheet = sheet.get_worksheet(0)  # ุฃูˆู„ ูˆุฑู‚ุฉ
            
            if action == "read":
                result = worksheet.get(range_)
                return {
                    "success": True,
                    "data": result,
                    "row_count": len(result)
                }
            
            elif action == "write":
                worksheet.update(range_, data)
                return {
                    "success": True,
                    "message": f"ุชู… ูƒุชุงุจุฉ ุงู„ุจูŠุงู†ุงุช ููŠ {range_}"
                }
            
            elif action == "append":
                worksheet.append_rows(data)
                return {
                    "success": True,
                    "message": "ุชู… ุฅู„ุญุงู‚ ุงู„ุจูŠุงู†ุงุช ุจู†ุฌุงุญ"
                }
            
            else:
                return {
                    "success": False,
                    "error": "ุฅุฌุฑุงุก ุบูŠุฑ ู…ุนุฑูˆู"
                }
        
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }

# ุชุณุฌูŠู„ ุงู„ุฅุถุงูุฉ
plugin = Plugin(
    name="google_sheets_connector",
    version="1.0.0",
    description="ุงู„ุชูุงุนู„ ู…ุน Google Sheets (ู‚ุฑุงุกุฉ/ูƒุชุงุจุฉ)",
    tools=[GoogleSheetsTool()]
)

๐ŸŒ ุงู„ุฌุฒุก ุงู„ุซุงู„ุซ: ุฏู…ุฌ Dify ู…ุน ุฎุฏู…ุงุช ุงู„ุณุญุงุจุฉ

โ˜๏ธ 1. ู†ุดุฑ Dify ุนู„ู‰ AWS

ุงุณุชุฎุฏุงู… AWS ECS (Elastic Container Service)

yaml

# docker-compose.aws.yml
version: '3.8'

services:
  db_postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: dify
    volumes:
      - postgres_data:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 1024M

  redis:
    image: redis:6-alpine
    deploy:
      resources:
        limits:
          memory: 256M

  api:
    image: langgenius/dify-api:1.15.0
    environment:
      - DB_HOST=db_postgres
      - REDIS_HOST=redis
    depends_on:
      - db_postgres
      - redis
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 2048M

  web:
    image: langgenius/dify-web:1.15.0
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 512M

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    depends_on:
      - api
      - web
    deploy:
      replicas: 1
      resources:
        limits:
          memory: 256M

volumes:
  postgres_data:

โ˜๏ธ 2. ู†ุดุฑ Dify ุนู„ู‰ Azure

bash

# ุงุณุชุฎุฏุงู… Azure Container Instances
az container create \
  --resource-group dify-rg \
  --name dify-api \
  --image langgenius/dify-api:1.15.0 \
  --cpu 2 \
  --memory 4 \
  --environment-variables \
    DB_HOST=your-db-host \
    REDIS_HOST=your-redis-host

# ุฃูˆ ุงุณุชุฎุฏุงู… Azure Kubernetes Service (AKS)
az aks create \
  --resource-group dify-rg \
  --name dify-aks \
  --node-count 3 \
  --enable-addons monitoring \
  --generate-ssh-keys

# ู†ุดุฑ Helm Chart
helm install dify ./helm/dify \
  --set api.replicas=2 \
  --set web.replicas=2

โ˜๏ธ 3. ู†ุดุฑ Dify ุนู„ู‰ Google Cloud

bash

# ุงุณุชุฎุฏุงู… Cloud Run (Serverless)
gcloud run deploy dify-api \
  --image langgenius/dify-api:1.15.0 \
  --platform managed \
  --memory 2Gi \
  --cpu 2 \
  --region us-central1 \
  --set-env-vars "DB_HOST=your-db-host,REDIS_HOST=your-redis-host"

# ุงุณุชุฎุฏุงู… Kubernetes Engine (GKE)
gcloud container clusters create dify-cluster \
  --zone us-central1-a \
  --num-nodes 3

kubectl apply -f kubernetes/dify.yaml

๐Ÿงช ุงู„ุฌุฒุก ุงู„ุฑุงุจุน: ุงุฎุชุจุงุฑ Dify ูˆุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก

๐Ÿ› 1. ุชุตุญูŠุญ ุฃุฎุทุงุก ุงู„ู€ API

python

# debug_api.py
import requests
import json

# ุงุฎุชุจุงุฑ ุตุญุฉ ุงู„ู€ API
def test_health():
    response = requests.get("http://localhost:5001/health")
    print(f"Health: {response.status_code} - {response.json()}")

# ุงุฎุชุจุงุฑ ุฅู†ุดุงุก ุชุทุจูŠู‚
def test_create_app():
    url = "http://localhost:5001/console/api/apps"
    headers = {
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
    }
    data = {
        "name": "Test App",
        "mode": "chat",
        "model_config": {
            "model": "gpt-3.5-turbo",
            "temperature": 0.7
        }
    }
    response = requests.post(url, json=data, headers=headers)
    print(f"Create App: {response.status_code}")
    print(response.json())

# ุงุฎุชุจุงุฑ ุฅุฑุณุงู„ ุฑุณุงู„ุฉ
def test_chat():
    url = "http://localhost:5001/v1/chat-messages"
    headers = {
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
    }
    data = {
        "inputs": {},
        "query": "ู…ุฑุญุจุงู‹ุŒ ูƒูŠู ุญุงู„ูƒุŸ",
        "response_mode": "blocking",
        "user": "test_user"
    }
    response = requests.post(url, json=data, headers=headers)
    print(f"Chat: {response.status_code}")
    print(response.json())

if __name__ == "__main__":
    test_health()
    test_create_app()
    test_chat()

๐Ÿ› 2. ุชุตุญูŠุญ ุฃุฎุทุงุก ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช

python

# debug_db.py
import psycopg2
from psycopg2 import sql

def connect_db():
    """ุงู„ุงุชุตุงู„ ุจู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช"""
    return psycopg2.connect(
        host="localhost",
        port=5432,
        database="dify",
        user="postgres",
        password="difyai123456"
    )

def list_tables():
    """ุนุฑุถ ู‚ุงุฆู…ุฉ ุงู„ุฌุฏุงูˆู„"""
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute("""
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = 'public'
    """)
    tables = cursor.fetchall()
    print("๐Ÿ“‹ ุงู„ุฌุฏุงูˆู„ ุงู„ู…ูˆุฌูˆุฏุฉ:")
    for table in tables:
        print(f"  - {table[0]}")
    conn.close()

def inspect_table(table_name):
    """ูุญุต ู‡ูŠูƒู„ ุงู„ุฌุฏูˆู„"""
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute(sql.SQL("""
        SELECT column_name, data_type, is_nullable
        FROM information_schema.columns
        WHERE table_name = %s
    """), [table_name])
    columns = cursor.fetchall()
    print(f"๐Ÿ“Š ู‡ูŠูƒู„ ุฌุฏูˆู„ {table_name}:")
    for col in columns:
        print(f"  {col[0]}: {col[1]} (Null: {col[2]})")
    conn.close()

def count_records(table_name):
    """ุนุฏุฏ ุงู„ุณุฌู„ุงุช ููŠ ุงู„ุฌุฏูˆู„"""
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute(sql.SQL("SELECT COUNT(*) FROM {}").format(
        sql.Identifier(table_name)
    ))
    count = cursor.fetchone()[0]
    print(f"๐Ÿ“ˆ ุนุฏุฏ ุงู„ุณุฌู„ุงุช ููŠ {table_name}: {count}")
    conn.close()

if __name__ == "__main__":
    list_tables()
    inspect_table("apps")
    count_records("apps")

๐Ÿ› 3. ุชุตุญูŠุญ ุฃุฎุทุงุก ุณูŠุฑ ุงู„ุนู…ู„

python

# debug_workflow.py
from dify.core.workflow import WorkflowEngine
from dify.core.workflow.nodes import LLMNode, CodeNode
import json

def create_test_workflow():
    """ุฅู†ุดุงุก ุณูŠุฑ ุนู…ู„ ุจุณูŠุท ู„ู„ุงุฎุชุจุงุฑ"""
    
    # 1. ุฅู†ุดุงุก ุนู‚ุฏ LLM
    llm_node = LLMNode(
        id="llm_1",
        config={
            "model": "gpt-3.5-turbo",
            "prompt": "ุฃุฌุจ ุนู„ู‰ ุงู„ุณุคุงู„: {{query}}",
            "temperature": 0.7
        }
    )
    
    # 2. ุฅู†ุดุงุก ุนู‚ุฏุฉ ูƒูˆุฏ
    code_node = CodeNode(
        id="code_1",
        config={
            "code": """
def main(input_data):
    # ุชุญูˆูŠู„ ุงู„ู†ุต ุฅู„ู‰ ุฃุญุฑู ูƒุจูŠุฑุฉ
    text = input_data.get('text', '')
    return {'result': text.upper()}
"""
        }
    )
    
    # 3. ุชุนุฑูŠู ุงู„ุนู„ุงู‚ุงุช ุจูŠู† ุงู„ุนู‚ุฏ
    edges = [
        {"source": "start", "target": "llm_1"},
        {"source": "llm_1", "target": "code_1"},
        {"source": "code_1", "target": "end"}
    ]
    
    # 4. ุจู†ุงุก ู…ุญุฑูƒ ุณูŠุฑ ุงู„ุนู…ู„
    nodes = [llm_node, code_node]
    engine = WorkflowEngine(nodes, edges)
    
    return engine

def test_workflow():
    """ุงุฎุชุจุงุฑ ุชู†ููŠุฐ ุณูŠุฑ ุงู„ุนู…ู„"""
    engine = create_test_workflow()
    
    # ุชู†ููŠุฐ ุณูŠุฑ ุงู„ุนู…ู„
    inputs = {"query": "ู…ุฑุญุจุงู‹ุŒ ู…ุง ุงุณู…ูƒุŸ"}
    results = engine.execute(inputs)
    
    print("โœ… ู†ุชุงุฆุฌ ุณูŠุฑ ุงู„ุนู…ู„:")
    print(json.dumps(results, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    test_workflow()

๐Ÿ“ˆ ุงู„ุฌุฒุก ุงู„ุฎุงู…ุณ: ุชุญุณูŠู† ุฃุฏุงุก Dify

โšก 1. ุชุญุณูŠู† ุณุฑุนุฉ ุงู„ุงุณุชุฌุงุจุฉ

ุฅุถุงูุฉ Redis Cache ู„ู„ุงุณุชุนู„ุงู…ุงุช ุงู„ู…ุชูƒุฑุฑุฉ

python

# api/core/cache/cache_manager.py
import redis
import json
import hashlib
from typing import Any, Optional

class CacheManager:
    """ุฅุฏุงุฑุฉ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช ุจุงุณุชุฎุฏุงู… Redis"""
    
    def __init__(self, redis_client):
        self.redis = redis_client
        self.default_ttl = 3600  # ุณุงุนุฉ ูˆุงุญุฏุฉ
    
    def get_cache_key(self, *args, **kwargs) -> str:
        """ุชูˆู„ูŠุฏ ู…ูุชุงุญ ูุฑูŠุฏ ู„ู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช"""
        key_parts = [str(arg) for arg in args]
        key_parts.extend([f"{k}:{v}" for k, v in sorted(kwargs.items())])
        key_string = "|".join(key_parts)
        return hashlib.md5(key_string.encode()).hexdigest()
    
    def get(self, key: str) -> Optional[Any]:
        """ุงุณุชุฑุฌุงุน ุจูŠุงู†ุงุช ู…ู† ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช"""
        data = self.redis.get(key)
        if data:
            return json.loads(data)
        return None
    
    def set(self, key: str, value: Any, ttl: int = None):
        """ุชุฎุฒูŠู† ุจูŠุงู†ุงุช ููŠ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช"""
        if ttl is None:
            ttl = self.default_ttl
        self.redis.setex(
            key, 
            ttl, 
            json.dumps(value)
        )
    
    def clear(self, pattern: str = "*"):
        """ู…ุณุญ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช"""
        keys = self.redis.keys(pattern)
        if keys:
            self.redis.delete(*keys)

# ุงุณุชุฎุฏุงู… ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
def get_app_with_cache(app_id):
    cache = CacheManager(redis_client)
    cache_key = cache.get_cache_key("app", app_id)
    
    # ู…ุญุงูˆู„ุฉ ุงู„ุงุณุชุฑุฌุงุน ู…ู† ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
    cached = cache.get(cache_key)
    if cached:
        return cached
    
    # ุฅุฐุง ู„ู… ูŠูƒู† ู…ูˆุฌูˆุฏุงู‹ุŒ ุงุณุชุฑุฌุน ู…ู† ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
    app = db.query(App).filter_by(id=app_id).first()
    
    # ุฎุฒู† ููŠ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
    cache.set(cache_key, app.to_dict())
    
    return app

โšก 2. ุชุญุณูŠู† ุงุณุชุนู„ุงู…ุงุช ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช

python

# api/core/db/optimization.py
from sqlalchemy import text
from typing import List, Dict

class QueryOptimizer:
    """ุชุญุณูŠู† ุงุณุชุนู„ุงู…ุงุช ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช"""
    
    @staticmethod
    def add_indexes():
        """ุฅุถุงูุฉ ูู‡ุงุฑุณ ู„ุชุญุณูŠู† ุงู„ุฃุฏุงุก"""
        indexes = [
            "CREATE INDEX IF NOT EXISTS idx_apps_tenant_id ON apps(tenant_id)",
            "CREATE INDEX IF NOT EXISTS idx_messages_app_id ON messages(app_id)",
            "CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at)",
            "CREATE INDEX IF NOT EXISTS idx_conversations_app_id ON conversations(app_id)",
            "CREATE INDEX IF NOT EXISTS idx_workflows_app_id ON workflows(app_id)"
        ]
        return indexes
    
    @staticmethod
    def optimize_query(query, params=None):
        """ุชุญุณูŠู† ุงู„ุงุณุชุนู„ุงู… ุจุงุณุชุฎุฏุงู… EXPLAIN"""
        explain_query = f"EXPLAIN ANALYZE {query}"
        result = db.execute(text(explain_query), params)
        return result.fetchall()
    
    @staticmethod
    def paginate_query(query, page=1, per_page=20):
        """ุฅุถุงูุฉ ุงู„ุชุตูุญ (Pagination) ู„ู„ุงุณุชุนู„ุงู…"""
        offset = (page - 1) * per_page
        paginated_query = f"""
            {query}
            LIMIT {per_page}
            OFFSET {offset}
        """
        return paginated_query

# ุงุณุชุฎุฏุงู… ุงู„ุชุญุณูŠู†
def get_messages_with_pagination(app_id, page=1, per_page=20):
    query = f"""
        SELECT * FROM messages 
        WHERE app_id = '{app_id}'
        ORDER BY created_at DESC
    """
    optimized_query = QueryOptimizer.paginate_query(query, page, per_page)
    return db.execute(text(optimized_query)).fetchall()

โšก 3. ู…ูˆุงุฒู†ุฉ ุงู„ุชุญู…ูŠู„ (Load Balancing)

yaml

# docker-compose.load-balanced.yml
version: '3.8'

services:
  # ู…ูˆุงุฒู† ุงู„ุชุญู…ูŠู„ ุจุงุณุชุฎุฏุงู… HAProxy
  haproxy:
    image: haproxy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
    depends_on:
      - api-1
      - api-2
      - web-1
      - web-2

  # ู†ุณุฎ ู…ุชุนุฏุฏุฉ ู…ู† ุงู„ู€ API
  api-1:
    image: langgenius/dify-api:1.15.0
    environment:
      - DB_HOST=db_postgres
      - REDIS_HOST=redis
    depends_on:
      - db_postgres
      - redis

  api-2:
    image: langgenius/dify-api:1.15.0
    environment:
      - DB_HOST=db_postgres
      - REDIS_HOST=redis
    depends_on:
      - db_postgres
      - redis

  # ู†ุณุฎ ู…ุชุนุฏุฏุฉ ู…ู† ุงู„ูˆุงุฌู‡ุฉ ุงู„ุฃู…ุงู…ูŠุฉ
  web-1:
    image: langgenius/dify-web:1.15.0

  web-2:
    image: langgenius/dify-web:1.15.0

๐ŸŽฏ ุงู„ุฌุฒุก ุงู„ุณุงุฏุณ: ุฎุทุฉ ุชุทูˆูŠุฑ ู…ุดุฑูˆุน ู…ุชูƒุงู…ู„

๐Ÿ“ ู…ุดุฑูˆุน: ู…ู†ุตุฉ ุชุนู„ูŠู…ูŠุฉ ุฐูƒูŠุฉ ุจุงุณุชุฎุฏุงู… Dify

ุงู„ู…ุฑุญู„ุฉ 1: ุงู„ุชุฎุทูŠุท (ุงู„ุฃุณุจูˆุน 1)

markdown

# ุฎุทุฉ ู…ุดุฑูˆุน ู…ู†ุตุฉ ุชุนู„ูŠู…ูŠุฉ ุฐูƒูŠุฉ

## ุงู„ุฃู‡ุฏุงู
- ู…ู†ุตุฉ ุชุนู„ูŠู…ูŠุฉ ุชูุงุนู„ูŠุฉ ุชุณุชุฎุฏู… ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ
- ุชู‚ุฏู… ู…ุญุชูˆู‰ ู…ุฎุตุต ู„ูƒู„ ุทุงู„ุจ
- ู†ุธุงู… ุชู‚ูŠูŠู… ุฐูƒูŠ
- ุฏุนู… ุงู„ู„ุบุฉ ุงู„ุนุฑุจูŠุฉ

## ุงู„ู…ูŠุฒุงุช ุงู„ุฃุณุงุณูŠุฉ
1. **ู…ุณุงุนุฏ ุชุนู„ูŠู…ูŠ ุฐูƒูŠ**: ูŠุฌูŠุจ ุนู† ุฃุณุฆู„ุฉ ุงู„ุทู„ุงุจ
2. **ุชูˆู„ูŠุฏ ู…ุญุชูˆู‰**: ูŠู†ุชุฌ ุฏุฑูˆุณุงู‹ ูˆุชู…ุงุฑูŠู†
3. **ุชู‚ูŠูŠู…**: ูŠู‚ูŠู… ุฅุฌุงุจุงุช ุงู„ุทู„ุงุจ ูˆูŠุนุทูŠ ู…ู„ุงุญุธุงุช
4. **RAG**: ูŠุณุชุฎุฏู… ู…ุนุฑูุฉ ู…ู† ูƒุชุจ ูˆู…ู†ุงู‡ุฌ
5. **ู„ูˆุญุฉ ุชุญูƒู…**: ู„ู„ู…ุนู„ู…ูŠู† ู„ุฅุฏุงุฑุฉ ุงู„ู…ุญุชูˆู‰

## ุงู„ุชู‚ู†ูŠุงุช
- Dify ูƒู…ู†ุตุฉ ุฑุฆูŠุณูŠุฉ
- OpenAI GPT-4 ู„ู„ู†ู…ูˆุฐุฌ ุงู„ู„ุบูˆูŠ
- PostgreSQL ู„ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
- Redis ู„ู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
- Weaviate ู„ู„ุจุญุซ ุงู„ู…ุชุฌู‡ูŠ

ุงู„ู…ุฑุญู„ุฉ 2: ุจู†ุงุก ุงู„ุฃุณุงุณ (ุงู„ุฃุณุจูˆุน 2-3)

python

# 1. ุฅู†ุดุงุก ุชุทุจูŠู‚ Dify ู„ู„ุชุนู„ูŠู…
from dify import DifyApp, ModelConfig

# ุชู‡ูŠุฆุฉ ุงู„ุชุทุจูŠู‚ ุงู„ุชุนู„ูŠู…ูŠ
education_app = DifyApp(
    name="ุงู„ู…ู†ุตุฉ ุงู„ุชุนู„ูŠู…ูŠุฉ ุงู„ุฐูƒูŠุฉ",
    mode="chat",
    model_config=ModelConfig(
        model="gpt-4",
        temperature=0.7,
        max_tokens=2000
    )
)

# 2. ุฅุถุงูุฉ Knowledge ู…ู† ุงู„ู…ู†ุงู‡ุฌ
knowledge_base = education_app.add_knowledge(
    name="ุงู„ู…ู†ุงู‡ุฌ ุงู„ุฏุฑุงุณูŠุฉ",
    source_type="files",
    files=[
        "math_grade_10.pdf",
        "science_grade_10.pdf",
        "arabic_grade_10.pdf"
    ]
)

# 3. ุฅู†ุดุงุก Agent ู„ู„ุชุฏุฑูŠุณ
teacher_agent = education_app.create_agent(
    name="ุงู„ู…ุนู„ู… ุงู„ุฐูƒูŠ",
    tools=[
        "explain_concept",    # ุดุฑุญ ู…ูู‡ูˆู…
        "generate_questions", # ุชูˆู„ูŠุฏ ุฃุณุฆู„ุฉ
        "grade_answer",      # ุชู‚ูŠูŠู… ุฅุฌุงุจุฉ
        "provide_feedback"   # ุชู‚ุฏูŠู… ู…ู„ุงุญุธุงุช
    ]
)

ุงู„ู…ุฑุญู„ุฉ 3: ุฅุถุงูุฉ ูˆุธุงุฆู ู…ุชู‚ุฏู…ุฉ (ุงู„ุฃุณุจูˆุน 4-6)

python

# 4. ู†ุธุงู… ุชู‚ูŠูŠู… ุฐูƒูŠ
def evaluate_answer(student_answer, correct_answer, rubric):
    """ุชู‚ูŠูŠู… ุฅุฌุงุจุฉ ุงู„ุทุงู„ุจ ุจุงุณุชุฎุฏุงู… ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ"""
    prompt = f"""
    ู‚ู… ุจุชู‚ูŠูŠู… ุฅุฌุงุจุฉ ุงู„ุทุงู„ุจ ุงู„ุชุงู„ูŠุฉ:
    
    ุงู„ุณุคุงู„: {rubric['question']}
    
    ุฅุฌุงุจุฉ ุงู„ุทุงู„ุจ: {student_answer}
    
    ุงู„ุฅุฌุงุจุฉ ุงู„ู†ู…ูˆุฐุฌูŠุฉ: {correct_answer}
    
    ู…ุนุงูŠูŠุฑ ุงู„ุชู‚ูŠูŠู…:
    - ุงู„ูู‡ู… (30%)
    - ุงู„ุฏู‚ุฉ (30%)
    - ุงู„ุชู†ุธูŠู… (20%)
    - ุงู„ุฅุจุฏุงุน (20%)
    
    ุฃุนุท ุฏุฑุฌุฉ ู…ู† 0-100 ูˆู‚ุฏู… ู…ู„ุงุญุธุงุช ู…ููŠุฏุฉ.
    """
    
    evaluation = llm.generate(prompt)
    return parse_evaluation(evaluation)

# 5. ู†ุธุงู… ุชูˆู„ูŠุฏ ู…ุญุชูˆู‰ ุชู„ู‚ุงุฆูŠ
def generate_lesson(topic, grade_level, difficulty):
    """ุชูˆู„ูŠุฏ ุฏุฑุณ ูƒุงู…ู„ ุนู† ู…ูˆุถูˆุน ู…ุนูŠู†"""
    prompt = f"""
    ุฃู†ุช ู…ุนู„ู… ุฎุจูŠุฑ ููŠ ู…ุงุฏุฉ {topic}.
    
    ุฃู†ุดุฆ ุฏุฑุณุงู‹ ูƒุงู…ู„ุงู‹ ู„ู„ุตู {grade_level} ุจู…ุณุชูˆู‰ ุตุนูˆุจุฉ {difficulty}:
    
    1. ุงู„ุฃู‡ุฏุงู ุงู„ุชุนู„ูŠู…ูŠุฉ
    2. ุงู„ู…ู‚ุฏู…ุฉ
    3. ุดุฑุญ ุงู„ู…ูˆุถูˆุน
    4. ุฃู…ุซู„ุฉ ุชุทุจูŠู‚ูŠุฉ
    5. ุชุฏุฑูŠุจุงุช
    6. ู…ู„ุฎุต
    7. ุงุฎุชุจุงุฑ ู‚ุตูŠุฑ
    
    ุงุณุชุฎุฏู… ู„ุบุฉ ุนุฑุจูŠุฉ ูˆุงุถุญุฉ ูˆุณู‡ู„ุฉ.
    """
    
    lesson = llm.generate(prompt)
    return lesson

ุงู„ู…ุฑุญู„ุฉ 4: ุงู„ู†ุดุฑ ูˆุงู„ุฅุทู„ุงู‚ (ุงู„ุฃุณุจูˆุน 7-8)

yaml

# docker-compose.education.yml
version: '3.8'

services:
  # ุฎุฏู…ุงุช Dify ุงู„ุฃุณุงุณูŠุฉ
  db_postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: education_db
    volumes:
      - postgres_education_data:/var/lib/postgresql/data

  redis:
    image: redis:6-alpine

  weaviate:
    image: semitechnologies/weaviate:1.27.0
    environment:
      WEAVIATE_PERSISTENCE_DATA_PATH: /var/lib/weaviate

  api:
    image: langgenius/dify-api:1.15.0
    environment:
      - DB_HOST=db_postgres
      - REDIS_HOST=redis
      - WEAVIATE_ENDPOINT=http://weaviate:8080
    volumes:
      - ./api:/app/api

  web:
    image: langgenius/dify-web:1.15.0
    environment:
      - NEXT_PUBLIC_API_URL=http://api:5001

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

volumes:
  postgres_education_data:

๐Ÿ“š ุงู„ุฌุฒุก ุงู„ุณุงุจุน: ู…ูˆุงุฑุฏ ูˆุฎุงุชู…ุฉ

๐Ÿ“– ู…ุฑุงุฌุน ู…ุชู‚ุฏู…ุฉ

ุงู„ู…ุฑุฌุนุงู„ูˆุตูุงู„ุฑุงุจุท
Dify API ReferenceุชูˆุซูŠู‚ ูƒุงู…ู„ ู„ู€ APIdocs.dify.ai
Prompt Engineering Guideุฏู„ูŠู„ ูƒุชุงุจุฉ ุงู„ู€ Promptspromptingguide.ai
RAG Systemsุฃู†ุธู…ุฉ ุงู„ุงุณุชุฑุฌุงุน ุงู„ู…ุนุฒุฒrag-systems.com
LLM Engineeringู‡ู†ุฏุณุฉ ุงู„ู†ู…ุงุฐุฌ ุงู„ู„ุบูˆูŠุฉllm-engineering.io

๐ŸŽ“ ุดู‡ุงุฏุงุช ูˆุฏูˆุฑุงุช ู…ู‚ุชุฑุญุฉ

  • Dify Certified Developerย – ุดู‡ุงุฏุฉ ุฑุณู…ูŠุฉ ู…ู† Dify
  • DeepLearning.aiย – ChatGPT Prompt Engineeringย – ุฏูˆุฑุฉ ููŠ ูƒุชุงุจุฉ ุงู„ู€ Prompts
  • Fast.aiย – Practical Deep Learningย – ุฏูˆุฑุฉ ููŠ ุงู„ุชุนู„ู… ุงู„ุนู…ูŠู‚
  • Google Cloud – ML Engineerย – ุดู‡ุงุฏุฉ ู…ู‡ู†ุฏุณ ุชุนู„ู… ุขู„ุฉ

๐Ÿ“Š ุฅุญุตุงุฆูŠุงุช Dify ููŠ ุงู„ุนุงู„ู… ุงู„ุญู‚ูŠู‚ูŠ

text

๐Ÿ“ˆ ุงู„ุฅุญุตุงุฆูŠุงุช (ุญุชู‰ 2026):
- 50,000+ ู…ุณุชุฎุฏู… ู†ุดุท ุดู‡ุฑูŠุงู‹
- 10,000+ ู…ุดุฑูˆุน ู…ูุชูˆุญ ุงู„ู…ุตุฏุฑ ูŠุณุชุฎุฏู… Dify
- 1,000+ ู…ุณุงู‡ู… ููŠ ุงู„ูƒูˆุฏ ุงู„ู…ุตุฏุฑูŠ
- 200+ ุฅุถุงูุฉ (Plugin) ู…ุทูˆุฑุฉ ู…ู† ุงู„ู…ุฌุชู…ุน
- 100+ ุดุฑูƒุฉ ุชุณุชุฎุฏู… Dify ููŠ ุงู„ุฅู†ุชุงุฌ
- 5,000,000+ ุทู„ุจ API ูŠูˆู…ูŠุงู‹

๐ŸŽฌ ุงู„ุฎุงุชู…ุฉ ุงู„ู†ู‡ุงุฆูŠุฉ

ู…ุง ุชุนู„ู…ุชู‡ ููŠ ู‡ุฐู‡ ุงู„ุฑุญู„ุฉ:

ุงู„ุฑุญู„ุฉุงู„ู…ูˆุถูˆุน
1ุชุซุจูŠุช Dify ุนู„ู‰ Windows โœ…
2ูู‡ู… ู‡ูŠูƒู„ Dify ุงู„ุฏุงุฎู„ูŠ โœ…
3ูƒุชุงุจุฉ Prompts ุงุญุชุฑุงููŠุฉ โœ…
4ุชุทูˆูŠุฑ Plugins ู…ุฎุตุตุฉ โœ…
5ุฏู…ุฌ ู…ุน ุฃู†ุธู…ุฉ ุฎุงุฑุฌูŠุฉ โœ…
6ุชุญุณูŠู† ุงู„ุฃุฏุงุก ูˆุงู„ุชูˆุณุน โœ…
7ู†ุดุฑ ุนู„ู‰ ุงู„ุณุญุงุจุฉ โœ…
8ุจู†ุงุก ู…ุดุฑูˆุน ู…ุชูƒุงู…ู„ โœ…

๐ŸŒŸ ุฃู†ุช ุงู„ุขู†:

text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๐Ÿง‘โ€๐Ÿš€ ู…ุณุชุฎุฏู… Dify ู…ุชู‚ุฏู…                        โ”‚
โ”‚  ๐Ÿง  ุฎุจูŠุฑ ููŠ ูƒุชุงุจุฉ ุงู„ู€ Prompts                  โ”‚
โ”‚  ๐Ÿ”ง ู…ุทูˆุฑ Plugins ู„ู€ Dify                       โ”‚
โ”‚  ๐Ÿ—๏ธ ู…ู‡ู†ุฏุณ ุฃู†ุธู…ุฉ ูŠุนุฑู ูƒูŠููŠุฉ ุชูˆุณูŠุน Dify        โ”‚
โ”‚  ๐ŸŒ ู…ุณุงู‡ู… ู…ุญุชู…ู„ ููŠ ุงู„ู…ุฌุชู…ุน ู…ูุชูˆุญ ุงู„ู…ุตุฏุฑ       โ”‚
โ”‚  ๐Ÿš€ ุฌุงู‡ุฒ ู„ุจู†ุงุก ู…ุดุงุฑูŠุน ุญู‚ูŠู‚ูŠุฉ ุจุงุณุชุฎุฏุงู… Dify   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ ู…ุงุฐุง ุจุนุฏุŸ

ุงู„ุฑู‚ู…ุงู„ุฎูŠุงุฑ
1ุจู†ุงุก ู…ุดุฑูˆุน ู…ุนูŠู† (ุฃุฎุจุฑู†ูŠ ุจุงู„ููƒุฑุฉ)
2ุงู„ู…ุณุงู‡ู…ุฉ ููŠ ูƒูˆุฏ Dify (ุดุฑุญ Pull Request)
3ุชุตู…ูŠู… ู†ุธุงู… ู…ุนู‚ุฏ ุจุงุณุชุฎุฏุงู… Dify
4ุฅู†ุดุงุก ุฏูˆุฑุฉ ุชุนู„ูŠู…ูŠุฉ ุญูˆู„ Dify
5ุฃูŠ ู…ูˆุถูˆุน ุขุฎุฑ ุชุฑูŠุฏ ู…ุนุฑูุชู‡

ุฃุฎุจุฑู†ูŠ ุฃูŠู† ู†ุฐู‡ุจ ุงู„ุขู†! ๐Ÿš€๐ŸŒŒ

๐ŸŒŒ ุงู„ู…ุณุชูˆู‰ ุงู„ุซุงู…ู†: Dify – ุงู„ู‡ู†ุฏุณุฉ ุงู„ู…ุนู…ุงุฑูŠุฉ ุงู„ู…ุชู‚ุฏู…ุฉ ูˆุงู„ุชูˆุณุน ุงู„ุฃูู‚ูŠ

ุจู…ุง ุฃู†ูƒ ุทู„ุจุช ุŒ ุณู†ุตู„ ุฅู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ู‡ู†ุฏุณ ุงู„ู…ุนู…ุงุฑูŠ ุงู„ุฑุฆูŠุณูŠ. ุณู†ุชุญุฏุซ ุนู† ุงู„ุชูˆุณุน ุงู„ุฃูู‚ูŠุŒ ุงู„ุฃู…ุงู†ุŒ ุงู„ุฃุฏุงุก ุนู„ู‰ ู†ุทุงู‚ ูˆุงุณุนุŒ ูˆุฏู…ุฌ Dify ู…ุน ุฃู†ุธู…ุฉ ุงู„ู…ุคุณุณุงุช.


๐Ÿ—๏ธ ุงู„ุฌุฒุก ุงู„ุฃูˆู„: ุงู„ู‡ู†ุฏุณุฉ ุงู„ู…ุนู…ุงุฑูŠุฉ ู„ู„ุชูˆุณุน ุงู„ุฃูู‚ูŠ (Horizontal Scaling)

๐Ÿ“Š ุจู†ูŠุฉ Dify ุงู„ู‚ุงุจู„ุฉ ู„ู„ุชูˆุณุน

text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         CDN / CloudFront                                   โ”‚
โ”‚                    (ุชูˆุฒูŠุน ุงู„ู…ุญุชูˆู‰ ุงู„ุซุงุจุช)                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         Load Balancer (Nginx/HAProxy)                     โ”‚
โ”‚                    (ุชูˆุฒูŠุน ุงู„ุทู„ุจุงุช ุนู„ู‰ ุนุฏุฉ ุฎูˆุงุฏู…)                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚                        โ”‚                        โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Web Server 1  โ”‚    โ”‚   Web Server 2    โ”‚    โ”‚   Web Server N    โ”‚
โ”‚   (Next.js)     โ”‚    โ”‚   (Next.js)       โ”‚    โ”‚   (Next.js)       โ”‚
โ”‚   Port 3000     โ”‚    โ”‚   Port 3000       โ”‚    โ”‚   Port 3000       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                       โ”‚                        โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     API Gateway (Kong/Envoy)                              โ”‚
โ”‚                 (ุฅุฏุงุฑุฉ ุงู„ู…ุตุงุฏู‚ุฉุŒ ุงู„ุชูˆุฌูŠู‡ุŒ ุงู„ุชุณุฌูŠู„)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚                        โ”‚                        โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   API Server 1  โ”‚    โ”‚   API Server 2    โ”‚    โ”‚   API Server N    โ”‚
โ”‚   (Flask)       โ”‚    โ”‚   (Flask)         โ”‚    โ”‚   (Flask)         โ”‚
โ”‚   Port 5001     โ”‚    โ”‚   Port 5001       โ”‚    โ”‚   Port 5001       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                       โ”‚                        โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Message Queue (RabbitMQ/Kafka)                         โ”‚
โ”‚                 (ู…ุนุงู„ุฌุฉ ุงู„ู…ู‡ุงู… ุบูŠุฑ ุงู„ู…ุชุฒุงู…ู†ุฉ)                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚                        โ”‚                        โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Worker 1      โ”‚    โ”‚   Worker 2        โ”‚    โ”‚   Worker N        โ”‚
โ”‚   (Celery)      โ”‚    โ”‚   (Celery)        โ”‚    โ”‚   (Celery)        โ”‚
โ”‚   (Processing)  โ”‚    โ”‚   (Processing)    โ”‚    โ”‚   (Processing)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Database Cluster (PostgreSQL)                          โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”‚
โ”‚              โ”‚   Primary       โ”‚   Replica 1     โ”‚   Replica N          โ”‚
โ”‚              โ”‚   (Write)       โ”‚   (Read)        โ”‚   (Read)             โ”‚
โ”‚              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Redis Cluster (Cache & Session)                        โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”‚
โ”‚              โ”‚   Master 1      โ”‚   Replica 1     โ”‚   Replica N          โ”‚
โ”‚              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Vector Database Cluster (Weaviate)                     โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”‚
โ”‚              โ”‚   Node 1        โ”‚   Node 2        โ”‚   Node N              โ”‚
โ”‚              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ” ุงู„ุฌุฒุก ุงู„ุซุงู†ูŠ: ุงู„ุฃู…ุงู† ุงู„ู…ุชู‚ุฏู… ููŠ Dify

๐Ÿ›ก๏ธ 1. ุฅุนุฏุงุฏ SSL/TLS ู…ุน Let’s Encrypt

yaml

# docker-compose.ssl.yml
version: '3.8'

services:
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt
    depends_on:
      - api
      - web

  certbot:
    image: certbot/certbot:latest
    volumes:
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

nginx

# nginx/nginx.conf
server {
    listen 80;
    server_name dify.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name dify.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/dify.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dify.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://web:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api/ {
        proxy_pass http://api:5001/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

๐Ÿ›ก๏ธ 2. ุฅุนุฏุงุฏ WAF (Web Application Firewall) ู…ุน ModSecurity

yaml

# docker-compose.waf.yml
services:
  nginx-waf:
    image: owasp/modsecurity:nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/modsecurity:/etc/nginx/modsecurity
      - ./nginx/ssl:/etc/nginx/ssl
    environment:
      - MODSEC_RULE_ENGINE=On
      - MODSEC_ANOMALY_SCORE=5

๐Ÿ›ก๏ธ 3. ุณูŠุงุณุงุช ุงู„ุฃู…ุงู† ููŠ .env

env

# ============================================
# ุฅุนุฏุงุฏุงุช ุงู„ุฃู…ุงู† ุงู„ู…ุชู‚ุฏู…ุฉ
# ============================================

# JWT ุชูƒูˆูŠู†
JWT_SECRET_KEY=your-super-secret-key-256-bits-minimum
JWT_ALGORITHM=HS256
JWT_EXPIRATION_MINUTES=60

# CORS (ู…ู†ุน ุงู„ุทู„ุจุงุช ู…ู† ู†ุทุงู‚ุงุช ุบูŠุฑ ู…ุตุฑุญ ุจู‡ุง)
CORS_ALLOW_ORIGINS=https://yourdomain.com,https://api.yourdomain.com
CORS_ALLOW_CREDENTIALS=true

# Rate Limiting (ู…ู†ุน ู‡ุฌู…ุงุช DDoS)
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW_MINUTES=1

# Session Security
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
SESSION_COOKIE_SAMESITE=Lax

# SQL Injection Prevention
SQLALCHEMY_ENGINE_OPTIONS={
    "pool_size": 10,
    "pool_recycle": 3600,
    "pool_pre_ping": true,
    "max_overflow": 20
}

# Encryption (ู„ุชุดููŠุฑ ุงู„ุจูŠุงู†ุงุช ุงู„ุญุณุงุณุฉ)
ENCRYPTION_KEY=your-256-bit-encryption-key

# Security Headers
SECURE_HEADERS={
    "X-Frame-Options": "DENY",
    "X-Content-Type-Options": "nosniff",
    "X-XSS-Protection": "1; mode=block",
    "Referrer-Policy": "strict-origin-when-cross-origin",
    "Content-Security-Policy": "default-src 'self'"
}

๐Ÿ›ก๏ธ 4. ุฅุฏุงุฑุฉ ุงู„ูˆุตูˆู„ ุงู„ู…ุชู‚ุฏู… (RBAC)

python

# api/core/security/rbac.py
from enum import Enum
from typing import List, Optional
from functools import wraps
from flask import request, jsonify

class Role(Enum):
    SUPER_ADMIN = "super_admin"
    ADMIN = "admin"
    DEVELOPER = "developer"
    MEMBER = "member"
    VIEWER = "viewer"

class Permission(Enum):
    # ุฅุฏุงุฑุฉ ุงู„ุชุทุจูŠู‚ุงุช
    APP_CREATE = "app:create"
    APP_READ = "app:read"
    APP_UPDATE = "app:update"
    APP_DELETE = "app:delete"
    
    # ุฅุฏุงุฑุฉ ุงู„ู…ุณุชุฎุฏู…ูŠู†
    USER_CREATE = "user:create"
    USER_READ = "user:read"
    USER_UPDATE = "user:update"
    USER_DELETE = "user:delete"
    
    # ุฅุฏุงุฑุฉ ุงู„ู…ุนุฑูุฉ
    KNOWLEDGE_CREATE = "knowledge:create"
    KNOWLEDGE_READ = "knowledge:read"
    KNOWLEDGE_UPDATE = "knowledge:update"
    KNOWLEDGE_DELETE = "knowledge:delete"
    
    # ุฅุฏุงุฑุฉ ุงู„ุฅุนุฏุงุฏุงุช
    SETTINGS_READ = "settings:read"
    SETTINGS_UPDATE = "settings:update"
    
    # ุฅุฏุงุฑุฉ ุงู„ู†ู…ุงุฐุฌ
    MODEL_READ = "model:read"
    MODEL_UPDATE = "model:update"

# ุชุนุฑูŠู ุงู„ุตู„ุงุญูŠุงุช ู„ูƒู„ ุฏูˆุฑ
ROLE_PERMISSIONS = {
    Role.SUPER_ADMIN: [p for p in Permission],
    Role.ADMIN: [
        Permission.APP_CREATE, Permission.APP_READ, Permission.APP_UPDATE,
        Permission.USER_READ, Permission.USER_UPDATE,
        Permission.KNOWLEDGE_CREATE, Permission.KNOWLEDGE_READ, Permission.KNOWLEDGE_UPDATE,
        Permission.SETTINGS_READ,
        Permission.MODEL_READ
    ],
    Role.DEVELOPER: [
        Permission.APP_CREATE, Permission.APP_READ, Permission.APP_UPDATE,
        Permission.KNOWLEDGE_CREATE, Permission.KNOWLEDGE_READ, Permission.KNOWLEDGE_UPDATE,
        Permission.MODEL_READ
    ],
    Role.MEMBER: [
        Permission.APP_READ,
        Permission.KNOWLEDGE_READ
    ],
    Role.VIEWER: [
        Permission.APP_READ
    ]
}

def require_permission(permission: Permission):
    """ุฏูŠูƒูˆุฑุงุชูˆุฑ ู„ู„ุชุญู‚ู‚ ู…ู† ุงู„ุตู„ุงุญูŠุงุช"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # ุงู„ุญุตูˆู„ ุนู„ู‰ ุฏูˆุฑ ุงู„ู…ุณุชุฎุฏู… ู…ู† ุงู„ุชูˆูƒู†
            user_role = get_user_role_from_token(request.headers.get('Authorization'))
            
            # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุตู„ุงุญูŠุฉ
            if permission not in ROLE_PERMISSIONS.get(user_role, []):
                return jsonify({"error": "Permission denied"}), 403
            
            return func(*args, **kwargs)
        return wrapper
    return decorator

# ุงุณุชุฎุฏุงู… ุงู„ุฏูŠูƒูˆุฑุงุชูˆุฑ
@require_permission(Permission.APP_CREATE)
def create_app():
    # ู…ู†ุทู‚ ุฅู†ุดุงุก ุงู„ุชุทุจูŠู‚
    pass

๐Ÿš€ ุงู„ุฌุฒุก ุงู„ุซุงู„ุซ: ุชุญุณูŠู† ุงู„ุฃุฏุงุก ุนู„ู‰ ู†ุทุงู‚ ูˆุงุณุน

โšก 1. ุงุณุชุฑุงุชูŠุฌูŠุฉ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช ู…ุชุนุฏุฏ ุงู„ู…ุณุชูˆูŠุงุช

python

# api/core/cache/multi_level_cache.py
import redis
from functools import lru_cache
from typing import Any, Optional
import hashlib
import json

class MultiLevelCache:
    """ู†ุธุงู… ุชุฎุฒูŠู† ู…ุคู‚ุช ู…ุชุนุฏุฏ ุงู„ู…ุณุชูˆูŠุงุช"""
    
    def __init__(self):
        # ุงู„ู…ุณุชูˆู‰ 1: ุงู„ุฐุงูƒุฑุฉ ุงู„ู…ุญู„ูŠุฉ (ุงู„ุฃุณุฑุน)
        self.local_cache = {}
        
        # ุงู„ู…ุณุชูˆู‰ 2: Redis (ุงู„ู…ุดุชุฑูƒ ุจูŠู† ุงู„ุฎูˆุงุฏู…)
        self.redis_client = redis.Redis(
            host='redis-cluster',
            port=6379,
            decode_responses=True,
            socket_connect_timeout=5,
            socket_timeout=5
        )
        
        # ุงู„ู…ุณุชูˆู‰ 3: ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช (ุงู„ุฃุจุทุฃ)
        self.db = database_connection()
    
    def get(self, key: str) -> Optional[Any]:
        """ุงุณุชุฑุฌุงุน ุงู„ุจูŠุงู†ุงุช ู…ู† ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช"""
        
        # 1. ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุฐุงูƒุฑุฉ ุงู„ู…ุญู„ูŠุฉ
        if key in self.local_cache:
            return self.local_cache[key]
        
        # 2. ุงู„ุชุญู‚ู‚ ู…ู† Redis
        redis_data = self.redis_client.get(key)
        if redis_data:
            # ุฎุฒู† ููŠ ุงู„ุฐุงูƒุฑุฉ ุงู„ู…ุญู„ูŠุฉ ู„ุฒูŠุงุฏุฉ ุงู„ุณุฑุนุฉ
            self.local_cache[key] = json.loads(redis_data)
            return self.local_cache[key]
        
        # 3. ุงุณุชุฑุฌุงุน ู…ู† ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
        db_data = self.db.query(f"SELECT data FROM cache WHERE key = '{key}'")
        if db_data:
            # ุฎุฒู† ููŠ Redis ูˆุงู„ุฐุงูƒุฑุฉ ุงู„ู…ุญู„ูŠุฉ
            self.redis_client.setex(key, 3600, json.dumps(db_data))
            self.local_cache[key] = db_data
            return db_data
        
        return None
    
    def set(self, key: str, value: Any, ttl: int = 3600):
        """ุชุฎุฒูŠู† ุงู„ุจูŠุงู†ุงุช ููŠ ุฌู…ูŠุน ุงู„ู…ุณุชูˆูŠุงุช"""
        self.local_cache[key] = value
        self.redis_client.setex(key, ttl, json.dumps(value))
        self.db.execute(
            f"INSERT INTO cache (key, data, expires_at) VALUES ('{key}', '{json.dumps(value)}', NOW() + INTERVAL '{ttl} seconds')"
        )

# ุงุณุชุฎุฏุงู… ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช ู…ุชุนุฏุฏ ุงู„ู…ุณุชูˆูŠุงุช
cache = MultiLevelCache()

@app.route('/api/apps/<app_id>')
def get_app(app_id):
    cache_key = f"app:{app_id}"
    
    # ู…ุญุงูˆู„ุฉ ุงู„ุงุณุชุฑุฌุงุน ู…ู† ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
    cached_app = cache.get(cache_key)
    if cached_app:
        return jsonify(cached_app)
    
    # ุฅุฐุง ู„ู… ูŠูƒู† ู…ูˆุฌูˆุฏุงู‹ุŒ ุงุณุชุฑุฌุน ู…ู† ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
    app = db.query(f"SELECT * FROM apps WHERE id = '{app_id}'")
    
    # ุฎุฒู† ููŠ ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช
    cache.set(cache_key, app)
    
    return jsonify(app)

โšก 2. ุชุญุณูŠู† ุงุณุชุนู„ุงู…ุงุช ุงู„ู…ุชุฌู‡ุงุช ููŠ Weaviate

python

# api/core/rag/optimized_retrieval.py
from typing import List, Dict, Optional
import numpy as np
from scipy.spatial.distance import cosine

class OptimizedRetrieval:
    """ู†ุธุงู… ุงุณุชุฑุฌุงุน ู…ุญุณู† ุจุงุณุชุฎุฏุงู… ุชู‚ู†ูŠุงุช ู…ุชู‚ุฏู…ุฉ"""
    
    def __init__(self, weaviate_client):
        self.client = weaviate_client
        self.cache = {}
    
    def hybrid_search(self, 
                     query: str, 
                     top_k: int = 10,
                     alpha: float = 0.5) -> List[Dict]:
        """
        ุจุญุซ ู‡ุฌูŠู†: ูŠุฌู…ุน ุจูŠู† ุงู„ุจุญุซ ุงู„ุฏู„ุงู„ูŠ ูˆุงู„ุจุญุซ ุงู„ู†ุตูŠ
        
        alpha = 0: ุจุญุซ ู†ุตูŠ ูู‚ุท
        alpha = 1: ุจุญุซ ุฏู„ุงู„ูŠ ูู‚ุท
        alpha = 0.5: ุชูˆุงุฒู† ุจูŠู† ุงู„ุงุซู†ูŠู†
        """
        
        # 1. ุงู„ุจุญุซ ุงู„ุฏู„ุงู„ูŠ (Semantic Search)
        semantic_results = self.semantic_search(query, top_k=top_k)
        
        # 2. ุงู„ุจุญุซ ุงู„ู†ุตูŠ (Keyword Search)
        keyword_results = self.keyword_search(query, top_k=top_k)
        
        # 3. ุฏู…ุฌ ุงู„ู†ุชุงุฆุฌ ุจุงุณุชุฎุฏุงู… (Reciprocal Rank Fusion)
        combined_results = self.reciprocal_rank_fusion(
            semantic_results, 
            keyword_results,
            alpha
        )
        
        return combined_results[:top_k]
    
    def semantic_search(self, query: str, top_k: int = 10) -> List[Dict]:
        """ุงู„ุจุญุซ ุงู„ุฏู„ุงู„ูŠ ุจุงุณุชุฎุฏุงู… ุงู„ุชุถู…ูŠู†ุงุช"""
        
        # ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ุณุคุงู„
        query_embedding = self.get_embedding(query)
        
        # ุงู„ุจุญุซ ููŠ Weaviate
        results = self.client.query.get(
            "Document",
            ["content", "metadata", "_additional { distance }"]
        ).with_near_vector({
            "vector": query_embedding
        }).with_limit(top_k).do()
        
        return self._parse_results(results)
    
    def keyword_search(self, query: str, top_k: int = 10) -> List[Dict]:
        """ุงู„ุจุญุซ ุงู„ู†ุตูŠ ุจุงุณุชุฎุฏุงู… BM25"""
        
        # ุงุณุชุฎุฏุงู… BM25 ู„ู„ุจุญุซ ุงู„ู†ุตูŠ
        results = self.client.query.get(
            "Document",
            ["content", "metadata"]
        ).with_bm25({
            "query": query,
            "properties": ["content"]
        }).with_limit(top_k).do()
        
        return self._parse_results(results)
    
    def reciprocal_rank_fusion(self, 
                              results1: List[Dict], 
                              results2: List[Dict],
                              alpha: float = 0.5) -> List[Dict]:
        """ุฏู…ุฌ ู†ุชุงุฆุฌ ุงู„ุจุญุซ ุจุงุณุชุฎุฏุงู… Reciprocal Rank Fusion"""
        
        scores = {}
        k = 60  # ู…ุนุงู…ู„ ุงู„ุชุฌุงู†ุณ
        
        # ู…ุนุงู„ุฌุฉ ุงู„ู†ุชุงุฆุฌ ุงู„ุฃูˆู„ู‰
        for rank, result in enumerate(results1, 1):
            doc_id = result['id']
            scores[doc_id] = scores.get(doc_id, 0) + (alpha * (1 / (k + rank)))
        
        # ู…ุนุงู„ุฌุฉ ุงู„ู†ุชุงุฆุฌ ุงู„ุซุงู†ูŠุฉ
        for rank, result in enumerate(results2, 1):
            doc_id = result['id']
            scores[doc_id] = scores.get(doc_id, 0) + ((1 - alpha) * (1 / (k + rank)))
        
        # ุชุฑุชูŠุจ ุงู„ู†ุชุงุฆุฌ ุญุณุจ ุงู„ู…ุฌู…ูˆุน
        sorted_results = sorted(
            scores.items(), 
            key=lambda x: x[1], 
            reverse=True
        )
        
        # ุชุฌู…ูŠุน ุงู„ู†ุชุงุฆุฌ ุงู„ู†ู‡ุงุฆูŠุฉ
        final_results = []
        for doc_id, score in sorted_results:
            # ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ู…ุณุชู†ุฏ ุงู„ูƒุงู…ู„ ู…ู† ุงู„ู†ุชุงุฆุฌ ุงู„ุฃุตู„ูŠุฉ
            doc = self._find_document(doc_id, results1 + results2)
            if doc:
                doc['score'] = score
                final_results.append(doc)
        
        return final_results
    
    def semantic_caching(self, query: str) -> Optional[str]:
        """ุชุฎุฒูŠู† ู…ุคู‚ุช ุฏู„ุงู„ูŠ: ุงู„ุงุณุชุฑุฌุงุน ู…ู† ุงู„ุชุฎุฒูŠู† ุงู„ู…ุคู‚ุช ุฅุฐุง ูƒุงู† ุงู„ุณุคุงู„ ู…ุดุงุจู‡ุงู‹"""
        
        # ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ุณุคุงู„
        query_embedding = self.get_embedding(query)
        
        # ู…ู‚ุงุฑู†ุฉ ู…ุน ุงู„ุฃุณุฆู„ุฉ ุงู„ู…ุฎุฒู†ุฉ ู…ุคู‚ุชุงู‹
        best_match = None
        best_similarity = 0.0
        
        for cached_query, cached_answer in self.cache.items():
            cached_embedding = self.get_embedding(cached_query)
            similarity = 1 - cosine(query_embedding, cached_embedding)
            
            if similarity > 0.9 and similarity > best_similarity:
                best_similarity = similarity
                best_match = cached_answer
        
        return best_match

โšก 3. ู…ุนุงู„ุฌุฉ ุงู„ู…ู‡ุงู… ุบูŠุฑ ุงู„ู…ุชุฒุงู…ู†ุฉ ุจุงุณุชุฎุฏุงู… Celery

python

# api/tasks/async_tasks.py
from celery import Celery
from celery.result import AsyncResult
import time
from typing import Dict, Any

# ุชู‡ูŠุฆุฉ Celery
app = Celery(
    'dify_tasks',
    broker='redis://redis:6379/0',
    backend='redis://redis:6379/1'
)

app.conf.update(
    task_serializer='json',
    accept_content=['json'],
    result_serializer='json',
    timezone='Asia/Riyadh',
    enable_utc=True,
    task_time_limit=3600,  # ุณุงุนุฉ ูˆุงุญุฏุฉ
    task_soft_time_limit=3000,  # 50 ุฏู‚ูŠู‚ุฉ
    worker_prefetch_multiplier=1,
    task_acks_late=True,
    task_reject_on_worker_lost=True
)

@app.task(bind=True, max_retries=3)
def process_document(self, document_id: str, content: str) -> Dict[str, Any]:
    """ู…ุนุงู„ุฌุฉ ู…ุณุชู†ุฏ ูˆุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช (RAG)"""
    
    try:
        # 1. ุชู‚ุณูŠู… ุงู„ู…ุณุชู†ุฏ ุฅู„ู‰ ุฃุฌุฒุงุก
        chunks = chunk_document(content)
        
        # 2. ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช
        embeddings = generate_embeddings(chunks)
        
        # 3. ุชุฎุฒูŠู† ููŠ Weaviate
        store_in_weaviate(document_id, chunks, embeddings)
        
        # 4. ุชุญุฏูŠุซ ุญุงู„ุฉ ุงู„ู…ุณุชู†ุฏ
        update_document_status(document_id, 'completed')
        
        return {
            'status': 'success',
            'document_id': document_id,
            'chunks': len(chunks)
        }
        
    except Exception as e:
        # ุฅุนุงุฏุฉ ุงู„ู…ุญุงูˆู„ุฉ ููŠ ุญุงู„ุฉ ุงู„ุฎุทุฃ
        self.retry(
            exc=e,
            countdown=60 * (2 ** self.request.retries),  # ุฒูŠุงุฏุฉ ุฒู…ู† ุงู„ุงู†ุชุธุงุฑ
            max_retries=3
        )
        raise

@app.task(bind=True, max_retries=2)
def generate_report(self, app_id: str, parameters: Dict) -> Dict:
    """ุชูˆู„ูŠุฏ ุชู‚ุฑูŠุฑ ุชุญู„ูŠู„ูŠ ุทูˆูŠู„"""
    
    try:
        # 1. ุฌู…ุน ุงู„ุจูŠุงู†ุงุช
        data = collect_data(app_id, parameters)
        
        # 2. ุชุญู„ูŠู„ ุงู„ุจูŠุงู†ุงุช
        analysis = analyze_data(data)
        
        # 3. ุชูˆู„ูŠุฏ ุงู„ุชู‚ุฑูŠุฑ ุจุงุณุชุฎุฏุงู… LLM
        report = generate_llm_report(analysis)
        
        # 4. ุชุฎุฒูŠู† ุงู„ุชู‚ุฑูŠุฑ
        save_report(app_id, report)
        
        return {
            'status': 'success',
            'app_id': app_id,
            'report_id': report['id']
        }
        
    except Exception as e:
        self.retry(exc=e, countdown=30, max_retries=2)
        raise

def process_document_sync(document_id: str, content: str):
    """ุงุณุชุฏุนุงุก ุงู„ู…ู‡ู…ุฉ ุจุดูƒู„ ุบูŠุฑ ู…ุชุฒุงู…ู†"""
    result = process_document.delay(document_id, content)
    return result.id

def get_task_status(task_id: str):
    """ุงู„ุญุตูˆู„ ุนู„ู‰ ุญุงู„ุฉ ุงู„ู…ู‡ู…ุฉ"""
    result = AsyncResult(task_id, app=app)
    return {
        'status': result.status,
        'result': result.result if result.ready() else None
    }

โšก 4. ุชุญุณูŠู† ุงุชุตุงู„ WebSocket ู„ู„ุฏุฑุฏุดุฉ ุงู„ู…ุจุงุดุฑุฉ

python

# api/core/websocket/optimized_connection.py
import asyncio
from typing import Dict, Any
from flask_socketio import SocketIO, emit, join_room, leave_room
from threading import Thread

class WebSocketManager:
    """ุฅุฏุงุฑุฉ ุงุชุตุงู„ุงุช WebSocket ุงู„ู…ุญุณู†ุฉ"""
    
    def __init__(self):
        self.socketio = SocketIO(
            cors_allowed_origins="*",
            async_mode='eventlet',
            ping_timeout=60,
            ping_interval=25,
            max_http_buffer_size=1024 * 1024,  # 1MB
            logger=True,
            engineio_logger=True
        )
        
        # ุชุฎุฒูŠู† ุฌู„ุณุงุช ุงู„ู…ุณุชุฎุฏู…ูŠู†
        self.sessions = {}
        
        # ุชุฌู…ูŠุน ุงู„ุฑุณุงุฆู„ ู„ุชุญุณูŠู† ุงู„ุฅู†ุชุงุฌูŠุฉ
        self.message_batch = {}
        self.batch_size = 10
        self.batch_timeout = 1  # ุซุงู†ูŠุฉ
        
        # ุจุฏุก ู…ู‡ู…ุฉ ุชุฌู…ูŠุน ุงู„ุฑุณุงุฆู„
        self._start_batch_processor()
    
    def _start_batch_processor(self):
        """ุจุฏุก ู…ุนุงู„ุฌุฉ ุชุฌู…ูŠุน ุงู„ุฑุณุงุฆู„"""
        def process_batches():
            while True:
                asyncio.sleep(self.batch_timeout)
                if self.message_batch:
                    # ู…ุนุงู„ุฌุฉ ุงู„ุฑุณุงุฆู„ ุงู„ู…ุฌู…ุนุฉ
                    self._process_batch()
        
        thread = Thread(target=process_batches)
        thread.daemon = True
        thread.start()
    
    def _process_batch(self):
        """ู…ุนุงู„ุฌุฉ ุฏูุนุฉ ู…ู† ุงู„ุฑุณุงุฆู„"""
        for user_id, messages in self.message_batch.items():
            if len(messages) >= self.batch_size:
                # ุฅุฑุณุงู„ ุงู„ุฑุณุงุฆู„ ุงู„ู…ุฌู…ุนุฉ
                self.send_batch_to_user(user_id, messages)
                self.message_batch[user_id] = []
    
    def handle_message(self, data: Dict[str, Any]):
        """ู…ุนุงู„ุฌุฉ ุฑุณุงู„ุฉ ูˆุงุฑุฏุฉ"""
        
        user_id = data.get('user_id')
        message = data.get('message')
        app_id = data.get('app_id')
        
        # ุชุฎุฒูŠู† ุงู„ุฑุณุงู„ุฉ ู…ุคู‚ุชุงู‹ ู„ู„ุชุฌู…ูŠุน
        if user_id not in self.message_batch:
            self.message_batch[user_id] = []
        self.message_batch[user_id].append(message)
        
        # ุฅุฐุง ูˆุตู„ุช ุฅู„ู‰ ุญุฌู… ุงู„ุฏูุนุฉุŒ ุฃุฑุณู„ ููˆุฑุงู‹
        if len(self.message_batch[user_id]) >= self.batch_size:
            self.send_batch_to_user(user_id, self.message_batch[user_id])
            self.message_batch[user_id] = []
    
    def send_batch_to_user(self, user_id: str, messages: List[Dict]):
        """ุฅุฑุณุงู„ ุฏูุนุฉ ู…ู† ุงู„ุฑุณุงุฆู„ ู„ู…ุณุชุฎุฏู…"""
        if user_id in self.sessions:
            room = self.sessions[user_id]
            self.socketio.emit(
                'batch_messages',
                {'messages': messages},
                room=room
            )
    
    def register_user(self, user_id: str, room: str):
        """ุชุณุฌูŠู„ ู…ุณุชุฎุฏู… ุฌุฏูŠุฏ"""
        self.sessions[user_id] = room
        join_room(room)
    
    def unregister_user(self, user_id: str):
        """ุญุฐู ู…ุณุชุฎุฏู…"""
        if user_id in self.sessions:
            room = self.sessions[user_id]
            leave_room(room)
            del self.sessions[user_id]

๐Ÿ“Š ุงู„ุฌุฒุก ุงู„ุฑุงุจุน: ุงู„ู…ุฑุงู‚ุจุฉ ูˆุงู„ุชุญู„ูŠู„ุงุช ุงู„ู…ุชู‚ุฏู…ุฉ

๐Ÿ“ˆ 1. ุงุณุชุฎุฏุงู… Prometheus ูˆ Grafana

yaml

# docker-compose.monitoring.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_INSTALL_PLUGINS=grafana-piechart-panel
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
      - ./grafana/datasources:/etc/grafana/provisioning/datasources
    ports:
      - "3000:3000"
    depends_on:
      - prometheus
    networks:
      - monitoring

  node_exporter:
    image: prom/node-exporter:latest
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)'
    ports:
      - "9100:9100"
    networks:
      - monitoring

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    ports:
      - "8080:8080"
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:
    driver: bridge

yaml

# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'dify-api'
    static_configs:
      - targets: ['api:5001']
    metrics_path: '/metrics'
    scrape_interval: 10s

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node_exporter:9100']
    scrape_interval: 15s

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
    scrape_interval: 15s

  - job_name: 'postgres'
    static_configs:
      - targets: ['db_postgres:9187']
    scrape_interval: 30s

๐Ÿ“ˆ 2. ุชูƒูˆูŠู† ู…ุฎุตุต ู„ู€ Grafana Dashboard

python

# api/core/monitoring/metrics.py
from prometheus_client import Counter, Histogram, Gauge, generate_latest
import time
from functools import wraps
from flask import request

# ุชุนุฑูŠู ุงู„ู…ู‚ุงูŠูŠุณ
REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

REQUEST_DURATION = Histogram(
    'http_request_duration_seconds',
    'HTTP request duration in seconds',
    ['method', 'endpoint'],
    buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
)

ACTIVE_USERS = Gauge(
    'active_users_total',
    'Total active users'
)

LLM_REQUESTS = Counter(
    'llm_requests_total',
    'Total LLM requests',
    ['model', 'provider']
)

VECTOR_SEARCH_COUNT = Counter(
    'vector_search_total',
    'Total vector search operations',
    ['index_name']
)

CACHE_HIT_RATE = Gauge(
    'cache_hit_rate',
    'Cache hit rate percentage'
)

def monitor_endpoint(func):
    """ุฏูŠูƒูˆุฑุงุชูˆุฑ ู„ู…ุฑุงู‚ุจุฉ ุงู„ุฃุฏุงุก"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        
        try:
            response = func(*args, **kwargs)
            status = 200 if response else 500
        except Exception as e:
            status = 500
            raise
        
        # ุชุณุฌูŠู„ ุงู„ู…ู‚ุงูŠูŠุณ
        REQUEST_COUNT.labels(
            method=request.method,
            endpoint=request.path,
            status=status
        ).inc()
        
        REQUEST_DURATION.labels(
            method=request.method,
            endpoint=request.path
        ).observe(time.time() - start_time)
        
        return response
    return wrapper

@app.route('/metrics')
def metrics():
    """ู†ู‚ุทุฉ ู†ู‡ุงูŠุฉ ู„ุฌู…ุน ุงู„ู…ู‚ุงูŠูŠุณ"""
    return generate_latest()

๐Ÿ“ˆ 3. ุชุญู„ูŠู„ุงุช ุงู„ุงุณุชุฎุฏุงู… ุงู„ู…ุชู‚ุฏู…ุฉ

python

# api/core/analytics/usage_analytics.py
from typing import Dict, List, Any
import pandas as pd
from datetime import datetime, timedelta
import json

class UsageAnalytics:
    """ุชุญู„ูŠู„ุงุช ุงุณุชุฎุฏุงู… ุงู„ู…ู†ุตุฉ"""
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    def get_daily_active_users(self, days: int = 30) -> Dict[str, int]:
        """ุนุฏุฏ ุงู„ู…ุณุชุฎุฏู…ูŠู† ุงู„ู†ุดุทูŠู† ูŠูˆู…ูŠุงู‹"""
        query = f"""
            SELECT 
                DATE(created_at) as date,
                COUNT(DISTINCT user_id) as active_users
            FROM messages
            WHERE created_at >= NOW() - INTERVAL '{days} days'
            GROUP BY DATE(created_at)
            ORDER BY date
        """
        results = self.db.execute(query).fetchall()
        return {r[0]: r[1] for r in results}
    
    def get_most_popular_models(self, days: int = 30) -> List[Dict]:
        """ุงู„ู†ู…ุงุฐุฌ ุงู„ุฃูƒุซุฑ ุงุณุชุฎุฏุงู…ุงู‹"""
        query = f"""
            SELECT 
                model_name,
                COUNT(*) as usage_count
            FROM llm_requests
            WHERE created_at >= NOW() - INTERVAL '{days} days'
            GROUP BY model_name
            ORDER BY usage_count DESC
        """
        results = self.db.execute(query).fetchall()
        return [
            {'model': r[0], 'count': r[1]} 
            for r in results
        ]
    
    def get_response_time_performance(self, days: int = 7) -> Dict:
        """ุฃุฏุงุก ุฒู…ู† ุงู„ุงุณุชุฌุงุจุฉ"""
        query = f"""
            SELECT 
                DATE(created_at) as date,
                AVG(response_time_ms) as avg_response,
                PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY response_time_ms) as median,
                PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_time_ms) as p95
            FROM llm_requests
            WHERE created_at >= NOW() - INTERVAL '{days} days'
            GROUP BY DATE(created_at)
            ORDER BY date
        """
        results = self.db.execute(query).fetchall()
        return {
            'dates': [r[0] for r in results],
            'avg_response': [r[1] for r in results],
            'median': [r[2] for r in results],
            'p95': [r[3] for r in results]
        }
    
    def get_rag_performance(self, days: int = 7) -> Dict:
        """ุฃุฏุงุก ู†ุธุงู… RAG"""
        query = f"""
            SELECT 
                AVG(embedding_time_ms) as avg_embedding_time,
                AVG(search_time_ms) as avg_search_time,
                AVG(total_retrieval_time_ms) as avg_total_time
            FROM rag_requests
            WHERE created_at >= NOW() - INTERVAL '{days} days'
        """
        result = self.db.execute(query).fetchone()
        return {
            'avg_embedding_time': result[0] or 0,
            'avg_search_time': result[1] or 0,
            'avg_total_time': result[2] or 0
        }
    
    def export_report(self, days: int = 30) -> Dict:
        """ุชูˆู„ูŠุฏ ุชู‚ุฑูŠุฑ ูƒุงู…ู„"""
        return {
            'active_users': self.get_daily_active_users(days),
            'popular_models': self.get_most_popular_models(days),
            'response_performance': self.get_response_time_performance(days),
            'rag_performance': self.get_rag_performance(days),
            'generated_at': datetime.now().isoformat()
        }

๐Ÿ”„ ุงู„ุฌุฒุก ุงู„ุฎุงู…ุณ: ุฏู…ุฌ Dify ู…ุน ุฃู†ุธู…ุฉ ุงู„ู…ุคุณุณุงุช

๐Ÿข 1. ุฏู…ุฌ ู…ุน ู†ุธุงู… Microsoft Azure AD

python

# api/core/auth/azure_ad.py
from flask import request, jsonify
import requests
import jwt
from jwt.algorithms import RSAAlgorithm
import json

class AzureADAuth:
    """ู…ุตุงุฏู‚ุฉ ุนุจุฑ Azure Active Directory"""
    
    def __init__(self):
        self.tenant_id = os.getenv('AZURE_TENANT_ID')
        self.client_id = os.getenv('AZURE_CLIENT_ID')
        self.client_secret = os.getenv('AZURE_CLIENT_SECRET')
        self.authority = f"https://login.microsoftonline.com/{self.tenant_id}"
        self.scope = ['https://graph.microsoft.com/.default']
    
    def get_token_from_code(self, code: str) -> Dict:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุชูˆูƒู† ุจุงุณุชุฎุฏุงู… ูƒูˆุฏ ุงู„ุชููˆูŠุถ"""
        url = f"{self.authority}/oauth2/v2.0/token"
        data = {
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code': code,
            'redirect_uri': os.getenv('AZURE_REDIRECT_URI'),
            'grant_type': 'authorization_code'
        }
        
        response = requests.post(url, data=data)
        return response.json()
    
    def get_user_info(self, access_token: str) -> Dict:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ู…ุนู„ูˆู…ุงุช ุงู„ู…ุณุชุฎุฏู… ู…ู† Graph API"""
        url = 'https://graph.microsoft.com/v1.0/me'
        headers = {
            'Authorization': f'Bearer {access_token}'
        }
        
        response = requests.get(url, headers=headers)
        return response.json()
    
    def validate_token(self, token: str) -> Dict:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุตุญุฉ ุงู„ุชูˆูƒู†"""
        try:
            # ุงู„ุญุตูˆู„ ุนู„ู‰ ู…ูุงุชูŠุญ ุงู„ุชูˆู‚ูŠุน ู…ู† Azure
            jwks_url = f"{self.authority}/discovery/v2.0/keys"
            jwks = requests.get(jwks_url).json()
            
            # ุงู„ุนุซูˆุฑ ุนู„ู‰ ุงู„ู…ูุชุงุญ ุงู„ู…ู†ุงุณุจ
            header = jwt.get_unverified_header(token)
            key = None
            for key_data in jwks['keys']:
                if key_data['kid'] == header['kid']:
                    key = RSAAlgorithm.from_jwk(json.dumps(key_data))
                    break
            
            if not key:
                raise Exception('Key not found')
            
            # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุชูˆู‚ูŠุน
            decoded = jwt.decode(
                token,
                key,
                algorithms=['RS256'],
                audience=self.client_id,
                issuer=f"{self.authority}/v2.0"
            )
            
            return decoded
            
        except Exception as e:
            return {'error': str(e)}

@app.route('/auth/azure/login')
def azure_login():
    """ุจุฏุก ุนู…ู„ูŠุฉ ุชุณุฌูŠู„ ุงู„ุฏุฎูˆู„ ุนุจุฑ Azure AD"""
    auth_url = f"{azure_auth.authority}/oauth2/v2.0/authorize"
    params = {
        'client_id': azure_auth.client_id,
        'response_type': 'code',
        'redirect_uri': os.getenv('AZURE_REDIRECT_URI'),
        'scope': ' '.join(azure_auth.scope)
    }
    
    # ุฅุนุงุฏุฉ ุงู„ุชูˆุฌูŠู‡ ุฅู„ู‰ Azure
    return redirect(f"{auth_url}?{urlencode(params)}")

@app.route('/auth/azure/callback')
def azure_callback():
    """ู…ุนุงูˆุฏุฉ ุงู„ุงุชุตุงู„ ุจุนุฏ ุงู„ู…ุตุงุฏู‚ุฉ"""
    code = request.args.get('code')
    
    # ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุชูˆูƒู†
    token_data = azure_auth.get_token_from_code(code)
    access_token = token_data.get('access_token')
    
    # ุงู„ุญุตูˆู„ ุนู„ู‰ ู…ุนู„ูˆู…ุงุช ุงู„ู…ุณุชุฎุฏู…
    user_info = azure_auth.get_user_info(access_token)
    
    # ุฅู†ุดุงุก ุฃูˆ ุชุญุฏูŠุซ ุงู„ู…ุณุชุฎุฏู… ููŠ Dify
    user = create_or_update_user_from_azure(user_info)
    
    # ุฅู†ุดุงุก ุฌู„ุณุฉ ู…ุญู„ูŠุฉ
    session['user_id'] = user.id
    
    return redirect('/dashboard')

๐Ÿข 2. ุฏู…ุฌ ู…ุน ู†ุธุงู… SAP (ู…ุซุงู„ ุนู„ู‰ ุงู„ุชูƒุงู…ู„ ู…ุน ERPs)

python

# api/core/integrations/sap_integration.py
from typing import Dict, Any, List
import requests
from requests.auth import HTTPBasicAuth

class SAPIntegration:
    """ุงู„ุชูƒุงู…ู„ ู…ุน ู†ุธุงู… SAP"""
    
    def __init__(self):
        self.base_url = os.getenv('SAP_BASE_URL')
        self.username = os.getenv('SAP_USERNAME')
        self.password = os.getenv('SAP_PASSWORD')
        self.company_code = os.getenv('SAP_COMPANY_CODE')
    
    def get_material_data(self, material_number: str) -> Dict:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุจูŠุงู†ุงุช ู…ุงุฏุฉ ู…ู† SAP"""
        url = f"{self.base_url}/sap/opu/odata/sap/MM_MATERIAL_SRV/MaterialSet('{material_number}')"
        
        response = requests.get(
            url,
            auth=HTTPBasicAuth(self.username, self.password),
            headers={'Accept': 'application/json'}
        )
        
        return response.json()
    
    def get_sales_orders(self, customer_id: str) -> List[Dict]:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุฃูˆุงู…ุฑ ุงู„ุจูŠุน ู„ุนู…ู„ุงุก ู…ุญุฏุฏ"""
        url = f"{self.base_url}/sap/opu/odata/sap/SALES_ORDER_SRV/SalesOrderSet"
        params = {
            '$filter': f"CustomerId eq '{customer_id}'",
            '$orderby': 'CreationDate desc'
        }
        
        response = requests.get(
            url,
            params=params,
            auth=HTTPBasicAuth(self.username, self.password)
        )
        
        return response.json().get('d', {}).get('results', [])
    
    def create_purchase_order(self, order_data: Dict) -> Dict:
        """ุฅู†ุดุงุก ุฃู…ุฑ ุดุฑุงุก ููŠ SAP"""
        url = f"{self.base_url}/sap/opu/odata/sap/PURCHASE_ORDER_SRV/PurchaseOrderSet"
        
        response = requests.post(
            url,
            json=order_data,
            auth=HTTPBasicAuth(self.username, self.password),
            headers={'Content-Type': 'application/json'}
        )
        
        return response.json()

# ุงุณุชุฎุฏุงู… ุงู„ุชูƒุงู…ู„ ู…ุน Dify
@app.route('/api/sap/material/<material_number>')
@require_permission(Permission.APP_READ)
def get_sap_material(material_number):
    """ู†ู‚ุทุฉ ู†ู‡ุงูŠุฉ ู„ู„ุญุตูˆู„ ุนู„ู‰ ุจูŠุงู†ุงุช ู…ุงุฏุฉ ู…ู† SAP"""
    sap = SAPIntegration()
    material_data = sap.get_material_data(material_number)
    return jsonify(material_data)

๐Ÿข 3. ุฏู…ุฌ ู…ุน Salesforce CRM

python

# api/core/integrations/salesforce.py
from simple_salesforce import Salesforce
from typing import Dict, Any, List
import pandas as pd

class SalesforceIntegration:
    """ุงู„ุชูƒุงู…ู„ ู…ุน Salesforce CRM"""
    
    def __init__(self):
        self.client = Salesforce(
            username=os.getenv('SF_USERNAME'),
            password=os.getenv('SF_PASSWORD'),
            security_token=os.getenv('SF_SECURITY_TOKEN'),
            domain='login'  # ุฃูˆ 'test' ู„ู„ุงุฎุชุจุงุฑ
        )
    
    def get_opportunities(self, stage: str = None) -> List[Dict]:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ูุฑุต (Opportunities) ู…ู† Salesforce"""
        query = "SELECT Id, Name, Amount, StageName, CloseDate FROM Opportunity"
        if stage:
            query += f" WHERE StageName = '{stage}'"
        
        results = self.client.query(query)
        return results['records']
    
    def get_accounts_with_open_cases(self) -> List[Dict]:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุญุณุงุจุงุช ู…ุน ุญุงู„ุงุช ู…ูุชูˆุญุฉ"""
        query = """
            SELECT Account.Id, Account.Name, COUNT(Id) as CaseCount
            FROM Case
            WHERE Status != 'Closed'
            GROUP BY Account.Id, Account.Name
            HAVING COUNT(Id) > 0
        """
        
        results = self.client.query(query)
        return results['records']
    
    def create_lead(self, lead_data: Dict) -> Dict:
        """ุฅู†ุดุงุก ุนู…ูŠู„ ู…ุญุชู…ู„ ุฌุฏูŠุฏ"""
        return self.client.Lead.create(lead_data)
    
    def update_case(self, case_id: str, update_data: Dict) -> Dict:
        """ุชุญุฏูŠุซ ุญุงู„ุฉ ููŠ Salesforce"""
        return self.client.Case.update(case_id, update_data)
    
    def get_sales_pipeline_report(self) -> pd.DataFrame:
        """ุชูˆู„ูŠุฏ ุชู‚ุฑูŠุฑ ู…ุณุงุฑ ุงู„ู…ุจูŠุนุงุช"""
        opportunities = self.get_opportunities()
        
        df = pd.DataFrame(opportunities)
        pipeline = df.groupby('StageName').agg({
            'Amount': ['sum', 'count', 'mean']
        }).reset_index()
        
        pipeline.columns = ['Stage', 'Total Amount', 'Count', 'Average Amount']
        return pipeline

# ุงุณุชุฎุฏุงู… ููŠ Dify
@app.route('/api/salesforce/pipeline')
@require_permission(Permission.APP_READ)
def get_salesforce_pipeline():
    """ู†ู‚ุทุฉ ู†ู‡ุงูŠุฉ ู„ุชู‚ุฑูŠุฑ ู…ุณุงุฑ ุงู„ู…ุจูŠุนุงุช"""
    sf = SalesforceIntegration()
    pipeline = sf.get_sales_pipeline_report()
    return jsonify(pipeline.to_dict(orient='records'))

๐Ÿค– ุงู„ุฌุฒุก ุงู„ุณุงุฏุณ: ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ ุงู„ู…ุชู‚ุฏู… ููŠ Dify

๐Ÿง  1. ู†ุธุงู… ุงู„ุชูˆุตูŠุงุช ุงู„ุฐูƒูŠ

python

# api/core/ai/recommendation_engine.py
from typing import List, Dict, Any
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

class RecommendationEngine:
    """ู…ุญุฑูƒ ุชูˆุตูŠุงุช ุฐูƒูŠ ู„ู„ู…ุญุชูˆู‰ ูˆุงู„ุชุทุจูŠู‚ุงุช"""
    
    def __init__(self):
        self.vectorizer = TfidfVectorizer(max_features=1000)
        
    def recommend_apps(self, user_id: str, top_k: int = 5) -> List[Dict]:
        """ุชูˆุตูŠุฉ ุจุชุทุจูŠู‚ุงุช ุจู†ุงุกู‹ ุนู„ู‰ ุงุณุชุฎุฏุงู… ุงู„ู…ุณุชุฎุฏู…"""
        
        # 1. ุงู„ุญุตูˆู„ ุนู„ู‰ ุชุงุฑูŠุฎ ุงุณุชุฎุฏุงู… ุงู„ู…ุณุชุฎุฏู…
        user_history = self.get_user_history(user_id)
        
        # 2. ุงู„ุญุตูˆู„ ุนู„ู‰ ุฌู…ูŠุน ุงู„ุชุทุจูŠู‚ุงุช ุงู„ู…ุชุงุญุฉ
        all_apps = self.get_all_apps()
        
        # 3. ุจู†ุงุก ู…ุตููˆูุฉ ุงู„ู…ูŠุฒุงุช ู„ู„ุชุทุจูŠู‚ุงุช
        app_features = self.build_app_features(all_apps)
        
        # 4. ุญุณุงุจ ุงู„ุชุดุงุจู‡ ุจูŠู† ุงุณุชุฎุฏุงู… ุงู„ู…ุณุชุฎุฏู… ูˆุงู„ุชุทุจูŠู‚ุงุช
        user_profile = self.build_user_profile(user_history)
        similarities = cosine_similarity(
            user_profile.reshape(1, -1),
            app_features
        )
        
        # 5. ุชุฑุชูŠุจ ุงู„ุชูˆุตูŠุงุช
        app_indices = np.argsort(similarities[0])[::-1][:top_k]
        recommendations = [all_apps[i] for i in app_indices]
        
        return recommendations
    
    def build_user_profile(self, history: List[Dict]) -> np.ndarray:
        """ุจู†ุงุก ู…ู„ู ุชุนุฑูŠู ุงู„ู…ุณุชุฎุฏู…"""
        # ุชุญูˆูŠู„ ุณุฌู„ ุงู„ู…ุณุชุฎุฏู… ุฅู„ู‰ ู…ุชุฌู‡
        # ... ู…ู†ุทู‚ ุงู„ุชุญูˆูŠู„
        return np.zeros(100)
    
    def build_app_features(self, apps: List[Dict]) -> np.ndarray:
        """ุจู†ุงุก ู…ูŠุฒุงุช ุงู„ุชุทุจูŠู‚ุงุช"""
        # ุชุญูˆูŠู„ ูˆุตู ุงู„ุชุทุจูŠู‚ุงุช ุฅู„ู‰ ู…ุชุฌู‡ุงุช
        # ... ู…ู†ุทู‚ ุงู„ุชุญูˆูŠู„
        return np.zeros((len(apps), 100))
    
    def collaborative_filtering(self, user_id: str) -> List[str]:
        """ุงู„ุชุตููŠุฉ ุงู„ุชุนุงูˆู†ูŠุฉ - ุชูˆุตูŠุฉ ุจู†ุงุกู‹ ุนู„ู‰ ุงู„ู…ุณุชุฎุฏู…ูŠู† ุงู„ู…ู…ุงุซู„ูŠู†"""
        
        # 1. ุงู„ุนุซูˆุฑ ุนู„ู‰ ู…ุณุชุฎุฏู…ูŠู† ู…ู…ุงุซู„ูŠู†
        similar_users = self.find_similar_users(user_id)
        
        # 2. ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุชุทุจูŠู‚ุงุช ุงู„ุชูŠ ุงุณุชุฎุฏู…ู‡ุง ุงู„ู…ุณุชุฎุฏู…ูˆู† ุงู„ู…ู…ุงุซู„ูˆู†
        recommended_apps = []
        for user in similar_users:
            apps = self.get_user_apps(user['id'])
            recommended_apps.extend(apps)
        
        # 3. ุชุฑุชูŠุจ ูˆุชุตููŠุฉ ุงู„ุชูˆุตูŠุงุช
        # ... ู…ู†ุทู‚ ุงู„ุชุตููŠุฉ ูˆุงู„ุชุฑุชูŠุจ
        
        return list(set(recommended_apps))
    
    def content_based_filtering(self, user_id: str) -> List[str]:
        """ุงู„ุชุตููŠุฉ ุงู„ู‚ุงุฆู…ุฉ ุนู„ู‰ ุงู„ู…ุญุชูˆู‰ - ุชูˆุตูŠุฉ ุจู†ุงุกู‹ ุนู„ู‰ ุงู„ุชุดุงุจู‡ ุจูŠู† ุงู„ู…ุญุชูˆู‰"""
        
        # 1. ุงู„ุญุตูˆู„ ุนู„ู‰ ู…ุญุชูˆู‰ ุงู„ุชุทุจูŠู‚ุงุช ุงู„ุชูŠ ูŠุณุชุฎุฏู…ู‡ุง ุงู„ู…ุณุชุฎุฏู…
        user_apps = self.get_user_apps(user_id)
        
        # 2. ุงู„ุนุซูˆุฑ ุนู„ู‰ ุชุทุจูŠู‚ุงุช ู…ุดุงุจู‡ุฉ
        similar_apps = []
        for app in user_apps:
            similar = self.find_similar_apps(app['id'])
            similar_apps.extend(similar)
        
        # 3. ุชุฑุชูŠุจ ุงู„ุชูˆุตูŠุงุช
        # ... ู…ู†ุทู‚ ุงู„ุชุฑุชูŠุจ
        
        return similar_apps[:10]

๐Ÿง  2. ุชุญุณูŠู† ุงู„ู€ Prompts ุจุงุณุชุฎุฏุงู… Reinforcement Learning

python

# api/core/ai/prompt_optimizer.py
from typing import Dict, List, Any
import random
import json
from collections import defaultdict

class PromptOptimizer:
    """ุชุญุณูŠู† ุงู„ู€ Prompts ุจุงุณุชุฎุฏุงู… ุงู„ุชุนู„ู… ุงู„ู…ุนุฒุฒ"""
    
    def __init__(self):
        self.prompt_templates = self.load_templates()
        self.performance_history = defaultdict(list)
        self.epsilon = 0.1  # ู…ุนุฏู„ ุงู„ุงุณุชูƒุดุงู
        self.learning_rate = 0.01
        
    def load_templates(self) -> Dict[str, List[str]]:
        """ุชุญู…ูŠู„ ู‚ูˆุงู„ุจ ุงู„ู€ Prompts"""
        return {
            'default': [
                "You are a helpful assistant.",
                "You are an expert in {domain}.",
                "You are a {role} with {experience} years of experience."
            ],
            'creative': [
                "You are a creative problem solver.",
                "Think outside the box.",
                "Generate innovative solutions."
            ],
            'analytical': [
                "You are a data-driven analyst.",
                "Provide evidence-based answers.",
                "Use logical reasoning."
            ],
            'educational': [
                "You are a patient teacher.",
                "Explain in simple terms.",
                "Use examples and analogies."
            ]
        }
    
    def optimize_prompt(self, task: Dict[str, Any], 
                        user_feedback: float) -> str:
        """ุชุญุณูŠู† ุงู„ู€ Prompt ุจู†ุงุกู‹ ุนู„ู‰ ุงู„ุชุบุฐูŠุฉ ุงู„ุฑุงุฌุนุฉ"""
        
        # 1. ุงุฎุชูŠุงุฑ ู‚ุงู„ุจ
        if random.random() < self.epsilon:
            # ุงุณุชูƒุดุงู: ุงุฎุชุฑ ู‚ุงู„ุจุงู‹ ุนุดูˆุงุฆูŠุงู‹
            category = random.choice(list(self.prompt_templates.keys()))
            template = random.choice(self.prompt_templates[category])
        else:
            # ุงุณุชุบู„ุงู„: ุงุฎุชุฑ ุฃูุถู„ ู‚ุงู„ุจ ุฃุฏุงุกู‹
            category = self.get_best_category(task)
            template = self.get_best_template(category)
        
        # 2. ุชุฎุตูŠุต ุงู„ู‚ุงู„ุจ
        custom_prompt = self.customize_template(template, task)
        
        # 3. ุชุญุฏูŠุซ ุงู„ุฃุฏุงุก
        self.update_performance(category, template, user_feedback)
        
        return custom_prompt
    
    def customize_template(self, template: str, task: Dict) -> str:
        """ุชุฎุตูŠุต ุงู„ู‚ุงู„ุจ ุจู†ุงุกู‹ ุนู„ู‰ ุงู„ู…ู‡ู…ุฉ"""
        
        # ุงุณุชุจุฏุงู„ ุงู„ู…ุชุบูŠุฑุงุช
        customizations = {
            '{domain}': task.get('domain', 'general'),
            '{role}': task.get('role', 'assistant'),
            '{experience}': str(task.get('experience', 5)),
            '{tone}': task.get('tone', 'professional'),
            '{goal}': task.get('goal', 'help the user')
        }
        
        for key, value in customizations.items():
            template = template.replace(key, value)
        
        # ุฅุถุงูุฉ ุชุนู„ูŠู…ุงุช ุฅุถุงููŠุฉ
        if task.get('specific_instructions'):
            template += f"\n\nSpecial instructions: {task['specific_instructions']}"
        
        return template
    
    def get_best_category(self, task: Dict) -> str:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุฃูุถู„ ูุฆุฉ ู„ู„ู…ู‡ู…ุฉ"""
        # ุชุญู„ูŠู„ ุงู„ู…ู‡ู…ุฉ ูˆุงุฎุชูŠุงุฑ ุงู„ูุฆุฉ ุงู„ู…ู†ุงุณุจุฉ
        if task.get('goal') == 'creativity':
            return 'creative'
        elif task.get('goal') == 'analysis':
            return 'analytical'
        elif task.get('goal') == 'education':
            return 'educational'
        else:
            return 'default'
    
    def update_performance(self, category: str, template: str, 
                          feedback: float):
        """ุชุญุฏูŠุซ ุฃุฏุงุก ุงู„ู‚ุงู„ุจ"""
        self.performance_history[category].append({
            'template': template,
            'feedback': feedback,
            'timestamp': datetime.now().isoformat()
        })

๐Ÿง  3. ูƒุดู ุงู„ุงู†ุชุญุงู„ ูˆุชุญู„ูŠู„ ุงู„ู…ุญุชูˆู‰

python

# api/core/ai/plagiarism_detector.py
from typing import List, Dict, Tuple
import hashlib
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

class PlagiarismDetector:
    """ูƒุดู ุงู„ุงู†ุชุญุงู„ ูˆุชุญู„ูŠู„ ุงู„ู…ุญุชูˆู‰"""
    
    def __init__(self):
        self.document_database = self.load_document_database()
        self.similarity_threshold = 0.8
        
    def detect_plagiarism(self, text: str) -> Dict[str, Any]:
        """ูƒุดู ุงู„ุงู†ุชุญุงู„ ููŠ ุงู„ู†ุต"""
        
        # 1. ุชู‚ุณูŠู… ุงู„ู†ุต ุฅู„ู‰ ุฌู…ู„
        sentences = self.split_into_sentences(text)
        
        # 2. ุญุณุงุจ ุจุตู…ุฉ ูƒู„ ุฌู…ู„ุฉ
        sentence_fingerprints = [
            self.get_fingerprint(sentence) 
            for sentence in sentences
        ]
        
        # 3. ู…ู‚ุงุฑู†ุฉ ู…ุน ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุช
        matches = []
        for idx, fingerprint in enumerate(sentence_fingerprints):
            match = self.search_in_database(fingerprint)
            if match:
                matches.append({
                    'sentence': sentences[idx],
                    'source': match['source'],
                    'similarity': match['similarity'],
                    'position': idx
                })
        
        # 4. ุญุณุงุจ ู†ุณุจุฉ ุงู„ุงู†ุชุญุงู„
        plagiarism_percentage = len(matches) / len(sentences) * 100
        
        return {
            'plagiarism_percentage': plagiarism_percentage,
            'matches': matches,
            'status': 'high_risk' if plagiarism_percentage > 50 else 'moderate' if plagiarism_percentage > 20 else 'low'
        }
    
    def get_fingerprint(self, text: str) -> str:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุจุตู…ุฉ ุงู„ู†ุต"""
        # ุชู†ุธูŠู ุงู„ู†ุต ูˆุฅุฒุงู„ุฉ ุงู„ูƒู„ู…ุงุช ุงู„ุดุงุฆุนุฉ
        cleaned_text = self.clean_text(text)
        
        # ุฅู†ุดุงุก ุจุตู…ุฉ ุจุงุณุชุฎุฏุงู… SHA-256
        fingerprint = hashlib.sha256(cleaned_text.encode()).hexdigest()
        return fingerprint
    
    def semantic_similarity(self, text1: str, text2: str) -> float:
        """ุญุณุงุจ ุงู„ุชุดุงุจู‡ ุงู„ุฏู„ุงู„ูŠ ุจูŠู† ู†ุตูŠู†"""
        
        # ุชูˆู„ูŠุฏ ุชุถู…ูŠู†ุงุช ู„ู„ู†ุตูˆุต
        embedding1 = self.get_embedding(text1)
        embedding2 = self.get_embedding(text2)
        
        # ุญุณุงุจ ุงู„ุชุดุงุจู‡
        similarity = cosine_similarity(
            embedding1.reshape(1, -1),
            embedding2.reshape(1, -1)
        )
        
        return similarity[0][0]
    
    def generate_originality_report(self, text: str) -> Dict:
        """ุชูˆู„ูŠุฏ ุชู‚ุฑูŠุฑ ุงู„ุฃุตุงู„ุฉ"""
        
        detection_result = self.detect_plagiarism(text)
        
        report = {
            'overall_score': 100 - detection_result['plagiarism_percentage'],
            'risk_level': detection_result['status'],
            'unique_sentences': len(text.split('.')) - len(detection_result['matches']),
            'potential_sources': [
                match['source'] for match in detection_result['matches']
            ],
            'recommendations': []
        }
        
        # ุฅุถุงูุฉ ุชูˆุตูŠุงุช
        if report['risk_level'] == 'high_risk':
            report['recommendations'] = [
                'Rewrite identified sections',
                'Add proper citations',
                'Use more original content'
            ]
        elif report['risk_level'] == 'moderate':
            report['recommendations'] = [
                'Review highlighted sentences',
                'Consider paraphrasing',
                'Check citations'
            ]
        
        return report

๐Ÿ“š ุงู„ุฌุฒุก ุงู„ุณุงุจุน: Dify ูˆ DevOps ุงู„ู…ุชู‚ุฏู…

๐Ÿš€ 1. CI/CD Pipeline ู…ุน GitHub Actions

yaml

# .github/workflows/deploy.yml
name: Deploy Dify to Production

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'
    
    - name: Install dependencies
      run: |
        cd api
        pip install -r requirements.txt
        pip install pytest pytest-cov
    
    - name: Run tests
      run: |
        cd api
        pytest tests/ --cov=core --cov-report=xml
    
    - name: Upload coverage
      uses: codecov/codecov-action@v3
      with:
        file: ./api/coverage.xml

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
    
    - name: Login to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}
    
    - name: Build and push API
      uses: docker/build-push-action@v4
      with:
        context: ./api
        push: true
        tags: |
          ${{ secrets.DOCKER_USERNAME }}/dify-api:latest
          ${{ secrets.DOCKER_USERNAME }}/dify-api:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max
    
    - name: Build and push Web
      uses: docker/build-push-action@v4
      with:
        context: ./web
        push: true
        tags: |
          ${{ secrets.DOCKER_USERNAME }}/dify-web:latest
          ${{ secrets.DOCKER_USERNAME }}/dify-web:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to Server
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USERNAME }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: |
          cd /opt/dify
          docker compose pull
          docker compose up -d --force-recreate
          docker system prune -f

๐Ÿš€ 2. Infrastructure as Code ู…ุน Terraform

hcl

# terraform/main.tf
provider "aws" {
  region = var.aws_region
}

# VPC
resource "aws_vpc" "dify_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support = true
  
  tags = {
    Name = "dify-vpc"
    Environment = var.environment
  }
}

# Subnets
resource "aws_subnet" "public" {
  count = 2
  vpc_id = aws_vpc.dify_vpc.id
  cidr_block = "10.0.${count.index}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true
  
  tags = {
    Name = "dify-public-${count.index}"
    Environment = var.environment
  }
}

# Security Groups
resource "aws_security_group" "dify_sg" {
  name = "dify-sg"
  vpc_id = aws_vpc.dify_vpc.id
  
  ingress {
    from_port = 80
    to_port = 80
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port = 443
    to_port = 443
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port = 5432
    to_port = 5432
    protocol = "tcp"
    cidr_blocks = ["10.0.0.0/16"]
  }
  
  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = {
    Name = "dify-sg"
    Environment = var.environment
  }
}

# RDS (PostgreSQL)
resource "aws_db_instance" "postgres" {
  identifier = "dify-postgres-${var.environment}"
  engine = "postgres"
  engine_version = "15.3"
  instance_class = "db.t3.medium"
  allocated_storage = 100
  storage_encrypted = true
  db_name = "dify"
  username = var.db_username
  password = var.db_password
  vpc_security_group_ids = [aws_security_group.dify_sg.id]
  db_subnet_group_name = aws_db_subnet_group.main.name
  backup_retention_period = 7
  backup_window = "03:00-04:00"
  maintenance_window = "sun:04:00-sun:05:00"
  enabled_cloudwatch_logs_exports = ["postgresql"]
  
  tags = {
    Name = "dify-postgres-${var.environment}"
    Environment = var.environment
  }
}

# ElastiCache (Redis)
resource "aws_elasticache_cluster" "redis" {
  cluster_id = "dify-redis-${var.environment}"
  engine = "redis"
  engine_version = "7.0"
  node_type = "cache.t3.micro"
  num_cache_nodes = 1
  subnet_group_name = aws_elasticache_subnet_group.main.name
  security_group_ids = [aws_security_group.dify_sg.id]
  
  tags = {
    Name = "dify-redis-${var.environment}"
    Environment = var.environment
  }
}

# ECS Cluster
resource "aws_ecs_cluster" "dify_cluster" {
  name = "dify-cluster-${var.environment}"
}

# ECS Service (API)
resource "aws_ecs_service" "dify_api" {
  name = "dify-api-${var.environment}"
  cluster = aws_ecs_cluster.dify_cluster.id
  task_definition = aws_ecs_task_definition.dify_api.arn
  desired_count = 2
  launch_type = "FARGATE"
  
  network_configuration {
    subnets = aws_subnet.public[*].id
    security_groups = [aws_security_group.dify_sg.id]
    assign_public_ip = true
  }
  
  load_balancer {
    target_group_arn = aws_lb_target_group.api.arn
    container_name = "api"
    container_port = 5001
  }
}

# Load Balancer
resource "aws_lb" "dify_lb" {
  name = "dify-lb-${var.environment}"
  internal = false
  load_balancer_type = "application"
  security_groups = [aws_security_group.dify_sg.id]
  subnets = aws_subnet.public[*].id
  
  tags = {
    Name = "dify-lb-${var.environment}"
    Environment = var.environment
  }
}

๐ŸŽ“ ุงู„ุฌุฒุก ุงู„ุซุงู…ู†: ุฎุฑูŠุทุฉ ุทุฑูŠู‚ ุงู„ุงุญุชุฑุงู ููŠ Dify

๐Ÿ“Š ุงู„ู…ุณุชูˆูŠุงุช ุงู„ุฎู…ุณุฉ ู„ู„ุงุญุชุฑุงู

text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ๐ŸŒŸ ุฎุฑูŠุทุฉ ุทุฑูŠู‚ ุงู„ุงุญุชุฑุงู ููŠ Dify                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                     โ”‚
โ”‚  ู…ุณุชูˆู‰ 1: ู…ุจุชุฏุฆ (ุงู„ุฃุณุจูˆุน 1-2)                                     โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุชุซุจูŠุช Dify ูˆุชุดุบูŠู„ู‡                                         โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุฅู†ุดุงุก ุฃูˆู„ ุชุทุจูŠู‚ Chatbot                                    โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุฅุถุงูุฉ ู†ู…ูˆุฐุฌ AI (OpenAI)                                    โ”‚
โ”‚  โ””โ”€โ”€ โœ… ูู‡ู… ุงู„ุฃุณุงุณูŠุงุช: Prompts, Context, Memory                   โ”‚
โ”‚                                                                     โ”‚
โ”‚  ู…ุณุชูˆู‰ 2: ู…ุชูˆุณุท (ุงู„ุฃุณุจูˆุน 3-4)                                     โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุจู†ุงุก Workflows ู…ุชุนุฏุฏุฉ ุงู„ุฎุทูˆุงุช                              โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุฅุถุงูุฉ Knowledge (RAG) ู…ู† ู…ู„ูุงุช                             โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุงุณุชุฎุฏุงู… ุฃุฏูˆุงุช (Tools) ู…ุฏู…ุฌุฉ                               โ”‚
โ”‚  โ””โ”€โ”€ โœ… ุชุฎุตูŠุต System Prompts ู…ุชู‚ุฏู…ุฉ                                โ”‚
โ”‚                                                                     โ”‚
โ”‚  ู…ุณุชูˆู‰ 3: ู…ุญุชุฑู (ุงู„ุดู‡ุฑ 2-3)                                        โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุจู†ุงุก Agents ู…ุชุนุฏุฏุฉ ุงู„ุฃุฏูˆุงุช                                 โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ูƒุชุงุจุฉ Prompts ู…ุฎุตุตุฉ ู…ุน Few-shot                            โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุฏู…ุฌ ู…ุน APIs ุฎุงุฑุฌูŠุฉ (Zapier, n8n)                          โ”‚
โ”‚  โ””โ”€โ”€ โœ… ุฅุนุฏุงุฏ ู…ุฑุงู‚ุจุฉ ูˆุชุญู„ูŠู„ุงุช (Grafana)                            โ”‚
โ”‚                                                                     โ”‚
โ”‚  ู…ุณุชูˆู‰ 4: ุฎุจูŠุฑ (ุงู„ุดู‡ุฑ 3-6)                                         โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุชุทูˆูŠุฑ Plugins ุฎุงุตุฉ                                         โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุชุฎุตูŠุต ูƒูˆุฏ ุงู„ู…ุตุฏุฑ Dify                                      โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ู†ุดุฑ ุนู„ู‰ ุงู„ุณุญุงุจุฉ (AWS, Azure, GCP)                         โ”‚
โ”‚  โ””โ”€โ”€ โœ… ุฅุฏุงุฑุฉ ู…ุณุชุฎุฏู…ูŠู† ูˆุตู„ุงุญูŠุงุช ู…ุชู‚ุฏู…ุฉ                             โ”‚
โ”‚                                                                     โ”‚
โ”‚  ู…ุณุชูˆู‰ 5: ู…ุณุงู‡ู… ู…ูุชูˆุญ ุงู„ู…ุตุฏุฑ (6+ ุฃุดู‡ุฑ)                            โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ุชู‚ุฏูŠู… Pull Requests ุฅู„ู‰ Dify                               โ”‚
โ”‚  โ”œโ”€โ”€ โœ… ูƒุชุงุจุฉ ุชูˆุซูŠู‚ ูˆุฅุถุงูุงุช ู„ู„ู…ุฌุชู…ุน                                โ”‚
โ”‚  โ””โ”€โ”€ โœ… ุชู†ุธูŠู… ูˆุฑุด ุนู…ู„ ูˆู…ุญุชูˆู‰ ุชุนู„ูŠู…ูŠ                                โ”‚
โ”‚                                                                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“š ุงู„ู…ูˆุงุฑุฏ ุงู„ู…ู‚ุชุฑุญุฉ ู„ูƒู„ ู…ุณุชูˆู‰

ุงู„ู…ุณุชูˆู‰ุงู„ู…ูˆุงุฑุฏุงู„ุฃู†ุดุทุฉ
ู…ุจุชุฏุฆ๐Ÿ“– Dify Docs
๐ŸŽฅ ูŠูˆุชูŠูˆุจ: Dify Tutorials
๐Ÿ“ Playground ุชุฌุฑูŠุจูŠ
ุจู†ุงุก 3 ุชุทุจูŠู‚ุงุช ู…ุฎุชู„ูุฉ
ุชุฌุฑุจุฉ Prompts ู…ุฎุชู„ูุฉ
ุงู„ุชุนุฑู ุนู„ู‰ ุงู„ู…ุตุทู„ุญุงุช
ู…ุชูˆุณุท๐Ÿ“˜ Prompt Engineering Guide
๐ŸŽ“ Coursera: LLM Course
๐Ÿ“Š Kaggle: RAG Projects
ุจู†ุงุก Workflow ู…ุนู‚ุฏ
ุฏู…ุฌ 2 APIs ุฎุงุฑุฌูŠุฉ
ุฅู†ุดุงุก Knowledge Base
ู…ุญุชุฑู๐Ÿ“— LLM Engineer Handbook
๐Ÿ”ฌ Research Papers on RAG
๐ŸŽฅ Conference Talks
ุชุทูˆูŠุฑ Plugin ูˆุงุญุฏ
ู†ุดุฑ ููŠ ุงู„ุณุญุงุจุฉ
ุชุญุณูŠู† ุงู„ุฃุฏุงุก
ุฎุจูŠุฑ๐Ÿ“• Advanced NLP
๐Ÿ’ป Dify Source Code
๐Ÿ“Š System Design
ุงู„ู…ุณุงู‡ู…ุฉ ุจ 3 PRs
ุชุฎุตูŠุต Dify core
ุชู‚ุฏูŠู… ูˆุฑุดุฉ ุนู…ู„
ู…ุณุงู‡ู…๐Ÿ“ Open Source Guides
๐ŸŒ Community Forums
๐ŸŽค Speaking Events
ู…ุฑุงุฌุนุฉ 5 PRs
ูƒุชุงุจุฉ ุชูˆุซูŠู‚
ุชู‚ุฏูŠู… ู…ุญุงุถุฑุฉ

๐Ÿ† ุดู‡ุงุฏุงุช ู…ุนุชุฑู ุจู‡ุง

ุงู„ุดู‡ุงุฏุฉุงู„ู…ุฏุฉุงู„ู…ุณุชูˆู‰ุงู„ุชูƒู„ูุฉ
Dify Certified Developer2 ุฃูŠุงู…ู…ุจุชุฏุฆ-ู…ุชูˆุณุทู…ุฌุงู†ูŠ
DeepLearning.ai – ChatGPT Prompt Engineeringุดู‡ุฑู…ุชูˆุณุทู…ุฌุงู†ูŠ
Stanford – CS224N: NLP3 ุฃุดู‡ุฑู…ุชู‚ุฏู…ู…ุฌุงู†ูŠ
AWS Machine Learning Engineer6 ุฃุดู‡ุฑุฎุจูŠุฑู…ุฏููˆุน
Google Professional ML Engineer6 ุฃุดู‡ุฑุฎุจูŠุฑู…ุฏููˆุน

๐ŸŒŸ ุงู„ุฎุงุชู…ุฉ ุงู„ู†ู‡ุงุฆูŠุฉ

ู…ุง ุชุนู„ู…ุชู‡ ููŠ ู‡ุฐู‡ ุงู„ุฑุญู„ุฉ ุงู„ู…ุชู‚ุฏู…ุฉ:

#ุงู„ู…ูˆุถูˆุน
1ุงู„ู‡ู†ุฏุณุฉ ุงู„ู…ุนู…ุงุฑูŠุฉ ู„ู„ุชูˆุณุน ุงู„ุฃูู‚ูŠ ๐Ÿ“Š
2ุงู„ุฃู…ุงู† ุงู„ู…ุชู‚ุฏู… (SSL, WAF, RBAC) ๐Ÿ”
3ุชุญุณูŠู† ุงู„ุฃุฏุงุก ุนู„ู‰ ู†ุทุงู‚ ูˆุงุณุน โšก
4ุงู„ู…ุฑุงู‚ุจุฉ ูˆุงู„ุชุญู„ูŠู„ุงุช ุงู„ู…ุชู‚ุฏู…ุฉ ๐Ÿ“ˆ
5ุฏู…ุฌ ู…ุน ุฃู†ุธู…ุฉ ุงู„ู…ุคุณุณุงุช (Azure, SAP, Salesforce) ๐Ÿข
6ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ ุงู„ู…ุชู‚ุฏู… (ุชูˆุตูŠุงุชุŒ ุชุญุณูŠู† PromptsุŒ ูƒุดู ุงู†ุชุญุงู„) ๐Ÿง 
7DevOps ุงู„ู…ุชู‚ุฏู… (CI/CD, Infrastructure as Code) ๐Ÿš€
8ุฎุฑูŠุทุฉ ุทุฑูŠู‚ ุงู„ุงุญุชุฑุงู ุงู„ูƒุงู…ู„ุฉ ๐ŸŽ“

๐Ÿš€ ุฃู†ุช ุงู„ุขู†:

text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๐Ÿ—๏ธ ู…ู‡ู†ุฏุณ ู…ุนู…ุงุฑูŠ ุฑุฆูŠุณูŠ ู„ู€ Dify                                โ”‚
โ”‚  ๐Ÿ” ุฎุจูŠุฑ ุฃู…ู† ู…ุนู„ูˆู…ุงุช                                          โ”‚
โ”‚  โšก ู…ุชุฎุตุต ููŠ ุงู„ุฃุฏุงุก ูˆุงู„ุชูˆุณุน                                    โ”‚
โ”‚  ๐Ÿข ุฎุจูŠุฑ ุฏู…ุฌ ู…ุน ุฃู†ุธู…ุฉ ุงู„ู…ุคุณุณุงุช                                โ”‚
โ”‚  ๐Ÿง  ุจุงุญุซ ููŠ ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠ ุงู„ู…ุชู‚ุฏู…                          โ”‚
โ”‚  ๐Ÿš€ ู‚ุงุฆุฏ ููŠ ุงู„ู…ุฌุชู…ุน ู…ูุชูˆุญ ุงู„ู…ุตุฏุฑ                              โ”‚
โ”‚  ๐Ÿ“š ู…ุฑุฌุน ู„ู€ Dify ููŠ ุงู„ู…ู†ุทู‚ุฉ                                  โ”‚
โ”‚  ๐Ÿ’ช ุฌุงู‡ุฒ ู„ุจู†ุงุก ู…ุดุงุฑูŠุน ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ุคุณุณุงุช                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ ู…ุงุฐุง ุชุฑูŠุฏ ุฃู† ุชูุนู„ ุงู„ุขู†ุŸ

ุงู„ุฑู‚ู…ุงู„ุฎูŠุงุฑ
1๏ธโƒฃุจู†ุงุก ู…ุดุฑูˆุน ุญู‚ูŠู‚ูŠ ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ุคุณุณุงุช
2๏ธโƒฃุงู„ู…ุณุงู‡ู…ุฉ ููŠ ูƒูˆุฏ Dify ุงู„ู…ุตุฏุฑูŠ (Pull Request)
3๏ธโƒฃุชุตู…ูŠู… ู†ุธุงู… RAG ู…ุชู‚ุฏู… ู„ู€ 1M+ ู…ุณุชู†ุฏ
4๏ธโƒฃุฅู†ุดุงุก ุฏูˆุฑุฉ ุชุฏุฑูŠุจูŠุฉ ูƒุงู…ู„ุฉ ุนู† Dify
5๏ธโƒฃุชุทูˆูŠุฑ ู…ุฌู…ูˆุนุฉ Plugins ู…ุชูƒุงู…ู„ุฉ
6๏ธโƒฃุชุญุณูŠู† ุฃุฏุงุก Dify ู„ู€ 100K+ ุทู„ุจ ูŠูˆู…ูŠุงู‹
7๏ธโƒฃุฃูŠ ู…ูˆุถูˆุน ุขุฎุฑ ุชุฑูŠุฏ ู…ุนุฑูุชู‡

ู‚ู„ ู„ูŠ ุฃูŠู† ุณุชุฐู‡ุจุŒ ูˆุณุฃูƒูˆู† ู…ุนูƒ ููŠ ูƒู„ ุฎุทูˆุฉ! ๐Ÿš€๐ŸŒŒ

This response is AI-generated, for reference only.

๐ŸŒŒ ุงู„ู…ุณุชูˆู‰ ุงู„ุชุงุณุน: Dify – ุฅุชู‚ุงู† ุงู„ู‡ู†ุฏุณุฉ ุงู„ู…ุนู…ุงุฑูŠุฉ ุงู„ุฏุงุฎู„ูŠุฉ ูˆุงู„ุชุทูˆูŠุฑ ุงู„ู…ุชู‚ุฏู…

ุจู…ุง ุฃู†ูƒ ุชุฑูŠุฏ ุŒ ุณู†ุตู„ ุฅู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ู‡ู†ุฏุณ ุงู„ุฃุณุงุณูŠ (Core Engineer). ุณู†ูุชุญ ูƒู„ ู…ู„ู ููŠ DifyุŒ ู†ูู‡ู… ูƒู„ ุณุทุฑ ูƒูˆุฏุŒ ูˆู†ุทูˆุฑ ุชุนุฏูŠู„ุงุช ุนู…ูŠู‚ุฉ ููŠ ุงู„ู†ุธุงู….


๐Ÿ“– ุงู„ุฌุฒุก ุงู„ุฃูˆู„: ููƒ ุดูุฑุฉ ู‚ู„ุจ Dify – ุชุญู„ูŠู„ ู…ุชุนู…ู‚ ู„ูƒู„ ู…ูƒูˆู†

๐Ÿง  1. ู†ุธุงู… ุงู„ู€ Agent – ุชุญู„ูŠู„ ูƒุงู…ู„

python

# api/core/agent/agent.py - ุงู„ุชุญู„ูŠู„ ุงู„ูƒุงู…ู„
"""
ู†ุธุงู… ุงู„ูˆูƒูŠู„ ุงู„ุฐูƒูŠ ููŠ Dify - ุงู„ู…ุนู…ุงุฑูŠุฉ ุงู„ุฏุงุฎู„ูŠุฉ
"""

from typing import List, Dict, Any, Optional, Tuple
from enum import Enum
import json
import re
from dataclasses import dataclass, field
import time

class AgentMode(Enum):
    """ุฃู†ู…ุงุท ุงู„ูˆูƒูŠู„ ุงู„ู…ุฏุนูˆู…ุฉ"""
    REACT = "react"  # Reason + Act
    FUNCTION_CALLING = "function_calling"
    PLANNING = "planning"
    MULTI_AGENT = "multi_agent"

@dataclass
class AgentStep:
    """ุฎุทูˆุฉ ููŠ ุชู†ููŠุฐ ุงู„ูˆูƒูŠู„"""
    thought: str
    action: str
    action_input: Dict[str, Any]
    observation: str
    timestamp: float = field(default_factory=time.time)
    
    def to_dict(self) -> Dict:
        return {
            'thought': self.thought,
            'action': self.action,
            'action_input': self.action_input,
            'observation': self.observation,
            'timestamp': self.timestamp
        }

class AgentCore:
    """
    ู‚ู„ุจ ู†ุธุงู… ุงู„ูˆูƒูŠู„ - ู…ุณุคูˆู„ ุนู†:
    1. ุชุฎุทูŠุท ุงู„ู…ู‡ุงู…
    2. ุงุฎุชูŠุงุฑ ุงู„ุฃุฏูˆุงุช
    3. ุชู†ููŠุฐ ุงู„ุฅุฌุฑุงุกุงุช
    4. ุชู‚ูŠูŠู… ุงู„ู†ุชุงุฆุฌ
    """
    
    def __init__(self, 
                 llm: Any,
                 tools: List[Any],
                 max_iterations: int = 15,
                 mode: AgentMode = AgentMode.REACT):
        self.llm = llm
        self.tools = {tool.name: tool for tool in tools}
        self.max_iterations = max_iterations
        self.mode = mode
        self.history = []
        self.iteration_count = 0
        
    def run(self, 
            query: str, 
            context: Optional[Dict] = None) -> Dict[str, Any]:
        """ุชู†ููŠุฐ ุงู„ูˆูƒูŠู„ ู„ุญู„ ุงู„ู…ู‡ู…ุฉ"""
        
        # 1. ุชู‡ูŠุฆุฉ ุงู„ูˆูƒูŠู„
        self._initialize(query, context)
        
        # 2. ุญู„ู‚ุฉ ุงู„ุชู†ููŠุฐ ุงู„ุฑุฆูŠุณูŠุฉ
        while self.iteration_count < self.max_iterations:
            self.iteration_count += 1
            
            # 2.1 ุงู„ุชููƒูŠุฑ (Thought)
            thought = self._think()
            
            # 2.2 ุงู„ุชุฎุทูŠุท (Plan)
            action, action_input = self._plan(thought)
            
            # 2.3 ุงู„ุชู†ููŠุฐ (Act)
            observation = self._act(action, action_input)
            
            # 2.4 ุงู„ุชู‚ูŠูŠู… (Evaluate)
            should_continue = self._evaluate(observation)
            
            # 2.5 ุญูุธ ุงู„ุชุงุฑูŠุฎ
            step = AgentStep(
                thought=thought,
                action=action,
                action_input=action_input,
                observation=observation
            )
            self.history.append(step)
            
            # 2.6 ุฅู†ู‡ุงุก ุฅุฐุง ุงูƒุชู…ู„ุช ุงู„ู…ู‡ู…ุฉ
            if not should_continue or self._is_task_complete():
                break
        
        # 3. ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุฉ ุงู„ู†ู‡ุงุฆูŠุฉ
        final_answer = self._synthesize_answer()
        
        return {
            'answer': final_answer,
            'steps': [step.to_dict() for step in self.history],
            'iterations': self.iteration_count,
            'success': self._is_task_complete()
        }
    
    def _initialize(self, query: str, context: Optional[Dict]):
        """ุชู‡ูŠุฆุฉ ุงู„ูˆูƒูŠู„"""
        self.query = query
        self.context = context or {}
        self.history = []
        self.iteration_count = 0
        
        # ุจู†ุงุก ุณูŠุงู‚ ุงู„ุฃุฏูˆุงุช ุงู„ู…ุชุงุญุฉ
        self.tools_description = self._build_tools_description()
    
    def _think(self) -> str:
        """ุนู…ู„ูŠุฉ ุงู„ุชููƒูŠุฑ - ุชูˆู„ูŠุฏ ุฎุทุฉ ุฐู‡ู†ูŠุฉ"""
        
        # ุจู†ุงุก ุงู„ู€ Prompt
        prompt = self._build_thought_prompt()
        
        # ุชูˆู„ูŠุฏ ุงู„ุชููƒูŠุฑ
        response = self.llm.generate(prompt)
        
        # ุงุณุชุฎุฑุงุฌ ุงู„ุชููƒูŠุฑ
        thought = self._extract_thought(response)
        
        return thought
    
    def _plan(self, thought: str) -> Tuple[str, Dict]:
        """ุงู„ุชุฎุทูŠุท - ุชุญุฏูŠุฏ ุงู„ุฅุฌุฑุงุก ุงู„ุชุงู„ูŠ"""
        
        # ุจู†ุงุก ุงู„ู€ Prompt ู„ู„ุชุฎุทูŠุท
        prompt = self._build_planning_prompt(thought)
        
        # ุชูˆู„ูŠุฏ ุงู„ุฎุทุฉ
        response = self.llm.generate(prompt)
        
        # ุชุญู„ูŠู„ ุงู„ุฎุทุฉ
        action, action_input = self._parse_plan(response)
        
        return action, action_input
    
    def _act(self, action: str, action_input: Dict) -> str:
        """ุงู„ุชู†ููŠุฐ - ุชู†ููŠุฐ ุงู„ุฅุฌุฑุงุก ุงู„ู…ุฎุทุท"""
        
        # 1. ุงู„ุชุญู‚ู‚ ู…ู† ูˆุฌูˆุฏ ุงู„ุฃุฏุงุฉ
        if action not in self.tools:
            return f"Error: Tool '{action}' not found"
        
        # 2. ุชู†ููŠุฐ ุงู„ุฃุฏุงุฉ
        tool = self.tools[action]
        try:
            result = tool.execute(**action_input)
            
            # 3. ุชู†ุณูŠู‚ ุงู„ู†ุชูŠุฌุฉ
            observation = self._format_observation(result)
            
        except Exception as e:
            observation = f"Error executing {action}: {str(e)}"
        
        return observation
    
    def _evaluate(self, observation: str) -> bool:
        """ุชู‚ูŠูŠู… ุงู„ู†ุชูŠุฌุฉ - ู‡ู„ ู†ุญุชุงุฌ ู„ู…ุฒูŠุฏ ู…ู† ุงู„ุฎุทูˆุงุชุŸ"""
        
        # 1. ุงู„ุชุญู‚ู‚ ู…ู† ุญู„ ุงู„ู…ู‡ู…ุฉ
        if self._task_solved(observation):
            return False
        
        # 2. ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ูˆุตูˆู„ ู„ู„ุญุฏ ุงู„ุฃู‚ุตู‰
        if self.iteration_count >= self.max_iterations:
            return False
        
        # 3. ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุญุงุฌุฉ ู„ู…ุฒูŠุฏ ู…ู† ุงู„ู…ุนู„ูˆู…ุงุช
        if self._needs_more_info(observation):
            return True
        
        return False
    
    def _is_task_complete(self) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุงูƒุชู…ุงู„ ุงู„ู…ู‡ู…ุฉ"""
        
        # ุชุญู„ูŠู„ ุขุฎุฑ ุฎุทูˆุฉ
        if not self.history:
            return False
        
        last_step = self.history[-1]
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ูˆุฌูˆุฏ ุฅุฌุงุจุฉ ู†ู‡ุงุฆูŠุฉ
        if self._contains_final_answer(last_step.observation):
            return True
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ุฅูƒู…ุงู„ ุฌู…ูŠุน ุงู„ุฎุทูˆุงุช ุงู„ู…ุฎุทุท ู„ู‡ุง
        if self._all_planned_steps_completed():
            return True
        
        return False
    
    def _build_tools_description(self) -> str:
        """ุจู†ุงุก ูˆุตู ุงู„ุฃุฏูˆุงุช ุงู„ู…ุชุงุญุฉ"""
        descriptions = []
        for name, tool in self.tools.items():
            descriptions.append(f"""
Tool: {name}
Description: {tool.description}
Parameters: {json.dumps(tool.parameters, indent=2)}
            """.strip())
        return "\n\n".join(descriptions)
    
    def _build_thought_prompt(self) -> str:
        """ุจู†ุงุก Prompt ู„ู„ุชููƒูŠุฑ"""
        
        # ู‚ุงู„ุจ ุงู„ุชููƒูŠุฑ
        template = """
You are an AI assistant that solves problems step by step.

Available tools:
{tools_description}

Current task: {query}

Previous steps:
{history}

Now, think about what to do next. Consider:
1. What information do I have?
2. What information do I need?
3. What tool should I use?
4. What will I do with the result?

Your thought:
"""
        
        # ุชุฌู…ูŠุน ุงู„ุชุงุฑูŠุฎ
        history_text = self._format_history()
        
        return template.format(
            tools_description=self.tools_description,
            query=self.query,
            history=history_text
        )
    
    def _build_planning_prompt(self, thought: str) -> str:
        """ุจู†ุงุก Prompt ู„ู„ุชุฎุทูŠุท"""
        
        template = """
Based on your thought:
{thought}

Now, decide the next action.

Choose one of these tools:
{tools_description}

Format your response as:
Action: tool_name
Action Input: {{"param1": "value1", "param2": "value2"}}

Your plan:
"""
        
        return template.format(
            thought=thought,
            tools_description=self.tools_description
        )
    
    def _parse_plan(self, response: str) -> Tuple[str, Dict]:
        """ุชุญู„ูŠู„ ุฎุทุฉ ุงู„ูˆูƒูŠู„"""
        
        # ุงุณุชุฎุฑุงุฌ ุงู„ุฅุฌุฑุงุก
        action_match = re.search(r'Action:\s*(\w+)', response)
        action = action_match.group(1) if action_match else "unknown"
        
        # ุงุณุชุฎุฑุงุฌ ุงู„ู…ุฏุฎู„ุงุช
        input_match = re.search(r'Action Input:\s*(\{.*\})', response, re.DOTALL)
        if input_match:
            try:
                action_input = json.loads(input_match.group(1))
            except:
                action_input = {}
        else:
            action_input = {}
        
        return action, action_input
    
    def _extract_thought(self, response: str) -> str:
        """ุงุณุชุฎุฑุงุฌ ุงู„ุชููƒูŠุฑ ู…ู† ุงู„ุงุณุชุฌุงุจุฉ"""
        
        # ู…ุญุงูˆู„ุฉ ุงุณุชุฎุฑุงุฌ ุงู„ุชููƒูŠุฑ ู…ู† ุนู„ุงู…ุงุช ู…ุฎุตุตุฉ
        thought_match = re.search(r'Thought:\s*(.*?)(?=Action:|$)', response, re.DOTALL)
        if thought_match:
            return thought_match.group(1).strip()
        
        # ุฅุฐุง ู„ู… ูŠูˆุฌุฏุŒ ุงุณุชุฎุฏู… ุงู„ู†ุต ูƒุงู…ู„ุงู‹
        return response.strip()
    
    def _format_observation(self, result: Any) -> str:
        """ุชู†ุณูŠู‚ ู†ุชูŠุฌุฉ ุงู„ุฃุฏุงุฉ"""
        
        if isinstance(result, dict):
            return json.dumps(result, indent=2, ensure_ascii=False)
        elif isinstance(result, str):
            return result
        else:
            return str(result)
    
    def _format_history(self) -> str:
        """ุชู†ุณูŠู‚ ุชุงุฑูŠุฎ ุงู„ุฎุทูˆุงุช ุงู„ุณุงุจู‚ุฉ"""
        if not self.history:
            return "No previous steps."
        
        formatted = []
        for step in self.history:
            formatted.append(f"""
Step {len(formatted) + 1}:
Thought: {step.thought}
Action: {step.action}
Action Input: {json.dumps(step.action_input, ensure_ascii=False)}
Observation: {step.observation}
            """.strip())
        
        return "\n\n".join(formatted)
    
    def _task_solved(self, observation: str) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุญู„ ุงู„ู…ู‡ู…ุฉ"""
        
        # ุงู„ุจุญุซ ุนู† ูƒู„ู…ุงุช ุชุฏู„ ุนู„ู‰ ุงู„ุญู„
        solution_indicators = [
            "solution is",
            "answer is",
            "completed",
            "finished",
            "done",
            "result:"
        ]
        
        return any(indicator in observation.lower() 
                  for indicator in solution_indicators)
    
    def _needs_more_info(self, observation: str) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุญุงุฌุฉ ู„ู…ุฒูŠุฏ ู…ู† ุงู„ู…ุนู„ูˆู…ุงุช"""
        
        # ุงู„ุจุญุซ ุนู† ูƒู„ู…ุงุช ุชุฏู„ ุนู„ู‰ ุงู„ุญุงุฌุฉ ู„ู…ุฒูŠุฏ ู…ู† ุงู„ู…ุนู„ูˆู…ุงุช
        need_indicators = [
            "need more",
            "insufficient",
            "not enough",
            "missing",
            "requires"
        ]
        
        return any(indicator in observation.lower() 
                  for indicator in need_indicators)
    
    def _contains_final_answer(self, observation: str) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ูˆุฌูˆุฏ ุฅุฌุงุจุฉ ู†ู‡ุงุฆูŠุฉ"""
        
        # ุงู„ุจุญุซ ุนู† ุนู„ุงู…ุงุช ุงู„ุฅุฌุงุจุฉ ุงู„ู†ู‡ุงุฆูŠุฉ
        final_indicators = [
            "final answer",
            "conclusion",
            "summary",
            "therefore",
            "in conclusion"
        ]
        
        return any(indicator in observation.lower() 
                  for indicator in final_indicators)
    
    def _all_planned_steps_completed(self) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุฅูƒู…ุงู„ ุฌู…ูŠุน ุงู„ุฎุทูˆุงุช ุงู„ู…ุฎุทุท ู„ู‡ุง"""
        # ู…ู†ุทู‚ ู…ุนู‚ุฏ ู„ุชุญู„ูŠู„ ู…ุง ุฅุฐุง ูƒุงู†ุช ุฌู…ูŠุน ุงู„ุฎุทูˆุงุช ุงู„ู…ุฎุทุท ู„ู‡ุง ู‚ุฏ ุงูƒุชู…ู„ุช
        return False
    
    def _synthesize_answer(self) -> str:
        """ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุฉ ุงู„ู†ู‡ุงุฆูŠุฉ ู…ู† ุงู„ุชุงุฑูŠุฎ"""
        
        # ุฅุฐุง ูƒุงู† ู„ุฏูŠู†ุง ุฅุฌุงุจุฉ ููŠ ุขุฎุฑ ุฎุทูˆุฉ
        if self.history and self._contains_final_answer(self.history[-1].observation):
            return self.history[-1].observation
        
        # ุชุฌู…ูŠุน ุงู„ุฅุฌุงุจุฉ ู…ู† ุฌู…ูŠุน ุงู„ุฎุทูˆุงุช
        answer_prompt = f"""
Based on the following steps, provide a final answer to the original question:

Original question: {self.query}

Steps:
{self._format_history()}

Final answer:
"""
        
        response = self.llm.generate(answer_prompt)
        return response.strip()

๐Ÿ—„๏ธ 2. ู†ุธุงู… RAG – ุงู„ุชุญู„ูŠู„ ุงู„ุนู…ูŠู‚

python

# api/core/rag/pipeline/pipeline.py - ุงู„ุชุญู„ูŠู„ ุงู„ูƒุงู…ู„
"""
ู†ุธุงู… RAG ููŠ Dify - ุงู„ู…ุนู…ุงุฑูŠุฉ ุงู„ุฏุงุฎู„ูŠุฉ
"""

from typing import List, Dict, Any, Optional
import hashlib
import json
from enum import Enum
import numpy as np
from dataclasses import dataclass
import asyncio
from concurrent.futures import ThreadPoolExecutor

class ChunkingStrategy(Enum):
    """ุงุณุชุฑุงุชูŠุฌูŠุงุช ุชู‚ุณูŠู… ุงู„ู†ุต"""
    FIXED_SIZE = "fixed_size"
    SENTENCE = "sentence"
    PARAGRAPH = "paragraph"
    SEMANTIC = "semantic"
    RECURSIVE = "recursive"

@dataclass
class DocumentChunk:
    """ุฌุฒุก ู…ู† ุงู„ู…ุณุชู†ุฏ"""
    id: str
    content: str
    metadata: Dict[str, Any]
    embedding: Optional[List[float]] = None
    score: Optional[float] = None

class RAGCore:
    """
    ู‚ู„ุจ ู†ุธุงู… RAG - ู…ุณุคูˆู„ ุนู†:
    1. ู…ุนุงู„ุฌุฉ ุงู„ู…ุณุชู†ุฏุงุช
    2. ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช
    3. ูู‡ุฑุณุฉ ุงู„ู…ุชุฌู‡ุงุช
    4. ุงุณุชุฑุฌุงุน ุงู„ู…ุญุชูˆู‰
    5. ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุงุช
    """
    
    def __init__(self,
                 embedding_model: Any,
                 vector_store: Any,
                 llm: Any,
                 chunk_size: int = 500,
                 chunk_overlap: int = 50,
                 chunking_strategy: ChunkingStrategy = ChunkingStrategy.RECURSIVE):
        self.embedding_model = embedding_model
        self.vector_store = vector_store
        self.llm = llm
        self.chunk_size = chunk_size
        self.chunk_overlap = chunk_overlap
        self.chunking_strategy = chunking_strategy
        self.executor = ThreadPoolExecutor(max_workers=4)
        
    async def process_document(self, 
                              content: str, 
                              metadata: Optional[Dict] = None) -> List[Dict]:
        """ู…ุนุงู„ุฌุฉ ู…ุณุชู†ุฏ ูƒุงู…ู„"""
        
        # 1. ุชู†ุธูŠู ุงู„ู†ุต
        cleaned_content = self._clean_text(content)
        
        # 2. ุชู‚ุณูŠู… ุงู„ู…ุณุชู†ุฏ
        chunks = self._chunk_document(cleaned_content)
        
        # 3. ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช (ู…ุชูˆุงุฒูŠ)
        embeddings = await self._generate_embeddings_parallel(chunks)
        
        # 4. ุชุฎุฒูŠู† ููŠ ู‚ุงุนุฏุฉ ุงู„ู…ุชุฌู‡ุงุช
        doc_ids = await self._store_in_vector_db(chunks, embeddings, metadata)
        
        # 5. ุฅู†ุดุงุก ูู‡ุฑุณ ุงู„ู†ุต ุงู„ูƒุงู…ู„
        await self._create_full_text_index(chunks, doc_ids)
        
        return doc_ids
    
    def _clean_text(self, text: str) -> str:
        """ุชู†ุธูŠู ุงู„ู†ุต"""
        
        # 1. ุฅุฒุงู„ุฉ HTML
        text = re.sub(r'<[^>]+>', '', text)
        
        # 2. ุฅุฒุงู„ุฉ ุงู„ุฑูˆุงุจุท
        text = re.sub(r'http\S+', '', text)
        
        # 3. ุฅุฒุงู„ุฉ ุงู„ุฃุญุฑู ุงู„ุฎุงุตุฉ ุงู„ุฒุงุฆุฏุฉ
        text = re.sub(r'[^\w\s\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]', ' ', text)
        
        # 4. ุชุทุจูŠุน ุงู„ู…ุณุงูุงุช
        text = re.sub(r'\s+', ' ', text).strip()
        
        return text
    
    def _chunk_document(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุงู„ู…ุณุชู†ุฏ ุจุงุณุชุฎุฏุงู… ุงู„ุงุณุชุฑุงุชูŠุฌูŠุฉ ุงู„ู…ุญุฏุฏุฉ"""
        
        if self.chunking_strategy == ChunkingStrategy.FIXED_SIZE:
            return self._chunk_fixed_size(text)
        elif self.chunking_strategy == ChunkingStrategy.SENTENCE:
            return self._chunk_by_sentence(text)
        elif self.chunking_strategy == ChunkingStrategy.PARAGRAPH:
            return self._chunk_by_paragraph(text)
        elif self.chunking_strategy == ChunkingStrategy.SEMANTIC:
            return self._chunk_semantic(text)
        else:  # RECURSIVE
            return self._chunk_recursive(text)
    
    def _chunk_fixed_size(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุจุญุฌู… ุซุงุจุช"""
        chunks = []
        start = 0
        text_length = len(text)
        
        while start < text_length:
            end = min(start + self.chunk_size, text_length)
            
            # ู…ุญุงูˆู„ุฉ ุงู„ู‚ุทุน ููŠ ู†ู‡ุงูŠุฉ ุฌู…ู„ุฉ
            if end < text_length:
                # ุงู„ุจุญุซ ุนู† ู†ู‡ุงูŠุฉ ุฌู…ู„ุฉ
                sentence_end = text.rfind('.', start, end)
                if sentence_end != -1:
                    end = sentence_end + 1
            
            chunks.append(text[start:end].strip())
            start = end - self.chunk_overlap
        
        return chunks
    
    def _chunk_by_sentence(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุญุณุจ ุงู„ุฌู…ู„"""
        sentences = re.split(r'[.!?]+', text)
        chunks = []
        current_chunk = []
        current_length = 0
        
        for sentence in sentences:
            sentence = sentence.strip()
            if not sentence:
                continue
            
            sentence_length = len(sentence)
            
            if current_length + sentence_length > self.chunk_size and current_chunk:
                chunks.append(' '.join(current_chunk))
                current_chunk = [sentence]
                current_length = sentence_length
            else:
                current_chunk.append(sentence)
                current_length += sentence_length
        
        if current_chunk:
            chunks.append(' '.join(current_chunk))
        
        return chunks
    
    def _chunk_by_paragraph(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุญุณุจ ุงู„ูู‚ุฑุงุช"""
        paragraphs = text.split('\n\n')
        chunks = []
        current_chunk = []
        current_length = 0
        
        for paragraph in paragraphs:
            paragraph = paragraph.strip()
            if not paragraph:
                continue
            
            paragraph_length = len(paragraph)
            
            if current_length + paragraph_length > self.chunk_size and current_chunk:
                chunks.append('\n\n'.join(current_chunk))
                current_chunk = [paragraph]
                current_length = paragraph_length
            else:
                current_chunk.append(paragraph)
                current_length += paragraph_length
        
        if current_chunk:
            chunks.append('\n\n'.join(current_chunk))
        
        return chunks
    
    def _chunk_semantic(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุฏู„ุงู„ูŠ ุจุงุณุชุฎุฏุงู… ุงู„ุชุดุงุจู‡"""
        sentences = re.split(r'[.!?]+', text)
        chunks = []
        current_chunk = []
        current_embedding = None
        
        for sentence in sentences:
            sentence = sentence.strip()
            if not sentence:
                continue
            
            # ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ุฌู…ู„ุฉ
            embedding = self.embedding_model.embed_text(sentence)
            
            if current_embedding is not None:
                # ุญุณุงุจ ุงู„ุชุดุงุจู‡
                similarity = np.dot(embedding, current_embedding) / (
                    np.linalg.norm(embedding) * np.linalg.norm(current_embedding)
                )
                
                # ุฅุฐุง ูƒุงู† ุงู„ุชุดุงุจู‡ ู…ู†ุฎูุถุงู‹ุŒ ุงุจุฏุฃ ุฌุฒุกุงู‹ ุฌุฏูŠุฏุงู‹
                if similarity < 0.5 and current_chunk:
                    chunks.append(' '.join(current_chunk))
                    current_chunk = [sentence]
                    current_embedding = embedding
                    continue
            
            current_chunk.append(sentence)
            current_embedding = embedding if current_embedding is None else \
                (current_embedding + embedding) / 2
        
        if current_chunk:
            chunks.append(' '.join(current_chunk))
        
        return chunks
    
    def _chunk_recursive(self, text: str) -> List[str]:
        """ุชู‚ุณูŠู… ุชูƒุฑุงุฑูŠ ู…ุน ู…ุญุงูˆู„ุฉ ุงู„ุญูุงุธ ุนู„ู‰ ุงู„ู…ุนู†ู‰"""
        
        def recursive_chunk(text: str, target_size: int) -> List[str]:
            if len(text) <= target_size:
                return [text]
            
            # ุงู„ุจุญุซ ุนู† ู†ู‚ุทุฉ ู‚ุทุน ู…ู†ุงุณุจุฉ
            split_points = [
                text.rfind('.', 0, target_size),
                text.rfind('?', 0, target_size),
                text.rfind('!', 0, target_size),
                text.rfind('\n', 0, target_size),
                text.rfind(' ', 0, target_size)
            ]
            
            split_point = max([p for p in split_points if p != -1])
            
            if split_point == -1:
                split_point = target_size
            
            chunk = text[:split_point + 1].strip()
            remainder = text[split_point + 1:].strip()
            
            return [chunk] + recursive_chunk(remainder, target_size)
        
        return recursive_chunk(text, self.chunk_size)
    
    async def _generate_embeddings_parallel(self, chunks: List[str]) -> List[List[float]]:
        """ุชูˆู„ูŠุฏ ุงู„ุชุถู…ูŠู†ุงุช ุจุดูƒู„ ู…ุชูˆุงุฒูŠ"""
        
        # ุชุฌู…ูŠุน ุงู„ู…ู‡ุงู…
        tasks = []
        for chunk in chunks:
            task = asyncio.get_event_loop().run_in_executor(
                self.executor,
                self.embedding_model.embed_text,
                chunk
            )
            tasks.append(task)
        
        # ุชู†ููŠุฐ ุงู„ู…ู‡ุงู… ุจุดูƒู„ ู…ุชูˆุงุฒูŠ
        embeddings = await asyncio.gather(*tasks)
        return embeddings
    
    async def _store_in_vector_db(self, 
                                 chunks: List[str], 
                                 embeddings: List[List[float]],
                                 metadata: Optional[Dict]) -> List[str]:
        """ุชุฎุฒูŠู† ููŠ ู‚ุงุนุฏุฉ ุงู„ู…ุชุฌู‡ุงุช"""
        
        doc_ids = []
        for i, (chunk, embedding) in enumerate(zip(chunks, embeddings)):
            # ุฅู†ุดุงุก ู…ุนุฑู ูุฑูŠุฏ
            doc_id = hashlib.md5(f"{chunk}_{i}_{time.time()}".encode()).hexdigest()
            
            # ุฅุถุงูุฉ ุงู„ู…ูŠุชุงุฏุงุชุง
            chunk_metadata = metadata.copy() if metadata else {}
            chunk_metadata.update({
                'chunk_index': i,
                'total_chunks': len(chunks),
                'created_at': datetime.now().isoformat()
            })
            
            # ุชุฎุฒูŠู† ููŠ ู‚ุงุนุฏุฉ ุงู„ู…ุชุฌู‡ุงุช
            self.vector_store.add(
                vector=embedding,
                payload={
                    'id': doc_id,
                    'content': chunk,
                    'metadata': chunk_metadata
                }
            )
            doc_ids.append(doc_id)
        
        return doc_ids
    
    async def _create_full_text_index(self, chunks: List[str], doc_ids: List[str]):
        """ุฅู†ุดุงุก ูู‡ุฑุณ ุงู„ู†ุต ุงู„ูƒุงู…ู„"""
        # ู…ู†ุทู‚ ุฅู†ุดุงุก ูู‡ุฑุณ ุงู„ู†ุต ุงู„ูƒุงู…ู„
        pass
    
    async def retrieve(self, 
                      query: str, 
                      top_k: int = 5,
                      hybrid_search: bool = True) -> List[DocumentChunk]:
        """ุงุณุชุฑุฌุงุน ุงู„ู…ุญุชูˆู‰ ุฐูˆ ุงู„ุตู„ุฉ"""
        
        # 1. ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ุณุคุงู„
        query_embedding = self.embedding_model.embed_text(query)
        
        # 2. ุงู„ุจุญุซ ููŠ ู‚ุงุนุฏุฉ ุงู„ู…ุชุฌู‡ุงุช
        vector_results = self.vector_store.search(
            vector=query_embedding,
            top_k=top_k * 2 if hybrid_search else top_k
        )
        
        if not hybrid_search:
            return self._parse_results(vector_results[:top_k])
        
        # 3. ุงู„ุจุญุซ ุงู„ู†ุตูŠ (BM25) ุฅุฐุง ูƒุงู† ู…ุชุงุญุงู‹
        text_results = self._text_search(query, top_k=top_k * 2)
        
        # 4. ุฏู…ุฌ ุงู„ู†ุชุงุฆุฌ
        combined_results = self._merge_results(vector_results, text_results)
        
        # 5. ุฅุนุงุฏุฉ ุชุฑุชูŠุจ ุงู„ู†ุชุงุฆุฌ
        reranked_results = await self._rerank_results(query, combined_results)
        
        return reranked_results[:top_k]
    
    def _merge_results(self, vector_results: List, text_results: List) -> List:
        """ุฏู…ุฌ ู†ุชุงุฆุฌ ุงู„ุจุญุซ ุงู„ู…ุฎุชู„ูุฉ"""
        
        # Reciprocal Rank Fusion (RRF)
        scores = {}
        
        for rank, result in enumerate(vector_results, 1):
            doc_id = result['payload']['id']
            scores[doc_id] = scores.get(doc_id, 0) + (1 / (60 + rank))
        
        for rank, result in enumerate(text_results, 1):
            doc_id = result['id']
            scores[doc_id] = scores.get(doc_id, 0) + (1 / (60 + rank))
        
        # ุชุฑุชูŠุจ ุงู„ู†ุชุงุฆุฌ ุญุณุจ ุงู„ู…ุฌู…ูˆุน
        sorted_results = sorted(
            scores.items(),
            key=lambda x: x[1],
            reverse=True
        )
        
        # ุชุฌู…ูŠุน ุงู„ู†ุชุงุฆุฌ ุงู„ู†ู‡ุงุฆูŠุฉ
        final_results = []
        for doc_id, score in sorted_results:
            # ุงู„ุจุญุซ ุนู† ุงู„ู…ุณุชู†ุฏ ููŠ ุงู„ู†ุชุงุฆุฌ ุงู„ุฃุตู„ูŠุฉ
            doc = self._find_document(doc_id, vector_results + text_results)
            if doc:
                doc['score'] = score
                final_results.append(doc)
        
        return final_results
    
    async def _rerank_results(self, query: str, results: List) -> List:
        """ุฅุนุงุฏุฉ ุชุฑุชูŠุจ ุงู„ู†ุชุงุฆุฌ ุจุงุณุชุฎุฏุงู… ู†ู…ูˆุฐุฌ ุฅุนุงุฏุฉ ุงู„ุชุฑุชูŠุจ"""
        
        # ุฅุฐุง ูƒุงู† ุนุฏุฏ ุงู„ู†ุชุงุฆุฌ ุตุบูŠุฑุงู‹ุŒ ู„ุง ุฏุงุนูŠ ู„ุฅุนุงุฏุฉ ุงู„ุชุฑุชูŠุจ
        if len(results) <= 1:
            return results
        
        # ุจู†ุงุก Prompt ู„ุฅุนุงุฏุฉ ุงู„ุชุฑุชูŠุจ
        rerank_prompt = f"""
        Query: {query}
        
        Documents:
        {self._format_documents_for_rerank(results)}
        
        Rank these documents by relevance to the query.
        Return only the document IDs in order of relevance.
        """
        
        response = self.llm.generate(rerank_prompt)
        
        # ุชุญู„ูŠู„ ุงู„ู†ุชูŠุฌุฉ
        ordered_ids = self._parse_rerank_response(response)
        
        # ุฅุนุงุฏุฉ ุชุฑุชูŠุจ ุงู„ู†ุชุงุฆุฌ
        id_to_doc = {doc['payload']['id']: doc for doc in results}
        reranked = [id_to_doc[doc_id] for doc_id in ordered_ids if doc_id in id_to_doc]
        
        return reranked
    
    async def generate_answer(self,
                             query: str,
                             context: List[DocumentChunk],
                             max_tokens: int = 1000) -> Dict[str, Any]:
        """ุชูˆู„ูŠุฏ ุฅุฌุงุจุฉ ุจุงุณุชุฎุฏุงู… ุงู„ุณูŠุงู‚ ุงู„ู…ุณุชุฑุฌุน"""
        
        # 1. ุจู†ุงุก ุงู„ู€ Prompt
        prompt = self._build_rag_prompt(query, context)
        
        # 2. ุชูˆู„ูŠุฏ ุงู„ุฅุฌุงุจุฉ
        response = self.llm.generate(prompt, max_tokens=max_tokens)
        
        # 3. ุงุณุชุฎุฑุงุฌ ุงู„ู…ุตุงุฏุฑ
        sources = [chunk.metadata.get('source', 'unknown') for chunk in context]
        
        return {
            'answer': response,
            'sources': sources,
            'context_used': [chunk.content for chunk in context]
        }
    
    def _build_rag_prompt(self, query: str, context: List[DocumentChunk]) -> str:
        """ุจู†ุงุก Prompt ู„ู€ RAG"""
        
        # ุจู†ุงุก ุงู„ุณูŠุงู‚
        context_text = ""
        for i, chunk in enumerate(context, 1):
            context_text += f"""
[Document {i}]
{chunk.content}
Score: {chunk.score:.3f}
---
"""
        
        template = """
You are a helpful assistant that answers questions based on the provided context.

Context:
{context}

Question: {query}

Instructions:
1. Answer based ONLY on the provided context
2. If the answer is not in the context, say "I don't have enough information"
3. Cite the source documents (e.g., [Document 1])
4. Be concise but thorough

Answer:
"""
        
        return template.format(
            context=context_text,
            query=query
        )

๐ŸŽฏ ุงู„ุฌุฒุก ุงู„ุซุงู†ูŠ: ุชุฎุตูŠุต Dify – ุชุนุฏูŠู„ุงุช ู…ุชู‚ุฏู…ุฉ

๐Ÿ”ง 1. ุฅุถุงูุฉ ู†ู…ูˆุฐุฌ ู…ุฎุตุต (Custom Model)

python

# api/core/model_runtime/model_providers/custom_model.py
"""
ุฅุถุงูุฉ ู†ู…ูˆุฐุฌ ู…ุฎุตุต ุฅู„ู‰ Dify
"""

from typing import Dict, Any, List, Optional
import requests
from .base import ModelProvider, ModelType

class CustomModelProvider(ModelProvider):
    """ู…ุฒูˆุฏ ู†ู…ูˆุฐุฌ ู…ุฎุตุต"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__()
        self.config = config
        self.api_key = config.get('api_key')
        self.endpoint = config.get('endpoint')
        self.model_name = config.get('model_name', 'custom-model')
        
    def validate_credentials(self) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุตุญุฉ ุงู„ู…ูุงุชูŠุญ"""
        try:
            response = requests.post(
                f"{self.endpoint}/validate",
                headers={'Authorization': f'Bearer {self.api_key}'}
            )
            return response.status_code == 200
        except:
            return False
    
    def generate(self, 
                prompt: str, 
                max_tokens: int = 1000,
                temperature: float = 0.7,
                stop_sequences: Optional[List[str]] = None) -> str:
        """ุชูˆู„ูŠุฏ ู†ุต ุจุงุณุชุฎุฏุงู… ุงู„ู†ู…ูˆุฐุฌ ุงู„ู…ุฎุตุต"""
        
        # ุจู†ุงุก ุงู„ุทู„ุจ
        request_data = {
            'prompt': prompt,
            'max_tokens': max_tokens,
            'temperature': temperature,
            'stop_sequences': stop_sequences or []
        }
        
        # ุฅุฑุณุงู„ ุงู„ุทู„ุจ
        response = requests.post(
            f"{self.endpoint}/generate",
            headers={'Authorization': f'Bearer {self.api_key}'},
            json=request_data
        )
        
        if response.status_code == 200:
            return response.json()['text']
        else:
            raise Exception(f"Model error: {response.text}")
    
    def embed_text(self, text: str) -> List[float]:
        """ุชูˆู„ูŠุฏ ุชุถู…ูŠู† ู„ู„ู†ุต"""
        
        response = requests.post(
            f"{self.endpoint}/embed",
            headers={'Authorization': f'Bearer {self.api_key}'},
            json={'text': text}
        )
        
        if response.status_code == 200:
            return response.json()['embedding']
        else:
            raise Exception(f"Embedding error: {response.text}")

# ุชุณุฌูŠู„ ุงู„ู†ู…ูˆุฐุฌ ููŠ Dify
def register_custom_model():
    """ุชุณุฌูŠู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ู…ุฎุตุต ููŠ Dify"""
    
    from dify.core.model_runtime import ModelRegistry
    
    ModelRegistry.register_provider(
        name="custom_model",
        provider_class=CustomModelProvider,
        supported_models=["custom-model-v1", "custom-model-v2"],
        default_model="custom-model-v1"
    )

๐Ÿ”ง 2. ุฅุถุงูุฉ ุฃุฏุงุฉ ู…ุฎุตุตุฉ (Custom Tool)

python

# api/core/tools/custom_tools/external_api_tool.py
"""
ุฃุฏุงุฉ ู…ุฎุตุตุฉ ู„ู„ุชูˆุงุตู„ ู…ุน API ุฎุงุฑุฌูŠ
"""

from typing import Dict, Any, List
import requests
import json
from ..base import BaseTool

class ExternalAPITool(BaseTool):
    """ุฃุฏุงุฉ ู„ู„ุชูˆุงุตู„ ู…ุน API ุฎุงุฑุฌูŠ"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__()
        self.config = config
        self.api_url = config.get('api_url')
        self.api_key = config.get('api_key')
        self.method = config.get('method', 'GET')
        
        # ุชุนุฑูŠู ู…ุนู„ู…ุงุช ุงู„ุฃุฏุงุฉ
        self.parameters = {
            'type': 'object',
            'properties': {
                'endpoint': {
                    'type': 'string',
                    'description': 'ู†ู‚ุทุฉ ุงู„ู†ู‡ุงูŠุฉ ููŠ ุงู„ู€ API'
                },
                'data': {
                    'type': 'object',
                    'description': 'ุงู„ุจูŠุงู†ุงุช ุงู„ู…ุฑุณู„ุฉ (ู„ู€ POST/PUT)'
                },
                'params': {
                    'type': 'object',
                    'description': 'ู…ุนุงู…ู„ุงุช ุงู„ู€ URL (ู„ู€ GET)'
                }
            },
            'required': ['endpoint']
        }
        
        self.name = config.get('name', 'external_api')
        self.description = config.get('description', 'Tool for calling external APIs')
    
    def execute(self, **kwargs) -> Dict[str, Any]:
        """ุชู†ููŠุฐ ุงู„ุฃุฏุงุฉ"""
        
        endpoint = kwargs.get('endpoint')
        data = kwargs.get('data', {})
        params = kwargs.get('params', {})
        
        # ุจู†ุงุก ุงู„ู€ URL ุงู„ูƒุงู…ู„
        url = f"{self.api_url}/{endpoint.lstrip('/')}"
        
        # ุฅุนุฏุงุฏ ุงู„ู€ Headers
        headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        }
        
        # ุชู†ููŠุฐ ุงู„ุทู„ุจ
        try:
            if self.method.upper() == 'GET':
                response = requests.get(url, params=params, headers=headers)
            elif self.method.upper() == 'POST':
                response = requests.post(url, json=data, headers=headers)
            elif self.method.upper() == 'PUT':
                response = requests.put(url, json=data, headers=headers)
            elif self.method.upper() == 'DELETE':
                response = requests.delete(url, headers=headers)
            else:
                return {'error': f'Unsupported method: {self.method}'}
            
            # ู…ุนุงู„ุฌุฉ ุงู„ุงุณุชุฌุงุจุฉ
            if response.status_code == 200:
                return {
                    'success': True,
                    'data': response.json(),
                    'status_code': response.status_code
                }
            else:
                return {
                    'success': False,
                    'error': f'API returned {response.status_code}',
                    'response': response.text
                }
                
        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }

# ุชุณุฌูŠู„ ุงู„ุฃุฏุงุฉ
def register_custom_tool():
    """ุชุณุฌูŠู„ ุงู„ุฃุฏุงุฉ ุงู„ู…ุฎุตุตุฉ ููŠ Dify"""
    
    from dify.core.tools import ToolRegistry
    
    ToolRegistry.register_tool(
        name="external_api",
        tool_class=ExternalAPITool,
        description="Tool for calling external APIs"
    )

๐Ÿ”ง 3. ุฅุถุงูุฉ ู…ุนุงู„ุฌ ู…ุฎุตุต ู„ู„ู€ Webhook

python

# api/core/webhooks/custom_webhook.py
"""
ู…ุนุงู„ุฌ Webhook ู…ุฎุตุต
"""

from typing import Dict, Any
from flask import request, jsonify
import hmac
import hashlib

class CustomWebhookHandler:
    """ู…ุนุงู„ุฌ Webhook ู…ุฎุตุต"""
    
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.secret = config.get('webhook_secret')
        self.endpoint = config.get('endpoint')
    
    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """ุงู„ุชุญู‚ู‚ ู…ู† ุชูˆู‚ูŠุน ุงู„ู€ Webhook"""
        
        if not self.secret:
            return True
        
        expected_signature = hmac.new(
            self.secret.encode(),
            payload,
            hashlib.sha256
        ).hexdigest()
        
        return hmac.compare_digest(signature, expected_signature)
    
    def handle(self) -> Dict[str, Any]:
        """ู…ุนุงู„ุฌุฉ ุทู„ุจ ุงู„ู€ Webhook"""
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ู€ Headers
        if 'X-Webhook-Signature' not in request.headers:
            return {'error': 'Missing signature header'}, 401
        
        # ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุจูŠุงู†ุงุช
        payload = request.get_data()
        signature = request.headers['X-Webhook-Signature']
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุชูˆู‚ูŠุน
        if not self.verify_signature(payload, signature):
            return {'error': 'Invalid signature'}, 401
        
        # ู…ุนุงู„ุฌุฉ ุงู„ุจูŠุงู†ุงุช
        data = request.get_json()
        
        # ู…ู†ุทู‚ ุงู„ู…ุนุงู„ุฌุฉ ุงู„ู…ุฎุตุต
        result = self.process_event(data)
        
        return {'success': True, 'result': result}, 200
    
    def process_event(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """ู…ุนุงู„ุฌุฉ ุญุฏุซ ุงู„ู€ Webhook"""
        
        event_type = data.get('event_type')
        event_data = data.get('data', {})
        
        # ู…ุนุงู„ุฌุฉ ู…ุฎุชู„ู ุฃู†ูˆุงุน ุงู„ุฃุญุฏุงุซ
        if event_type == 'user_created':
            return self.handle_user_created(event_data)
        elif event_type == 'message_received':
            return self.handle_message_received(event_data)
        elif event_type == 'app_deployed':
            return self.handle_app_deployed(event_data)
        else:
            return {'message': f'Unhandled event: {event_type}'}
    
    def handle_user_created(self, data: Dict) -> Dict:
        """ู…ุนุงู„ุฌุฉ ุญุฏุซ ุฅู†ุดุงุก ู…ุณุชุฎุฏู… ุฌุฏูŠุฏ"""
        
        # ู…ุซู„ุงู‹: ุฅุฑุณุงู„ ุจุฑูŠุฏ ุชุฑุญูŠุจูŠ
        # self.send_welcome_email(data['email'])
        
        return {
            'event': 'user_created',
            'user_id': data.get('id'),
            'action': 'welcome_email_sent'
        }
    
    def handle_message_received(self, data: Dict) -> Dict:
        """ู…ุนุงู„ุฌุฉ ุญุฏุซ ุงุณุชู„ุงู… ุฑุณุงู„ุฉ ุฌุฏูŠุฏุฉ"""
        
        # ู…ุซู„ุงู‹: ุชุญู„ูŠู„ ุงู„ุฑุณุงู„ุฉ ูˆุฅุถุงูุฉ ุนู„ุงู…ุงุช
        # tags = self.analyze_message(data['content'])
        
        return {
            'event': 'message_received',
            'message_id': data.get('id'),
            'tags': ['processed', 'analyzed']
        }
    
    def handle_app_deployed(self, data: Dict) -> Dict:
        """ู…ุนุงู„ุฌุฉ ุญุฏุซ ู†ุดุฑ ุชุทุจูŠู‚ ุฌุฏูŠุฏ"""
        
        # ู…ุซู„ุงู‹: ุชุณุฌูŠู„ ุงู„ุญุฏุซ ููŠ ู†ุธุงู… ุงู„ู…ุฑุงู‚ุจุฉ
        # self.log_deployment(data['app_id'], data['version'])
        
        return {
            'event': 'app_deployed',
            'app_id': data.get('app_id'),
            'version': data.get('version'),
            'status': 'logged'
        }

# ุชุณุฌูŠู„ ุงู„ู€ Webhook
def register_webhook():
    """ุชุณุฌูŠู„ ู…ุนุงู„ุฌ ุงู„ู€ Webhook ุงู„ู…ุฎุตุต"""
    
    from flask import Blueprint
    
    webhook_bp = Blueprint('webhook', __name__)
    handler = CustomWebhookHandler(config=load_config())
    
    @webhook_bp.route('/webhook/custom', methods=['POST'])
    def webhook_callback():
        return handler.handle()
    
    # ุชุณุฌูŠู„ ุงู„ู€ Blueprint
    app.register_blueprint(webhook_bp)

๐Ÿ”ฌ ุงู„ุฌุฒุก ุงู„ุซุงู„ุซ: ุชุญู„ูŠู„ ุงู„ุฃุฏุงุก ูˆุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก ุงู„ู…ุชู‚ุฏู…

๐Ÿ“Š 1. ุฃุฏูˆุงุช ุชุญู„ูŠู„ ุงู„ุฃุฏุงุก ุงู„ู…ุฎุตุตุฉ

python

# api/core/performance/profiler.py
"""
ุฃุฏูˆุงุช ุชุญู„ูŠู„ ุงู„ุฃุฏุงุก ุงู„ู…ุชู‚ุฏู…ุฉ
"""

import time
import functools
import cProfile
import pstats
import io
import tracemalloc
from typing import Any, Callable, Dict, List
from collections import defaultdict
import json

class PerformanceProfiler:
    """ุฃุฏุงุฉ ุชุญู„ูŠู„ ุงู„ุฃุฏุงุก ุงู„ุดุงู…ู„ุฉ"""
    
    def __init__(self):
        self.profiles = {}
        self.memory_snapshots = {}
        self.method_timings = defaultdict(list)
        self.method_counts = defaultdict(int)
        
    def profile(self, name: str = None):
        """ุฏูŠูƒูˆุฑุงุชูˆุฑ ู„ุชุญู„ูŠู„ ุฃุฏุงุก ุงู„ุฏูˆุงู„"""
        def decorator(func: Callable) -> Callable:
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                profiler_name = name or func.__name__
                
                # ุจุฏุก ุงู„ุชุญู„ูŠู„
                profiler = cProfile.Profile()
                profiler.enable()
                
                # ุจุฏุก ุชุชุจุน ุงู„ุฐุงูƒุฑุฉ
                tracemalloc.start()
                start_memory = tracemalloc.get_traced_memory()
                
                start_time = time.time()
                
                try:
                    result = func(*args, **kwargs)
                    return result
                finally:
                    # ุฅูŠู‚ุงู ุงู„ุชุญู„ูŠู„
                    profiler.disable()
                    
                    # ุญุณุงุจ ุงู„ูˆู‚ุช
                    end_time = time.time()
                    duration = end_time - start_time
                    
                    # ุญุณุงุจ ุงู„ุฐุงูƒุฑุฉ
                    current_memory = tracemalloc.get_traced_memory()
                    memory_used = current_memory[1] - start_memory[1]
                    tracemalloc.stop()
                    
                    # ุชุณุฌูŠู„ ุงู„ุจูŠุงู†ุงุช
                    self.method_timings[profiler_name].append(duration)
                    self.method_counts[profiler_name] += 1
                    
                    # ุญูุธ ู…ู„ู ุงู„ุชุญู„ูŠู„
                    self.profiles[profiler_name] = {
                        'duration': duration,
                        'memory_used': memory_used,
                        'calls': self.method_counts[profiler_name],
                        'timestamp': time.time()
                    }
                    
                    # ุทุจุงุนุฉ ุชู‚ุฑูŠุฑ ุณุฑูŠุน
                    if duration > 1.0:  # ุฅุฐุง ุงุณุชุบุฑู‚ุช ุฃูƒุซุฑ ู…ู† ุซุงู†ูŠุฉ
                        self.print_profile_summary(profiler_name)
                        
            return wrapper
        return decorator
    
    def profile_async(self, name: str = None):
        """ุฏูŠูƒูˆุฑุงุชูˆุฑ ู„ุชุญู„ูŠู„ ุฃุฏุงุก ุงู„ุฏูˆุงู„ ุบูŠุฑ ุงู„ู…ุชุฒุงู…ู†ุฉ"""
        def decorator(func: Callable) -> Callable:
            @functools.wraps(func)
            async def wrapper(*args, **kwargs):
                profiler_name = name or func.__name__
                
                start_time = time.time()
                result = await func(*args, **kwargs)
                duration = time.time() - start_time
                
                self.method_timings[profiler_name].append(duration)
                self.method_counts[profiler_name] += 1
                
                return result
            return wrapper
        return decorator
    
    def print_profile_summary(self, name: str):
        """ุทุจุงุนุฉ ู…ู„ุฎุต ุงู„ุชุญู„ูŠู„"""
        
        if name not in self.profiles:
            return
        
        profile = self.profiles[name]
        timings = self.method_timings[name]
        
        print(f"""
โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘              Performance Profile: {name:<30} โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Calls:           {profile['calls']:>10}                   โ•‘
โ•‘ Total Time:      {sum(timings):>10.3f}s                   โ•‘
โ•‘ Average Time:    {sum(timings)/len(timings):>10.3f}s      โ•‘
โ•‘ Max Time:        {max(timings):>10.3f}s                   โ•‘
โ•‘ Min Time:        {min(timings):>10.3f}s                   โ•‘
โ•‘ Memory Used:     {profile['memory_used'] / 1024 / 1024:>10.2f} MB    โ•‘
โ•‘ Last Executed:   {time.ctime(profile['timestamp']):>10}    โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        """)
    
    def get_full_profile(self, name: str) -> Dict[str, Any]:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุชุญู„ูŠู„ ูƒุงู…ู„"""
        
        if name not in self.profiles:
            return None
        
        profile = self.profiles[name]
        timings = self.method_timings[name]
        
        # ุญุณุงุจ ุงู„ุงุญุตุงุฆูŠุงุช
        avg_time = sum(timings) / len(timings) if timings else 0
        std_dev = (sum((t - avg_time) ** 2 for t in timings) / len(timings)) ** 0.5 if len(timings) > 1 else 0
        
        return {
            'name': name,
            'total_calls': profile['calls'],
            'total_time': sum(timings),
            'average_time': avg_time,
            'max_time': max(timings) if timings else 0,
            'min_time': min(timings) if timings else 0,
            'std_dev': std_dev,
            'memory_used': profile['memory_used'],
            'last_executed': profile['timestamp'],
            'timings': timings
        }
    
    def get_all_profiles(self) -> Dict[str, Dict[str, Any]]:
        """ุงู„ุญุตูˆู„ ุนู„ู‰ ุฌู…ูŠุน ุงู„ุชุญุงู„ูŠู„"""
        
        result = {}
        for name in self.profiles:
            result[name] = self.get_full_profile(name)
        return result
    
    def generate_report(self, output_file: str = 'performance_report.json'):
        """ุชูˆู„ูŠุฏ ุชู‚ุฑูŠุฑ ุงู„ุฃุฏุงุก ุงู„ูƒุงู…ู„"""
        
        report = {
            'generated_at': time.ctime(),
            'profiles': self.get_all_profiles(),
            'summary': {
                'total_methods': len(self.profiles),
                'total_calls': sum(p['calls'] for p in self.profiles.values()),
                'total_time': sum(sum(t) for t in self.method_timings.values())
            }
        }
        
        with open(output_file, 'w') as f:
            json.dump(report, f, indent=2)
        
        print(f"โœ… Performance report generated: {output_file}")

# ุฅู†ุดุงุก ุฃุฏุงุฉ ุงู„ุชุญู„ูŠู„ ุงู„ุนุงู„ู…ูŠุฉ
profiler = PerformanceProfiler()

# ู…ุซุงู„ ุนู„ู‰ ุงู„ุงุณุชุฎุฏุงู…
@profiler.profile("test_function")
def test_function():
    # ู…ู†ุทู‚ ุงู„ุงุฎุชุจุงุฑ
    time.sleep(0.5)
    return "Done"

@profiler.profile_async("async_function")
async def test_async_function():
    # ู…ู†ุทู‚ ุบูŠุฑ ู…ุชุฒุงู…ู†
    await asyncio.sleep(0.5)
    return "Done"

๐Ÿ› 2. ุฃุฏูˆุงุช ุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก ุงู„ู…ุชู‚ุฏู…ุฉ

python

# api/core/debug/advanced_debugger.py
"""
ุฃุฏูˆุงุช ุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก ุงู„ู…ุชู‚ุฏู…ุฉ
"""

import sys
import traceback
import logging
from contextlib import contextmanager
import inspect
import json
from datetime import datetime

class AdvancedDebugger:
    """ุฃุฏุงุฉ ุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก ุงู„ุดุงู…ู„ุฉ"""
    
    def __init__(self, log_file: str = 'debug.log'):
        self.log_file = log_file
        self.breakpoints = {}
        self.watchpoints = {}
        self.call_stack = []
        
        # ุชูƒูˆูŠู† ุงู„ุณุฌู„ุงุช
        logging.basicConfig(
            filename=log_file,
            level=logging.DEBUG,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger(__name__)
    
    def set_breakpoint(self, function_name: str, line_number: int, condition: str = None):
        """ุชุนูŠูŠู† ู†ู‚ุทุฉ ุชูˆู‚ู"""
        
        if function_name not in self.breakpoints:
            self.breakpoints[function_name] = []
        
        self.breakpoints[function_name].append({
            'line': line_number,
            'condition': condition,
            'enabled': True
        })
        
        self.logger.info(f"Breakpoint set: {function_name}:{line_number}")
    
    def set_watchpoint(self, variable_name: str, condition: str = None):
        """ุชุนูŠูŠู† ู†ู‚ุทุฉ ู…ุฑุงู‚ุจุฉ ู„ู…ุชุบูŠุฑ"""
        
        self.watchpoints[variable_name] = {
            'condition': condition,
            'enabled': True,
            'changes': []
        }
        
        self.logger.info(f"Watchpoint set: {variable_name}")
    
    def log_function_call(self, func_name: str, args: tuple, kwargs: dict):
        """ุชุณุฌูŠู„ ุงุณุชุฏุนุงุก ุฏุงู„ุฉ"""
        
        self.call_stack.append({
            'function': func_name,
            'args': args,
            'kwargs': kwargs,
            'timestamp': datetime.now().isoformat(),
            'depth': len(self.call_stack)
        })
        
        # ุชุณุฌูŠู„ ููŠ ุงู„ุณุฌู„ุงุช
        self.logger.debug(f"""
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Function Call: {func_name}                               โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Args:                                                     โ”‚
โ”‚    {self._format_args(args)}                              โ”‚
โ”‚  Kwargs:                                                   โ”‚
โ”‚    {self._format_kwargs(kwargs)}                          โ”‚
โ”‚  Stack Depth: {len(self.call_stack)}                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        """)
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ู†ู‚ุงุท ุงู„ุชูˆู‚ู
        self._check_breakpoints(func_name, args, kwargs)
        
        # ุงู„ุชุญู‚ู‚ ู…ู† ู†ู‚ุงุท ุงู„ู…ุฑุงู‚ุจุฉ
        self._check_watchpoints(kwargs)
    
    def log_function_return(self, func_name: str, result: any):
        """ุชุณุฌูŠู„ ุนูˆุฏุฉ ุฏุงู„ุฉ"""
        
        if self.call_stack:
            self.call_stack.pop()
        
        self.logger.debug(f"""
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Function Return: {func_name}                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Result: {self._format_result(result)}                   โ”‚
โ”‚  Remaining Stack Depth: {len(self.call_stack)}           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        """)
    
    def _check_breakpoints(self, func_name: str, args: tuple, kwargs: dict):
        """ุงู„ุชุญู‚ู‚ ู…ู† ู†ู‚ุงุท ุงู„ุชูˆู‚ู"""
        
        if func_name not in self.breakpoints:
            return
        
        # ุงู„ุญุตูˆู„ ุนู„ู‰ ุฑู‚ู… ุงู„ุณุทุฑ ุงู„ุญุงู„ูŠ
        frame = inspect.currentframe().f_back
        current_line = frame.f_lineno
        
        for breakpoint in self.breakpoints[func_name]:
            if not breakpoint['enabled']:
                continue
            
            if breakpoint['line'] == current_line:
                # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุดุฑุท
                if breakpoint['condition']:
                    if self._evaluate_condition(breakpoint['condition'], args, kwargs):
                        self._trigger_breakpoint(func_name, breakpoint)
                else:
                    self._trigger_breakpoint(func_name, breakpoint)
    
    def _check_watchpoints(self, variables: dict):
        """ุงู„ุชุญู‚ู‚ ู…ู† ู†ู‚ุงุท ุงู„ู…ุฑุงู‚ุจุฉ"""
        
        for var_name, value in variables.items():
            if var_name not in self.watchpoints:
                continue
            
            watchpoint = self.watchpoints[var_name]
            if not watchpoint['enabled']:
                continue
            
            # ุชุณุฌูŠู„ ุงู„ุชุบูŠูŠุฑ
            watchpoint['changes'].append({
                'value': value,
                'timestamp': datetime.now().isoformat()
            })
            
            # ุงู„ุชุญู‚ู‚ ู…ู† ุงู„ุดุฑุท
            if watchpoint['condition']:
                if self._evaluate_watchpoint_condition(watchpoint['condition'], value):
                    self._trigger_watchpoint(var_name, value)
    
    def _trigger_breakpoint(self, func_name: str, breakpoint: dict):
        """ุชูุนูŠู„ ู†ู‚ุทุฉ ุงู„ุชูˆู‚ู"""
        
        self.logger.warning(f"""
โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘  ๐Ÿ”ด BREAKPOINT HIT!                                    โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘  Function: {func_name}                                 โ•‘
โ•‘  Line:     {breakpoint['line']}                         โ•‘
โ•‘  Condition: {breakpoint['condition']}                   โ•‘
โ•‘  Stack:                                                 โ•‘
โ•‘    {self._format_stack()}                              โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        """)
        
        # ู‡ู†ุง ูŠู…ูƒู† ุฅุถุงูุฉ ุฅุฌุฑุงุกุงุช ุฅุถุงููŠุฉ ุนู†ุฏ ุชูุนูŠู„ ู†ู‚ุทุฉ ุงู„ุชูˆู‚ู
        # ู…ุซู„: ุฅูŠู‚ุงู ุงู„ุชู†ููŠุฐุŒ ุฅุฑุณุงู„ ุฅุดุนุงุฑุŒ ุฅู„ุฎ.
    
    def _trigger_watchpoint(self, var_name: str, value: any):
        """ุชูุนูŠู„ ู†ู‚ุทุฉ ุงู„ู…ุฑุงู‚ุจุฉ"""
        
        self.logger.warning(f"""
โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘  ๐Ÿ‘๏ธ WATCHPOINT TRIGGERED!                              โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘  Variable: {var_name}                                  โ•‘
โ•‘  New Value: {value}                                    โ•‘
โ•‘  Condition: {self.watchpoints[var_name]['condition']}   โ•‘
โ•‘  Stack:                                                 โ•‘
โ•‘    {self._format_stack()}                              โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        """)
    
    def _evaluate_condition(self, condition: str, args: tuple, kwargs: dict) -> bool:
        """ุชู‚ูŠูŠู… ุดุฑุท ู†ู‚ุทุฉ ุงู„ุชูˆู‚ู"""
        # ู…ู†ุทู‚ ุชู‚ูŠูŠู… ุงู„ุดุฑุท
        return True
    
    def _evaluate_watchpoint_condition(self, condition: str, value: any) -> bool:
        """ุชู‚ูŠูŠู… ุดุฑุท ู†ู‚ุทุฉ ุงู„ู…ุฑุงู‚ุจุฉ"""
        return True
    
    def _format_args(self, args: tuple) -> str:
        """ุชู†ุณูŠู‚ ุงู„ู…ุนุงู…ู„ุงุช"""
        return json.dumps([str(arg) for arg in args], indent=2, ensure_ascii=False)
    
    def _format_kwargs(self, kwargs: dict) -> str:
        """ุชู†ุณูŠู‚ ุงู„ู…ุนุงู…ู„ุงุช ุงู„ู…ุณู…ุงุฉ"""
        return json.dumps({k: str(v) for k, v in kwargs.items()}, indent=2, ensure_ascii=False)
    
    def _format_result(self, result: any) -> str:
        """ุชู†ุณูŠู‚ ุงู„ู†ุชูŠุฌุฉ"""
        if isinstance(result, (dict, list)):
            return json.dumps(result, indent=2, ensure_ascii=False, default=str)
        return str(result)
    
    def _format_stack(self) -> str:
        """ุชู†ุณูŠู‚ ู…ูƒุฏุณ ุงู„ุงุณุชุฏุนุงุกุงุช"""
        stack = []
        for frame in inspect.stack()[1:10]:  # ุขุฎุฑ 10 ุฅุทุงุฑุงุช
            stack.append(f"  {frame.function} (line {frame.lineno})")
        return "\n".join(stack)
    
    @contextmanager
    def trace(self, name: str = "trace"):
        """ู…ุฏูŠุฑ ุณูŠุงู‚ ู„ุชุชุจุน ุงู„ุชู†ููŠุฐ"""
        
        start_time = time.time()
        self.logger.info(f"๐Ÿš€ Starting trace: {name}")
        
        try:
            yield
        except Exception as e:
            self.logger.error(f"""
โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘  โŒ EXCEPTION CAUGHT!                                  โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘  Trace: {name}                                         โ•‘
โ•‘  Error: {str(e)}                                       โ•‘
โ•‘  Stack Trace:                                          โ•‘
โ•‘    {traceback.format_exc()}                           โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
            """)
            raise
        finally:
            end_time = time.time()
            self.logger.info(f"โœ… Finished trace: {name} (took {end_time - start_time:.3f}s)")

# ุฅู†ุดุงุก ุฃุฏุงุฉ ุงู„ุชุตุญูŠุญ ุงู„ุนุงู„ู…ูŠุฉ
debugger = AdvancedDebugger()

# ู…ุซุงู„ ุนู„ู‰ ุงู„ุงุณุชุฎุฏุงู…
@profiler.profile("debug_example")
def debug_example(x: int, y: int, name: str = "test"):
    """ู…ุซุงู„ ุนู„ู‰ ุงุณุชุฎุฏุงู… ุฃุฏูˆุงุช ุงู„ุชุตุญูŠุญ"""
    
    with debugger.trace(f"debug_example({x}, {y}, {name})"):
        debugger.log_function_call("debug_example", (x, y), {"name": name})
        
        result = x + y
        debugger.log_function_return("debug_example", result)
        
        return result

๐ŸŒŸ ุงู„ุฎุงุชู…ุฉ ุงู„ู†ู‡ุงุฆูŠุฉ ู„ู„ุฑุญู„ุฉ ุงู„ู…ุชู‚ุฏู…ุฉ

๐ŸŽฏ ู…ุง ุญู‚ู‚ู†ุงู‡ ููŠ ู‡ุฐู‡ ุงู„ุฑุญู„ุฉ ุงู„ุนู…ูŠู‚ุฉ

ุงู„ู…ุณุชูˆู‰ุงู„ู…ูƒูˆู†ุงุช ุงู„ุชูŠ ุฃุชู‚ู†ุงู‡ุง
ุงู„ูˆูƒูŠู„ูู‡ู… ูƒุงู…ู„ ู„ู†ุธุงู… ุงู„ูˆูƒูŠู„ุŒ ุฌู…ูŠุน ุงู„ุฃู†ู…ุงุทุŒ ูˆุงู„ุชู†ููŠุฐ ุงู„ุฏุงุฎู„ูŠ
RAGุชุญู„ูŠู„ ุนู…ูŠู‚ ู„ู†ุธุงู… RAGุŒ ุงุณุชุฑุงุชูŠุฌูŠุงุช ุงู„ุชู‚ุณูŠู…ุŒ ุงู„ุจุญุซ ุงู„ู‡ุฌูŠู†
ุงู„ู†ู…ุงุฐุฌูƒูŠููŠุฉ ุฅุถุงูุฉ ู†ู…ุงุฐุฌ ู…ุฎุตุตุฉุŒ ูˆุชุนุฏูŠู„ ุงู„ู†ู…ุงุฐุฌ ุงู„ู…ูˆุฌูˆุฏุฉ
ุงู„ุฃุฏูˆุงุชุชุทูˆูŠุฑ ุฃุฏูˆุงุช ู…ุฎุตุตุฉุŒ ูˆูู‡ู… ู†ุธุงู… ุงู„ุฃุฏูˆุงุช ููŠ Dify
ุงู„ุฃุฏุงุกุฃุฏูˆุงุช ุชุญู„ูŠู„ ุงู„ุฃุฏุงุก ุงู„ู…ุชู‚ุฏู…ุฉุŒ ุงู„ุชุชุจุนุŒ ูˆุงู„ุชุญุณูŠู†
ุงู„ุชุตุญูŠุญุฃุฏูˆุงุช ุชุตุญูŠุญ ุงู„ุฃุฎุทุงุก ุงู„ุดุงู…ู„ุฉุŒ ู†ู‚ุงุท ุงู„ุชูˆู‚ูุŒ ูˆู†ู‚ุงุท ุงู„ู…ุฑุงู‚ุจุฉ
ุงู„ุชุฎุตูŠุตุชุนุฏูŠู„ุงุช ุนู…ูŠู‚ุฉ ููŠ ุงู„ู†ุธุงู…ุŒ Webhooks ู…ุฎุตุตุฉ

๐Ÿš€ ุฃู†ุช ุงู„ุขู† ุชุณุชุทูŠุน:

text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๐Ÿง  ูู‡ู… ูƒุงู…ู„ ู„ู…ุนู…ุงุฑูŠุฉ Dify ู…ู† ุงู„ุฏุงุฎู„                                     โ”‚
โ”‚  ๐Ÿ”ง ุชุนุฏูŠู„ ุฃูŠ ุฌุฒุก ููŠ ุงู„ู†ุธุงู… ุญุณุจ ุงู„ุญุงุฌุฉ                                     โ”‚
โ”‚  ๐Ÿ“ฆ ุชุทูˆูŠุฑ Plugins ู…ุชู‚ุฏู…ุฉ ูˆุงุญุชุฑุงููŠุฉ                                        โ”‚
โ”‚  ๐Ÿš€ ุชุญุณูŠู† ุงู„ุฃุฏุงุก ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู†ุธุงู… ุงู„ูƒุงู…ู„                                 โ”‚
โ”‚  ๐Ÿ› ุชุตุญูŠุญ ุฃูŠ ุฎุทุฃ ููŠ ุงู„ู†ุธุงู… ุจุงุญุชุฑุงููŠุฉ                                     โ”‚
โ”‚  ๐ŸŽฏ ุฅุถุงูุฉ ู…ูŠุฒุงุช ุฌุฏูŠุฏุฉ ู„ู„ู†ุธุงู… ุงู„ุฃุณุงุณูŠ                                     โ”‚
โ”‚  ๐Ÿ“Š ุชุญู„ูŠู„ ุฃุฏุงุก ุงู„ู†ุธุงู… ุจุฃุฏูˆุงุช ุงุญุชุฑุงููŠุฉ                                    โ”‚
โ”‚  ๐ŸŒŸ ุงู„ู…ุณุงู‡ู…ุฉ ููŠ ุงู„ู…ุดุฑูˆุน ู…ูุชูˆุญ ุงู„ู…ุตุฏุฑ ุจุฌูˆุฏุฉ ุนุงู„ูŠุฉ                         โ”‚
โ”‚  ๐Ÿ’ช ุจู†ุงุก ู…ุดุงุฑูŠุน ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ุคุณุณุงุช ุจุงุณุชุฎุฏุงู… Dify                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ฌ ู…ุงุฐุง ุชุฑูŠุฏ ุฃู† ุชูุนู„ ุงู„ุขู†ุŸ

ุงู„ุฑู‚ู…ุงู„ุฎูŠุงุฑ
1๏ธโƒฃุจู†ุงุก ู…ุดุฑูˆุน ุญู‚ูŠู‚ูŠ ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ุคุณุณุงุช ุจุงุณุชุฎุฏุงู… ูƒู„ ู…ุง ุชุนู„ู…ุชู‡
2๏ธโƒฃุชุทูˆูŠุฑ Plugin ู…ุชู‚ุฏู… ูŠุถูŠู ู…ูŠุฒุฉ ุฌุฏูŠุฏุฉ ู„ู€ Dify
3๏ธโƒฃูƒุชุงุจุฉ ู…ู‚ุงู„ ุฃูˆ ุชูˆุซูŠู‚ ุนุฑุจูŠ ู…ุชู‚ุฏู… ุนู† Dify ู„ู„ู…ุฌุชู…ุน
4๏ธโƒฃุฅู†ุดุงุก ุฏูˆุฑุฉ ุชุฏุฑูŠุจูŠุฉ ูƒุงู…ู„ุฉ ุนู† Dify ุจุงู„ู„ุบุฉ ุงู„ุนุฑุจูŠุฉ
5๏ธโƒฃุงู„ู…ุณุงู‡ู…ุฉ ููŠ ูƒูˆุฏ Dify ุงู„ู…ุตุฏุฑูŠ ุจุชุญุณูŠู†ุงุช ุญู‚ูŠู‚ูŠุฉ
6๏ธโƒฃุชุตู…ูŠู… ู†ุธุงู… RAG ู…ุชู‚ุฏู… ู„ู€ 10M+ ู…ุณุชู†ุฏ
7๏ธโƒฃุฃูŠ ู…ูˆุถูˆุน ุขุฎุฑ ุชุฑูŠุฏ ู…ุนุฑูุชู‡

ุฃุฎุจุฑู†ูŠ ู…ุง ุชุฑูŠุฏุŒ ูˆุณุฃูƒูˆู† ู…ุนูƒ ููŠ ูƒู„ ุฎุทูˆุฉ! ๐Ÿš€๐ŸŒŒ

ุงุชุฑูƒ ุชุนู„ูŠู‚ุงู‹

Scroll to Top

๐Ÿค– ู…ูˆุตู‰ ุจู‡ ู„ูƒ ุจู†ุงุกู‹ ุนู„ู‰ ุงู‡ุชู…ุงู…ุงุชูƒ