Skip to content

sun-k-like/LangGraph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

16 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

LangGraph HITL ์Šน์ธ ์‹œ์Šคํ…œ & ์ œ์กฐ QA ์ ์šฉ

๐ŸŽฏ LangGraph์˜ Human-in-the-Loop ํŒจํ„ด์„ ํ•™์Šตํ•˜๊ณ  ์ œ์กฐ/์—๋„ˆ์ง€ ํ˜„์žฅ ํ’ˆ์งˆ ๊ด€๋ฆฌ์— ์ ์šฉํ•œ ํ”„๋กœ์ ํŠธ

Python LangGraph License


๐Ÿ“‹ ๋ชฉ์ฐจ


๐ŸŽฏ ๊ฐœ์š”

์ด ํ”„๋กœ์ ํŠธ๋Š” LangGraph์˜ HITL(Human-in-the-Loop) ํŒจํ„ด์„ ํ•™์Šตํ•˜๊ณ , ์ด๋ฅผ ์ œ์กฐ/์—๋„ˆ์ง€ ํ˜„์žฅ์˜ ํ’ˆ์งˆ ๊ด€๋ฆฌ(QA) ์‹œ์Šคํ…œ์— ์ ์šฉํ•œ ์‹ค์Šต ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์™œ HITL์ธ๊ฐ€?

ํ˜„๋Œ€ ์ œ์กฐ ํ˜„์žฅ์—์„œ๋Š” AI๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜์ง€๋งŒ, ์ค‘์š”ํ•œ ๊ฒฐ์ •์€ ์—ฌ์ „ํžˆ ์ธ๊ฐ„ ์ „๋ฌธ๊ฐ€์˜ ํŒ๋‹จ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ:

  • ๐Ÿญ ๊ฐ€๋™ ์ค‘์ง€ ๊ฒฐ์ •: AI๊ฐ€ ๊ณผ์—ด์„ ๊ฐ์ง€ํ–ˆ์–ด๋„, ์ „๋ฌธ๊ฐ€๊ฐ€ ์ตœ์ข… ์Šน์ธ
  • โš ๏ธ ์•ˆ์ „ ํ”„๋กœํ† ์ฝœ: ์œ„ํ—˜ํ•œ ์กฐ์น˜๋Š” ๋ฐ˜๋“œ์‹œ ์ธ๊ฐ„ ๊ฒ€ํ†  ํ•„์š”
  • ๐Ÿ”ง ์œ ์ง€๋ณด์ˆ˜ ์Šค์ผ€์ค„๋ง: AI ๊ถŒ์žฅ ์กฐ์น˜๋ฅผ ์ „๋ฌธ๊ฐ€๊ฐ€ ํ˜„์žฅ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐ์ •

ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ

1. โœ… LangGraph์˜ `interrupt()` ๋ฐ `Command` ํŒจํ„ด ์ดํ•ด
2. โœ… ๊ธฐ๋ณธ ์Šน์ธ ์›Œํฌํ”Œ๋กœ์šฐ ๊ตฌํ˜„ (`lg_approval.py`)
3. โœ… ์‹ค์ œ ์ œ์กฐ ํ˜„์žฅ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ์šฉ (`lg_app_qa.py`)
4. โœ… ๋™๊ธฐ/๋น„๋™๊ธฐ ์‹คํ–‰ ๋ชจ๋“œ ๋น„๊ต ํ•™์Šต

๐Ÿš€ ์ฃผ์š” ๊ธฐ๋Šฅ

1๏ธโƒฃ ๊ธฐ๋ณธ ์Šน์ธ ์‹œ์Šคํ…œ (lg_approval.py)

# ์Šน์ธ/๊ฑฐ๋ถ€์— ๋”ฐ๋ฅธ ์กฐ๊ฑด๋ถ€ ๋ผ์šฐํŒ…
approved = interrupt({"question": "์ด ์ž‘์—…์„ ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"})

if approved:
    return Command(goto="do", update={"status": "approved"})
else:
    return Command(goto="cancel", update={"status": "rejected"})

