Issue 01중국 AI
AC POST
중국 AI 목록
掘金2026년 11월 1일 11:08중국어 → 한국어

16-Agent 실전: 생선 슈퍼마켓 52주 MD와 지능형 출청

앞선 15편에서 Agent 실행, 도구 호출, 구조화 출력, PlanMode, 메모리와 RAG, 멀티 Agent, MCP, A2A, Harness까지 단계별로 다룬 뒤 생선 슈퍼마켓 사례로 이어간다.

중국어 원문을 AI로 번역했습니다. 고유명사와 수치는 원문 표기를 우선하며, 중요한 판단에는 아래 출처 원문을 함께 확인하세요.

시리즈 16번째 글 · 실전편

앞선 15편에서는 Agent의 단일 능력을 하나씩 분리해 설명했다. 첫 Agent를 구동하는 것부터 도구 호출, 구조화된 출력, PlanMode, 메모리와 RAG, 나아가 멀티 Agent, MCP, A2A, 마지막으로 HarnessAgent, Workspace, Context와 권한·내결함성·관측 가능성까지 다뤘다. 그러나 "부품이 모두 갖춰진 것"이 "조립해서 돌아가는 것"과 같지는 않다. 이번 글에서는 이것들을 엮어 진짜 프로덕션 Agent로 완성하고자 한다.

필자는 소프트웨어 업계에서 오랫동안 일하면서 수많은 기업 정보화 구축의 "짜깁기" 과정을 봐 왔다. 전통 소매업체를 예로 들면, ERP, CRM, WMS, 이커머스 중간 플랫폼이 서로 다른 공급업체에 의해 조립되어 데이터 모델과 인터페이스가 제각기 말이 달라, 이들을 연결하고 협업시키는 일이 유난히 힘들다. 그런데 바로 이것이야말로 Agent가 가장 가치 있게 안착해 해결할 수 있는 유형의 시나리오다.

먼저 본문과 관련된 두 가지 실제 리테일 시나리오를 소개하며, 이후 실전은 이 두 시나리오를 중심으로 전개된다.

시나리오 1 · 52주 MD 주간 계획("주"를 단위로 한 경영 계획): 점장이 한마디로 다음 분기 MD 요구를 제기하면, Agent는 역사적 판매량(동일 주·동일 시기, 최근 주간 추세)을 실증적 기반으로 삼고, 고객의 생활 리듬/계절/절기/명절/장면(날씨, 고객군, 경쟁사 정보를 보조로)을 결합하여 중점 상품을 선정하고 주간 경영 제안을 산출하며, 나아가 프로모션을 직접 실행하고 카피를 생성하며 회원을 선별한다.

시나리오 2 · 지혜로운 출청(出清)("일"을 단위로 한 경영 실행): 매일 저녁 폐점 전 몇 시간은 신선식품 출청의 주전장이다. Agent는 그날 저녁의 날씨, 당일 실시간 판매에 따라, 그리고 해당 카테고리의 역사적 판매량(동일 날씨/동일 시간대 소진 기준선)과 대조하여 출청 할인 강도와 최적 출청 시간대를 동적으로 정하고, 가격 인하 전에는 수동 확인을 거친다.

一. 프로젝트 배경과 두 가지 시나리오

1.1 리테일 정보화의 "짜깁기" 현황

앞서 말한 "짜깁기"가 가져오는 것은 어떤 단일 지점의 문제가 아니라, 시스템 간의 타고난 이질성과 파편화다. 데이터 모델이 다르고, 인터페이스 프로토콜이 각기 다르며, 기준이 제각기 말이 다르다. 매장이 "전역 경영 데이터에 기반해 작은 일 하나를 하려면"(예를 들어 이번 주 중점 상품을 고르거나 오늘 밤 출청 할인을 정하려면), 먼저 여러 시스템을 넘나들며 데이터를 가져오고, 정렬하고, 다시 판단해야 한다. 이 일을 사람이 하면 시간이 많이 들고 경험에 의존하는데, 바로 이 지점이 Agent + MCP 통합 접속 + Harness 안정적 오케스트레이션이 가장 가치를 발휘할 수 있는 곳이다.

1.2 시나리오 1: 52주 MD 주간 계획

52주 MD는 신선식품 슈퍼마켓이 "년"을 완전한 시간 범위로 삼는 경영 활동 관리 방법이다. 1년 52주를 완전한 시간 축으로 삼아, 고객의 생활 리듬, 계절, 절기, 명절, 장면에 따라 주 단위로 실행을 추진하고, 매주 주력 상품, 진열, 프로모션, 매장 테마를 미리 계획하여 주마다 판매의 작은 고점을 만들어낸다. 구매‑운영‑마케팅‑매장 전 부서가 협업하여 실행하고, 매주 복기하며 반복 최적화한다. 이 시간 축 위에서 중점 상품의 선택 역시 시간을 노드로 삼아, 고객의 생활 리듬, 계절, 절기, 명절, 장면 등의 요인을 결합하여, 소비자의 현재 수요에 부합하면서도 기업의 경영 목표를 충족하는 상품을 선정하고, 이를 근거로 일주일의 진열, 프로모션과 마케팅을 배치한다.

52주 MD 주간 계획은 이렇게 생겼다.

이번 주(2026-W34, 8월 셋째 주) 52주 MD 주축: 입추 말복 · 개학 전 비축 역사적 판매량 참고: 연근 최근 4주 주간 판매 120kg, 녹두 90kg; 작년 동기(2025-W34) 연근 +18%, 녹두 +12%——수요는 안정 속 상승 중점 상품: 1. 제철 신선식: 무침 연근줄기, 얼음 녹두죽 원료(연근 / 녹두)—— "입추에 연근을 먹는다"는 풍습에 부합 + 고온 해소 2. 개학 시즌 조합: 반조리 아침식사(만두 / 빵류) + 학생 우유 —— 개학이 다가옴, 지역사회 가정 고객군 3. 지역사회 중장년: 저당 잡곡, 제철 배(윤조)—— 고객군 페르소나 기반 경영 제안: - 진열: 입구 엔드 진열대에 "개학 아침식사 + 해소" 테마 마대 진열 - 프로모션: 연근 / 녹두 조합 8.8할인, 학생 우유 두 번째 반값 - 커뮤니티: 수요일 "입추 양생" 카피 발송, 25–45세 가정 회원 선별

1.3 시나리오 2: 지혜로운 출청

신선식품은 유통기한이 짧아, 폐점 전 몇 시간 동안 안 팔리는 재고를 출청하는 것은 모든 슈퍼마켓의 매일의 "고된 싸움"이다. 어려운 점은 할인을 너무 깊게 잡으면 마진이 손해이고, 너무 얕게 잡으면 다 못 팔아 손실 처리해야 하며, 시간대를 너무 이르게 잡으면 정가 판매를 흔들고, 너무 늦게 잡으면来得及来不及(때를 놓친다)는 것이다. 지혜로운 출청의 방식은 이렇다. 매일 저녁 폐점 전 몇 시간, 그날 저녁의 날씨(객류 유입에 영향), 당일 실시간 판매(잔여 재고와 판매 회전율)에 근거하고, 해당 카테고리의 역사적 판매량(동일 날씨 / 동일 시간대의 통상 소진 수준)과 대조하여 합리적인 소진 기준선을 설정하고, 출청 할인 강도와 최적 출청 시간대를 동적으로 계산하며, 가격 인하 전에 점장의 확인을 거친다.

