나무숲
경력근거: LLM 기반 서비스 흐름을 구현해요
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고가 주요업무에 '멀티 에이전트 플로우' 설계와 'LLM 기반 서비스 흐름을 구현해요' 를 적고, 뒤이어 '성능·비용 최적화를 진행해요' 를 나란히 두었습니다. 흐름이 길어지면 비용과 지연이 어느 단계에서 새는지가 곧 문제가 되고, 이 포트폴리오는 그 측정을 예산표로 옮깁니다. 'AI 기반 사내 AX 전환' 을 돕는 자리라 사내 실행을 재는 눈금이 먼저 필요합니다. 이 포트폴리오가 보여주는 것은 예산을 데이터로 두고 실행 기록과 맞춰 새는 단계를 짚는 기술이며, 모델 자체를 손보는 능력이나 특정 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
'멀티 에이전트 플로우' 를 설계하는 자리입니다. 흐름이 길어질수록 어느 단계가 비싼지가 문제로 올라옵니다.
'AI 기반 사내 AX 전환' 을 돕는 일입니다. 사내 실행을 재는 도구가 그 전환의 눈금입니다.
'성능·비용 최적화를 진행해요'. 이 편은 그 최적화의 앞단인 측정을 맡습니다.
에이전트가 남긴 실행 기록을 단계별 예산과 맞춰 어디서 비용과 시간이 새는지 가릅니다. 단계 종류와 모델별 예산표에 천 토큰 단가와 허용 지연, 같은 단계를 몇 번까지 부를지를 두고 실행 기록과 맞춰, 기준 안이면 비용을 계산해 붙이고 그렇지 않으면 지연 초과·되돌이 초과·단계 실패 중 어느 쪽인지 사유로 남깁니다. 토큰 수가 안 기록된 단계는 비용을 셀 수 없다고 표시하고, 예산표에 없는 모델은 단가 미정으로 남겨 먼저 정하게 합니다. 실행 기록은 사내 운영 로그 대신 지원자가 자기 계정으로 에이전트를 돌려 공개 규약 형식으로 내보낸 것을 가정합니다.
사내 업무를 에이전트로 옮기는 일을 맡은 개발자입니다. 팀에서 '느리다'거나 '비용이 많이 나온다'는 말이 나올 때마다 실행 로그를 펼쳐 단계를 눈으로 세며 어디가 문제인지 찾던 사람입니다.
단계 종류와 모델마다 단가와 허용 지연, 반복 상한을 예산으로 적어 두고 실행 기록과 맞추면, 비용과 시간이 새는 단계가 실행마다 드러나고 그 판단 근거가 함께 남는다는 가정입니다. 예산표에 없는 모델은 단가 미정으로 빠지므로 아직 값을 정하지 않은 모델이 그대로 보입니다.
실행 기록과 예산표를 받아 단계마다 비용을 계산하고, 기준을 넘으면 지연 초과·되돌이 초과·단계 실패·토큰 미기록·단가 미정 중 어느 쪽인지 붙여 실행별로 묶은 점검 리포트를 터미널과 Markdown 파일로 내는 CLI 가 도는 완결품입니다.
지원자 계정으로 에이전트를 200번 돌려 실행 기록을 모으고, 손으로 붙인 정답 판정과 대조해 일치율을 내며 실행당 평균 비용을 냅니다.
값싼 모델로 내리거나 중복 호출을 묶는 변경안을 적용해 같은 입력으로 다시 돌리고, 실행별 비용과 지연이 어떻게 달라졌는지 표로 내는 단계입니다.
개발자가 실행 기록과 예산표를 넣으면 단계마다 비용과 초과 사유가 실행별로 묶여 나오는 과정을 시연합니다.
지원자 계정으로 돌린 실행 200번의 단계 600건과 손으로 붙인 정답 판정을 대조해 일치율을 냅니다. 단, 정답을 작성자가 붙인 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
토큰 수가 비었거나 모델 표기가 흔들리거나 예산표에 없는 모델이 섞인 입력 30종을 넣어도 출력 형식이 안 깨지고 사유별로 빠지는 비율을 보여줍니다.
허용 지연과 반복 상한을 바꿨을 때 옛 판정이 어떻게 뒤집히는지 회귀로 확인합니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 작성자가 돌린 실행의 자가채점이며, 실제 절감액은 나무숲 사내 에이전트의 실 실행량과 실 단가가 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태까지 만듭니다.
수치는 작성자가 돌린 실행의 자가채점이라 성능 증명이 아니므로, 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 붙였으니 잘 맞는 게 당연하므로, 토큰 미기록과 실패한 단계, 예산표에 없는 모델을 일부러 섞어 쉬운 단계만 푸는 함정을 피합니다.
이 포트폴리오가 보여주는 것은 예산을 데이터로 두고 실행 기록과 맞춰 새는 단계를 짚는 기술이며, 모델 자체를 손보는 능력이나 특정 도메인 전문성은 주장하지 않습니다. 계산은 곱셈과 임계 비교가 하고 LLM 은 담당자가 읽을 사유 문장만 맡습니다.
같은 단계 이름을 여러 번 부른 것이 늘 되돌이는 아니어서, 반복 초과로 걸린 실행은 단계 순서를 함께 실어 사람이 보게 둡니다.
실제 도입 때는 모델과 단가가 자주 바뀌므로, 예산표를 이어서 고치는 일을 운영 과제로 둡니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
에이전트가 남긴 실행 기록을 단계 단위로 편 것입니다. 단계 번호와 실행 번호, 상위 단계, 단계 이름, 종류 표기, 모델 표기, 입력·출력 토큰, 지연, 상태, 시작 시각을 한 행에 둡니다. 한 실행 안에 같은 단계 이름이 여러 번 나오면 되돌이가 됩니다. 토큰이 안 기록된 행, 실패한 행, 예산표에 없는 모델을 일부러 섞어 두어 사유가 갈리게 둡니다.
| span_no | trace_no | parent_span_no | step_name | kind_raw | model_raw | input_tokens | output_tokens | latency_ms | status_raw | started_at |
|---|---|---|---|---|---|---|---|---|---|---|
| SP-001 | TR-01 | — | 문의 처리 에이전트 | agent | — | — | — | 8,400 | OK | 2026-08-21T10:00:00 |
| SP-002 | TR-01 | SP-001 | 의도 분류 | llm | haiku | 820 | 40 | 640 | OK | 2026-08-21T10:00:00 |
| SP-003 | TR-01 | SP-001 | 사내 문서 검색 | retrieval | — | — | — | 310 | OK | 2026-08-21T10:00:01 |
| SP-004 | TR-01 | SP-001 | 답변 작성 | llm | sonnet | 6,400 | 900 | 5,200 | OK | 2026-08-21T10:00:02 |
| SP-005 | TR-02 | — | 발주 확인 에이전트 | agent | — | — | — | 41,000 | OK | 2026-08-21T11:00:00 |
아래 JSON Schema 를 그대로 만족하는 실행 단계 행을 600개 더 만들어 주세요. 실행 하나에 상위 단계 하나와 하위 단계 서넛을 붙이고 종류를 모델 호출·도구 호출·검색으로 섞습니다. 몇 실행에는 같은 단계 이름을 세 번 이상 넣어 되돌이를 만들고, 2할은 토큰 수나 지연을 비우며, 몇 행은 상태를 실패로 두세요. 예산표에 없는 모델 이름도 몇 행 넣습니다. 비용이나 판정은 넣지 마세요.
{"type": "object", "properties": {"span_no": {"type": "string"}, "trace_no": {"type": "string"}, "parent_span_no": {"type": ["string", "null"]}, "step_name": {"type": "string"}, "kind_raw": {"type": "string"}, "model_raw": {"type": ["string", "null"]}, "input_tokens": {"type": ["integer", "null"]}, "output_tokens": {"type": ["integer", "null"]}, "latency_ms": {"type": ["integer", "null"]}, "status_raw": {"type": "string"}, "started_at": {"type": "string"}}, "required": ["span_no", "trace_no", "parent_span_no", "step_name", "kind_raw", "model_raw", "input_tokens", "output_tokens", "latency_ms", "status_raw", "started_at"]}{"type": "object", "properties": {"span_no": {"type": "string"}, "trace_no": {"type": "string"}, "parent_span_no": {"type": ["string", "null"]}, "step_name": {"type": "string"}, "kind_raw": {"type": "string"}, "model_raw": {"type": ["string", "null"]}, "input_tokens": {"type": ["integer", "null"]}, "output_tokens": {"type": ["integer", "null"]}, "latency_ms": {"type": ["integer", "null"]}, "status_raw": {"type": "string"}, "started_at": {"type": "string"}}, "required": ["span_no", "trace_no", "parent_span_no", "step_name", "kind_raw", "model_raw", "input_tokens", "output_tokens", "latency_ms", "status_raw", "started_at"]}단계 종류와 모델별 예산표입니다. 예산 번호와 종류, 모델, 입력·출력 천 토큰 단가, 허용 지연, 반복 상한, 담당을 한 행에 둡니다. 모델마다 단가가 크게 달라 같은 토큰 수라도 어느 모델을 썼느냐로 비용이 갈립니다. 실행에 나오는 모델 중 일부는 일부러 넣지 않아 단가 미정이 도출되게 둡니다.
| budget_no | kind_raw | model_raw | usd_per_1k_input | usd_per_1k_output | max_latency_ms | max_repeat | owner_name |
|---|---|---|---|---|---|---|---|
| BG-01 | llm | haiku | 0.001 | 0.004 | 3,000 | 2 | AX플랫폼 |
| BG-02 | llm | sonnet | 0.003 | 0.015 | 8,000 | 2 | AX플랫폼 |
| BG-03 | retrieval | — | — | — | 1,000 | 3 | AX플랫폼 |
| BG-04 | tool | — | — | — | 5,000 | 2 | AX플랫폼 |
| BG-05 | agent | — | — | — | 30,000 | 1 | AX플랫폼 |
아래 JSON Schema 를 그대로 만족하는 예산 행을 30개 더 만들어 주세요. 모델 호출에는 입력·출력 단가를 두고 도구·검색에는 비웁니다. 허용 지연은 종류에 맞게 1초에서 60초 사이로 두고 반복 상한은 1에서 5 사이로 둡니다. 실행에 나오는 모델 중 일부는 일부러 넣지 마세요 — 단가 미정이 있어야 예산표의 구멍이 드러납니다. 판정 결과는 넣지 마세요.
{"type": "object", "properties": {"budget_no": {"type": "string"}, "kind_raw": {"type": "string"}, "model_raw": {"type": ["string", "null"]}, "usd_per_1k_input": {"type": ["number", "null"]}, "usd_per_1k_output": {"type": ["number", "null"]}, "max_latency_ms": {"type": "integer"}, "max_repeat": {"type": "integer"}, "owner_name": {"type": "string"}}, "required": ["budget_no", "kind_raw", "model_raw", "max_latency_ms", "max_repeat", "owner_name"]}{"type": "object", "properties": {"budget_no": {"type": "string"}, "kind_raw": {"type": "string"}, "model_raw": {"type": ["string", "null"]}, "usd_per_1k_input": {"type": ["number", "null"]}, "usd_per_1k_output": {"type": ["number", "null"]}, "max_latency_ms": {"type": "integer"}, "max_repeat": {"type": "integer"}, "owner_name": {"type": "string"}}, "required": ["budget_no", "kind_raw", "model_raw", "max_latency_ms", "max_repeat", "owner_name"]}| Python | 실행 기록과 예산표를 맞춰 비용을 계산하고 반복 호출을 세는 판정기를 맡습니다. |
| OpenTelemetry | 에이전트 실행을 공개 규약 형식으로 내보내 입력을 만듭니다. |
| FastAPI | 2주차부터 실행 기록을 받아 쌓는 수집 엔드포인트를 맡습니다. |
| Markdown | 실행별 점검 리포트와 비용 비교 표를 파일로 냅니다. |
✗ fail → 단가 미정이 절반을 넘으면 예산표를 먼저 채웁니다.
✓ pass → 2주차 실제 실행 수집으로 넘어갑니다.
✗ fail → 어긋난 단계를 모아 허용 지연과 반복 상한을 다시 봅니다.
✓ pass → 3주차 변경안 비교로 넘어갑니다.
단계 하나와 예산이 만나는 자리마다 기준 안인지 정하고 비용을 냅니다. 넘으면 지연 초과·되돌이 초과·단계 실패·토큰 미기록·단가 미정 중 어느 쪽인지 싣습니다. 리포트는 이 사유 코드로 묶습니다.
{"type": "object", "properties": {"verdict_no": {"type": "string"}, "span_no": {"type": "string"}, "trace_no": {"type": "string"}, "kind_raw": {"type": "string"}, "budget_no": {"type": ["string", "null"]}, "span_code": {"type": "string", "enum": ["within_budget", "latency_over", "repeat_over", "step_failed", "usage_missing", "model_unpriced"]}, "cost_usd": {"type": ["number", "null"], "description": "input_tokens 에 천 토큰 단가를 곱한 값과 output_tokens 에 천 토큰 단가를 곱한 값을 더한 비용입니다. 단가가 선언된 단계에만 채웁니다."}, "max_latency_ms": {"type": ["integer", "null"]}, "owner_name": {"type": ["string", "null"]}, "evidence": {"type": "array", "items": {"type": "object", "properties": {"field": {"type": "string"}, "agg": {"type": ["string", "null"]}, "value": {}}, "required": ["field", "value"]}}, "summary": {"type": "string"}, "hedge": {"type": ["string", "null"]}}, "required": ["verdict_no", "span_no", "trace_no", "kind_raw", "span_code", "evidence", "summary"]}[
{
"verdict_no": "SV-001",
"span_no": "SP-002",
"trace_no": "TR-01",
"kind_raw": "llm",
"budget_no": "BG-01",
"span_code": "within_budget",
"cost_usd": 0.00082,
"max_latency_ms": 3000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "input_tokens",
"agg": null,
"value": 820
},
{
"field": "usd_per_1k_input",
"agg": null,
"value": 0.0008
}
],
"summary": "짧은 분류 단계라 값싼 모델로 기준 안에서 끝났습니다.",
"hedge": null
},
{
"verdict_no": "SV-002",
"span_no": "SP-004",
"trace_no": "TR-01",
"kind_raw": "llm",
"budget_no": "BG-02",
"span_code": "within_budget",
"cost_usd": 0.0327,
"max_latency_ms": 8000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "output_tokens",
"agg": null,
"value": 900
},
{
"field": "usd_per_1k_output",
"agg": null,
"value": 0.015
}
],
"summary": "답변 작성이 가장 비싼 단계지만 지연도 비용도 기준 안입니다.",
"hedge": null
},
{
"verdict_no": "SV-003",
"span_no": "SP-006",
"trace_no": "TR-02",
"kind_raw": "tool",
"budget_no": "BG-04",
"span_code": "latency_over",
"cost_usd": null,
"max_latency_ms": 5000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "latency_ms",
"agg": null,
"value": 9200
},
{
"field": "max_latency_ms",
"agg": null,
"value": 5000
}
],
"summary": "재고 조회가 9.2초 걸려 도구 기준 5초를 넘겼습니다.",
"hedge": null
},
{
"verdict_no": "SV-004",
"span_no": "SP-008",
"trace_no": "TR-02",
"kind_raw": "tool",
"budget_no": "BG-04",
"span_code": "repeat_over",
"cost_usd": null,
"max_latency_ms": 5000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "step_name",
"agg": "count",
"value": 3
},
{
"field": "max_repeat",
"agg": null,
"value": 2
}
],
"summary": "한 실행에서 같은 조회를 세 번 불러 되돌이가 생겼습니다.",
"hedge": null
},
{
"verdict_no": "SV-005",
"span_no": "SP-009",
"trace_no": "TR-02",
"kind_raw": "llm",
"budget_no": "BG-02",
"span_code": "usage_missing",
"cost_usd": null,
"max_latency_ms": 8000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "output_tokens",
"agg": null,
"value": null
},
{
"field": "input_tokens",
"agg": null,
"value": 3100
}
],
"summary": "출력 토큰이 안 기록돼 이 단계 비용을 셀 수 없습니다.",
"hedge": null
},
{
"verdict_no": "SV-006",
"span_no": "SP-011",
"trace_no": "TR-03",
"kind_raw": "tool",
"budget_no": "BG-04",
"span_code": "step_failed",
"cost_usd": null,
"max_latency_ms": 5000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "status_raw",
"agg": null,
"value": "ERROR"
},
{
"field": "latency_ms",
"agg": null,
"value": 400
}
],
"summary": "파일 읽기가 실패했는데 뒤 단계가 그대로 이어졌습니다.",
"hedge": "실패해도 답이 나온 실행이라 어떤 근거로 요약했는지 사람이 봅니다."
},
{
"verdict_no": "SV-007",
"span_no": "SP-012",
"trace_no": "TR-03",
"kind_raw": "llm",
"budget_no": null,
"span_code": "model_unpriced",
"cost_usd": null,
"max_latency_ms": null,
"owner_name": null,
"evidence": [
{
"field": "model_raw",
"agg": null,
"value": "opus"
},
{
"field": "budget_no",
"agg": "count",
"value": 12
}
],
"summary": "예산표 열두 행에 없는 모델이라 단가를 정해 두지 않았습니다.",
"hedge": null
},
{
"verdict_no": "SV-008",
"span_no": "SP-005",
"trace_no": "TR-02",
"kind_raw": "agent",
"budget_no": "BG-05",
"span_code": "latency_over",
"cost_usd": null,
"max_latency_ms": 30000,
"owner_name": "AX플랫폼",
"evidence": [
{
"field": "latency_ms",
"agg": null,
"value": 41000
},
{
"field": "max_latency_ms",
"agg": null,
"value": 30000
}
],
"summary": "실행 전체가 41초 걸려 한 번에 끝낼 기준 30초를 넘겼습니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. span_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. verdict_no·span_no·trace_no·kind_raw·span_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. span_no 가 실행 기록에 실재하는 번호인지 대조합니다. 4. budget_no 가 있으면 예산표에 실재하는 행 번호인지 대조합니다. 5. budget_no 가 있으면 max_latency_ms·owner_name 이 그 예산 행의 값과 같은지 대조합니다. 6. kind_raw·model_raw 를 대소문자를 접어 예산표와 맞추고, 맞는 행이 없으면 span_code 가 model_unpriced 인지 확인합니다. 7. cost_usd 를 input_tokens 와 output_tokens 에 각각 천 토큰 단가를 곱해 더한 값으로 다시 계산합니다. 8. span_code 가 within_budget 이면 latency_ms 가 max_latency_ms 이하이고 status_raw 가 성공인지 다시 확인합니다. 9. span_code 가 latency_over 이면 latency_ms 가 max_latency_ms 를 넘는지 다시 계산합니다. 10. span_code 가 repeat_over 이면 같은 trace_no 안에서 같은 step_name 이 max_repeat 보다 많이 나오는지 다시 셉니다. 11. status_raw 가 실패이면 span_code 가 step_failed 인지 확인합니다. 12. 단가가 선언된 종류인데 토큰 수가 비어 있으면 span_code 가 usage_missing 인지 확인합니다. 13. evidence 의 field 이름이 실행 기록과 예산표에 실재하는 컬럼인지 대조합니다. 14. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 행 값과 같은지 확인합니다. 15. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 지원자 계정으로 돌린 실행 200번의 단계에 손으로 붙인 정답 판정과 대조해 일치율과 사유 코드별 분포를 내고, 실행당 평균 비용과 단가 미정이 몰리는 모델을 목록으로 냅니다. [수동 검증 · 사람이 봅니다] 1. 새 모델의 단가와 허용 지연을 담당 조직이 정합니다. 2. 되돌이로 걸린 실행이 정말 낭비인지 단계 순서를 보고 사람이 판단합니다.