ํŠน์ง•:

  • ๐Ÿ’ก interrupt()๋ฅผ ํ†ตํ•œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋Œ€๊ธฐ
  • ๐Ÿ”€ Command๋กœ ๋™์  ๋ผ์šฐํŒ… (์Šน์ธ โ†’ do, ๊ฑฐ๋ถ€ โ†’ cancel)
  • โšก ๋™๊ธฐ/๋น„๋™๊ธฐ ์‹คํ–‰ ๋ชจ๋“œ ์ง€์›
  • ๐Ÿ› ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆฌ๋ฐ ๋””๋ฒ„๊น… ํฌํ•จ

2๏ธโƒฃ ์ œ์กฐ QA ์‹œ์Šคํ…œ (lg_app_qa.py)

# ์œ„ํ—˜๋„์— ๋”ฐ๋ฅธ ์กฐ๊ฑด๋ถ€ ์Šน์ธ
if risk_level == "LOW":
    return Command(goto="execute_action", update={"human_approval": True})
else:
    # HIGH/CRITICAL์€ ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ†  ํ•„์ˆ˜
    approval_data = interrupt({
        "type": "expert_approval_required",
        "risk_level": risk_level,
        "sensor_data": sensor_data
    })

ํŠน์ง•:

  • ๐Ÿค– AI ์„ผ์„œ ๋ถ„์„: ์˜จ๋„/์••๋ ฅ/์ง„๋™/์œ ๋Ÿ‰/์ „๋ ฅ ๋ชจ๋‹ˆํ„ฐ๋ง
  • ๐Ÿ“Š ์œ„ํ—˜๋„ ์ž๋™ ๋ถ„๋ฅ˜: LOW/HIGH/CRITICAL
  • ๐Ÿ‘ค ์กฐ๊ฑด๋ถ€ ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ† :
    • LOW โ†’ ์ž๋™ ์Šน์ธ (์ „๋ฌธ๊ฐ€ ๊ฐœ์ž… ๋ถˆํ•„์š”)
    • HIGH/CRITICAL โ†’ ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ†  ํ•„์ˆ˜
  • โœ๏ธ ์ „๋ฌธ๊ฐ€ Override: AI๊ฐ€ "๊ธด๊ธ‰ ์ค‘์ง€"๋ฅผ ๊ถŒ์žฅํ•ด๋„ ์ „๋ฌธ๊ฐ€๊ฐ€ "๋ถ€ํ•˜ ๊ฐ์†Œ"๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
  • ๐ŸŽฌ 4๊ฐ€์ง€ ์‹œ๋‚˜๋ฆฌ์˜ค: ์ •์ƒ/๊ณผ์—ด/์••๋ ฅ๊ธ‰์ฆ/์ง„๋™์ด์ƒ
  • ๐Ÿ“ ์™„์ „ํ•œ ๊ฐ์‚ฌ ์ถ”์ : ํƒ€์ž„์Šคํƒฌํ”„, ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ, ์‹คํ–‰ ์กฐ์น˜ ๊ธฐ๋ก

๐Ÿ›  ์„ค์น˜ ๋ฐ ์‹คํ–‰

1. ํ™˜๊ฒฝ ์„ค์ •

# Python 3.10 ์ด์ƒ ํ•„์š”
python --version

# ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ (๊ถŒ์žฅ)
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# ์˜์กด์„ฑ ์„ค์น˜
pip install langgraph langchain-core typing-extensions

2. ๊ธฐ๋ณธ ์Šน์ธ ์‹œ์Šคํ…œ ์‹คํ–‰

python lg_approval.py

๋Œ€ํ™”ํ˜• ํ”„๋กฌํ”„ํŠธ:

์‹คํ–‰ ๋ชจ๋“œ๋ฅผ ์„ ํƒํ•˜์„ธ์š”:
1. ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹ (๊ถŒ์žฅ)
2. ๋™๊ธฐ ๋ฐฉ์‹ (๊ฐ„๋‹จ)

์„ ํƒ (1 or 2): 2

=== ์ฒซ ๋ฒˆ์งธ ์‹คํ–‰ ===
์ƒˆ๋กœ์šด ์„ธ์…˜ ID: abc123...

์งˆ๋ฌธ: ์ด ์ž‘์—…์„ ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?
์ž‘์—…: ์„œ๋ฒ„ ์žฌ๋ถ€ํŒ…

์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (yes/no): yes

=== 'True'๋กœ ์žฌ๊ฐœ ===
--- ์ž‘์—… ์‹คํ–‰: ์„œ๋ฒ„ ์žฌ๋ถ€ํŒ… ---
์ตœ์ข… ์ƒํƒœ: completed

