0Pricing
AI Engineering Academy · บทเรียน

การจัดการสถานะระหว่างขั้นตอนการเรียกใช้

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

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

ปัญหาสถานะไม่คงอยู่

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

# Iteration 1 - works fine
df = pd.read_csv('data.csv')  # df is in memory
df_cleaned = df.dropna()
print('Rows after cleaning:', len(df_cleaned))

# Iteration 2 - NEW container, df_cleaned is GONE
result = df_cleaned.groupby('category').sum()  # NameError: df_cleaned is not defined
print(result)  # This will fail!

การคงสถานะด้วยไฟล์

แนวทางที่ง่ายและนำไปใช้ได้หลากหลายที่สุดคือการบันทึกสถานะลงในไฟล์ เมื่อจบแต่ละบล็อกโค้ด เอเจนต์จะบันทึก DataFrames พจนานุกรม หรือออบเจ็กต์อื่น ๆ ลงในไฟล์ในไดเรกทอรีพื้นที่ทำงาน จากนั้นการทำงานรอบถัดไปจะโหลดไฟล์เหล่านั้นกลับมา Parquet เหมาะอย่างยิ่งสำหรับ DataFrames, JSON เหมาะสำหรับพจนานุกรม และ pickle เหมาะสำหรับออบเจ็กต์ Python ทั่วไป (แม้ว่า pickle จากโค้ดที่ไม่น่าเชื่อถือจะมีความเสี่ยงด้านความปลอดภัย)

import pandas as pd
import json
from pathlib import Path

WORKSPACE = Path('/workspace')

# Iteration 1: process and SAVE
df = pd.read_csv(WORKSPACE / 'raw_data.csv')
df_cleaned = df.dropna().reset_index(drop=True)
df_cleaned.to_parquet(WORKSPACE / 'cleaned.parquet')  # save for next iteration

stats = {'rows': len(df_cleaned), 'columns': list(df_cleaned.columns)}
with open(WORKSPACE / 'stats.json', 'w') as f:
    json.dump(stats, f)

print('Saved cleaned data:', len(df_cleaned), 'rows')

# Iteration 2: LOAD and continue
df_cleaned = pd.read_parquet(WORKSPACE / 'cleaned.parquet')  # restore state
with open(WORKSPACE / 'stats.json') as f:
    stats = json.load(f)
result = df_cleaned.groupby('category')['value'].sum()
print(result)

การสอนเอเจนต์ให้ใช้สถานะที่เก็บในไฟล์

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

STATE_MANAGEMENT_INSTRUCTIONS = '''
State persistence rules:
- You have a persistent workspace at /workspace/
- After computing any result you will need later, SAVE it to /workspace/
  - DataFrames: use .to_parquet('/workspace/name.parquet')
  - Dicts/lists: use json.dump to /workspace/name.json
  - Text: write to /workspace/name.txt
- At the start of each code block, LOAD the files you need from previous steps
- Use clear, descriptive filenames like 'cleaned_data.parquet', not 'tmp1.parquet'
- When listing files, use: import os; print(os.listdir('/workspace/'))
'''

สถานะระดับเคอร์เนลด้วยกระบวนการ Python ที่คงอยู่

ทางเลือกแทนสถานะที่เก็บในไฟล์คือการคง กระบวนการ Python ที่ทำงานต่อเนื่อง (เช่น เคอร์เนล Jupyter) ไว้ระหว่างการทำงานแต่ละรอบ แล้วส่งบล็อกโค้ดแต่ละบล็อกเข้าไปด้วย exec() ตัวแปรที่กำหนดในรอบหนึ่งจะยังเข้าถึงได้ในรอบถัดไป แนวทางนี้เร็วกว่าและเป็นธรรมชาติมากกว่า แต่จำเป็นต้องเรียกใช้กระบวนการที่ทำงานต่อเนื่องแยกกันสำหรับแต่ละเซสชันของเอเจนต์ แทนการใช้คอนเทนเนอร์ชั่วคราว

import jupyter_client

