링크알파
경력근거: evaluation rubrics grounded in capital markets expertise
링크알파의 이 포지션은 고객사 리서치 워크플로우를 최종 사용자 관점에서 훑어 가장 효과가 큰 개선 지점을 찾고, 자본시장 전문성에 기반한 프롬프트와 평가 루브릭을 저작하며, disclosures·financial statements를 오가는 풀사이클 리서치를 QA하는 일입니다. 고객사 애널리스트의 질문을 조건으로 바꿔 그 회사 커버리지 시트에 실행하고 값마다 원본 행과 공시 표기를 다는 이 도구는, 질문 로그를 입력으로 삼아 개선 지점을 드러내고 조건 변환 프롬프트와 채점 루브릭을 함께 내놓으므로 그 세 가지를 한 산출물로 보여줍니다. 링크알파가 지향하는 decision-grade 출력은 답보다 출처 귀속이 먼저라, 근거 없는 값을 아예 내지 않는 이 구조가 곧 제품 방향입니다. 다만 이 포트폴리오가 보여주는 것은 자연어를 검증 가능한 질의 조건으로 바꾸고 실행 결과에 원본 근거를 묶는 기술이며, 자본시장 리서치 도메인 전문성은 주장하지 않습니다.
최종 사용자 관점에서 제품 워크플로우를 훑어 가장 효과가 큰 개선 지점을 찾는 일이 이 포지션의 핵심입니다. 이 도구는 고객사 애널리스트가 실제로 던지는 질문 로그를 입력으로 삼아, 어떤 질문이 시트 한 장으로 즉시 답되고 어떤 질문이 막히는지를 실행 결과로 드러냅니다.
자본시장 전문성에 기반한 도메인 콘텐츠·프롬프트·평가 루브릭을 직접 만들고 다듬습니다. 질문을 질의 조건으로 바꾸는 프롬프트와 그 조건이 정답 조건에 맞았는지 채점하는 규칙이 곧 그 루브릭의 실행 형태입니다.
클라이언트 리서치·인터뷰·QA에서 정성·정량 피드백을 모아 팀이 바로 움직일 수 있는 인사이트로 바꾸는 일입니다. 이 도구는 실행 로그를 모아 어떤 질문 유형이 반복해서 조건으로 안 잡히는지 집계해 다음에 만들 기능의 순서를 정합니다.
고객사 데이터팀이 내려준 커버리지 재무 시트 한 장과 애널리스트가 한 줄로 적은 질문을 받아, 질문을 지표·기간·비교 연산·임계·종목 범위로 이뤄진 질의 조건으로 바꾸고 그 조건을 SQL로 옮겨 시트에 실행합니다. 결과 표에 각 값이 어느 행 어느 시트 어느 공시 표기에서 왔는지를 근거로 붙여 터미널에 냅니다. 조건 변환만 LLM이 맡고 실행·근거 추적·채점은 규칙이 맡아, 조건이 안 잡히면 실행하지 않고 무엇이 모자란지 출력합니다.
링크알파와 계약한 자산운용사에서 반도체·인터넷 섹터를 보는 리서치 애널리스트입니다. 분기 실적이 나오면 데이터팀이 내려준 커버리지 엑셀을 열어 열 이름을 눈으로 맞추고 소계 행을 걸러 가며 필터와 피벗을 다시 만들고, 숫자를 메모로 옮길 때마다 어느 행에서 온 값인지 되짚어 확인하는 사람입니다. 질문을 적은 그대로 답과 근거를 함께 받고 싶은 사람입니다.
질문을 자유 문장으로 두지 않고 지표·기간·비교 연산·임계·종목 범위라는 다섯 칸의 조건으로 강제하면, (a) 조건이 채워졌는지로 실행 가능 여부가 규칙으로 갈리고, (b) 실행은 SQL이라 결과 행마다 원본 행 위치와 공시 표기를 그대로 달 수 있으며, (c) 조건이 정답 조건과 맞았는지를 사람 판단 없이 채점합니다.
커버리지 시트 한 장과 질문 한 줄을 인자로 받아 결과 표·실행 SQL·근거 행을 터미널에 출력하는 동작하는 CLI입니다. 시트와 질문 로그는 고객사 추출본을 가정한 더미로 시작합니다.
질문 60건을 정답 조건과 비교해 조건 일치율과 결과 행 집합 일치율을 내고, 조건 신뢰도가 낮으면 실행을 막는 가드를 붙인 상태입니다.
열 이름과 단위가 다른 두 번째 시트에 별칭 사전만 갈아끼워 같은 질문 세트를 돌리고, 어떤 질문 유형이 반복해서 조건으로 안 잡히는지 집계한 리포트 파일입니다.
커버리지 시트 한 장과 자연어 질문 15개를 넣으면, 각 질문의 조건·결과 표·근거 행이 몇 초 안에 터미널에 나오는 걸 시연합니다
일부러 만든 애매·결측 질문 30종(지표 표기 흔들림·기간 없음·미등록 지표·시트로 답할 수 없는 업황 질문)을 넣어도 출력 형식(스키마)이 한 번도 안 깨지고, 조건이 안 잡히면 실행 없이 사유가 나옵니다
2주차에 질문 60건을 정답 조건과 비교하면 조건 일치가 51건(약 85%), 결과 행 집합 일치가 54건(약 90%)입니다. 단, 질문과 정답을 모두 작성자가 만든 합성 기준이라 '성능 검증'이 아니라 '데모 조건에서의 동작'임을 명시합니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 정확도는 링크알파 고객사의 실 커버리지 시트와 실제 애널리스트 질문 로그가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
이 포트폴리오가 보여주는 것은 자연어를 검증 가능한 질의 조건으로 바꾸고 실행 결과에 원본 근거를 묶는 기술이며, 자본시장 리서치 도메인 전문성은 주장하지 않습니다. 밸류에이션 판단이나 섹터 뷰처럼 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다
정답 조건을 작성자가 만들었으니 잘 맞는 게 당연합니다. 지표 표기 흔들림·기간 미지정·미등록 지표·소계 행 혼입 같은 경계 케이스를 질문과 시트에 일부러 섞어, 쉬운 질문만 통과시키는 게 아님을 보여줍니다
조건이 반쯤 잡히는 질문에서 억지로 실행하면 그럴듯하게 틀린 표가 나옵니다. 신뢰도가 낮으면 실행을 막고 무엇이 모자란지만 출력하도록 기본값을 잡았고, 이 보수적 기본값 때문에 실행 거부가 늘어나는 것은 감수합니다
실제 도입에서는 시트가 매 분기 열 이름과 단위를 바꾸고 소계 규칙도 회사마다 다릅니다. 별칭 사전과 정리 규칙을 고객사별로 다시 맞춰야 하므로 도입 단계 과제로 분리합니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 데이터팀이 내려준 커버리지 재무 시트 추출본입니다. 질의가 실행되는 대상 표이자 근거의 원본이며, 열 이름 별칭·소계 행·단위 혼재·결측·미등록 지표를 일부러 섞어 정리 규칙이 실제로 걸리는지 봅니다.
| row_id | sheet_name | ticker | company_name | metric_label | metric_id | period | value | unit | source_note | row_kind |
|---|---|---|---|---|---|---|---|---|---|---|
| cs_001 | KR_Semis_2025Q4 | 005930 | 삼성전자 | 영업이익률(%) | op_margin | 2025Q4 | 15.5 | pct | 사업보고서 2025.12 | data |
| cs_002 | KR_Semis_2025Q4 | 005930 | 삼성전자 | 매출성장률 YoY | rev_yoy | 2025Q4 | 12 | pct | 사업보고서 2025.12 | data |
| cs_003 | KR_Semis_2025Q3 | 005930 | 삼성전자 | 영업이익률(%) | op_margin | 2025Q3 | 17.2 | pct | 분기보고서 2025.09 | data |
| cs_004 | KR_Semis_2025Q4 | 000660 | SK하이닉스 | OPM | op_margin | 2025Q4 | 41.2 | pct | 분기보고서 2025.12 | data |
| cs_005 | KR_Semis_2025Q3 | 000660 | SK하이닉스 | OPM | op_margin | 2025Q3 | 38.4 | pct | 분기보고서 2025.09 | data |
아래 JSON Schema에 맞는 고객사 커버리지 시트 추출본 80행을 JSON 배열로 생성합니다. 종목 6~8개(반도체·인터넷 섹터), 지표는 표준 키 rev_yoy·op_margin·op_income·per, 기간은 2025Q3·2025Q4·2026Q1을 씁니다. 같은 종목의 인접 분기 행을 함께 두어 전분기 대비 비교가 가능하게 합니다.
{"type":"object","properties":{"row_id":{"type":"string"},"sheet_name":{"type":"string"},"ticker":{"type":"string"},"company_name":{"type":"string"},"metric_label":{"type":"string"},"metric_id":{"type":["string","null"]},"period":{"type":"string"},"value":{"type":["number","null"]},"unit":{"type":"string","enum":["pct","krw_bn","x","none"]},"source_note":{"type":["string","null"]},"row_kind":{"type":"string","enum":["data","subtotal"]}},"required":["row_id","sheet_name","ticker","company_name","metric_label","period","unit","row_kind"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(정리 규칙이 실제로 걸리게): (1) 같은 지표의 열 이름 별칭('영업이익률(%)'/'영업이익률'/'OPM'/'opm(%)') 6건 이상, (2) 섹터 합계 소계 행(row_kind=subtotal, ticker='-') 4건 이상. 이 행은 질의 결과에 절대 들어가면 안 됩니다. (3) value가 null인 미공시 행 3건, source_note가 null인 행 3건, (4) 경계값 0.0% 성장·음수 영업이익 각 2건, (5) 별칭 사전에 없는 지표(metric_id=null) 2건, (6) 같은 지표를 pct와 krw_bn으로 섞어 적은 단위 혼재 2건. JSON 배열만.{"type":"object","properties":{"row_id":{"type":"string"},"sheet_name":{"type":"string"},"ticker":{"type":"string"},"company_name":{"type":"string"},"metric_label":{"type":"string"},"metric_id":{"type":["string","null"]},"period":{"type":"string"},"value":{"type":["number","null"]},"unit":{"type":"string","enum":["pct","krw_bn","x","none"]},"source_note":{"type":["string","null"]},"row_kind":{"type":"string","enum":["data","subtotal"]}},"required":["row_id","sheet_name","ticker","company_name","metric_label","period","unit","row_kind"]}고객사 애널리스트가 실제로 던진다고 가정한 질문 로그입니다. 각 행에 정답 판정과 정답 질의 조건을 심어 두어, 변환 결과를 사람 판단 없이 규칙으로 채점합니다. 한국어와 영어를 섞고 조건이 잡히지 않는 질문도 함께 둡니다.
| question_id | asked_by | asked_at | text | lang | gold_verdict | gold_query |
|---|---|---|---|---|---|---|
| q_001 | 반도체 애널리스트 | 2026-02-03T09:12:00+09:00 | 커버리지 중에 2025Q4 영업이익률이 전분기보다 떨어진 종목만 뽑아줘 | ko | runnable | {"metric_id":"op_margin","period":"2025Q4","baseline_period":"2025Q3","op":"drop","threshold":null,"n":null,"universe":null} |
| q_002 | 반도체 애널리스트 | 2026-02-03T09:31:00+09:00 | Which names had revenue growth above 2… | en | runnable | {"metric_id":"rev_yoy","period":"2025Q4","baseline_period":null,"op":"gt","threshold":20,"n":null,"universe":null} |
| q_003 | 인터넷 애널리스트 | 2026-02-03T10:02:00+09:00 | 2025Q4 영업이익률 상위 3개 알려줘 | ko | runnable | {"metric_id":"op_margin","period":"2025Q4","baseline_period":null,"op":"top_n","threshold":null,"n":3,"universe":null} |
| q_004 | 인터넷 애널리스트 | 2026-02-03T10:20:00+09:00 | 이익률 좋은 애들 좀 정리해줘 | ko | ambiguous_metric | — |
| q_005 | 리서치 어시스턴트 | 2026-02-03T11:05:00+09:00 | 삼성전자 PER 얼마야 | ko | missing_period | — |
아래 JSON Schema에 맞는 애널리스트 질문 로그 60행을 JSON 배열로 생성합니다. coverage_sheet의 종목·지표·기간과 같은 키 공간을 쓰고, 한국어와 영어를 6대 4로 섞습니다. gold_verdict는 다음 규칙으로 정합니다. 지표와 기간이 모두 잡히고 별칭 사전에 있으면 runnable, 지표어가 여러 표준 지표에 걸리면 ambiguous_metric, 기간이 문장에 없으면 missing_period, 지표가 사전에 없으면 unmapped_metric, 시트 값으로 답할 수 없는 전망·업황 질문이면 out_of_scope. runnable일 때만 gold_query를 채우고 나머지는 null로 둡니다.
{"type":"object","properties":{"question_id":{"type":"string"},"asked_by":{"type":"string"},"asked_at":{"type":"string"},"text":{"type":"string"},"lang":{"type":"string","enum":["ko","en"]},"gold_verdict":{"type":"string","enum":["runnable","ambiguous_metric","missing_period","unmapped_metric","out_of_scope"]},"gold_query":{"type":["object","null"],"properties":{"metric_id":{"type":["string","null"]},"period":{"type":["string","null"]},"baseline_period":{"type":["string","null"]},"op":{"type":["string","null"]},"threshold":{"type":["number","null"]},"n":{"type":["integer","null"]},"universe":{"type":["array","null"],"items":{"type":"string"}}}}},"required":["question_id","text","lang","gold_verdict"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라: (1) 기간을 4Q25·FY25·작년 4분기처럼 다르게 적은 표기 6건 이상, (2) '이익률'처럼 여러 지표에 걸리는 모호 질문 5건, (3) 기간이 아예 없는 질문 5건, (4) 사전에 없는 지표를 묻는 질문 3건, (5) 전망·업황을 묻는 범위 밖 질문 4건, (6) 한 문장에 조건을 두 개 건 질문(기간 비교와 임계 동시) 4건. JSON 배열만.{"type":"object","properties":{"question_id":{"type":"string"},"asked_by":{"type":"string"},"asked_at":{"type":"string"},"text":{"type":"string"},"lang":{"type":"string","enum":["ko","en"]},"gold_verdict":{"type":"string","enum":["runnable","ambiguous_metric","missing_period","unmapped_metric","out_of_scope"]},"gold_query":{"type":["object","null"],"properties":{"metric_id":{"type":["string","null"]},"period":{"type":["string","null"]},"baseline_period":{"type":["string","null"]},"op":{"type":["string","null"]},"threshold":{"type":["number","null"]},"n":{"type":["integer","null"]},"universe":{"type":["array","null"],"items":{"type":"string"}}}}},"required":["question_id","text","lang","gold_verdict"]}| Excel | 고객사 데이터팀이 내려준 커버리지 시트를 openpyxl로 읽어 소계 행·병합 머리글·단위 혼재를 걷어내고 질의 가능한 표로 만드는 입력 경로입니다 |
| Python | 별칭 사전 정규화·조건 스키마 검증·근거 추적·정답 조건 채점을 구현하는 언어입니다 |
| SQL | 확정된 조건을 파라미터 질의로 조립해 DuckDB에 적재한 시트에 실행하는 실행 계층입니다 |
| LLM | 자연어 질문을 다섯 칸 조건 JSON으로 옮기는 변환에만 쓰고 값 계산에는 쓰지 않습니다 |
| Claude | 조건 변환 프롬프트와 모호 질문 판정에 쓰는 모델입니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(조건과 결과 채점)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(다른 시트 적용과 실패 질문 리포트)로 진행합니다.
질문 1건의 실행 기록입니다. 판정·조건·실행 SQL·결과 행·근거를 한 스키마에 담아 터미널 출력과 --json 저장에 그대로 씁니다. 실행하지 않은 질문도 같은 스키마로 사유를 남깁니다.
{"type":"object","properties":{"question_id":{"type":"string"},"verdict":{"type":"string","enum":["runnable","ambiguous_metric","missing_period","unmapped_metric","out_of_scope"]},"parsed_query":{"type":["object","null"],"properties":{"metric_id":{"type":["string","null"]},"period":{"type":["string","null"]},"baseline_period":{"type":["string","null"]},"op":{"type":["string","null"]},"threshold":{"type":["number","null"]},"n":{"type":["integer","null"]},"universe":{"type":["array","null"],"items":{"type":"string"}}}},"sql":{"type":["string","null"]},"rows":{"type":"array","items":{"type":"object","properties":{"ticker":{"type":"string"},"company_name":{"type":"string"},"value":{"type":["number","null"]},"unit":{"type":"string"},"delta":{"type":["number","null"]}},"required":["ticker","company_name","value","unit"]}},"evidence":{"type":"array","items":{"type":"object","properties":{"row_id":{"type":"string"},"sheet_name":{"type":"string"},"source_note":{"type":["string","null"]}},"required":["row_id","sheet_name"]}},"note":{"type":"string","minLength":20},"hedge":{"type":["string","null"]}},"required":["question_id","verdict","parsed_query","rows","evidence","note"]}[
{
"input_ref": "q_001 (2025Q4 영업이익률이 전분기보다 떨어진 종목)",
"output": {
"question_id": "q_001",
"verdict": "runnable",
"parsed_query": {
"metric_id": "op_margin",
"period": "2025Q4",
"baseline_period": "2025Q3",
"op": "drop",
"threshold": null,
"n": null,
"universe": null
},
"sql": "SELECT c.ticker, c.company_name, c.value, c.unit, c.value - b.value AS delta FROM sheet c JOIN sheet b USING (ticker, metric_id) WHERE c.metric_id = 'op_margin' AND c.period = '2025Q4' AND b.period = '2025Q3' AND c.row_kind = 'data' AND b.row_kind = 'data' AND c.value IS NOT NULL AND b.value IS NOT NULL AND c.value < b.value ORDER BY delta ASC",
"rows": [
{
"ticker": "005930",
"company_name": "삼성전자",
"value": 15.5,
"unit": "pct",
"delta": -1.7,
"evidence": [
{
"field": "value",
"value": 15.5
},
{
"field": "value",
"agg": "prev_period",
"value": 17.2
}
]
},
{
"ticker": "035420",
"company_name": "NAVER",
"value": 16,
"unit": "pct",
"delta": -0.4,
"evidence": [
{
"field": "value",
"value": 16
},
{
"field": "value",
"agg": "prev_period",
"value": 16.4
}
]
}
],
"evidence": [
{
"row_id": "cs_001",
"sheet_name": "KR_Semis_2025Q4",
"source_note": "사업보고서 2025.12"
},
{
"row_id": "cs_003",
"sheet_name": "KR_Semis_2025Q3",
"source_note": "분기보고서 2025.09"
},
{
"row_id": "cs_007",
"sheet_name": "KR_Internet_2025Q4",
"source_note": "실적발표 2025.12"
},
{
"row_id": "cs_008",
"sheet_name": "KR_Internet_2025Q3",
"source_note": "실적발표 2025.09"
}
],
"note": "영업이익률을 2025Q4와 2025Q3 두 기간으로 조인해 하락한 종목만 남겼습니다. 소계 행(cs_012)과 값이 빈 행은 제외했습니다.",
"hedge": null
}
},
{
"input_ref": "q_002 (4Q25 매출성장률 20% 초과)",
"output": {
"question_id": "q_002",
"verdict": "runnable",
"parsed_query": {
"metric_id": "rev_yoy",
"period": "2025Q4",
"baseline_period": null,
"op": "gt",
"threshold": 20,
"n": null,
"universe": null
},
"sql": "SELECT ticker, company_name, value, unit FROM sheet WHERE metric_id = 'rev_yoy' AND period = '2025Q4' AND row_kind = 'data' AND value > 20 ORDER BY value DESC",
"rows": [
{
"ticker": "000660",
"company_name": "SK하이닉스",
"value": 34,
"unit": "pct",
"delta": null
}
],
"evidence": [
{
"row_id": "cs_006",
"sheet_name": "KR_Semis_2025Q4",
"source_note": "분기보고서 2025.12"
}
],
"note": "4Q25를 2025Q4로 정규화하고 매출성장률 열 별칭을 rev_yoy로 묶어 20% 초과 행만 남겼습니다.",
"hedge": null
}
},
{
"input_ref": "q_004 (이익률 좋은 애들 정리해줘)",
"output": {
"question_id": "q_004",
"verdict": "ambiguous_metric",
"parsed_query": {
"metric_id": null,
"period": null,
"baseline_period": null,
"op": null,
"threshold": null,
"n": null,
"universe": null
},
"sql": null,
"rows": [],
"evidence": [],
"note": "'이익률'이 영업이익률과 순이익률 두 표준 지표에 걸리고 기간도 문장에 없어 실행하지 않았습니다. 어느 지표의 어느 분기인지 정해 주면 바로 실행합니다.",
"hedge": "조건이 반만 잡혀 추측 실행은 하지 않습니다"
}
},
{
"input_ref": "q_007 (혁신지수 상위 5개)",
"output": {
"question_id": "q_007",
"verdict": "unmapped_metric",
"parsed_query": {
"metric_id": null,
"period": "2025Q4",
"baseline_period": null,
"op": "top_n",
"threshold": null,
"n": 5,
"universe": null
},
"sql": null,
"rows": [],
"evidence": [
{
"row_id": "cs_013",
"sheet_name": "KR_Internet_2025Q4",
"source_note": "내부 추정"
}
],
"note": "'혁신지수'는 별칭 사전에 없는 지표라 표준 키로 잇지 못했습니다. 시트에 같은 이름 행이 있으나 출처가 내부 추정이라 공시 근거가 없어 실행하지 않고 사전 등록을 요청합니다.",
"hedge": "사전 미등록 지표라 값을 답으로 내지 않습니다"
}
}
][자동 검증] 1. JSON Schema parse로 출력 형식(필드·enum·배열 구조)이 스키마를 통과하는지 확인합니다. 2. verdict를 심은 gold_verdict와 비교하고, runnable이면 parsed_query 다섯 칸이 gold_query와 값까지 같은지 봅니다. 3. 실행 결과 행 집합(ticker 집합)을 정답 조건을 직접 SQL로 돌린 결과와 비교해 일치율을 계산합니다. 4. rows가 비어 있지 않은데 evidence가 비면 차단하고, row_kind가 subtotal인 행이 rows에 들어가면 실패로 셉니다. 5. verdict가 runnable이 아닌데 sql이 채워져 있으면 차단합니다. [수동 검증] 1. 애널리스트 2명이 비동기로 모호·기간 누락 판정을 라벨링해 규칙 판정과의 일치도를 봅니다. 2. 전체의 5%를 사람이 직접 원본 시트에서 행을 찾아 근거가 맞는지 점검합니다.
근거: evaluation rubrics grounded in capital markets expertise