3. ์ œ์กฐ QA ์‹œ์Šคํ…œ ์‹คํ–‰

```bash
python lg_app_qa.py
```

๋Œ€ํ™”ํ˜• ํ”„๋กฌํ”„ํŠธ:

๐Ÿญ ์ œ์กฐ/์—๋„ˆ์ง€ ํ˜„์žฅ HITL ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ

    ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์„ ํƒํ•˜์„ธ์š”:
    1. normal - ์ •์ƒ ์ž‘๋™
    2. overheating - ๊ณผ์—ด ๊ฐ์ง€
    3. pressure_spike - ์••๋ ฅ ๊ธ‰์ฆ
    4. vibration_anomaly - ์ง„๋™ ์ด์ƒ

  ์„ ํƒ (1-4): 2
  
  ์‹คํ–‰ ๋ชจ๋“œ:
    1. ๋™๊ธฐ ๋ฐฉ์‹ (๊ถŒ์žฅ)
    2. ๋น„๋™๊ธฐ ๋ฐฉ์‹

  ์„ ํƒ (1-2): 1

    ############################################################
    ๐Ÿญ ์„ค๋น„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘
    ############################################################
    ์„ค๋น„ ID: PLANT-A1B2C3D4
    ์‹œ๋‚˜๋ฆฌ์˜ค: overheating
    ############################################################
    
    ๐Ÿค– AI ๋ถ„์„ ์™„๋ฃŒ - 2025-02-10 14:30:25
    ============================================================
    ์œ„ํ—˜๋„: CRITICAL
    ๋ถ„์„ ๊ฒฐ๊ณผ:
    ๐Ÿšจ ๊ธด๊ธ‰ ์ƒํ™ฉ ๊ฐ์ง€!
    ์˜จ๋„ ์œ„ํ—˜: 95.8ยฐC (์ž„๊ณ„๊ฐ’: 90.0ยฐC)
    ๊ถŒ์žฅ ์กฐ์น˜: IMMEDIATE_SHUTDOWN
    ============================================================

    โธ๏ธ  ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ†  ๋Œ€๊ธฐ ์ค‘...

    ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (yes/no): yes
    ์˜๊ฒฌ์„ ์ž…๋ ฅํ•˜์„ธ์š”: ํ˜„์žฅ ํ™•์ธ ์™„๋ฃŒ, ์ฆ‰์‹œ ๊ฐ€๋™ ์ค‘์ง€ ์Šน์ธ
    
    โš™๏ธ ์กฐ์น˜ ์‹คํ–‰: IMMEDIATE_SHUTDOWN
    ๐Ÿ›‘ ๊ธด๊ธ‰ ๊ฐ€๋™ ์ค‘์ง€ ์‹คํ–‰
    ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ: ํ˜„์žฅ ํ™•์ธ ์™„๋ฃŒ, ์ฆ‰์‹œ ๊ฐ€๋™ ์ค‘์ง€ ์Šน์ธ
    
    ############################################################
    ๐Ÿ“‹ ์ตœ์ข… ๋ฆฌํฌํŠธ
    ############################################################
    ์„ค๋น„ ID: PLANT-A1B2C3D4
    ์œ„ํ—˜๋„: CRITICAL
    ์ „๋ฌธ๊ฐ€ ์Šน์ธ: โœ… ์˜ˆ
    ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ: ํ˜„์žฅ ํ™•์ธ ์™„๋ฃŒ, ์ฆ‰์‹œ ๊ฐ€๋™ ์ค‘์ง€ ์Šน์ธ
    ์‹คํ–‰๋œ ์กฐ์น˜: IMMEDIATE_SHUTDOWN
    ํƒ€์ž„์Šคํƒฌํ”„: 2025-02-10T14:30:25.123456
    ############################################################
    ```

---

## ๐Ÿ’ก ์‚ฌ์šฉ ์˜ˆ์‹œ

### ์˜ˆ์‹œ 1: ์ •์ƒ ์ž‘๋™ (์ž๋™ ์Šน์ธ)

    ```bash
    # ์‹œ๋‚˜๋ฆฌ์˜ค 1 ์„ ํƒ
    ์„ ํƒ (1-4): 1

    ๐Ÿค– AI ๋ถ„์„: ์˜จ๋„ 72.5ยฐC โ†’ ์œ„ํ—˜๋„ LOW
    โœ… ์œ„ํ—˜๋„ ๋‚ฎ์Œ - ์ž๋™ ์Šน์ธ
    ๐Ÿ‘๏ธ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ณ„์†
    ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ: ์ž๋™ ์Šน์ธ (์ •์ƒ ๋ฒ”์œ„)
    
    โœ… ์ •์ƒ ์ž‘๋™ - ์ „๋ฌธ๊ฐ€ ๊ฐœ์ž… ๋ถˆํ•„์š”
    ```

### ์˜ˆ์‹œ 2: ๊ณผ์—ด ๊ฐ์ง€ (์ „๋ฌธ๊ฐ€ ์Šน์ธ)

    ```bash
    # ์‹œ๋‚˜๋ฆฌ์˜ค 2 ์„ ํƒ
    ์„ ํƒ (1-4): 2
    
    ๐Ÿค– AI ๋ถ„์„: ์˜จ๋„ 95.8ยฐC โ†’ ์œ„ํ—˜๋„ CRITICAL
    โธ๏ธ  ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ†  ๋Œ€๊ธฐ ์ค‘...
    
    ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (yes/no): yes
    ์˜๊ฒฌ: ๊ธด๊ธ‰ ๊ฐ€๋™ ์ค‘์ง€ ์Šน์ธ
    
    โš™๏ธ ์กฐ์น˜ ์‹คํ–‰: IMMEDIATE_SHUTDOWN
    ๐Ÿ›‘ ๊ธด๊ธ‰ ๊ฐ€๋™ ์ค‘์ง€ ์‹คํ–‰
    ```

### ์˜ˆ์‹œ 3: ์ „๋ฌธ๊ฐ€ Override

    ```bash
    # ์‹œ๋‚˜๋ฆฌ์˜ค 2 ์„ ํƒ
    ์„ ํƒ (1-4): 2

    ๐Ÿค– AI ๋ถ„์„: ์˜จ๋„ 95.8ยฐC โ†’ ์œ„ํ—˜๋„ CRITICAL
    AI ๊ถŒ์žฅ ์กฐ์น˜: IMMEDIATE_SHUTDOWN
    
    ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (yes/no): yes
    ์˜๊ฒฌ: ๋ถ€ํ•˜๋งŒ ๊ฐ์†Œํ•˜์—ฌ ์ ๊ฒ€
    ์กฐ์น˜๋ฅผ ์ˆ˜์ •ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (yes/no): yes
    
    ๊ฐ€๋Šฅํ•œ ์กฐ์น˜:
    1. IMMEDIATE_SHUTDOWN
    2. CONTROLLED_SHUTDOWN
    3. REDUCE_LOAD
    4. MAINTENANCE_ALERT
    5. CONTINUE_MONITORING

    ์„ ํƒ: 3

    โš ๏ธ ์ „๋ฌธ๊ฐ€๊ฐ€ AI ๊ถŒ์žฅ ์กฐ์น˜๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค
    ์›๋ž˜ ๊ถŒ์žฅ ์กฐ์น˜: IMMEDIATE_SHUTDOWN
    ์ˆ˜์ •๋œ ์กฐ์น˜: REDUCE_LOAD
    
    โš™๏ธ ์กฐ์น˜ ์‹คํ–‰: REDUCE_LOAD
    ๐Ÿ“‰ ๋ถ€ํ•˜ ๊ฐ์†Œ ์‹คํ–‰
    ```

---

## ๐Ÿง  ํ•ต์‹ฌ ๊ฐœ๋…

### 1. `interrupt()` vs `interrupt_before`

    | ํŠน์ง• | `interrupt()` | `interrupt_before` |
    |------|---------------|-------------------|
    | ์œ„์น˜ | ๋…ธ๋“œ ๋‚ด๋ถ€ | ์ปดํŒŒ์ผ ์‹œ ์„ค์ • |
    | ์กฐ๊ฑด๋ถ€ ์‹คํ–‰ | โœ… ๊ฐ€๋Šฅ | โŒ ๋ถˆ๊ฐ€๋Šฅ |
    | ๋ฐ์ดํ„ฐ ์ „๋‹ฌ | โœ… ๊ฐ€๋Šฅ | โŒ ์ œํ•œ์  |
    | ์‚ฌ์šฉ ๋‚œ์ด๋„ | โš ๏ธ ์ค‘๊ธ‰ | โœ… ์‰ฌ์›€ |

**`interrupt()` ์˜ˆ์‹œ (๋ณธ ํ”„๋กœ์ ํŠธ):**
```python
def expert_approval_node(state):
    if state["risk_level"] == "LOW":
        return Command(goto="execute", update={...})  # ์ž๋™ ์Šน์ธ
    
    # HIGH/CRITICAL๋งŒ interrupt
    approval = interrupt({"question": "์Šน์ธ?"})
    return Command(goto="execute", update={...})

