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

การตรวจจับและหยุดลูปไม่สิ้นสุด

ตัวป้องกันจำนวนรอบสูงสุด การตรวจจับการดำเนินการซ้ำ และตัวตัดวงจรลูป

การตรวจจับและหยุดลูปไม่สิ้นสุด เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

ภัยคุกคามจากการวนรอบไม่สิ้นสุด

เอเจนต์ที่อยู่ในการวนรอบไม่สิ้นสุดจะใช้โทเค็นสิ้นเปลือง ขัดขวางการใช้ทรัพยากร และไม่สร้างผลลัพธ์ที่เป็นประโยชน์ ในระบบจริง สิ่งนี้หมายถึงการสูญเสียเงินและความไม่พอใจของผู้ใช้โดยตรง

กลไกสามประการทำงานร่วมกันเพื่อป้องกันการวนรอบไม่สิ้นสุด ได้แก่ ขีดจำกัดจำนวนรอบสูงสุด การตรวจจับการกระทำซ้ำ และ ระยะหมดเวลา

ตัวป้องกัน 1: ขีดจำกัดจำนวนรอบสูงสุด

ตัวป้องกันที่ง่ายและสำคัญที่สุดคือขีดจำกัดขั้นตอนที่แน่นอน การวนรอบของเอเจนต์ทุกตัวต้องมีขีดจำกัดนี้ เมื่อถึงขีดจำกัด เอเจนต์จะส่งคืนคำตอบปัจจุบันที่ดีที่สุดหรือข้อความแจ้งความล้มเหลวอย่างชัดเจน

MAX_ITERATIONS = 20

def run_agent(query: str) -> dict:
    history = []

    for step in range(1, MAX_ITERATIONS + 1):
        action = decide_action(query, history)

        if action['type'] == 'final_answer':
            return {'status': 'ok', 'answer': action['answer'], 'steps': step}

        result = execute_tool(action['tool'], action['args'])
        history.append({'step': step, 'tool': action['tool'], 'result': result})

    # Hard stop — max iterations reached
    return {
        'status': 'max_iterations_reached',
        'answer': None,
        'steps': MAX_ITERATIONS
    }

ตัวป้องกัน 2: การตรวจจับการกระทำซ้ำ

การกระทำซ้ำเป็นลักษณะสำคัญของการวนรอบไม่สิ้นสุด ให้ติดตามประวัติของคู่ (ชื่อเครื่องมือ, อาร์กิวเมนต์) หากคู่เดิมปรากฏมากกว่า N ครั้ง แสดงว่าเอเจนต์ติดค้าง ให้หยุดการวนรอบและแทรกข้อความข้อผิดพลาด

import hashlib
import json

def action_hash(tool_name: str, args: dict) -> str:
    payload = json.dumps({'tool': tool_name, 'args': args}, sort_keys=True)
    return hashlib.md5(payload.encode()).hexdigest()

def run_agent_with_repeat_detection(query: str) -> dict:
    history = []
    action_counts = {}

    for step in range(1, 21):
        action = decide_action(query, history)
        if action['type'] == 'final_answer':
            return {'status': 'ok', 'answer': action['answer']}

        key = action_hash(action['tool'], action['args'])
        action_counts[key] = action_counts.get(key, 0) + 1

        if action_counts[key] > 2:  # seen this exact action more than twice
            history.append({
                'role': 'system',
                'content': f'You have called {action["tool"]} with the same arguments {action_counts[key]} times. '
                           f'This approach is not working. Try a completely different strategy or state what you know so far.'
            })
            continue

        result = execute_tool(action['tool'], action['args'])
        history.append({'tool': action['tool'], 'result': result})

    return {'status': 'loop_detected', 'answer': None}

การติดตามการกระทำซ้ำในหน้าต่างช่วงหนึ่ง

แทนที่จะติดตามจำนวนสะสมตลอดเวลา ให้ตรวจจับการทำซ้ำในหน้าต่างเลื่อนของ N ขั้นตอนล่าสุด วิธีนี้จะตรวจจับการวนรอบที่เปลี่ยนแปลงเล็กน้อยแต่เป็นวงจรในช่วงเวลาสั้น ๆ ได้

