-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_pdf.py
More file actions
507 lines (469 loc) · 29.9 KB
/
Copy pathgenerate_pdf.py
File metadata and controls
507 lines (469 loc) · 29.9 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
# -*- coding: utf-8 -*-
"""生成「姚顺雨 ReAct 与 SWE-agent 概念介绍」PDF。"""
import os
import sys
import hashlib
# 接入 pdf 技能脚本目录以使用 install_font_fallback
PDF_SKILL_DIR = r"C:\Users\22771\.zcode\cli\plugins\cache\zcode-plugins-official\document-skills\0.1.0\skills\pdf"
_scripts = os.path.join(PDF_SKILL_DIR, "scripts")
if _scripts not in sys.path:
sys.path.insert(0, _scripts)
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.styles import ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, PageBreak, KeepTogether,
Table, TableStyle, HRFlowable, CondPageBreak,
)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.pdfmetrics import registerFontFamily
# ━━ Color Palette (auto-generated by pdf.py palette.generate) ━━
# Intent: neutral | Mode: minimal | Harmony: split_complementary
ACCENT = colors.HexColor('#4925b7')
TEXT_PRIMARY = colors.HexColor('#232220')
TEXT_MUTED = colors.HexColor('#847f77')
BG_SURFACE = colors.HexColor('#e3e0dc')
BG_PAGE = colors.HexColor('#f5f4f2')
LINK_COLOR = colors.HexColor('#3b5bdb')
TABLE_HEADER_COLOR = ACCENT
TABLE_HEADER_TEXT = colors.white
TABLE_ROW_EVEN = colors.white
TABLE_ROW_ODD = BG_SURFACE
# ━━ Font Registration ━━
# 本机 Windows 字体路径(技能默认的 Linux 路径在本机不存在)
pdfmetrics.registerFont(TTFont('Microsoft YaHei', r'C:\Windows\Fonts\msyh.ttc'))
pdfmetrics.registerFont(TTFont('SimHei', r'C:\Windows\Fonts\simhei.ttf'))
pdfmetrics.registerFont(TTFont('Times New Roman', r'C:\Windows\Fonts\times.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSans', r'C:\Windows\Fonts\times.ttf')) # 占位
registerFontFamily('Microsoft YaHei', normal='Microsoft YaHei', bold='Microsoft YaHei')
registerFontFamily('SimHei', normal='SimHei', bold='SimHei')
registerFontFamily('Times New Roman', normal='Times New Roman', bold='Times New Roman')
# 中英混排自动 fallback
from pdf import install_font_fallback
install_font_fallback()
OUT_PDF = r"C:\Users\22771\ZCodeProject\姚顺雨_ReAct与SWE-agent概念介绍.pdf"
# ━━ Styles ━━
title_style = ParagraphStyle(
name='DocTitle', fontName='Microsoft YaHei', fontSize=28,
leading=38, textColor=TEXT_PRIMARY, alignment=TA_CENTER, wordWrap='CJK',
)
subtitle_style = ParagraphStyle(
name='DocSubtitle', fontName='Microsoft YaHei', fontSize=15,
leading=22, textColor=TEXT_MUTED, alignment=TA_CENTER, wordWrap='CJK',
)
meta_style = ParagraphStyle(
name='DocMeta', fontName='SimHei', fontSize=11,
leading=18, textColor=TEXT_MUTED, alignment=TA_CENTER, wordWrap='CJK',
)
h1_style = ParagraphStyle(
name='H1', fontName='Microsoft YaHei', fontSize=18,
leading=26, textColor=ACCENT, spaceBefore=20, spaceAfter=10,
alignment=TA_LEFT, wordWrap='CJK',
)
h2_style = ParagraphStyle(
name='H2', fontName='Microsoft YaHei', fontSize=13.5,
leading=20, textColor=TEXT_PRIMARY, spaceBefore=14, spaceAfter=6,
alignment=TA_LEFT, wordWrap='CJK',
)
body_style = ParagraphStyle(
name='Body', fontName='SimHei', fontSize=10.8,
leading=18.5, textColor=TEXT_PRIMARY, spaceBefore=0, spaceAfter=8,
alignment=TA_LEFT, wordWrap='CJK', firstLineIndent=0,
)
body_indent_style = ParagraphStyle(
name='BodyIndent', parent=body_style, leftIndent=16,
)
bullet_style = ParagraphStyle(
name='Bullet', fontName='SimHei', fontSize=10.8,
leading=18, textColor=TEXT_PRIMARY, spaceBefore=0, spaceAfter=4,
leftIndent=18, bulletIndent=4, alignment=TA_LEFT, wordWrap='CJK',
)
quote_style = ParagraphStyle(
name='Quote', fontName='SimHei', fontSize=11,
leading=20, textColor=TEXT_PRIMARY, leftIndent=20, rightIndent=12,
spaceBefore=8, spaceAfter=8, alignment=TA_LEFT, wordWrap='CJK',
borderColor=ACCENT, borderWidth=0, borderPadding=0,
)
code_style = ParagraphStyle(
name='Code', fontName='Courier', fontSize=9.2,
leading=14, textColor=TEXT_PRIMARY, leftIndent=14, rightIndent=8,
spaceBefore=4, spaceAfter=8, alignment=TA_LEFT,
backColor=BG_SURFACE,
)
ref_style = ParagraphStyle(
name='Ref', fontName='SimHei', fontSize=9.6,
leading=15, textColor=TEXT_PRIMARY, leftIndent=8, spaceBefore=0, spaceAfter=4,
alignment=TA_LEFT, wordWrap='CJK',
)
caption_style = ParagraphStyle(
name='Caption', fontName='SimHei', fontSize=9,
leading=13, textColor=TEXT_MUTED, alignment=TA_CENTER, spaceBefore=3, spaceAfter=6,
)
toc_title_style = ParagraphStyle(
name='TocTitle', fontName='Microsoft YaHei', fontSize=18,
leading=26, textColor=ACCENT, spaceAfter=14, alignment=TA_LEFT, wordWrap='CJK',
)
toc_entry_style = ParagraphStyle(
name='TocEntry', fontName='SimHei', fontSize=10.8,
leading=22, textColor=TEXT_PRIMARY, alignment=TA_LEFT, wordWrap='CJK',
)
table_header_style = ParagraphStyle(
name='TH', fontName='Microsoft YaHei', fontSize=10.5,
leading=15, textColor=colors.white, alignment=TA_CENTER, wordWrap='CJK',
)
table_cell_style = ParagraphStyle(
name='TC', fontName='SimHei', fontSize=10,
leading=15, textColor=TEXT_PRIMARY, alignment=TA_LEFT, wordWrap='CJK',
)
table_cell_center_style = ParagraphStyle(
name='TCC', parent=table_cell_style, alignment=TA_CENTER,
)
def add_heading(text, level=0):
"""添加带书签锚点的标题。"""
style = h1_style if level == 0 else h2_style
key = 'h_%s' % hashlib.md5(text.encode('utf-8')).hexdigest()[:8]
p = Paragraph('<a name="%s"/>%s' % (key, text), style)
p.bookmark_name = text
p.bookmark_level = level
p.bookmark_text = text
p.bookmark_key = key
return p
def link(text, url):
return '<a href="%s" color="#3b5bdb"><u>%s</u></a>' % (url, text)
# ━━ Document setup ━━
doc = SimpleDocTemplate(
OUT_PDF, pagesize=A4,
leftMargin=0.95 * inch, rightMargin=0.95 * inch,
topMargin=0.9 * inch, bottomMargin=0.9 * inch,
title='姚顺雨 ReAct 与 SWE-agent 概念介绍',
author='Z.ai', creator='Z.ai',
subject='面向计算机系毕业生的 AI Agent 科普介绍',
)
story = []
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 标题页
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(Spacer(1, 90))
story.append(Paragraph('姚顺雨的 ReAct 与 SWE-agent', title_style))
story.append(Spacer(1, 6))
story.append(Paragraph('—— 用计算机系同学能听懂的话,讲清楚 AI 智能体(Agent)', subtitle_style))
story.append(Spacer(1, 10))
story.append(HRFlowable(width=180, thickness=1.2, color=ACCENT,
hAlign='CENTER', spaceBefore=6, spaceAfter=18))
story.append(Spacer(1, 150))
story.append(Paragraph('从「推理 + 行动」的协同,到「为智能体设计接口」', meta_style))
story.append(Spacer(1, 6))
story.append(Paragraph('整理 · Z.ai 2026-06-24', meta_style))
story.append(PageBreak())
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 目录
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(Paragraph('目 录', toc_title_style))
toc_entries = [
'一、先认识作者:姚顺雨是谁',
'二、几个前置概念(CS 视角的快速对齐)',
'三、ReAct:让模型「边想边做」',
'四、SWE-agent:把循环用到「改代码」这件事上',
'五、把两个概念串起来:对比总结',
'六、参考资料来源',
]
for e in toc_entries:
story.append(Paragraph(e, toc_entry_style))
story.append(Spacer(1, 20))
story.append(HRFlowable(width='100%', thickness=0.6, color=ACCENT, spaceBefore=4, spaceAfter=14))
# 不强制分页:目录与第一章紧接在同一页,避免目录页下方大片空白
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 一、作者简介
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(add_heading('一、先认识作者:姚顺雨是谁', level=0))
story.append(Paragraph(
'在讲概念前,先简单交代一下作者背景,方便你理解他为什么做这些研究。', body_style))
story.append(Spacer(1, 4))
story.append(Paragraph('教育经历', h2_style))
story.append(Paragraph('• <b>清华姚班</b>本科(2015–2019),普林斯顿大学计算机系博士(2019–2024),'
'导师是 Karthik Narasimhan 教授。', bullet_style))
story.append(Paragraph('• 博士论文题目是《Language Agents: From Next-Token Prediction to Digital '
'Automation》(语言智能体:从「预测下一个词」到「数字自动化」)——这基本概括了他整个博士期间的研究主线。', bullet_style))
story.append(Spacer(1, 4))
story.append(Paragraph('代表成果与职业经历', h2_style))
story.append(Paragraph('• 代表作:<b>ReAct</b>(ICLR 2023,引用量过万)、<b>Tree of Thoughts</b>'
'(思维树)、<b>SWE-bench / SWE-agent</b>、<b>CoALA</b>(语言智能体概念框架)。', bullet_style))
story.append(Paragraph('• 2024 年加入 <b>OpenAI</b>,参与 Computer-Using Agent、Deep Research 等项目;'
'2025 年发表引发热议的《The Second Half》(AI 下半场)博文;现已回国加入<b>腾讯</b>任首席 AI 科学家。', bullet_style))
story.append(Paragraph(
'他研究的核心问题其实就一句话:<b>怎么让大语言模型(LLM)不再只是「聊天机器人」,而变成能动手干活的「智能体(Agent)」。</b>'
'ReAct 和 SWE-agent 正是这条线上的两个里程碑。', quote_style))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 二、前置概念
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(add_heading('二、几个前置概念(CS 视角的快速对齐)', level=0))
story.append(Paragraph(
'讲 ReAct 之前,先把几个词对齐一下,因为你可能没跟过 NLP 这几年的进展。'
'我用你熟悉的类比来解释:', body_style))
pre_data = [
[Paragraph('<b>概念</b>', table_header_style), Paragraph('<b>你熟悉的类比</b>', table_header_style)],
[Paragraph('LLM(大语言模型)', table_cell_style),
Paragraph('一个超级强大的函数 f(text) → text,输入一段话、输出一段话。本质上是「给定前文,预测下一个词」。', table_cell_style)],
[Paragraph('Prompt(提示)', table_cell_style), Paragraph('给这个函数的输入参数。', table_cell_style)],
[Paragraph('Chain-of-Thought(思维链 CoT)', table_cell_style),
Paragraph('让模型「把推导过程写出来」再给答案,类似你在草稿纸上一步步算,而不是心算,能显著提高正确率。', table_cell_style)],
[Paragraph('Tool use / Acting(工具使用 / 行动)', table_cell_style),
Paragraph('让模型不只是输出文字,还能调用外部工具(搜索、查数据库、运行代码),拿到结果再继续。', table_cell_style)],
[Paragraph('Agent(智能体)', table_cell_style),
Paragraph('一个以 LLM 为「大脑」、能感知环境、做决策、执行动作、再根据反馈调整的循环系统。可理解为「以 LLM 为控制流的程序」。', table_cell_style)],
]
pre_table = Table(pre_data, colWidths=[1.55 * inch, 4.45 * inch], hAlign='CENTER')
pre_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), ACCENT),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, BG_SURFACE]),
('GRID', (0, 0), (-1, -1), 0.5, TEXT_MUTED),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 8),
('RIGHTPADDING', (0, 0), (-1, -1), 8),
('TOPPADDING', (0, 0), (-1, -1), 7),
('BOTTOMPADDING', (0, 0), (-1, -1), 7),
]))
story.append(Spacer(1, 8))
story.append(pre_table)
story.append(Spacer(1, 6))
story.append(Paragraph('表 1 AI Agent 领域核心概念与 CS 类比对齐', caption_style))
story.append(Spacer(1, 10))
story.append(Paragraph(
'关键张力在于:在 2022 年之前,「推理(reasoning,比如 CoT)」和「行动(acting,比如调工具)」是'
'<b>两条分开的研究路线</b>。ReAct 的贡献就是把它们缝合在一起。', body_style))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 三、ReAct
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(add_heading('三、ReAct:让模型「边想边做」', level=0))
story.append(Paragraph('论文信息', h2_style))
info_data = [
[Paragraph('<b>标题</b>', table_header_style),
Paragraph('ReAct: Synergizing Reasoning and Acting in Language Models', table_cell_style)],
[Paragraph('<b>作者</b>', table_header_style),
Paragraph('Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, Yuan Cao', table_cell_style)],
[Paragraph('<b>发表</b>', table_header_style),
Paragraph('ICLR 2023(arXiv: 2210.03629,2022 年 10 月首发)', table_cell_style)],
]
info_table = Table(info_data, colWidths=[0.9 * inch, 5.1 * inch], hAlign='CENTER')
info_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), ACCENT),
('TEXTCOLOR', (0, 0), (0, -1), colors.white),
('BACKGROUND', (1, 0), (1, -1), colors.white),
('GRID', (0, 0), (-1, -1), 0.5, TEXT_MUTED),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 8),
('RIGHTPADDING', (0, 0), (-1, -1), 8),
('TOPPADDING', (0, 0), (-1, -1), 6),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
]))
story.append(info_table)
story.append(Spacer(1, 12))
story.append(Paragraph('它要解决什么问题', h2_style))
story.append(Paragraph(
'想象你用 LLM 回答一个问题:「清华大学 2018 年的校长是谁?」', body_style))
story.append(Paragraph(
'• <b>纯推理(CoT)路线</b>:模型只能靠记忆里「挤」答案。训练数据里有就答对;没有或记错了,就会'
'<b>一本正经地胡说八道(幻觉,hallucination)</b>。它没法去查证。', bullet_style))
story.append(Paragraph(
'• <b>纯行动路线</b>:模型直接调搜索引擎拿到一堆网页,但它不会「想一想该查什么、查到的够不够、'
'要不要换关键词」,容易陷入无效操作,错误还会一路传播。', bullet_style))
story.append(Spacer(1, 4))
story.append(Paragraph(
'ReAct 的洞察是:<b>这两件事天生该配合着做。</b>你查资料的时候,不也是「想一想 → 查一下 → '
'看结果 → 再想想 → 再查」这样交替进行的吗?', body_style))
story.append(Paragraph('核心机制:Thought–Action–Observation 循环', h2_style))
story.append(Paragraph(
'ReAct 让模型按固定格式,交替产生三类内容:', body_style))
code_text = (
'Thought(思考): 我需要先知道现任校长是谁……<br/>'
'Action(动作): Search["清华大学 校长 2018"]<br/>'
'Observation(观察): <搜索引擎返回的网页摘要><br/>'
'Thought: 摘要里提到邱勇在 2015–2023 年任校长……<br/>'
'Action: Lookup["邱勇"]<br/>'
'Observation: <更细的信息><br/>'
'Thought: 确认了,2018 年是邱勇。<br/>'
'Action: Finish["邱勇"]'
)
story.append(Paragraph(code_text, code_style))
story.append(Paragraph('用计算机系的话翻译这三类内容:', body_style))
story.append(Paragraph(
'• <b>Thought</b> = 模型的内部「日志 / 调试输出」,即推理过程(reasoning trace)。它不被外部执行,'
'只是帮模型自己理清思路,也帮人类看懂它为什么这么做。', bullet_style))
story.append(Paragraph(
'• <b>Action</b> = 一次<b>对外部环境 / 工具的函数调用</b>(搜索、查表、跳转、Finish 等),有严格格式。', bullet_style))
story.append(Paragraph(
'• <b>Observation</b> = 工具返回的结果,作为下一轮的输入。', bullet_style))
story.append(Paragraph(
'• 这三者构成一个<b>循环(agent loop)</b>,直到模型发出 Finish 动作为止。', bullet_style))
story.append(Spacer(1, 4))
story.append(Paragraph(
'这本质上是把 LLM 当成一个<b>解释器在跑的、用自然语言写的控制流程序</b>:Thought 是注释和条件判断,'
'Action 是函数调用,Observation 是返回值。', quote_style))
story.append(Paragraph('为什么这个设计有效', h2_style))
story.append(Paragraph(
'论文在 HotpotQA 多跳问答、ALFWorld 文字游戏、WebShop 网购模拟等任务上做了实验,发现:', body_style))
story.append(Paragraph('• <b>协同 > 单干</b>:ReAct 比纯 CoT、纯 Acting 都好。推理帮它规划下一步该干嘛,'
'行动帮它拿到真实信息来支撑推理。', bullet_style))
story.append(Paragraph('• <b>减少幻觉</b>:关键事实从外部工具拿到,不是模型瞎编的。', bullet_style))
story.append(Paragraph('• <b>可解释性更好</b>:Thought 是白盒的,人能看见模型的「思考过程」,方便调试。', bullet_style))
story.append(Paragraph('• 也有个权衡:纯 CoT 在模型「本身就会」的任务上推理更强,而 ReAct 更适合需要外部知识 / '
'交互的任务。后来还有人把两者结合(先 ReAct,答不上来再回退到内部推理)。', bullet_style))
story.append(Paragraph('它的地位', h2_style))
story.append(Paragraph(
'ReAct 的意义在于<b>它确立了一个范式(paradigm)</b>。今天你看到的几乎所有 AI agent 框架——'
'LangChain、AutoGPT、各种「能上网能写代码的 AI 助手」——底层几乎都是 ReAct 这个「思考-行动-观察」循环。'
'它把 agent 从概念变成了一个可复用的工程模板。作者在 GitHub 上也建议:实际用就直接上 LangChain 的 '
'ReAct 实现,他的仓库只是教学用的最小实现。', body_style))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 四、SWE-agent
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(CondPageBreak(2.0 * inch))
story.append(add_heading('四、SWE-agent:把循环用到「改代码」这件事上', level=0))
story.append(Paragraph(
'理解了 ReAct,SWE-agent 就好懂了——它是 <b>ReAct 思想在「软件工程」这个垂直领域的强化版</b>。', body_style))
story.append(Paragraph('论文信息', h2_style))
swe_info = [
[Paragraph('<b>标题</b>', table_header_style),
Paragraph('SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering', table_cell_style)],
[Paragraph('<b>作者</b>', table_header_style),
Paragraph('John Yang, Carlos E. Jimenez, Alexander Wettig, Kilian Lieret, Shunyu Yao, Karthik Narasimhan, Ofir Press(普林斯顿 NLP 组)', table_cell_style)],
[Paragraph('<b>发表</b>', table_header_style),
Paragraph('NeurIPS 2024(arXiv: 2405.15793,2024 年 5 月)', table_cell_style)],
]
swe_info_table = Table(swe_info, colWidths=[0.9 * inch, 5.1 * inch], hAlign='CENTER')
swe_info_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), ACCENT),
('TEXTCOLOR', (0, 0), (0, -1), colors.white),
('BACKGROUND', (1, 0), (1, -1), colors.white),
('GRID', (0, 0), (-1, -1), 0.5, TEXT_MUTED),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 8),
('RIGHTPADDING', (0, 0), (-1, -1), 8),
('TOPPADDING', (0, 0), (-1, -1), 6),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
]))
story.append(swe_info_table)
story.append(Spacer(1, 8))
story.append(Paragraph(
'注:姚顺雨是共同作者之一,整个项目出自他所在的普林斯顿实验室。'
'它和姊妹工作 <b>SWE-bench</b> 配套:SWE-bench 是「考题」(从真实 GitHub 仓库收集的 issue + 测试用例,'
'用来考 AI 修 bug 的能力),SWE-agent 则是去「考试的考生」。', body_style))
story.append(Paragraph('它要解决什么问题', h2_style))
story.append(Paragraph(
'给 agent 一个真实软件仓库里的 GitHub issue,比如:', body_style))
story.append(Paragraph(
'「运行 python main.py --debug 时报 KeyError: \'config\',期望它能优雅降级。」', quote_style))
story.append(Paragraph(
'让 agent <b>自主</b>完成人类工程师会做的事:浏览仓库文件、找到相关代码、读懂逻辑、修改、跑测试、改对。'
'这比 ReAct 论文里的「调一下搜索」难多了——它要在一个<b>完整的代码仓库 + 终端</b>里操作。', body_style))
story.append(Paragraph(
'直接把 ReAct 套上去效果不好。作者发现,<b>瓶颈不在模型本身,而在「模型怎么和计算机交互」这件事上。</b>', body_style))
story.append(Paragraph('核心创新:ACI(Agent-Computer Interface,智能体-计算机接口)', h2_style))
story.append(Paragraph(
'这是 SWE-agent 最重要的概念,论文标题里的「Agent-Computer Interfaces」就是它。', body_style))
story.append(Paragraph(
'类比一下:人类程序员写代码靠的是 <b>IDE / 人机接口(HCI)</b>——有文件树、语法高亮、跳转定义、错误提示、'
'git diff,这些 HCI 设计得好,人写得又快又准。', body_style))
story.append(Paragraph(
'SWE-agent 说:<b>agent 也需要一套专门为它设计的「人机接口」,只不过对面不是人,是 LLM,所以叫 ACI。</b>'
'现成的 shell 和编辑器是给人用的,对 LLM 不友好(输出太杂、反馈格式不固定、容易让模型迷糊)。'
'于是他们专门设计了一套 ACI,给 agent 提供几个<b>格式干净、反馈明确</b>的工具命令,比如:', body_style))
story.append(Paragraph(
'• <b>find_file / search_dir / search_file</b>:在仓库里找文件和代码,返回结果规整成容易「读」的格式;', bullet_style))
story.append(Paragraph(
'• <b>open / goto / scroll</b>:浏览文件,<b>每次只显示一个固定大小的「窗口」</b>(就像人一次只看一屏),'
'避免模型被几千行代码淹没;', bullet_style))
story.append(Paragraph(
'• <b>edit</b>:编辑代码,并<b>强制打开修改后的文件让模型核对</b>,防止「改完就忘」;', bullet_style))
story.append(Paragraph(
'• <b>python</b>:运行代码 / 测试,把终端输出处理干净再喂回去。', bullet_style))
story.append(Spacer(1, 4))
story.append(Paragraph('每个工具的输入输出都被精心设计成「对 LLM 友好」的形式。设计原则包括:', body_style))
story.append(Paragraph('• <b>简洁、明确</b>:输出要紧凑、格式固定;', bullet_style))
story.append(Paragraph('• <b>窗口化、可操作</b>:信息要分块,agent 能逐块操作(像人滚动屏幕);', bullet_style))
story.append(Paragraph('• <b>反馈要让人(模型)「看见后果」</b>:任何改动之后,立刻把结果反馈给模型确认。', bullet_style))
story.append(Spacer(1, 4))
story.append(Paragraph(
'这套设计的思路其实非常「工程化」:<b>你不是在等一个更聪明的模型出现,而是在改「API 的设计」</b>'
'——就像给一个新来的实习生设计一套好用的内部工具和规范,他就能干得更好。作者甚至发现,'
'用更弱的模型 + 更好的 ACI,能打过更强的模型 + 差的接口。', quote_style))
story.append(Paragraph('效果', h2_style))
story.append(Paragraph(
'在 SWE-bench 上,SWE-agent 当时拿到了 SOTA(最先进水平),能自主解决相当比例的真实 GitHub issue。'
'更重要的是它证明了一个论点:<b>做 agent,除了堆模型能力,「接口设计」是被严重低估的杠杆。</b>', body_style))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 五、对比总结
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(CondPageBreak(2.2 * inch))
story.append(add_heading('五、把两个概念串起来:对比总结', level=0))
cmp_data = [
[Paragraph('<b></b>', table_header_style),
Paragraph('<b>ReAct</b>', table_header_style),
Paragraph('<b>SWE-agent</b>', table_header_style)],
[Paragraph('解决的问题', table_cell_center_style),
Paragraph('怎么让 LLM 边推理边行动(通用范式)', table_cell_style),
Paragraph('怎么让 LLM 自主完成软件工程任务(垂直应用)', table_cell_style)],
[Paragraph('核心机制', table_cell_center_style),
Paragraph('Thought–Action–Observation 循环', table_cell_style),
Paragraph('在 ReAct 式循环之上,重点设计了 ACI(接口)', table_cell_style)],
[Paragraph('关键贡献', table_cell_center_style),
Paragraph('提出并验证了「推理 + 行动协同」的范式', table_cell_style),
Paragraph('提出「为 agent 设计专属接口」这个被忽视的维度', table_cell_style)],
[Paragraph('地位', table_cell_center_style),
Paragraph('agent 领域的奠基性范式', table_cell_style),
Paragraph('agent 在软件工程领域的里程碑 + 工程方法论', table_cell_style)],
[Paragraph('关系', table_cell_center_style),
Paragraph('提供「骨架」(循环结构)', table_cell_style),
Paragraph('在骨架上针对「操作代码仓库」做深度优化', table_cell_style)],
]
cmp_table = Table(cmp_data, colWidths=[1.25 * inch, 2.55 * inch, 2.2 * inch], hAlign='CENTER')
cmp_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), ACCENT),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, BG_SURFACE]),
('GRID', (0, 0), (-1, -1), 0.5, TEXT_MUTED),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 7),
('RIGHTPADDING', (0, 0), (-1, -1), 7),
('TOPPADDING', (0, 0), (-1, -1), 7),
('BOTTOMPADDING', (0, 0), (-1, -1), 7),
]))
story.append(Spacer(1, 6))
story.append(cmp_table)
story.append(Spacer(1, 6))
story.append(Paragraph('表 2 ReAct 与 SWE-agent 对比', caption_style))
story.append(Spacer(1, 8))
story.append(Paragraph('一句话总结这条线索:', body_style))
story.append(Paragraph(
'<b>ReAct 回答了「agent 该怎么想、怎么做」(建立循环范式);SWE-agent 回答了「当 agent 要在一个复杂环境'
'(代码仓库 / 终端)里做事时,怎么给它设计一套好用的交互接口,让它干得又准又稳」。</b>', quote_style))
story.append(Paragraph(
'姚顺雨的研究主线就是把这些一步步做实——从「模型能推理」到「模型能行动」,再到「模型能在真实的数字世界里'
'自动化地完成专业工作」。这也是为什么他的博士论文标题是「从预测下一个词,到数字自动化」。', body_style))
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 六、参考资料来源
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
story.append(add_heading('六、参考资料来源', level=0))
story.append(Paragraph('ReAct', h2_style))
story.append(Paragraph('• ' + link('ReAct 论文(arXiv:2210.03629)', 'https://arxiv.org/abs/2210.03629'), ref_style))
story.append(Paragraph('• ' + link('ReAct 项目主页', 'https://react-lm.github.io/'), ref_style))
story.append(Paragraph('• ' + link('ReAct ICLR 2023 论文 PDF', 'https://openreview.net/pdf?id=WE_vluYUL-X'), ref_style))
story.append(Paragraph('• ' + link('作者代码仓库 ysymyth/ReAct', 'https://github.com/ysymyth/ReAct'), ref_style))
story.append(Paragraph('• ' + link('Google Research Blog 对 ReAct 的解读', 'https://research.google/blog/react-synergizing-reasoning-and-acting-in-language-models/'), ref_style))
story.append(Paragraph('• ' + link('Latent Space 对姚顺雨的访谈', 'https://www.latent.space/p/shunyu'), ref_style))
story.append(Paragraph('SWE-agent / SWE-bench', h2_style))
story.append(Paragraph('• ' + link('SWE-agent 论文(arXiv:2405.15793)', 'https://arxiv.org/abs/2405.15793'), ref_style))
story.append(Paragraph('• ' + link('SWE-agent 官方文档与架构介绍', 'https://swe-agent.com/latest/background/'), ref_style))
story.append(Paragraph('• ' + link('SWE-agent ACI 设计说明', 'https://swe-agent.com/latest/background/aci/'), ref_style))
story.append(Paragraph('• ' + link('NeurIPS 2024 正式版 PDF', 'https://proceedings.neurips.cc/paper_files/paper/2024/file/5a7c947568c1b1328ccc5230172e1e7c-Paper-Conference.pdf'), ref_style))
story.append(Paragraph('作者背景', h2_style))
story.append(Paragraph('• ' + link('姚顺雨个人主页', 'https://ysymyth.github.io/'), ref_style))
story.append(Paragraph('• ' + link('Google Scholar 主页', 'https://scholar.google.com/citations?user=qJBXk9cAAAAJ'), ref_style))
# ━━ Build ━━
doc.build(story)
print('PDF generated:', OUT_PDF)
print('Size (KB): %.1f' % (os.path.getsize(OUT_PDF) / 1024.0))