interrupt_before ์˜ˆ์‹œ:

graph = builder.compile(
    checkpointer=memory,
    interrupt_before=["approval_node"]  # ํ•ญ์ƒ ์ด ๋…ธ๋“œ ์ „์— ๋ฉˆ์ถค
)

2. Command๋ฅผ ํ†ตํ•œ ๋™์  ๋ผ์šฐํŒ…

def approval_node(state):
    decision = interrupt({"question": "์Šน์ธ?"})
    
    # ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋…ธ๋“œ๋กœ ์ด๋™
    if decision["approved"]:
        return Command(goto="execute", update={"status": "approved"})
    elif decision.get("override"):
        return Command(goto="override", update={"action": decision["override"]})
    else:
        return Command(goto="cancel", update={"status": "rejected"})

3. ์กฐ๊ฑด๋ถ€ HITL ํŒจํ„ด

# ์œ„ํ—˜๋„๊ฐ€ ๋‚ฎ์œผ๋ฉด ์ž๋™ ์Šน์ธ, ๋†’์œผ๋ฉด ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ† 
if risk_level == "LOW":
    return auto_approve()
else:
    return request_expert_approval()

๐Ÿญ ์‹ค์ „ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค

์‹œ๋‚˜๋ฆฌ์˜ค 1: ์ œ์กฐ ๋ผ์ธ ๊ฐ€๋™ ์ค‘์ง€