from collections import deque

def is_cycling(recent_actions: deque, window: int = 6) -> bool:
    if len(recent_actions) < window:
        return False

    # Check if the last window/2 actions repeat the first window/2
    half = window // 2
    first_half = list(recent_actions)[:half]
    second_half = list(recent_actions)[half:window]
    return first_half == second_half

# In the agent loop:
# recent_actions = deque(maxlen=6)
# recent_actions.append(action_hash(tool, args))
# if is_cycling(recent_actions):
#     print('Cycling detected — breaking loop')
#     break

recent = deque(['a', 'b', 'a', 'b'], maxlen=6)
print(is_cycling(recent, window=4))  # True — cycling detected

ตัวป้องกัน 3: ระยะหมดเวลาตามเวลาจริงด้วย signal.alarm

บนระบบ Unix signal.alarm() จะทำให้เกิด SIGALRM หลังผ่านจำนวนวินาทีที่ระบุ วิธีนี้ให้ระยะหมดเวลาที่แน่นอน แม้ว่าการวนรอบของเอเจนต์จะติดอยู่ในการเรียกใช้เครื่องมือที่ทำงานช้าก็ตาม

import signal

class AgentTimeout(Exception):
    pass

def timeout_handler(signum, frame):
    raise AgentTimeout('Agent exceeded time limit')

def run_agent_with_signal_timeout(query: str, timeout_seconds: int = 60) -> dict:
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout_seconds)  # set the alarm

    try:
        result = run_core_agent_loop(query)
        signal.alarm(0)  # cancel the alarm on success
        return result
    except AgentTimeout:
        signal.alarm(0)
        return {'status': 'timeout', 'answer': None}
    except Exception as e:
        signal.alarm(0)
        raise

# Note: signal.alarm is Unix-only (Linux/Mac)

ระยะหมดเวลาด้วย threading.Timer (ใช้ได้ข้ามแพลตฟอร์ม)

threading.Timer ทำงานได้บนทุกแพลตฟอร์ม รวมถึง Windows ให้ตั้งค่าสถานะหลังพ้นช่วงเวลาที่กำหนด การวนรอบของเอเจนต์จะตรวจสอบสถานะนี้และออกจากการทำงานหากถูกตั้งค่าไว้

import threading

def run_agent_with_timer_timeout(query: str, timeout_seconds: int = 60) -> dict:
    timed_out = threading.Event()

    def set_timeout():
        timed_out.set()

    timer = threading.Timer(timeout_seconds, set_timeout)
    timer.start()

    history = []
    try:
        for step in range(1, 21):
            if timed_out.is_set():
                return {'status': 'timeout', 'answer': None, 'steps': step}

            action = decide_action(query, history)
            if action['type'] == 'final_answer':
                return {'status': 'ok', 'answer': action['answer']}

            result = execute_tool(action['tool'], action['args'])
            history.append({'tool': action['tool'], 'result': result})

    finally:
        timer.cancel()  # always cancel if done before timeout

    return {'status': 'max_steps', 'answer': None}

ระยะหมดเวลาแบบอะซิงโครนัสด้วย asyncio.wait_for

ในสถาปัตยกรรมเอเจนต์แบบอะซิงโครนัส ให้ใช้ asyncio.wait_for(coroutine, timeout=N) ซึ่งจะทำให้เกิด asyncio.TimeoutError หากโคโรทีนไม่ทำงานเสร็จภายในจำนวนวินาทีที่ระบุ

import asyncio

async def run_async_agent(query: str) -> dict:
    history = []
    for step in range(1, 21):
        action = await async_decide_action(query, history)
        if action['type'] == 'final_answer':
            return {'status': 'ok', 'answer': action['answer']}
        result = await async_execute_tool(action['tool'], action['args'])
        history.append({'tool': action['tool'], 'result': result})
    return {'status': 'max_steps', 'answer': None}

