AI 가 말한 평균·비율을 CSV 로 다시 계산해 대조하기
CSV 를 주고 평균·중앙값·비율을 JSON 으로 받은 뒤 pandas 로 같은 값을 다시 계산합니다. 반올림 허용 오차 규칙, 틀린 항목만 재질문하는 방법까지 다룹니다.
AI 가 「평균 126.54, 반품률 26.7%」처럼 숫자를 말하면 맞는지는 직접 계산해야 압니다. 키를 고정한 JSON 으로 받아 pandas 가 같은 키를 다시 계산하게 하고, 어긋난 항목만 표시합니다. 논문 인용이나 문장 주장의 대조는 인용 검증과 주장-근거 표 글에 있고, 여기서는 숫자만 다룹니다.
[1] 시험용 CSV 를 만듭니다. 주문 60건(주문번호·지역·금액·반품 여부)을 난수로 만드는 스크립트입니다. 시드를 고정했으니 같은 파일이 나옵니다.
import csv, randomrandom.seed(7)rows = [[f"A{1000+i}", random.choice(["north", "south", "east", "west"]), round(random.uniform(12, 240), 2), random.choice(["yes", "no", "no", "no"])] for i in range(1, 61)]with open("orders.csv", "w", newline="") as f: w = csv.writer(f) w.writerow(["order_id", "region", "amount", "returned"]) w.writerows(rows)$ python3 gen.py$ head -4 orders.csvorder_id,region,amount,returnedA1001,east,228.11,noA1002,north,28.52,yesA1003,east,144.88,no[2] 대조용 pandas 를 가상환경에 설치합니다.
$ python3 -m venv .venv$ .venv/bin/pip install -q pandas$ .venv/bin/python -c "import pandas; print(pandas.__version__)"3.0.6[3] 프롬프트를 만듭니다. 키 이름을 못 박고, 반올림 자리를 지정하고, JSON 한 개만 내라고 합니다. 반올림 자리를 지정해 두어야 5단계의 허용 오차를 정할 수 있습니다.
Analyze the CSV inside <data>. Do not use any tools. Report these figures as a single JSON object and nothing else. Use these keys exactly:rows, mean_amount, median_amount, max_amount, returned_pct, mean_amount_north, mean_amount_south, mean_amount_east, mean_amount_west, total_amount returned_pct is the percentage of rows where returned is "yes". Round money to 2 decimals and percentages to 1 decimal. <data>[4] CSV 를 프롬프트에 붙여 실행합니다. --tools "" 로 도구를 꺼서 모델이 코드를 돌리지 못하게 합니다. 이 시험은 모델이 본문만 보고 말한 숫자를 보려는 것입니다.
$ (cat head.txt orders.csv; echo "</data>") > prompt.txt$ claude -p --model claude-sonnet-5-5 --max-turns 1 --tools "" < prompt.txt > out1.txt$ cat out1.txt{"rows":60,"mean_amount":126.54,"median_amount":139.33,"max_amount":238.43,"returned_pct":26.7,"mean_amount_north":115.54,"mean_amount_south":126.5,"mean_amount_east":128.69,"mean_amount_west":135.55,"total_amount":7592.57}[5] 대조 스크립트를 만듭니다. 답에 있는 키마다 pandas 로 정답을 계산하고, 답한 값이 정답과 마지막 자리의 반 단위 이내이면 통과시킵니다. 정답을 먼저 반올림해서 비교하지 않는 것이 요점입니다. 정답이 31.25 처럼 딱 반인 값이면 31.2 도 31.3 도 맞는 반올림이기 때문입니다.
#!/usr/bin/env python3"""usage: recheck.py data.csv answer.json — recompute each stated figure with pandas and compare"""import json, sysimport pandas as pd df = pd.read_csv(sys.argv[1])stated = json.load(open(sys.argv[2], encoding="utf-8"))yes = df["returned"] == "yes" actual = { "rows": len(df), "mean_amount": df["amount"].mean(), "median_amount": df["amount"].median(), "max_amount": df["amount"].max(), "returned_pct": yes.mean() * 100, "total_amount": df["amount"].sum(), "std_amount": df["amount"].std(), # pandas default is n-1 "p90_amount": df["amount"].quantile(0.9), # linear interpolation "mean_amount_returned": df.loc[yes, "amount"].mean(), "mean_amount_not_returned": df.loc[~yes, "amount"].mean(), "amount_over_200_count": int((df["amount"] > 200).sum()),}for region, g in df.groupby("region"): actual[f"mean_amount_{region}"] = g["amount"].mean() actual[f"returned_pct_{region}"] = (g["returned"] == "yes").mean() * 100 DIGITS = {"rows": 0, "amount_over_200_count": 0}DIGITS.update({k: 1 for k in actual if "pct" in k})bad = 0for key, got in stated.items(): if key not in actual: print(f"UNKNOWN {key}"); bad += 1; continue nd = DIGITS.get(key, 2) want = round(actual[key], nd) ok = abs(got - actual[key]) <= 0.5 * 10 ** -nd + 1e-9 # within half a unit of the last digit bad += not ok print(f"{'OK ' if ok else 'DIFF'} {key:<26} stated={got:<9} actual={want}")print(f"{len(stated)} figures, {bad} differ")sys.exit(1 if bad else 0)[6] 실행합니다. 열 개가 모두 맞았습니다.
$ .venv/bin/python -I recheck.py orders.csv out1.txt; echo exit $?OK rows stated=60 actual=60OK mean_amount stated=126.54 actual=126.54OK median_amount stated=139.33 actual=139.33OK max_amount stated=238.43 actual=238.43OK returned_pct stated=26.7 actual=26.7OK mean_amount_north stated=115.54 actual=115.54OK mean_amount_south stated=126.5 actual=126.5OK mean_amount_east stated=128.69 actual=128.69OK mean_amount_west stated=135.55 actual=135.55OK total_amount stated=7592.57 actual=7592.5710 figures, 0 differexit 0같은 파일에 표준편차·90분위수·반품 여부별 평균·지역별 반품률·200 초과 건수를 묻는 두 번째 프롬프트(out2.txt)도 돌렸습니다. 아홉 개 중 returned_pct_east 만 모델이 31.3, pandas 가 31.2 를 냈습니다.
$ .venv/bin/python -I -c "import pandas as pd; d=pd.read_csv('orders.csv'); e=d[d.region=='east']; print(len(e), (e.returned=='yes').sum(), (e.returned=='yes').mean()*100)"16 5 31.2516건 중 5건이라 정답은 31.25 입니다. 파이썬의 round(31.25, 1) 은 31.2 를 내지만 「반올림」으로 31.3 도 틀린 답이 아닙니다. 허용 오차를 반 단위로 잡았으므로 5단계 스크립트는 이 줄을 OK 로 처리합니다(9개 모두 통과). 정답을 먼저 round() 해서 == 로 비교하면 이런 줄이 오탐으로 나옵니다.
[7] 일부러 틀린 답으로 DIFF 가 나오는지 봅니다. 300건짜리 파일(시드만 11 로 바꿔 만든 orders300.csv)에서 받은 답 out3.txt 의 두 값을 손으로 바꾼 것입니다. 모델이 낸 오류가 아닙니다.
$ sed 's/"mean_amount_south":109.78/"mean_amount_south":105.78/;s/"returned_pct":25.3/"returned_pct":27.3/' out3.txt > out3-bad.json$ .venv/bin/python -I recheck.py orders300.csv out3-bad.json | grep -v '^OK'; echo exit ${PIPESTATUS[0]}DIFF returned_pct stated=27.3 actual=25.3DIFF mean_amount_south stated=105.78 actual=109.7810 figures, 2 differexit 1[8] 어긋난 키만 다시 묻습니다. 스크립트가 알려 준 정답을 넘기지 않고 「다른 도구가 다르다고 한다」는 사실만 전하고, 그 키들을 처음부터 다시 계산하게 합니다. 정답을 알려 주면 그 값을 따라 쓰는지 다시 계산했는지 구분할 수 없습니다.
Recompute only these figures from the CSV inside <data>, from scratch. Do not use any tools.A separate script disagrees with an earlier answer for them, so do not reuse that answer.Output a single JSON object with exactly these keys and nothing else: mean_amount_south, returned_pctRound money to 2 decimals and percentages to 1 decimal. <data>$ (cat reask-head.txt orders300.csv; echo "</data>") > prompt4.txt$ claude -p --model claude-sonnet-5-5 --max-turns 1 --tools "" < prompt4.txt > out4.txt$ cat out4.txt{"mean_amount_south": 109.78, "returned_pct": 25.3}$ .venv/bin/python -I recheck.py orders300.csv out4.txt; echo exit $?OK mean_amount_south stated=109.78 actual=109.78OK returned_pct stated=25.3 actual=25.32 figures, 0 differexit 0재질문한 답도 같은 스크립트로 다시 대조합니다. 두 번째에도 DIFF 가 나오면 모델에게 더 묻지 말고 pandas 값을 쓰는 편이 빠릅니다.