**์ƒํ™ฉ:**
- AI๊ฐ€ ์„ค๋น„ ๊ณผ์—ด ๊ฐ์ง€ (95.8ยฐC)
- ์ •์ƒ ๋ฒ”์œ„: 70-80ยฐC
- ์ž„๊ณ„๊ฐ’: 90ยฐC

**์›Œํฌํ”Œ๋กœ์šฐ:**
1. ๐Ÿค– AI ์„ผ์„œ ๋ถ„์„ โ†’ CRITICAL ์œ„ํ—˜๋„ ๊ฐ์ง€
2. โธ๏ธ ๊ทธ๋ž˜ํ”„ ์ค‘๋‹จ, ์ „๋ฌธ๊ฐ€ ์•Œ๋ฆผ ์ „์†ก
3. ๐Ÿ‘ค ์ „๋ฌธ๊ฐ€ ํ˜„์žฅ ํ™•์ธ
4. โœ… ์ „๋ฌธ๊ฐ€ ์Šน์ธ โ†’ ๊ธด๊ธ‰ ๊ฐ€๋™ ์ค‘์ง€ ์‹คํ–‰
5. ๐Ÿ“ ๊ฐ์‚ฌ ๊ธฐ๋ก: "์ „๋ฌธ๊ฐ€ A, 2025-02-10 14:30, ์Šน์ธ, ๊ธด๊ธ‰ ๊ฐ€๋™ ์ค‘์ง€"

์‹œ๋‚˜๋ฆฌ์˜ค 2: ์—๋„ˆ์ง€ ๋ถ€ํ•˜ ๊ด€๋ฆฌ

**์ƒํ™ฉ:**
- AI๊ฐ€ ์••๋ ฅ ๊ธ‰์ฆ ๊ฐ์ง€ (125.5 kPa)
- AI ๊ถŒ์žฅ: IMMEDIATE_SHUTDOWN

