-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path18-code.html
More file actions
1362 lines (1275 loc) · 70.8 KB
/
Copy path18-code.html
File metadata and controls
1362 lines (1275 loc) · 70.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>程式碼任務與工程 agent — LLM 基礎 · 文件十八</title>
<style>
:root{
--paper:#EDF0F3; --paper-2:#E4E9ED; --panel:#F7F9FA;
--ink:#16212B; --ink-2:#56646F; --ink-3:#8494A0;
--rule:#C7D1D9; --rule-2:#DCE3E8;
--acc:#732C77; --acc-soft:#E7D8E8;
--alt:#1F5C99; --alt-soft:#D6E4F1;
--vio:#6B4E9E; --vio-soft:#E3DBF1;
--warn:#B4553A; --warn-soft:#F0DED6;
/* 圖表序列:已驗證 CVD ΔE 16.7、常視 20.6、對比 ≥3:1 */
--s1:#1F5C99; --s2:#B4553A; --s3:#6B4E9E;
--ramp-1:#C3D8EA; --ramp-2:#5E93C0; --ramp-3:#1F5C99;
}
@media (prefers-color-scheme: dark){
:root{
--paper:#0E161D; --paper-2:#16212A; --panel:#131D25;
--ink:#DCE5EC; --ink-2:#8B9BA8; --ink-3:#647684;
--rule:#2A3844; --rule-2:#1E2A34;
--acc:#B767BB; --acc-soft:#29172A;
--alt:#5FA8E0; --alt-soft:#1B3040;
--vio:#A98CD9; --vio-soft:#241D33;
--warn:#E08A63; --warn-soft:#33241E;
--s1:#4A97D6; --s2:#D0764A; --s3:#9179CE;
--ramp-1:#1E3A4F; --ramp-2:#2F6893; --ramp-3:#4A97D6;
}
}
:root[data-theme="dark"]{
--paper:#0E161D; --paper-2:#16212A; --panel:#131D25;
--ink:#DCE5EC; --ink-2:#8B9BA8; --ink-3:#647684;
--rule:#2A3844; --rule-2:#1E2A34;
--acc:#B767BB; --acc-soft:#29172A;
--alt:#5FA8E0; --alt-soft:#1B3040;
--vio:#A98CD9; --vio-soft:#241D33;
--warn:#E08A63; --warn-soft:#33241E;
--s1:#4A97D6; --s2:#D0764A; --s3:#9179CE;
--ramp-1:#1E3A4F; --ramp-2:#2F6893; --ramp-3:#4A97D6;
}
:root[data-theme="light"]{
--paper:#EDF0F3; --paper-2:#E4E9ED; --panel:#F7F9FA;
--ink:#16212B; --ink-2:#56646F; --ink-3:#8494A0;
--rule:#C7D1D9; --rule-2:#DCE3E8;
--acc:#732C77; --acc-soft:#E7D8E8;
--alt:#1F5C99; --alt-soft:#D6E4F1;
--vio:#6B4E9E; --vio-soft:#E3DBF1;
--warn:#B4553A; --warn-soft:#F0DED6;
--s1:#1F5C99; --s2:#B4553A; --s3:#6B4E9E;
--ramp-1:#C3D8EA; --ramp-2:#5E93C0; --ramp-3:#1F5C99;
}
*{box-sizing:border-box}
body{margin:0;background:var(--paper);color:var(--ink);
font-family:"Helvetica Neue",Helvetica,Arial,"PingFang TC","Noto Sans CJK TC","Noto Sans TC","Microsoft JhengHei",sans-serif;
font-size:16px;line-height:1.75;-webkit-font-smoothing:antialiased}
.wrap{max-width:1060px;margin:0 auto;padding:0 clamp(1.1rem,4vw,2.5rem)}
.col{max-width:68ch}
h1,h2,h3,h4{font-family:Georgia,"Songti TC","Noto Serif CJK TC","Noto Serif TC",serif;
text-wrap:balance;margin:0}
h1{font-size:clamp(1.9rem,4.6vw,2.9rem);line-height:1.18;letter-spacing:-.01em}
h2{font-size:clamp(1.3rem,3vw,1.65rem);line-height:1.3}
h3{font-size:1.05rem;line-height:1.45;margin:1.7rem 0 .5rem}
h4{font-size:.95rem;line-height:1.45;margin:1.3rem 0 .4rem;color:var(--ink-2)}
p{margin:0 0 1rem}
strong{font-weight:700;color:var(--ink)}
em{font-style:normal;color:var(--acc);font-weight:600}
a{color:var(--alt)}
.mono,code{font-family:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace}
code{font-size:.86em;background:var(--paper-2);border-radius:2px;padding:.06em .34em}
.eyebrow{font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
font-family:ui-monospace,Menlo,Consolas,monospace;margin:0 0 .8rem}
.masthead{padding:clamp(2.2rem,6vw,3.4rem) 0 0}
.masthead h1{margin-bottom:.7rem}
.masthead .sub{color:var(--ink-2);font-size:clamp(.95rem,2vw,1.06rem);max-width:62ch;margin-bottom:1.4rem}
.masthead .up{font-size:.82rem;color:var(--ink-3);margin-bottom:1.2rem}
.masthead .up a{color:var(--ink-2)}
.tabnav{position:sticky;top:0;z-index:20;background:var(--paper);
margin:0 calc(-1 * clamp(1.1rem,4vw,2.5rem));
padding:.7rem clamp(1.1rem,4vw,2.5rem);border-bottom:1px solid var(--rule)}
.tabnav .hint{font-family:ui-monospace,Menlo,monospace;font-size:.66rem;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-3);margin:0 0 .45rem}
.navtop{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
flex-wrap:wrap;margin-bottom:.45rem}
.navtop .hint{margin:0}
.back{font-family:ui-monospace,Menlo,monospace;font-size:.72rem;color:var(--ink-2);
text-decoration:none;border:1px solid var(--rule);border-radius:3px;padding:.25rem .65rem;
transition:background .15s,color .15s;white-space:nowrap}
.back:hover{background:var(--paper-2);color:var(--ink)}
.back:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.tabs{display:flex;flex-wrap:wrap;border:1px solid var(--rule);border-radius:5px;
overflow:hidden;background:var(--panel);width:fit-content;max-width:100%}
.tabs button{appearance:none;background:none;border:none;border-right:1px solid var(--rule);
font-family:inherit;font-size:.9rem;color:var(--ink-2);cursor:pointer;padding:.55rem 1.1rem;
transition:background .15s,color .15s;display:flex;align-items:baseline;gap:.5rem;white-space:nowrap}
.tabs button:last-child{border-right:none}
.tabs button:hover{background:var(--paper-2);color:var(--ink)}
.tabs button:focus-visible{outline:2px solid var(--acc);outline-offset:-2px}
.tabs button .n{font-family:ui-monospace,Menlo,monospace;font-size:.68rem;color:var(--ink-3)}
.tabs button[aria-selected="true"]{background:var(--acc-soft);color:var(--acc);font-weight:600;
box-shadow:inset 0 2px 0 var(--acc)}
.tabs button[aria-selected="true"] .n{color:var(--acc)}
[role="tabpanel"][hidden]{display:none}
.panel-intro{padding:clamp(2rem,5vw,3rem) 0 clamp(1.6rem,4vw,2.4rem);border-bottom:1px solid var(--rule-2)}
.panel-intro h2{margin-bottom:.8rem}
.panel-intro p{color:var(--ink-2);max-width:66ch}
.panel-intro p b{color:var(--ink)}
.stage{display:grid;grid-template-columns:3.4rem minmax(0,1fr);gap:0 1.6rem;
padding:clamp(2rem,4.5vw,3rem) 0;border-bottom:1px solid var(--rule-2)}
.stage:last-child{border-bottom:none}
.stage-n{font-family:ui-monospace,Menlo,monospace;font-size:.76rem;color:var(--acc);
padding-top:.55rem;font-weight:600}
.stage-body{min-width:0}
@media (max-width:640px){.stage{grid-template-columns:1fr}.stage-n{padding:0 0 .5rem}}
figure{margin:1.7rem 0}
figure svg{display:block;width:100%;max-width:100%;height:auto;color:var(--ink)}
figure svg text{font-family:ui-monospace,Menlo,Consolas,monospace}
figcaption{font-size:.82rem;color:var(--ink-2);line-height:1.6;margin-top:.7rem;
padding-left:.85rem;border-left:2px solid var(--rule)}
figcaption b{color:var(--ink)}
.scroll{overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch}
.fig-head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
gap:.4rem 1.4rem;margin-bottom:.9rem}
.fig-t{font-size:.9rem;font-weight:700}
.legend{display:flex;flex-wrap:wrap;gap:.3rem 1rem;font-family:ui-monospace,Menlo,monospace;
font-size:.72rem;color:var(--ink-2)}
.legend i{display:inline-flex;align-items:center;gap:.42em;font-style:normal}
.legend i::before{content:"";width:11px;height:11px;border-radius:2px;background:var(--k,var(--ink-3))}
.anim-ctl{display:flex;flex-wrap:wrap;align-items:center;gap:.45rem .8rem;margin:.5rem 0 .7rem}
.anim-ctl button{appearance:none;font:inherit;font-size:.8rem;padding:.22rem .7rem;border:1px solid var(--rule);
border-radius:4px;background:var(--panel);color:var(--ink);cursor:pointer}
.anim-ctl button:hover{background:var(--paper-2)}
.anim-ctl button:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.anim-cap{font-size:.82rem;color:var(--ink-2);flex:1 1 18rem;min-width:0}
.note{background:var(--panel);border:1px solid var(--rule);border-left:3px solid var(--warn);
padding:1rem 1.15rem;margin:1.5rem 0;font-size:.93rem;line-height:1.7}
.note p:last-child{margin-bottom:0}
.note .tag{font-family:ui-monospace,Menlo,monospace;font-size:.68rem;letter-spacing:.12em;
text-transform:uppercase;color:var(--warn);display:block;margin-bottom:.4rem}
.note.acc{border-left-color:var(--acc)} .note.acc .tag{color:var(--acc)}
.note.alt{border-left-color:var(--alt)} .note.alt .tag{color:var(--alt)}
.formula{background:var(--panel);border:1px solid var(--rule);border-radius:3px;
padding:1.15rem 1rem;margin:1.3rem 0;text-align:center;
font-family:ui-monospace,Menlo,monospace;font-size:clamp(.86rem,1.9vw,1rem);
color:var(--ink);overflow-x:auto;line-height:2}
.formula .op{color:var(--acc);font-weight:600}
.formula small{display:block;font-size:.72rem;color:var(--ink-3);margin-top:.5rem;line-height:1.6}
.tbl-wrap{overflow-x:auto;overscroll-behavior-x:contain;margin:1.4rem 0}
table{border-collapse:collapse;width:100%;font-size:.86rem;min-width:32rem}
th,td{text-align:left;padding:.55rem .8rem;border-bottom:1px solid var(--rule-2);vertical-align:top}
th{font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
font-family:ui-monospace,Menlo,monospace;border-bottom:1px solid var(--rule);white-space:nowrap}
td.k{font-family:ui-monospace,Menlo,monospace;font-size:.8rem;color:var(--acc);white-space:nowrap}
td.num{font-family:ui-monospace,Menlo,monospace;font-variant-numeric:tabular-nums;
text-align:right;white-space:nowrap}
caption{text-align:left;font-size:.72rem;color:var(--ink-3);padding-bottom:.6rem;
font-family:ui-monospace,Menlo,monospace;letter-spacing:.08em}
tr.hl td{background:var(--acc-soft)}
.good{color:var(--alt);font-weight:700}
.bad{color:var(--warn);font-weight:700}
ul,ol{margin:0 0 1rem;padding-left:1.2rem}
li{margin-bottom:.5rem}
li::marker{color:var(--ink-3)}
dl{margin:1.2rem 0;display:flex;flex-direction:column;gap:.9rem}
dt{font-family:ui-monospace,Menlo,monospace;font-size:.8rem;font-weight:700;color:var(--acc)}
dd{margin:.15rem 0 0;font-size:.93rem;line-height:1.7;color:var(--ink-2)}
.decide{border:1px solid var(--rule);border-left:3px solid var(--acc);background:var(--panel);
padding:.85rem 1.1rem;margin:1.6rem 0 0;font-size:.9rem;line-height:1.65}
.decide b{display:block;font-family:ui-monospace,Menlo,monospace;font-size:.64rem;letter-spacing:.1em;
text-transform:uppercase;color:var(--acc);margin-bottom:.25rem;font-weight:400}
/* ── 教科書元件 ── */
.obj{border:1px solid var(--rule);background:var(--panel);border-radius:3px;
padding:1.1rem 1.25rem;margin:1.6rem 0;display:grid;gap:.9rem}
@media(min-width:46rem){.obj{grid-template-columns:1fr 1fr;gap:.9rem 2rem}}
.obj h3{margin:0 0 .3rem;font-size:.82rem;font-family:ui-monospace,Menlo,monospace;
letter-spacing:.1em;text-transform:uppercase;color:var(--acc);font-weight:600}
.obj ul{margin:0;padding-left:1.05rem;font-size:.88rem;line-height:1.65;color:var(--ink-2)}
.obj li{margin-bottom:.35rem}
.obj li:last-child{margin-bottom:0}
.obj a{color:var(--alt)}
.ex{border:1px solid var(--rule);border-left:3px solid var(--alt);background:var(--panel);
padding:1.1rem 1.25rem;margin:1.6rem 0;font-size:.92rem;line-height:1.7}
.ex .tag{font-family:ui-monospace,Menlo,monospace;font-size:.68rem;letter-spacing:.12em;
text-transform:uppercase;color:var(--alt);display:block;margin-bottom:.5rem;font-weight:700}
.ex .q{font-weight:600;color:var(--ink);margin-bottom:.7rem}
.ex .work{font-family:ui-monospace,Menlo,monospace;font-size:.79rem;line-height:2;
background:var(--paper);border:1px solid var(--rule-2);border-radius:2px;
padding:.75rem .9rem;margin:.7rem 0;overflow-x:auto;white-space:pre;color:var(--ink-2)}
.ex .work b{color:var(--ink)}
.ex .work .c{color:var(--ink-3)}
.ex .a{border-top:1px solid var(--rule-2);padding-top:.65rem;margin-top:.8rem}
.ex .a::before{content:"結論 ";font-family:ui-monospace,Menlo,monospace;font-size:.68rem;
letter-spacing:.1em;color:var(--alt);font-weight:700}
.ex p:last-child{margin-bottom:0}
.summary{border:1px solid var(--rule);border-top:3px solid var(--acc);background:var(--panel);
padding:1.2rem 1.3rem;margin:2rem 0 0}
.summary h3{margin:0 0 .7rem;font-size:1rem}
.summary ol{margin:0;padding-left:1.2rem;font-size:.91rem;line-height:1.7;color:var(--ink-2)}
.summary ol li{margin-bottom:.5rem}
.summary ol li:last-child{margin-bottom:0}
.summary ol li b{color:var(--ink)}
.quiz{margin:1.6rem 0 0}
.quiz > .lbl{font-family:ui-monospace,Menlo,monospace;font-size:.68rem;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-3);display:block;margin-bottom:.6rem}
.quiz details{border:1px solid var(--rule);border-radius:3px;background:var(--panel);
margin-bottom:.5rem;font-size:.9rem}
.quiz summary{cursor:pointer;padding:.65rem .9rem;line-height:1.6;color:var(--ink);
list-style:none;display:flex;gap:.6rem;align-items:baseline}
.quiz summary::-webkit-details-marker{display:none}
.quiz summary::before{content:"?";font-family:ui-monospace,Menlo,monospace;font-size:.72rem;
color:var(--acc);font-weight:700;flex-shrink:0}
.quiz summary:hover{background:var(--paper-2)}
.quiz summary:focus-visible{outline:2px solid var(--acc);outline-offset:-2px}
.quiz details[open] summary{border-bottom:1px solid var(--rule-2)}
.quiz .ans{padding:.7rem .9rem .8rem 2.1rem;color:var(--ink-2);line-height:1.7;font-size:.88rem}
.quiz .ans p:last-child{margin-bottom:0}
/* 計算器 */
.calc{background:var(--panel);border:1px solid var(--rule);border-radius:3px;
padding:clamp(1rem,3vw,1.4rem);margin:1.7rem 0}
.calc h3{margin:0 0 .2rem;font-size:.98rem}
.calc .cap{font-size:.8rem;color:var(--ink-3);margin-bottom:1rem}
.rows{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:.9rem}
.rows label{display:block;font-family:ui-monospace,Menlo,monospace;font-size:.72rem;
color:var(--ink-2);margin-bottom:.25rem}
.rows select,.rows input{width:100%;font-family:ui-monospace,Menlo,monospace;font-size:.84rem;
padding:.4rem .5rem;background:var(--paper);color:var(--ink);
border:1px solid var(--rule);border-radius:2px}
.rows select:focus-visible,.rows input:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.out{margin-top:1.2rem;border-top:1px solid var(--rule);padding-top:.9rem;
display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:.9rem}
.out div{font-family:ui-monospace,Menlo,monospace}
.out .lb{font-size:.68rem;color:var(--ink-3);letter-spacing:.06em}
.out .vv{font-size:1.2rem;color:var(--acc);font-variant-numeric:tabular-nums}
.out .vv.bad{color:var(--warn)}
.verdict{margin-top:1rem;font-size:.86rem;line-height:1.6;color:var(--ink-2)}
.verdict b{color:var(--ink)}
.serial{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin:0 0 1.4rem}
.serial .lbl{font-family:ui-monospace,Menlo,monospace;font-size:.62rem;letter-spacing:.14em;
text-transform:uppercase;color:var(--ink-3);margin-right:.35rem;flex-basis:100%}
.serial a,.serial .cur{font-size:.78rem;line-height:1.4;text-decoration:none;white-space:nowrap;
border:1px solid var(--rule);border-radius:3px;padding:.25rem .6rem;color:var(--ink-2);
transition:background .15s,color .15s}
.serial a:hover{background:var(--paper-2);color:var(--ink);border-color:var(--ink-3)}
.serial a:focus-visible{outline:2px solid var(--ink-3);outline-offset:2px}
.serial .cur{background:var(--paper-2);color:var(--ink-3);border-style:dashed;cursor:default}
footer{border-top:1px solid var(--rule);padding:2.4rem 0 4rem;font-size:.84rem;color:var(--ink-2)}
footer ul{padding-left:1.1rem;margin:.4rem 0 0}
.tip{position:fixed;z-index:60;pointer-events:none;opacity:0;transition:opacity .12s;
background:var(--ink);color:var(--paper);font-family:ui-monospace,Menlo,monospace;
font-size:.72rem;line-height:1.6;padding:.45rem .6rem;border-radius:3px;white-space:nowrap;
box-shadow:0 4px 16px #0004}
.tip.on{opacity:1}
@media (prefers-reduced-motion:reduce){*{transition:none !important;scroll-behavior:auto !important}}
/* 流程線的走動:只是指示方向,減少動態時停掉 */
@keyframes flowdash{to{stroke-dashoffset:-24}}
.flowline{stroke-dasharray:6 6;animation:flowdash 1.6s linear infinite}
@media (prefers-reduced-motion:reduce){.flowline{animation:none;stroke-dasharray:none}}
/* ── 排名融合示意 ── */
.rank{display:grid;gap:.6rem;margin:1.4rem 0}
@media(min-width:44rem){.rank{grid-template-columns:repeat(3,1fr)}}
.rank > div{border:1px solid var(--rule);background:var(--panel);padding:.8rem .9rem}
.rank h4{margin:0 0 .5rem;font-size:.8rem;font-family:ui-monospace,Menlo,Consolas,monospace;
letter-spacing:.06em;color:var(--ink-3);text-transform:uppercase}
.rank ol{margin:0;padding-left:1.4rem;font-family:ui-monospace,Menlo,Consolas,monospace;
font-size:.82rem;line-height:1.9;color:var(--ink-2)}
.rank ol li{margin:0}
.rank ol li b{color:var(--acc)}
.rank .win{border-top:3px solid var(--acc)}
</style>
</head>
<body>
<div class="wrap">
<header class="masthead">
<p class="eyebrow">LLM 基礎 · 文件十八</p>
<h1>程式碼任務與工程 agent</h1>
<p class="sub">
程式碼是目前 LLM 最大的應用類別,也是唯一一個<strong>自帶客觀驗證訊號</strong>的類別——
測試綠就是綠。這一份講怎麼用那個訊號,以及它的兩個漏洞:
<strong>agent 可以改測試</strong>,而<strong>審查容量不會跟著產出一起變大</strong>。
前者用執行層的護欄堵,後者是導入時真正的工程問題。
</p>
<p class="up">
前置:<a href="./09-measure.html">文件九</a>分頁 01(讀懂公開評測)、
<a href="./11-retrieval.html">文件十一</a>分頁 06(結構化與圖)、
<a href="./14-agent-ops.html">文件十四</a>(軌跡評估、權限分層、harness 骨架)。
這一份是<strong>應用層</strong>:把文件十四那套通用的 agent 工程,
放進一個有測試、有編譯器、有 code review 流程的具體環境裡。
</p>
</header>
<div class="tabnav">
<div class="navtop">
<p class="hint">↓ 點這裡切換分頁</p>
<a class="back" href="../index.html">← 回系列索引</a>
</div>
<div class="tabs" role="tablist" aria-label="分頁">
<button role="tab" id="tab-a" aria-controls="p-a" aria-selected="true"><span class="n">01</span>五種任務</button>
<button role="tab" id="tab-b" aria-controls="p-b" aria-selected="false" tabindex="-1"><span class="n">02</span>讀懂 SWE-bench</button>
<button role="tab" id="tab-c" aria-controls="p-c" aria-selected="false" tabindex="-1"><span class="n">03</span>倉庫與定位</button>
<button role="tab" id="tab-d" aria-controls="p-d" aria-selected="false" tabindex="-1"><span class="n">04</span>驗證迴圈</button>
<button role="tab" id="tab-e" aria-controls="p-e" aria-selected="false" tabindex="-1"><span class="n">05</span>審查瓶頸</button>
</div>
</div>
<!-- ══════════ 五種任務 ══════════ -->
<div role="tabpanel" id="p-a" aria-labelledby="tab-a" tabindex="0">
<div class="panel-intro col">
<h2>五種程式碼任務,五種帳</h2>
<p>
「AI 寫程式」不是一件事,是五件。它們的<b>輸入規模、驗證方式、
失敗成本</b>差好幾個數量級,而混在一起談是這個題目上最常見的錯誤。
這一頁先把它們分開,因為後面每一頁的答案都取決於你在講哪一種。
</p>
</div>
<div class="obj">
<div>
<h3>前置</h3>
<ul>
<li><a href="./09-measure.html">文件九</a>分頁 01——讀懂公開評測</li>
<li><a href="./14-agent-ops.html">文件十四</a>分頁 01——軌跡評估</li>
</ul>
</div>
<div>
<h3>學完能做什麼決定</h3>
<ul>
<li>把「導入 AI 寫程式」拆成五種任務,分別評估</li>
<li>判斷哪幾種適合讓 agent 自跑,判準是驗證成本而不是難度</li>
<li>看懂補全類工具的接受率,並知道它為什麼會誤導</li>
</ul>
</div>
</div>
<div class="stage">
<div class="stage-n">01</div>
<div class="stage-body col">
<h2>五種任務</h2>
<div class="tbl-wrap">
<table>
<caption>依「驗證成本」排序,不是依難度</caption>
<thead><tr><th>任務</th><th>輸入規模</th><th>怎麼驗證</th><th>能不能自跑</th></tr></thead>
<tbody>
<tr><td class="k">行內補全</td><td>當前檔 + 幾個相關檔</td><td>使用者當場接受或拒絕</td><td class="good">是(人隱含在迴圈裡)</td></tr>
<tr><td class="k">單檔生成</td><td>一段規格</td><td>跑既有測試</td><td class="good">是</td></tr>
<tr class="hl"><td class="k">跨檔修改</td><td>整個 repo(要自己定位)</td><td>完整測試 + 人審</td><td>部分</td></tr>
<tr><td class="k">除錯</td><td>錯誤訊息 + repo</td><td class="good">重現 bug 的測試——最乾淨的訊號</td><td class="good">是(如果測試寫得出來)</td></tr>
<tr><td class="k">遷移/重構</td><td>整個 repo + 目標 API</td><td class="bad">測試 + 行為對照,而且測試常常也要改</td><td class="bad">否</td></tr>
</tbody>
</table>
</div>
<div class="note acc">
<span class="tag">為什麼用驗證成本排序</span>
<p>
agent 的價值<strong>全部來自「它能自己判斷做完了沒」</strong>。
能自動驗證的任務,agent 可以跑十輪自我修正;
不能自動驗證的,它跑十輪只是燒十倍的錢,然後交給你一份你還是要從頭看的東西。<br>
<em>難度不是判準——除錯往往比單檔生成難得多,但它的驗證訊號乾淨,所以更適合自跑。</em>
這和 <a href="./12-posttrain.html">文件十二</a>分頁 04 的 RLVR 是同一個道理:
<strong>可驗證的獎勵值錢,不是因為它容易,是因為它不用人。</strong>
</p>
</div>
</div>
</div>
<div class="stage">
<div class="stage-n">02</div>
<div class="stage-body col">
<h2>補全:一個容易衝高的指標</h2>
<div class="ex">
<span class="tag">範例 18.1</span>
<p class="q">接受率 26%,這是好還是不好?</p>
<div class="work"><b>三個原始數字</b>
建議顯示次數 100,000 / 天
接受率 <b>26%</b>
接受後 10 分鐘仍存活 <b>71%</b>
<b>真正留下來的</b>
100,000 × 0.26 × 0.71 = <b>18,460</b> 個 / 天
平均每個 18 token → <b>332,000 token / 天</b>
<b>但這不等於省了 332,000 token 的打字</b>
被接受的多半是樣板
(import、迴圈骨架、null 檢查、getter)
<span class="c">而開發者本來就打得很快</span>
<b>接受率為什麼容易衝高</b>
把建議變短 → 接受率一定上升
一個字的建議接受率可以到 60%+
<span class="c">→ 接受率單獨看沒有意義</span>
<b>要看的複合指標</b>
接受率 × 存活率 × 平均長度
= 0.26 × 0.71 × 18 = <b>3.32 token / 次建議</b>
<span class="c">這個數字不會因為把建議變短而變好</span>
<b>但最終還是要 A/B(文件九分頁 05)</b>
關掉一半人的補全兩週
量「完成同等 story point 的時間」
典型報告區間 <b>−5% 到 −25%</b>
<b>為什麼區間這麼寬</b>
取決於樣板密度
強型別 + 樣板多的語言 省得多
動態語言 + 高度抽象 省得少</div>
<p class="a">
<strong>接受率是一個「改指標就能變好」的指標,所以它不能單獨當 KPI。</strong>
<br><br>
複合指標(接受率 × 存活率 × 長度)擋掉了最明顯的作弊,
但它仍然只量「產出了多少字元」,不量「省了多少時間」——
而那兩件事在樣板密集的程式碼上關係很弱。
<br><br>
所以順序是:<em>複合指標用來做日常監控</em>(它每天都算得出來),
<em>A/B 用來做採購決策</em>(它一年做一次就夠)。
<strong>把 A/B 的結論套用到日常監控,或者拿日常監控去做採購決策,兩個都會錯。</strong>
</p>
</div>
<div class="decide">
<b>決定</b>
<strong>先分類,再談導入。</strong>
「要不要導入 AI 寫程式」不是一個可以回答的問題;
「要不要在補全上導入」和「要不要讓 agent 做跨檔修改」是兩個完全不同的決定,
風險差兩個數量級。<br>
<strong>從驗證成本最低的兩種開始</strong>(補全、除錯),
它們的失敗成本低、訊號乾淨、而且不需要改變任何流程。
<em>跨檔修改和遷移要等到你有分頁 05 那套審查容量之後再談。</em>
</div>
<div class="summary">
<h3>分頁小結</h3>
<ol>
<li><b>五種任務不是一件事</b>:補全、單檔生成、跨檔修改、除錯、遷移重構。</li>
<li><b>用驗證成本排序,不用難度</b>——agent 的價值全在「能自己判斷做完了沒」。</li>
<li><b>除錯的訊號最乾淨</b>:一個能重現 bug 的測試就是完整的驗證。</li>
<li><b>接受率把建議變短就會上升</b>,要看接受率 × 存活率 × 長度。</li>
<li><b>複合指標做日常監控,A/B 做採購決策</b>,兩者不能互換。</li>
<li><b>從補全與除錯開始</b>,跨檔修改要等審查容量準備好。</li>
</ol>
</div>
<div class="quiz">
<span class="lbl">自我檢核</span>
<details>
<summary>供應商 A 的接受率 34%,供應商 B 是 22%。A 比較好嗎?</summary>
<div class="ans"><p>看不出來。<b>接受率和建議長度是反向相關的</b>——
A 可能只是建議比較短。要問的是平均建議長度和接受後的存活率,
算出「每次建議真正留下多少 token」。
<br><br>更根本的是:這兩個數字都不量時間。
如果 A 的建議短而頻繁,開發者要看更多次才拿到同樣的產出,
<b>注意力成本反而更高</b>。要決策就 A/B 量完成時間,
那是唯一直接對應到你在乎的東西的指標。</p></div>
</details>
<details>
<summary>為什麼除錯比單檔生成更適合讓 agent 自跑?</summary>
<div class="ans"><p>因為<b>驗證訊號更乾淨</b>。除錯有一個明確的成功條件:
那個能重現 bug 的測試從紅變綠,而且不能弄壞別的測試。
這是二元的、可程式化的、不需要人判斷。
<br><br>單檔生成的「做對了沒」則模糊得多——測試通過不代表寫法合理、
不代表符合團隊慣例、不代表沒有引入技術債。
<b>難度和適不適合自跑是兩回事</b>:除錯難,但可驗證。</p></div>
</details>
<details>
<summary>團隊說「我們導入了 AI 寫程式,效率提升 30%」。你要問什麼?</summary>
<div class="ans"><p>先問<b>哪一種任務</b>——五種的答案完全不同。然後問三件事:
<b>①</b> 這 30% 怎麼量的(自評?story point?週期時間?);
<b>②</b> 有沒有對照組,還是拿導入前後比(那會混進季節性與人員變動);
<b>③</b> 審查時間有沒有算進去(分頁 05 的範例 18.8 顯示它會漲三倍)。
<br><br>最後那一項最常被漏:<b>寫得快但審得慢,端到端的週期時間可能沒有改善</b>。</p></div>
</details>
</div>
</div>
</div>
</div>
<!-- ══════════ 讀懂 SWE-bench ══════════ -->
<div role="tabpanel" id="p-b" aria-labelledby="tab-b" tabindex="0" hidden>
<div class="panel-intro col">
<h2>讀懂 SWE-bench</h2>
<p>
程式碼 agent 的公開評測有一個和其他領域不同的性質:
<b>分數有一半是 harness 貢獻的,不是模型</b>。
所以同一個模型可以誠實地報出 30% 或 68%。
這一頁講那個區間怎麼來的,以及為什麼你還是需要自己的三十題。
</p>
</div>
<div class="obj">
<div>
<h3>前置</h3>
<ul>
<li><a href="./09-measure.html">文件九</a>分頁 01——榜單、污染、雜訊底線</li>
<li><a href="./14-agent-ops.html">文件十四</a>分頁 03——harness 六職責</li>
</ul>
</div>
<div>
<h3>學完能做什麼決定</h3>
<ul>
<li>看到一個 SWE-bench 分數時,問出四個讓它變得可比的問題</li>
<li>估出一份報告裡有多少比例是記憶而不是能力</li>
<li>用自己 repo 的近期 PR 建一份三十題的題庫</li>
</ul>
</div>
</div>
<div class="stage">
<div class="stage-n">01</div>
<div class="stage-body col">
<h2>SWE-bench 是什麼</h2>
<p>
從真實的公開 repo 抓 issue 與它對應的 PR:
給模型 issue 描述與 repo,要它產生一個 patch,
然後用<strong>那個 PR 附帶的測試</strong>來判定成功。
這個設計的優點是判準完全客觀——測試綠就是綠。
缺點是它<em>把「harness 有多好」和「模型有多好」混在同一個數字裡</em>。
</p>
<div class="ex">
<span class="tag">範例 18.2</span>
<p class="q">「我們在 SWE-bench Verified 上 65%」——這句話缺什麼?</p>
<div class="work"><b>① 哪個子集</b>
full(2,294 題) 最難
Verified(500 題,人工篩過) 通常高 <b>10–20 pp</b>
Lite(300 題) 更高
<span class="c">→ 不說子集的數字不可比</span>
<b>② 檔案定位是給的還是自己找的</b>
oracle(直接告訴它改哪個檔) <b>+15–25 pp</b>
BM25 檢索 基準
agent 自己探索 <b>−5 pp</b>,但最接近真實
<span class="c">→ 這一項的幅度比換模型還大</span>
<b>③ pass@1 還是 best-of-N</b>
同一個模型 pass@1 <b>42%</b>
pass@5 <b>68%</b>
<span class="c">→ 報 best-of-5 而不說,就是 26 pp 的差</span>
<span class="c"> (而且 best-of-N 要有辦法挑出對的那個,實務上未必有)</span>
<b>④ harness 給了什麼工具</b>
能不能跑測試 → 能的話可以自我修正(分頁 04)
有沒有 lint、能不能讀 stack trace
工具輸出有沒有做預算管理(文件十四分頁 03)
<span class="c">→ 這一項的影響可以超過換模型</span>
<b>綜合起來</b>
同一個模型,最寬鬆 vs 最嚴格的配置
可以報 <b>68%</b> 或 <b>30%</b>,兩個都不算造假</div>
<p class="a">
<strong>SWE-bench 的數字沒有 harness 描述,就等於沒有數字。</strong>
<br><br>
這和 <a href="./09-measure.html">文件九</a>分頁 01 講的是同一件事,
但程度更極端:一般評測裡 prompt 的影響是幾個百分點,
這裡 harness 的影響是<em>幾十個百分點</em>。
<br><br>
實用的做法是把四個問題當成一張檢核表。
<strong>對方答不出來的項目越多,那個數字的資訊量越低</strong>——
而多數行銷素材四項都答不出來。
</p>
</div>
</div>
</div>
<div class="stage">
<div class="stage-n">02</div>
<div class="stage-body col">
<h2>污染:題目就在訓練資料裡</h2>
<div class="ex">
<span class="tag">範例 18.3</span>
<p class="q">怎麼估一個分數裡有多少是「記得」而不是「會」?</p>
<div class="work"><span class="c">SWE-bench 的題目來自公開 repo,而模型也讀過那些 repo</span>
<b>檢查方法:把 context 全部拿掉</b>
只給 issue 描述,不給任何檔案內容
如果它能直接說出正確的檔名與函式名
→ 那是<b>記憶</b>,不是定位能力
<b>某模型在 Verified 500 題上的三段測量</b>
完全不給 context <b>8.4%</b>
給 BM25 檢索結果 <b>31%</b>
給 oracle 檔案 <b>48%</b>
<b>怎麼讀這三個數</b>
8.4% 是<b>記憶的下界</b>
<span class="c">(下界而非估計值:沒有 context 還答對,只可能是記得)</span>
報 31% 時,其中<b>至少 8.4 pp</b> 不是解題能力
→ 真實的「檢索+解題」貢獻 ≤ 22.6 pp
<b>時間切點檢查</b>
把題目按 PR 合併日期分兩堆
模型知識截止<b>之前</b>的 正確率 35%
截止<b>之後</b>的 正確率 24%
<span class="c">→ 11 pp 的差距,方向和污染假設一致</span>
<b>繞開的方法</b>
用你自己 repo 的<b>近期</b> PR 建題
30–50 題就有用
時間切點要晚於模型的知識截止</div>
<p class="a">
<strong>「完全不給 context」那一招是這裡最實用的東西</strong>——
它只要跑一次,就給你一個記憶的下界。
<br><br>
而時間切點那個檢查更好:它<em>不需要你相信任何關於訓練資料的說法</em>,
只要把題目按日期分堆再比。
如果兩堆的分數差很多,污染就是事實,不需要爭論。
<br><br>
最後那一段和 <a href="./10-multimodal.html">文件十</a>分頁 05 的
「30 張自己的圖」完全同構:
<strong>三十題你自己的、時間夠新的題目,資訊量高於任何公開榜</strong>。
在程式碼上它甚至更容易做——PR 和測試本來就在你的 repo 裡,
<em>建題幾乎只是寫一個腳本去撈</em>。
</p>
</div>
<div class="decide">
<b>決定</b>
<strong>用近三個月的 PR 建三十題,一天的事。</strong>
撈條件:有對應測試、改動涉及 1–5 個檔、測試在改動前是紅的。
這三個條件可以直接寫成查詢。<br>
<strong>然後每個月補五題、汰換最舊的五題</strong>——
這樣時間切點永遠比任何模型的知識截止新,污染問題自動消失。
<em>這也是<a href="./15-track.html">文件十五</a> M5 說的回流,只是在程式碼上特別便宜。</em>
</div>
<div class="summary">
<h3>分頁小結</h3>
<ol>
<li><b>SWE-bench 的分數混了 harness 和模型</b>,而 harness 的影響可以超過換模型。</li>
<li><b>四個必問</b>:哪個子集、檔案定位怎麼給、pass@1 還是 best-of-N、harness 有什麼工具。</li>
<li><b>同一個模型可以誠實地報 30% 或 68%</b>,取決於這四項怎麼配。</li>
<li><b>拿掉 context 還答對的比例,是記憶的下界</b>——某模型是 8.4%。</li>
<li><b>按 PR 日期分堆比分數</b>,不需要相信任何關於訓練資料的說法。</li>
<li><b>用自己 repo 近三個月的 PR 建三十題</b>,每月補五題汰換五題。</li>
</ol>
</div>
<div class="quiz">
<span class="lbl">自我檢核</span>
<details>
<summary>兩份報告,A 說 52%、B 說 41%。A 比較強嗎?</summary>
<div class="ans"><p>不能這樣比。先確認四件事:子集(Lite 和 full 差很多)、
檔案定位(oracle 比 agent 自己找高 20 pp 以上)、pass@1 還是 best-of-N、
以及 harness 給了什麼工具。
<br><br><b>只要有一項不同,這 11 pp 就沒有意義</b>——
它可能完全來自 A 用了 oracle retrieval 而 B 讓 agent 自己探索,
而後者才是你實際會遇到的情況。
<b>四項全部對齊之後才有資格比,而多數報告對不齊。</b></p></div>
</details>
<details>
<summary>你的 repo 是私有的,模型不可能訓練過。還需要擔心污染嗎?</summary>
<div class="ans"><p>你自己的題庫不用擔心,<b>但你拿來選型的公開榜單分數仍然被污染</b>。
所以順序是:用公開榜單<b>粗篩</b>(刷掉明顯不行的),
用你自己的三十題<b>做決定</b>。
<br><br>另外有一個私有 repo 也躲不掉的:<b>你用的開源套件是公開的</b>。
如果你的題目大量涉及某個熱門框架的用法,模型對那個框架的熟悉度
仍然來自訓練資料——這不算污染(那是真的有用的知識),
但它會讓分數在<b>換一個冷門框架時掉下來</b>,而你的題庫要涵蓋到那種情況。</p></div>
</details>
<details>
<summary>「拿掉 context 還答對 8.4%」為什麼說是下界而不是估計值?</summary>
<div class="ans"><p>因為沒有 context 還答對,<b>只可能</b>是記得——沒有別的解釋。
但反過來不成立:模型可能記得某題的答案,卻在沒有 context 時因為
不確定該回什麼格式而答錯,等給了 context 又「想起來」。
那種情況不會出現在 8.4% 裡,但它確實是記憶。
<br><br>所以真實的記憶貢獻<b>大於等於</b> 8.4%。
這個方向很重要:<b>它讓你可以安全地說「至少有這麼多不是能力」</b>,
而不需要對上界做任何猜測。</p></div>
</details>
</div>
</div>
</div>
</div>
<!-- ══════════ 倉庫與定位 ══════════ -->
<div role="tabpanel" id="p-c" aria-labelledby="tab-c" tabindex="0" hidden>
<div class="panel-intro col">
<h2>倉庫比 context 大很多</h2>
<p>
一個中型 repo 是 <b>400 萬 token</b>,而一次修改真正需要的是
<b>1.4 萬</b>。所以程式碼 agent 的核心問題從來不是 context 大小,
是<b>定位</b>——而定位做得爛的代價,會以 token 的形式在每一輪累積。
</p>
</div>
<div class="obj">
<div>
<h3>前置</h3>
<ul>
<li><a href="./11-retrieval.html">文件十一</a>分頁 06(結構化與圖)、分頁 07(長 context)</li>
<li><a href="./14-agent-ops.html">文件十四</a>分頁 03——工具輸出的預算</li>
</ul>
</div>
<div>
<h3>學完能做什麼決定</h3>
<ul>
<li>算出你的 repo 有多少 token,以及一次修改真正需要多少</li>
<li>在「全塞 / 檢索 / 自己探索 / 符號索引」之間選一個</li>
<li>估出 agent 探索的 token 成本,並知道最有效的那一刀砍在哪</li>
</ul>
</div>
</div>
<div class="stage">
<div class="stage-n">01</div>
<div class="stage-body col">
<h2>算一下 repo 有多大</h2>
<div class="ex">
<span class="tag">範例 18.4</span>
<p class="q">一個中型服務的 repo,塞得進 context 嗎?</p>
<div class="work"><b>repo 規模</b>
檔案數 2,400
程式碼行數 380,000
每行約 11 token(含空白與符號)
→ <b>4.18M token</b>
<b>塞得進嗎</b>
128k context 放得下 <b>3.1%</b>
1M context 放得下 <b>23.9%</b>
<span class="c">→ 即使 1M 也放不下四分之一</span>
<b>就算放得下也不該塞</b>
成本 4.18M × $3/M = <b>$12.5</b> 一次
品質 長 context 的注意力衰退
(文件十一分頁 07)
延遲 prefill 4.18M token,好幾十秒
<b>一次跨檔修改真正需要多少</b>
典型涉及 3–8 個檔
平均每檔 160 行 = 1,760 token
8 檔 = <b>14,080 token</b>
→ 是全 repo 的 <b>0.34%</b>
<b>所以問題長什麼樣</b>
不是「context 不夠大」
是「怎麼從 4.18M 裡找出那 0.34%」
<span class="c">→ 這是檢索問題,不是 context 問題</span></div>
<p class="a">
<strong>0.34%。這個數字是這一頁的全部。</strong>
<br><br>
它同時說明了兩件事:<em>擴大 context 解決不了問題</em>
(從 128k 到 1M 只是從 3% 到 24%,而你要的是 0.34%),
以及<em>定位做對的話成本極低</em>(14k token 是四毛錢)。
<br><br>
這和 <a href="./11-retrieval.html">文件十一</a>的整份主旨一樣,
只是程式碼有一個文字檢索沒有的優勢:
<strong>它有結構</strong>。函式定義在哪、被誰呼叫、型別怎麼流動,
這些都是可以<em>精確計算</em>的,不需要靠語意相似度去猜。
</p>
</div>
<div class="tbl-wrap">
<table>
<caption>四種給 context 的方式</caption>
<thead><tr><th>方式</th><th>怎麼做</th><th>問題</th></tr></thead>
<tbody>
<tr><td class="k">全塞</td><td>把 repo 倒進去</td><td class="bad">放不下,而且貴、慢、注意力衰退</td></tr>
<tr><td class="k">向量檢索</td><td>把程式碼切塊做 embedding</td><td>對「這個函式在哪被呼叫」這種問題很差——那不是語意問題</td></tr>
<tr><td class="k">agent 自己探索</td><td>給它 grep、read、ls</td><td>最靈活,但 token 成本高(範例 18.5)</td></tr>
<tr class="hl"><td class="k">符號索引 + 探索</td><td>預先建定義/呼叫圖,agent 查圖而不是掃檔</td><td class="good">第一輪就精準,token 掉一個數量級</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="stage">
<div class="stage-n">02</div>
<div class="stage-body col">
<h2>探索的 token 帳</h2>
<div class="ex">
<span class="tag">範例 18.5</span>
<p class="q">agent 自己探索一次跨檔修改,要花多少 token?</p>
<div class="work"><span class="c">基礎脈絡(系統提示+工具定義+issue)6k token</span>
輪 動作 工具輸出
1 grep 關鍵字 4,200
2 讀檔 A 1,800
3 grep 呼叫者 6,500
4 讀檔 B、C 3,400
5 跑測試(失敗) 2,100
6 讀 stack trace 指的檔 1,500
7 改檔、再跑測試 900
合計 <b>20,400</b>
<b>直接塞回脈絡(文件十四範例 14.5 的算法)</b>
第 n 輪輸入 = 6k + 前面所有工具輸出
輪 1 6,000
輪 2 10,200
輪 3 12,000
輪 4 18,500
輪 5 21,900
輪 6 24,000
輪 7 25,500
累計輸入 = <b>118,100 token</b>
<b>外部化(工具回 400 token 摘要 + id)</b>
輪 1 6,000 … 輪 7 8,400
累計 = <b>50,400 token</b>
<span class="c">→ 省 <b>57%</b></span>
<b>但真正的省法在第 1 輪</b>
預先建符號索引之後
第 1 輪直接回「定義在 A:142,被 B、C 呼叫」
<b>300 token</b> 取代 4,200
而且輪 3 那次 grep 呼叫者<b>整個不需要了</b>
→ 少兩輪、少 10,700 token 的工具輸出</div>
<p class="a">
<strong>外部化省 57%,符號索引省的是「輪次」——而輪次比 token 貴。</strong>
<br><br>
每少一輪就少一次模型呼叫、少一次往返延遲,
而且少一次<em>走錯方向的機會</em>。
範例裡輪 3 那次「grep 呼叫者」回了 6,500 token,
agent 要在裡面挑出相關的——那正是它最容易挑錯的地方。
<strong>符號索引把一次容易出錯的判斷,換成一次確定的查詢。</strong>
<br><br>
這是程式碼相對於一般 RAG 的結構性優勢:
<em>你可以用編譯器已經知道的事實,取代模型的猜測</em>。
能用 <code>ctags</code>、LSP、或編譯器 API 拿到的東西,
就不要讓 agent 用 grep 去找。
</p>
</div>
<div class="decide">
<b>決定</b>
<strong>先建符號索引,再談模型。</strong>
定義位置、呼叫關係、型別引用——這三樣用現成工具就拿得到,
一兩天的事,而它讓每一次任務少兩輪、少一萬 token、少一次挑錯的機會。<br>
<strong>向量檢索在程式碼上是輔助不是主力</strong>:
它適合「找出處理付款的那幾個模組」這種模糊查詢,
不適合「這個函式在哪被呼叫」——後者有精確答案,不該用相似度猜。
</div>
<div class="summary">
<h3>分頁小結</h3>
<ol>
<li><b>中型 repo 約 4.2M token</b>,128k 只放得下 3.1%,1M 也只有 23.9%。</li>
<li><b>一次修改真正需要 0.34%</b>——所以這是定位問題,不是 context 問題。</li>
<li><b>程式碼有結構</b>:定義、呼叫、型別可以精確計算,不需要用相似度猜。</li>
<li><b>工具輸出外部化省 57%</b>(118k → 50k),這是文件十四那一招的直接套用。</li>
<li><b>符號索引省的是輪次</b>,而輪次比 token 貴——少一輪就少一次走錯方向的機會。</li>
<li><b>向量檢索是輔助</b>:模糊查詢用它,精確關係不要用它。</li>
</ol>
</div>
<div class="quiz">
<span class="lbl">自我檢核</span>
<details>
<summary>有人提案「換成 1M context 的模型,就不用做檢索了」。怎麼回?</summary>
<div class="ans"><p>1M 只放得下這個 repo 的 <b>23.9%</b>,而你需要的是特定的 0.34%。
擴大 context 沒有解決「哪 0.34%」這個問題,只是讓你能塞進更多不相關的東西。
<br><br>而且塞進去要付三筆錢:$12.5 的 token 費、幾十秒的 prefill、
以及長 context 的注意力衰退(<a href="./11-retrieval.html">文件十一</a>分頁 07)。
<b>更大的 context 讓「不做檢索」變得可能,但沒有讓它變得正確。</b></p></div>
</details>
<details>
<summary>為什麼向量檢索對「這個函式在哪被呼叫」很差?</summary>
<div class="ans"><p>因為那不是語意問題,是<b>精確關係</b>。呼叫點的程式碼在語意上
可能和函式定義完全不像——一個是 <code>def process_payment(...)</code>,
另一個是 <code>result = svc.process_payment(order)</code>,
周圍的內容講的是完全不同的事。
<br><br>而編譯器或 LSP 對這個問題有<b>確定的答案</b>,
不需要任何相似度計算。<b>凡是有確定答案的問題,用相似度去猜都是退步</b>——
這是程式碼檢索和文字檢索最大的差別。</p></div>
</details>
<details>
<summary>你的 agent 常常改錯檔案。第一個該做什麼?</summary>
<div class="ans"><p>看它<b>第一輪拿到了什麼</b>。範例 18.5 裡第一輪的 grep 回了 4,200 token,
agent 要自己從裡面判斷哪個相關——那是最容易出錯的一步,
而且錯了之後後面每一輪都在錯的方向上。
<br><br>修法不是換更強的模型,是<b>讓第一輪的輸出更精準</b>:
符號索引直接回「定義在 A:142」,把一次判斷換成一次查詢。
<b>agent 走錯方向的問題,八成在第一輪就決定了。</b></p></div>
</details>
</div>
</div>
</div>
</div>
<!-- ══════════ 驗證迴圈 ══════════ -->
<div role="tabpanel" id="p-d" aria-labelledby="tab-d" tabindex="0" hidden>
<div class="panel-intro col">
<h2>驗證迴圈:測試是免費的獎勵訊號</h2>
<p>
程式碼有一個其他任務都羨慕的東西:<b>一個能自動判定成敗的訊號</b>。
這讓 agent 可以自我修正,也讓 RLVR 那一套用得上。
但它有一個很大的漏洞——<b>agent 可以改測試</b>。
這一頁講迴圈跑幾輪划算,以及怎麼把那個漏洞堵起來。
</p>
</div>
<div class="obj">
<div>
<h3>前置</h3>
<ul>
<li><a href="./12-posttrain.html">文件十二</a>分頁 04——可驗證的獎勵</li>
<li><a href="./14-agent-ops.html">文件十四</a>分頁 02——權限分層在執行層做</li>
</ul>
</div>
<div>
<h3>學完能做什麼決定</h3>
<ul>
<li>訂自我修正迴圈的輪數上限與放棄條件</li>
<li>量出「靠動測試通過」的比例,並選一組護欄</li>
<li>知道為什麼護欄要在執行層做,不是在 prompt 裡講</li>
</ul>
</div>
</div>
<div class="stage">
<div class="stage-n">01</div>
<div class="stage-body col">
<h2>迴圈跑幾輪划算</h2>
<div class="ex">
<span class="tag">範例 18.6</span>
<p class="q">「跑測試 → 看失敗 → 再改」,幾輪之後不划算?</p>
<div class="work"> 輪次 累計通過率 本輪增量 本輪 token
0(直接交) 31% — 18k
1 45% +14 pp +22k
2 52% +7 pp +25k
3 56% +4 pp +26k
4 58% +2 pp +27k
5 59% +1 pp +28k
<b>每 pp 的邊際成本</b>
輪 1 22k / 14 = <b>1.6k token / pp</b>
輪 3 26k / 4 = <b>6.5k token / pp</b>
輪 5 28k / 1 = <b>28k token / pp</b>
<span class="c">→ 第五輪的效率是第一輪的 1/17</span>
<b>而失敗的那 41% 呢</b>
多跑五輪不會讓它們變成成功
它們卡在「方向錯了」而不是「差一點」
→ 那五輪純粹在燒錢
<b>放棄條件</b>
連續兩輪「通過的測試數」沒有增加就停
<span class="c">注意是通過數,不是「有沒有全過」</span>
<b>三輪+放棄條件 vs 五輪硬跑</b>
累計 token 91k vs 146k <b>省 38%</b>
通過率 56% vs 59% 差 <b>3 pp</b>
<span class="c">→ 用 3 pp 換 38% 的成本</span></div>
<p class="a">
<strong>邊際回報遞減在這裡特別陡:第五輪的效率是第一輪的十七分之一。</strong>
<br><br>
而放棄條件比輪數上限更重要。
<em>「連續兩輪通過的測試數沒有增加」</em>抓的是「方向錯了」,
而那正好是多跑幾輪救不回來的那一類。
用通過<em>數</em>而不是「有沒有全過」,是因為前者是連續的、
能看出有沒有在進步;後者在成功之前一直是 0,沒有資訊。
<br><br>
這和 <a href="./15-track.html">文件十五</a>範例 15.6 的
「每人日的提升」是同一種思路:
<strong>不要問「還能不能更好」,要問「下一單位投入買到多少」。</strong>
</p>
</div>
</div>
</div>
<div class="stage">
<div class="stage-n">02</div>
<div class="stage-body col">
<h2>漏洞:它可以改測試</h2>
<div class="ex">
<span class="tag">範例 18.7</span>
<p class="q">200 個任務全部「通過」。逐一看 diff 呢?</p>
<div class="work"><b>分類 200 個通過的 diff</b>
只改程式碼 <b>141</b> (70.5%)
改程式碼 + 加新測試 <b>32</b> (16.0%) <span class="c">通常是好的</span>
改了既有測試的斷言 <b>19</b> ( 9.5%) <span class="c">← 有問題</span>
刪掉測試或加 skip <b>8</b> ( 4.0%) <span class="c">← 明確作弊</span>
<b>27 個(13.5%)是靠動測試通過的</b>
→ 真實通過率是 <b>86.5%</b> 而不是 100%
<b>三條護欄各擋掉多少</b>
① tests/ 唯讀(執行層擋寫入)
擋掉 <b>27 個全部</b>
代價:合法的測試更新也被擋,要有例外流程
② 覆蓋率不得下降
擋掉刪測試那 <b>8 個</b>
改斷言的 19 個<b>擋不到</b>——覆蓋率沒變