class PersistentKernel:
    def __init__(self):
        km, self.kc = jupyter_client.manager.start_new_kernel(kernel_name='python3')
        self.kc.wait_for_ready(timeout=30)
        print('Kernel started')

    def execute(self, code: str, timeout=60) -> tuple[str, str]:
        msg_id = self.kc.execute(code)
        outputs, errors = [], []
        
        while True:
            msg = self.kc.get_iopub_msg(timeout=timeout)
            if msg['msg_type'] == 'stream':
                if msg['content']['name'] == 'stdout':
                    outputs.append(msg['content']['text'])
                else:
                    errors.append(msg['content']['text'])
            if msg['msg_type'] == 'status' and msg['content']['execution_state'] == 'idle':
                break
        
        return ''.join(outputs), ''.join(errors)

    def shutdown(self):
        self.kc.shutdown()

# Variables persist across execute() calls!
kernel = PersistentKernel()
kernel.execute('x = 42')  # x is now defined in kernel
output, _ = kernel.execute('print(x)')  # prints 42 - state persists!
kernel.shutdown()

การจัดการสถานะด้วยออบเจ็กต์สถานะ

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

from dataclasses import dataclass, field
from typing import Any

@dataclass
class ExecutionState:
    task: str
    iteration: int = 0
    workspace_files: list[str] = field(default_factory=list)
    computed_values: dict[str, Any] = field(default_factory=dict)
    completed_steps: list[str] = field(default_factory=list)
    last_output: str = ''

    def to_context_summary(self) -> str:
        return f'''Current task: {self.task}
Iteration: {self.iteration}
Completed steps: {', '.join(self.completed_steps) or 'None yet'}
Workspace files: {', '.join(self.workspace_files) or 'None yet'}
Key values: {self.computed_values}
Last output: {self.last_output[:500]}'''

    def after_execution(self, output: str, step_name: str):
        import os
        self.workspace_files = os.listdir('/workspace')
        self.completed_steps.append(step_name)
        self.last_output = output
        self.iteration += 1

การแทรกบริบทสถานะลงในพรอมต์

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

def build_iteration_prompt(task: str, state: ExecutionState, last_observation: str) -> str:
    return f'''ORIGINAL TASK: {task}

CURRENT STATE:
{state.to_context_summary()}

LAST EXECUTION OUTPUT:
{last_observation}

What is the next step? Write Python code to continue. 
Remember:
- Load data from workspace files if needed
- Save any results you will need in future steps
- If the task is complete, say TASK COMPLETE and summarize the result'''

# Use in the main loop
for step_num in range(max_iterations):
    context = build_iteration_prompt(task, state, last_observation)
    response = llm.complete(messages + [{'role': 'user', 'content': context}])
    code = extract_code_block(response)
    if not code:
        break  # done
    output, err = execute_in_sandbox(code)
    state.after_execution(output, step_name=f'step_{step_num}')
    last_observation = format_observation(output, err)

การจัดการข้อมูลระหว่างการประมวลผลขนาดใหญ่

เอเจนต์วิเคราะห์ข้อมูลมักสร้างชุดข้อมูลระหว่างการประมวลผลขนาดใหญ่ ซึ่งมีค่าใช้จ่ายสูงเมื่อต้องโหลดใหม่ในทุกการทำงาน ใช้ การโหลดแบบขี้เกียจ โดยโหลดเฉพาะข้อมูลที่จำเป็นสำหรับขั้นตอนปัจจุบัน ใช้รูปแบบข้อมูลแบบคอลัมน์ เช่น Parquet ซึ่งรองรับการอ่านเฉพาะคอลัมน์อย่างมีประสิทธิภาพ สำหรับชุดข้อมูลที่มีขนาดใหญ่มากจริง ๆ (100MB ขึ้นไป) ให้ใช้เคอร์เนลที่คงอยู่ เพื่อให้ DataFrame อยู่ในหน่วยความจำข้ามการทำงานแต่ละรอบ แทนการแปลงเป็นข้อมูลอนุกรมและแปลงกลับทุกครั้ง

# Good: load only needed columns
df = pd.read_parquet('/workspace/full_data.parquet', columns=['date', 'revenue', 'region'])

# Bad: load everything even if you only need 2 columns
# df = pd.read_parquet('/workspace/full_data.parquet')  # loads 50 columns you don't need

# Good: filter early before loading full dataset
df = pd.read_parquet('/workspace/full_data.parquet', filters=[('region', '=', 'EMEA')])

