Azure Functions Dynamic Workflows: DAG ile Dayanıklı AI
Azure Functions, kuyruk mesajı, blob yüklemesi, veritabanı değişikliği veya HTTP isteği geldiğinde kodunuzu çalıştıran olay güdümlü bir uygulama platformu. Azure Functions Hosted Skills bu yapıya yapay zekâ muhakemesi ekliyor: olay geldiğinde bir LLM olayı okuyor, verdiğiniz yönergeleri, becerileri ve araçları uygulayıp ne yapılacağına karar veriyor. Dynamic Workflows özelliği ise bu tabloda kritik bir ayrımı getiriyor — planlamayı yürütmeden ayırıyor. LLM işi bir DAG (yönlü çevrimsiz çizge) olarak planlıyor, gömülü Durable Functions motoru bu planı planlama döngüsünün dışında çalıştırıyor. Sonuç: ara araç çıktıları tekrar LLM’e dönmediği için token kullanımı ve gecikme azalabiliyor, uzun süren işler ise çökmelerden etkilenmeden tamamlanabiliyor.
Özellik, Azure Functions Hosted Skills (public preview) kapsamında preview durumunda. API yüzeyi küçük tutulmuş ve geri bildirime göre değişebileceği belirtiliyor. Yerel çalıştırma için Python 3.13 veya üzeri, Azure Functions Core Tools v4, Azurite ve Microsoft Foundry, Azure OpenAI ya da OpenAI tarafında bir model gerekiyor.
LLM muhakeme döngüsü neden pahalıya mal oluyor?
Dynamic Workflows olmadan çok adımlı bir iş, LLM döngüsünde adım adım ilerler: model bir aracı çağırır, sonucu okur, sonraki aracı çağırır. Her adımda tüm konuşma geçmişi, araç tanımları ve o ana kadarki tüm araç sonuçları yeniden modele gönderilir. Kaynak bu yaklaşımın üç maliyetini sıralıyor:
- Token: Her araç çağrısında aynı bağlam tekrar tekrar gönderilir. Sağlayıcınız token başına ücretlendiriyor veya hız sınırı uyguluyorsa, aynı iş için fiilen birden çok kez ödeme yaparsınız.
- Gecikme: Her adım bir çıkarım daha gerektirir; 10 araç çağrısı içeren bir görev 20 veya daha fazla ardışık çıkarım anlamına gelir.
- Güvenilirlik: Bağlam penceresi doldukça model yönergeleri izlemekte zorlanır; işin bir kısmını atlayabilir veya tamamlamadan durabilir.
Anthropic’in programmatic tool calling yaklaşımı, modelin araçları çağıran kod yazmasıyla aynı problemi çözüyor. Dynamic Workflows benzer fikirden hareket ediyor; fark şu: LLM kod değil, güçlü tipli bir workflow DAG’ı üretiyor. Tipli şema planın yapısını sınırlıyor, çalıştırmadan önce bağımlılıklar, referanslar ve araç/alt ajan izinleri doğrulanıyor. DAG’ı yürüten Durable Functions motoru da bulutta olay güdümlü uygulamaların sıkça ihtiyaç duyduğu dayanıklılık ve gözlemlenebilirlik katmanını ekliyor.
Kaynakta ayrıca önemli bir ayrım yapılıyor: burada anlatılan Dynamic Workflows, benzer adı taşıyan Copilot CLI ve Copilot uygulamasındaki Dynamic workflows özelliğinden farklı. İki temel fark: Hosted Skills tarafında plan üretilen kod değil bildirimsel bir yapı, ve yürütme motoru dayanıklı.
Planlama ile yürütmeyi ayırmak
Dynamic Workflows ile LLM istemi bir kez okur, yürütme planını DAG olarak üretir ve yerleşik bir araç çağrısıyla bu DAG’ı bir Durable Functions orkestrasyonunda zamanlar. DAG içindeki tüm görevler planlama döngüsünden bağımsız çalışır; orkestratör görevleri zamanlar ve planlayıcı LLM’e dönmeden bekler.
Burada bir nüans var: sub_agent tipindeki görev kendi LLM çağrılarını yapar, token tüketir ve çalışırken LLM gecikmesi ekler. Bir workflow aracı da uygulaması gerektiriyorsa LLM çağırabilir. LLM sonunda nihai sonucu özetlerse, o tür da token tüketir.
Özelliği açmak için .agent.md dosyasının front matter bölümüne ilgili ayar ekleniyor:
---
name: Incident Triage Assistant
description: Investigates incidents by gathering evidence in parallel.
workflows:
enabled: true
---
Bu ayardan sonra Hosted Skills çalışma zamanı, modele dinamik iş akışlarını üretmek ve yönetmek için beş yerleşik araç sunuyor:
| Araç | İşlevi |
|---|---|
start_workflow(plan) |
LLM’in ürettiği DAG’ı doğrular, bir Durable orkestrasyonu başlatır ve anında workflow_id döndürür. |
get_workflow_status(workflow_id) |
İş akışının çalışma durumunu, tamamlandıysa nihai sonucunu döndürür. |
list_workflows() |
Mevcut oturumun iş akışlarını listeler. |
cancel_workflow(workflow_id) |
İş akışını işbirlikçi biçimde iptal eder. |
terminate_workflow(workflow_id) |
İş akışını anında durdurur. |
start_workflow “fire-and-forget” çalışıyor. LLM workflow_id‘yi aldığı anda turunu bitiriyor, tamamlanma için yoklama yapmıyor. İş akışı istemciye ihtiyaç duymadan sonuna kadar çalışıyor. Sonucu almak için LLM get_workflow_status‘ı çağırabiliyor; istemci tarafında ise çalışma zamanının GET /agents/{slug}/workflows uç noktası kullanılıyor. HTTP tetikleyicili hosted skill’lerde / adresinde sunulan yerleşik sohbet arayüzü de bu uç noktayla her görevin canlı ilerlemesini gösteriyor; iş akışı bittiğinde arayüz LLM’e bildirim mesajı gönderiyor ve model sonucu bir kez özetliyor.
LLM’in yazdığı DAG neye benziyor?
Aşağıdaki örnek, paralel çalışabilen çok sayıda araç çağrısından oluşan basit bir fan-out/fan-in planı:
{
"tasks": [
{ "id": "inspect_00", "type": "tool", "tool": "inspect_service_evidence",
"args": { "service": "checkout-api-00", "evidence_lines": 40 } },
{ "id": "inspect_01", "type": "tool", "tool": "inspect_service_evidence",
"args": { "service": "checkout-api-01", "evidence_lines": 40 } },
{ "id": "inspect_02", "type": "tool", "tool": "inspect_service_evidence",
"args": { "service": "checkout-api-02", "evidence_lines": 40 } },
{ "id": "publish", "type": "tool", "tool": "publish_benchmark_report",
"depends_on": ["inspect_00", "inspect_01", "inspect_02"],
"args": {
"report_blob": "runs/services-3/workflow.json",
"service_reports": ["${inspect_00.result}", "${inspect_01.result}", "${inspect_02.result}"]
} }
]
}
Üç inspect_* görevinin bağımlılığı yok; orkestratör bunları aynı dalgada paralel çalıştırıyor. publish ise ancak üçü de tamamlandıktan sonra başlıyor. Kritik nokta şu: ${inspect_00.result} gibi şablonlar iş akışı motorunun içinde çözülüyor, yani inceleme sonuçları hiçbir zaman LLM’e geri dönmüyor. publish_benchmark_report birleşik raporu Azure Blob Storage’a yazıyor; depolama DAG’daki bir görev değil, dışarıdaki bir kaynak.
Daha karmaşık senaryolarda veri güdümlü akış denetimi, yürütme politikası ve alt ajan devreye giriyor:
{
"tasks": [
{ "id": "discover", "type": "tool", "tool": "list_services",
"args": { "environment": "prod" } },
{ "id": "inspect", "type": "tool", "tool": "inspect_service_evidence",
"depends_on": ["discover"],
"for_each": "${discover.result.services}",
"when": { "ref": "${item.in_scope}", "operator": "equals", "value": true },
"args": { "service": "${item.name}", "evidence_lines": 40 },
"execution": {
"timeout": "PT30S",
"continue_on_error": true,
"retry": { "max_attempts": 3,
"backoff": { "initial": "PT1S", "multiplier": 2.0, "max": "PT4S" } }
} },
{ "id": "analyze", "type": "sub_agent", "agent": "incident_evidence_analyst",
"depends_on": ["inspect"],
"task": "Find the likely root cause from these inspection results: ${inspect.result}" },
{ "id": "publish", "type": "tool", "tool": "publish_report",
"depends_on": ["analyze"],
"args": { "summary": "${analyze.result.text}" } }
]
}
Burada discover bir servis listesi dönduruyor. for_each sayesinde inspect görevi çalışma zamanında her öğe için ayrı bir göreve (inspect[0], inspect[1]…) genişletiliyor; LLM planı yazarken öğe sayısını bilmek zorunda kalmıyor. when koşulu bir öğe için false dönerse o öğe skipped durumuna geçiyor ve araç çalışmıyor. Geçici hatalarda orkestratör execution.retry politikasına göre yeniden deniyor. Tüm paralel görevler bittiğinde sonuçlar {index, status, result} biçiminde toplanıp alt ajana veriliyor.
| Öğe | Anlamı |
|---|---|
depends_on |
DAG kenarı. Bağımlılıkları karşılanan görevler paralel çalışır. |
${node.result.path} |
Yukarı akıştaki bir görevin sonucuna referans; iş akışı motoru çözer. |
for_each / when |
Veri güdümlü akış denetimi: genişletme ve koşullu yürütme. |
execution |
Durable yerleşik yeniden deneme, deneme başına zaman aşımı ve continue_on_error. |
sub_agent |
İzin verilen bir hosted skill’i tek düğümde alt ajan olarak çalıştırır. |
wait |
Durable zamanlayıcı (PT30S ile 24 saat arası). Bekleme sırasında uygulama güvenle sıfıra ölçeklenebilir, süre dolunca iş akışı hemen devam eder. |
Çalışma zamanı nasıl kurgulanmış?
workflows.enabledtrue olduğunda Hosted Skills çalışma zamanı, dahili olarak genel bir Durable Functions orkestratörü ve iki aktivite kaydediyor. Bu yerleşik fonksiyonlar uygulamadaki tüm hosted skill’ler için yeniden kullanılıyor:
| Durable fonksiyon | İşlevi |
|---|---|
agents_workflow_orchestrator |
DAG’ı okur ve hazır görevleri paralel dalgalar hâlinde zamanlayarak sonuna kadar yürütür. |
agents_workflow_run_tool |
Tek bir @workflow_tool aracını çalıştırır. |
agents_workflow_run_sub_agent |
Bir alt ajanı bir kez çalıştırır. |
İş akışlarını etkinleştiren her hosted skill için çalışma zamanı, başlangıçta değiştirilemez bir politika üretiyor. Bu politika, o becerinin hangi araçları ve alt ajanları kullanabileceğini belirliyor. Tek bir agents_workflow_orchestrator tanımı tüm dinamik iş akışlarını çalıştırdığı için yürütme ve izleme deneyimi de tutarlı kalıyor.
LLM’den gelen planı güvenle çalıştırmak
Yazıda LLM’in ürettiği DAG açıkça “güvenilmeyen girdi” olarak ele alınıyor ve risk üç aşamada azaltılıyor.
1. Plan yazılırken: yalnızca izin verileni göster
start_workflow argümanı, bilinmeyen alanları reddeden tipli bir şemaya sahip. Çalışma zamanı sistem istemine yalnızca ilgili hosted skill’in kullanabileceği araç ve alt ajanları ekliyor — hem de doğrulamada kullanılan aynı politika nesnesinden üreterek. Böylece model, doğrulamanın reddedeceği bir araç çağrısını planlayamıyor. Dahili uçtan uca testlerde istemin yalnızca iş akışı yeteneğinden bahsettiği, modelin yerleşik araçları kullanıp kullanmayacağına kendisinin karar verdiği aktarılıyor.
2. Plan gönderildiğinde: başlamadan önce doğrula
start_workflow orkestrasyonu başlatmadan tüm DAG’ı doğruluyor. Sorun varsa iş akışı hiç başlamıyor; LLM gerekçesiyle birlikte bir hata alıyor ve planı düzeltebiliyor. Doğrulama şunları kontrol ediyor:
- Hosted skill’in her araç ve alt ajanı kullanma izni var mı?
- Araç ve alt ajan adlarında şablon yok — böylece veri, çağrılacak fonksiyonu değiştiremiyor.
- Yinelenen ID, bilinmeyen göreve bağımlılık veya döngü yok.
- Şablonlar yalnızca yukarı akıştaki görevlere referans veriyor.
- Plan sınırların içinde: 50 düğüm, 10 paralel görev, 24 saat bekleme ve oturum başına 10 aktif iş akışı.
- Plan, iş akışının içinden
start_workflowgibi yönetim araçlarını çağırmıyor.
when bilinçli olarak genel bir ifade dili değil; yalnızca equals ve not_equals operatörlerini destekliyor, JSON değerlerini tip eşleşmesi dâhil katı biçimde karşılaştırıyor. Bir yol mevcut değilse sonuç skip değil error oluyor; yani modelin hatası sessizce görev atlamaya dönüşmüyor.
3. Her görev çalışırken: yeniden yetkilendir
Her aktivite, kendi aracını veya alt ajanını o an dağıtılmış politikaya göre yeniden yetkilendiriyor. Bir dağıtım izni kaldırırsa, henüz çalışmamış düğümler başarısız oluyor ve çalışmıyor. Çalışma zamanı workflow ID’sini hosted skill ve oturumdan üretiyor; böylece bir oturum başka oturumların iş akışlarını göremiyor. Yürütme politikası plan gönderildiğinde orkestrasyon girdisinde saklandığı için yeni bir dağıtım replay davranışını değiştiremiyor.
Neden Durable Functions?
Tasarımın başında üç yürütme modeli karşılaştırılmış: sohbet içinde araç döngüsü, özel bir in-process zamanlayıcı ve Durable Functions çalışma zamanı. Tercih Durable Functions’tan yana kullanılmış.
Azure Durable Functions 2018’den bu yana genel kullanımda ve Microsoft içinde ve dışında birçok kritik üretim yükü buna bağlı. Kod ile programladığınız sunucusuz bir iş akışı motoru olarak çalışıyor; uzun süren görevler ve durumlu iş akışları, durumsuz ve geçici olay güdümlü hesaplama platformlarında güvenilir biçimde uygulanması zor olan şeyler. Uygulama çökerse veya yeniden başlarsa iş, otomatik olarak başka bir örnekte devam ediyor. Kaynak, bu kanıtlanmış teknolojiyi sıfırdan yeniden yazmak yerine yeniden kullanmanın mantıklı olduğunu belirtiyor.
Durable Task Scheduler (DTS) seçeneği de kararda etkili olmuş. DTS panosu iş akışı durumunu, görev giriş-çıkışlarını ve yürütme zaman çizelgesini gösteriyor. Dayanıklı arka ucu (Azure Storage veya DTS) uygulamanın host.json dosyasından yapılandırıyorsunuz; .agent.md dosyası değişmiyor.
Panodaki zaman çizelgesi DAG’ın şeklini de görünür kılıyor: önce discover_services, ardından paralel üç inspect_service aktivitesi (fan-out), sonra sonuçları birleştiren summarize_scan (fan-in). Yeniden denemeler ve hatalar da aynı çizelgede görünüyor. Çalışma zamanı görünen ad etiketleri ekliyor: orkestrasyon <agent_name>-orchestration biçiminde görünüyor, her araç aktivitesi araç adını, her alt ajan aktivitesi alt ajan adını gösteriyor. Input alanını açtığınızda LLM’in yazdığı DAG’ı ve her adımdaki giriş-çıkışı görebiliyorsunuz — hata ayıklama açısından önemli bir ayrıntı.
Dayanıklılık tarafında bir uyarı da var: Durable Functions yeniden deneme, çökme sonrası kurtarma ve bir Functions örneğini ayakta tutmadan uzun bekleme sağlıyor; ancak bekleme sırasında arka uç ücretleri yine de doğabiliyor. Ayrıca bu yeteneklerden yararlanmak için kendi orkestratörünüzü yazmanız gerekmiyor.
İş akışına uygun araç tasarlamak
İş akışı görevi, LLM muhakeme döngüsünün dışında bir Durable aktivitesi olarak çalışıyor. Görevler, kendilerini zamanlayan örnekle aynı uygulama örneğinde çalışabileceği gibi farklı bir örnekte de çalışabiliyor. Bu nedenle iş akışlarının kullandığı özel araç fonksiyonlarının durumsuz ve idempotent olacak şekilde tasarlanması gerekiyor. Bir özel aracın iş akışlarında kullanılabilmesi için @workflow_tool dekoratörüyle işaretlenmesi şart:
# tools/incident_tools.py
from typing import Any
from azure_functions_agents import workflow_tool
@workflow_tool(description="Fetch recent log lines for a service.")
async def fetch_logs(args: dict[str, Any]) -> dict[str, Any]:
service = args["service"]
return {"service": service, "lines": ["..."]}
İş akışı araçları normal araçlarla aynı tools/ klasörüne konuyor. Bir fonksiyonu hem olay istemlerinde hem iş akışlarında kullanmak isterseniz @tool ve @workflow_tool dekoratörlerini sırası önemsiz olacak şekilde birlikte ekliyorsunuz. Kurallar dört maddede özetleniyor:
- Tek bir
dictargümanı alır. - JSON’a serileştirilebilir bir değer döndürür, çünkü Durable Functions bu değeri geçmişinde saklar.
- Senkron (
def) veya asenkron (async def) olabilir. - Tüm girdisini
dictargümanından alır. İş akışını başlatan turdan durum okumaz; çünkü görev daha sonra farklı bir örnekte çalışabilir. Örneğin normal bir aracın o turda ayarladığı modül düzeyindeki bir değişkeni veya turun oluşturduğu yerel bir dosyayı okumayın. Araç bir müşteri ID’sine ihtiyaç duyuyorsa, LLM bunu görevinargsalanına koymalıdır.
Yeniden deneme politikasını aracın yazarı belirliyor; çünkü bir yeniden denemenin güvenli olup olmadığını en iyi aracı yazan kişi bilir.
Nereden başlamalı?
Özelliği denemek için dokümantasyonu okuyup örnek projeleri çalıştırabilir ya da GitHub Copilot içinde Azure Functions Hosted Skills canvas’ını açıp azure-functions-hosted-skills becerisiyle Copilot’a bir iş akışı kurdurabilirsiniz. Kaynakta olay inceleme (incident triage) ve kuyruk tabanlı rapor senaryoları için hazır örnekler de yer alıyor.
Terminoloji açısından not: Hosted Skills, daha önceki yazılarda Azure Functions serverless agents runtime olarak anılan bileşenle aynı şey. Python paketi azurefunctions-agents-runtime adını taşıyor ve kaynak kodu Azure/azure-functions-agents-runtime deposunda bulunuyor.
Kaynaklar ve İleri Okuma
- Dynamic Workflows in Azure Functions Hosted Skills (Tsuyoshi Ushio, Azure SDK Blog)
- Dynamic Workflows dokümantasyonu
- Azure/azure-functions-agents-runtime deposu
- Hosted Skills örnekleri
- Örnek: workflow-incident-triage
- Örnek: workflow-queue-p0-report
- Durable Functions genel bakış
- Durable Task Scheduler dokümantasyonu
- Hosted Skills canvas: GitHub Copilot içinde derleme ve hata ayıklama
- İlgili pull request (#177)
- Vally
- Durable Workflows ile Microsoft Agent Framework
- azure-functions-skills: Azure Functions için yapay zekâ çalışma alanı
- Azure Functions ile uzun süreli MCP araçları geliştirmek







Yorum gönder