0Pricing
AI Agents · บทเรียน

การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก

ใช้ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Python แบบอะซิงโครนัสสำหรับนักพัฒนาเอเจนต์
  2. คิวเหตุการณ์และตัวกลางรับส่งข้อความ
  3. การทำงานของเครื่องมือแบบขนานโดยไม่บล็อก
  4. เฟรมเวิร์กเอเจนต์แบบอะซิงโครนัส: LangChain และอื่น ๆ
← กลับไปที่ AI Agents