**์›Œํฌํ”Œ๋กœ์šฐ:**
1. ๐Ÿค– AI ๋ถ„์„ โ†’ HIGH ์œ„ํ—˜๋„
2. โธ๏ธ ์ „๋ฌธ๊ฐ€ ๊ฒ€ํ†  ์š”์ฒญ
3. ๐Ÿ‘ค ์ „๋ฌธ๊ฐ€ ํŒ๋‹จ: "์ฆ‰์‹œ ์ค‘์ง€๋ณด๋‹ค ๋ถ€ํ•˜ ๊ฐ์†Œ๊ฐ€ ์ ์ ˆ"
4. โœ๏ธ ์ „๋ฌธ๊ฐ€ Override โ†’ REDUCE_LOAD ์„ ํƒ
5. ๐Ÿ“‰ ๋ถ€ํ•˜ ๊ฐ์†Œ ์‹คํ–‰
6. ๐Ÿ“ ๊ฐ์‚ฌ ๊ธฐ๋ก: "AI ๊ถŒ์žฅ ์ˆ˜์ •, ์ „๋ฌธ๊ฐ€ B, ๋ถ€ํ•˜ ๊ฐ์†Œ"

์‹œ๋‚˜๋ฆฌ์˜ค 3: ์ •์ƒ ๋ชจ๋‹ˆํ„ฐ๋ง (์ž๋™ํ™”)

**์ƒํ™ฉ:**
- ๋ชจ๋“  ์„ผ์„œ ์ •์ƒ ๋ฒ”์œ„
- ์œ„ํ—˜๋„: LOW

**์›Œํฌํ”Œ๋กœ์šฐ:**
1. ๐Ÿค– AI ๋ถ„์„ โ†’ LOW ์œ„ํ—˜๋„
2. โœ… ์ž๋™ ์Šน์ธ (์ „๋ฌธ๊ฐ€ ๊ฐœ์ž… ์—†์ด ๋ฐ”๋กœ ์‹คํ–‰)
3. ๐Ÿ‘๏ธ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ณ„์†
4. ๐Ÿ“ ์ž๋™ ๊ธฐ๋ก: "์ž๋™ ์Šน์ธ, ์ •์ƒ ๋ฒ”์œ„"

๐Ÿ”ง ๊ธฐ์ˆ  ์Šคํƒ

| ์นดํ…Œ๊ณ ๋ฆฌ | ๊ธฐ์ˆ  |
|----------|------|
| **์–ธ์–ด** | Python 3.10+ |
| **ํ”„๋ ˆ์ž„์›Œํฌ** | LangGraph |
| **์ƒํƒœ ๊ด€๋ฆฌ** | LangGraph Checkpointer (MemorySaver) |
| **๋น„๋™๊ธฐ** | asyncio |
| **ํƒ€์ž…** | typing, typing_extensions |

์˜์กด์„ฑ

```txt
langgraph>=0.2.0
langchain-core>=0.3.0
typing-extensions>=4.5.0
```

๐Ÿ“š ํ•™์Šต ๋‚ด์šฉ

ํ•™์Šตํ•œ ํ•ต์‹ฌ ํŒจํ„ด

  1. โœ… HITL ๊ธฐ๋ณธ ํŒจํ„ด

    • interrupt()๋กœ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋Œ€๊ธฐ
    • Command(resume=value)๋กœ ์žฌ๊ฐœ
  2. โœ… ์กฐ๊ฑด๋ถ€ ๋ผ์šฐํŒ…

    • Command(goto="node_name")๋กœ ๋™์  ์ด๋™
    • ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
  3. โœ… ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆฌ๋ฐ

    • astream()์˜ ์ด๋ฒคํŠธ ์–ธํŒจํ‚น
    • __interrupt__ ๊ฐ์ง€ ๋ฐ ์ฒ˜๋ฆฌ
  4. โœ… ์ƒํƒœ ๊ด€๋ฆฌ

    • Checkpointer๋ฅผ ํ†ตํ•œ ์„ธ์…˜ ์œ ์ง€
    • thread_id๋กœ ์„ธ์…˜ ์‹๋ณ„

์ฃผ์š” ๋””๋ฒ„๊น… ๊ฒฝํ—˜

๋ฌธ์ œ: ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆฌ๋ฐ์—์„œ interrupt ๊ฐ์ง€ ์•ˆ ๋จ

# โŒ ์ž˜๋ชป๋œ ์–ธํŒจํ‚น
async for mode, *rest in graph.astream(...):
    chunk = rest[0]  # ๊ตฌ์กฐ ๋ถˆ์ผ์น˜

# โœ… ์˜ฌ๋ฐ”๋ฅธ ์–ธํŒจํ‚น
async for event in graph.astream(...):
    if isinstance(event, tuple):
        _, content = event
        chunk = content[1] if isinstance(content, tuple) else content
    else:
        chunk = event

