๐ ู ูุฎุต ุณุฑูุน ูู ุง ุฃูุฌุฒูุงู
ููุฏ ูู
ูุง ุจุชุซุจูุช Dify ุจูุฌุงุญ ุนูู ูุธุงู
ู ุจุงุณุชุฎุฏุงู
Dockerุ ูุงูุขู ูุฏูู ู
ูุตุฉ ู
ุชูุงู
ูุฉ ููุฐูุงุก ุงูุงุตุทูุงุนู ุชุนู
ู ุนูู http://localhost/install.
๐ฏ ุงูุฎุทูุฉ ุงูุชุงููุฉ: ุงุฎุชุฑ ู ุณุงุฑู
๐น ุฃููุงู: ุงูุฅุนุฏุงุฏ ุงูุฃุณุงุณู (ุถุฑูุฑู)
| ุงูุฎุทูุฉ | ุงููุตู | ุงูุฑุงุจุท/ุงูู ุณุงุฑ |
|---|---|---|
| 1๏ธโฃ | ุงูุชุญ ุงูู ุชุตูุญ | http://localhost/install |
| 2๏ธโฃ | ุฃูุดุฆ ุญุณุงุจ Admin | ุจุฑูุฏ: admin@example.comููู ุฉ ุงูู ุฑูุฑ: Admin@123456 |
| 3๏ธโฃ | ุฃุถู ูู ูุฐุฌ AI | Settings โ Model Providers |
| 4๏ธโฃ | ุฃูุดุฆ ุชุทุจููู ุงูุฃูู | Apps โ Create App |
๐น ุซุงููุงู: ุงุฎุชุฑ ู ุง ุชุฑูุฏ ุชุนูู ู ุงูุขู
| # | ุงูู ูุถูุน | ุงูู ุณุชูู | ุงูุดุฑุญ |
|---|---|---|---|
| ุฃ | ุฅุถุงูุฉ ูู ุงุฐุฌ AI (OpenAI/Claude/Gemini) | ๐ข ู ุจุชุฏุฆ | ุดุฑุญ ูุงู ู ู ุน ุตูุฑ |
| ุจ | ุจูุงุก Chatbot ุงุญุชุฑุงูู | ๐ข ู ุจุชุฏุฆ | ู ู ุงูุตูุฑ ุฅูู ุงููุดุฑ |
| ุฌ | ุงุณุชุฎุฏุงู ุงูู ุนุฑูุฉ (RAG) ู ุน ุงูู ููุงุช | ๐ก ู ุชูุณุท | ุฑูุน PDF ูุงุณุชุนูุงู ุงุช ุฐููุฉ |
| ุฏ | ุฅูุดุงุก ุณูุฑ ุนู ู (Workflow) ู ุฑุฆู | ๐ก ู ุชูุณุท | ุฃุชู ุชุฉ ุงูู ูุงู ุงูู ุนูุฏุฉ |
| ูู | ุชุทููุฑ Plugin ุฎุงุต ุจู Dify | ๐ด ู ุชูุฏู | ุชู ุฏูุฏ ูุฏุฑุงุช ุงูู ูุตุฉ |
| ู | ุฅุนุฏุงุฏุงุช ุงูุฃู ุงู ูุงูู ุณุชุฎุฏู ูู | ๐ด ู ุชูุฏู | ุตูุงุญูุงุช ูุฅุฏุงุฑุฉ ูุฑูู |
๐ ุณุฃูุชุฑุถ ุฃูู ุชุฑูุฏ ุงูุจุฏุก ุจู ุงูุฎูุงุฑ (ุฃ) – ุฅุถุงูุฉ ูู ูุฐุฌ AI
๐ ุฅุถุงูุฉ ูู ูุฐุฌ OpenAI
- ุณุฌู ุงูุฏุฎููย ุฅูู ููุญุฉ ุงูุชุญูู
- ู
ู ุงููุงุฆู
ุฉ ุงูุฌุงูุจูุฉ:ย
Settings โ Model Providers - ุงุฎุชุฑย OpenAIย ู ู ุงููุงุฆู ุฉ
- ุงูุตู ู
ูุชุงุญ API:ย
sk-...ย (ุชุญุตู ุนููู ู ูย platform.openai.com) - ุงุถุบุทย “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 | ุชูุซูู ูุงู ู ูู API | docs.dify.ai |
| Prompt Engineering Guide | ุฏููู ูุชุงุจุฉ ุงูู Prompts | promptingguide.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 Developer | 2 ุฃูุงู | ู ุจุชุฏุฆ-ู ุชูุณุท | ู ุฌุงูู |
| DeepLearning.ai – ChatGPT Prompt Engineering | ุดูุฑ | ู ุชูุณุท | ู ุฌุงูู |
| Stanford – CS224N: NLP | 3 ุฃุดูุฑ | ู ุชูุฏู | ู ุฌุงูู |
| AWS Machine Learning Engineer | 6 ุฃุดูุฑ | ุฎุจูุฑ | ู ุฏููุน |
| Google Professional ML Engineer | 6 ุฃุดูุฑ | ุฎุจูุฑ | ู ุฏููุน |
๐ ุงูุฎุงุชู ุฉ ุงูููุงุฆูุฉ
ู ุง ุชุนูู ุชู ูู ูุฐู ุงูุฑุญูุฉ ุงูู ุชูุฏู ุฉ:
| # | ุงูู ูุถูุน |
|---|---|
| 1 | ุงูููุฏุณุฉ ุงูู ุนู ุงุฑูุฉ ููุชูุณุน ุงูุฃููู ๐ |
| 2 | ุงูุฃู ุงู ุงูู ุชูุฏู (SSL, WAF, RBAC) ๐ |
| 3 | ุชุญุณูู ุงูุฃุฏุงุก ุนูู ูุทุงู ูุงุณุน โก |
| 4 | ุงูู ุฑุงูุจุฉ ูุงูุชุญูููุงุช ุงูู ุชูุฏู ุฉ ๐ |
| 5 | ุฏู ุฌ ู ุน ุฃูุธู ุฉ ุงูู ุคุณุณุงุช (Azure, SAP, Salesforce) ๐ข |
| 6 | ุงูุฐูุงุก ุงูุงุตุทูุงุนู ุงูู ุชูุฏู (ุชูุตูุงุชุ ุชุญุณูู Promptsุ ูุดู ุงูุชุญุงู) ๐ง |
| 7 | DevOps ุงูู ุชูุฏู (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๏ธโฃ | ุฃู ู ูุถูุน ุขุฎุฑ ุชุฑูุฏ ู ุนุฑูุชู |
ุฃุฎุจุฑูู ู ุง ุชุฑูุฏุ ูุณุฃููู ู ุนู ูู ูู ุฎุทูุฉ! ๐๐