การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก
ใช้ asyncio.gather() เพื่อเรียกใช้เครื่องมือหลายรายการพร้อมกัน
การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงดำเนินการเครื่องมือแบบขนาน
เมื่อเอเจนต์ต้องการผลลัพธ์จากเครื่องมืออิสระหลายรายการ การเรียกใช้ตามลำดับจะเสียเวลา หากเครื่องมือแต่ละรายการใช้เวลา 500ms เครื่องมือ 3 รายการจะใช้เวลา 1.5 วินาทีเมื่อเรียกตามลำดับ แต่ใช้เวลาเพียง 500ms เมื่อเรียกแบบขนาน ซึ่งเร็วขึ้น 3 เท่า
asyncio.gather() สำหรับการเรียกแบบขนาน
asyncio.gather() เรียกใช้โครูทีนหลายรายการพร้อมกันและส่งคืนผลลัพธ์ทั้งหมดตามลำดับ นี่คือเครื่องมือหลักสำหรับการเรียกใช้เครื่องมือของเอเจนต์แบบขนาน
import asyncio
import time
async def search_web(query: str) -> list:
await asyncio.sleep(0.5) # Simulate 500ms web search
return [f'Web result for: {query}']
async def search_database(query: str) -> list:
await asyncio.sleep(0.3) # Simulate 300ms DB query
return [f'DB result for: {query}']
async def get_weather(location: str) -> dict:
await asyncio.sleep(0.4) # Simulate 400ms API call
return {'location': location, 'temp': '22C'}
async def run_parallel():
start = time.perf_counter()
# Sequential: 0.5 + 0.3 + 0.4 = 1.2s
# Parallel: max(0.5, 0.3, 0.4) = 0.5s
web_results, db_results, weather = await asyncio.gather(
search_web('Python async'),
search_database('Python async'),
get_weather('New York')
)
elapsed = (time.perf_counter() - start) * 1000
print(f'Completed in {elapsed:.0f}ms (parallel)')
return web_results, db_results, weather
asyncio.run(run_parallel())การจัดการความล้มเหลวของเครื่องมือแต่ละรายการ
เมื่อใช้ return_exceptions=True ความล้มเหลวของเครื่องมือรายการเดียวจะไม่ยกเลิกการเรียกแบบขนานทั้งหมด ผลลัพธ์แต่ละรายการจะเป็นค่าหรือข้อยกเว้นอย่างใดอย่างหนึ่ง คุณจึงต้องตรวจสอบทีละรายการ
import asyncio
async def tool_that_fails(name: str):
await asyncio.sleep(0.2)
if name == 'flaky_api':
raise ConnectionError(f'{name}: service unavailable')
return f'{name}: success'
async def parallel_with_fault_tolerance():
tool_names = ['web_search', 'flaky_api', 'database', 'weather_api']
coros = [tool_that_fails(name) for name in tool_names]
results = await asyncio.gather(*coros, return_exceptions=True)
tool_results = {}
errors = {}
for name, result in zip(tool_names, results):
if isinstance(result, Exception):
errors[name] = str(result)
print(f'Tool {name} FAILED: {result}')
else:
tool_results[name] = result
print(f'Tool {name} OK: {result}')
print(f'\nSucceeded: {len(tool_results)}/{len(tool_names)}')
print('Errors:', errors)
return tool_results, errors
asyncio.run(parallel_with_fault_tolerance())การรวมผลลัพธ์แบบขนาน
หลังการดำเนินการแบบขนาน ให้รวมผลลัพธ์เป็นบริบทเดียวสำหรับ LLM และระบุให้ชัดเจนว่าข้อมูลแต่ละส่วนมาจากที่ใด
import asyncio
import json
async def parallel_research(query: str) -> dict:
web_task = search_web(query)
db_task = search_database(query)
weather_task = get_weather('New York')
results = await asyncio.gather(
web_task, db_task, weather_task,
return_exceptions=True
)
context_parts = []
sources_used = []
tool_names = ['web_search', 'database', 'weather']
for name, result in zip(tool_names, results):
if isinstance(result, Exception):
context_parts.append(f'[{name}]: unavailable ({result})')
else:
context_parts.append(f'[{name}]: {json.dumps(result)}')
sources_used.append(name)
combined_context = '\n'.join(context_parts)
return {
'query': query,
'context': combined_context,
'sources': sources_used
}
result = asyncio.run(parallel_research('Python performance tips'))
print('Sources used:', result['sources'])การกำหนดเวลาหมดสำหรับเครื่องมือแต่ละรายการ
ใช้ asyncio.wait_for() เพื่อกำหนดเวลาหมดให้กับการเรียกใช้เครื่องมือแต่ละรายการ เครื่องมือที่ทำงานช้าไม่ควรขัดขวางชุดงานแบบขนานทั้งหมดไปโดยไม่มีกำหนด
import asyncio
async def slow_tool(name: str) -> str:
await asyncio.sleep(10) # Very slow
return f'{name} result'
async def tool_with_timeout(coro, tool_name: str, timeout_seconds: float):
try:
result = await asyncio.wait_for(coro, timeout=timeout_seconds)
return result
except asyncio.TimeoutError:
return f'TIMEOUT: {tool_name} exceeded {timeout_seconds}s'
except Exception as e:
return f'ERROR: {tool_name}: {str(e)}'
async def parallel_with_timeouts():
results = await asyncio.gather(
tool_with_timeout(search_web('query'), 'web_search', 2.0),
tool_with_timeout(slow_tool('slow_api'), 'slow_api', 1.0),
tool_with_timeout(search_database('query'), 'database', 2.0)
)
for result in results:
print(result)
asyncio.run(parallel_with_timeouts())การเลือกเครื่องมือแบบขนานแบบไดนามิก
เอเจนต์อาจตัดสินใจแบบไดนามิกว่าจะเรียกใช้เครื่องมือใดแบบขนาน โดยพิจารณาจากคำค้นหา สร้างตัวกระจายงานที่จับคู่ชื่อเครื่องมือกับฟังก์ชันแบบอะซิงโครนัส แล้วเรียกใช้เครื่องมือที่เลือกพร้อมกัน
import asyncio
TOOL_REGISTRY = {
'web_search': search_web,
'database': search_database,
'weather': get_weather
}
async def execute_tools_parallel(tool_calls: list) -> dict:
'''
tool_calls: list of {'name': str, 'args': dict}
'''
tasks = {}
for call in tool_calls:
tool_name = call['name']
args = call.get('args', {})
fn = TOOL_REGISTRY.get(tool_name)
if fn:
# Get first positional arg (simplified)
first_arg = next(iter(args.values()), '') if args else ''
tasks[tool_name] = fn(first_arg)
else:
print(f'Unknown tool: {tool_name}')
if not tasks:
return {}
results = await asyncio.gather(*tasks.values(), return_exceptions=True)
return {
name: result
for name, result in zip(tasks.keys(), results)
}
tool_calls = [
{'name': 'web_search', 'args': {'query': 'async Python'}},
{'name': 'weather', 'args': {'location': 'London'}}
]
results = asyncio.run(execute_tools_parallel(tool_calls))
print('Results:', results)การจำกัดการทำงานแบบขนานด้วยเซมาฟอร์
การเรียกใช้เครื่องมือมากเกินไปพร้อมกันอาจทำให้เกินขีดจำกัดอัตราการเรียกของส่วนเชื่อมต่อโปรแกรมประยุกต์ หรือทำให้บริการรับภาระมากเกินไป ใช้ asyncio.Semaphore เพื่อจำกัดจำนวนการเรียกใช้เครื่องมือที่ทำงานพร้อมกัน
import asyncio
MAX_CONCURRENT = 3
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
async def rate_limited_tool(tool_fn, *args):
async with semaphore: # Blocks if MAX_CONCURRENT calls already running
return await tool_fn(*args)
async def process_many_queries(queries: list) -> list:
print(f'Processing {len(queries)} queries with max {MAX_CONCURRENT} concurrent')
tasks = [rate_limited_tool(search_web, q) for q in queries]
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = [r for r in results if not isinstance(r, Exception)]
print(f'Completed: {len(successes)}/{len(queries)}')
return results
queries = [f'query-{i}' for i in range(10)]
results = asyncio.run(process_many_queries(queries))
print('Done')การสตรีมผลลัพธ์บางส่วน
เมื่อใช้ asyncio.as_completed() ให้ประมวลผลผลลัพธ์ของเครื่องมือทันทีที่มาถึง แทนที่จะรอให้ทั้งหมดเสร็จสิ้น แล้วแสดงผลลัพธ์บางส่วนแก่ผู้ใช้ทันที
import asyncio
async def search_slow(query: str) -> dict:
await asyncio.sleep(1.0)
return {'source': 'slow_db', 'results': [f'Slow result for: {query}']}
async def search_fast(query: str) -> dict:
await asyncio.sleep(0.2)
return {'source': 'fast_cache', 'results': [f'Fast result for: {query}']}
async def process_as_available(query: str):
coros = [
search_fast(query),
search_slow(query),
search_web(query),
search_database(query)
]
tasks = [asyncio.create_task(c) for c in coros]
partial_results = []
print('Processing results as they arrive:')
for future in asyncio.as_completed(tasks):
result = await future
partial_results.append(result)
print(f' Got result {len(partial_results)}: {result}')
# In a real agent: stream this to the user interface
return partial_results
asyncio.run(process_as_available('machine learning'))การเรียกใช้เครื่องมือแบบขนานใน LangChain
LangChain รองรับการเรียกใช้เครื่องมือแบบขนานโดยกำเนิด เมื่อ LLM ส่งคืนการเรียกใช้เครื่องมือหลายรายการในคำตอบเดียว จัดการการเรียกเหล่านั้นด้วย asyncio.gather() เพื่อประสิทธิภาพ
import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model='gpt-4o-mini', api_key='sk-...')
TOOL_EXECUTORS = {
'search_web': lambda args: search_web(args.get('query', '')),
'search_database': lambda args: search_database(args.get('query', '')),
'get_weather': lambda args: get_weather(args.get('location', 'New York'))
}
async def handle_parallel_tool_calls(response_message) -> list:
if not response_message.tool_calls:
return []
tasks = []
tool_call_ids = []
for tool_call in response_message.tool_calls:
executor = TOOL_EXECUTORS.get(tool_call['name'])
if executor:
tasks.append(executor(tool_call['args']))
tool_call_ids.append(tool_call['id'])
results = await asyncio.gather(*tasks, return_exceptions=True)
return [
{'tool_call_id': tc_id, 'result': r}
for tc_id, r in zip(tool_call_ids, results)
]
print('Parallel LangChain tool execution defined')การกำจัดผลลัพธ์ซ้ำ
เมื่อดำเนินการค้นหาแบบขนาน เครื่องมือแต่ละรายการอาจส่งคืนผลลัพธ์ที่ซ้ำซ้อนกัน กำจัดผลลัพธ์ซ้ำก่อนนำเสนอแก่ LLM เพื่อป้องกันไม่ให้ข้อมูลเดียวกันปรากฏซ้ำหลายครั้งในบริบท
import hashlib
def deduplicate_results(all_results: list) -> list:
seen_hashes = set()
unique_results = []
for result in all_results:
content = str(result)
content_hash = hashlib.md5(content.encode()).hexdigest()
if content_hash not in seen_hashes:
seen_hashes.add(content_hash)
unique_results.append(result)
return unique_results
def merge_parallel_results(tool_results: dict) -> list:
all_items = []
for tool_name, results in tool_results.items():
if isinstance(results, Exception):
continue
if isinstance(results, list):
for item in results:
if isinstance(item, dict):
item['source'] = tool_name
all_items.append(item)
else:
all_items.append({'source': tool_name, 'data': results})
return deduplicate_results(all_items)
sample_results = {
'web': ['Result A', 'Result B'],
'db': ['Result B', 'Result C'] # Result B is duplicate
}
merged = merge_parallel_results(sample_results)
print(f'Before: {sum(len(v) for v in sample_results.values())} items')
print(f'After dedup: {len(merged)} items')การวัดความเร็วที่เพิ่มขึ้นจากการทำงานแบบขนาน
วัดความเร็วที่เพิ่มขึ้นจริงจากการทำงานแบบขนาน เปรียบเทียบเวลาการดำเนินการตามลำดับกับแบบขนาน เพื่อวัดประโยชน์และใช้เหตุผลรองรับความซับซ้อนที่เพิ่มขึ้น
import asyncio
import time
async def measure_speedup(tools_and_args: list):
# Sequential timing
seq_start = time.perf_counter()
seq_results = []
for fn, args in tools_and_args:
result = await fn(*args)
seq_results.append(result)
seq_time = (time.perf_counter() - seq_start) * 1000
# Parallel timing
par_start = time.perf_counter()
par_results = await asyncio.gather(*[fn(*args) for fn, args in tools_and_args])
par_time = (time.perf_counter() - par_start) * 1000
speedup = seq_time / par_time if par_time > 0 else 0
print(f'Sequential: {seq_time:.0f}ms')
print(f'Parallel: {par_time:.0f}ms')
print(f'Speedup: {speedup:.1f}x')
return speedup
tools = [
(search_web, ('query',)),
(search_database, ('query',)),
(get_weather, ('London',))
]
asyncio.run(measure_speedup(tools))แบบทดสอบความเข้าใจ: การดำเนินการแบบขนาน
ทดสอบความเข้าใจของคุณเกี่ยวกับการเรียกใช้เครื่องมือแบบขนานที่ไม่บล็อกการทำงาน
สรุปการดำเนินการเครื่องมือแบบขนาน
การดำเนินการเครื่องมือแบบขนานด้วย asyncio ช่วยลดความหน่วงของเอเจนต์ได้อย่างมาก ใช้ asyncio.gather() สำหรับการเรียกพร้อมกัน, return_exceptions=True เพื่อรองรับความล้มเหลว, asyncio.wait_for() เพื่อกำหนดเวลาหมดของเครื่องมือแต่ละรายการ, Semaphore เพื่อจำกัดอัตราการเรียก และ asyncio.as_completed() เพื่อสตรีมผลลัพธ์บางส่วน ควรกำจัดผลลัพธ์ซ้ำที่รวมกันแล้วเสมอ เพื่อให้บริบทสำหรับ LLM สะอาดและไม่ซ้ำซ้อน
คำถามที่พบบ่อย
บทเรียน “การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก”
ใช้ asyncio.gather() เพื่อเรียกใช้เครื่องมือหลายรายการพร้อมกัน คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม
ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- Python แบบอะซิงโครนัสสำหรับนักพัฒนาเอเจนต์
- คิวเหตุการณ์และตัวกลางรับส่งข้อความ
- การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก
- เฟรมเวิร์กเอเจนต์แบบอะซิงโครนัส: LangChain และอื่น ๆ