교안추가

This commit is contained in:
mcchae committed 2026-10-14 11:12:25 +09:00
1 parent e0987c02ce
commit 8740d2985b
3 files changed
+1334

No files matched your search

+504
View File
@@ -0,0 +1,504 @@
# 1단계 · AI 워크플로우의 개념과 전체 아키텍처
- 시간: 1~2교시 (1교시 앞부분은 [오늘의 개요](00-DAY64-OVERVIEW.md))
- 학습 목표
- AI 워크플로우가 무엇이고, 일반 자동화·AI Agent와 어떻게 다른지 설명할 수 있다.
- 노드·연결·데이터(페이로드)·실행 기록이라는 워크플로우 기본 용어를 사용할 수 있다.
- 실행 기록을 읽고 노드마다 무엇이 들어가고 나왔는지 설명할 수 있다.
- 워크플로우의 전체 아키텍처에서 LLM·RAG·Tool·Memory·데이터 처리 노드의 역할을 구분할 수 있다.
- 실습: [Lab 01 · 첫 번째 워크플로우 실행과 실행 기록 읽기](../../Last3Mon/64/LAB-01-FIRST-WORKFLOW.md)
```mermaid
flowchart LR
A["1. 지금까지 만든 것"] --> B["2. AI 워크플로우란"]
B --> C["3. 기본 용어·실행 기록"]
C --> D["4. 전체 아키텍처"]
D --> E["5. 노코드 도구"]
E --> F["6. Lab 01"]
```
---
## 1. 지금까지 만든 것을 다시 보기
지난 3개월 동안 여러분은 코드로 AI 서비스를 만들었습니다. 구조를 단순하게 그리면 거의 모든 프로젝트가 아래 모양이었습니다.
```mermaid
flowchart LR
FE["Streamlit 화면"] --> BE["FastAPI 백엔드"] --> EN["실행 엔진<br/>(Agent · Orchestration)"]
EN --> LLM{{"LLM<br/>OpenAI · Anthropic · Gemini · Llama"}}
EN --> MCP["Tool · MCP Server"]
EN --> DB[("PostgreSQL · Redis<br/>pgvector")]
```
`mini_multi_agent_01_patterns`의 `PatternEngine`은 "어떤 순서로, 어떤 Agent와 Tool을 실행할지"를 Python 코드로 정했습니다. `mini_multi_agent_04`에서는 그 순서를 `ExecutionPlan`과 `teams.yaml`로 코드 밖에 적기 시작했습니다.
> 실행 순서를 코드가 아니라 **그림(노드와 연결)으로 정하고, 화면에서 바로 고치고 실행**할 수 있다면 어떨까요?
> 이것이 이번 교과목에서 배우는 **노코드 AI 워크플로우**입니다.
| 코드로 만들 때 | 노코드 워크플로우에서 |
| --- | --- |
| `engine.py`에 실행 순서를 작성 | 캔버스에 노드를 놓고 선으로 연결 |
| 함수 인자로 데이터 전달 | 앞 노드의 결과를 다음 노드 입력에 연결(매핑) |
| `providers/` 레지스트리로 LLM 선택 | LLM 노드 설정에서 모델 선택 |
| MCP Client로 Tool 호출 | Tool(HTTP·DB·MCP) 노드 추가 |
| Redis Stream·로그로 실행 추적 | 실행 이력 화면에서 노드별 입력·출력 확인 |
### 1.1 실행 순서를 코드 밖으로 꺼내면 좋은 점
코드로 만든 AI 서비스도 잘 동작합니다. 그런데 서비스를 실제 업무에 붙이면 아래 같은 요청이 매주 들어옵니다.
- "분류에 `보안` 항목을 하나 더 넣어 주세요."
- "답변을 보내기 전에 팀장이 한 번 보게 해 주세요."
- "FAQ에 없으면 담당자에게 메신저로 알려 주세요."
코드로 만들었다면 매번 개발자가 코드를 고치고, 테스트하고, 다시 배포해야 합니다. 실행 순서가 그림으로 밖에 나와 있으면 다음이 쉬워집니다.
| 좋아지는 점 | 무엇이 쉬워지나 | 이번 교과목에서 |
| --- | --- | --- |
| 바꾸는 속도 | 노드 하나 추가·연결 변경으로 업무 절차 변경 | 65~67일차 n8n 설계·구현 |
| 함께 보기 | 업무 담당자도 그림을 보고 절차를 확인·제안 | 오늘 3단계 시나리오 분석 |
| 문제 찾기 | 실행 이력에서 어느 노드가 무엇을 받고 무엇을 냈는지 바로 확인 | 오늘 Lab 01, 76~77일차 |
| 운영 | 버전·비용·오류를 워크플로우 단위로 관리 | 74~79일차 운영 단원 |
### 1.2 노코드가 코드를 없애지는 않는다
노코드 도구도 결국 내부에서는 코드가 실행됩니다. 노코드는 **자주 바뀌는 "순서와 연결"을 그림으로 꺼내고, 잘 바뀌지 않는 "복잡한 처리"는 코드나 API로 남기는** 방식입니다.
| 그림(노드)으로 두기 좋은 것 | 코드·API로 두기 좋은 것 |
| --- | --- |
| 업무 절차, 분기 조건, 알림 대상 | 복잡한 계산, 대량 데이터 처리 |
| 프롬프트, 모델 선택 | 사내 시스템과의 복잡한 인증·연동 |
| 사람 확인 단계의 위치 | 성능이 아주 중요한 부분 |
n8n에도 **Code 노드**(JavaScript·Python)와 **HTTP Request 노드**가 있어서, 그림으로 표현하기 어려운 부분은 작은 코드나 지난 3개월 동안 만든 FastAPI 서비스를 호출해 해결합니다. 지난 3개월의 코드 실력이 이 교과목에서도 그대로 쓰입니다.
---
## 2. AI 워크플로우란
**워크플로우(Workflow)** 는 하나의 업무 목표를 달성하기 위해 여러 작업 단계를 **정해진 연결 순서**대로 실행하는 흐름입니다.
**AI 워크플로우**는 그 단계 중 일부를 LLM·Agent 같은 AI가 맡는 워크플로우입니다. 사람이 하던 "읽고, 분류하고, 요약하고, 초안을 쓰는" 판단 작업을 AI 단계가 맡고, 나머지 단계(데이터 조회, 저장, 전송)는 정해진 규칙대로 실행합니다.
```mermaid
flowchart LR
T(["이벤트<br/>문의 메일 도착"]) --> A["데이터 정리<br/>본문·발신자 추출"]
A --> B{{"AI 단계<br/>문의 분류"}}
B --> C["Tool 단계<br/>FAQ 검색"]
C --> D{{"AI 단계<br/>답변 초안 작성"}}
D --> E[("기록 저장")]
E --> F(["결과 전달<br/>담당자 메신저"])
```
위 그림에서 AI가 맡은 단계는 6개 중 2개뿐입니다. 나머지 4개는 규칙대로 움직입니다. **AI 워크플로우라고 해서 모든 단계가 AI는 아닙니다.** 오히려 AI 단계가 적고 위치가 분명할수록 결과를 예측하고 관리하기 쉽습니다.
### 2.1 세 가지 자동화 방식 비교
9월 초 `06_agent-workflow` 단원에서 배운 질문을 다시 떠올려 봅시다. **"누가 다음 행동을 결정하는가?"**
| 구분 | 규칙 기반 자동화 | AI 워크플로우 | AI Agent |
| --- | --- | --- | --- |
| 다음 단계를 정하는 주체 | 개발자가 정한 규칙 | 설계자가 정한 경로 (일부 단계만 AI) | AI 모델이 상황을 보고 결정 |
| 예시 | 매일 9시 매출 엑셀을 메일로 발송 | 문의 분류 → FAQ 검색 → 답변 초안 | "고객 문제를 해결해 줘"라는 목표만 주고 Tool을 스스로 선택 |
| 결과 예측 | 매우 쉬움 | 쉬움 | 어려움 |
| 유연성 | 낮음 | 중간 | 높음 |
| 비용·속도 | 저렴·빠름 | 중간 | LLM 호출이 많아 비쌈·느림 |
| 문제 추적 | 쉬움 | 노드별로 확인 가능 | 실행 기록이 길고 복잡 |
```mermaid
flowchart LR
R["규칙 기반 자동화"] --> W["AI 워크플로우"] --> A["AI Agent"] --> M["Multi-Agent<br/>워크플로우"]
R -. "예측 가능성 ↑" .- W
A -. "자율성·유연성 ↑" .- M
```
실무에서는 **"가능한 한 워크플로우로, 꼭 필요한 곳에만 Agent로"** 설계하는 경우가 많습니다. 경로가 정해져 있으면 결과를 검증하기 쉽고, 비용과 장애를 관리하기 쉽기 때문입니다. 이번 교과목의 후반(68일차 이후)에서는 워크플로우 안에 여러 Agent를 넣는 **Multi-Agent 워크플로우**로 확장합니다.
### 2.2 같은 업무를 세 방식으로 만들어 보기
"출장 가는 직원에게 날씨와 준비물을 알려 준다"는 업무를 세 방식으로 만들면 차이가 분명해집니다.
**① 규칙 기반 자동화**: 비가 올 확률이 50% 이상이면 "우산을 챙기세요"라고 보냅니다.
```mermaid
flowchart LR
A(["출장 등록"]) --> B["날씨 조회"] --> C{"강수 확률 ≥ 50%?"}
C -->|"예"| D(["'우산을 챙기세요'"])
C -->|"아니요"| E(["'맑은 날씨입니다'"])
```
빠르고 항상 같은 결과가 나옵니다. 하지만 "바람이 강하니 얇은 겉옷을 챙기세요"처럼 규칙에 없는 안내는 할 수 없습니다.
**② AI 워크플로우**: 날씨 조회는 항상 하고, 안내 문장만 LLM이 씁니다.
```mermaid
flowchart LR
A(["출장 등록"]) --> B["날씨 조회<br/>(Tool)"] --> C{{"안내 문장 작성<br/>(LLM)"}} --> D(["안내 발송"])
```
경로는 고정이고 문장만 상황에 맞게 바뀝니다. 오늘 실습 `02-llm-tool.json`이 이 구조입니다.
**③ AI Agent**: "출장 준비를 도와줘"라는 목표만 주고, 날씨 조회·일정 조회·숙소 검색 중 무엇을 쓸지 LLM이 고릅니다.
```mermaid
flowchart LR
A(["'출장 준비를 도와줘'"]) --> B{{"Agent<br/>무엇을 할지 판단"}}
B -->|"필요하면"| T1["날씨 조회"]
B -->|"필요하면"| T2["일정 조회"]
B -->|"필요하면"| T3["숙소 검색"]
T1 & T2 & T3 --> B
B --> D(["안내"])
```
질문이 다양해도 대응할 수 있지만, 어떤 Tool을 몇 번 부를지 매번 달라 비용과 결과를 예측하기 어렵습니다.
| 질문 | ① 규칙 | ② AI 워크플로우 | ③ Agent |
| --- | --- | --- | --- |
| LLM 호출 횟수 | 0 | 1 | 2~5 (상황마다 다름) |
| 같은 입력이면 같은 결과? | 예 | 거의 같음 | 달라질 수 있음 |
| "숙소도 알려 줘" 같은 새 요청 | 불가 | 경로를 고쳐야 함 | 바로 대응 |
### 2.3 언제 Agent가 필요한가
아래 질문 중 "예"가 많을수록 Agent를, 적을수록 워크플로우를 고릅니다.
- 들어오는 요청의 종류가 너무 다양해서 경로를 미리 그릴 수 없는가?
- 필요한 Tool이 요청마다 다르고, 순서도 그때그때 달라지는가?
- 몇 번 시도하고 결과를 보며 다음 행동을 바꿔야 하는가?
- 비용과 응답 시간이 조금 늘어나도 괜찮은가?
대부분의 사내 업무(문의 처리, 보고서 작성, 승인 요청)는 절차가 정해져 있어 워크플로우가 먼저입니다. 67일차에 워크플로우 안에 **AI Agent 노드**를 넣어 두 방식을 함께 써 봅니다.
### 2.4 주변에서 볼 수 있는 AI 워크플로우
| 분야 | 시작 | AI 단계 | 규칙·Tool 단계 | 결과 |
| --- | --- | --- | --- | --- |
| 사내 IT 문의 | 문의 메일 | 분류, 답변 초안 | FAQ 검색, 이력 저장 | 답변 메일 |
| 쇼핑몰 리뷰 | 리뷰 등록 | 긍정·부정 판단, 답글 초안 | 상품 정보 조회 | 답글 게시 대기 |
| 채용 | 지원서 접수 | 경력 요약 | 필수 항목 확인, 일정 등록 | 면접관 메일 |
| 회의 | 회의 종료 | 회의록 요약, 할 일 추출 | 담당자 조회, 일정 등록 | 메신저 알림 |
네 사례 모두 "AI가 읽고 판단하는 단계"와 "정해진 규칙대로 처리하는 단계"가 섞여 있습니다. 3단계에서 이런 업무를 직접 하나 골라 분석합니다.
---
## 3. 워크플로우의 기본 용어
| 용어 | 뜻 | 코드에서 비슷한 것 |
| --- | --- | --- |
| 노드(Node) | 한 가지 일을 하는 단계 하나 | 함수 하나 |
| 연결(Edge) | 노드 사이의 실행 순서와 데이터 전달 경로 | 함수 호출 순서 |
| 페이로드(Payload) | 노드 사이를 이동하는 데이터 (보통 JSON) | 함수의 인자·반환값 |
| 입력(Input) | 워크플로우가 시작될 때 받는 데이터 | API 요청 본문 |
| 출력(Output) | 워크플로우가 최종으로 만드는 결과 | API 응답 |
| 실행(Run) | 워크플로우를 한 번 처음부터 끝까지 돌린 것 | 요청 1건 처리 |
| 실행 기록(Trace) | 실행마다 남는 노드별 입력·출력·시간·성공 여부 | `TraceEvent` 목록 |
| 트리거(Trigger) | 워크플로우를 시작시키는 사건 | API 호출, 스케줄러 (65일차) |
### 3.1 워크플로우 정의: 오늘은 JSON으로 적는다
오늘 실습 도구 `miniflow`는 워크플로우를 JSON 파일로 적습니다. 실습 파일 `02-llm-tool.json`을 줄여 보면 다음과 같습니다.
```json
{
"name": "02 LLM + Tool",
"inputs": ["city", "question"],
"nodes": [
{"id": "start", "type": "input"},
{"id": "weather", "type": "tool", "tool": "get_weather", "args": {"city": "{{input.city}}"}},
{"id": "answer", "type": "llm", "prompt": "날씨 데이터: {{nodes.weather.output}}\n질문: {{input.question}}"},
{"id": "result", "type": "output", "value": "{{nodes.answer.output}}"}
],
"edges": [["start", "weather"], ["weather", "answer"], ["answer", "result"]]
}
```
| 부분 | 뜻 |
| --- | --- |
| `inputs` | 실행할 때 받아야 하는 값 (`-i city=제주`처럼 넘김) |
| `nodes` | 노드 목록. `id`는 이름, `type`은 종류 (`input`·`llm`·`tool`·`memory_read`·`memory_write`·`output`) |
| `edges` | `["앞 노드", "뒤 노드"]` 쌍의 목록. 실행 순서와 데이터 흐름을 정함 |
| `{{ }}` | 표현식. 실행할 때 실제 값으로 바뀜 (2단계 5절에서 자세히) |
n8n에서는 이 JSON을 직접 쓰지 않고 화면에서 노드를 놓고 선을 잇습니다. 하지만 n8n도 워크플로우를 내보내면 **노드 목록과 연결 목록이 들어 있는 JSON 파일**이 나옵니다. 오늘 JSON을 읽을 줄 알면 n8n 워크플로우 파일도 읽을 수 있습니다.
### 3.2 실행 기록(Trace) 읽기
워크플로우를 실행할 때마다 노드별 기록이 남습니다. 문제가 생기면 가장 먼저 보는 것이 실행 기록입니다. 아래는 수업 준비 때 `02-llm-tool.json`을 실행해 남은 `runs/*.json`의 일부입니다.
```json
{
"workflow": "02 LLM + Tool",
"status": "completed",
"trace": [
{"step": 1, "id": "start", "type": "input", "status": "ok", "ms": 0.0,
"output": {"city": "제주", "question": "우산이 필요할까?"}},
{"step": 2, "id": "weather", "type": "tool", "status": "ok", "ms": 0.3,
"args": {"city": "제주"},
"output": {"city": "제주", "condition": "비", "temp_c": 20, "rain_chance": 80, "air": "좋음"}},
{"step": 3, "id": "answer", "type": "llm", "status": "ok", "ms": 4978.8, "provider": "openai",
"prompt": "날씨 데이터: {\"city\": \"제주\", \"condition\": \"비\", ...}\n질문: 우산이 필요할까?",
"output": "네, 제주에 비가 오고 비 올 확률이 80%이므로 우산이 필요합니다."},
{"step": 4, "id": "result", "type": "output", "status": "ok", "ms": 0.0, "output": "네, 제주에 ..."}
]
}
```
| 필드 | 읽는 법 |
| --- | --- |
| `status` | 노드가 성공(`ok`)했는지 실패(`failed`)했는지. 실패하면 `error`에 이유가 남음 |
| `ms` | 걸린 시간(밀리초). Tool은 0.3ms, LLM은 수천 ms → **전체 시간은 거의 LLM이 결정** |
| `args` | Tool 노드에 실제로 들어간 인자. `{{input.city}}`가 `제주`로 바뀐 것을 확인 |
| `prompt` | LLM에 실제로 보낸 프롬프트. 앞 노드 결과가 제대로 들어갔는지 확인 |
| `provider` | 어느 LLM이 답했는지 (`openai`, 대체 경로라면 `ollama`, 연습용 `mock`) |
| `output` | 노드가 낸 결과. 다음 노드는 이 값을 받음 |
실행 기록을 읽을 때는 **"들어간 것(args·prompt) → 나온 것(output) → 걸린 시간(ms)"** 순서로 봅니다. LLM 답이 이상하면 대부분 프롬프트에 들어간 데이터가 이상한 경우입니다.
### 3.3 실행 순서와 DAG
노드와 연결로 만든 그림은 대부분 **한 방향으로만 흐르는 그래프**입니다. 이런 그래프를 DAG(Directed Acyclic Graph, 방향이 있고 되돌아가는 고리가 없는 그래프)라고 부릅니다. 고리가 없으면 "누가 먼저 실행되어야 하는지"를 항상 계산할 수 있습니다.
```mermaid
flowchart LR
S["start"] --> A["사용자 기억 읽기"]
S --> B["날씨 조회"]
A --> C{{"LLM 추천"}}
B --> C
C --> D[("기억 저장")]
```
위 그림에서 `사용자 기억 읽기`와 `날씨 조회`는 서로를 기다릴 필요가 없습니다. `LLM 추천`은 두 결과가 모두 있어야 실행됩니다. 이 관계가 `mini_multi_agent_04`의 병렬 실행과 Join과 같은 개념입니다.
엔진은 실행 순서를 이렇게 계산합니다.
1. 들어오는 연결이 없는 노드(`start`)부터 실행합니다.
2. 실행이 끝난 노드에서 나가는 연결을 지웁니다.
3. 들어오는 연결이 모두 지워진 노드를 다음에 실행합니다.
4. 모든 노드를 실행할 때까지 반복합니다.
이 방법을 **위상 정렬**이라고 부릅니다. `miniflow validate`의 `Execution order` 줄이 이렇게 계산한 결과입니다. `miniflow`는 계산된 순서대로 하나씩 실행하고, n8n 같은 도구는 서로 기다릴 필요가 없는 노드를 동시에 실행하기도 합니다.
**고리가 있으면?** `A → B → C → A`처럼 되돌아가는 연결이 있으면 "먼저 실행할 노드"를 정할 수 없습니다. `miniflow`는 이런 파일을 실행 전에 거부합니다. 실제 업무에는 "다시 시도", "모든 항목에 반복" 같은 되돌아가는 흐름이 필요한데, 워크플로우 도구는 이것을 고리 대신 **항목별 반복(Split Out 등)이나 재시도 설정**으로 따로 제공합니다 (66일차 반복·병렬, 72일차 재시도).
### 3.4 연결은 "순서"와 "데이터" 두 가지를 정한다
연결 `["weather", "answer"]`는 두 가지 뜻을 가집니다.
- **순서**: `answer`는 `weather`가 끝난 뒤에 실행된다.
- **데이터**: `answer`는 `weather`의 결과(`{{nodes.weather.output}}`)를 쓸 수 있다.
연결 없이 다른 노드의 결과를 쓰려고 하면 그 노드가 아직 실행되지 않았을 수 있습니다. Lab 02에서 연결을 빼먹으면 `'answer' not found` 같은 오류가 나는 이유입니다. **데이터를 쓰는 노드는 반드시 그 데이터를 만드는 노드 뒤에 연결**합니다.
---
## 4. AI 워크플로우의 전체 아키텍처
워크플로우 도구가 달라도 전체 구조는 거의 같습니다.
```mermaid
flowchart TB
subgraph IN["① 시작"]
T1(["사용자 요청<br/>채팅·폼"])
T2(["시스템 이벤트<br/>메일·티켓·웹훅"])
T3(["일정<br/>매일 9시"])
end
subgraph ENG["② 워크플로우 엔진"]
O["실행 순서 계산 · 데이터 전달 · 오류 처리"]
end
subgraph NODES["③ 노드"]
N1{{"LLM 노드<br/>이해·분류·요약·생성"}}
N2["RAG 노드<br/>문서 검색 → 근거 제공"]
N3["Tool 노드<br/>API·DB·MCP 호출"]
N4[("Memory 노드<br/>대화·사용자 기억")]
N5["데이터 처리 노드<br/>변환·필터·병합"]
end
subgraph OUT["④ 결과"]
R1(["응답 화면"])
R2(["메신저·메일 알림"])
R3[("DB·문서 저장")]
end
subgraph OPS["⑤ 운영"]
L["실행 기록 · 버전 · 비용 · 보안"]
end
IN --> ENG --> NODES --> OUT
ENG -.-> OPS
```
| 계층 | 하는 일 | 지난 실습에서 본 모습 |
| --- | --- | --- |
| ① 시작 | 언제, 무엇으로 시작할지 | Streamlit 버튼, FastAPI 요청 |
| ② 엔진 | 순서 계산, 데이터 전달, 실패 처리 | `PatternEngine`, `orchestration/engine.py` |
| ③ 노드 | 실제 작업 | Agent, MCP Tool, RAG 서비스, Memory 저장소 |
| ④ 결과 | 사람 또는 다른 시스템에 전달 | 화면 응답, SSE 이벤트 |
| ⑤ 운영 | 기록·버전·비용·보안 관리 | Redis Stream 추적, 로그, 가드레일 |
### 4.1 요청 하나가 다섯 계층을 지나는 과정
IT 문의 한 건이 들어왔을 때 각 계층이 하는 일을 시간 순서로 보면 다음과 같습니다.
```mermaid
sequenceDiagram
participant U as ① 시작 (문의 접수)
participant E as ② 엔진
participant N as ③ 노드들
participant R as ④ 결과
participant O as ⑤ 운영
U->>E: {"user": "kim", "ticket": "VPN이 끊겨요"}
E->>E: 실행 순서 계산
E->>N: history (Memory 읽기)
E->>N: classify (LLM) → "network"
E->>N: faq (Tool) → FAQ-003
E->>N: draft (LLM) → 답변 초안
E->>N: log (Memory 쓰기)
E->>R: 분류·FAQ·답변 초안
E-->>O: 노드별 입력·출력·시간 기록
```
엔진은 노드가 실패하면 그 자리에서 멈추고 실패를 기록합니다. 오늘 `miniflow`는 "멈추고 기록"까지만 합니다. 실패했을 때 다른 경로로 보내거나 다시 시도하는 일은 65일차(Condition)와 76일차(오류 처리)에서 배웁니다.
### 4.2 노드별 역할
| 노드 | 무엇을 하나 | 입력 → 출력 예시 | 주의할 점 |
| --- | --- | --- | --- |
| LLM | 글을 이해하고 판단·생성 | 문의 문장 → `"account"` 분류 | 답이 매번 조금씩 다를 수 있음 |
| RAG(검색) | 질문과 관련된 문서를 찾아 근거로 제공 | 질문 → 관련 문서 조각 3개 | 검색이 틀리면 답도 틀림 |
| Tool | 외부 시스템의 기능 실행 | 도시 이름 → 날씨 데이터 | 인자 형식과 실패 처리 |
| Memory | 이전 실행의 정보를 읽고 저장 | 사용자 ID → 선호·이력 | 저장할 것과 버릴 것 구분 |
| 데이터 처리 | 형식 변환·필터·병합 | 검색 결과 목록 → 상위 1개 | AI가 필요 없는 일은 여기서 처리 |
데이터 처리처럼 규칙으로 해결되는 일에 LLM을 쓰면 느리고 비싸며 결과도 불안정해집니다. **판단이 필요한 곳에만 LLM 노드**를 두는 것이 좋은 설계의 출발점입니다.
| 일 | LLM으로 하면 | 데이터 처리로 하면 |
| --- | --- | --- |
| 글자 수 세기 | 1~2초, 가끔 틀림 | 0.1ms 미만, 항상 정확 (실습 Tool `count_chars`) |
| 날짜를 `2026-10-14` 형식으로 | 형식이 가끔 흔들림 | 항상 같은 형식 |
| 목록에서 첫 번째 항목 고르기 | 불필요한 호출 | 표현식 `{{nodes.faq.output.0}}` |
### 4.3 운영 계층은 왜 따로 있나
워크플로우를 한 번 만들고 끝나는 일은 거의 없습니다. 매일 수백 건이 실행되면 다음 질문에 답할 수 있어야 합니다.
| 운영 질문 | 필요한 것 | 다루는 날 |
| --- | --- | --- |
| 어제 실패한 실행은 몇 건이고 왜 실패했나? | 실행 기록 모아 보기 | 76~77일차 |
| 지난주 프롬프트를 바꾼 뒤 품질이 나빠졌나? | 버전 관리, 품질 지표 | 74·78일차 |
| 이번 달 LLM 비용은 얼마인가? | 호출 수·토큰 기록 | 75일차 |
| 개인정보가 LLM에 그대로 넘어가지 않나? | 입력·출력 검사 | 73일차 |
오늘 남기는 `runs/*.json`이 이 모든 운영 기능의 출발점입니다.
### 4.4 좋은 노드 설계 원칙
| 원칙 | 나쁜 예 | 좋은 예 |
| --- | --- | --- |
| 한 노드는 한 가지 일 | LLM 노드 하나가 "분류하고, FAQ 찾고, 답변까지" | 분류(LLM) → 검색(Tool) → 답변(LLM) |
| 이름은 하는 일로 | `llm1`, `node3` | `classify`, `faq`, `draft` |
| 출력 형식을 정한다 | "분류해 주세요" | "account, network, device, software, other 중 하나만" |
| 실패를 생각한다 | Tool이 실패하면 어떻게 될지 모름 | "도시를 못 찾으면?"을 설계 단계에서 적어 둠 |
| AI는 꼭 필요한 곳에만 | 글자 수 세기를 LLM에 맡김 | 규칙으로 되는 일은 데이터 처리 노드로 |
한 노드에 여러 일을 몰아넣으면 실행 기록을 봐도 어디서 틀렸는지 알 수 없습니다. 노드를 나누면 **실행 기록이 곧 문제 찾기 도구**가 됩니다.
---
## 5. 노코드 워크플로우 도구
노코드 도구는 위 아키텍처를 화면으로 제공합니다. 노드 목록에서 끌어다 놓고, 선으로 잇고, 각 노드의 설정 창에서 값을 채웁니다.
```mermaid
flowchart LR
P["노드 목록<br/>(팔레트)"] --> C["캔버스<br/>노드 배치·연결"]
C --> S["노드 설정<br/>프롬프트·모델·인자"]
S --> X["실행·테스트"]
X --> H["실행 이력<br/>노드별 입력·출력"]
H --> C
```
이번 교과목에서는 오픈소스 노코드 워크플로우 도구 **n8n**을 사용합니다. 65일차부터 실습 환경을 구성합니다.
| 도구 | 특징 |
| --- | --- |
| n8n | 오픈소스 워크플로우 자동화 도구. Slack·Email·Jira·Notion·DB 등 수백 개의 서비스 연동 노드와 AI Agent 노드 제공 |
이 밖에도 Make·Zapier처럼 업무용 서비스 연동에 강한 도구들이 있습니다. 도구마다 화면과 노드 이름은 다르지만, 오늘 배우는 **노드·연결·페이로드·실행 기록**이라는 개념은 모든 도구에 그대로 적용됩니다.
### 5.1 n8n의 노드 미리 보기
n8n의 노드는 크게 다음과 같이 나뉩니다. 오늘 배우는 노드 종류가 어디에 해당하는지 미리 연결해 둡니다.
| n8n 노드 분류 | 예 | 오늘 개념 | 처음 쓰는 날 |
| --- | --- | --- | --- |
| Trigger | Webhook, Manual Trigger, Schedule Trigger | ① 시작 | 65일차 (Schedule은 72일차) |
| 흐름 제어 | IF, Switch, Merge | ② 엔진의 분기·합치기 | 65~66일차 |
| 데이터 처리 | Edit Fields(Set), Code, Split Out | 데이터 처리 노드 | 65~66일차 |
| 앱·HTTP | HTTP Request (LLM API·사내 데이터 API 호출) | Tool 노드 | 65일차 |
| AI | AI Agent, OpenAI Chat Model, Vector Store | LLM·RAG 노드 | 67일차 (Vector Store는 71일차) |
### 5.2 오늘의 `miniflow`와 n8n 비교
| 하는 일 | 오늘 `miniflow` | 65일차부터 n8n |
| --- | --- | --- |
| 워크플로우 만들기 | JSON 파일 작성 | 캔버스에 노드 배치·연결 |
| 노드 연결 | `edges` 목록 | 노드 사이에 선 긋기 |
| 앞 노드 결과 쓰기 | `{{nodes.weather.output.city}}` | `{{ $json.city }}`, `{{ $('weather').item.json.city }}` |
| 실행 | `uv run miniflow run ...` | 화면의 Execute 버튼, Webhook 호출 |
| 실행 기록 | `runs/*.json` | Executions 화면 |
| 그림으로 보기 | `miniflow show` → Mermaid | 캔버스 그 자체 |
`miniflow`는 수업용으로 만든 작은 엔진입니다. n8n으로 넘어가기 전에 **화면 뒤에서 엔진이 무엇을 하는지** 눈으로 보려고 씁니다.
---
## 6. Lab 01 안내
오늘 실습은 노코드 도구로 넘어가기 전에, 같은 개념을 직접 눈으로 확인하는 미니 워크플로우 엔진 `miniflow`로 진행합니다. 워크플로우를 화면 대신 JSON 파일로 적는다는 점만 다릅니다.
실습은 WSL(Ubuntu)에서 진행하고, LLM 노드는 **OpenAI API**(`gpt-4.1-mini`, 키는 `Last3Mon/.env`)가 맡습니다. 키나 인터넷에 문제가 있으면 노트북에서 실행하는 **Ollama**(`gemma3:4b`)가 대신 답합니다. 준비는 [사전 준비 안내](../../Last3Mon/64/SETUP-WSL-LLM.md)를 따릅니다.
- 실습 안내: [Last3Mon/64/LAB-01-FIRST-WORKFLOW.md](../../Last3Mon/64/LAB-01-FIRST-WORKFLOW.md)
- 할 일
1. `uv run pytest`와 `uv run miniflow check`로 실습 환경·OpenAI 연결(대체 Ollama) 확인
2. `01-llm-only.json` 실행 후 실행 기록(`runs/*.json`) 읽기
3. 지난 실습 프로젝트 하나를 노드와 연결로 다시 그려 보기
실행 기록을 읽을 때는 3.2절의 표를 옆에 두고 `args`·`prompt` → `output` → `ms` 순서로 확인합니다.
---
## 7. 스스로 확인하기
<details><summary>Q1. "AI 워크플로우는 모든 단계를 AI가 처리한다"는 맞는 말인가요?</summary>
아닙니다. 경로는 설계자가 정하고, 판단이 필요한 일부 단계만 AI가 맡습니다. 조회·저장·전송은 규칙대로 실행합니다.
</details>
<details><summary>Q2. 출장 날씨 안내를 규칙 기반에서 AI 워크플로우로 바꾸면 무엇이 좋아지고 무엇을 잃나요?</summary>
규칙에 없는 상황(바람, 기온 차이)도 자연스러운 문장으로 안내할 수 있습니다. 대신 LLM 호출 비용과 1~2초의 시간이 들고, 문장이 매번 조금씩 달라집니다.
</details>
<details><summary>Q3. 실행 기록에서 LLM 답이 이상할 때 가장 먼저 볼 필드는?</summary>
`prompt`입니다. 앞 노드의 결과가 프롬프트에 제대로 들어갔는지부터 확인합니다.
</details>
<details><summary>Q4. 워크플로우에 A → B → A 같은 고리가 있으면 왜 실행할 수 없나요?</summary>
서로가 서로를 기다리므로 먼저 실행할 노드를 정할 수 없습니다. 반복이 필요하면 항목별 반복 노드나 재시도 설정을 씁니다.
</details>
<details><summary>Q5. "고객 문의 글자 수 세기"를 LLM 노드에 맡기면 안 되는 이유는?</summary>
규칙으로 항상 정확하게 처리할 수 있는 일인데, LLM은 느리고 비싸며 가끔 틀립니다. 데이터 처리 노드(실습에서는 `count_chars` Tool)로 처리합니다.
</details>
## 정리
- AI 워크플로우는 **정해진 경로** 위에 **AI 판단 단계**를 배치한 업무 흐름입니다.
- 실행 순서를 코드 밖 그림으로 꺼내면 바꾸기·함께 보기·문제 찾기·운영이 쉬워집니다. 복잡한 처리는 여전히 코드·API로 둡니다.
- 워크플로우는 노드·연결·페이로드로 구성되고, 실행할 때마다 실행 기록이 남습니다. 실행 기록은 `args`·`prompt` → `output` → `ms` 순서로 읽습니다.
- 연결은 실행 순서와 데이터 흐름을 함께 정하고, 엔진은 고리 없는 그래프(DAG)에서 순서를 계산합니다.
- 전체 구조는 시작 → 엔진 → 노드 → 결과 → 운영의 다섯 계층입니다.
- LLM은 판단이 필요한 곳에만, 규칙으로 되는 일은 데이터 처리 노드로 맡깁니다.
+470
View File
@@ -0,0 +1,470 @@
# 2단계 · LLM·Tool·Memory 노드와 데이터 흐름
- 시간: 3~4교시
- 학습 목표
- LLM 노드 하나만으로는 업무를 끝낼 수 없는 이유를 LLM의 한계로 설명할 수 있다.
- LLM 노드의 설정값(시스템 지시·프롬프트·모델·temperature)과 출력 형식 지시의 효과를 설명할 수 있다.
- 로컬 LLM과 클라우드 LLM API의 차이를 비용·데이터·품질 관점에서 비교할 수 있다.
- Tool·Memory 노드가 LLM의 어떤 한계를 보완하는지 구분할 수 있다.
- LLM·Tool·Memory가 상호작용하는 대표 패턴 네 가지를 그림으로 그릴 수 있다.
- 노드 사이에서 데이터(페이로드)가 어떻게 전달되는지 실행 기록으로 확인할 수 있다.
- 실습: [Lab 02 · LLM·Tool·Memory 연결하기](../../Last3Mon/64/LAB-02-LLM-TOOL-MEMORY.md)
```mermaid
flowchart LR
A["1. LLM 노드<br/>판단"] --> B["2. Tool 노드<br/>손과 발"]
B --> C["3. Memory 노드<br/>기억"]
C --> D["4. 상호작용 패턴"]
D --> E["5. 데이터 흐름"]
E --> F["6. Lab 02"]
```
---
## 1. LLM 노드: 워크플로우의 "판단" 담당
LLM 노드는 텍스트를 받아 텍스트(또는 구조화된 JSON)를 돌려주는 단계입니다.
```mermaid
flowchart LR
subgraph IN["LLM 노드 입력"]
S["시스템 지시<br/>역할·규칙"]
P["프롬프트<br/>+ 앞 노드의 데이터"]
K["설정<br/>모델·temperature"]
end
IN --> L{{"LLM"}} --> O["출력<br/>텍스트 또는 JSON"]
```
| LLM 노드가 잘하는 일 | 예 |
| --- | --- |
| 분류 | 문의를 `account`, `network`, `device` 중 하나로 |
| 추출 | 문장에서 날짜·이름·주문번호 뽑기 |
| 요약 | 회의록 3줄 요약 |
| 생성 | 고객 답변 초안 작성 |
| 판단 | 이 요청이 승인 대상인지 판단 |
10일차에 LLM API(OpenAI/Anthropic/Gemini)를 처음 호출했을 때와 27일차 Structured Output에서 본 것처럼, LLM 노드 출력은 **다음 노드가 읽을 수 있는 형식**이어야 합니다. 분류 결과를 다음 노드에서 비교하려면 `"account"`처럼 정해진 값으로 받는 것이 안전합니다.
### 1.1 LLM 노드의 설정값
실습 파일 `05-tech-support-ticket.json`의 분류 노드는 다음과 같습니다.
```json
{"id": "classify", "type": "llm",
"system": "Classify the ticket into one of: account, network, device, software, other. Reply with the label only.",
"prompt": "문의: {{input.ticket}}",
"mock": {"rules": [{"if_contains": ["비밀번호", "계정", "로그인", "잠김"], "then": "account"}, "..."], "default": "other"}}
```
| 설정 | 하는 일 | 오늘 실습 |
| --- | --- | --- |
| `system` (시스템 지시) | 역할과 지켜야 할 규칙. 모든 실행에 똑같이 들어감 | "다섯 값 중 하나만 답하라" |
| `prompt` (프롬프트) | 이번 실행의 데이터. 앞 노드 결과를 `{{ }}`로 넣음 | `문의: {{input.ticket}}` |
| 모델 | 어떤 LLM이 답할지 | `.env`의 `OPENAI_MODEL=gpt-4.1-mini` |
| `temperature` | 답의 무작위성. 낮을수록 같은 입력에 비슷한 답 | 기본 0.2 (분류·추출은 낮게) |
| `mock` | LLM 없이 정해진 규칙으로 답하는 연습용 설정 | `MINIFLOW_LLM=mock`일 때 사용 |
n8n의 OpenAI·AI Agent 노드에도 같은 항목이 있습니다. 이름만 조금 다릅니다 (System Message, Prompt, Model, Sampling Temperature).
### 1.2 출력 형식을 정하면 무엇이 달라지나
수업 준비 때 같은 문의를 두 가지 프롬프트로 분류해 본 결과입니다 (`gpt-4.1-mini`).
| 문의 | 형식 지시 없음 ("다음 IT 문의를 분류해 주세요") | 형식 지시 있음 (`classify` 노드) |
| --- | --- | --- |
| 프린터에서 출력이 안 됩니다 | "해당 IT 문의는 \"프린터 문제\" 또는 \"출력 문제\"로 분류할 수 있습니다." | `device` |
| 메일 프로그램에 로그인이 안 돼요 | "… 분류는 다음과 같이 할 수 있습니다: - 분류: 이메…" (여러 줄) | `software` |
| 회의실 예약은 어떻게 하나요? | "… \"시스템 사용 방법\" 또는 \"서비스 이용 문의\"로 분류할 수 있습니다." | `other` |
형식 지시가 없으면 답은 사람에게는 친절하지만, 다음 노드가 `"device"`와 비교할 수 없습니다. **사람이 읽을 답과 다음 노드가 읽을 답은 다르게 설계**합니다.
### 1.3 노드용 프롬프트 작성법
| 원칙 | 예 |
| --- | --- |
| 역할을 한 줄로 | "IT 문의를 분류한다." |
| 답할 수 있는 값을 모두 적기 | "account, network, device, software, other 중 하나" |
| 형식만 답하게 | "Reply with the label only." (설명·인사 금지) |
| 근거 범위를 정하기 | "주어진 FAQ 결과만 사용한다." |
| 모를 때의 답을 정하기 | "해당하는 값이 없으면 other." "FAQ가 없으면 담당자가 연락한다고 쓴다." |
| 데이터와 지시를 구분 | 지시는 `system`, 이번 데이터는 `prompt`에 |
### 1.4 같은 답이 계속 나와도 맞는 답은 아닐 수 있다
"메일 프로그램에 로그인이 안 돼요"를 `classify` 노드로 4번 분류했더니 4번 모두 `software`였습니다. 답은 흔들리지 않았습니다. 그런데 같은 문장으로 FAQ를 검색하면 `FAQ-002 계정 잠김 해제`와 `FAQ-005 메일함 용량 초과`가 나옵니다. **분류 노드는 "소프트웨어 문제", 검색 노드는 "계정 문제"로 본 것**입니다.
이것은 모델의 잘못이라기보다 **분류 기준이 정해지지 않은 문제**입니다. "메일 로그인 문제는 account로 분류한다"처럼 경계가 모호한 경우의 규칙을 시스템 지시에 적거나, 분류 결과와 검색 결과가 다르면 사람에게 보내는 경로를 둡니다. 일관성(같은 답)과 정확성(맞는 답)은 다른 문제라는 점을 기억해 둡니다.
### 1.5 LLM 혼자서는 할 수 없는 일
| LLM의 한계 | 실제로 생기는 문제 | 보완하는 노드 |
| --- | --- | --- |
| 학습 이후의 최신 정보를 모름 | "오늘 제주 날씨"를 지어냄 | Tool |
| 회사 내부 문서를 모름 | 사내 규정을 그럴듯하게 틀리게 답함 | RAG(검색) |
| 이전 대화를 기억하지 못함(호출마다 새로 시작) | 매번 같은 정보를 다시 물어봄 | Memory |
| 외부 시스템을 직접 바꾸지 못함 | 티켓 등록·메일 발송 불가 | Tool |
| 정확한 계산·규칙 처리에 약함 | 금액 합계 오류 | Tool·데이터 처리 |
오늘 실습에서도 이 한계가 실제로 보입니다. Lab 02의 `04-recommend-with-memory.json`에서 모델은 Tool 데이터에 없는 미술관 이름을 자기 지식으로 덧붙였습니다. 맞을 수도 있지만 **근거를 확인할 수 없는 정보**입니다.
### 1.6 LLM 노드는 어디서 실행되나
LLM 노드의 설정에서 가장 먼저 정하는 것은 **어떤 모델을 어디서 실행할지**입니다. 노코드 도구에서도 LLM 노드마다 모델과 연결 정보를 고릅니다.
```mermaid
flowchart LR
N{{"LLM 노드"}} -->|"인터넷 · API 키 (오늘 기본)"| C["클라우드 LLM<br/>OpenAI gpt-4.1-mini<br/>(Anthropic · Gemini도 가능)"]
N -.->|"localhost:11434 (대체)"| L["로컬 LLM<br/>Ollama · gemma3:4b<br/>(내 노트북)"]
```
| 구분 | 로컬 LLM (오늘 대체용) | 클라우드 LLM API (오늘 기본) |
| --- | --- | --- |
| 예 | Ollama + `gemma3:4b` | OpenAI `gpt-4.1-mini`, Anthropic, Gemini |
| 비용·키 | 없음 | API 키 필요, 사용량만큼 비용 또는 무료 한도 |
| 데이터 | 노트북 밖으로 나가지 않음 | 제공사 서버로 전송 |
| 속도·품질 | 노트북 성능과 모델 크기에 따라 달라짐 | 대체로 빠르고 품질이 높음 |
| 어울리는 경우 | 실습, 민감한 사내 데이터, 오프라인 | 높은 품질, 많은 사용자 |
수업 전에 같은 문의 4건(비밀번호, VPN, 프린터, 메일 프로그램 설치)을 세 모델로 분류해 본 결과입니다. "분류 정답"은 4건 중 정해진 값(`account`·`network`·`device`·`software`)과 정확히 같은 답을 낸 건수입니다.
| 모델 | 어디서 | 분류 정답 | 분류 노드 소요 시간 |
| --- | --- | --- | --- |
| `gpt-4.1-mini` (오늘 기본) | OpenAI API | 4/4 | 약 1초 |
| `gemma3:4b` (대체) | 노트북, 3.3GB | 4/4 | 약 1.6~2.3초 |
| `gemma3:1b` (8GB 노트북 대체) | 노트북, 815MB | 1/4 (나머지는 모두 `software`) | 약 1초 |
작은 모델은 빠르지만 판단이 틀릴 수 있습니다. 오늘 기본 모델이 맞혀도, 키·인터넷 문제로 대체 모델로 넘어가면 결과가 달라질 수 있다는 점을 기억해 두세요. 워크플로우를 설계할 때는 **노드마다 필요한 품질에 맞는 모델을 고르고**, LLM 출력이 다음 노드로 가기 전에 검증하는 단계를 둡니다. 모델 선택과 비용은 75일차에 다시 다룹니다.
### 1.7 대체 경로: 기본 모델이 실패하면
오늘 실습 환경은 OpenAI 호출이 실패하면(키 오류, 사용 한도 초과, 인터넷 끊김) Ollama로 한 번 더 시도합니다.
```mermaid
sequenceDiagram
participant N as LLM 노드
participant O as OpenAI
participant L as Ollama (노트북)
N->>O: 요청
O--xN: 실패 (예: 잘못된 키)
Note over N: openai failed → trying ollama
N->>L: 같은 요청
L-->>N: 답 (실행 기록 provider: "ollama")
```
| 확인할 것 | 어디서 |
| --- | --- |
| 누가 답했나 | 실행 기록의 `provider` |
| 왜 넘어갔나 | 터미널의 `openai failed (...)` 줄 |
| 품질 차이 | 1.6절 표처럼 대체 모델은 결과가 다를 수 있음 |
대체 경로는 "서비스가 멈추지 않게" 해 주지만 "같은 품질"을 보장하지는 않습니다. 72일차(안정성)와 76일차(장애 대응)에서 이 구조를 n8n으로 다시 만듭니다.
---
## 2. Tool 노드: 워크플로우의 "손과 발"
Tool 노드는 정해진 인자를 받아 외부 기능을 실행하고 결과 데이터를 돌려줍니다. 날씨 API, DB 조회, 사내 시스템 API, MCP Server의 Tool이 모두 Tool 노드가 될 수 있습니다.
```text
$ uv run miniflow tools
get_weather Return today's sample weather for a city.
search_faq Return FAQ entries ranked by keyword overlap with the query.
count_chars Count characters and words of a text.
```
### 2.1 Tool 노드를 설계할 때 정하는 것
| 정할 것 | `get_weather` 예 | 놓치면 생기는 일 |
| --- | --- | --- |
| 이름·설명 | 오늘의 도시 날씨 조회 | 다른 사람이 언제 써야 하는지 모름 |
| 인자 | `city` (서울·부산·제주·강릉) | 앞 노드가 엉뚱한 값을 넣음 |
| 결과 형식 | `{"city", "condition", "temp_c", "rain_chance", "air"}` | 다음 노드가 필드 이름을 틀림 |
| 실패 | 없는 도시 → `ToolError: Unknown city '평양'` | 워크플로우가 이유 없이 멈춘 것처럼 보임 |
Tool의 결과는 **다음 노드가 쓰기 좋은 모양**이어야 합니다. 필요 없는 필드가 많으면 LLM 프롬프트가 길어져 느려지고 비싸집니다. 실패했을 때는 "무엇이 왜 실패했는지"를 오류 메시지에 남겨야 실행 기록만 보고 원인을 찾을 수 있습니다.
### 2.2 Function Calling과 워크플로우 Tool 노드의 차이
29~31일차에 배운 Function Calling·MCP에서는 **LLM이 어떤 Tool을 쓸지 고르고**, 백엔드가 검증한 뒤 실행했습니다. 워크플로우의 Tool 노드는 **설계자가 미리 위치를 정해 둔** Tool입니다.
```mermaid
flowchart TB
subgraph FC["Function Calling (Agent 방식)"]
direction LR
q1["질문"] --> l1{{"LLM이 Tool 선택"}} --> v1["백엔드 검증"] --> t1["Tool 실행"] --> l1b{{"LLM 최종 답변"}}
end
subgraph WF["워크플로우 Tool 노드"]
direction LR
q2["질문"] --> t2["Tool 노드<br/>(항상 실행)"] --> l2{{"LLM 답변"}}
end
```
| 구분 | Function Calling | 워크플로우 Tool 노드 |
| --- | --- | --- |
| Tool 선택 | LLM | 설계자 |
| 실행 여부 | 상황마다 다름 | 경로에 있으면 항상 실행 |
| 장점 | 다양한 질문에 유연하게 대응 | 결과 예측·검증이 쉬움 |
| 어울리는 경우 | 질문 유형이 넓을 때 | 업무 절차가 정해져 있을 때 |
노코드 도구에도 "AI Agent 노드"처럼 LLM이 Tool을 고르게 하는 노드가 있습니다. 67일차에 두 방식을 함께 써 봅니다.
### 2.3 Tool이 실패하면
```text
$ uv run miniflow run flows/02-llm-tool.json -i city=평양 -i question="우산이 필요할까?"
[2/4] weather tool FAILED ToolError: Unknown city '평양'. Available: 서울, 부산, 제주, 강릉
Status: failed
```
`weather`가 실패하자 뒤의 `answer`·`result`는 실행되지 않았습니다. LLM이 날씨를 지어내는 것보다는 낫지만, 사용자는 아무 답도 받지 못합니다. 실무에서는 Tool 실패를 세 가지로 나눠 설계합니다.
| 실패 종류 | 예 | 처리 |
| --- | --- | --- |
| 입력이 잘못됨 | 없는 도시 | 사용자에게 다시 물음 (65일차 Condition) |
| 잠깐 안 됨 | 네트워크 끊김, 시간 초과 | 몇 번 다시 시도 (72일차 재시도) |
| 결과가 없음 | FAQ 검색 0건 | 담당자에게 보냄 (오늘 3단계 사례) |
### 2.4 검색 Tool의 한계: 단어가 달라지면
실습의 `search_faq`는 문의와 FAQ의 **키워드가 겹치는 정도**로 순위를 매깁니다. 수업 준비 때 확인한 결과입니다.
| 문의 | FAQ 검색 (키워드) | LLM 분류 (`classify`) |
| --- | --- | --- |
| 패스워드 초기화 부탁드립니다 | `FAQ-001 비밀번호 재설정` | |
| 암호를 바꾸고 싶어요 | **0건** ("암호"가 키워드에 없음) | `account` (뜻을 알아들음) |
| 재택 중인데 회사 시스템 연결이 안 돼요 | `FAQ-003 VPN 접속 오류` ("재택", "연결"이 겹침) | |
LLM은 "암호"와 "비밀번호"가 같은 뜻이라는 것을 알지만, 키워드 검색은 글자가 같아야 찾습니다. 그래서 실무 검색에는 **뜻이 비슷한 문서를 찾는 임베딩 검색(RAG)** 을 씁니다. 71일차에 `text-embedding-3-large`로 사내 규정 문서를 검색하는 RAG를 만듭니다.
---
## 3. Memory 노드: 워크플로우의 "기억"
| 종류 | 무엇을 기억하나 | 보관 기간 | 지난 실습 |
| --- | --- | --- | --- |
| 실행 컨텍스트 | 이번 실행에서 앞 노드들이 만든 결과 | 실행 1회 | 노드 결과 전달 |
| 단기 기억 | 최근 대화 몇 턴, 진행 중인 작업 상태 | 분~시간 (TTL) | Redis 세션 (`mini_agent_05_memory`) |
| 장기 기억 | 사용자 선호, 처리 이력 | 계속 | PostgreSQL `user_memories` |
| 지식 기억 | 회사 문서·FAQ | 문서가 바뀔 때까지 | pgvector RAG (`mini_agent_04_rag`) |
Memory를 설계할 때는 세 가지를 정합니다.
1. **무엇을** 저장할까: 다음 실행에 실제로 필요한 것만 (전체 대화를 무조건 저장하지 않음)
2. **어떤 키로** 찾을까: `user:minji`, `ticket:kim`처럼 사용자·업무 단위 키
3. **언제** 지울까: 보관 기간과 사용자의 삭제 요청
### 3.1 오늘 실습의 Memory는 어디에 저장되나
`miniflow`의 Memory는 실습 폴더의 `.miniflow-memory.json` 파일 하나입니다. 키와 값이 그대로 들어갑니다.
```json
{
"user:minji": {"preference": "실내 전시"},
"ticket:kim": {"last_ticket": "VPN 접속이 계속 끊겨요", "last_category": "network"}
}
```
```json
{"id": "log", "type": "memory_write", "key": "ticket:{{input.user}}",
"value": {"last_ticket": "{{input.ticket}}", "last_category": "{{nodes.classify.output}}"}}
```
키에 `{{input.user}}`를 넣었기 때문에 사용자마다 따로 저장됩니다. `uv run miniflow memory`로 내용을 보고, `--clear`로 지웁니다. 실무에서는 이 파일 대신 Redis·PostgreSQL을 쓰고, n8n에서는 Memory 노드나 DB 노드가 같은 역할을 합니다.
### 3.2 Memory와 실행 기록은 다르다
| 구분 | Memory | 실행 기록 |
| --- | --- | --- |
| 목적 | **다음 실행**이 쓰려고 | **사람**이 나중에 확인하려고 |
| 무엇을 | 필요한 값 몇 개 (선호, 마지막 분류) | 모든 노드의 입력·출력·시간 |
| 누가 읽나 | 워크플로우의 `memory_read` 노드 | 개발자·운영자 |
| 오늘 | `.miniflow-memory.json` | `runs/*.json` |
### 3.3 기억할 때 조심할 것
- **개인정보**: 이름·연락처·문의 원문을 그대로 오래 저장하면 유출되었을 때 피해가 큽니다. 필요한 것만 저장하고 보관 기간을 정합니다 (73일차 보안).
- **오래된 기억**: "지난달 선호"가 지금도 맞는지 알 수 없습니다. 저장한 날짜를 함께 남기면 판단하기 쉽습니다.
- **잘못된 기억**: LLM이 틀린 분류를 내면 그 값이 Memory에 저장되어 다음 실행까지 영향을 줍니다. 저장하기 전에 값을 확인하는 단계가 필요한 이유입니다.
---
## 4. LLM·Tool·Memory의 상호작용 패턴
```mermaid
flowchart TB
subgraph P1["① 근거 제공: Tool → LLM"]
direction LR
a1["Tool<br/>날씨 조회"] --> a2{{"LLM<br/>데이터 기반 답변"}}
end
subgraph P2["② 인자 추출: LLM → Tool"]
direction LR
b1{{"LLM<br/>문장에서 도시 추출"}} --> b2["Tool<br/>날씨 조회"]
end
subgraph P3["③ 개인화 순환: Memory → LLM → Memory"]
direction LR
c1[("Memory 읽기<br/>선호")] --> c2{{"LLM<br/>맞춤 추천"}} --> c3[("Memory 쓰기<br/>이력")]
end
subgraph P4["④ 검색 증강: RAG → LLM"]
direction LR
d1["검색<br/>관련 문서"] --> d2{{"LLM<br/>근거 인용 답변"}}
end
```
| 패턴 | 언제 쓰나 | 실습 파일 |
| --- | --- | --- |
| ① 근거 제공 | 최신·정확한 데이터가 필요할 때 | `02-llm-tool.json` |
| ② 인자 추출 | 자유 문장을 Tool 인자로 바꿔야 할 때 | Lab 02 도전 과제 |
| ③ 개인화 순환 | 사용자마다 다른 결과가 필요할 때 | `03-save-profile.json` → `04-recommend-with-memory.json` |
| ④ 검색 증강 | 내부 문서를 근거로 답해야 할 때 | `05-tech-support-ticket.json` (FAQ 검색) |
### 4.1 패턴마다 자주 생기는 실패
| 패턴 | 자주 생기는 실패 | 예방 |
| --- | --- | --- |
| ① 근거 제공 | LLM이 데이터에 없는 내용을 덧붙임 | "주어진 데이터만 사용" 지시, 근거 필드 인용 요구 |
| ② 인자 추출 | 형식이 달라 Tool이 못 알아들음 ("제주도", "Jeju") | 허용 값 목록을 프롬프트에, 추출 뒤 값 확인 |
| ③ 개인화 순환 | 틀린 값이 기억되어 다음 실행까지 영향 | 저장 전 확인, 기억에 날짜 남기기 |
| ④ 검색 증강 | 검색 결과가 없거나 엉뚱함 → 답도 틀림 | 결과 0건일 때의 경로, 검색 품질 측정 (71일차) |
② 인자 추출은 Lab 02 도전 과제에서 직접 확인합니다. 수업 준비 때 "다음 주에 부산 가는데 날씨 어때?"에서 형식 지시 없이 도시를 추출하게 했더니 기본 모델은 "질문에서 도시: 부산", 대체 모델은 "질문에서 언급된 도시: 부산"처럼 답해 Tool이 `Unknown city`로 실패했습니다 (2회 모두). 도시 이름만 답하도록 지시하자 둘 다 성공했습니다.
### 4.2 패턴을 이어 붙이면 업무가 된다
실제 업무 워크플로우는 이 패턴들을 이어 붙여 만듭니다. `05-tech-support-ticket.json`은 ③과 ④를 함께 사용합니다.
```mermaid
flowchart LR
S(["start<br/>user · ticket"]) --> H[("history<br/>Memory 읽기")]
S --> C{{"classify<br/>LLM 분류"}}
S --> F["faq<br/>FAQ 검색"]
H --> D{{"draft<br/>LLM 답변 초안"}}
C --> D
F --> D
D --> L[("log<br/>Memory 쓰기")]
L --> R(["result"])
classDef p3 fill:#e8f0fe,stroke:#4a6fa5
classDef p4 fill:#fdf2e3,stroke:#c08a2e
class H,L p3
class F p4
```
- 파란색(`history` → `draft` → `log`): ③ 개인화 순환. 같은 사람의 이전 문의를 참고하고, 이번 문의를 남깁니다.
- 주황색(`faq` → `draft`): ④ 검색 증강. FAQ를 근거로 답변 초안을 씁니다.
- `classify`: LLM 분류. 결과는 초안과 기록 두 곳에 쓰입니다.
---
## 5. 데이터 흐름: 노드 사이에서 무엇이 오가나
노드는 앞 노드의 **출력**을 자신의 **입력**으로 사용합니다. 노코드 도구에서는 이 연결을 "필드 매핑" 화면에서 마우스로 선택하고, `miniflow`에서는 `{{...}}` 표현으로 적습니다.
```mermaid
sequenceDiagram
participant U as 입력
participant W as weather (Tool)
participant A as answer (LLM)
participant R as result (Output)
U->>W: {"city": "제주"}
W->>A: {"city":"제주","condition":"비","temp_c":20,"rain_chance":80}
Note over A: 프롬프트에 {{nodes.weather.output}} 삽입
A->>R: "네, 비가 오므로 우산이 필요할 것 같습니다."
```
```json
{"id": "answer", "type": "llm",
"prompt": "날씨 데이터: {{nodes.weather.output}}\n질문: {{input.question}}"}
```
### 5.1 표현식 `{{ }}` 읽는 법
| 표현식 | 뜻 | 실제 값 (제주) |
| --- | --- | --- |
| `{{input.city}}` | 워크플로우 입력의 `city` | `제주` |
| `{{nodes.weather.output}}` | `weather` 노드의 결과 전체 | `{"city": "제주", "condition": "비", ...}` |
| `{{nodes.weather.output.rain_chance}}` | 결과 중 한 필드 | `80` |
| `{{nodes.faq.output.0.id}}` | 결과 목록의 첫 번째(0번) 항목의 `id` | `FAQ-003` (VPN 문의일 때) |
- 값이 표현식 하나뿐이면(`"{{nodes.weather.output}}"`) 원래 모양(객체·숫자)이 그대로 전달됩니다.
- 글자와 섞여 있으면(`"날씨 데이터: {{...}}"`) 문자열로 바뀌어 들어갑니다. 객체는 JSON 문자열이 됩니다.
- 없는 경로를 가리키면 `Cannot resolve '{{nodes.faq.output.0.id}}': '0' not found`처럼 **어느 부분이 없는지** 알려 줍니다. FAQ 검색 결과가 0건이면 `0`번 항목이 없어서 이 오류가 납니다 (3단계 점심 메뉴 사례).
n8n도 같은 생각입니다. `{{ $json.city }}`(바로 앞 노드 결과의 `city`), `{{ $('weather').item.json.rain_chance }}`(이름으로 고른 노드의 결과)처럼 씁니다.
### 5.2 데이터 흐름에서 자주 생기는 문제
| 문제 | 원인 | 예방 |
| --- | --- | --- |
| 다음 노드에서 값을 못 찾음 | 앞 노드 출력의 필드 이름이 다름 | 실행 기록에서 실제 출력 구조를 먼저 확인 |
| LLM이 엉뚱한 형식으로 답함 | 출력 형식을 지정하지 않음 | 정해진 값·JSON으로 답하도록 지시 |
| 빈 결과가 다음 노드로 전달됨 | 검색 결과 0건 | 결과가 없을 때의 경로 설계 (65일차 Condition) |
| 데이터가 너무 큼 | 문서 전체를 LLM에 전달 | 필요한 필드만 골라 전달 |
### 5.3 데이터 문제를 찾는 순서
1. `Status: failed`라면 실패한 노드의 `error`를 읽습니다. `not found`면 표현식 경로 문제입니다.
2. 앞 노드의 `output`을 실행 기록에서 열어 **실제 필드 이름과 모양**을 확인합니다.
3. LLM 답이 이상하면 그 노드의 `prompt`를 읽습니다. 데이터가 빠졌거나 `{}`처럼 비어 있지 않은지 봅니다.
4. 고친 뒤 `validate` → `run`으로 다시 확인합니다.
### 5.4 실행 순서는 연결이 정한다
`miniflow validate`는 연결(edges)을 보고 실행 순서를 계산합니다.
```text
$ uv run miniflow validate flows/05-tech-support-ticket.json
OK: 05 Tech support ticket (7 nodes, 8 edges)
Execution order: start -> history -> classify -> faq -> draft -> log -> result
```
`history`, `classify`, `faq`는 모두 `start`만 기다리므로 서로 독립입니다. 노코드 도구에서는 이런 노드를 동시에 실행해 전체 시간을 줄이기도 합니다. `draft`는 세 노드가 모두 끝나야 실행됩니다.
`validate`가 `OK`라고 해서 흐름이 맞는 것은 아닙니다. Lab 02에서 `length` 노드를 추가하고 `["answer", "length"]` 연결만 넣으면 `validate`는 통과하지만 순서가 `... answer -> result -> length`가 되어 `result`에서 `'length' not found`가 납니다. **`Execution order` 줄을 읽고, 내가 원한 순서인지 확인**하는 습관을 들입니다.
---
## 6. Lab 02 안내
- 실습 안내: [Last3Mon/64/LAB-02-LLM-TOOL-MEMORY.md](../../Last3Mon/64/LAB-02-LLM-TOOL-MEMORY.md)
- 할 일
1. `02-llm-tool.json`으로 Tool 결과가 LLM 프롬프트에 들어가는 과정 확인
2. `03` → `04` 순서로 실행해 Memory가 실행과 실행 사이를 이어 주는 것 확인
3. 워크플로우에 Tool 노드를 하나 추가하고 데이터 연결 고치기
4. Mock, OpenAI, 대체 Ollama 모델의 결과·속도 비교 (작은 모델이 분류를 틀리는 경우 관찰)
---
## 7. 스스로 확인하기
<details><summary>Q1. 분류 노드의 시스템 지시에 "Reply with the label only."를 넣는 이유는?</summary>
다음 노드가 `"device"`처럼 정해진 값과 비교하려면 설명 없는 값 하나가 필요합니다. 형식 지시가 없으면 "프린터 문제로 분류할 수 있습니다" 같은 문장이 나옵니다.
</details>
<details><summary>Q2. 같은 문의를 4번 분류해 4번 모두 같은 답이 나왔습니다. 맞는 답이라고 할 수 있나요?</summary>
아닙니다. 일관성과 정확성은 다릅니다. "메일 프로그램 로그인"은 4번 모두 `software`였지만 FAQ 검색은 계정 문제로 봤습니다. 경계가 모호한 경우의 분류 기준을 정해야 합니다.
</details>
<details><summary>Q3. "암호를 바꾸고 싶어요"에서 FAQ 검색이 0건인 이유와 해결 방향은?</summary>
키워드 검색이라 "암호"가 FAQ 키워드("비밀번호", "패스워드")와 글자가 달라 찾지 못했습니다. 뜻으로 찾는 임베딩 검색(RAG, 71일차)이나 키워드 보강이 필요합니다.
</details>
<details><summary>Q4. Memory와 실행 기록의 차이는?</summary>
Memory는 다음 실행이 쓰려고 필요한 값만 남기고, 실행 기록은 사람이 확인하려고 모든 노드의 입력·출력·시간을 남깁니다.
</details>
<details><summary>Q5. `Cannot resolve '{{nodes.faq.output.0.id}}': '0' not found` 오류는 무슨 뜻인가요?</summary>
FAQ 검색 결과 목록에 0번(첫 번째) 항목이 없다는 뜻입니다. 검색 결과가 0건일 때 납니다. 결과가 없을 때의 경로를 설계해야 합니다.
</details>
## 정리
- LLM은 판단과 생성을, Tool은 최신 데이터와 외부 실행을, Memory는 실행 사이의 연속성을 맡습니다.
- LLM 노드는 시스템 지시·프롬프트·모델·temperature로 설정하고, 다음 노드가 읽을 답은 **정해진 형식**으로 받습니다.
- 대체 모델은 서비스를 이어 주지만 같은 품질을 보장하지 않습니다. 실행 기록의 `provider`로 누가 답했는지 확인합니다.
- 워크플로우 Tool 노드는 설계자가 위치를 정하므로 결과를 예측하기 쉽습니다. 실패는 입력 오류·일시 장애·결과 없음으로 나눠 설계합니다.
- 상호작용 패턴은 근거 제공, 인자 추출, 개인화 순환, 검색 증강 네 가지로 정리할 수 있습니다.
- 노드 사이의 데이터 연결이 틀리면 워크플로우가 멈춥니다. `error` → 앞 노드 `output` → `prompt` 순서로 실행 기록을 확인합니다.
+360
View File
@@ -0,0 +1,360 @@
# 3단계 · 업무 자동화 시나리오 분석
- 시간: 5~6교시
- 학습 목표
- 업무를 보고 자동화하기 좋은 후보인지 기준에 따라 판단할 수 있다.
- 하나의 업무를 현재 방식(AS-IS)과 자동화 이후(TO-BE)로 나누어 분석할 수 있다.
- 정상 경로뿐 아니라 예외 경로와 사람 확인 단계를 설계에 넣을 수 있다.
- 업무 단계를 LLM·Tool·Memory·데이터 처리·사람 확인 노드로 바꿀 수 있다.
- 분석 결과를 워크플로우 JSON으로 옮기고 실행해 검증할 수 있다.
- 실습: [Lab 03 · 업무 자동화 시나리오 분석과 워크플로우 설계](../../Last3Mon/64/LAB-03-SCENARIO-ANALYSIS.md)
---
## 1. 좋은 워크플로우는 좋은 분석에서 시작한다
도구부터 열고 노드를 놓기 시작하면 금방 길을 잃습니다. 먼저 **업무를 글과 그림으로 분해**하고, 그다음에 노드로 옮깁니다.
```mermaid
flowchart LR
A["① 후보 업무 고르기"] --> B["② 현재 방식 파악<br/>AS-IS"]
B --> C["③ 자동화 방식 설계<br/>TO-BE"]
C --> D["④ 단계 → 노드 매핑"]
D --> E["⑤ 워크플로우 작성·실행"]
E --> F["⑥ 결과 검증·개선"]
F -.-> C
```
| 단계 | 결과물 | 오늘 어디서 |
| --- | --- | --- |
| ① 후보 고르기 | 5기준 점수표 | 템플릿 1절 |
| ② AS-IS | 현재 흐름 그림, 단계별 시간·문제점 | 템플릿 3절 |
| ③ TO-BE | 자동화 흐름 그림 (정상 + 예외) | 템플릿 4절 |
| ④ 노드 매핑 | 단계별 노드 종류·입력·출력 표 | 템플릿 5절 |
| ⑤ 작성·실행 | `flows/my-scenario.json` | Lab 03 3절 |
| ⑥ 검증 | 정상·문제 입력의 실행 결과 | Lab 03 4절 |
분석에 시간이 더 걸리는 것이 정상입니다. 실무에서는 ①~④에 전체 시간의 절반 이상을 씁니다. 분석이 잘 되어 있으면 ⑤는 표를 그대로 옮기는 일이 됩니다.
---
## 2. 자동화 후보 고르기
모든 업무가 자동화에 어울리지는 않습니다. 다음 다섯 가지 기준으로 점수를 매겨 봅니다.
| 기준 | 높은 점수(자동화 유리) | 낮은 점수(자동화 불리) |
| --- | --- | --- |
| 반복성 | 매일·매주 같은 일이 반복 | 1년에 몇 번 |
| 절차의 명확성 | 단계가 문서로 정리됨 | 담당자마다 방식이 다름 |
| 데이터 준비 | 입력이 메일·폼·DB에 디지털로 있음 | 종이·구두 전달 |
| 판단의 난이도 | 분류·요약·초안처럼 AI가 잘하는 판단 | 법적 책임이 큰 최종 결정 |
| 실패했을 때 영향 | 사람이 검토하면 되돌릴 수 있음 | 결제·삭제처럼 되돌리기 어려움 |
```mermaid
quadrantChart
title 자동화 후보 판단
x-axis "반복 적음" --> "반복 많음"
y-axis "실패 영향 큼" --> "실패 영향 작음"
quadrant-1 "우선 자동화"
quadrant-2 "부분 자동화"
quadrant-3 "사람이 처리"
quadrant-4 "사람 확인 후 자동화"
"IT 문의 분류": [0.85, 0.80]
"회의록 요약": [0.70, 0.85]
"주간 매출 리포트": [0.60, 0.70]
"계약서 최종 승인": [0.20, 0.15]
"환불 처리": [0.75, 0.30]
```
환불처럼 반복은 많지만 실패 영향이 큰 업무는 **사람 확인 단계**를 넣어 자동화합니다. 9월의 `mini_agent_07_human_approval`에서 변경 작업 직전에 멈추고 승인받던 구조가 여기에 해당합니다.
### 2.1 점수 매겨 보기
각 기준을 1~5점으로 매기고 합칩니다. 실패 영향은 **작을수록 높은 점수**입니다.
| 기준 | IT 문의 분류·답변 초안 | 환불 처리 | 계약서 최종 승인 |
| --- | :-: | :-: | :-: |
| 반복성 | 5 (하루 수십 건) | 4 | 1 |
| 절차의 명확성 | 4 (FAQ·매뉴얼 있음) | 4 (환불 규정 있음) | 2 |
| 데이터 준비 | 5 (메일·폼) | 4 (주문 DB) | 3 |
| 판단의 난이도 | 4 (분류·초안) | 3 | 1 (법적 판단) |
| 실패했을 때 영향 | 4 (사람이 고쳐 보내면 됨) | 2 (돈이 나감) | 1 |
| 합계 (25점 만점) | **22** | 17 | 8 |
| 결론 | 우선 자동화 | 사람 확인 후 자동화 | 사람이 처리, 자료 정리만 보조 |
점수는 정답이 있는 계산이 아니라 **팀이 같은 기준으로 이야기하기 위한 도구**입니다. 특히 "실패했을 때 영향"이 1~2점이면 합계가 높아도 사람 확인을 반드시 넣습니다.
### 2.2 "전부 자동화"보다 "가장 아픈 단계부터"
후보를 골랐으면 업무 전체가 아니라 **가장 시간이 많이 들거나 실수가 잦은 단계**부터 자동화합니다. IT 문의라면 "FAQ 검색 + 답변 초안"이 가장 시간이 많이 듭니다(3.1절 표). 메일 발송까지 처음부터 자동화하지 않고, 초안을 담당자에게 보여 주는 것부터 시작하면 실패해도 피해가 없습니다.
---
## 3. 사례로 분석하기: 사내 IT 문의
80~82일차 단위 프로젝트 주제인 "지능형 Tech Support 자동화"의 가장 작은 버전으로 분석해 봅니다.
### 3.1 AS-IS (현재 방식)
```mermaid
flowchart LR
U["직원<br/>메일로 문의"] --> H["헬프데스크 담당자<br/>메일 확인"]
H --> C["문의 유형 판단<br/>계정·네트워크·장비·SW"]
C --> S["FAQ·매뉴얼 검색"]
S --> W["답변 메일 작성"]
W --> R[("처리 대장 엑셀 기록")]
W --> U
```
| 단계 | 소요 시간 | 문제점 |
| --- | --- | --- |
| 메일 확인 | 수시 | 출근 전·회의 중에는 처리 지연 |
| 유형 판단 | 1~2분 | 담당자마다 분류가 다름 |
| FAQ 검색 | 3~5분 | 같은 질문을 매번 다시 검색 |
| 답변 작성 | 5분 | 반복 문장을 매번 새로 작성 |
| 대장 기록 | 1분 | 누락이 잦아 통계가 부정확 |
AS-IS를 그릴 때는 담당자에게 직접 물어봅니다. "보통 몇 건이 오나요?", "가장 오래 걸리는 단계는?", "실수가 가장 많이 나는 곳은?" 세 질문의 답이 TO-BE의 목표가 됩니다.
### 3.2 무엇이 좋아져야 하나: 측정 지표
자동화가 성공했는지 말하려면 AS-IS 때 숫자를 먼저 적어 둡니다. 아래 숫자는 하루 문의 40건을 가정한 예시입니다.
| 지표 | AS-IS (예시) | TO-BE 목표 | 어떻게 재나 |
| --- | --- | --- | --- |
| 한 건 처리 시간 | 약 10~13분 | 담당자 확인 2분 | 접수~발송 시간 |
| 하루 담당자 시간 | 약 7~8시간 | 약 1.5시간 | 건수 × 처리 시간 |
| 분류 일치율 | 담당자마다 다름 | 정해진 값 기준 90% 이상 | 담당자가 고친 비율 |
| 대장 기록 누락 | 자주 | 0건 | 실행 기록 건수와 대장 비교 |
| 첫 답변까지 시간 | 몇 시간 | 몇 분 | 접수 시각과 초안 시각 |
지표가 없으면 "좋아진 것 같다"로 끝납니다. 지표를 정하면 75일차(비용), 77~78일차(운영 지표·품질)에서 실제로 측정할 수 있습니다.
### 3.3 TO-BE (자동화 이후)
```mermaid
flowchart LR
T(["문의 접수"]) --> M[("이전 문의 이력<br/>Memory 읽기")]
T --> L1{{"문의 분류<br/>LLM"}}
T --> F["FAQ 검색<br/>Tool·RAG"]
M --> L2{{"답변 초안<br/>LLM"}}
L1 --> L2
F --> L2
L2 --> K["담당자 확인<br/>(사람)"]
K --> W[("이력 저장<br/>Memory 쓰기")]
W --> O(["답변 발송"])
```
### 3.4 예외 경로까지 그리기
위 TO-BE는 **모든 것이 잘될 때**의 그림입니다. 오늘 Lab 03 1절에서 "점심 메뉴 추천해줘"를 넣으면 FAQ 검색이 0건이라 마지막 `result` 노드에서 실패합니다. 실제 업무에서는 이런 경우가 하루에도 여러 번 생깁니다. 분석 단계에서 예외를 먼저 적어 둡니다.
| 예외 | 언제 생기나 | 어디로 보낼까 |
| --- | --- | --- |
| 범위 밖 문의 | 분류가 `other` | 담당자에게 바로 전달, 초안 만들지 않음 |
| 근거 없음 | FAQ 검색 0건 | "담당자가 확인 후 연락" 안내 + 담당자 전달 |
| 분류와 검색이 다름 | 분류는 `software`, FAQ는 계정 문서 | 담당자 확인 필수 |
| Tool 실패 | 검색 시스템 장애 | 다시 시도 → 그래도 실패하면 담당자 전달 |
| 반복 문의 | 이력에 같은 문의가 최근에 있음 | 우선순위 올려 담당자 전달 |
```mermaid
flowchart LR
T(["문의 접수"]) --> L1{{"분류"}}
T --> F["FAQ 검색"]
L1 & F --> C{"분류가 other?<br/>또는 FAQ 0건?"}
C -->|"아니요 (정상)"| L2{{"답변 초안"}} --> K["담당자 확인"] --> O(["발송"])
C -->|"예 (예외)"| P(["담당자에게 바로 전달<br/>+ 접수 안내"])
```
가운데 마름모(`C`)가 65일차에 배우는 **Condition(IF) 노드**입니다. 오늘 `miniflow`에는 분기 노드가 없으므로, 예외 경로는 분석 문서에 그림과 표로 남겨 둡니다.
### 3.5 단계를 노드로 바꾸기
| 업무 단계 | 노드 종류 | 이유 |
| --- | --- | --- |
| 문의 접수 | 입력(Trigger) | 메일·폼이 들어오면 시작 |
| 이전 문의 확인 | Memory 읽기 | 같은 사람이 반복 문의했는지 파악 |
| 유형 판단 | LLM | 자유 문장을 정해진 분류로 바꾸는 판단 |
| FAQ 검색 | Tool(RAG) | 회사 문서는 LLM이 모르므로 검색해 근거 확보 |
| 답변 작성 | LLM | 검색 결과를 바탕으로 문장 생성 |
| 답변 확인 | 사람 | 잘못된 안내를 막기 위한 확인 단계 |
| 처리 기록 | Memory 쓰기 | 다음 문의와 통계를 위해 저장 |
이 TO-BE에서 사람 확인을 뺀 형태가 실습 파일 `05-tech-support-ticket.json`입니다.
노드 매핑 표에는 **입력과 출력**도 적습니다. 그래야 JSON으로 옮길 때 `{{ }}` 연결을 바로 쓸 수 있습니다.
| 노드 | 입력 | 출력 | `05` 파일에서 |
| --- | --- | --- | --- |
| history | 사용자 ID | 이전 문의·분류 | `ticket:{{input.user}}` |
| classify | 문의 문장 | `account` 등 정해진 값 하나 | `{{nodes.classify.output}}` |
| faq | 문의 문장 | FAQ 목록 (최대 2건) | `{{nodes.faq.output.0.id}}` |
| draft | 문의, 분류, 이력, FAQ | 답변 초안 문장 | `{{nodes.draft.output}}` |
| log | 문의, 분류 | 저장한 값 | `last_ticket`, `last_category` |
---
## 4. 시나리오 분석 템플릿
시나리오를 분석할 때는 아래 항목을 빠짐없이 채웁니다. 실습 폴더의 [SCENARIO-ANALYSIS-TEMPLATE.md](../../Last3Mon/64/templates/SCENARIO-ANALYSIS-TEMPLATE.md)를 복사해서 사용합니다.
| 항목 | 질문 |
| --- | --- |
| 목표 | 이 자동화로 무엇이 좋아지나? (시간·품질·누락 감소) |
| 시작 조건 | 무엇이 들어오면 시작하나? |
| 입력 데이터 | 어떤 필드가 들어오나? |
| 처리 단계 | 어떤 순서로 무엇을 하나? |
| 필요한 Tool·데이터 | 외부에서 무엇을 가져오거나 실행해야 하나? |
| 기억 | 이전 실행에서 무엇을 읽고, 무엇을 남기나? |
| 결과와 전달 | 결과는 어떤 형식으로 누구에게 가나? |
| 사람 확인 | 어느 단계에서 사람이 확인해야 하나? |
| 예외 상황 | 데이터가 없거나 Tool이 실패하면? |
| 성공 기준 | 무엇을 보면 잘 동작한다고 말할 수 있나? |
### 4.1 채운 예시: IT 문의
| 항목 | 내용 |
| --- | --- |
| 목표 | 담당자의 반복 작업(분류·FAQ 검색·답변 작성) 시간을 줄이고, 처리 대장 누락을 없앤다 |
| 시작 조건 | 직원이 문의 폼을 제출하거나 헬프데스크 메일이 도착 |
| 입력 데이터 | `user`(사번 또는 이름), `ticket`(문의 문장) |
| 처리 단계 | 이력 읽기 → 분류 → FAQ 검색 → 답변 초안 → 담당자 확인 → 이력 저장 → 발송 |
| 필요한 Tool·데이터 | FAQ 검색(`search_faq`), 향후 사내 매뉴얼 검색 |
| 기억 | 읽기: 같은 사용자의 마지막 문의·분류 / 쓰기: 이번 문의·분류 |
| 결과와 전달 | 분류·FAQ 번호·답변 초안을 담당자에게, 확인 후 직원에게 메일 |
| 사람 확인 | 답변 발송 직전 (초안 수정 가능) |
| 예외 상황 | 분류 `other`·FAQ 0건 → 담당자에게 바로 / 검색 실패 → 재시도 후 담당자 |
| 성공 기준 | 테스트 문의 10건 중 분류 9건 이상 정답, FAQ 0건 문의는 모두 담당자에게 전달, 대장 누락 0건 |
성공 기준은 **확인할 수 있는 문장**으로 적습니다. "잘 동작한다" 대신 "10건 중 9건"처럼 적어야 67일차의 워크플로우 테스트로 그대로 옮길 수 있습니다.
### 4.2 AI를 쓰지 않아야 하는 단계
| 단계 | AI 대신 | 이유 |
| --- | --- | --- |
| 금액·수량 계산 | Tool·데이터 처리 | 정확해야 하고 규칙으로 해결됨 |
| 날짜 형식 변환 | 데이터 처리 | 항상 같은 결과가 필요 |
| 권한 확인 | 규칙·Tool | 보안 판단을 AI 답변에 맡기면 위험 |
| 결제·삭제 실행 | 사람 확인 후 Tool | 되돌리기 어려움 |
수업 준비 때 S5(분류별 담당 부서 안내)를 만들면서, 분류값 → 담당 부서 대응표를 LLM 프롬프트에 넣어 봤습니다. 대체 모델(`gemma3:4b`)은 부서 이름 뒤에 알 수 없는 로마자를 덧붙였습니다. **정해진 대응표는 LLM이 아니라 규칙(데이터 처리·Condition 노드)으로** 처리하는 것이 맞습니다.
### 4.3 사람 확인을 어디에 둘까
| 방식 | 언제 확인하나 | 어울리는 업무 | 단점 |
| --- | --- | --- | --- |
| 사전 승인 | 실행 직전 (보내기·결제 전) | 환불, 외부 발송, 데이터 변경 | 사람이 바쁘면 처리가 늦어짐 |
| 예외만 확인 | 조건에 걸린 것만 (FAQ 0건, 낮은 확신) | IT 문의, 리뷰 답글 | 조건을 잘못 정하면 빠져나감 |
| 사후 검토 | 처리 후 일부를 골라 확인 | 회의록 요약, 내부 보고 | 잘못된 결과가 이미 나간 뒤 |
처음에는 **사전 승인**으로 시작하고, 실행 기록으로 품질을 확인한 뒤 **예외만 확인**으로 줄여 가는 것이 안전합니다. 65일차에는 먼저 "담당자 확인" 경로로 넘기는 것부터 만들고, 69일차에는 초안을 규칙으로 검토하는 단계를 넣습니다. n8n에서 실제 승인 대기는 Wait 노드나 메신저·메일 승인 버튼으로 만들 수 있습니다.
---
## 5. 분석 연습용 시나리오
Lab 03에서 아래 중 하나를 골라 분석합니다. 자신의 업무 경험에서 시나리오를 만들어도 좋습니다.
| 번호 | 시나리오 | 힌트 |
| --- | --- | --- |
| S1 | 회의록 요약 후 할 일 정리 | LLM 요약·추출 → 담당자별 할 일 → 기록 |
| S2 | 쇼핑몰 상품 리뷰 분류와 답글 초안 | LLM 감정·유형 분류 → FAQ·정책 검색 → 답글 초안 |
| S3 | 신규 입사자 질문 응답 | 사내 규정 검색(RAG) → 답변 → 질문 이력 저장 |
| S4 | 출장 전 날씨·준비물 안내 | 일정 입력 → 날씨 Tool → 사용자 선호 Memory → 안내 |
| S5 | IT 문의 처리 확장 | `05` 워크플로우에 분류별 담당 부서 안내 추가 |
### 5.1 오늘 만들 수 있는 부분과 "향후 노드"
오늘 쓸 수 있는 노드는 입력·LLM·Tool(`get_weather`, `search_faq`, `count_chars`)·Memory 읽기/쓰기·출력입니다. 나머지는 분석 문서에 **향후 노드**로 적고, 65일차 이후 n8n에서 만듭니다.
| 번호 | 오늘 `my-scenario.json`으로 만들 수 있는 것 | 향후 노드로 남길 것 |
| --- | --- | --- |
| S1 | 회의록 요약(LLM), 할 일 추출(LLM), 글자 수(`count_chars`), 회의 이력 저장(Memory) | 담당자 조회, 메신저 알림, 일정 등록 |
| S2 | 리뷰 감정 분류(LLM), 답글 초안(LLM), 고객별 리뷰 이력(Memory) | 상품·정책 검색, 답글 게시, 부정 리뷰 담당자 알림 |
| S3 | 질문 분류(LLM), 답변(LLM), 질문 이력(Memory). IT 관련 질문은 `search_faq` 사용 가능 | 사내 규정 문서 검색(RAG, 71일차) |
| S4 | 날씨 조회(`get_weather`, 서울·부산·제주·강릉), 선호 읽기(Memory), 준비물 안내(LLM) | 일정 시스템 연동, 출장 전날 자동 실행(Schedule) |
| S5 | `05`의 분류·FAQ·초안·이력 | 분류별 담당 부서 연결(Condition·Switch, 65·68일차), 메신저 알림 |
### 5.2 직접 시나리오를 만들 때
자기 업무로 만들면 분석이 훨씬 구체적이 됩니다. 아래 질문으로 시작합니다.
1. 일주일에 몇 번 하는 일인가? (3번 이하면 다른 일을 고릅니다)
2. 입력은 어디서 오나? (메일, 엑셀, 메신저, 웹 폼)
3. 내가 "읽고 판단하는" 단계는 어디인가? → LLM 후보
4. "찾아보거나 복사하는" 단계는 어디인가? → Tool 후보
5. "지난번 것을 참고하는" 단계는 어디인가? → Memory 후보
6. 틀리면 누가 곤란해지나? → 사람 확인 위치
---
## 6. 흔한 분석 실수
| 실수 | 모습 | 고치는 법 |
| --- | --- | --- |
| LLM 하나로 다 하기 | "LLM이 분류하고 검색하고 답한다" | 단계마다 노드를 나누고 종류를 정함 |
| 정상 경로만 그리기 | 검색 0건, Tool 실패가 그림에 없음 | 3.4절 예외 표를 먼저 채움 |
| 입력·출력이 없는 매핑 | "분류 → 답변"만 적음 | 노드마다 무엇이 들어가고 나오는지 적음 |
| 성공 기준이 막연함 | "잘 동작한다" | "10건 중 9건", "누락 0건"처럼 숫자로 |
| 규칙으로 될 일을 AI에 | 부서 대응표, 날짜 변환을 LLM에 | 데이터 처리·Condition 노드로 |
| 사람 확인 위치가 없음 | 결제·발송이 자동으로 나감 | 되돌리기 어려운 단계 앞에 확인 |
---
## 7. Lab 03 안내
- 실습 안내: [Last3Mon/64/LAB-03-SCENARIO-ANALYSIS.md](../../Last3Mon/64/LAB-03-SCENARIO-ANALYSIS.md)
- 할 일
1. 시나리오 하나를 골라 분석 템플릿 작성 (AS-IS·TO-BE Mermaid 포함)
2. TO-BE를 `flows/my-scenario.json`으로 작성하고 `validate`·`show`로 확인
3. 정상 입력과 문제가 생기는 입력으로 각각 실행하고 실행 기록 비교
4. 분석 문서와 워크플로우 파일 제출
### 7.1 발표와 제출 확인 기준
| 기준 | 확인할 것 |
| --- | --- |
| 후보 판단 | 5기준 점수와 근거가 있다 |
| AS-IS·TO-BE | 두 그림이 있고, TO-BE에 예외 경로가 하나 이상 있다 |
| 노드 매핑 | 단계마다 노드 종류·입력·출력이 있다 |
| AI 사용 | AI 노드마다 "왜 AI가 필요한지"를 설명할 수 있다 |
| 실행 | `validate` 통과, 정상 입력과 문제 입력의 결과를 비교했다 |
| 향후 노드 | 오늘 못 만든 노드를 적었다 |
---
## 8. 스스로 확인하기
<details><summary>Q1. 환불 처리는 반복이 많은데 왜 "우선 자동화"가 아닌가요?</summary>
실패했을 때 돈이 나가 되돌리기 어렵기 때문입니다. 실패 영향 점수가 낮으면 합계가 높아도 사람 확인 단계를 넣어 자동화합니다.
</details>
<details><summary>Q2. TO-BE 그림에 예외 경로를 꼭 그려야 하는 이유는?</summary>
예외는 실제 업무에서 매일 생깁니다. 점심 메뉴 사례처럼 예외 경로가 없으면 워크플로우가 마지막에 가서야 실패하고, 사용자는 답을 받지 못합니다.
</details>
<details><summary>Q3. "분류값 → 담당 부서" 대응을 LLM 노드에 맡기지 않는 이유는?</summary>
정해진 대응표라 규칙으로 항상 정확하게 처리할 수 있습니다. LLM에 맡기면 느리고, 수업 준비 때처럼 부서 이름에 엉뚱한 글자를 붙이는 일이 생길 수 있습니다.
</details>
<details><summary>Q4. 성공 기준 "답변 품질이 좋아진다"를 어떻게 고치면 좋을까요?</summary>
확인할 수 있는 숫자로 바꿉니다. 예: "테스트 문의 10건 중 담당자가 고치지 않고 보낸 초안이 7건 이상", "FAQ 0건 문의는 모두 담당자에게 전달".
</details>
<details><summary>Q5. 처음 자동화할 때 사람 확인을 사전 승인으로 시작하는 이유는?</summary>
품질을 아직 모르기 때문입니다. 실행 기록으로 품질을 확인한 뒤 예외만 확인하는 방식으로 줄여 갑니다.
</details>
## 정리
- 자동화는 도구보다 분석이 먼저입니다. 반복성·명확성·데이터·판단 난이도·실패 영향으로 후보를 고릅니다.
- AS-IS의 문제점과 숫자를 먼저 적고, TO-BE의 목표와 성공 기준을 확인할 수 있는 숫자로 정합니다.
- TO-BE에는 정상 경로와 함께 예외 경로(범위 밖, 근거 없음, Tool 실패)를 그립니다.
- 각 단계를 노드 종류로 바꾸고, 노드마다 입력과 출력을 적습니다.
- 계산·변환·권한·대응표처럼 규칙으로 되는 일은 AI에게 맡기지 않습니다.
- 실패 영향이 큰 단계에는 사람 확인을 두고, 품질을 확인한 뒤 확인 범위를 줄여 갑니다.