상위 사상: philosophy.md · 자가개선 루프: coreloop.md
주체: dartlab 공개 API 계약 (Company · 모든 엔진 · property). 현재: Dual Access (call form + attr form) 확립 · 무인자 호출 = 가이드 DataFrame · camelCase + snake_case shim · 영문·한글 alias. 방향: guide DataFrame 표준 확산 · override 키 자동 노출 · contract 위반 CI 게이트.
dartlab 의 모든 공개 API 가 따르는 contract 와 사상. 새 함수·엔진·property 를 추가하기 전에 이 문서를 먼저 읽고 contract 위반이 없는지 확인한다.
이 문서가 source of truth — 다른 ops 문서나 코드와 충돌하면 이 문서가 기준이다. 각 섹션은 "이렇게 한다" 명제로 열고, 반복된 실수는 섹션 하단 "반복 실패" 에 정리한다.
원칙: 모든 사용자 진입점은 같은 함수 하나지만 두 가지 access form 을 모두 허용한다 (pandas 가 df["col"] 와 df.col 둘 다 허용하는 것과 같다).
# call form (canonical)
c.show("IS")
c.show("IS", freq="Y")
c.analysis("financial", "수익성")
c.credit("등급")
c.story("수익성")
c.scan("governance")
c.quant("rsi", "005930")
# attr form (sugar)
c.show.IS() # → c.show("IS")
c.show.IS(freq="Y") # → c.show("IS", freq="Y")
c.analysis.financial("수익성") # → c.analysis("financial", "수익성")
c.credit.등급() # → c.credit("등급")
c.story.수익성() # → c.story("수익성")
c.scan.governance() # → c.scan("governance")
c.quant.rsi("005930") # → c.quant("rsi", "005930")두 form 의 동작은 완전히 동일. 같은 함수를 다른 access syntax 로 부르는 것일 뿐.
core/dualAccess.py::CallableAccessor SSOT 헬퍼 — __call__ + __getattr__ 양쪽 지원. 모든 진입점이 이 클래스를 통해 wrap.
c.show("IS") # 분기 연결 (기본)
c.show("IS", freq="Y") # 연간 연결
c.show("IS", freq="YTD") # YTD 누적 연결
c.show("IS", scope="separate") # 분기 별도
c.show("IS", freq="Y", scope="separate") # 연간 별도
c.show("IS", period="2023") # 2023 필터
c.show("ratios") # 비율
c.show("inventory") # 재고자산 주석
c.show("dividend") # 배당
c.select("IS", ["매출액"], freq="Y") # 행 필터 + 연간calc 모듈 (analysis · forecast · valuation · credit · story · excel) 이 (series, periods) 튜플 형태가 필요한 경우 private 메서드 c._buildFinanceSeries(freq=, scope=) 를 호출한다. 사용자 진입점이 아니다.
# property/메서드 잔존 0
grep -rn "@property\s*$" src/dartlab/providers/dart/company.py | wc -l # finance topic property 없어야
grep -rn "def timeseries\|def annual\|def cumulative" src/dartlab/ # 0
grep -rn "c\.IS\b\|c\.BS\b\|c\.CF\b\|c\.CIS\b" src/dartlab tests # 0 (사용자 코드)
grep -rn "c\.timeseries\|c\.annual\|c\.cumulative" src/dartlab tests # 0반복 실패 — 같은 데이터에 다른 이름의 진입점을 추가하면 contract 가 무너진다. 아래 패턴은 쓰지 않는다:
c.IS # ✗ 별도 property — c.show("IS") 로 통합
c.BS / c.CF / c.CIS # ✗ 동일 — c.show(...)
c.timeseries() / c.timeseries # ✗ 별도 method
c.annual / c.cumulative # ✗ 별도 property
c.ratios / c.ratioSeries # ✗ 별도 property — c.show("ratios")
c.SCE / c.sceMatrix # ✗ 별도 property — c.show("SCE")
c.notes.inventory # ✗ accessor namespace — c.show("inventory")
c.IS_annual # ✗ 새 property
c.financeAnnual.IS # ✗ 새 namespace
c.show("IS").annual() # ✗ DataFrame 메서드 추가원칙: 모든 finance topic 의 default = 분기 standalone + 연결재무 (CFS). 연간·누적·별도는 freq · scope 파라미터로 토글.
| 모드 | 호출 | schema · 의미 |
|---|---|---|
| 분기 연결 (기본) | c.show("IS") |
2025Q4 .. 2024Q1 (CFS) |
| 연간 연결 | c.show("IS", freq="Y") |
2025, 2024, ... (4 분기 strict 합) |
| YTD 누적 | c.show("IS", freq="YTD") |
2025Q4(YTD), 2025Q3(YTD), ... |
| 분기 별도 | c.show("IS", scope="separate") |
OFS (모회사) |
| 연간 별도 | c.show("IS", freq="Y", scope="separate") |
둘 다 토글 |
| 파라미터 | 표준값 | 의미 |
|---|---|---|
freq |
"Q" (default) · "Y" · "YTD" |
pandas 주기 코드 |
scope |
"consolidated" (default) · "separate" |
회계 범위 |
- IS · CIS · CF (flow) — 4 분기 모두 있을 때만 단순 합 (3 분기 이하 → None).
- BS (stock) — Q4 (= 연말잔액). 없으면 그 해 가장 최근 분기.
분기 → 연간 합성 로직은 core/finance/flow.py::synthesizeAnnualFromQuarters 한 함수에만 존재. toDict · toDictBySnakeId · _financeToDataFrame 모두가 위임.
같은 의미의 옵션은 모든 함수에서 같은 이름을 쓴다. 신규 코드는 이 이름만 허용.
| 표준 | 타입·값 | 의미 | 쓰지 않는 변형 |
|---|---|---|---|
freq |
"Q" (default) · "Y" · "YTD" |
시계열 주기 | annual, cumulative, period_type, interval |
scope |
"consolidated" (default) · "separate" |
회계 범위 | fsDiv, fsDivPref, consolidated_only |
basePeriod |
str | None |
분석 기준 시점 ("2024Q4") | as_of, asOf, cutoff, referenceDate |
period |
str | list[str] | None |
데이터 필터 기간 | quarter, year, time, dt |
stockCode |
str |
종목코드 (6 digit DART · ticker EDGAR) | code, corp, ticker, symbol |
topic |
str |
show·select topic 키 | category, block, kind |
axis |
str |
엔진 축 이름 (analysis · scan · quant · …) | metric, feature, name |
target |
str | None |
엔진 호출 대상 (옵션) | subject, entity |
indList |
str | list[str] |
select 행 필터 | rows, accounts, items |
colList |
str | list[str] |
select 열 필터 | cols, columns |
maxYears |
int |
시계열 최대 연도 | years_back, nYears, lookback |
maxQuarters |
int |
시계열 최대 분기 | quarters_back, nQuarters |
withFallback |
bool |
fallback 활성 | useFallback, enable_fallback |
dryRun |
bool |
미리보기 | simulate, preview |
detail |
bool |
상세 출력 (credit 등) | verbose, expand |
같은 함수에 의미 충돌하는 bool 2 개 이상은 쓰지 않는다. enum · Literal 을 쓴다.
# ✗ 쓰지 않음
def f(*, annual: bool = False, cumulative: bool = False)
# ✓ 사용
def f(*, freq: Literal["Q", "Y", "YTD"] = "Q")엔진 호출 시 한국어·영문 둘 다 받는 alias 는 허용된다 (양방향). 파라미터 이름 자체는 항상 영문 camelCase.
모든 엔진은 같은 패턴으로 호출. 축을 루트 함수로 직접 노출하지 않는다.
| 엔진 | 진입점 | 예시 |
|---|---|---|
| analysis | c.analysis() |
c.analysis("financial", "수익성") 또는 c.analysis("수익성") |
| credit | c.credit() |
c.credit("등급") |
| quant | c.quant() · dartlab.quant() |
c.quant("rsi") |
| macro | dartlab.macro() |
dartlab.macro("사이클") |
| scan | dartlab.scan() |
dartlab.scan("financial", "수익성") |
| story | c.story() |
c.story("수익성") |
| gather | dartlab.gather() |
dartlab.gather("price", "005930") |
| ai | dartlab.ask() |
dartlab.ask("...") |
| listing | dartlab.listing() |
dartlab.listing("filings", corp="005930") |
| search | dartlab.search() |
dartlab.search("유상증자") |
반복 실패 — c.profitability() · c.kr_profitability() · c.financialAnalysis.profitability() 같이 축을 루트로 노출하거나 namespace 중첩. 항상 c.analysis("수익성") 형태.
이 원칙은 Company-bound API 의 설계 원칙이며, dartlab 전체 사상이 아니다.
- 공개 함수는 종목코드 (str) 또는 Company 만 받는다.
- 추가 import 금지 —
import dartlab하나로 모든 기능 접근. - 2-Tier: 루트 함수 (
dartlab.X("005930")) + Company 메서드 (c.X()) 양쪽.
- 첫 호출 5 초 이내.
- lazy load 기본 — finance · report · docs 는 접근 시점에 로드.
- BoundedCache 로 같은 세션 내 재사용.
- 숫자는 원본 그대로 (DART · EDGAR 원본 보존).
- 없으면 None — 추정값 만들지 않는다.
- 출처 추적 가능:
c.trace(topic). - 에러는 명시적 — 파싱 실패·매핑 누락 숨기지 않음.
- 결손은 None (0 으로 채우지 않음).
- 진짜 0 과 결손을 구분.
- 분모 가드 이외에
data.get("매출액") or 0같은 결과 dict 의or 0을 쓰지 않는다 (분모 가드는 예외,# noqa: zero-guard표시).
tests/test_ast_calc_patterns.py::test_no_or_zero_in_return_dict 가 calc 함수의 return dict 에서 or 0 사용을 AST 로 감지해 차단.
반복 실패 — 결손을 0 으로 채우면 "매출 0 인 회사" 와 "매출 데이터 없음" 을 구분 못 한다. None 을 써야 후단 계산이 None-propagation 으로 건전하게 전파.
dartlab 은 6 레이어 구조. L2 엔진 4 개 (analysis · quant · credit · macro) 는 동등하고 상호 독립.
L0 core/ ← 순수 유틸 + 공통 타입
L1 providers/ gather/ ← 데이터 수집
L1.5 scan/ ← 전종목 사전 빌드 (parquet)
L2 analysis/ quant/ credit/ macro/ industry/ ← 5 분석 엔진 (동등, 상호 import 없음)
L3 story/ ← 이야기꾼 (보고서 조립)
L4 ai/ + 사람 ← 소비자 (해석과 판단)
교차 관심사: guide/
- L2 엔진 간 상호 import 없음 (
analysis ↛ quant·macro ↛ credit등). - L2 → L1.5 (scan) 하향 참조 허용 (scan 은 순수 데이터 빌더).
- L3 (story) 만 L2 를 import — 모든 엔진 결과를 소비해 보고서 조립.
- L4 (ai) → L3·L2 import 허용 — AI 는 story + 엔진 직접 호출 가능.
- L2 엔진은 L3 (story) 를 import 하지 않음 — 엔진은 dict·숫자만 반환, 서사 생성 없음.
- 공유 데이터는 L0·L1 (
core/,providers/,gather/) 에서 직접 가져온다. - 공유 헬퍼는 한 위치에서 import — SSOT 단일 함수 (예:
core/finance/helpers.py::toDictBySnakeId). - 해석의 조합은 story (L3) 또는 AI (L4) 의 몫.
L0 ← L1 ← L1.5 ← L2 ← L3 ← L4
CI sentinel tests/test_imports.py 가 강제.
각 엔진은 dict · 숫자 · DataFrame 만 반환한다. 다음은 엔진에서 쓰지 않는다:
- 해석 문장 (narrative string).
- Block 객체 (
HeadingBlock·TextBlock등 — story.blocks). - Section · Story 객체 (story 전용).
- 렌더링 로직 (story · renderer 에서만).
핵심 SSOT 헬퍼:
| 헬퍼 | 위치 | 용도 |
|---|---|---|
synthesizeAnnualFromQuarters |
core/finance/flow.py |
분기→연간 합성 |
mergeAliasRows |
core/finance/labels.py |
SNAKEID_ALIASES 머지 |
SNAKEID_ALIASES |
core/finance/labels.py |
DART↔EDGAR snakeId alias |
get_korean_labels |
core/finance/labels.py |
snakeId → 한국어 라벨 |
toDictBySnakeId |
analysis/financial/_helpers.py |
SelectResult → dict (단일) |
annualColsFromPeriods |
analysis/financial/_helpers.py |
기간 컬럼 추출 (단일) |
sumBorrowingsKorean |
analysis/financial/_helpers.py |
차입금 합산 fallback |
annualSumFlow |
core/finance/flow.py |
credit 모드 부분 합산 (legacy) |
규칙 변경 시 SSOT 한 곳만 수정. 호출자는 위임.
DartCompany에 public 메서드를 추가하면EdgarCompany에도 동일 메서드 추가.- DART 전용은
tests/test_protocol.py::_DART_ONLY_EXEMPT에 사유 주석과 함께 등록. - analysis 축 · story 블록 · CLI 명령 추가 시 EDGAR Company 에서도 실행하여 crash 없음 확인.
- 통화 분기는
company.currency참조 (하드코딩 쓰지 않음). - 동기화 검증:
bash scripts/dev/test-lock.sh tests/ -k "test_edgar_has_all_dart_public_methods" -v.
except Exception:대신 구체적 예외 타입을 명시.- 사용자 입력 검증은 early return (try-except 아님).
- 에러 swallowing 없음 (specific 예외만 잡고 의미 있게 처리).
- 공개 API 함수 9 섹션 필수 — Capabilities · AIContext · Guide · SeeAlso · Args · Returns · Requires · Example · Raises.
- 상세:
ops/code.md.
- 함수·변수 — camelCase.
- 클래스 — PascalCase.
- 상수 — UPPER_SNAKE_CASE.
- keyword-only 옵션은
*,뒤에 배치 —def f(stockCode, *, annual=False). - bool flag 는 항상 keyword-only.
모든 기간은 비교 가능해야 하고, 모든 회사는 비교 가능해야 한다.
sections 사상 (topic × period 수평화) 과 계정 표준화 (XBRL 정규화) 가 이 전제를 현실로 만드는 두 엔진.
- 회사 내 비교 (sections · diff) — 같은 회사의 과거와 현재.
- 회사 간 비교 (scan · analysis) — 다른 회사의 같은 지표.
- 시장 내 비교 (macro · gather) — 경제 사이클과 기업의 관계.
- 시장 간 비교 (macro · gather) — KR·US 매크로 교차.
- 모든 quarterly period 컬럼은
{calYear}Q{calQ}—end_date의 캘린더 분기로 통일. - 12 월 결산 (DART 99% · EDGAR Dec) — fiscal == calendar identity.
- 비-12 월 결산 (UAA 3 월 · NKE 5 월 · AAPL 9 월) — fy·fp → end-month CY 자동 매핑.
- SSOT:
core/finance/period.py::buildFiscalToCalendarMap(providers/edgar/finance/pivot.py에서 호출). - 근거: cross-company join 가능성 (
UA 2025Q4 ↔ Samsung 2025Q4동일 시점 데이터).
분석은 숫자 나열이 아니라 6 막 인과 구조의 스토리텔링 이다 (story 사상). analysis = 도구, story = 사람의 보고서, AI = 적극적 분석가.
sections (topic × period 수평화) 가 회사의 전체 지도. Company 엔진의 핵심 사상.
sections = { topic: { period: content } }
source 우선순위: finance > report > docs (숫자 → 정형 → 서술).
dartlab 자체는 SEC 벌크가 primary 소스. 자동 CI · 프리빌드 · HF 배포는 전부 벌크 (companyfacts.zip + 분기 {Y}q{Q}.zip) 파이프라인을 쓴다. 상세: ops/edgar.md.
| 경로 | 트리거 | 소스 | 용도 |
|---|---|---|---|
| 자동 파이프라인 (벌크) | edgarSync.yml cron + workflow_run |
Archives/edgar/daily-index/xbrl/companyfacts.zip + files/dera/data/financial-statement-data-sets/{Y}q{Q}.zip |
HF 배포, scan 프리빌드, 전 종목 커버 |
| 사용자 선택 (API) | c.refreshFromApi() 명시 호출 |
data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json |
공시 당일 최신 분기 즉시 반영 원할 때만 |
# CI 워크플로우가 API 엔드포인트 호출하는지 확인 (0건이어야 함)
grep -rn "data.sec.gov/api/xbrl" .github/ scripts/ src/dartlab/core/ src/dartlab/providers/edgar/bulk/
# 자동 파이프라인 함수에서 companyfacts API 호출 안 하는지
grep -n "companyfacts/CIK" src/dartlab/providers/edgar/bulk/반복 실패 — 자동 파이프라인에서 data.sec.gov/api/xbrl/companyfacts 엔드포인트 호출 → SEC API rate limit 위반 위험. _collectEdgarFinance 같은 per-ticker API 수집 함수는 사용자 경로 (refreshFromApi) 에서만 노출한다. num.tsv 는 받지 않는다 — companyfacts.zip 이 같은 값의 더 신선한 번들이라 중복.
- 코드 ↔ ops/ 충돌 시 코드 기준으로 ops 갱신.
ops/api-contract.md(이 문서) 가 모든 API 규칙의 진입점.- 엔진별 상세는
ops/{engine}.md참조. - 사상 변경·새 규칙 추가 시 이 문서 갱신.
Plan v9:
c.timeseries()·c.annual·c.cumulative제거 →c.show("IS", freq=, scope=)통합.- DART · EDGAR
getTimeseries()deprecated 제거. c.show()·c.select()에freq·scope파라미터 추가.synthesizeAnnualFromQuartersSSOT (core/finance/flow.py).mergeAliasRowsSSOT (core/finance/labels.py).- credit · metrics → analysis._helpers 위임.
Plan v10 (1.0.0 전 클린업):
- P0
c.IS·c.BS·c.CF·c.CIS별도 property 제거 →c.show("IS")(DART + EDGAR). - P1
c.ratios·c.ratioSeries·c.SCE·c.sceMatrix별도 property 제거 →c.show("ratios")등. - P2
c.notes.X12 sub-property 제거 →c.show("inventory")등. - P3 4 namespace 전면 제거 —
c.docs·c.finance·c.report·c.profile(public 접근 0).- 사용자 surface =
c.show()·c.select()·c.sections·c.diff()·c.filings()·c.facts·c.story()·c.analysis()·c.credit(). - 내부 compute (story · credit · valuation · analysis) 는
c._docs·_finance·_reportprivate 백엔드 사용.
- 사용자 surface =
- P4 Plan vN · R26 마커 정리.
- P5 finance DataFrame 컬럼
계정명→항목단일화 (sections 사상 정합). - P6 snakeId → 한국어 라벨 SSOT 통합 (
core/finance/labels.py::get_korean_labels(),AccountMapper.labelMap()한 줄 위임).
- 데이터 파일 (
accountMappings.json) 위치 — 현재providers/dart/finance/mapperData/. 진정한 SSOT 면core/data/로 이동 필요. 후속. _KR_SUPPLEMENTS하드코딩 —accountMappings.json에 흡수 필요. 후속.
src/dartlab/README.md— Company facade (sections 사상 · 4 namespace · canHandle 라우팅).src/dartlab/analysis/README.md— analysis 엔진 + SSOT 헬퍼 위치.src/dartlab/analysis/CREDIT.md— credit 독립 엔진.src/dartlab/providers/edgar/README.md— DART · EDGAR 동기화 규칙.ops/code.md— camelCase · 독스트링 9 섹션 · 릴리즈 · Git.ops/architecture.md— 전체 청사진.
- 모든 사용자 진입점은 Dual Access (call + attr) 로 동일 함수를 두 form 으로 허용한다.
- finance default 는 분기 연결 (CFS), 연간·YTD·별도는
freq·scope파라미터로 토글. - 같은 의미 옵션은 같은 이름으로 통일 (
freq·scope·basePeriod·stockCode·topic·axis). - 엔진 호출은
엔진("그룹","축")또는엔진("축")로 통일, 축을 루트로 노출하지 않는다. - Company 는 접근성 + 속도 5 초 + 신뢰성 (원본·None·trace) 3 원칙.
- 숫자는 원본 그대로, 없으면 None —
or 0금지 (분모 가드 예외). - L0→L1→L1.5→L2→L3→L4 import 하향만 허용, L2 엔진 간 상호 import 없음.
- 같은 로직은 SSOT 한 곳 (
synthesizeAnnualFromQuarters·mergeAliasRows등) 에서만 구현. - DART 에 public 메서드 추가 시 EDGAR 에도 동일 추가, 통화는
company.currency참조. - 예외는 구체적 타입, 독스트링 9 섹션, camelCase, bool flag keyword-only.
- 근본 전제 4 비교 가능성 (회사 내 · 회사 간 · 시장 내 · 시장 간) 이 dartlab 존재 이유.
- period 라벨은 캘린더 기준 (
{calYear}Q{calQ},buildFiscalToCalendarMapSSOT). - EDGAR 자동 파이프라인은 벌크, 사용자 API 호출은
refreshFromApi에서만.
공개 API docstring 은 사용법뿐 아니라 AI runtime 이 검증할 최소 계약도 담을 수 있다. 섹션 이름은 AIContract 이며, scripts/build/generateSpec.py 가 이를 파싱해 _generated.py::CAPABILITIES 와 _generated_analysis_graph.py::ANALYSIS_GRAPH 에 구조화한다.
허용 필드는 contractId, whenToUse, questionTypes, questionTriggers, toolMatch, toolNames, requiredInputs, requiredEvidence, evidenceSchema, outputShape, dataColumns, freshness, comparisonCompleteness, commonCalculations, verification, visualPolicy, artifactPolicy, toolArgPolicy, toolBudget, preflightActions, acceptanceCriteria, failurePolicy, failureModes, badUses, priority 다. 같은 의미의 계약을 runtime dict 에 다시 만들지 않는다.
processMap 은 사람이 docstring 에 직접 쓰는 새 필드가 아니다. scripts/build/generateSpec.py 가 위 계약 필드와 capability metadata 를 읽어 Analysis Graph 의 processMaps 를 자동 생성한다. 질문별 실행 절차가 필요하면 원천 docstring/capabilities 를 고치고 generated graph 를 재생성한다.
acceptanceCriteria 는 generated Process Map 의 최종 충족 조건이다. 직접 입력이 필요한 특수 계약만 docstring 에 둔다. 보통은 requiredEvidence, artifactPolicy, visualPolicy, comparisonCompleteness 에서 자동 파생된다.
Intelligence Pack 은 scripts/build/generateSpec.py 가 같은 원천에서 만든 설치형 generated artifact다. 위치는 src/dartlab/ai/intelligence/pack.json 이며 apiMap, capabilitySkillMap, analysisGraph, processMap, dataCatalog, recipeMap, visualContract, safetyPolicy 를 포함한다. Pack은 SSOT가 아니므로 직접 수정하지 않는다.
failurePolicy 는 계약 미충족 시 답변 강도를 낮추는 규칙이다. 예: evidence 가 부족하면 강한 판단 금지, freshness 가 낡으면 “가용 데이터 기준”으로 제한, visual evidence 가 없으면 visual 을 만들지 않는다.
AIContract:
contractId: gather.krx.close
questionTypes: recent_price_mover
requiredEvidence: ["asOf", "period", "universe", "metric"]
freshness: {"cadence": "daily", "maxStaleBusinessDays": 10}
반복 실패 → docstring/capabilities 에 없는 질문별 규칙을 quality.py, toolLoop.py, selectToolsForQuestion() 에 다시 하드코딩하는 것. runtime 은 Analysis Graph 를 조회한다.
/api/ask 응답은 호환 확장만 허용한다. 기존 소비자가 읽는 answer 와 artifacts 는 유지하고, AI 분석 작업공간 산출물은 선택 필드로 추가한다.
{
"answer": "...",
"artifacts": [],
"evidence": [],
"claims": [],
"visuals": [],
"limits": [],
"responseMeta": {
"evidenceCount": 0,
"claimCount": 0,
"visualCount": 0,
"limitCount": 0,
"coverage": {"routeIds": [], "contractIds": [], "processMapIds": [], "graph": {"graphVersion": 1, "sourceHash": "..."}},
"graph": {
"routeHit": false,
"contractHit": false,
"processMapUsed": false,
"processMapSatisfied": false,
"requiredEvidenceSatisfied": false,
"artifactSatisfied": false,
"visualSatisfied": false,
"acceptanceCriteria": {}
},
"trace": {
"selectedTools": [],
"skippedCandidateTools": [],
"toolArgs": [],
"sanitizedArgs": [],
"evidenceIds": [],
"claimIds": [],
"visualIds": []
},
"quality": {
"processMapSatisfied": false,
"claimSupportRate": 0.0,
"toolArgValidRate": 1.0,
"freshnessSatisfied": true,
"visualSatisfied": false
},
"llmRoundMs": 0,
"toolTotalMs": 0,
"rewriteCount": 0,
"maxRoundsReached": false,
"slowReason": []
}
}stream=true 에서는 기존 이벤트를 유지하면서 observe, inspect, compute, verify, artifact, evidence, claim, chart 이벤트를 추가할 수 있다. 기존 소비자는 모르는 이벤트를 무시해도 된다. done.responseMeta 는 evidence/claim/visual/limit count 와 latency 요약을 담는다.
artifact 는 answer 의 부속 문자열이 아니라 재사용 가능한 산출물이다. 랭킹·스크리닝·표 계산 질문은 가능한 경우 primary CSV artifact 를 만든다. 서버 응답의 artifacts[] 와 stream tool_result.artifacts[] 는 같은 artifact contract 를 따른다: format, mimeType, fileName, url, rows, columns, primary 를 포함할 수 있다.
evidence, claims, visuals, responseMeta.graph, responseMeta.agent.packVersion, responseMeta.agent.packHitCount, responseMeta.agent.selectedProcess, responseMeta.agent.visualValidity 는 사람이 직접 입력하는 필드가 아니다. runtime workspace session 이 Pack hit, tool trace, verification 결과를 통해 자동 생성한다. 원천 규칙은 docstring/capabilities/AIContract 이며, generated Intelligence Pack, Analysis Graph, Process Map 을 통한다.
반복 실패 → 기존 answer 계약을 깨거나, 필수 필드로 바꿔 오래된 UI/SDK 를 깨는 것. 새 필드는 additive optional 이다.