async def run_with_timeout(query: str, timeout: float = 60.0) -> dict:
    try:
        return await asyncio.wait_for(run_async_agent(query), timeout=timeout)
    except asyncio.TimeoutError:
        return {'status': 'timeout', 'answer': None}

# asyncio.run(run_with_timeout('What is Python?', timeout=30.0))

การแทรกคำสั่งให้ออกจากการวนรอบลงในประวัติ

เมื่อพบการวนรอบ อย่าหยุดการทำงานอย่างเงียบ ๆ เพียงอย่างเดียว ให้แทรกข้อความระบบลงในประวัติการสนทนาเพื่ออธิบายสิ่งที่เกิดขึ้นและขอให้เอเจนต์สรุป วิธีนี้เปิดโอกาสให้ LLM สร้างคำตอบสุดท้ายก่อนถูกตัดการทำงาน

def inject_loop_escape(history: list, step: int, reason: str):
    message = (
        f'[SYSTEM] You have been running for {step} steps. Reason: {reason}. '
        f'You MUST now provide a FINAL_ANSWER based on what you have found so far, '
        f'even if the information is incomplete. Do not call any more tools.'
    )
    history.append({'role': 'system', 'content': message})

# In the agent loop, when approaching the limit:
# if step >= MAX_ITERATIONS - 2:
#     inject_loop_escape(history, step, 'approaching max iteration limit')

# Or when a repeat is detected:
# if action_counts[key] > 2:
#     inject_loop_escape(history, step, 'repeated action detected')

if __name__ == '__main__':
    demo_history = []
    inject_loop_escape(demo_history, step=18, reason='approaching max iteration limit')
    print(demo_history[-1]['content'])

การบันทึกเมื่อหยุดการวนรอบ

บันทึกทุกครั้งที่ตัวป้องกันการวนรอบทำงาน วิธีนี้จะสร้างบันทึกว่าเอเจนต์ติดค้างบ่อยเพียงใดและเพราะเหตุใด ซึ่งเป็นข้อมูลที่มีคุณค่าอย่างยิ่งต่อการปรับปรุงคำสั่งและการใช้งานเครื่องมือของคุณ

import logging

logger = logging.getLogger('agent_guard')

def check_and_break_loop(step: int, action_counts: dict, current_key: str) -> bool:
    count = action_counts.get(current_key, 0)

    if count > 2:
        logger.warning(
            f'Infinite loop detected at step {step}. '
            f'Action hash {current_key[:8]} seen {count} times. '
            f'Breaking loop.'
        )
        return True  # signal to break

    if step >= 18:  # approaching limit
        logger.warning(
            f'Approaching max iterations at step {step}. '
            f'Injecting escape prompt.'
        )

    return False

if __name__ == '__main__':
    import sys
    logging.basicConfig(level=logging.WARNING, format='%(message)s', stream=sys.stdout)
    demo_counts = {'search:{"q": "weather"}': 3}
    check_and_break_loop(step=10, action_counts=demo_counts, current_key='search:{"q": "weather"}')

การรวมตัวป้องกันทั้งสามแบบ

เอเจนต์ในระบบจริงควรรวมตัวป้องกันทั้งสามแบบเข้าด้วยกัน ได้แก่ จำนวนรอบสูงสุด (จำเป็นต้องมี) การตรวจจับการทำซ้ำ (ตรวจจับวงจร) และระยะหมดเวลา (ตรวจจับการเรียกใช้เครื่องมือที่ค้างอยู่) เมื่อทำงานร่วมกัน ทั้งสามแบบจะทำให้การวนรอบมีความแข็งแกร่งอย่างมาก

import threading
import hashlib
import json

