AI가 "업무를 대신한다"는 말은 이제 누구나 한다. 문제는 구체적으로 어떻게인지를 설명하는 곳이 드물다는 거다. LLM이 텍스트를 잘 생성한다는 건 모두가 안다. 그런데 그 LLM이 실제로 발주서를 작성하고, 물류를 배정하고, 서류를 검증하려면 어떤 구조가 필요한가? 이 글은 REINDEERS에서 현재 설계 중인 AI Agent 시스템의 구조에 대한 이야기다. Phase 1 구현에 진입하는 단계이며, 여기서 설명하는 내용은 설계 방향과 목표 아키텍처다.
이 구조가 최종적으로 지향하는 모습부터 먼저 밝히겠다. REINDEERS·POP·DVRP는 지금 AI로 전환되는 구조 위에 올라서고 있다. 조직도 안에서 직원을 등록할 때 '사람', 'AI Agent', '로봇' 중 하나를 선택하는 구조다. 사람은 전략과 방향을 결정하고, 실제 업무는 조직도 안에 등록된 AI Agent가 수행한다. paperclip 같은 외부 AI 도구를 외부에서 연결하는 방식이 아니라, 회사 조직 구조 자체에 AI가 직원으로 편입되는 방식이다. 이 글은 그 AI 직원들이 실제로 업무를 실행하기 위해 시스템이 어떤 모습이어야 하는지에 대한 이야기다.
REINDEERS에서는 이걸 AX라고 부른다. Agent Experience. UX가 사람이 시스템을 경험하는 방식이라면, AX는 AI Agent가 시스템 안에서 업무를 수행하는 방식이다. 사람을 위해 UI를 설계하듯, Agent를 위해 시스템의 인터페이스를 설계한다는 뜻이다.
Chatbot과 Agent의 차이
먼저 용어를 명확히 하자. 우리가 만들고 있는 건 챗봇이 아니다.
챗봇은 질문에 답한다. "이 화물의 현재 위치가 어디야?"라고 물으면 DB에서 조회해서 알려준다. 유용하지만, 업무를 대신하는 건 아니다. 정보를 전달할 뿐이고, 실행은 전적으로 사람의 몫이다.
Agent는 다르다. 목표를 받으면 그 목표를 달성하기 위해 여러 행동을 스스로 계획하고 실행한다. "태국 A사에 지난번과 같은 조건으로 견적 요청해줘"라는 명령을 받으면, Agent는 이전 견적 이력을 조회하고, 조건을 추출하고, 새 견적 요청서를 작성하고, 승인을 요청한다. 사람이 각 단계를 일일이 수행하는 게 아니라, Agent가 전체 흐름을 진행하면서 필요한 지점에서만 사람에게 확인을 받는다.
이 차이가 B2B에서 왜 중요한가. REINDEERS 파트너사의 운영 담당자는 하루에 다수의 반복 업무를 처리한다. 견적 요청, 발주 확인, 서류 체크, 물류 배정, 대금 확인. 각각의 업무는 10~30분이 걸리고, 대부분은 패턴이 반복된다. 인보이스 금액이 한 자리 틀리면 통관이 막히고, 선적 일정이 하루 밀리면 컨테이너를 놓친다. 정확해야 하면서 반복적인 업무 — Agent가 가장 잘하는 영역이다.
조직도 안의 CEO Agent와 부서별 Agent
기술적인 층 설명으로 들어가기 전에, 이 Agent들이 조직도에서 어떻게 배치될지부터 이야기해야 한다. 우리가 그리는 그림은 단순하다. 최상위에 CEO Agent가 있고, 그 아래에 구매·생산·영업·물류·재무·통관 부서마다 부서별 Agent가 있다. 사람 사용자는 CEO Agent에게 전략 수준의 방향을 제시한다. CEO Agent는 그 방향을 KPI 단위로 해석해서 부서별 Agent에게 업무를 배분하고, 부서 Agent는 자기 도메인에서 실제 업무를 실행한다. 판단이 어려운 예외만 다시 사람에게 올라온다.
이 글에서 이야기하는 Orchestrator Agent, Document Agent, Trading Agent, Logistics Agent, Workflow Agent는 이 조직도의 초기 버전이다. Orchestrator는 CEO Agent의 프로토타입에 가깝고, 네 개의 전문 Agent는 부서 Agent의 초기 형태다. 앞으로 POP와 연결되면 생산 Agent가, 재무·통관 영역이 붙으면 재무 Agent와 통관 Agent가 조직도에 추가된다. 중요한 건 이 구조가 그냥 "마이크로서비스의 다른 이름"이 아니라는 점이다. 각 Agent는 조직도의 직원 레코드로 실제로 등록되고, 사람 직원과 동일한 권한/예산/KPI 모델을 공유한다.
AX 아키텍처 전체 구조
AX는 네 개의 층으로 구성된다.
┌─────────────────────────────────────────────────────┐
│ 사용자 인터페이스 │
│ (Chat UI, Dashboard, Notification) │
└──────────────────────┬──────────────────────────────┘
│ 자연어 요청 / 승인·반려
▼
┌─────────────────────────────────────────────────────┐
│ Orchestrator Agent │
│ │
│ - Intent Classification (요청 분석) │
│ - Plan Generation (실행 계획 수립) │
│ - Agent Dispatch (전문 Agent 호출) │
│ - Result Aggregation (결과 종합) │
└──────┬─────────┬─────────┬─────────┬────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐┌─────────┐┌─────────┐┌─────────┐
│Document ││Workflow ││Trading ││Logistics│
│ Agent ││ Agent ││ Agent ││ Agent │
└────┬────┘└────┬────┘└────┬────┘└────┬────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ Tool Layer │
│ │
│ Read Tools Write Tools External Tools │
│ (조회, 검색) (생성/수정) (외부 API) │
│ 자동 승인 사람 승인 필요 조건부 승인 │
└─────────────────────────────────────────────────────┘
가장 위에 사용자가 있고, 사용자의 요청은 자연어로 Orchestrator에게 전달된다. Orchestrator는 요청을 분석해서 어떤 전문 Agent를 호출할지 결정하고, 각 Agent는 자신의 도메인에서 Tool을 사용하여 실제 작업을 수행한다.
Orchestrator Agent: 관제탑
사용자의 요청이 가장 먼저 도달하는 곳이다. Orchestrator의 역할은 세 가지로 명확하다. 첫째, 사용자의 의도를 파악한다(Intent Classification). 둘째, 실행 계획을 수립한다(Plan Generation). 셋째, 전문 Agent를 호출하고 결과를 종합한다.
중요한 원칙이 하나 있다. Orchestrator는 직접 데이터를 조회하거나 수정하지 않는다. 오직 "누구에게 시킬 것인가"만 결정한다. 이 분리가 필요한 이유는 권한 관리와 감사 추적 때문이다. 모든 실제 작업은 전문 Agent를 통해 수행되고, 전문 Agent의 Tool 호출은 전부 로그에 기록된다.
Orchestrator의 의사결정 과정을 의사코드로 보면 이렇다.
user_input = "태국 B사 지난주 견적이랑 같은 조건으로 중국 C사에 견적 보내줘"
# 1. Intent Classification
intent = llm.classify(user_input)
# → { action: "create_quote", reference: "recent_quote_B", target: "company_C" }
# 2. Plan Generation
plan = llm.generate_plan(intent, available_agents)
# → [
# { agent: "trading", action: "find_recent_quote", params: { company: "B", period: "1w" } },
# { agent: "trading", action: "extract_conditions", params: { quote_id: "${step1.result}" } },
# { agent: "document", action: "create_quote_doc", params: { conditions: "${step2.result}", target: "C" } },
# { agent: "workflow", action: "submit_approval", params: { doc_id: "${step3.result}" } }
# ]
# 3. Sequential execution with dependency resolution
results = {}
for step in plan:
resolved_params = resolve_references(step.params, results)
result = dispatch_to_agent(step.agent, step.action, resolved_params)
if result.requires_human_approval:
approval = await request_approval(result.preview)
if not approval.approved:
return handle_rejection(approval.feedback)
results[step.id] = result
Plan의 각 단계는 이전 단계의 결과를 참조할 수 있다. ${step1.result}처럼 이전 단계의 output을 다음 단계의 input으로 연결한다. 이 의존성 해결(dependency resolution)이 Orchestrator의 핵심 기능 중 하나다.
Specialized Agents: 도메인 전문가
현재 설계된 전문 Agent는 네 종류다.
| Agent | 도메인 | 보유 Tool (주요) | 실행 예시 |
|---|---|---|---|
| Document Agent | 문서 생성, 검증, OCR | generate_invoice, validate_bl, extract_from_image | 인보이스 자동 생성, B/L 정합성 검증 |
| Workflow Agent | 승인 프로세스, 업무 자동화 | create_approval, assign_task, check_status | 결재 요청 생성, 후속 업무 배정 |
| Trading Agent | 견적, 발주, 결제 | search_quotes, create_po, check_payment | 견적 이력 분석, PO 생성 |
| Logistics Agent | 물류, 배차, 운송 추적 | find_route, create_do, track_shipment, estimate_cost | 최적 경로 조회, DO 생성, 물류비 추정 |
각 Agent의 내부 구조는 동일한 패턴을 따른다. LLM + System Prompt + Tools + Memory.
class SpecializedAgent:
def __init__(self, domain):
self.llm = LLMClient(model="...")
self.system_prompt = load_prompt(f"agents/{domain}/system.txt")
self.tools = load_tools(f"agents/{domain}/tools/")
self.memory = SessionMemory()
def execute(self, action, params, session_context):
self.memory.add(session_context)
messages = [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": format_action_request(action, params)}
]
# Tool calling loop - Agent가 최종 답변을 낼 때까지 반복
while True:
response = self.llm.chat(
messages=messages,
tools=self.tools,
context=self.memory.get_context_window()
)
if response.has_tool_call:
tool_result = self.invoke_tool(response.tool_call)
messages.append({"role": "tool", "content": tool_result})
else:
return response.final_answer
def invoke_tool(self, tool_call):
tool = self.tools[tool_call.name]
# Read Tool은 즉시 실행, Write Tool은 승인 대기
if tool.requires_approval:
return PendingApproval(tool_call, tool.generate_preview(tool_call.params))
return tool.execute(tool_call.params)
LLM은 Agent의 두뇌다. System Prompt는 역할 정의다. Trading Agent의 system prompt에는 "당신은 B2B 거래 전문 에이전트입니다. 견적, 발주, 결제 관련 업무를 처리합니다"라는 역할 정의와 함께, 사용 가능한 Tool의 목록, 호출 규칙, 안전 가이드라인이 포함된다. Tools는 Agent가 실제로 할 수 있는 일의 범위를 결정한다. Memory는 현재 세션의 맥락을 유지한다.
Tool Layer: Agent가 세상과 만나는 접점
Agent가 아무리 똑똑해도 Tool 없이는 아무것도 할 수 없다. LLM은 텍스트를 생성하는 모델일 뿐이고, 실제로 DB를 조회하고 API를 호출하고 문서를 생성하는 건 Tool이다. Agent의 능력은 곧 Tool의 범위다.
AX에서 Tool은 세 가지 타입으로 분류된다. 이 분류가 AX 안전 모델의 기반이다.
Read Tools: 자동 승인
시스템 상태를 변경하지 않는 Tool이다. 데이터 조회, 검색, 계산이 여기에 해당한다. Agent가 정보를 수집하는 과정에서 매번 사람에게 허락을 구하면 쓸 수가 없다. Read Tool은 호출 즉시 실행되고 결과가 Agent에게 반환된다.
# Read Tool 예시: 견적 이력 조회
{
"name": "search_quotes",
"type": "read",
"approval": "auto",
"description": "조건에 맞는 견적 이력을 검색한다",
"parameters": {
"company_id": { "type": "string", "required": false },
"date_from": { "type": "date", "required": false },
"date_to": { "type": "date", "required": false },
"product_category": { "type": "string", "required": false },
"status": { "type": "enum", "values": ["draft","sent","accepted","rejected"] },
"limit": { "type": "integer", "default": 10 }
},
"returns": {
"quotes": [{
"id": "string",
"company_name": "string",
"date": "date",
"total_amount": "number",
"currency": "string",
"status": "string",
"items": [{ "product", "qty", "unit_price" }]
}]
}
}
Write Tools: 승인 필요
시스템 상태를 변경하는 Tool이다. 견적서 생성, PO 발행, DO 생성, 재고 조정. 실행하면 되돌리기 어려운 결과가 만들어지기 때문에, 반드시 사람의 승인을 거친다.
Agent가 Write Tool을 호출하면 실행이 일시 중단된다. 사용자에게 "이 견적서를 중국 C사에 전송합니다. 내용을 확인해 주세요"라는 메시지와 함께 미리보기가 표시된다. 사용자가 승인하면 실행, 반려하면 Agent에게 피드백이 전달되고 Agent가 수정을 시도한다.
def execute_tool(self, tool_call):
tool = self.tools[tool_call.name]
if tool.type == "write":
# 실행 전 미리보기 생성 및 승인 요청
preview = tool.generate_preview(tool_call.params)
approval = await request_human_approval(
action=tool_call.name,
preview=preview,
agent=self.domain,
risk_level=tool.risk_level
)
if approval.status == "approved":
result = tool.execute(tool_call.params)
emit_event("tool.executed", {
"tool": tool_call.name,
"params": tool_call.params,
"approved_by": approval.user_id,
"result": result.summary
})
return result
elif approval.status == "rejected":
return ToolResult(
status="rejected",
feedback=approval.feedback # 사용자의 반려 사유
)
elif tool.type == "read":
return tool.execute(tool_call.params)
elif tool.type == "external":
if tool.auto_approved:
return tool.execute(tool_call.params)
else:
return await request_human_approval_and_execute(tool, tool_call)
External Tools: 조건부 승인
외부 시스템의 API를 호출하는 Tool이다. 환율 조회, 포워더 API 호출, 통관 상태 확인. 비용이 없는 조회성 API는 자동 승인, 비용이 발생하거나 외부 시스템을 변경하는 API는 승인 필요. 이 분류는 Tool 등록 시점에 설정된다.
예를 들어 환율 조회 API는 자동 승인이다. REINDEERS가 자체적으로 운영하는 환율 스크래퍼(태국, 한국, 중국, 말레이시아 4개국)를 호출하는 것이니 비용도 없고 부작용도 없다. 반면 포워더에게 비딩 요청을 보내는 API는 외부 효과가 있으므로 승인이 필요하다.
Memory: 단기와 장기
Agent가 하나의 요청을 처리하는 동안 여러 번의 Tool 호출이 일어난다. 견적 이력 조회 → 조건 추출 → 문서 생성 → 승인 요청. 이 과정에서 Agent는 자신이 지금까지 무엇을 했고 무엇을 알게 되었는지를 기억해야 한다.
AX의 Memory는 두 층으로 구성된다.
Session Memory (단기)는 캐시 스토어에 저장된다. 현재 진행 중인 작업의 대화 기록, Tool 호출 결과, 사용자 피드백이 여기에 들어간다. 세션이 종료되면 만료된다. LLM의 context window는 유한하기 때문에, Session Memory에서 가장 관련성 높은 정보만 선택하여 context에 포함시킨다.
class SessionMemory:
"""활성 세션의 단기 기억 - 캐시 스토어 저장"""
def __init__(self, session_id, ttl=3600):
self.session_id = session_id
self.messages = [] # 대화 기록
self.tool_results = [] # Tool 호출 결과
self.retrieved_context = [] # RAG 검색 결과
self.ttl = ttl # 1시간 후 만료
def get_context_window(self, max_tokens=8000):
"""LLM에 전달할 컨텍스트를 토큰 한도 내에서 구성"""
context_parts = []
# 최근 대화를 우선 포함
for msg in reversed(self.messages):
if estimate_tokens(context_parts) + estimate_tokens(msg) > max_tokens:
break
context_parts.insert(0, msg)
# 남은 공간에 Tool 결과와 RAG 컨텍스트 추가
remaining = max_tokens - estimate_tokens(context_parts)
context_parts.extend(
prioritize_by_relevance(self.tool_results + self.retrieved_context, remaining)
)
return context_parts
Persistent Memory (장기)는 영구 스토어에 저장된다. 세션이 종료될 때, LLM이 해당 세션을 요약하고 핵심 정보를 추출한다. "이 사용자는 견적서에 항상 결제조건 T/T 30일을 넣는다", "태국 A사와의 거래에서는 항상 TISI 인증 확인이 필요하다" 같은 패턴이 여기에 축적된다.
이 장기 기억은 새로운 세션이 시작될 때 RAG로 검색되어 Agent에게 제공된다. 사용자가 "태국 A사에 견적 보내줘"라고 하면, Agent는 과거 세션에서 학습된 "이 사용자의 태국 A사 거래 패턴"을 참고하여 더 정확한 견적서를 작성할 수 있다.
class PersistentMemory:
"""완료된 세션의 장기 기억 - 영구 스토어 + Vector Store"""
def store_session(self, session):
summary = self.llm.summarize(session.messages)
entities = extract_entities(session) # 회사, 상품, 금액 등
patterns = extract_patterns(session) # 반복되는 선호/규칙
self.db.insert("agent_memory", {
"user_id": session.user_id,
"session_id": session.session_id,
"summary": summary,
"entities": entities,
"patterns": patterns,
"embedding": embed(summary), # 벡터 검색용
"created_at": now()
})
def retrieve_relevant(self, query, user_id, limit=5):
"""현재 요청과 관련된 과거 기억을 검색"""
return self.db.vector_search(
table="agent_memory",
query_embedding=embed(query),
filter={"user_id": user_id},
limit=limit
)
Human-in-the-Loop: 어디서 사람이 개입하는가
AX 설계에서 가장 많이 고민한 부분이다.
극단적으로 두 가지 방향이 있다. 모든 것을 자동화하는 것과 모든 것에 승인을 받는 것. 전자는 위험하고 후자는 쓸모없다. Agent가 매 단계마다 "이거 조회해도 될까요?"라고 물으면 사람이 직접 하는 것보다 느리다.
우리가 선택한 기준은 "되돌릴 수 있는가"다.
자동 실행 (승인 불필요)
데이터 조회, 검색, 계산, 문서 초안 생성(저장 전), 비용 추정, 재고 확인, 이력 분석
Human Checkpoint (승인 필요)
문서 외부 발송, PO/DO 확정, 결제 실행, 파트너에게 알림 전송, 외부 시스템 데이터 변경
핵심은 "외부 효과(side effect)가 있는가"다. 시스템 안에서만 일어나는 일은 되돌릴 수 있다. 하지만 공급사에 견적이 발송되거나, 물류비가 결제되거나, 파트너에게 알림이 가면 되돌리기 어렵다. 그래서 외부 효과가 있는 모든 행동에 승인을 건다.
승인 과정도 단순한 OK/Cancel 버튼이 아니다. Agent는 자신이 왜 이 행동을 하려고 하는지, 어떤 데이터를 기반으로 판단했는지를 함께 보여준다. 사용자는 근거를 확인한 뒤 승인하거나 반려한다. 반려 시 사유를 입력할 수 있고, Agent는 이 피드백을 반영해서 수정된 결과를 다시 제안한다.
Phase 1: AI 보조 추천 (2026 Q2-Q3)
구현을 시작할 예정인 Phase 1에서 Agent는 직접 실행하지 않고 추천만 한다.
담당자가 REINDEERS에서 새 견적 요청을 열면, 화면 옆에 AI 패널이 있다. Agent가 분석한 정보와 추천 행동이 표시된다.
┌───────────────────────────────────────────────┐ │ AI Assistant │ │ │ │ 이 견적 요청을 분석했습니다: │ │ │ │ - 요청 품목: 실리콘 고무 시트 500kg │ │ - 이 바이어의 최근 3회 주문 평균 단가: $12.5/kg │ │ - 현재 POP 재고: 320kg (부족분 180kg) │ │ - 공급사 X의 최근 납기: 평균 14일 │ │ │ │ 주의: TISI 인증이 이 품목에 필요하지만, │ │ 견적 요청에 인증 관련 언급이 없습니다. │ │ 공급사에 확인 요청을 보낼까요? │ │ │ │ 추천: │ │ [1] 공급사 X에 견적 요청 (이전 조건 기반) │ │ [2] TISI 인증 확인 먼저 요청 │ │ [3] 직접 처리 │ └───────────────────────────────────────────────┘
이 추천을 만들기 위해 Agent가 내부적으로 수행한 일은 이렇다. Trading Agent가 해당 바이어의 거래 이력을 조회했다(Read Tool). Logistics Agent가 관련 공급사의 납기 이력을 확인했다(Read Tool). Document Agent가 해당 품목의 인증 요구사항을 RAG로 검색했다(Read Tool). 이 정보를 종합해서 Orchestrator가 추천 목록을 만들었다.
Phase 1에서 Agent는 Read Tool만 자동으로 사용한다. Write Tool은 사용하지 않는다. 실행은 전부 사람이 한다. Agent는 "무엇을 하면 좋겠다"고 제안하고, 사람이 직접 클릭해서 수행한다.
이 단계가 존재하는 이유는 두 가지다. 첫째, Agent의 추천 품질을 실전에서 검증할 시간이 필요하다. 엉뚱한 추천을 한다면 Phase 2에서 자동 실행을 허용하면 안 된다. 둘째, 사용자가 Agent와 일하는 방식에 적응하는 시간이 필요하다. 갑자기 AI가 업무를 대신하면 불안해하는 게 자연스러운 반응이다. 먼저 추천을 받아보고, 신뢰가 쌓이면 실행을 맡기는 게 현실적이다.
Phase 2: 승인 기반 자율 실행 (2026 Q4~)
Phase 1에서 추천 품질이 검증되면, Phase 2에서 Agent가 직접 실행한다. 다만 Write 행동에서는 반드시 사람의 승인을 거친다.
전체 흐름을 예시로 보자.
사용자: "태국 A사에 지난번과 같은 조건으로 견적 요청해줘"
[Orchestrator] Intent 분석 → create_quote, ref=last_quote_A, target=company_A
[Trading Agent] search_quotes(company="A", limit=1) ← Read, 자동
→ Quote #2847 (2026-04-15, 실리콘 시트 300kg, $12.8/kg, FOB Bangkok)
[Trading Agent] extract_conditions(quote_id=2847) ← Read, 자동
→ { product: "실리콘 시트", unit_price: 12.8,
incoterm: "FOB", payment: "T/T 30일", port: "Bangkok" }
[Document Agent] create_quote_document(conditions, target="A") ← Write, 승인 대기
→ 견적서 미리보기 표시:
"다음 견적서를 태국 A사에 전송합니다:
품목: 실리콘 시트 300kg | 단가: $12.8/kg
결제조건: T/T 30일 | Incoterm: FOB Bangkok
[승인] [수정] [취소]"
사용자: [승인]
[Document Agent] send_quote(quote_id=NEW, to="company_A") ← Write, 승인됨
→ 견적서 전송 완료
[Workflow Agent] schedule_follow_up(quote_id=NEW, after="3d") ← Write, 내부 자동
→ 3일 후 응답 미확인 시 알림 예약
이 흐름에서 Read 행동은 자동으로 4번 발생했고, Write 행동 중 외부 발송(send_quote)에서만 사용자 승인을 요청했다. 사용자가 실질적으로 쓴 시간은 견적서를 확인하는 1~2분이다. 이전에 같은 작업을 수동으로 하면 상당한 시간이 걸렸다.
Tool Calling 상세: LLM이 Tool을 선택하는 과정
Agent가 Tool을 선택하는 구체적인 과정을 더 파고들어 보겠다.
LLM은 사용 가능한 Tool의 목록(이름, 설명, 파라미터 스펙)을 system prompt에서 받는다. 사용자 요청과 현재까지의 컨텍스트를 분석해서, 지금 호출해야 할 Tool과 파라미터를 JSON으로 출력한다. 이 JSON을 파싱해서 실제 Tool 함수를 실행하고, 결과를 다시 LLM에 전달한다. LLM은 결과를 보고 다음 행동을 결정한다.
# Trading Agent의 Tool 정의 (발췌)
tools = [
{
"name": "search_quotes",
"description": "조건에 맞는 견적 이력을 검색. 회사명, 기간, 상품 카테고리로 필터.",
"type": "read",
"parameters": {
"company_id": "string | null",
"date_from": "date | null",
"date_to": "date | null",
"product_category": "string | null",
"status": "enum(draft,sent,accepted,rejected) | null",
"limit": "integer = 10"
}
},
{
"name": "create_purchase_order",
"description": "승인된 견적 기반으로 구매주문서를 생성한다.",
"type": "write",
"parameters": {
"quote_id": "string (required)",
"delivery_date": "date (required)",
"shipping_address_id": "string (required)",
"notes": "string | null"
}
},
{
"name": "calculate_landed_cost",
"description": "품목 단가+환율+물류비+관세를 종합하여 도착지 기준 총비용 계산.",
"type": "read",
"parameters": {
"items": [{ "product_id": "string", "qty": "number", "unit_price": "number" }],
"origin_country": "string",
"dest_country": "string",
"shipping_method": "enum(ocean,air,road) | null"
}
}
]
Tool 설계에서 중요한 원칙이 하나 있다. 파라미터의 필수(required)와 선택(optional) 구분이 명확해야 한다. LLM이 필수 파라미터를 누락하면 Tool 실행이 실패하고 불필요한 재시도 루프가 생긴다. 반대로 너무 많은 파라미터를 필수로 설정하면 Agent의 유연성이 떨어진다. "company_id 없이 최근 견적 5건 보여줘"라는 요청도 처리할 수 있어야 한다.
또 하나, Tool의 설명(description)은 LLM이 Tool을 선택하는 근거가 된다. 설명이 모호하면 LLM이 엉뚱한 Tool을 호출한다. "견적 관련 뭔가를 한다"보다 "조건에 맞는 견적 이력을 검색한다. 회사명, 기간, 상품 카테고리로 필터링 가능"이 훨씬 정확한 선택을 유도한다. 이 Tool description 최적화가 실제로 Agent 성능에 상당한 영향을 미친다.
RAG: Agent의 지식 기반
Agent가 맥락 있는 추천을 하려면 관련 데이터를 빠르게 찾아야 한다. LLM의 context window에 모든 데이터를 넣을 수는 없으므로, 필요한 정보만 검색해서 가져오는 RAG(Retrieval-Augmented Generation)가 핵심이다.
AX에서 RAG의 knowledge base는 다양한 소스로 구성된다.
| 소스 | 데이터 | 업데이트 |
|---|---|---|
| 거래 이력 | 견적, PO, 인보이스, 결제 기록 | 실시간 (이벤트 기반) |
| 파트너 프로필 | 회사 정보, 담당자, 거래 선호도 | 변경 시 |
| 인증/규제 DB | TISI, FDA, HS Code별 요구사항 | 월 1회 크롤링 |
| 물류 이력 | 경로, 소요시간, 비용, 포워더 실적 | 실시간 |
| 환율 | 4개국 실시간 환율 | 일 1~2회 |
| Agent 기억 | 과거 세션 요약, 사용자 패턴 | 세션 종료 시 |
검색은 키워드 기반과 벡터 기반의 하이브리드다. "태국 A사 실리콘"이라는 키워드로 거래 이력을 필터링하면서, 동시에 요청의 의미적 유사도를 기반으로 관련 인증 요구사항이나 과거 이슈를 벡터 검색으로 찾는다.
안전장치 설계
Agent가 자율적으로 행동하는 시스템에서 안전장치는 선택이 아니라 전제 조건이다.
금액 한도. Agent가 단일 거래에서 처리할 수 있는 금액의 상한이 있다. 이 한도를 초과하면 Write Tool 여부와 관계없이 사람의 승인이 필요하다. 한도는 사용자별, 파트너별로 설정할 수 있다. 신규 파트너와의 거래는 한도를 낮게 시작하고, 거래 이력이 쌓이면 점진적으로 올린다.
Rate Limiting. 단위 시간 내에 Agent가 실행할 수 있는 Write 행동의 횟수가 제한된다. 비정상적으로 많은 Write가 발생하면 자동으로 중단되고 관리자에게 알림이 간다. Agent가 루프에 빠져서 같은 행동을 반복하는 것을 방지하는 장치이기도 하다.
Audit Log. Agent의 모든 행동은 Event+State+Log 모델에 따라 기록된다. 어떤 Agent가 어떤 Tool을 어떤 파라미터로 언제 왜 호출했는지가 전부 추적 가능하다. 사후에 문제가 발생하면 전체 실행 이력을 역추적할 수 있다.
Rollback. Write Tool의 실행 결과는 가능한 한 되돌릴 수 있게 설계한다. 외부 발송처럼 물리적으로 되돌릴 수 없는 행동도 있지만, 내부 데이터 변경(재고 조정, 상태 변경 등)은 이벤트 기반이므로 보상 이벤트(compensating event)를 발행해서 되돌릴 수 있다.
Escalation. Agent가 판단하기 어려운 상황을 감지하면 자동으로 사람에게 넘긴다. Tool 호출이 3회 연속 실패하거나, LLM의 응답이 불확실하거나, 요청이 Agent의 권한 범위를 벗어나면 "사람이 필요한 상태"로 전환된다. Agent가 "모르겠다"고 말할 수 있는 능력은 안전의 핵심이다.
B2B에서 Agent가 특별히 중요한 이유
B2C에서 AI Agent는 주로 고객 서비스에 쓰인다. 상품 추천, 반품 처리, FAQ 응답. 건당 규모가 작기 때문에 실수의 비용도 상대적으로 낮다.
B2B는 다르다. 건당 금액이 크고, 프로세스가 길고, 실수의 비용이 높다. 인보이스 금액이 틀리면 통관이 멈추고, 선적 일정이 밀리면 컨테이너를 놓치고, 인증 서류가 빠지면 수입 자체가 불가능하다. 그래서 B2B에서의 AI Agent는 자동화율이 아니라 오류율이 핵심 지표다. 90%를 자동화하더라도 10%에서 큰 실수가 나면 신뢰를 잃는다. Human-in-the-loop에 이만큼 공을 들이는 이유다.
동시에, B2B의 반복 업무량은 거래 건수에 비례해서 증가한다. REINDEERS의 거래가 계속 늘어나고 있고, 각 거래마다 견적, 발주, 서류, 물류, 정산이 따른다. 운영 인력을 거래량에 비례해서 늘리는 건 구조적 한계가 있다. Agent가 반복 업무의 상당 부분을 맡고, 사람은 예외 상황과 의사결정에 집중하는 구조가 되면, 거래 건당 운영 비용이 낮아진다.
이것이 AX가 만들려는 것이다. 개별 기능의 자동화가 아니라 운영 구조의 전환. Agent가 기본이고 사람이 감독하는 구조. 이 전환은 기술 하나로 되는 게 아니라, 데이터(RAG의 knowledge base), 안전장치(Human-in-the-loop), 축적(Persistent Memory)이 함께 성숙해야 가능하다.
AI Agent 진화 4단계와 Phase 로드맵
우리는 REINDEERS Works의 AI Agent 진화를 네 단계로 구분하고 있다.
| 단계 | 자동화 | 사람의 역할 | AX Phase 매핑 |
|---|---|---|---|
| 1. Tool | ~20% | 사람이 직접 지시, AI는 개별 작업 실행 | Phase 1 (AI 보조 추천) |
| 2. Assistant | ~50% | AI가 제안, 사람이 승인 | Phase 2 (승인 기반 자율 실행) |
| 3. Agent Team | ~80% | AI 팀이 자율 실행, 사람은 예외만 | Phase 3 (부서 Agent 팀 자율 운영) |
| 4. Autonomous Operator | ~95% | AI가 법인 운영, 사람은 전략만 | Phase 4 (AI Agent 법인 운영) |
2026년 현재 REINDEERS AX는 1단계 Tool에서 2단계 Assistant로 넘어가는 구간에 있다. Phase 1이 올 여름에 시작된다. 그 시점에서 Agent는 제안만 하고 실행은 사람이 한다. Phase 2에 들어가면 Write 행동 중 외부 효과가 없는 것은 자동 실행되고, 외부 발송이나 결제만 사람 승인을 거친다. Phase 3에서 조직도의 부서 Agent들이 서로 협력해서 반복 업무의 80%를 자율적으로 처리하고, 사람은 예외와 전략만 본다. Phase 4는 — 솔직히 아직 먼 이야기지만 — 사람 한 명이 여러 법인을 동시에 운영하는 수준, 즉 직원 10명이 하던 일을 AI Agent 팀이 수행하고 사람은 방향 제시만 하는 상태다.
이 로드맵에서 한 가지는 분명하게 말할 수 있다. 마지막 단계에서 사람이 사라지는 것이 아니라, 사람의 역할이 "실행"에서 "결정과 방향"으로 이동한다. 어떤 단계에서든 "왜 그 결정을 내렸는지"는 사람이 감사할 수 있어야 하고, 그래서 Event+State+Log 기반의 audit log가 모든 Phase의 전제 조건이다. Phase 1의 첫 결과물이 나오면 다시 공유하겠다.