๐ŸŽฏ ๋‹ค์Œ ๋‹จ๊ณ„

๋‹จ๊ธฐ ๊ฐœ์„  (1-2์ฃผ)

  • SQLite Checkpointer ์ „ํ™˜

    from langgraph.checkpoint.sqlite import SqliteSaver
    checkpointer = SqliteSaver.from_conn_string("qa_checkpoints.db")
    • ์„ธ์…˜ ์˜๊ตฌ ์ €์žฅ
    • ํ”„๋กœ์„ธ์Šค ์žฌ์‹œ์ž‘ ํ›„ ๋ณต๊ตฌ
  • ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๊ฐ•ํ™”

    • ์„ผ์„œ ์—ฐ๊ฒฐ ์‹คํŒจ ์ฒ˜๋ฆฌ
    • ํƒ€์ž„์•„์›ƒ ์„ค์ •
    • ์žฌ์‹œ๋„ ๋กœ์ง
  • ์•Œ๋ฆผ ์‹œ์Šคํ…œ ํ†ตํ•ฉ

    # ์ „๋ฌธ๊ฐ€์—๊ฒŒ ์ด๋ฉ”์ผ/Slack ์•Œ๋ฆผ
    send_notification(
        expert="safety_officer@company.com",
        subject=f"๊ธด๊ธ‰: {facility_id} ์Šน์ธ ํ•„์š”",
        priority="HIGH"
    )

์ค‘๊ธฐ ๊ฐœ์„  (1-2๊ฐœ์›”)

  • ์›น UI ๊ฐœ๋ฐœ

    • FastAPI ๋ฐฑ์—”๋“œ
    • React ํ”„๋ก ํŠธ์—”๋“œ
    • ์‹ค์‹œ๊ฐ„ ์„ผ์„œ ๋Œ€์‹œ๋ณด๋“œ
  • ์‹ค์ œ ์„ผ์„œ ์—ฐ๋™

    # SCADA/IoT ์‹œ์Šคํ…œ ์—ฐ๊ฒฐ
    sensor_data = scada_client.read_tags([
        "TEMP_SENSOR_01",
        "PRESSURE_GAUGE_02"
    ])
  • ๋‹ค์ค‘ ์ „๋ฌธ๊ฐ€ ์Šน์ธ

    # CRITICAL์€ 2๋ช… ์Šน์ธ ํ•„์š”
    approval_1 = interrupt({"expert": "shift_manager"})
    approval_2 = interrupt({"expert": "safety_officer"})

์žฅ๊ธฐ ๋ชฉํ‘œ (3-6๊ฐœ์›”)

  • ML ๋ชจ๋ธ ํ†ตํ•ฉ

    • ์ด์ƒ ์ง•ํ›„ ์˜ˆ์ธก
    • ์ž„๊ณ„๊ฐ’ ์ž๋™ ์กฐ์ •
  • PostgreSQL/MySQL ์ „ํ™˜

    • ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ DB
    • ๊ณ ๊ฐ€์šฉ์„ฑ
  • ๊ฐ์‚ฌ ๋ฆฌํฌํŒ…

    • ์Šน์ธ ์ค€์ˆ˜์œจ ๋Œ€์‹œ๋ณด๋“œ
    • ์˜์‚ฌ๊ฒฐ์ • ๋ถ„์„

๐Ÿค ๊ธฐ์—ฌ ๋ฐฉ๋ฒ•

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

๐Ÿ“– ์ฐธ๊ณ  ์ž๋ฃŒ

๊ณต์‹ ๋ฌธ์„œ

๊ด€๋ จ ํ”„๋กœ์ ํŠธ

ํ•™์Šต ์ž๋ฃŒ


๐Ÿ“ ๋ผ์ด์„ ์Šค

MIT License - ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉ, ์ˆ˜์ •, ๋ฐฐํฌ ๊ฐ€๋Šฅ


๐Ÿ“ž ๋ฌธ์˜

์งˆ๋ฌธ์ด๋‚˜ ์ œ์•ˆ์‚ฌํ•ญ์ด ์žˆ์œผ์‹œ๋ฉด Issue๋ฅผ ์—ด์–ด์ฃผ์„ธ์š”!

Happy Learning! ๐Ÿš€

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages