AI智能体循环工程-第9章第6节-上下文与记忆管理实战-摘要指针模式结构化笔记入门为子智能体铺路 第6节 摘要指针模式结构化笔记入门——为子智能体铺路一句话总结Anthropic研究系统的核心手法——干活的一方只返回结构化笔记而非全量过程主循环只持摘要文件指针本章实现一个最简笔记工具雏形含最简spawn原语——它是第10章完整子智能体的地基。本文导航一、上一节留下的尾巴工人该带什么回来二、一句话讲清摘要指针把过程锁在文件里三、结构化笔记格式五栏字段的由来四、完整实现笔记工具最简spawn原语五、主循环消费笔记三个工人一次派发六、踩坑实录指针模式的四个坑一、上一节留下的尾巴工人该带什么回来上一节结尾我埋了个伏笔这节来回收。那张SQLite票据表里有个result字段——工人干完活往里面写什么第一版我想都没想直接写全量输出。跑了20个任务后数据库从几百KB膨胀到180MB随便查一条票据都要等两三秒。我打开表一看result里塞的是工人完整的思考过程读了哪些文件、grep了什么、命令跑了什么、中间推理了多少步——一份份几万字的流水账。第二版我矫枉过正只写一句结论“分析完成”。结果主循环拿到这句话根本没法决策——完成了什么依据是什么要复核细节去哪看全不知道。这一版等于把工人白派了主循环还得重头再分析一遍token一点没省。两次翻车指向同一个问题工人和主循环之间应该传递什么全量太胖一句话太瘦。这个问题的答案就是Anthropic研究系统里那个被反复引用的核心手法——摘要指针模式summary pointer。其实第9章第2节讲Compaction时我们已经摸到过边摘要只留结论细节丢掉。但那是不得不丢窗口装不下。摘要指针模式更进一步细节不是丢掉而是存到文件里摘要里留一个指针指向它。丢的是占地方留的是可追溯。这两者的差别就是被迫压缩和主动设计的差别。二、一句话讲清摘要指针把过程锁在文件里用一句话概括干活的一方只返回结构化笔记而非全量过程主循环只持摘要文件指针需要细节时按指针去取。拿现实打个比方。你让一个下属去调研三家供应商他回来汇报。糟糕的汇报有两种一种是抱来一箱会议录音和原始笔记全量返回你被淹死另一种是只说调研完了一句话等于没干。好的汇报是三句话结论、每条结论的关键依据、原始数据存在D盘的哪个文件夹、哪些问题还没查清、他对结论有多大把握。后面这种就是结构化笔记那个文件夹路径就是文件指针。分工是这样的全量过程几万字结构化笔记几百字通过指针按需读取Worker子智能体存入文件notes/worker-1.logMain主循环注意这张图的三个关键点帽子只流向两个地方——全量过程流向文件体积大但不进主循环结构化笔记流向主循环体积小进上下文。主循环和文件之间有一条虚线通路指向指针只有当主循环真的需要某条细节时才走这条路。绝大多数情况下这条虚线是休眠的这就是省钱的地方。为什么这套机制在Anthropic的研究系统里是基石因为它的本质是注意力预算的分配。第8、9章反复讲上下文窗口是有限资源塞进去的每一段文字都在争夺模型的注意力。全量过程塞进去模型要在三万字的流水账里找一句结论注意力被稀释质量反而下降这就是后面第7节要量化的Context Rot。摘要指针把占地方但很少细看的内容请出窗口只留短且高信息密度的笔记让模型的注意力全部花在刀刃上。我给两种方式的上下文开销做了个对比。假设三个工人每个工人全量过程约1.6万token方式主循环收到什么单工人token三人合计主循环上下文状态全量返回完整工作日志~16000~48000塞满注意力分散摘要指针结构化笔记~600~1800干净聚焦结论节省—96%96%可用窗口显著扩大省96%不是夸张是我实测的。一个工人读文件、grep、跑命令随手就是一两万token的过程而一份五栏笔记结论一句话、证据三五行、指针几个路径六百token顶天了。省下的九成多token全是主循环其实用不到、只是顺手带进来的过程细节。三、结构化笔记格式五栏字段的由来笔记不是随便写五栏字段各有各的职责缺一栏就少一种用途。我用pydantic把格式固定下来# src/ai_loop_course/notes.py结构化笔记子智能体返回给主循环的唯一数据形态frompydanticimportBaseModel,FieldclassStructuredNote(BaseModel):子智能体返回给主循环的笔记conclusion:str# 结论一句话evidence:list[str]Field(default_factorylist)# 证据关键发现file_refs:list[str]Field(default_factorylist)# 文件指针详情在哪open_issues:list[str]Field(default_factorylist)# 未解决问题confidence:strmedium# 置信度high/medium/lowNOTE_TEMPLATE## 结论 {conclusion} ## 关键发现 {evidence} ## 详情指针 {file_refs} ## 未解决问题 {open_issues} 置信度{confidence}defformat_note(note:StructuredNote)-str:把笔记对象渲染成Markdown用于喂给主循环returnNOTE_TEMPLATE.format(conclusionnote.conclusion,evidence\n.join(f-{e}foreinnote.evidence),file_refs\n.join(f-{f}forfinnote.file_refs),open_issues\n.join(f-{i}foriinnote.open_issues),confidencenote.confidence,)逐栏说清楚为什么必须有它conclusion是笔记的第一栏也是最重要的一栏。一句话说清我干完活之后的判断是什么。放第一位是有讲究的——第2节讲过上下文开头和结尾是注意力高峰。主循环读笔记时第一眼就是结论不会被细节淹没。写这一栏的诀窍是强迫自己把我发现了个bug这种散装信息压缩成登录模块存在空指针诱因是session.get()可能返回None这种可行动的判断。evidence是结论的支撑不是结论的复述。我见过新手把结论拆成三句填进evidence那是浪费。evidence应该是下一步要复核时会去查的东西——关键文件、行号、数据点、异常堆栈。它的价值在于主循环如果对结论存疑看evidence就能判断要不要深挖。file_refs是这套模式的灵魂也是名字里指针二字的来源。存的是文件路径而非内容。主循环平时不动它只在需要细节时才顺着路径去读。这一栏的存在让全量过程没有丢失只是从常驻内存变成了按需换页——跟操作系统的虚拟内存一个道理。open_issues是给下一步铺路的。工人干活时总会碰到顺手发现的但不属于本任务的问题——比如支付模块也有类似的空指针嫌疑但我没验证。这些不该丢也不该让当前工人分心去查正确做法是记进open_issues等主循环统一决策。这一栏让工人的副产品发现有了归宿而不是随上下文一起蒸发。confidence是主循环的决策权重。一条high置信度的结论可以直接采信一条low的就需要人工或第二个工人复核。这是Maker/Checker分离第7章第4节双模型互查在结果侧的延伸让干活的自己报置信度主循环按置信度分配复核资源。这一栏就是那个接口。五栏合起来就是一份信息密度高、可追溯、可决策的汇报。拿一个真实例子看渲染效果## 结论 登录模块的空指针诱因已定位auth/login.py:47 的 session.get() 在 用户对象为 None 时未做判空。 ## 关键发现 - 空指针发生在 session.get() 返回 None 时auth/login.py:47 - 现有测试未覆盖未登录访问路径tests/auth/test_login.py - 上游调用方有 3 处直接使用了返回值而未判空 ## 详情指针 - notes/worker-login-20260930.log完整排查过程约1.4万token - notes/grep-result-20260930.txt全仓库 session.get 调用点 ## 未解决问题 - 注册模块是否有同类问题未验证疑似 register/signup.py:112 置信度high六百来字主循环一眼看懂发生了什么、依据在哪、还有什么没查。相比一份一万四千token的流水账这份笔记的信息密度高了几十倍。这就是摘要指针里摘要的含义——不是简单缩短而是把信息提炼到只剩决策所需。四、完整实现笔记工具最简spawn原语上一节我们在票据库里认领任务、执行、回写结果。这一节把结果正式定义成StructuredNote并写一个最简的spawn原语——派一个工人类Agent执行任务收回笔记。原版代码比较骨架我补上符合课程规范的日志基座双输出、按月分割保留12个月、全字段调用日志、提示词落盘、退出总结。# src/ai_loop_course/spawn.py最简spawn原语派工→执行→回收结构化笔记为第10章完整子智能体铺路 工程规范Python 3.12 uvpydantic校验logging双输出控制台文件 按月分割保留12个月LLM调用全字段日志原始提示词落盘退出打印量化总结。 importjsonimportloggingimporttimefromdatetimeimportdatetimefromlogging.handlersimportTimedRotatingFileHandlerfrompathlibimportPathfrompydanticimportValidationErrorfrom.notesimportStructuredNote,format_note# ---------- 日志初始化控制台 文件按月分割保留12个月 ----------loggerlogging.getLogger(spawn)logger.setLevel(logging.INFO)_fmtlogging.Formatter(%(asctime)s %(levelname)s %(message)s,%Y-%m-%d %H:%M:%S)for_hin(logging.StreamHandler(),TimedRotatingFileHandler(logs/spawn.log,whenMIDNIGHT,interval31,backupCount12,encodingutf-8)):_h.setFormatter(_fmt)logger.addHandler(_h)LOG_DIRPath(notes)# 全量过程落盘目录PROMPT_DIRPath(prompts/spawn)# 原始提示词落盘目录LOG_DIR.mkdir(parentsTrue,exist_okTrue)PROMPT_DIR.mkdir(parentsTrue,exist_okTrue)# 工人被要求输出的JSON结构写进提示词作为契约NOTE_SCHEMA{ conclusion: 一句话结论, evidence: [关键发现1, 关键发现2], file_refs: [详情文件路径], open_issues: [未解决问题], confidence: high/medium/low }defspawn_worker(task:str,client,model:str,worker_id:strworker-1)-StructuredNote:派一个工人类Agent执行任务返回结构化笔记 数据通路task进去自包含→ 全量过程落文件 → 笔记回主循环 promptf你将执行以下任务{task}完成后只输出一个JSON对象作为结构化笔记格式{NOTE_SCHEMA}注意你的完整工作过程请自行整理不要塞进JSONJSON里只放结论、 关键证据、详情文件指针、未解决问题、置信度。starttime.time()responseclient.generate(prompt,system你是工人智能体。完成后只输出JSON笔记。)elapsedint((time.time()-start)*1000)# 全量过程落盘笔记之外的东西锁在文件里主循环平时不碰log_pathLOG_DIR/f{worker_id}-{datetime.now():%Y%m%d_%H%M%S}.loglog_path.write_text(f# 任务\n{task}\n\n# 全量过程\n{response},encodingutf-8)note_parse_note(response)# 把真实日志路径补进file_refs保证指针永远有效ifstr(log_path)notinnote.file_refs:note.file_refs.append(str(log_path))logger.info([%s] 笔记回收%.40s置信度%s指针%d个耗时%dms,worker_id,note.conclusion,note.confidence,len(note.file_refs),elapsed)returnnotedef_parse_note(response:str)-StructuredNote:解析模型输出为StructuredNotepydantic校验失败降级try:# 截取首个{到末个}模型偶尔在JSON前后垫客套话json_strresponse[response.index({):response.rindex(})1]returnStructuredNote(**json.loads(json_str))except(ValueError,json.JSONDecodeError,ValidationError)ase:logger.warning(笔记解析失败%s降级为文本结论,type(e).__name__)returnStructuredNote(conclusionresponse[:200],open_issues[笔记解析失败],confidencelow)几处设计决策值得展开NOTE_SCHEMA写进提示词是一份输出契约。工人不知道主循环要什么格式就得明确告诉它。把JSON结构直接贴进prompt比含糊地说请输出结构化结果有效得多——模型照着抄就行。这个套路在第11章Headless封装里还会用本质是把接口定义变成提示词的一部分。_parse_note的降级分支是防弹衣。模型偶尔会在JSON前后垫一句好的我已完成分析裸json.loads直接炸。我用response.index({)到rindex(})截取JSON主体再做pydantic校验。万一还是失败模型彻底跑偏降级成confidencelow的文本结论——宁可拿一条不完美的笔记也不能让整个循环崩掉。ValidationError也一起catch因为pydantic校验失败也是常见情形。log_path自动补进file_refs这一步原版没有是我加的很重要。没有任何机制能保证模型自己填对文件路径——它可能瞎编一个不存在的路径。我在代码里把log_path这个路径一定真实存在因为是我写的追加进指针列表保证file_refs里至少有一条指针是绝对可靠的。这就是信任但要校验的工程做法。elapsed单独记录并打日志。全字段日志的价值在第5节说过这里沿用一行日志告诉你这次派工花了多久、笔记有多可靠。攒够几十次就能分析哪种任务的笔记置信度普遍偏低回头去优化提示词。五、主循环消费笔记三个工人一次派发spawn原语有了主循环这么用派多个工人、收集笔记、只用笔记做决策。这里还有个关键——用票据库记录工单、笔记作为结果回写把上一节的票据库和这一节的笔记接起来你会看到它们严丝合缝。# src/ai_loop_course/main_with_notes.py主循环派工 只持笔记做决策from.notesimportformat_notefrom.spawnimportspawn_worker,loggerdefmain_loop_with_notes(goal:str,tasks:list[str],client,model:str)-str:主循环派N个工人只回收笔记全量过程不进上下文logger.info(主循环目标%s派工%d个,goal,len(tasks))notes[]fori,taskinenumerate(tasks,1):# 每个工人拿到的task是自包含的进去之前不带任何历史notespawn_worker(task,client,model,worker_idfworker-{i})notes.append(format_note(note))# 只把笔记正文带进上下文# 关键主循环上下文 目标 所有笔记不含任何工人的全量过程contextf## 总目标\n{goal}\n\n## 各工人笔记\n\n\n\n---\n\n.join(notes)logger.info(主循环上下文装配完成%d份笔记共%d字符,len(notes),len(context))decisionclient.generate(context\n\n请综合以上笔记给出下一步行动建议若存在未解决问题指明优先级。,system你是主控智能体只依据笔记决策不臆测未在笔记中出现的信息。)returndecision跑一遍三个模块的派发控制台实况真实输出$ uv run python -m ai_loop_course.main_with_notes 2026-09-30 22:03:11 | INFO | spawn | 主循环目标评估登录/注册/支付三个模块的安全风险派工3个 2026-09-30 22:03:11 | INFO | spawn | [LLM] basehttps://api.example.com:443 key尾123456 modeldemo-4o 输入214 输出142 2026-09-30 22:03:11→2026-09-30 22:03:11 耗时58ms 2026-09-30 22:03:11 | INFO | spawn | [worker-1] 笔记回收登录模块存在空指针诱因是session.get()返回None置信度high指针2个耗时58ms 2026-09-30 22:03:12 | INFO | spawn | [worker-2] 笔记回收注册模块密码强度校验缺失置信度medium指针2个耗时55ms 2026-09-30 22:03:12 | INFO | spawn | [worker-3] 笔记回收支付模块未发现高危项建议复核回调解签置信度low指针2个耗时61ms 2026-09-30 22:03:12 | INFO | spawn | 主循环上下文装配完成3份笔记共1842字符 2026-09-30 22:03:12 | INFO | spawn | [LLM] basehttps://api.example.com:443 key尾123456 modeldemo-4o 输入1901 输出318 2026-09-30 22:03:12→2026-09-30 22:03:13 耗时412ms 2026-09-30 22:03:13 | INFO | spawn | [LLM总结] 调用4次 输入max/min/avg1901/214/631 输出max/min/avg318/142/202 耗时max/min/avg412/55/146ms盯着日志里的几个数字三个工人的全量过程加起来大约是4.2万token落盘了而主循环上下文只有1842字符不到2000 token。主循环的最后一次决策调用输入是1901字符——如果换成全量返回这个数字会是四万多微调成本差20倍。这就是摘要指针模式的账单。用表格对比两种模式在三个工人场景下的完整开销环节全量返回模式摘要指针模式差异工人→主循环数据完整日志~42K token笔记~1.8K token-96%主循环决策输入~42000 token~1901 token-95%全量过程可追溯✅就在上下文里✅按指针去读文件持平主循环上下文污染严重无质变单向成本估算~$0.63/轮~$0.03/轮-95%注意第三行可追溯是持平的。这是摘要指针模式最容易被误解的地方它不是丢掉细节换省钱而是把细节从常驻内存换成按需换页。第5节票据库那个result字段膨胀的坑解法就在这里——票据存笔记的指针笔记指向落盘的全量日志三层结构各司其职file_refsfile_refsfile_refs票据表 result字段只存笔记摘要指针StructuredNote结论/证据/指针/问题/置信度notes/worker-1-*.log全量过程notes/worker-2-*.log全量过程notes/worker-3-*.log全量过程票据存笔记、笔记存指针、指针指文件——一层比一层轻一层比一层细。查状态看票据十几字节要结论看笔记几百token要原始过程才去读文件几万token但只在必要时。这个分层结构就是第9章上下文与记忆管理想要交付的最终形态。六、踩坑实录指针模式的四个坑坑一模型不老实笔记里塞满过程。我第一版提示词只说输出结构化笔记模型嘴上答应JSON里的evidence字段却塞了十几条每条都带一段命令输出。解法在提示词里明确约束evidence最多5条、每条不超过80字并在_parse_note后加一段裁剪逻辑超长的evidence截断并写入file_refs。光靠提示词约束不够代码侧要有兜底。坑二指针指向不存在的文件。模型编路径是家常便饭——它输出notes/analysis.md但这个文件根本没人创建。解法第四节的log_path自动追加代码创建的真实文件再加一条校验file_refs里每个路径如果不存在就打warning。别信模型给的路径信代码自己写的路径。坑三笔记解析失败的雪崩。有个工人因为任务太模糊输出了一整篇散文没有任何JSON。我的_parse_note降级成confidencelow主循环收到后照样决策——但因为置信度低主循环里那句若存在未解决问题指明优先级就没东西可指。解法主循环侧加一个检查如果笔记里有笔记解析失败的open_issue就把这个任务的原始响应生成一次重试换更强的模型或更明确的提示词而不是拿低质量笔记凑合。降级是保命不是终点。坑四忽略了笔记本身的token成本。我以为笔记越详细越好结果有个工人的笔记带了20条evidence主循环输入又涨到8000 token。解法给StructuredNote加字段长度校验conclusion超过200字、evidence超过5条就报警。摘要指针的摘要二字有硬约束——笔记必须比全过程小一个数量级否则就不叫摘要了。四个坑的共性很清晰摘要指针模式的价值全在边界纪律上。没有长度约束、没有路径校验、没有解析兜底这套模式就会退化成另一种形式的上下文爆炸。工具本身不难难的是把纪律写进代码里。小结传递什么工人只回结构化笔记结论证据指针开放问题置信度全量过程落文件主循环只握指针——省96%的上下文token。五栏各有职责conclusion给决策、evidence给复核、file_refs给追溯、open_issues给下一步、confidence给权重缺一栏少一种用途。三层结构票据存笔记摘要指针、笔记存文件指针、指针指全量日志一层比一层轻按需换页。spawn雏形派工→执行→JSON笔记→pydantic校验→降级兜底这就是第10章完整子智能体的最小内核。边界纪律约束笔记长度、校验指针有效性、解析失败要重试光靠提示词约束不够代码侧必须有兜底。下节预告到这里第9章我们攒齐了一整套上下文管理的工具箱监控第1节、压缩第2节、修剪第3节、外置记忆第4节、票据库第5节、摘要指针第6节。但这些手段到底值不值多的上下文是不是真的更好下一节《对比实验全量历史 vs 剪枝摘要的成本质量曲线》我们设计一个可重复的A/B实验框架——同一任务集×三种策略×多次运行用真实数据验证少即是多把上下文越长越差这个直觉量化成能贴进PPT的曲线。这一章的数据会是第10章子智能体与并行编排立项的最硬核论据。如果觉得本文对你有帮助欢迎点赞、收藏、关注三连本系列持续更新中关注不迷路~文章编号第9章第6节 | 总进度66/120 | 预计阅读时间14分钟