# For very large files, tell the LLM about data shape upfront
df_info = {'shape': (1_000_000, 50), 'size_mb': 850, 'columns': [...]}
# Include df_info in state so LLM plans accordingly

การสร้างจุดตรวจสอบสำหรับงานที่ทำงานต่อเนื่องนาน

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

import json
import time

def checkpoint_state(state: ExecutionState, checkpoint_id: str, db):
    data = {
        'task': state.task,
        'iteration': state.iteration,
        'completed_steps': state.completed_steps,
        'workspace_files': state.workspace_files,
        'timestamp': time.time()
    }
    db.execute(
        'INSERT INTO agent_checkpoints (id, state) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET state=excluded.state',
        (checkpoint_id, json.dumps(data))
    )
    print(f'Checkpoint saved at iteration {state.iteration}')

def resume_from_checkpoint(checkpoint_id: str, db) -> ExecutionState | None:
    row = db.execute('SELECT state FROM agent_checkpoints WHERE id = ?', (checkpoint_id,)).fetchone()
    if not row:
        return None
    data = json.loads(row[0])
    state = ExecutionState(task=data['task'])
    state.iteration = data['iteration']
    state.completed_steps = data['completed_steps']
    print(f'Resumed from iteration {state.iteration}')
    return state

การล้างสถานะหลังเสร็จสิ้น

พื้นที่ทำงานของเอเจนต์จะสะสมไฟล์และอาจมีขนาดใหญ่ขึ้นตามเวลา ให้ใช้ ขั้นตอนการล้างข้อมูลที่ทำงานเมื่องานเสร็จสมบูรณ์หรือล้มเหลวเสมอ โดยลบไฟล์ระหว่างการประมวลผล (cleaned.parquet, tmp_output.csv) และเก็บไว้เฉพาะไฟล์ผลลัพธ์สุดท้ายที่ผู้ใช้ต้องการ สำหรับพื้นที่จัดเก็บบนคลาวด์ ให้ตั้งนโยบายวงจรชีวิตที่ลบไฟล์ในพื้นที่ทำงานโดยอัตโนมัติหลังพ้นระยะเวลาเก็บรักษา

import shutil
from pathlib import Path

INTERMEDIATE_PATTERNS = ['*.parquet', 'tmp_*.csv', 'step_*.json', 'debug_*.txt']
FINAL_OUTPUT_PATTERNS = ['report.pdf', 'final_*.csv', 'summary.json']

def cleanup_workspace(workspace_dir: str, keep_final=True):
    workspace = Path(workspace_dir)
    final_outputs = []
    
    if keep_final:
        for pattern in FINAL_OUTPUT_PATTERNS:
            final_outputs.extend(workspace.glob(pattern))
        # Move final outputs to output directory
        output_dir = workspace.parent / 'outputs'
        output_dir.mkdir(exist_ok=True)
        for f in final_outputs:
            shutil.move(str(f), str(output_dir / f.name))
    
    # Delete the workspace
    shutil.rmtree(workspace_dir)
    print(f'Workspace cleaned up. Kept {len(final_outputs)} output files.')
    return [str(f) for f in final_outputs]

การกำหนดเวอร์ชันสถานะเพื่อการดีบัก

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

หน้าต่างบริบทเทียบกับสถานะภายนอก

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

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจเกี่ยวกับการจัดการสถานะข้ามขั้นตอนการประมวลผลโค้ดจากบทเรียนนี้

สรุปบทเรียน

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

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

บทเรียน “การจัดการสถานะระหว่างขั้นตอนการเรียกใช้” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การจัดการสถานะระหว่างขั้นตอนการเรียกใช้”

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

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

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

บทเรียน “การจัดการสถานะระหว่างขั้นตอนการเรียกใช้” ใช้เวลานานแค่ไหน

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

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

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

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

  1. ลูปการเรียกใช้โค้ด
  2. การทำแซนด์บ็อกซ์ด้วย Docker และ RestrictedPython
  3. การจัดการสถานะระหว่างขั้นตอนการเรียกใช้
  4. การสร้างเอเจนต์วิเคราะห์ข้อมูล
← กลับไปที่ AI Engineering Academy