已保存可复算的 CPU 教学输出。ICL fixture 为合成评分测试,所有 Lab 均未调用真实模型。
LAB 04 · 固定参数的 ICL 对照:192 请求离线链路
这项研究设计问:固定模型时,本轮提示里的示例与规则怎样影响关系判断与任意标签约定?我们先把数据、请求与评分做成可检查的完整链路,再决定是否投入真实模型运行。
现有结果仅来自离线 dry-run。所有带 SYNTHETIC 的输出都是人工合成评分 fixture,不是模型实测,不能用于判断 ICL 效果。没有调用 API、下载模型或发起 192 次真实推理。
你会获得 6 个示例、24 条留出记录、3 种全局循环映射、192 个完整请求、严格评分及逐请求存档格式。
1. 任务与评价先于模型
读“对照 12,处理 9”应先判断减少;当前任意标签映射若是增加→K、减少→M、相同→R,才输出 M。数字关系与本轮约定是两个不同问题。
留出集固定处理值 9、12、15、18、21、24、27、30。每个处理值配对照 t−3、t、t+3,因此同一处理值包含三种答案。部分文字把处理写在前面,部分把对照写在前面;同组措辞与对象对三类保持平衡。这样不能只看处理数值、词汇或数字位置作答。示例对照/处理数值对与留出完全分离。
查看与修改计算代码
import json, hashlib, collections, pathlib, datetime
LABELS=['增加','减少','相同']
MAPS=[dict(zip(LABELS,labels)) for labels in [('K','M','R'),('M','R','K'),('R','K','M')]]
DEMO_PAIRS=[(10,14),(10,6),(10,10),(8,11),(8,5),(8,8)]
SYSTEM='你根据本次输入完成记录比较任务。仅输出要求的单个标签,不补充解释。'
def relation(control,treatment):
return '增加' if treatment>control else ('减少' if treatment<control else '相同')
def make_data():
demos=[]
for i,(c,t) in enumerate(DEMO_PAIRS):
demos.append({'id':f'E{i+1:02d}','control':c,'treatment':t,'text':f'样本标记值:对照组 {c},处理组 {t}。','semantic':relation(c,t)})
test=[]
objects=['菌落计数','荧光读数','叶片数量','吸光读数','分子数量','信号强度','斑点数量','颗粒数量']
# Every treatment value has one row from each class; wording/object stay identical within each triple.
for j,t in enumerate(range(9,31,3)):
for c in (t-3,t,t+3):
wording=(f'{objects[j]}记录中,对照为 {c},处理为 {t}。' if j%2==0 else f'{objects[j]}记录中,处理为 {t},对照为 {c}。')
test.append({'id':f'T{len(test)+1:02d}','control':c,'treatment':t,'object':objects[j],'text':wording,'semantic':relation(c,t)})
return demos,test
def build_prompt(condition,row,demos,mapping=None):
intro='请比较记录中的处理读数相对于对照读数的方向。'
if condition in ('A','B'): intro+='只输出增加、减少或相同之一。'
else: intro+='按本轮约定只输出 K、M 或 R 之一。'
if condition=='C': intro+='本轮规则:'+';'.join(f'{x}→{mapping[x]}' for x in LABELS)+'。'
lines=[intro]
if condition in ('B','D'):
lines.append('示例:')
for ex in demos:
label=ex['semantic'] if condition=='B' else mapping[ex['semantic']]
lines.append(ex['text']+' 输出:'+label)
lines+=['当前记录:'+row['text'],'输出:']
return '\n'.join(lines)
def make_requests():
demos,test=make_data(); requests=[]
for condition in ('A','B','C','D'):
maps=[(None,None)] if condition in ('A','B') else list(enumerate(MAPS))
for mapping_id,mapping in maps:
for row in test:
expected=row['semantic'] if mapping is None else mapping[row['semantic']]
prompt=build_prompt(condition,row,demos,mapping)
identity=f"{condition}-m{mapping_id if mapping_id is not None else 'na'}-{row['id']}"
requests.append({'request_id':identity,'condition':condition,'record_id':row['id'],
'mapping_id':mapping_id,'semantic':row['semantic'],'expected':expected,
'system':SYSTEM,'prompt':prompt,'messages':[{'role':'system','content':SYSTEM},{'role':'user','content':prompt}],
'input_sha256':hashlib.sha256((SYSTEM+'\n'+prompt).encode()).hexdigest(),
'max_visible_output_tokens':8})
return requests
def grade(raw,request):
normalized=raw.strip()
allowed=LABELS if request['condition'] in ('A','B') else ['K','M','R']
return {'normalized':normalized,'correct':normalized==request['expected'],
'format_error':normalized not in allowed,'expected':request['expected']}
def synthetic_fixture(requests):
# This is an intentionally mixed synthetic grading fixture, never a model.
records=[]
for i,r in enumerate(requests):
allowed=LABELS if r['condition'] in ('A','B') else ['K','M','R']
if i%13==0: raw='答案是 '+r['expected'] # format error
elif i%17==0: raw='' # empty error
elif i%11==0: raw=next(x for x in allowed if x!=r['expected']) # valid wrong label
else: raw=' '+r['expected']+'\n' # only outer whitespace may be removed
records.append({**r,'raw_output':raw,'score':grade(raw,r),
'evidence_type':'SYNTHETIC_FIXTURE_NOT_MODEL_OUTPUT','model_id':None,
'model_revision':None,'tokenizer_revision':None,'decoding':None,
'request_started_at':None,'request_finished_at':None})
return records
def aggregate(records):
groups={}
for r in records:
group=(r['condition'],r['mapping_id'],r['semantic'])
a=groups.setdefault(group,{'n':0,'correct':0,'format_errors':0})
a['n']+=1; a['correct']+=int(r['score']['correct']); a['format_errors']+=int(r['score']['format_error'])
return [{'condition':k[0],'mapping_id':k[1],'semantic':k[2],**v} for k,v in groups.items()]
def check_design():
demos,test=make_data(); requests=make_requests()
assert len(demos)==6 and len(test)==24 and len(requests)==192
assert collections.Counter(x['semantic'] for x in demos)==dict.fromkeys(LABELS,2)
assert collections.Counter(x['semantic'] for x in test)==dict.fromkeys(LABELS,8)
assert not ({(r['control'],r['treatment']) for r in demos}&{(r['control'],r['treatment']) for r in test})
assert collections.Counter(r['condition'] for r in requests)=={'A':24,'B':24,'C':72,'D':72}
for t in range(9,31,3):
rows=[r for r in test if r['treatment']==t]
assert set(r['semantic'] for r in rows)==set(LABELS)
assert {(9,12),(12,9)}.issubset({(r['control'],r['treatment']) for r in test})
assert len({r['request_id'] for r in requests})==192
assert all(not any(label in r['text'] for label in LABELS) for r in test)
return {'demos':len(demos),'heldout':len(test),'independent_requests':len(requests),'requests_per_condition':dict(collections.Counter(r['condition'] for r in requests))}
def run_with_generator(generate,model_manifest,out_dir,max_requests=3):
# Future extension: explicit model adapter; never called in R1 dry-run.
required=['model_id','model_revision','tokenizer_revision','software_versions','decoding','system_prompt']
if any(not model_manifest.get(k) for k in required): raise ValueError('先冻结完整模型与解码身份')
if model_manifest['system_prompt']!=SYSTEM: raise ValueError('system prompt 与冻结设计不一致')
if not 1<=max_requests<=192: raise ValueError('max_requests 须明确设为1..192')
out=pathlib.Path(out_dir); out.mkdir(parents=True,exist_ok=True)
path=out/'model-responses.jsonl'
if path.exists(): raise FileExistsError('为保护教师基线,请选新输出目录')
records=[]
with path.open('x') as f:
for request in make_requests()[:max_requests]:
started=datetime.datetime.now(datetime.timezone.utc).isoformat()
# Adapter must perform one stateless call with exactly these messages and fixed settings.
raw=generate(request['messages'],dict(model_manifest['decoding']),request['max_visible_output_tokens'])
if not isinstance(raw,str): raise TypeError('模型适配器必须返回原始字符串')
record={**request,'raw_output':raw,'score':grade(raw,request),'model_manifest':model_manifest,
'evidence_type':'MODEL_ADAPTER_OUTPUT','request_started_at':started,
'request_finished_at':datetime.datetime.now(datetime.timezone.utc).isoformat()}
f.write(json.dumps(record,ensure_ascii=False)+'\n'); f.flush(); records.append(record)
return records
def write_artifacts(out_dir,records=None):
out=pathlib.Path(out_dir); out.mkdir(parents=True,exist_ok=True)
demos,test=make_data(); requests=make_requests()
for name,data in [('demonstrations.json',demos),('heldout24.json',test),('mappings.json',MAPS),('requests192.json',requests)]:
(out/name).write_text(json.dumps(data,ensure_ascii=False,indent=2))
if records is not None:
(out/'SYNTHETIC-fixture192.jsonl').write_text(''.join(json.dumps(r,ensure_ascii=False)+'\n' for r in records))
(out/'SYNTHETIC-grading-summary.json').write_text(json.dumps(aggregate(records),ensure_ascii=False,indent=2))
return out
def evaluate_responses(requests,responses,model_manifest):
required=['model_id','model_revision','tokenizer_revision','software_versions','decoding','system_prompt']
missing=[k for k in required if not model_manifest.get(k)]
if missing: raise ValueError('缺少真实运行身份字段: '+','.join(missing))
by_id={r['request_id']:r for r in responses}
if len(by_id)!=len(responses): raise ValueError('重复request_id')
if set(by_id)!=set(r['request_id'] for r in requests): raise ValueError('响应必须恰好覆盖这192个request_id')
result=[]
for r in requests:
answer=by_id[r['request_id']]
if answer.get('input_sha256')!=r['input_sha256']: raise ValueError('请求哈希不符: '+r['request_id'])
if not isinstance(answer.get('raw_output'),str): raise ValueError('raw_output必须是字符串')
result.append({**r,**answer,'score':grade(answer['raw_output'],r),'evidence_type':'USER_SUPPLIED_MODEL_OUTPUT_NOT_EXECUTED_BY_THIS_SCRIPT','model_manifest':model_manifest})
return result
查看与修改计算代码
print(check_design())
demos,test=make_data()
print('完整示例:')
for row in demos: print(row['id'],row['text'],'→',row['semantic'])
print('完整留出:')
for row in test: print(row['id'],row['text'],'参考答案=',row['semantic'])
print('三轮映射:',MAPS)
保存的本地 CPU 输出 · icl192-03
{'demos': 6, 'heldout': 24, 'independent_requests': 192, 'requests_per_condition': {'A': 24, 'B': 24, 'C': 72, 'D': 72}}
完整示例:
E01 样本标记值:对照组 10,处理组 14。 → 增加
E02 样本标记值:对照组 10,处理组 6。 → 减少
E03 样本标记值:对照组 10,处理组 10。 → 相同
E04 样本标记值:对照组 8,处理组 11。 → 增加
E05 样本标记值:对照组 8,处理组 5。 → 减少
E06 样本标记值:对照组 8,处理组 8。 → 相同
完整留出:
T01 菌落计数记录中,对照为 6,处理为 9。 参考答案= 增加
T02 菌落计数记录中,对照为 9,处理为 9。 参考答案= 相同
T03 菌落计数记录中,对照为 12,处理为 9。 参考答案= 减少
T04 荧光读数记录中,处理为 12,对照为 9。 参考答案= 增加
T05 荧光读数记录中,处理为 12,对照为 12。 参考答案= 相同
T06 荧光读数记录中,处理为 12,对照为 15。 参考答案= 减少
T07 叶片数量记录中,对照为 12,处理为 15。 参考答案= 增加
T08 叶片数量记录中,对照为 15,处理为 15。 参考答案= 相同
T09 叶片数量记录中,对照为 18,处理为 15。 参考答案= 减少
T10 吸光读数记录中,处理为 18,对照为 15。 参考答案= 增加
T11 吸光读数记录中,处理为 18,对照为 18。 参考答案= 相同
T12 吸光读数记录中,处理为 18,对照为 21。 参考答案= 减少
T13 分子数量记录中,对照为 18,处理为 21。 参考答案= 增加
T14 分子数量记录中,对照为 21,处理为 21。 参考答案= 相同
T15 分子数量记录中,对照为 24,处理为 21。 参考答案= 减少
T16 信号强度记录中,处理为 24,对照为 21。 参考答案= 增加
T17 信号强度记录中,处理为 24,对照为 24。 参考答案= 相同
T18 信号强度记录中,处理为 24,对照为 27。 参考答案= 减少
T19 斑点数量记录中,对照为 24,处理为 27。 参考答案= 增加
T20 斑点数量记录中,对照为 27,处理为 27。 参考答案= 相同
T21 斑点数量记录中,对照为 30,处理为 27。 参考答案= 减少
T22 颗粒数量记录中,处理为 30,对照为 27。 参考答案= 增加
T23 颗粒数量记录中,处理为 30,对照为 30。 参考答案= 相同
T24 颗粒数量记录中,处理为 30,对照为 33。 参考答案= 减少
三轮映射: [{'增加': 'K', '减少': 'M', '相同': 'R'}, {'增加': 'M', '减少': 'R', '相同': 'K'}, {'增加': 'R', '减少': 'K', '相同': 'M'}]
2. 四个可识别条件
A:语义标签、零示例,24 次。B:语义标签、六示例,24 次。C:任意标签、显式规则、零示例,三轮共 72 次。D:任意标签、由六示例传达映射,三轮共 72 次。A/B 不随映射重复。
每个请求只含固定 system、当次任务材料和一条留出输入,单独发起。无映射任意标签条件缺少可识别答案,不进入主准确率排名。下面打印同一条 12→9 的四种完整输入;D 条件不泄露显式映射。
查看与修改计算代码
requests=make_requests()
row_id=next(x['id'] for x in test if (x['control'],x['treatment'])==(12,9))
for condition in ('A','B','C','D'):
r=next(r for r in requests if r['condition']==condition and r['record_id']==row_id and r['mapping_id'] in (None,0))
print('\n---',r['request_id'],'---\nSYSTEM:',r['system'],'\nUSER:\n'+r['prompt'])
保存的本地 CPU 输出 · icl192-05
--- A-mna-T03 --- SYSTEM: 你根据本次输入完成记录比较任务。仅输出要求的单个标签,不补充解释。 USER: 请比较记录中的处理读数相对于对照读数的方向。只输出增加、减少或相同之一。 当前记录:菌落计数记录中,对照为 12,处理为 9。 输出: --- B-mna-T03 --- SYSTEM: 你根据本次输入完成记录比较任务。仅输出要求的单个标签,不补充解释。 USER: 请比较记录中的处理读数相对于对照读数的方向。只输出增加、减少或相同之一。 示例: 样本标记值:对照组 10,处理组 14。 输出:增加 样本标记值:对照组 10,处理组 6。 输出:减少 样本标记值:对照组 10,处理组 10。 输出:相同 样本标记值:对照组 8,处理组 11。 输出:增加 样本标记值:对照组 8,处理组 5。 输出:减少 样本标记值:对照组 8,处理组 8。 输出:相同 当前记录:菌落计数记录中,对照为 12,处理为 9。 输出: --- C-m0-T03 --- SYSTEM: 你根据本次输入完成记录比较任务。仅输出要求的单个标签,不补充解释。 USER: 请比较记录中的处理读数相对于对照读数的方向。按本轮约定只输出 K、M 或 R 之一。本轮规则:增加→K;减少→M;相同→R。 当前记录:菌落计数记录中,对照为 12,处理为 9。 输出: --- D-m0-T03 --- SYSTEM: 你根据本次输入完成记录比较任务。仅输出要求的单个标签,不补充解释。 USER: 请比较记录中的处理读数相对于对照读数的方向。按本轮约定只输出 K、M 或 R 之一。 示例: 样本标记值:对照组 10,处理组 14。 输出:K 样本标记值:对照组 10,处理组 6。 输出:M 样本标记值:对照组 10,处理组 10。 输出:R 样本标记值:对照组 8,处理组 11。 输出:K 样本标记值:对照组 8,处理组 5。 输出:M 样本标记值:对照组 8,处理组 8。 输出:R 当前记录:菌落计数记录中,对照为 12,处理为 9。 输出:
3. 评分:只去掉首尾空白
主指标是预先声明的标签精确匹配。多余解释、无效标签、空输出都计错,并单列格式错误。下面刻意混入“正确标签+解释”、有效错标签与空串,检验评分不会误把输出中出现了正确字母就算正确。
接下来所有数字是合成 fixture 的测试结果,不是模型准确率。 不会生成“某模型达到 X%”的图。
查看与修改计算代码
fixture=synthetic_fixture(requests)
for raw in [' M ','答案是 M','K','']:
r=next(r for r in requests if r['expected']=='M')
print('人工构造原始输出=',repr(raw),grade(raw,r))
summary=aggregate(fixture)
print('SYNTHETIC FIXTURE grading summary — NOT MODEL ACCURACY')
for row in summary: print(row)
assert len(fixture)==192
assert sum(row['n'] for row in summary)==192
assert grade(' M ',r)['correct'] and not grade('答案是 M',r)['correct']
assert grade('',r)['format_error']
保存的本地 CPU 输出 · icl192-07
人工构造原始输出= ' M ' {'normalized': 'M', 'correct': True, 'format_error': False, 'expected': 'M'}
人工构造原始输出= '答案是 M' {'normalized': '答案是 M', 'correct': False, 'format_error': True, 'expected': 'M'}
人工构造原始输出= 'K' {'normalized': 'K', 'correct': False, 'format_error': False, 'expected': 'M'}
人工构造原始输出= '' {'normalized': '', 'correct': False, 'format_error': True, 'expected': 'M'}
SYNTHETIC FIXTURE grading summary — NOT MODEL ACCURACY
{'condition': 'A', 'mapping_id': None, 'semantic': '增加', 'n': 8, 'correct': 7, 'format_errors': 1}
{'condition': 'A', 'mapping_id': None, 'semantic': '相同', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'A', 'mapping_id': None, 'semantic': '减少', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'B', 'mapping_id': None, 'semantic': '增加', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'B', 'mapping_id': None, 'semantic': '相同', 'n': 8, 'correct': 7, 'format_errors': 1}
{'condition': 'B', 'mapping_id': None, 'semantic': '减少', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'C', 'mapping_id': 0, 'semantic': '增加', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'C', 'mapping_id': 0, 'semantic': '相同', 'n': 8, 'correct': 6, 'format_errors': 1}
{'condition': 'C', 'mapping_id': 0, 'semantic': '减少', 'n': 8, 'correct': 6, 'format_errors': 2}
{'condition': 'C', 'mapping_id': 1, 'semantic': '增加', 'n': 8, 'correct': 7, 'format_errors': 1}
{'condition': 'C', 'mapping_id': 1, 'semantic': '相同', 'n': 8, 'correct': 5, 'format_errors': 2}
{'condition': 'C', 'mapping_id': 1, 'semantic': '减少', 'n': 8, 'correct': 7, 'format_errors': 0}
{'condition': 'C', 'mapping_id': 2, 'semantic': '增加', 'n': 8, 'correct': 5, 'format_errors': 2}
{'condition': 'C', 'mapping_id': 2, 'semantic': '相同', 'n': 8, 'correct': 8, 'format_errors': 0}
{'condition': 'C', 'mapping_id': 2, 'semantic': '减少', 'n': 8, 'correct': 5, 'format_errors': 2}
{'condition': 'D', 'mapping_id': 0, 'semantic': '增加', 'n': 8, 'correct': 7, 'format_errors': 0}
{'condition': 'D', 'mapping_id': 0, 'semantic': '相同', 'n': 8, 'correct': 5, 'format_errors': 2}
{'condition': 'D', 'mapping_id': 0, 'semantic': '减少', 'n': 8, 'correct': 7, 'format_errors': 1}
{'condition': 'D', 'mapping_id': 1, 'semantic': '增加', 'n': 8, 'correct': 5, 'format_errors': 2}
{'condition': 'D', 'mapping_id': 1, 'semantic': '相同', 'n': 8, 'correct': 7, 'format_errors': 0}
{'condition': 'D', 'mapping_id': 1, 'semantic': '减少', 'n': 8, 'correct': 8, 'format_errors': 0}
{'condition': 'D', 'mapping_id': 2, 'semantic': '增加', 'n': 8, 'correct': 8, 'format_errors': 0}
{'condition': 'D', 'mapping_id': 2, 'semantic': '相同', 'n': 8, 'correct': 6, 'format_errors': 2}
{'condition': 'D', 'mapping_id': 2, 'semantic': '减少', 'n': 8, 'correct': 5, 'format_errors': 2}
4. 完整输出落盘,教师基线与学生重跑分开
默认写入 icl192-student-output/;可自行改目录。原始输入与输出始终保存,评分不覆盖原文。合成记录的模型身份字段为 null,证据字段明确写 SYNTHETIC_FIXTURE_NOT_MODEL_OUTPUT。这比填一个虚假的模型名更容易防止误用。
查看与修改计算代码
out=write_artifacts('icl192-student-output',fixture)
print('生成文件:')
for p in sorted(out.iterdir()): print(p.name,p.stat().st_size,'bytes')
print('首条记录证据身份:',fixture[0]['evidence_type'],'model_id=',fixture[0]['model_id'])
保存的本地 CPU 输出 · icl192-09
生成文件: SYNTHETIC-fixture192.jsonl 319734 bytes SYNTHETIC-grading-summary.json 3164 bytes demonstrations.json 920 bytes heldout24.json 4574 bytes mappings.json 197 bytes requests192.json 282002 bytes 首条记录证据身份: SYNTHETIC_FIXTURE_NOT_MODEL_OUTPUT model_id= None
5. 如果以后运行真实模型,哪些条件先冻结?
固定 checkpoint/revision、tokenizer revision、system、解码参数、最大输出与软件版本。每条请求独立,保留 request_id、输入 SHA、原始输出、请求起止时间、映射与评分。封闭 API 只能说未发起微调,不能声称已检查服务端权重;开放权重可在纯推理流程记录前后权重哈希。
本交付的 icl192.py 是可执行离线入口:不加参数生成全部请求和合成评分;--responses 加 --model-manifest 可评分外部运行产生的完整 JSONL。还提供 run_with_generator(generate, model_manifest, out_dir, max_requests=3):由调用者传入已选模型适配器和完整身份,明确设置请求上限,逐条保存原始输出与时间。该函数尚未调用。它不内置某个尚未选择的模型适配器,不会悄悄调用付费服务;新运行目录已存在时拒绝覆盖教师基线。
每条输入 1000 tokens、可见输出 8 tokens 时,192 请求上限估算为 192000 输入、1536 可见输出 tokens,不含重试和服务端隐藏推理。这只是预算上限,本本没有按 tokenizer 测量真实 token 数。 本轮尚未选定具体模型,成本与设备可行性未验证。
6. 结果可以支持什么?
A 很高可能是天花板;D 随映射改变且留出表现好,支持当前设置采用本轮约定;C 优于 D,支持当前显式规则方案更有效。C/D 还在长度、冗余与形式上不同,不能据此识别唯一内部机制,也不能把 D−C 称为“示例净收益”。24 条是教学诊断,不是通用能力估计。
单变量练习:先保持记录与映射固定,只打乱六示例的顺序,并另存请求清单;不要同时改模型、系统提示和评分。若破坏映射一致性,该条件不再有唯一规则,单独分析冲突偏向,不纳入主能力排名。
来源:本课已归档的 ICL 实验设计;真实模型运行另列为扩展任务;Min et al.、Wei et al. 为设计背景,不是此处结果来源。