출청 방안은 이렇게 생겼다.

폐점 3시간 전 출청 방안(당일 19:00 발동) 날씨: 소나기에서 서늘해짐, 저녁 피크 객류 감소 실시간 판매: 엽채류 판매율 62%(38kg 남음), 신선 우유 판매율 55%(120팩 남음) 역사적 참고: 엽채류 최근 7일 동일 날씨 저녁 21:00 이전 평균 소진 78%, 오늘 밤 21:00 이전 예상 소진은 겨우 55% → 할인을 더 깊게 해야 함 제안: - 엽채류: 20:30부터 7할, 21:00(폐점 1시간 전)부터 5할로 완전 소진 - 신선 우유: 20:00부터 두 번째 1위안, 21:00부터 6할 예측: 점장 경험 출청 대비 판매 소진 +12%, 손실 -15%

1.4 두 시나리오의 관계

| 차원 | 시나리오 1 · 52주 MD(주) | 시나리오 2 · 지혜로운 출청(일) | | --- | --- | --- | | 경영 척도 | 주 계획, 사전 계획 | 일 실행, 실행 중 대응 | | 발동 방식 | 능동 제기 / 주 단위 정시 | 매일 저녁 정시 | | 핵심 입력 | 역사적 판매량 / 달력 / 절기 / 날씨 / 고객군 / 경쟁사 | 그날 저녁 날씨 / 역사적 판매량(동일 날씨 동일 시간대 기준선) / 당일 실시간 판매 / 폐점 카운트다운 | | 핵심 산출 | 중점 상품 + 경영 제안 + 프로모션 / 카피 / 선별 | 출청 할인 강도 + 출청 시간대 + 가격 인하 | | 쓰기 작업 여부 | 예(프로모션 생성 / 쿠폰 발송) | 예(가격 인하 실행) | | 모두 필요로 함 | 의도 인식, MCP 데이터 조회, 권한 확인, 관측 가능성 | 의도 인식, MCP 데이터 조회, 권한 확인, 관측 가능성 |

二. 남은 문제와 해결 방안

| 남은 난제 | 근본 원인 | AgentScope 해법(Harness가 통일적으로 오케스트레이션) | | --- | --- | --- | | 핵심 상품 선정이 경험에 의존하고 시즌과 자주 동떨어짐 | 선정을 경험에 의지해 절기/날씨/고객군/경쟁사 정보를 체계적으로 결합하기 어려움 | MCP로 행사력/절기/날씨/회원/경쟁사 정보를 가져오고, Agent가 52주 MD 주간 계획을 생성 | | 프로모션 효과 평가가 어려움 | 다중 시스템 데이터가 분절되어 기준이 제각각 | MCP로 통일적으로 데이터를 가져오고, 구조화 출력으로 ROI를 계산하며, LLM이 귀인 보고서 생성 | | 회원 접촉이 정밀하지 못함 | 태그가 분산되고 프로필이 낡음 | Agent가 장기 기억을 검색 + 개인화 문안을 생성하고 인원을 선별 | | 마케팅 콘텐츠 생산이 느리고 실행이 어려움 | 문안/프로모션 방안을 수작업에 의존하고 시스템 간 생성에 시간이 많이 듦 | 다중 Agent가 문안과 방안을 생성하고, 실행 전담 담당자가 HITL을 거쳐 프로모션을 생성하고 쿠폰을 발급 | | 신선 상품 처분이 감으로 이뤄지고 폐점 전에 손이 바쁨 | 할인과 시간대를 경험에 의존하고 실시간 날씨와 판매를 보지 않음 | MCP로 날씨 + 당일 실시간 판매를 가져오고, Agent가 처분 할인과 시간대를 동적으로 정하며, 가격 인하 전 HITL 확인을 거침 | | 데이터 권한이 혼란스러움 | 각 시스템이 권한을 자체 구축해 권한 초과 위험 | PermissionEngine 4상태 + 사용자 정의 Middleware 도구 수준 차단 | | 운영 데이터 기준이 일치하지 않음 | 지표 정의가 분산됨 | 기준을 통일된 @Tool 지표 API로 축적하고, Agent는 호출만 하고 재계산하지 않음 |

3. 전체 아키텍처 설계

3.1 계층형 아키텍처

계층형 + 모듈형 아키텍처를 채택한다. 접근 계층은 요청을 HarnessAgent에 넘겨 애플리케이션 오케스트레이션을 수행하고, MCP는 인프라 통합 프로토콜로 삼는다.

