এই টিউটোরিয়ালটি আপনাকে একটি ReAct-স্টাইলের এজেন্টিক লুপ তৈরির পথ দেখাবে যা যুক্তির জন্য Gemini API এবং স্থায়িত্বের জন্য Temporal ব্যবহার করে। এই টিউটোরিয়ালের সম্পূর্ণ সোর্স কোড GitHub- এ উপলব্ধ।
এজেন্টটি আবহাওয়ার সতর্কতা খোঁজা বা আইপি ঠিকানার ভূ-স্থান নির্ধারণের মতো সরঞ্জামগুলিতে কল করতে পারে এবং প্রতিক্রিয়া জানানোর জন্য পর্যাপ্ত তথ্য না পাওয়া পর্যন্ত লুপ করবে।
এটিকে একটি সাধারণ এজেন্ট ডেমো থেকে আলাদা করে তোলে স্থায়িত্ব । প্রতিটি LLM কল, প্রতিটি টুল ইনভোকেশন এবং এজেন্টিক লুপের প্রতিটি ধাপ টেম্পোরাল দ্বারা স্থায়ী থাকে। যদি প্রক্রিয়াটি ক্র্যাশ হয়, নেটওয়ার্ক বন্ধ হয়ে যায়, অথবা একটি API টাইম আউট হয়, তাহলে টেম্পোরাল স্বয়ংক্রিয়ভাবে শেষ সম্পন্ন ধাপ থেকে পুনরায় চেষ্টা করে এবং পুনরায় শুরু করে। কোনও কথোপকথনের ইতিহাস হারিয়ে যায় না এবং কোনও টুল কল ভুলভাবে পুনরাবৃত্তি হয় না।
স্থাপত্য
স্থাপত্যটি তিনটি অংশ নিয়ে গঠিত:
- কর্মপ্রবাহ: এজেন্টিক লুপ যা এক্সিকিউশন লজিককে সুসংগঠিত করে।
- কার্যকলাপ: কাজের পৃথক একক (এলএলএম কল, টুল কল) যা টেম্পোরাল টেকসই করে তোলে।
- কর্মী: যে প্রক্রিয়াটি কর্মপ্রবাহ এবং ক্রিয়াকলাপ সম্পাদন করে।
এই উদাহরণে, আপনি এই তিনটি অংশই একটি ফাইলে রাখবেন ( durable_agent_worker.py )। বাস্তব-বিশ্বের বাস্তবায়নে, বিভিন্ন স্থাপনা এবং স্কেলেবিলিটি সুবিধার জন্য আপনি এগুলিকে আলাদা করবেন। আপনি এজেন্টকে প্রম্পট সরবরাহকারী কোডটি দ্বিতীয় ফাইলে ( start_workflow.py ) রাখবেন।
পূর্বশর্ত
এই নির্দেশিকাটি সম্পূর্ণ করতে, আপনার প্রয়োজন হবে:
- একটি জেমিনি এপিআই কী। আপনি গুগল এআই স্টুডিওতে বিনামূল্যে একটি তৈরি করতে পারেন।
- পাইথন সংস্করণ ৩.১০ বা তার পরবর্তী।
- স্থানীয় ডেভেলপমেন্ট সার্ভার চালানোর জন্য টেম্পোরাল CLI ।
সেটআপ
শুরু করার আগে, নিশ্চিত করুন যে আপনার স্থানীয়ভাবে একটি টেম্পোরাল ডেভেলপমেন্ট সার্ভার চলছে:
temporal server start-devএরপর, প্রয়োজনীয় নির্ভরতা ইনস্টল করুন:
pip install temporalio google-genai httpx pydantic python-dotenv আপনার Gemini API কী দিয়ে আপনার প্রোজেক্ট ডিরেক্টরিতে একটি .env ফাইল তৈরি করুন। আপনি Google AI Studio থেকে একটি API কী পেতে পারেন।
echo "GOOGLE_API_KEY=your-api-key-here" > .envবাস্তবায়ন
এই টিউটোরিয়ালের বাকি অংশটি durable_agent_worker.py এর উপর থেকে নীচে পর্যন্ত চলে, এজেন্টটিকে টুকরো টুকরো করে তৈরি করে। ফাইলটি তৈরি করুন এবং অনুসরণ করুন।
আমদানি এবং স্যান্ডবক্স সেটআপ
যেসব আমদানি আগে থেকেই সংজ্ঞায়িত করতে হবে, সেগুলো দিয়ে শুরু করুন। workflow.unsafe.imports_passed_through() ব্লকটি Temporal-এর ওয়ার্কফ্লো স্যান্ডবক্সকে নির্দিষ্ট মডিউলগুলিকে কোনও বাধা ছাড়াই পাস করতে বলে। এটি প্রয়োজনীয় কারণ বেশ কয়েকটি লাইব্রেরি (বিশেষ করে httpx , যা urllib.request.Request সাবক্লাস) এমন প্যাটার্ন ব্যবহার করে যা স্যান্ডবক্স অন্যথায় ব্লক করত।
from temporalio import workflow
with workflow.unsafe.imports_passed_through():
import pydantic_core # noqa: F401
import annotated_types # noqa: F401
import httpx
from pydantic import BaseModel, Field
from google import genai
from google.genai import types
সিস্টেম নির্দেশাবলী
এরপর, এজেন্টের ব্যক্তিত্ব সংজ্ঞায়িত করুন। সিস্টেমের নির্দেশাবলী মডেলকে কীভাবে আচরণ করতে হবে তা বলে। এই এজেন্টকে হাইকুসে প্রতিক্রিয়া জানাতে নির্দেশ দেওয়া হয় যখন কোনও সরঞ্জামের প্রয়োজন হয় না।
SYSTEM_INSTRUCTIONS = """
You are a helpful agent that can use tools to help the user.
You will be given an input from the user and a list of tools to use.
You may or may not need to use the tools to satisfy the user ask.
If no tools are needed, respond in haikus.
"""
টুলের সংজ্ঞা
এখন এজেন্ট কোন কোন টুল ব্যবহার করতে পারে তা নির্ধারণ করুন। প্রতিটি টুল একটি বর্ণনামূলক ডকস্ট্রিং সহ একটি অ্যাসিঙ্ক ফাংশন। যেসব টুল প্যারামিটার গ্রহণ করে তারা তাদের একক আর্গুমেন্ট হিসেবে একটি পাইড্যান্টিক মডেল ব্যবহার করে। এটি একটি টেম্পোরাল সেরা অনুশীলন যা সময়ের সাথে সাথে ঐচ্ছিক ক্ষেত্র যোগ করার সাথে সাথে কার্যকলাপ স্বাক্ষরগুলিকে স্থিতিশীল রাখে।
import json
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
class GetWeatherAlertsRequest(BaseModel):
"""Request model for getting weather alerts."""
state: str = Field(description="Two-letter US state code (e.g. CA, NY)")
async def get_weather_alerts(request: GetWeatherAlertsRequest) -> str:
"""Get weather alerts for a US state.
Args:
request: The request object containing:
- state: Two-letter US state code (e.g. CA, NY)
"""
headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
url = f"{NWS_API_BASE}/alerts/active/area/{request.state}"
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers, timeout=5.0)
response.raise_for_status()
return json.dumps(response.json())
এরপর, আইপি অ্যাড্রেস জিওলোকেশনের জন্য টুলগুলি সংজ্ঞায়িত করুন:
class GetLocationRequest(BaseModel):
"""Request model for getting location info from an IP address."""
ipaddress: str = Field(description="An IP address")
async def get_ip_address() -> str:
"""Get the public IP address of the current machine."""
async with httpx.AsyncClient() as client:
response = await client.get("https://icanhazip.com")
response.raise_for_status()
return response.text.strip()
async def get_location_info(request: GetLocationRequest) -> str:
"""Get the location information for an IP address including city, state, and country.
Args:
request: The request object containing:
- ipaddress: An IP address to look up
"""
async with httpx.AsyncClient() as client:
response = await client.get(f"http://ip-api.com/json/{request.ipaddress}")
response.raise_for_status()
result = response.json()
return f"{result['city']}, {result['regionName']}, {result['country']}"
টুল রেজিস্ট্রি
এরপর, একটি রেজিস্ট্রি তৈরি করুন যা হ্যান্ডলার ফাংশনের সাথে টুলের নাম ম্যাপ করে। get_tools() ফাংশন FunctionDeclaration.from_callable_with_api_option() ব্যবহার করে callables থেকে Gemini-compatible FunctionDeclaration অবজেক্ট তৈরি করে।
from typing import Any, Awaitable, Callable
ToolHandler = Callable[..., Awaitable[Any]]
def get_handler(tool_name: str) -> ToolHandler:
"""Get the handler function for a given tool name."""
if tool_name == "get_location_info":
return get_location_info
if tool_name == "get_ip_address":
return get_ip_address
if tool_name == "get_weather_alerts":
return get_weather_alerts
raise ValueError(f"Unknown tool name: {tool_name}")
def get_tools() -> types.Tool:
"""Get the Tool object containing all available function declarations.
Uses FunctionDeclaration.from_callable_with_api_option() from the Google GenAI SDK
to generate tool definitions from the handler functions.
"""
return types.Tool(
function_declarations=[
types.FunctionDeclaration.from_callable_with_api_option(
callable=get_weather_alerts, api_option="GEMINI_API"
),
types.FunctionDeclaration.from_callable_with_api_option(
callable=get_location_info, api_option="GEMINI_API"
),
types.FunctionDeclaration.from_callable_with_api_option(
callable=get_ip_address, api_option="GEMINI_API"
),
]
)
এলএলএম কার্যক্রম
এখন Gemini API কল করে এমন কার্যকলাপ সংজ্ঞায়িত করুন। GeminiChatRequest এবং GeminiChatResponse ডেটা ক্লাস চুক্তিকে সংজ্ঞায়িত করে।
আপনি স্বয়ংক্রিয় ফাংশন কলিং অক্ষম করবেন যাতে LLM ইনভোকেশন এবং টুল ইনভোকেশন পৃথক কাজ হিসেবে পরিচালিত হয়, যা আপনার এজেন্টের স্থায়িত্ব আরও বাড়িয়ে তোলে। আপনি SDK এর অন্তর্নির্মিত পুনঃপ্রয়াস ( attempts=1 )ও অক্ষম করবেন কারণ টেম্পোরাল পুনঃপ্রয়াসগুলি টেকসইভাবে পরিচালনা করে।
import os
from dataclasses import dataclass
from temporalio import activity
@dataclass
class GeminiChatRequest:
"""Request parameters for a Gemini chat completion."""
model: str
system_instruction: str
contents: list[types.Content]
tools: list[types.Tool]
@dataclass
class GeminiChatResponse:
"""Response from a Gemini chat completion."""
text: str | None
function_calls: list[dict[str, Any]]
raw_parts: list[types.Part]
@activity.defn
async def generate_content(request: GeminiChatRequest) -> GeminiChatResponse:
"""Execute a Gemini chat completion with tool support."""
api_key = os.environ.get("GOOGLE_API_KEY")
if not api_key:
raise ValueError("GOOGLE_API_KEY environment variable is not set")
client = genai.Client(
api_key=api_key,
http_options=types.HttpOptions(
retry_options=types.HttpRetryOptions(attempts=1),
),
)
config = types.GenerateContentConfig(
system_instruction=request.system_instruction,
tools=request.tools,
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
)
response = await client.aio.models.generate_content(
model=request.model,
contents=request.contents,
config=config,
)
function_calls = []
raw_parts = []
text_parts = []
if response.candidates and response.candidates[0].content:
for part in response.candidates[0].content.parts:
raw_parts.append(part)
if part.function_call:
function_calls.append(
{
"name": part.function_call.name,
"args": dict(part.function_call.args) if part.function_call.args else {},
}
)
elif part.text:
text_parts.append(part.text)
text = "".join(text_parts) if text_parts and not function_calls else None
return GeminiChatResponse(
text=text,
function_calls=function_calls,
raw_parts=raw_parts,
)
গতিশীল টুল কার্যকলাপ
এরপর, টুলগুলি কার্যকর করে এমন কার্যকলাপ সংজ্ঞায়িত করুন। এটি টেম্পোরালের গতিশীল কার্যকলাপ বৈশিষ্ট্য ব্যবহার করে: টুল হ্যান্ডলার (একটি কলযোগ্য) টুল রেজিস্ট্রি থেকে get_handler ফাংশনের মাধ্যমে প্রাপ্ত হয়। এটি বিভিন্ন এজেন্টকে কেবল বিভিন্ন সরঞ্জাম এবং সিস্টেম নির্দেশাবলী সরবরাহ করে সংজ্ঞায়িত করার অনুমতি দেয়; এজেন্টিক লুপ বাস্তবায়নের কর্মপ্রবাহে কোনও পরিবর্তনের প্রয়োজন হয় না।
এই কার্যকলাপটি হ্যান্ডলারের স্বাক্ষর পরীক্ষা করে আর্গুমেন্ট কীভাবে পাস করতে হবে তা নির্ধারণ করে। যদি হ্যান্ডলার একটি পাইড্যান্টিক মডেল আশা করে, তাহলে এটি জেমিনি দ্বারা তৈরি নেস্টেড আউটপুট ফর্ম্যাটটি পরিচালনা করে (উদাহরণস্বরূপ, {"request": {"state": "CA"}} এর পরিবর্তে একটি ফ্ল্যাট {"state": "CA"} )।
import inspect
from collections.abc import Sequence
from temporalio.common import RawValue
@activity.defn(dynamic=True)
async def dynamic_tool_activity(args: Sequence[RawValue]) -> dict:
"""Execute a tool dynamically based on the activity name."""
tool_name = activity.info().activity_type
tool_args = activity.payload_converter().from_payload(args[0].payload, dict)
activity.logger.info(f"Running dynamic tool '{tool_name}' with args: {tool_args}")
handler = get_handler(tool_name)
if not inspect.iscoroutinefunction(handler):
raise TypeError("Tool handler must be async (awaitable).")
sig = inspect.signature(handler)
params = list(sig.parameters.values())
if len(params) == 0:
result = await handler()
else:
param = params[0]
param_name = param.name
ann = param.annotation
if isinstance(ann, type) and issubclass(ann, BaseModel):
nested_args = tool_args.get(param_name, tool_args)
result = await handler(ann(**nested_args))
else:
result = await handler(**tool_args)
activity.logger.info(f"Tool '{tool_name}' result: {result}")
return result
এজেন্টিক লুপ ওয়ার্কফ্লো
এখন এজেন্ট তৈরির জন্য আপনার কাছে সমস্ত কাজ শেষ। AgentWorkflow ক্লাস এজেন্ট লুপ ধারণকারী একটি ওয়ার্কফ্লো বাস্তবায়ন করে। সেই লুপের মধ্যে, LLM কার্যকলাপের মাধ্যমে আহ্বান করা হয় (এটিকে টেকসই করে তোলে), আউটপুট পরিদর্শন করা হয়, এবং যদি LLM দ্বারা কোনও টুল বেছে নেওয়া হয়, তবে এটি dynamic_tool_activity এর মাধ্যমে আহ্বান করা হয়।
এই সহজ ReAct স্টাইল এজেন্টে, একবার LLM কোনও টুল ব্যবহার না করার সিদ্ধান্ত নিলে, লুপটি সম্পূর্ণ বলে বিবেচিত হয় এবং চূড়ান্ত LLM ফলাফল ফেরত পাঠানো হয়।
from datetime import timedelta
@workflow.defn
class AgentWorkflow:
"""Agentic loop workflow that uses Gemini for LLM calls and executes tools."""
@workflow.run
async def run(self, input: str) -> str:
contents: list[types.Content] = [
types.Content(role="user", parts=[types.Part.from_text(text=input)])
]
tools = [get_tools()]
while True:
result = await workflow.execute_activity(
generate_content,
GeminiChatRequest(
model="gemini-3-flash-preview",
system_instruction=SYSTEM_INSTRUCTIONS,
contents=contents,
tools=tools,
),
start_to_close_timeout=timedelta(seconds=60),
)
if result.function_calls:
# Sending the complete raw_parts here ensures Gemini 3 thought
# signatures are propagated correctly.
contents.append(types.Content(role="model", parts=result.raw_parts))
for function_call in result.function_calls:
tool_result = await self._handle_function_call(function_call)
contents.append(
types.Content(
role="user",
parts=[
types.Part.from_function_response(
name=function_call["name"],
response={"result": tool_result},
)
],
)
)
else:
return result.text
# Leave this in place. You will un-comment it during a durability
# test later on.
# await asyncio.sleep(10)
async def _handle_function_call(self, function_call: dict) -> str:
"""Execute a tool via dynamic activity and return the result."""
tool_name = function_call["name"]
tool_args = function_call.get("args", {})
result = await workflow.execute_activity(
tool_name,
tool_args,
start_to_close_timeout=timedelta(seconds=30),
)
return result
এজেন্টিক লুপটি সম্পূর্ণ টেকসই। যদি এজেন্ট কর্মী লুপের মধ্য দিয়ে বেশ কয়েকবার পুনরাবৃত্তির পরে ক্র্যাশ করে, তাহলে টেম্পোরাল ঠিক যেখানে ছেড়েছিল সেখান থেকে শুরু করবে, ইতিমধ্যেই সম্পাদিত LLM ইনভোকেশন বা টুল কল পুনরায় আহ্বান করার প্রয়োজন ছাড়াই।
কর্মী স্টার্টআপ
অবশেষে, সবকিছু একসাথে সংযুক্ত করুন। কোডটি প্রয়োজনীয় ব্যবসায়িক যুক্তি এমনভাবে প্রয়োগ করে যাতে এটি একটি একক প্রক্রিয়ায় চলমান বলে মনে হয়, টেম্পোরালের ব্যবহার এটিকে একটি ইভেন্ট-চালিত সিস্টেমে পরিণত করে (বিশেষ করে, ইভেন্ট-সোর্সড) যেখানে কর্মপ্রবাহ এবং কার্যকলাপের মধ্যে যোগাযোগ টেম্পোরাল দ্বারা প্রদত্ত বার্তার মাধ্যমে ঘটে।
টেম্পোরাল কর্মী টেম্পোরাল পরিষেবার সাথে সংযোগ স্থাপন করে এবং কর্মপ্রবাহ এবং কার্যকলাপের কাজের জন্য সময়সূচীকারী হিসেবে কাজ করে। কর্মী কর্মপ্রবাহ এবং উভয় কার্যকলাপ নিবন্ধন করে, তারপর কাজের জন্য শুনতে শুরু করে।
import asyncio
from concurrent.futures import ThreadPoolExecutor
from dotenv import load_dotenv
from temporalio.client import Client
from temporalio.contrib.pydantic import pydantic_data_converter
from temporalio.envconfig import ClientConfig
from temporalio.worker import Worker
async def main():
config = ClientConfig.load_client_connect_config()
config.setdefault("target_host", "localhost:7233")
client = await Client.connect(
**config,
data_converter=pydantic_data_converter,
)
worker = Worker(
client,
task_queue="gemini-agent-python-task-queue",
workflows=[
AgentWorkflow,
],
activities=[
generate_content,
dynamic_tool_activity,
],
activity_executor=ThreadPoolExecutor(max_workers=10),
)
await worker.run()
if __name__ == "__main__":
load_dotenv()
asyncio.run(main())
ক্লায়েন্ট স্ক্রিপ্ট
ক্লায়েন্ট স্ক্রিপ্ট তৈরি করুন ( start_workflow.py )। এটি একটি কোয়েরি জমা দেয় এবং ফলাফলের জন্য অপেক্ষা করে। লক্ষ্য করুন এটি এজেন্ট ওয়ার্কারে উল্লেখিত একই টাস্ক কিউয়ের সাথে সংযোগ স্থাপন করে - start_workflow স্ক্রিপ্টটি ব্যবহারকারীর প্রম্পট সহ একটি ওয়ার্কফ্লো টাস্ককে সেই টাস্ক কিউতে প্রেরণ করে, এজেন্টের সম্পাদন শুরু করে।
import asyncio
import sys
import uuid
from temporalio.client import Client
from temporalio.contrib.pydantic import pydantic_data_converter
async def main():
client = await Client.connect(
"localhost:7233",
data_converter=pydantic_data_converter,
)
query = sys.argv[1] if len(sys.argv) > 1 else "Tell me about recursion"
result = await client.execute_workflow(
"AgentWorkflow",
query,
id=f"gemini-agent-id-{uuid.uuid4()}",
task_queue="gemini-agent-python-task-queue",
)
print(f"\nResult:\n{result}")
if __name__ == "__main__":
asyncio.run(main())
এজেন্ট চালান।
যদি আপনি ইতিমধ্যেই না করে থাকেন, তাহলে টেম্পোরাল ডেভেলপমেন্ট সার্ভারটি শুরু করুন:
temporal server start-devএকটি নতুন টার্মিনাল উইন্ডোতে, এজেন্ট কর্মী শুরু করুন:
python -m durable_agent_workerতৃতীয় টার্মিনাল উইন্ডোতে, আপনার এজেন্টের কাছে একটি প্রশ্ন জমা দিন:
python -m start_workflow "are there any weather alerts for where I am?"durable_agent_worker এর টার্মিনালে আউটপুটটি লক্ষ্য করুন যা এজেন্টিক লুপের প্রতিটি পুনরাবৃত্তিতে ঘটে যাওয়া ক্রিয়াগুলি দেখায়। LLM তার হাতে থাকা বিভিন্ন সরঞ্জাম ব্যবহার করে ব্যবহারকারীর অনুরোধ পূরণ করতে সক্ষম। আপনি http://localhost:8233/namespaces/default/workflows এ Temporal UI এর মাধ্যমে সম্পাদিত পদক্ষেপগুলি দেখতে পারেন।
এজেন্টের কারণ এবং কল টুলগুলি দেখতে কয়েকটি ভিন্ন প্রম্পট চেষ্টা করুন:
python -m start_workflow "are there any weather alerts for New York?"python -m start_workflow "where am I?"python -m start_workflow "what is my ip address?"python -m start_workflow "tell me a joke"
শেষ প্রম্পটে কোনও সরঞ্জামের প্রয়োজন হয় না, তাই এজেন্ট SYSTEM_INSTRUCTIONS উপর ভিত্তি করে একটি হাইকুতে উত্তর দেয়।
স্থায়িত্ব পরীক্ষা করুন (ঐচ্ছিক)
টেম্পোরালের উপর ভিত্তি করে তৈরি করলে আপনার এজেন্ট ব্যর্থতা থেকে নির্বিঘ্নে বেঁচে যাবে। আপনি দুটি স্বতন্ত্র পরীক্ষা ব্যবহার করে এটি পরীক্ষা করতে পারেন।
নেটওয়ার্ক বিভ্রাটের অনুকরণ করা
এই পরীক্ষায়, আপনি আপনার কম্পিউটারের ইন্টারনেট সংযোগ সাময়িকভাবে বন্ধ করে দেবেন, একটি ওয়ার্কফ্লো জমা দেবেন, টেম্পোরাল স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করে দেখবেন এবং তারপর নেটওয়ার্কটি পুনরুদ্ধার করতে দেখবেন।
- আপনার মেশিনটি ইন্টারনেট থেকে সংযোগ বিচ্ছিন্ন করুন (উদাহরণস্বরূপ, আপনার ওয়াই-ফাই বন্ধ করুন)।
একটি কর্মপ্রবাহ জমা দিন:
python -m start_workflow "tell me a joke"টেম্পোরাল UI (
http://localhost:8233) পরীক্ষা করুন। আপনি LLM কার্যকলাপ ব্যর্থ দেখতে পাবেন এবং টেম্পোরাল স্বয়ংক্রিয়ভাবে ব্যাকগ্রাউন্ডে পুনরায় চেষ্টা পরিচালনা করছে।ইন্টারনেটে পুনরায় সংযোগ করুন।
পরবর্তী স্বয়ংক্রিয় পুনঃচেষ্টা সফলভাবে জেমিনি API-তে পৌঁছাবে এবং আপনার টার্মিনাল চূড়ান্ত ফলাফল প্রিন্ট করবে।
শ্রমিক দুর্ঘটনা থেকে বেঁচে যাওয়া
এই পরীক্ষায়, আপনি এক্সিকিউশনের মাঝখানে কর্মীকে মেরে ফেলেন এবং এটি পুনরায় চালু করেন। টেম্পোরাল ওয়ার্কফ্লো ইতিহাস (ইভেন্ট সোর্সিং) পুনরায় দেখায় এবং শেষ সম্পন্ন কার্যকলাপ থেকে পুনরায় শুরু করে—ইতিমধ্যে সম্পন্ন LLM আমন্ত্রণ এবং টুল কল পুনরাবৃত্তি হয় না।
- কর্মীকে হত্যা করার জন্য নিজেকে সময় দিতে,
durable_agent_worker.pyখুলুন এবংAgentWorkflowrunলুপের ভিতরেawait asyncio.sleep(10)অস্থায়ীভাবে মন্তব্য মুক্ত করুন। কর্মী পুনরায় চালু করুন:
python -m durable_agent_workerএমন একটি কোয়েরি জমা দিন যা বেশ কয়েকটি টুল ট্রিগার করে:
python -m start_workflow "are there any weather alerts where I am?"ওয়ার্কার প্রক্রিয়াটি সম্পন্ন হওয়ার আগে যেকোনো সময় বন্ধ করুন (ওয়ার্কার টার্মিনালে
Ctrl-C, অথবা ব্যাকগ্রাউন্ডে চলমান থাকলেkill %1ব্যবহার করে)।কর্মী পুনরায় চালু করুন:
python -m durable_agent_worker
টেম্পোরাল ওয়ার্কফ্লো ইতিহাস পুনরায় প্রদর্শন করে। ইতিমধ্যে সম্পন্ন হওয়া LLM কল এবং টুল ইনভোকেশনগুলি পুনরায় সম্পাদিত হয় না - তাদের ফলাফলগুলি তাৎক্ষণিকভাবে ইতিহাস (ইভেন্ট লগ) থেকে পুনরায় প্রদর্শিত হয়। ওয়ার্কফ্লো সফলভাবে শেষ হয়।