Ejen AI · Pelajaran

Mengesan dan Memutuskan Gelung Tanpa Penghujung

Pengawal lelaran maksimum, pengesanan tindakan berulang dan pemutus litar gelung.

Pelajaran 3 daripada 413 langkah

Mengesan dan Memutuskan Gelung Tanpa Penghujung ialah pelajaran Ejen AI percuma di CoddyKit. Ini ialah pelajaran 3 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Ejen AI, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Ejen AI merangkumi sejumlah 4 pelajaran.

Ancaman Gelung Tanpa Kesudahan

Ejen yang berada dalam gelung tanpa kesudahan membazirkan token, menyekat sumber dan tidak menghasilkan hasil yang berguna. Dalam persekitaran pengeluaran, keadaan ini secara langsung bermakna pembaziran wang dan kekecewaan pengguna.

Tiga mekanisme bekerjasama untuk menghalang gelung tanpa kesudahan: had bilangan lelaran maksimum, pengesanan tindakan berulang dan had masa.

Perlindungan 1: Had Bilangan Lelaran Maksimum

Perlindungan yang paling mudah dan penting ialah had langkah yang tetap. Setiap gelung ejen mesti mempunyainya. Apabila had dicapai, ejen sama ada mengembalikan jawapan semasa terbaiknya atau mesej kegagalan yang jelas.

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
    }

Perlindungan 2: Mengesan Tindakan Berulang

Tindakan berulang ialah ciri utama gelung tanpa kesudahan. Jejaki sejarah pasangan (nama alat, argumen). Jika pasangan yang sama muncul lebih daripada N kali, ejen tersekat — hentikan gelung dan suntik mesej ralat.

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}

Menjejak Tindakan Berulang dalam Tetingkap

Daripada menjejak kiraan sepanjang masa, kesan pengulangan dalam tetingkap gelongsor yang mengandungi N langkah terakhir. Kaedah ini mengesan gelung yang berubah sedikit tetapi berulang dalam kitaran singkat.

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

Perlindungan 3: Had Masa Sebenar dengan signal.alarm

Pada sistem Unix, signal.alarm() mencetuskan SIGALRM selepas bilangan saat yang ditetapkan. Ini memberikan had masa yang tetap walaupun gelung ejen tersekat dalam panggilan alat yang perlahan.

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)

Had Masa dengan threading.Timer untuk Semua Platform

threading.Timer berfungsi pada semua platform termasuk Windows. Tetapkan bendera selepas tempoh had masa — gelung ejen menyemak bendera itu dan keluar jika bendera telah ditetapkan.

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}

Had Masa Tak Segerak dengan asyncio.wait_for

Dalam seni bina ejen tak segerak, gunakan asyncio.wait_for(coroutine, timeout=N). Fungsi ini mencetuskan asyncio.TimeoutError jika rutin bersama tidak selesai dalam tempoh saat yang ditetapkan.

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))

Menyuntik Arahan Keluar ke dalam Sejarah

Apabila gelung dikesan, jangan hanya menghentikannya secara senyap. Suntik mesej sistem ke dalam sejarah perbualan yang menerangkan perkara yang berlaku dan meminta ejen membuat kesimpulan. Ini memberi LLM peluang untuk menghasilkan jawapan akhir sebelum dihentikan.

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'])

Pengelogan Apabila Gelung Diputuskan

Sentiasa rekodkan apabila perlindungan gelung dicetuskan. Ini menghasilkan rekod tentang kekerapan dan sebab ejen tersekat — data yang amat berguna untuk menambah baik arahan anda dan pelaksanaan alat.

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"}')

Menggabungkan Ketiga-tiga Perlindungan

Ejen dalam persekitaran pengeluaran patut menggabungkan ketiga-tiga perlindungan: bilangan lelaran maksimum (wajib), pengesanan ulangan (mengesan kitaran) dan had masa (mengendalikan panggilan alat yang menyekat). Kesemuanya menjadikan gelung sangat teguh.

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'}

Menguji Perlindungan Gelung dengan Ujian Unit

Tulis ujian unit khusus untuk perlindungan gelung anda. Cipta ejen olok-olok yang sentiasa memanggil alat yang sama dan sahkan bahawa perlindungan tersebut mengesannya dalam bilangan langkah yang dijangka.

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')

Semakan Pengetahuan: Mengesan Gelung Tanpa Kesudahan

Uji pemahaman anda tentang teknik mengesan dan memutuskan gelung tanpa kesudahan.

Imbas Kembali: Mengesan dan Memutuskan Gelung Tanpa Kesudahan

Anda kini boleh melindungi ejen daripada gelung tanpa kesudahan dengan tiga perlindungan yang saling melengkapi:

  • Bilangan lelaran maksimum: Had langkah tetap yang wajib ada dalam setiap gelung ejen
  • Pengesanan ulangan: Kira nilai cincang bagi pasangan (alat, argumen) dan hentikan gelung apabila pasangan itu dilihat lebih daripada N kali
  • Had masa: signal.alarm() pada Unix, threading.Timer merentas platform dan asyncio.wait_for() untuk operasi tak segerak
  • Suntikan arahan keluar: Beri ejen peluang untuk membuat kesimpulan sebelum dihentikan secara paksa
  • Sentiasa rekodkan apabila perlindungan dicetuskan — data ini bernilai untuk menambah baik arahan
Percuma untuk bermula

Pelajari Ejen AI dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
60
Pelajaran
239

Soalan Lazim

Adakah pelajaran “Mengesan dan Memutuskan Gelung Tanpa Penghujung” percuma?

Ya — teks penuh “Mengesan dan Memutuskan Gelung Tanpa Penghujung” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Ejen AI, tingkat taraf kepada CoddyKit PRO. Kursus Ejen AI merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Mengesan dan Memutuskan Gelung Tanpa Penghujung”?

Pengawal lelaran maksimum, pengesanan tindakan berulang dan pemutus litar gelung. Anda berlatih Ejen AI menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Ejen AI?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Ejen AI di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 3 daripada 4.

Berapa lamakah pelajaran “Mengesan dan Memutuskan Gelung Tanpa Penghujung” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Ejen AI ini?

Ya. Setiap pelajaran Ejen AI menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. Kegagalan Gelung Ejen yang Lazim
  2. Pengelogan Jejak untuk Langkah Ejen
  3. Mengesan dan Memutuskan Gelung Tanpa Penghujung
  4. Teknik Penyahpepijatan Langkah demi Langkah
← Kembali ke Ejen AI