standarda-core
이 문서는 core의 코드 구현이 아니라 AI 하네싱 관점의 동작 원리를 다룹니다. core가 왜 존재하고, 무엇을 표준화하며, 무엇이 자동으로 굴러가는지가 주제입니다.
1. 단일 문과 여섯 능력
standarda-core는 LangChain 위에 얇게 얹은 의견 있는 표준 계층입니다.
핵심은 영리한 코드가 아니라, 모든 프로젝트가 같은 문으로 LLM·구글 API에 접근한다는 사실입니다. 그래서 프로젝트마다 제각각 다시 구현하다 어긋나는 일이 없어집니다.
core가 제공하는 능력은 여섯 가지입니다.
각 능력에서 무엇이 자동이고 무엇이 직접 켜야 하는지를 먼저 표로 못 박습니다. 이 구분이 core를 가장 헷갈리게 하는 지점입니다.
| 능력 | 자동(무설정) | 직접(opt-in) |
|---|---|---|
| LLM 접근 | 모델·온도·키 env 기본값, 키 없으면 None, 추론모델 온도 quirk 자동 처리 | 프로바이더(팩토리) 선택 |
| 관측(트레이싱) | 트레이싱 env가 있으면 LangChain이 자동 적용 | env 설정 + core 우회 금지 |
| 비용·지연(캐싱) | tool-loop 안에서는 캐싱 기본 ON | 그 밖에선 캐싱 헬퍼·비용 집계 직접 호출 |
| 문서 파싱 | (없음) | 파서 서브에이전트가 방식 결정 |
| 응답 처리 | (없음) | parse_json_from_llm 호출 |
| 구글 API | 토큰 갱신·재인증·스코프 검사 | credentials 준비 |
2. LLM 접근
core의 LLM 팩토리는 어느 프로바이더든 동일한 시그니처로 준비된 모델을 돌려줍니다.
get_anthropic_llm() · get_openai_llm() · get_google_llm() 은 각각 LangChain의 ChatAnthropic·ChatOpenAI·ChatGoogleGenerativeAI를 감쌉니다.
호출부는 팩토리만 바꾸면 프로바이더를 교체할 수 있고, 돌려받는 객체는 어느 쪽이든 .invoke() 되는 LangChain 모델입니다.
기본으로 녹아 있는 것:
- 환경변수 기본값 + 인자 우선: 모델·온도·키를 env에서 읽되, 명시한 인자가 항상 이깁니다.
- 키 없으면 예외가 아니라 None: 키가 비면 경고 로그만 남기고
None을 돌려줘, 호출부가 널 체크로 처리합니다. - 모델 quirk 자동 흡수: 온도 파라미터를 거부하는 추론 모델은 그 인자를 자동으로 빼고 호출합니다.
- 멀티모델 한 문으로:
get_openrouter_llm()은 키 하나로 여러 회사 모델을 조합하고, 실제 청구 비용까지 응답에 실어올 수 있습니다.
3. 관측
LangSmith 트레이싱은 core가 무언가를 설정해서가 아니라, core가 항상 LangChain 객체를 돌려주기 때문에 붙습니다.
core 안에는 트레이싱 설정 코드가 한 줄도 없습니다. 대신 (1) 팩토리가 늘 LangChain 모델을 돌려주고, (2) 프로젝트 환경에 트레이싱 env가 있으면, LangChain 런타임이 그 호출을 알아서 트레이스에 실어 보냅니다.
그래서 core를 우회하면 트레이스가 조용히 사라집니다.
직접 anthropic.Anthropic() 같은 SDK 클라이언트를 만들면 LangChain을 거치지 않으니 관측에서 빠지고, 온도 quirk·캐싱·비용 로직도 각자 다시 구현하게 됩니다.
"모든 LLM 호출은 core로"라는 규칙의 강제 이유가 바로 이 관측 일관성입니다.
4. 비용·지연
같은 프리픽스를 반복 호출할 때, core의 캐싱 헬퍼로 입력 토큰 재청구와 지연을 줄입니다.
cached_system_message·with_cache_control로 system 블록에 캐시 마커를 붙이면, 캐시 읽기는 입력가의 약 0.1배로 떨어집니다(쓰기는 약 1.25배).- 멀티 프로바이더 안전: 대상 모델이 Anthropic이 아니면 마커를 붙이지 않고 평문을 돌려줘, 다른 프로바이더에 잘못된 필드가 가지 않습니다.
- 비용 집계는 opt-in:
track_usage()로 감싸고record_usage()를 부른 뒤estimate_cost_usd()로 추정합니다. core가 자동으로 집계하지는 않습니다. - 예외적으로 tool-loop 안에서는 캐싱이 기본 ON 이라, 반복 turn마다 프리픽스를 자동으로 다시 캐시합니다.
5. 문서 파싱
core는 형식마다 저렴한 텍스트 경로와 정확한 비전 경로를 나눠 두지만, 둘 중 무엇을 쓸지는 사람이 눈대중으로 고르지 않습니다.
방식 선택은 파서 서브에이전트(pdf-parser · excel-parser)가 샘플로 품질을 테스트해 결정합니다. 로직 흐름은 이렇습니다.
- 샘플 추출: pdfplumber(PDF)·openpyxl(Excel)로 앞 몇 페이지·행을 실제로 뽑아 본다.
- 품질 평가: 숫자가 정확한가, 표 구조가 유지되나, 한글이 안 깨지나를 본다.
- 방식 결정: 품질이 좋으면 텍스트형(10배 저렴·빠름), 텍스트가 깨지거나 이미지·병합으로 구조가 안 잡히면 비전형. 원칙은 텍스트 우선, 비전 폴백이다.
Excel에는 한 갈래가 더 있습니다. 컬럼 인덱스가 고정된 정형 데이터는 LLM 없이 openpyxl로 바로 읽습니다(가장 저렴).
어느 경로든 LLM은 함수 안에서 만들지 않고 인자로 주입해, 호출부가 프로바이더를 고릅니다.
6. 응답 처리
LLM 응답에서 JSON을 꺼낼 때는 parse_json_from_llm 하나만 씁니다.
이 함수는 마크다운 코드펜스로 감싼 JSON, 산문 속에 박힌 {…}, 순수 JSON을 모두 처리합니다.
프로젝트마다 펜스를 문자열로 잘라내던 코드를 복붙하다 조금씩 어긋나던 것을, 하나의 정본으로 통일한 것입니다.
7. 구글 API
Gmail·Sheets·Drive·Docs 네 클라이언트가 하나의 인증 믹스인을 공유합니다.
- 스코프는
DEFAULT_SCOPES한 곳이 단일 소스입니다. - 토큰 갱신·재인증·스코프 검사는 자동입니다. credentials·token만 준비하면 됩니다.
- 사용자별 OAuth가 필요하면
Credentials객체를 주입할 수 있고, 이때 토큰은 메모리에서만 갱신되고 디스크에 쓰지 않습니다.
8. 동작 원리
여섯 능력을 관통하는 두 원칙이 core의 나머지를 설명합니다.
단일 출처(single source). JSON 추출도, 구글 스코프도, 캐싱 헬퍼도 하나뿐입니다. 프로젝트들이 예전엔 각자 복붙해 두고 어긋났는데, core가 정본을 쥐면 그 드리프트가 사라집니다.
고정 git 태그로 설치(editable 금지). core는 특정 태그(git+...@vX.Y.Z)로 설치합니다. editable 설치는 한 곳을 고치면 그 core를 공유하는 모든 프로젝트에 조용히 번져 금지입니다.
업데이트는 자동이 아닙니다. 새 태그가 나와도 각 프로젝트가 자기 requirements.txt의 태그를 직접 올려 재설치할 때까지 기존 버전에 머뭅니다. 그래서 하위호환이 하드 룰입니다.
마무리
- 핵심 정리: core는 여섯 능력(LLM 접근·관측·캐싱·파싱·응답·구글 API)을 한 문으로 표준화합니다. 트레이싱·인증·quirk는 자동, 캐싱·비용·파싱 방식은 직접(또는 서브에이전트가) 결정합니다.
- 주의 사항: core를 우회해 SDK를 직접 쓰면 트레이싱이 사라집니다. 파싱 방식은 눈대중이 아니라 파서 서브에이전트가 샘플 품질로 결정합니다.
- 다음 문서: 이 core 위에서 새 프로젝트가 물려받는 하네스와 자동화를 standarda-template · devtools에서 다룹니다.