┌─────────────────────────────────────────────────────────────┐ │ 접근 계층 / Interface │ │ REST API | WebSocket └───────────────────────────┬─────────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────────┐ │ 애플리케이션 오케스트레이션 계층 / Application(HarnessAgent 실행 계층) │ │ HarnessAgent(오케스트레이션 총괄) │ │ ├─ SubagentDeclaration: 의도 / 선정 계획 / 마케팅 실행 / 처분 전담 / 권한 │ │ ├─ Toolkit: @Tool 비즈니스 도구 + 자사 리테일 MCP Server 등록 도구 │ │ ├─ Middleware: 권한 차단(RBAC / ABAC) + OtelTracing 관측 │ │ └─ Plan Mode / Memory / Workspace / Sandbox │ └───────────────────────────┬─────────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────────┐ │ 비즈니스 서비스 계층 / Service(@Tool 도구 구현체, AgentScope 오케스트레이션 호출을 받음 │ │ 선정 / 마케팅 / 회원 등 비즈니스 서비스 구현(@Tool을 통해 노출) │ └───────────────────────────┬─────────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────────┐ │ 인프라 계층 / Infrastructure │ │ AgentScope - Java 2.0 Runtime | MCP Client / Server │ │ OpenTelemetry | Micrometer | Redis | DB │ │ Legacy ERP / WMS / POS / CRM 어댑터 │ └─────────────────────────────────────────────────────────────┘

3.2 AgentScope-Java 2.0이 제공하는 지원

Agent 런타임: HarnessAgent가 오케스트레이션 총괄로서 내부적으로 무상태 ReActAgent로 추론 루프를 돌린다. 다중 Agent는 SubagentDeclaration으로 선언형 오케스트레이션한다.

LLM 연동: 문자열 모델 + ModelRegistry, 예컨대 "dashscope:qwen-max"로, 환경 변수의 Key를 자동으로 읽는다.

도구 추상화: @Tool + Toolkit으로 비즈니스 도구를 등록하고, 구조화 출력은 agent.call(msgs, Class, ctx)를 사용한다.

MCP 지원: MCP Client로서 외부 MCP Server를 호출하며(McpClientBuilder / workspace/tools.json), 내부 역량을 MCP Server로 노출할 수도 있다.

관측 확장점: Middleware 채널(5개 훅: onAgent / onReasoning / onActing / onModelCall / onSystemPrompt)과 내장 OtelTracingMiddleware, 그리고 streamEvents()의 28가지 유형화 이벤트 스트림.

비즈니스 시스템 통합: 사용자 정의 @Tool, 사용자 정의 Middleware, 사용자 정의 Memory 모두 표준 확장점을 통해 접속되며, 결정론적 비즈니스 로직은 도구 구현체에 남긴다.

3.3 오케스트레이션 총괄: 다중 에이전트, 도구, 미들웨어를 조립하기

HarnessAgent.builder()를 사용해 하위 Agent, 도구 집합(MCP 포함), 미들웨어를 한 번에 붙이면 추론 루프, 다중 Agent 스케줄링, 권한과 관측을 전부 프레임워크가 맡는다. 먼저 다섯 명의 전담자(subAgent)를 선언한다:

import io.agentscope.harness.HarnessAgent; import io.agentscope.harness.agent.subagent.SubagentDeclaration; // 의도 전문관: 구조화된 의도를 받아 어느 비즈니스 라인으로 갈지 결정 SubagentDeclaration intentAgent = SubagentDeclaration .builder() .name(”intent_agent”) .description(”의도 전문관: 사용자 질의를 md_plan(기획) / md_execute(이행) / clearance(출하·처분) / query(조회) 중 하나로 식별하고 슬롯을 추출”) .inlineAgentsBody(”너는 의도 전문관으로, 구조화된 의도와 슬롯만 출력하고 비즈니스 계산은 펼치지 않는다. ”) .build(); // 상품 선정 기획 전문관: 복합 MD 요구를 순서 있는 단계로 분해(예: 상품 선정 기획 → 마케팅 방안 → 이행 실행) SubagentDeclaration mdPlannerAgent = SubagentDeclaration .builder() .name(“md_planner_agent”) .description(”상품 선정 기획 전문관: 역사적 판매량(작년 동주 동기·최근 주간 추세)을 실증 근거로 삼고, 고객 생활 리듬 / 계절 / 절기 / 명절 / 장면(날씨·고객군·경쟁사 정보 보조)을 결합해 이번 주 중점 상품을 선정하고 52주 MD 주간 계획을 산출”) .inlineAgentsBody(”너는 상품 선정 기획 전문관으로, 먼저 역사적 판매량으로 수요 기준선(작년 동주 동기·최근 주간 추세)을 잡고, 이어 고객 생활 리듬 / 계절 / 절기 / 명절 / 장면을 결합해 중점 상품을 선정하며, 일주일 경영 제안을 제시한다. ”) .build(); // 마케팅 실행 전문관: MCP + @Tool 도구 세트를 보유하고 문안을 생성해 실행까지 담당 SubagentDeclaration marketingAgent = SubagentDeclaration .builder() .name(“marketing_agent”) .description(“마케팅 실행 전문관: weather / calendar / member / competitor / promotion 등 MCP 도구를 호출해 마케팅 문안을 생성하고 프로모션을 만들며 쿠폰을 발행”) .inlineAgentsBody(“너는 마케팅 실행 전문관으로, 단계에 따라 엄격히 도구를 호출해 문안 생성·프로모션 생성·쿠폰 발행을 수행하고 데이터를 임의로 꾸며내지 않는다. ”) .build(); // 출하·처분 전문관: 매일 저녁 날씨와 당일 실시간 판매에 따라 신선 식품 출하·처분 할인 강도와 최적 출하·처분 시간대를 동적으로 결정 SubagentDeclaration clearanceAgent = SubagentDeclaration .builder() .name(“clearance_agent”) .description(“출하·처분 전문관: weather.query와 당일 실시간 판매를 조회하고, 해당 카테고리 역사적 판매량(동일 날씨 / 동일 시간대 소진 기준선)과 대조하며, 폐점 카운트다운을 결합해 신선 식품 출하·처분 할인과 최적 출하·처분 시간대 제안을 제시”) .inlineAgentsBody(“너는 출하·처분 전문관으로, 날씨·실시간 판매 동향·역사적 판매량 기준선(동일 날씨 / 동일 시간대)·폐점까지 남은 시간에 근거해 할인 강도와 출하·처분 시점을 제시하고 데이터를 임의로 꾸며내지 않는다. ”) .build(); // 권한 전문관: 호출 전 RBAC/ABAC 검증을 수행하고 고위험 작업에는 HITL 확인을 발동 SubagentDeclaration guardAgent = SubagentDeclaration .builder() .name(“guard_agent”) .description(“권한 전문관: 도구 호출에 대해 권한과 데이터 범위 검증을 수행하고, 월권 또는 고위험이면 사람의 확인을 요구”) .inlineAgentsBody(“너는 권한 전문관으로, 어떤 호출이든 전에 먼저 역할과 데이터 범위를 검증한다. ”) .build();

다섯 전문관을 선언한 뒤, 총괄 제어 Agent로 이들을 도구 세트·미들웨어와 함께 엮어 경영 도우미의 전체 골격을 완성한다:

import io.agentscope.harness.HarnessAgent; import io.agentscope.core.tool.Toolkit; import io.agentscope.core.middleware.MiddlewareBase; import io.agentscope.core.tracing.OtelTracingMiddleware; import io.agentscope.core.memory.MemoryConfig; import io.agentscope.core.context.CompactionConfig; import io.agentscope.core.context.ToolResultEvictionConfig; import java.nio.file.Path; import java.util.List; HarnessAgent assistant = HarnessAgent.builder() .name("store-ops-assistant") // 총괄(편집장) .model("dashscope:qwen-max") .workspace(Path.of("./store-ops-workspace")) .sysPrompt(""" 당신은 매장 경영 지능형 어시스턴트의 52주 MD 총괄입니다. 절차: 1. agent_spawn intent_agent로 의도(기획 or 실행 or 클리어런스)와 슬롯을 식별한다; 2. agent_spawn md_planner_agent로 먼저 과거 판매량을 가져와 실증 기준선(동일 주차 동일 시기, 최근 주차 추세)을 세운 뒤, 고객 생활 리듬/계절/절기/명절/장면(날씨, 고객군, 경쟁사 정보를 보조로 활용)을 결합하여 중점 상품을 선정하고 주간 계획을 낸다; 3. agent_spawn marketing_agent로 마케팅 카피와 프로모션 방안을 생성하고 도구를 호출해 실행한다; 4. 프로모션/쿠폰 발행 등 고위험 작업은 guard_agent에 넘겨 검증하고, 필요 시 수동 확인을 개시한다; 5. 매일 저녁 폐점 전 몇 시간 동안 agent_spawn clearance_agent가 날씨와 실시간 판매 동향에 따라 클리어런스 할인과 시간대를 정하고, 가격 인하 전에 guard_agent 검증을 거친다; 6. 52주 MD 주간 계획과 클리어런스 실행 결과를 집계하여 관측 가능한 링크를 출력한다. """) .subagent(intentAgent).subagent(mdPlannerAgent) // 다중 에이전트 선언형 오케스트레이션 .subagent(marketingAgent).subagent(clearanceAgent) .subagent(guardAgent) .toolkit(toolkit) // 등록된 MCP 도구 + @Tool 비즈니스 도구 .middlewares(List.of( new PermissionMiddleware (rbacService), // 권한 차단 new OtelTracingMiddleware ())) // 관측 가능 .memory(MemoryConfig.builder().build()) // 장기 기억 .compaction(CompactionConfig.builder() .triggerMessages( 30 ).keepMessages( 10 ) .truncateArgs(CompactionConfig.TruncateArgsConfig.builder() .maxArgLength( 2000 ).build()).build()) // 컨텍스트 엔지니어링 .toolResultEviction(ToolResultEvictionConfig.defaults()) // 대형 결과 오프로드 .build();

4. 코드 세분화: 두 시나리오의 구현

3장(3.3)에서 이미 HarnessAgent 총괄, 다섯 개 서브 에이전트, MCP 통로를 갖추었고, 이 장의 4.4에서는 Harness의 조립 코드를 제시하며, 5장에서는 런타임 관점에서 하나의 요청이 Harness 안에서 어떻게 흐르는지 설명한다. 이 절은 "어떤 역량이 있는가"를 나열하는 대신 두 시나리오를 실제로 코드에 담는다. 4.1, 4.2는 각각 52주 MD와 스마트 클리어런스의 비즈니스 구현이고, 4.3은 두 시나리오가 공유하는 역량 모듈(의도 / MCP / 권한 / 관측 가능 / 기억) 코드다. 이 코드를 읽으면 매장 어시스턴트가 매일 돌리는 것이 그대로 보인다. "왜 이렇게 하는가"는 모두 주석에 적혀 있다.

4.1 시나리오 1: 52주 MD 주간 계획 코드 구현

한 문장으로 다음 분기 MD 요구를 제시하면, Agent는 의도 식별 → 과거 판매량으로 실증 기준선 수립 → 절기 / 날씨 / 고객군 / 경쟁사 기반 상품 선정 → 주간 계획 작성 → 프로모션 및 쿠폰 발행 실행(수동 확인)을 해야 한다. 비즈니스 구현 코드는 다음과 같다:

// 场景一 진입점: 점장이 52주 MD 주간 계획 요구사항을 제출한다 public Mono planWeeklyMd ( String utterance, SessionContext ctx ) { // ① 의도 인식(3계층 퍼널 + 구조화 출력, 4.3 참조): 자연어를 강타입 의도로 변환하여 LLM이 자유롭게 하지 못하게 한다 IntentResult intent = intentAgent.call( List.of( new UserMessage("user", utterance)), IntentResult. class , RuntimeContext.builder().sessionId(ctx.sid).userId(ctx.uid).build()) .block().getStructuredData(IntentResult. class ); // 예상 intent = { intent:"md_plan", slots:{ week:"2026-W34", season:"입추", // weather:"고온", segment:"지역사회 중장년층", event:"개학 시즌" } } // ② 상품 선정 계획: md_planner_agent에 맡기며, 이는 4.3에서 등록된 MCP 통로에서 데이터를 가져온다 // —— '역사 판매량'을 실증적 기준선으로 삼아 모델이 근거 없이 상품을 만들어내지 않게 한다; 권한/데이터 범위는 PermissionMiddleware가 onActing에서 차단한다(4.3 참조) return mdPlannerAgent.call( List.of( new UserMessage("user", "" + intent.slots. get ("week") + ""에 대한 52주 MD를 수행하라: " + "먼저 sales.history에서 동일 주차 동일 기간 + 최근 주차 추세를 가져오고, 다음으로 calendar.solar_terms에서 절기를, " + "weather.query에서 날씨를, member.segment에서 고객군을, competitor.intel에서 경쟁사 정보를 가져온 뒤, " + "마지막으로 md.weekly_plan으로 주간 계획을 산출하라")), MdPlanReply. class , RuntimeContext.builder().sessionId(ctx.sid).userId(ctx.uid).build()) .flatMap(reply -> { MdPlanReply r = reply.getStructuredData(MdPlanReply. class ); // ③ 실제 실행: 마케팅 실행 전문가가 프로모션을 생성하고 쿠폰을 발급하며, 이는 고위험 쓰기 작업에 해당한다 // tools.json은 promotion.create / coupon.issue를 이미 ASK로 표시해 두었고, 프레임워크가 호출 전에 일시 정지하고 // REQUIRE_USER_CONFIRM을 던지며, 프런트엔드가 getReplyId()로 승인을 회신한다(4.3 권한 / 관측 가능성 참조) marketingAgent.call(List.of( new UserMessage("user", "주간 계획을 기반으로 프로모션을 생성하고, 커뮤니티 문안을 작성하고, 회원을 선별하라: " + r.toBrief())), Void. class , RuntimeContext.builder() .sessionId(ctx.sid).userId(ctx.uid).build()).block(); // ④ 관측 가능성: 전체 체인은 streamEvents를 통해 도구 호출/인간 승인/텍스트 증분을 실시간으로 흘려보낸다(4.3 참조) return Mono.just(r); }); }

상품 선정 도구 자체는 @Tool로 노출되며, 역사 판매량을 입력으로 삼는다:

public class MdPlanningTools { private final MdPlanningService mdService; public MdPlanningTools ( MdPlanningService svc) { this . mdService = svc; } @Tool (name = "md_weekly_plan", description = "역사 판매량(동일 주차 동일 기간, 최근 주차 추세)을 실증 기반으로 하고, 시간 노드(고객 생활 리듬/계절/절기/명절/장면)를 결합하여 지정 주차의 52주 MD 중점 상품과 경영 제안을 생성한다") public MdWeeklyPlanResult weeklyPlan ( @ToolParam (name = "week", description = "ISO 주차, 예: '2026-W34' ") String week, // 역사 판매량: 지정 주차의 최근 N주 판매량과 전년 동기로, 실증적 상품 선정과 수요 기준선에 사용 —— 이것이 52주 MD의 "토대"다 @ToolParam (name = "historySales", description = "역사 판매량: 동일 주차 동일 기간/최근 주차 추세, 실증적 상품 선정과 수요 기준선에 사용") String historySales, @ToolParam (name = "context", description = "컨텍스트 요약: 고객 생활 리듬/계절/절기/명절/장면") String context ) { return mdService. plan ( new MdPlanCommand (week, historySales, context)); } }

장면의 "결정성"은 두 가지에서 나온다: 상품 선정에는 sales.history 실증이 있고, 가격 인하에는 REQUIRE_USER_CONFIRM의 관문이 있다.

4.2 장면 2: 지혜로운 소진 처리(출청) 코드 구현

매일 저녁 폐점 전의 출청은 "정시 트리거 + 실시간 대응"이다: 정해진 시각에 스케줄러가 기동하고, 그날 저녁 날씨, 당일 실시간 판매에 따라, 그리고 해당 카테고리의 역사 판매량(동일 날씨 / 동일 시간대 소진 기준선)을 대조하여 할인율과 시간대를 동적으로 정하며, 가격을 내리기 전에 점장에게 확인을 받는다.

// 场景二 진입점: 스케줄러가 폐점 3시간 전에 트리거하며(cron / 스케줄링 프레임워크), sessionId를 고정해 크래시 복구에 유리하게 한다(4.3 기억 참조) public Mono runClearance ( SessionContext ctx ) { // ① 의도 식별은 clearance (4.3의 동일한 3계층 퍼널 재사용), 여기서는 곧바로 출청 전문가로 진입 return clearanceAgent.call( List.of( new UserMessage("user", "오늘 밤 출청 실행: weather.query로 당일 밤 날씨를 가져오고, sales.history로 해당 카테고리의 동일 날씨/동일 시간대 소진 베이스라인을 가져오며, " + "sales.realtime으로 당일 실시간 판매 동향을 가져와, 폐점 카운트다운과 결합해 출청 할인 강도와 최적 출청 시간대를 산출한다")), ClearanceReply. class , RuntimeContext.builder() .sessionId("clearance-daily") // 고정 sessionId: 일별 출청 작업이 각각 복구 가능(보장 3, 4.3 참조) .userId(ctx.uid).build()) .flatMap(reply -> { ClearanceReply r = reply.getStructuredData(ClearanceReply. class ); // ② 가격 인하 실행: clearance.apply는 고위험 쓰기 작업으로, tools.json에 ASK로 표시되어 프레임워크가 REQUIRE_USER_CONFIRM을 던지고, // 점장이 터미널/App에서 확인하기를 기다린다(4.3 권한 참조); 할인이 너무 깊으면(< 6할) PermissionMiddleware가 추가로 지역 매니저 승인을 요구한다 clearanceAgent.call(List.of( new UserMessage("user", "제안에 따라 가격 인하 실행: " + r.toBrief())), Void. class , RuntimeContext.builder() .sessionId("clearance-daily").userId(ctx.uid).build()).block(); // ③ 관측 가능성: 출청 전 과정이 streamEvents로 흘러나오고 AgentStateStore에 저장되어 복구 가능(4.3 참조) return Mono.just(r); }); }

출청의 가격 인하와 제안 역시 @Tool로 노출하며, 핵심은 "역사 베이스라인 + 실시간 판매 동향 + 폐점 카운트다운" 세 가지의 차이값 의사결정이다:

public class ClearanceTools { private final ClearanceService clearanceService; public ClearanceTools ( ClearanceService svc) { this . clearanceService = svc; } @Tool (name = "clearance_suggest", description = "당일 밤 날씨, 당일 실시간 판매 동향과 역사 판매량(동일 날씨/동일 시간대 소진 베이스라인), 폐점 카운트다운을 결합해 신선식품 출청 할인 강도와 최적 출청 시간대를 제시한다") public ClearanceSuggestion suggest ( @ToolParam (name = "weather", description = "당일 밤 날씨, 저녁 피크 시간대 유동 인구에 영향") String weather, @ToolParam (name = "realtimeSales", description = "당일 실시간 판매 동향: 잔여 재고와 판매율") String realtimeSales, // 역사 판매량을 동일 날씨/동일 시간대의 통상 소진 베이스라인으로 삼아 "오늘 밤이 평소보다 얼마나 더 팔기 어려운지"를 판단한다 @ToolParam (name = "historyBaseline", description = "역사 판매량 동일 날씨/동일 시간대 소진 베이스라인") String historyBaseline, @ToolParam (name = "minutesToClose", description = "폐점까지 남은 분 수") int minutesToClose ) { return clearanceService. suggest ( new ClearanceCommand (weather, realtimeSales, historyBaseline, minutesToClose)); } @Tool (name = "clearance_apply", description = "출청 가격 인하 실행(고위험 쓰기 작업으로 점장 확인 필요; 할인 6할 미만은 지역 매니저 승인 필요)") public void apply ( @ToolParam (name = "discount", description = "출청 할인, 예: 0.7은 7할을 의미") double discount, @ToolParam (name = "startAt", description = "최적 출청 시간대, ISO 시간") String startAt ) { clearanceService. apply ( new ApplyCommand (discount, startAt)); } }

场景二와 场景一은 동일한 Harness 섀시를 공유한다: 의도, 데이터 조회(MCP), 권한(PermissionMiddleware + REQUIRE_USER_CONFIRM ), 관측 가능성(OtelTracingMiddleware + streamEvents ), 기억(장기 MEMORY.md + 단기 AgentStateStore ).

4.3 공통 역량 모듈

① 의도 식별(3계층 퍼널 + 구조화 출력) — 규칙 / 소형 모델은 애플리케이션 계층에 있고, 세 번째 계층만 AgentScope를 사용한다:

// 응용 계층: 의도 인식 결과 (구조화 출력 목표 클래스) public class IntentResult { @JsonPropertyDescription("의도 유형: md_plan / md_execute / clearance / query / unknown") public String intent; @JsonPropertyDescription("추출된 핵심 슬롯, 예: 매장 ID, 상품 SKU, 시간 범위") public Map slots; @JsonPropertyDescription("모델 자기평가 신뢰도 0~1") public double confidence; } // 세 번째 계층: AgentScope-Java 2.0의 구조화 출력으로 자연어를 강타입 객체로 변환 public class LlmIntentRecognizer implements IntentRecognizer { private final HarnessAgent intentAgent; public LlmIntentRecognizer (HarnessAgent intentAgent) { this .intentAgent = intentAgent; } @Override public IntentRecognitionResult recognize (String userInput, SessionContext context) { Msg msg = intentAgent.call( List.of( new UserMessage ("user", userInput)), IntentResult.class, RuntimeContext.builder() .sessionId(context.getSessionId()) .userId(context.getUserId()) .build()) .block(); IntentResult r = msg.getStructuredData(IntentResult.class); return IntentRecognitionResult.of(r.intent, r.slots, r.confidence); } }

② MCP 도구 연동——workspace/tools.json으로 선언형으로 리테일 내부 MCP Server에 연동하며, 기동 즉시 자동 등록된다(더 세밀한 제어가 필요하면 McpClientBuilder 사용 가능). 두 시나리오의 도구는 모두 동일한 통로에 걸려 있다:

{ "mcpServers": { "retail-gateway": { "command": "npx", "args": ["-y", "@retail/mcp-server"], "env": { "RETAIL_API_TOKEN": "${env:RETAIL_API_TOKEN}" } } } }

Agent Tool Call → McpClientWrapper → MCP Server (리테일 내부 도구 게이트웨이) ├─ calendar.solar_terms 절기/캘린더 조회 (시나리오 1·상품 선정 입력) ├─ weather.query 날씨 조회 (시나리오 1/2 공용) ├─ member.segment 고객군 태그 조회 (시나리오 1·마케팅 타겟 선정) ├─ competitor.intel 경쟁사 정보 조회 (시나리오 1·상품 선정 입력) ├─ md.weekly_plan 중점 상품 선정+주간 계획 작성 (시나리오 1·핵심) ├─ sales.realtime 당일 실시간 판매 동향 조회 (시나리오 2·출청 입력) ├─ sales.history 이력 판매량 조회(동일 주차 동기/최근 주간 추세/동일 날씨 동일 시간대 기준선) (시나리오 1·상품 선정 실증 / 시나리오 2·출청 기준선) ├─ clearance.suggest 출청 제안(할인+시간대) (시나리오 2·핵심) ├─ promotion.create 프로모션 생성(고위험 쓰기) (시나리오 1·실행) ├─ clearance.apply 출청 가격 인하 실행(고위험 쓰기) (시나리오 2·실행) └─ coupon.issue 쿠폰 발급(고위험 쓰기) (시나리오 1·실행)

③ 권한 제어(RBAC + ABAC + 데이터 권한)——굵은 단위의 "호출 가능 여부"는 PermissionEngine(선언형, HITL 포함)에 맡기고, 세밀한 단위의 "어떤 규칙으로, 어느 범위를 조회할지"는 onActing 훅에서 구현한다.

@Component public class PermissionMiddleware extends MiddlewareBase { private final RbacService rbacService; public PermissionMiddleware ( RbacService rbacService ) { this .rbacService = rbacService; } @Override public Mono onActing ( MiddlewareContext ctx, HookEvent event ) { UserPrincipal user = ctx.runtime().getUser(); String toolName = currentToolName( event ); // 1) RBAC 검사 if (!rbacService.hasPermission(user.getRole(), toolName)) { return Mono.error( new PermissionDeniedException(”도구 호출 권한 없음: ” + toolName)); } // 2) ABAC: 프로모션 생성/쿠폰 발행은 점장 또는 지역 매니저가 필요하고, 단일 쿠폰 액면가가 50위안을 초과하면 2차 확인 필요 if (”promotion.create”. equals (toolName) || ”coupon.issue”. equals (toolName)) { BigDecimal faceValue = toolArg( event , ”faceValue”, BigDecimal. class ); if (faceValue != null && faceValue.compareTo( new BigDecimal(” 50 ”)) > 0 && !”REGION_MANAGER”. equals (user.getRole())) { return Mono.error( new PermissionDeniedException(”쿠폰 액면가가 임계값을 초과하여 지역 매니저 승인이 필요함”)); } } // 출청 할인 폭이 지나치게 큰 경우(예: 6할 미만) 지역 매니저 승인 필요 if (”clearance.apply”. equals (toolName)) { BigDecimal discount = toolArg( event , ”discount”, BigDecimal. class ); if (discount != null && discount.compareTo( new BigDecimal(” 0.6 ”)) < 0 && !”REGION_MANAGER”. equals (user.getRole())) { return Mono.error( new PermissionDeniedException(”출청 할인이 6할 미만이므로 지역 매니저 승인이 필요함”)); } } // 3) 데이터 권한 주입: 점장은 자기 매장만 관리할 수 있음(RuntimeContext에 기록하여 도구 구현 계층이 읽도록 함) if (”md.weekly_plan”. equals (toolName) || ”promotion.create”. equals (toolName) || ”clearance.apply”. equals (toolName)) { ctx.runtime().setDataScope( new StoreDataScope(user.getStoreId())); } return Mono.just( event ); } }

④ 관측 가능성(추적 + 타입화된 이벤트 스트림) — OtelTracingMiddleware는 한 줄로 OTel에 연동되며, streamEvents()는 모델 호출 / 도구 실행 / 사람 승인을 타입화된 이벤트로 실시간 흘려보내는데, 두 시나리오의 링크 추적과 감사 모두 이것에 의존한다:

HarnessAgent agent = HarnessAgent.builder() .name(”store-ops-assistant”) .model(”dashscope:qwen-max”) .workspace(Path.of(”./store-ops-workspace”)) .middlewares(List.of( new PermissionMiddleware(rbacService), // ③ 권한 차단 new OtelTracingMiddleware())) // 관측성: 한 줄로 OTel 연동 .build(); // 타입화된 이벤트 스트림: 프런트엔드 실시간 추적, 계측 통계를 한 번의 구독으로 전부 커버 agent.streamEvents( List.of( new UserMessage(”user”, ”이번 주(8월 셋째 주) 52주 MD를 만들어 줘: 입추가 막 지났고, 고온, 커뮤니티 중장년층 위주, 개학 시즌이 다가옴, 중점 상품을 선정해 줘; 확정 후 프로모션 생성, 커뮤니티 문안 생성, 회원 타깃 선별.”)), RuntimeContext.builder().sessionId(sid).userId(uid).build()) .doOnNext( event -> { switch ( event .type()) { case AGENT_START: /* 한 번의 완전한 응답 시작, 전역 진행률 */ break ; case TEXT_BLOCK_DELTA: /* 텍스트 글자 단위 증분, 스트리밍 UI 구동 */ break ; case TOOL_CALL_START: /* 도구 호출 시작, 관측 가능 */ break ; case TOOL_RESULT_END: /* 도구 반환 결과 */ break ; case REQUIRE_USER_CONFIRM: /* 사람의 승인 필요(HITL), 권한 확인 트리거 */ break ; case MODEL_CALL_START: case MODEL_CALL_END: /* 모델 호출 소요 시간 모니터링 */ break ; case AGENT_END: /* 응답 종료 */ break ; } }) .blockLast();

⑤ 기억과 컨텍스트 엔지니어링——장기 기억은 MEMORY.md에 기록하고, 단기 세션 상태는 sessionId에 따라 AgentStateStore(Redis)에 연결한다(시나리오 2에서는 clearance-daily를 고정해 일별 일소 작업이 각각 복구 가능하도록 함); 컨텍스트 유계화는 compaction + toolResultEviction에 의존한다:

HarnessAgent agent = HarnessAgent.builder() .name(”store-ops-assistant”) .model(”dashscope:qwen-max”) .workspace(Path.of(”./store-ops-workspace”)) .memory(MemoryConfig.builder().build()) // 장기 기억: 프로필/선호를 MEMORY.md에 기록 .compaction(CompactionConfig.builder() // ① 구조화 압축 .triggerMessages( 30 ).keepMessages( 10 ) .truncateArgs(CompactionConfig.TruncateArgsConfig.builder() .maxArgLength( 2000 ).build()) // ④ 파라미터 절단 .build()) .toolResultEviction(ToolResultEvictionConfig.defaults()) // ② 대형 결과 오프로드 .build(); // ③ 오버플로 폴백: compaction을 켜 두면, 모델이 context_length_exceeded를 반환할 때 프레임워크가 자동으로 극단 압축 후 재시도

4.4 HarnessAgent 조립: builder와 미들웨어 채널

앞의 4.1~4.3은 모두 “Harness에 능력을 채워 넣는” 내용이었고, 이 절에서는 Harness 자체가 어떻게 builder로 껍데기를 세우고, 두 개의 확장 채널로 능력을 걸어 붙이는지 살펴본다. 아래는 매장 어시스턴트가 기동될 때의 조립 코드다:

HarnessAgent agent = HarnessAgent.builder() .name(”store-ops-assistant”) .model(”dashscope:qwen-max”) // 채널 1: Middleware —— 5개 훅(onSystemPrompt/onReasoning/onActing/onModelCall/onAgent)에 로직 삽입 // 권한 검증, 추적, 컨텍스트 주입은 모두 여기에 걸며, 이 글의 PermissionMiddleware / OtelTracingMiddleware가 이 채널을 이용함 .middlewares(List.of( new PermissionMiddleware (rbac), new OtelTracingMiddleware ())) // 채널 2: Toolkit —— Agent의 능력 컨테이너, @Tool 메서드, MCP 클라이언트, 검색 도구, 스킬 패키지를 모두 담음 .toolkit(toolkit) .build(); // 추론은 ReAct에, 실행은 Harness에: 덧붙일 뿐, 대체하지 않음

채널 1 Middleware 구현: MiddlewareBase를 상속하고, 필요에 따라 다섯 개 훅을 재정의한다(순서 onSystemPrompt → onReasoning → onActing → onModelCall → onAgent), 훅의 책임은 주석 참조:

public class TracingMiddleware extends MiddlewareBase { @Override public Mono onSystemPrompt ( MiddlewareContext c, HookEvent e ) { /* Workspace 페르소나+기억 주입(보장 1) */ return Mono . just (e); } @Override public Mono onReasoning ( MiddlewareContext c, HookEvent e ) { /* 보안 감사/하드 제약 주입 */ return Mono . just (e); } @Override public Mono onActing ( MiddlewareContext c, HookEvent e ) { /* 권한/민감정보 마스킹의 주전장(4.3 참조) */ return Mono . just (e); } @Override public Mono onModelCall ( MiddlewareContext c, HookEvent e ) { /* 속도 제한/계량/라우팅 */ return Mono . just (e); } @Override public Mono onAgent ( MiddlewareContext c, HookEvent e ) { /* 라이프사이클 계측 */ return Mono . just (e); } }

네 가지 엔지니어링 보장을 builder에 얹으면 몇 줄의 설정에 불과하다:

HarnessAgent . builder () . name (”store-ops-assistant”) . workspace ( Path . of (”./store-ops-workspace”)) // 보장 1+자산 Workspace: 페르소나+장기 기억+도메인 지식, 매 라운드 자동으로 prompt에 주입 . memory ( MemoryConfig . builder (). build ()) // 보장 1 의 ”기억” 측(Workspace 영속화) . compaction ( CompactionConfig . defaults ()) // 보장 2+자산: 자동 구조화 압축, 컨텍스트 유계 . toolResultEviction ( ToolResultEvictionConfig . defaults ()) // 보장 2: 대형 도구 결과 언로드 . middlewares ( List . of ( new PermissionMiddleware (rbac), // 보장 3 횡단 보호 + 자산 Sandbox 보안 측(위험 작업 차단/롤백) new OtelTracingMiddleware ())) . toolkit (toolkit) // 보장 4 능력 축적 + 자산 Skill 4계층 조합: 도구 + SubagentDeclaration 오케스트레이션 // 보장 3 상태 복구 가능: 세션 상태는 sessionId에 따라 AgentStateStore(Redis)에 기록, 크래시/배포 시 복구 가능 // 자산 Plan Mode: 복잡한 작업은 먼저 Plan Mode로 진입해 단계 목록을 생성하고, 확인/자동 승인 후 실행(처음부터 도구를 마구 호출하는 것 방지) . build ();

5. HarnessAgent 런타임

5.1 하나의 요청이 Harness 안에서 흐르는 방식

앞서 4.4에서 HarnessAgent의 조립 코드를 제시했으니, 여기서는 그것이 "실행될 때" 어떤 모습인지 살펴본다: 하나의 요청이 접속 계층에서 들어오면 다섯 개 Middleware 훅(onSystemPrompt → onReasoning → onActing → onModelCall → onAgent)을 순차적으로 통과하며, 각 단계 뒤에서 하나의 엔지니어링 보장이 켜진다. 아래에서는 시나리오 1의 전체 링크와 시나리오 2의 정기 링크를 각각 그려 보며, 코드가 런타임에서 어떻게 구현되는지 살펴본다:

먼저 시나리오 1을 보자:

接入层(REST /WS) │ 包成 RuntimeContext(sessionId, userId, tenant) ▼ HarnessAgent ”store-ops-assistant” │ ├─① onSystemPrompt ── Workspace 주입: 페르소나 + MEMORY.md 장기 기억 + 도메인 지식 → 보장 1 Identity persists │ ├─② 총괄 제어가 sysPrompt 5단계 프로세스에 따라 agent_spawn intent_agent │ → 구조화된 의도 {intent: md_plan, slots: {week: 2026-W34, season: 立秋, weather: 高温, segment: 社区中老年, event: 开学季}} │ ├─③ onReasoning ── 이 훅에서 감사/재작성 가능(예: "반드시 도구 데이터를 사용하고, 날조를 금지"라는 하드 제약 주입) │ ├─④ md_planner_agent 단계 분해: │ ① sales.history로 과거 판매량 조회(동일 주차·동일 시기, 최근 주 추세) → ② calendar.solar_terms로 절기 캘린더 조회 │ ③ weather.query로 날씨 조회 → ④ member.segment로 고객군 조회 → ⑤ competitor.intel로 경쟁사 정보 조회 │ ⑥ md.weekly_plan으로 중점 상품 선정 + 주간 계획 작성(과거 판매량을 실증 베이스라인으로 사용) │ ├─⑤ 각 단계 실행 전 onActing ── PermissionMiddleware가 RBAC/ABAC 수행 + 데이터 범위 주입(점장은 자기 매장만 관리 가능) │ └─ promotion.create / coupon.issue는 고위험 쓰기 작업 → PermissionEngine이 ASK 표시 → REQUIRE_USER_CONFIRM을 던져 사람의 확인 대기 │ ├─⑥ onModelCall ── 속도 제한/계량 → McpClientWrapper가 JSON-RPC로 리테일 MCP Server 호출 │ → 비즈니스 서비스 계산 → 결과 반환 │ ├─⑦ 수십 라운드 실행 후 컨텍스트가 상한에 근접 → CompactionConfig가 자동 구조화 압축 + ToolResultEviction이 대형 결과를 언로드 → 보장 2 Context stays bounded │ ├─⑧ streamEvents가 AGENT_START / TOOL_CALL_* / REQUIRE_USER_CONFIRM / AGENT_END를 실시간 스트리밍 → 프런트엔드 렌더링 + 감사 → 보장 2 + 관측 가능성(4.3 관측 가능성 참조) │ ├─⑨ 응답 종료 시 전체 상태를 sessionId 기준으로 AgentStateStore(Redis)에 저장 → 크래시/배포 시 복구 가능 → 보장 3 State recoverable │ └─⑩ 전 과정의 역량은 .toolkit()에 등록된 도구와 SubagentDeclaration 오케스트레이션에서 나옴 → 경험이 세션을 넘어 축적 → 보장 4 Capabilities accrue

이제 시나리오 2를 보자:

저녁 정시 작업(cron / 스케줄러) → RuntimeContext(sessionId=clearance-daily, userId=점장) │ ├─ 의도 전문 에이전트가 clearance로 식별 ├─ clearance_agent: │ ① weather.query로当晚 날씨 조회(예: 소나기/기온 급강하가 유동 인구에 영향) │ ② sales.history로 해당 카테고리 과거 판매량 조회(동일 날씨/동일 시간대 소진 베이스라인) │ ③ sales.realtime으로 당일 실시간 판매 동향 조회(잎채소/신선 우유 잔여 재고와 판매율) │ ④ 과거 베이스라인 대조 + 폐점 카운트다운 결합 → 출청 할인 강도(예: 7할)와 최적 출청 시간대(예: 폐점 2시간 전) 계산 │ ⑤ clearance.apply로 가격 적용(고위험 쓰기) → PermissionEngine이 ASK 표시 → REQUIRE_USER_CONFIRM으로 점장 확인 대기 └─ 전 과정 streamEvents로 관측 가능, AgentStateStore에 저장되어 복구 가능

두 링크를 대조해 보면 핵심은 동일한 Harness 박스가 어떻게 "서로 다른 행동에 대응"하는가이다. 시나리오 1은 "사람이 한마디 던짐 → 다중 Agent 협업 → 고위험 작업은 확인 대기", 시나리오 2는 "정시 트리거 → 실시간 대응 → 가격 적용은 확인 대기"다. 둘은 동일한 훅과 보장 체계를 공유한다—onSystemPrompt가 신원을 주입하고(보장 1), compaction이 컨텍스트를 지키고(보장 2), onActing이 위험한 쓰기 작업을 차단하며 AgentStateStore가 상태를 보강하고(보장 3), .toolkit()이 역량을 축적한다(보장 4). 훅의 역할과 builder 조립은 4.4, 권한/관측 가능성/기억 등 모듈은 4.3을 참조하라.

六、효과 평가 지표

앞서 매장 어시스턴트의 역량과 런타임을 모두 설명했고, 마지막 관문은 "출시 후 실제로 유용하다는 것을 어떻게 증명할 것인가"다. 아래 지표 세트는 비즈니스 가치와 엔지니어링 확실성을 동시에 정량화한다—앞의 다섯 항목은 비즈니스 효과(상품 선정이 정확한지, 출청이 합리적인지, 손실이 줄었는지)를, 뒤의 세 항목은 엔지니어링 하한선(권한 월권 제로, 지연 제어 가능, 비용 산정 가능)을 본다:

| 지표 | 목표값 | 측정 방식 | | --- | --- | --- | | 의도 인식 정확도 | ≥ 90% | 수동 라벨링 테스트 세트 | | 도구 호출 성공률 | ≥ 99% | 온라인 모니터링(streamEvents + Micrometer) | | 중점 상품 선정 적중률 | ≥ 85% | 점장 수동 선정 / 실제 판매 동향 대비 | | 출청 할인 합리율 | ≥ 80% | 점장 수동 출청 / 실제 판매 소진 대비 | | 신선 식품 손실 저감율 | ≥ 15% | 출청 전후 손실 신고 금액 대비 | | 월권 차단율 | 100% | 권한 테스트 케이스 | | 종단간 P99 지연 | ≤ 3s | 링크 추적(OtelTracingMiddleware) | | 단일 상호작용 비용 | ≤ 0.05 元 | Token 통계 |

이 숫자들은 KPI 전시용이 아니다. 비즈니스 측에서는 "시스템을 짜깁기해서 생긴 손실과 비효율"에 직접 응답하고, 엔지니어링 측에서는 제5장의 네 가지 보장에 대응한다. 즉, 권한 초과 차단율 100%는 onActing 권한 게이트이며, P99 ≤ 3s와 단일 호출 비용은 OtelTracingMiddleware 관측성 + compaction 구조화 압축에서 나오고, 상품 선정/출고의 적중률은 바로 sales.history의 실증 베이스라인으로부터 얻은 확실성이다.

7. 정리

본 방안은 AgentScope-Java 2.0으로 리테일 신선 식품 슈퍼마켓의 "52주 MD 주간 계획 + 지능형 출고 일일 정리"라는 두 가지 시나리오 어시스턴트를 구축했다. 이 글의 핵심은 Harness 실행 계층이다. 이것은 "생각할 줄 아는 추론 코어"를 "실무에 투입할 수 있는 비즈니스 어시스턴트"로 바꾸며, 네 가지 보장(신원 지속 / 컨텍스트 유계 / 상태 복구 가능 / 능력 축적 가능)이 처음부터 끝까지 관통한다:

Harness는 골격이다. HarnessAgent + 다섯 개의 SubagentDeclaration(의도/상품 선정 계획/마케팅 실행/출고/권한 전문관)이 총괄 제어로 연결되고, 두 개의 확장 채널(Middleware 다섯 개 훅 + Toolkit)이 모든 횡단 관심사와 능력을 담당하며, Workspace / Sandbox / Plan Mode / Skill이라는 네 가지 자산이 "프로덕션 운영"을 실현한다(제3장, 제5장). 두 시나리오, 즉 주간 계획과 일일 정리는 모두 이 골격 위에서 작동한다.

코드 정밀화는 기어다. 제4장은 두 시나리오를 실제로 코드에 써 넣는다. MCP 통합 도구 계층, agent.call(Class) 구조화 출력, 3계층 의도 식별, PermissionEngine 사상(四態) + PermissionMiddleware 세밀한 차단, OtelTracingMiddleware + streamEvents 전 구간 관측, MemoryConfig + CompactionConfig 컨텍스트 엔지니어링이 전부 코드 + 주석으로 52주 MD와 지능형 출고라는 두 비즈니스 라인에 구현된다(제4장 4.3 공통 모듈 참조).每一项 모두 Harness 보장의 구현이다.

마지막으로

여기까지 왔다면, 이 AgentScope Java 2.0 여정은 공식적으로 마무리된다. 처음부터 따라 쳐 왔다면, 이제 AgentScope Java 2.0으로 엔터프라이즈급 지능형 에이전트를 구축하는 완전한 지식 스택, 즉 핵심 개념, 도구 호출부터 기억, RAG, 다중 Agent, 나아가 프로덕션급 Harness, 권한, 내결함성과 관측성까지 갖추게 되었을 것이다.

하지만 지식 포인트보다, 나는 당신에게 몇 가지 속마음을 말하고 싶다.

나는 안다. 이 글을 읽고 있는 당신은 최근 대개 어떤 감정에 휩싸여 있을 것이다. "배울 수 없다"가 아니라 "혹시 대체되지 않을까"라는 감정이다. 바이브 코딩은 하루가 다르게 발전한다. V0, Cursor, Claude Code 같은 것들은 이미 몇 줄의 코드를 보완하는 데 그치지 않고, 자연어 설명 하나로 곧바로 프런트엔드와 백엔드 전체를 튀어나오게 하고, 스스로 반복하고, 스스로 버그를 고친다. SNS에는 매일 "반나절 만에 제품 하나를 만들었다"고 자랑하는 사람이 있고, 회사는 "AI가 더 많이 일하게 하자"를 크게 내세운다. 다년간 코드를 작성해 온 사람이라면, 처음으로 진짜로 느낄 것이다. 자신의 가장 값진 능력이 다시 가격이 매겨지고 있는 듯한 느낌을.

이런 두려움은 너무나 당연하다. 나도 안다.

사실 우리 세대는 이미 시대의 후대를 두 번이나 받았다. 우리는 정보화가 가속되던 시대에 태어나, 인터넷이 세상을 하나의 그물로 연결하는 것을 직접 보았고, 또 모바일 인터넷이 이 그물을 모든 사람의 주머니에 밀어 넣는 것을 보았다. 이커머스, 모바일 결제, 인스턴트 메신저……앞의 두 물결은 실제로 삶 속으로 스며들어 "연결"과 "효율"을 전례 없는 높이로 끌어올렸고, 우리는 거의 손쉽게 이를 받아안았으며 그다지 애쓰지 않아도 되었다.

이제, 세 번째 물결이 발밑까지 밀려왔다. 하지만 이번 물결은 다르다. 앞의 두 물결은 "연결", 즉 사람과 정보, 사람과 서비스를 더 빠르게 맞춰 주는 것이었다. 반면 AI라는 이번 물결은 "코드를 작성하는" 일 자체를 직접 건드린다. 그것은 누군가 준비되기를 기다리지 않고, 구경꾼에게만 상을 주지도 않는다.

그래서 내가 말하고 싶은 것은 바로 이것이다. 코딩이 재정의되고 있기 때문에, 당신은 더욱 직접 Agent를 한 번 구축해 보아야 한다. AI는 멋진 CRUD를 작성할 수 있지만, 왜 이 신선 식품 슈퍼마켓이 비 오는 저녁에 할인을 더 깊게 해야 하는지, 매장의 재고 데이터가 어느 "짜깁기된" 낡은 시스템에 숨어 있는지, 필드마저 잘못 적혀 있는지는 알지 못한다. 이런 "비즈니스 속의 지저분한 일과 진짜 문제"야말로 당신의 진짜 해자이며, AgentScope 같은 엔지니어링 프레임워크는 바로 경험을 시스템으로 고정하는 길을 제공한다. 당신이 가장 잘 아는 비즈니스 영역부터 시작해, 직접 Agent를 하나 구축하고 실제 시스템에 연결해 한 바퀴 돌려 보면, 당신은 더 이상 "코드를 작성하는 사람"이 아니라 "시스템이 어떻게 사고할지를 정의하는 사람"이 될 것이다.

이 여정에서 당신은 혼자 탐색하는 것이 아니다. 여기까지 읽어 주셔서 감사하다. 유용하다고 느꼈다면 즐겨찾기와 관심 부탁드립니다. 스택지견(栈知见) 위챗 공식 계정이며, 다음 시리즈 《LangChain + LangGraph 엔지니어링 실전》에서 다시 만나겠다.

이 글이 도움이 되었다면, 위챗 공식 계정 「栈知见」을 팔로우해 주세요. 계속해서 깊이 있는 기술 실전 노트를 발행하겠습니다.

栈知见

16

글

630

읽음

2

팔로워