def run_production_agent(query: str) -> dict:
    MAX_STEPS = 20
    TIMEOUT_SEC = 120

    timed_out = threading.Event()
    timer = threading.Timer(TIMEOUT_SEC, timed_out.set)
    timer.start()

    history = []
    action_counts = {}

    try:
        for step in range(1, MAX_STEPS + 1):
            if timed_out.is_set():
                return {'status': 'timeout'}

            action = decide_action(query, history)
            if action['type'] == 'final_answer':
                return {'status': 'ok', 'answer': action['answer']}

            key = hashlib.md5(json.dumps(action, sort_keys=True).encode()).hexdigest()
            action_counts[key] = action_counts.get(key, 0) + 1
            if action_counts[key] > 2:
                inject_loop_escape(history, step, 'repeat detected')
                continue

            result = execute_tool(action['tool'], action['args'])
            history.append({'tool': action['tool'], 'result': result})
    finally:
        timer.cancel()

    return {'status': 'max_steps'}

การทดสอบตัวป้องกันการวนรอบด้วยการทดสอบหน่วย

เขียนการทดสอบหน่วยโดยเฉพาะสำหรับตัวป้องกันการวนรอบ สร้างเอเจนต์จำลองที่เรียกใช้เครื่องมือเดิมเสมอ และตรวจสอบว่าตัวป้องกันตรวจจับได้ภายในจำนวนขั้นตอนที่คาดไว้

from unittest.mock import MagicMock

def test_repeat_detection_breaks_loop():
    # Create a mock that always returns the same action
    always_same_action = MagicMock(return_value={
        'type': 'tool',
        'tool': 'search_web',
        'args': {'query': 'same query'}
    })
    always_success = MagicMock(return_value='some result')

    result = run_agent_with_repeat_detection(
        query='test',
        decide_action=always_same_action,
        execute_tool=always_success
    )

    # Should stop due to loop detection, not run all 20 steps
    assert result['status'] in ('loop_detected', 'max_iterations_reached')
    # Should not have run all 20 steps (loop should be detected by step 6-7)
    print('Loop guard test passed')

ทดสอบความรู้: การตรวจจับการวนรอบไม่สิ้นสุด

ทดสอบความเข้าใจของคุณเกี่ยวกับการตรวจจับและหยุดการวนรอบไม่สิ้นสุด

สรุป: การตรวจจับและหยุดการวนรอบไม่สิ้นสุด

ขณะนี้คุณสามารถป้องกันเอเจนต์จากการวนรอบไม่สิ้นสุดด้วยตัวป้องกันเสริมกันสามแบบได้แล้ว:

  • จำนวนรอบสูงสุด: จำกัดขั้นตอนอย่างตายตัว ซึ่งจำเป็นต้องมีในการวนรอบของเอเจนต์ทุกแบบ
  • การตรวจจับการทำซ้ำ: คำนวณแฮชของคู่ (เครื่องมือ, อาร์กิวเมนต์) และหยุดเมื่อพบมากกว่า N ครั้ง
  • ระยะหมดเวลา: signal.alarm() บน Unix, threading.Timer สำหรับการใช้งานข้ามแพลตฟอร์ม และ asyncio.wait_for() สำหรับงานแบบอะซิงโครนัส
  • การแทรกคำสั่งให้ออกจากการวนรอบ: เปิดโอกาสให้เอเจนต์สรุปก่อนหยุดการทำงานอย่างเด็ดขาด
  • บันทึกทุกครั้งที่ตัวป้องกันทำงาน เพราะเป็นข้อมูลที่มีคุณค่าต่อการปรับปรุงคำสั่ง

คำถามที่พบบ่อย

บทเรียน “การตรวจจับและหยุดลูปไม่สิ้นสุด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การตรวจจับและหยุดลูปไม่สิ้นสุด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การตรวจจับและหยุดลูปไม่สิ้นสุด”

ตัวป้องกันจำนวนรอบสูงสุด การตรวจจับการดำเนินการซ้ำ และตัวตัดวงจรลูป คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การตรวจจับและหยุดลูปไม่สิ้นสุด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม

ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. ความล้มเหลวทั่วไปของลูปตัวแทน
  2. การบันทึกการติดตามขั้นตอนของตัวแทน
  3. การตรวจจับและหยุดลูปไม่สิ้นสุด
  4. เทคนิคการแก้ไขข้อบกพร่องแบบเดินทีละขั้น
← กลับไปที่ AI Agents