跳到主要内容

reading-notes — learning-langchain(边读边记,行号 = text/xx.txt:N)

Preface(text/03-fm-preface.txt,441 行)

  • L5: ChatGPT 2022-11-30 发布;两个月 1 亿月活,消费级应用最快采纳速度。
  • L9: 本书用 OpenAI API 做示例;LangChain 好处是换供应商不改代码。
  • L13: ML 两类算法:工程师直接写的 vs 从训练样本学出来的(工程师写的是「造算法的逻辑」)。
  • L15: LLM=专生成文本的生成式模型。
  • L17–23: LLM 与旧 ML 两点不同:训练数据量大得多、从头训很贵;更通用(同一个模型干摘要/翻译/分类)。
  • L25: 软件工程师的工作重心转向「怎么让 LLM 为我的用例工作」——这正是 LangChain 存在的理由。★全书主题句
  • L27: 2023 底出现 Claude/Bard(后改名 Gemini)。
  • L29: LangChain 起点:Harrison Chase 2022-10-22 第一个 GitHub commit。核心洞察:最有意思的 LLM 应用要把 LLM 和「其他计算源或知识源」组合。
  • L31–37: 球的算术题(1234 个球分给 123 人)+ calculator 组合示例——LangChain 的动机案例。
  • L41–47: LLM=接收文本预测生成类人文本,像手机自动补全的极端版。Large=GPT-3 175B 参数、45TB 文本训练。参数=控制每个神经元输出及其连接权重的数。语言模型=神经网络(neurons 的组合输出)。
  • L49: 英文主导训练数据所以英文更好;BLOOM 多语;研究发现语义理解可跨语言迁移(脚注2 EMNLP 2023 论文)。
  • L53–57: 「英格兰首都是___」→ London;本质是按前文词序列估计后续词的概率。
  • L61–83: token 概念+cl100k tokenizer 示例(good morning dearest friend → 5 个 token,含 ID);1 token≈4 字符≈0.75 词;常见词单 token,生僻词多 token。
  • L85: 驱动引擎=transformer 架构(脚注3 Vaswani 2017 Attention Is All You Need)。设计意图:把每个词放在与其他所有词的关系里理解,句义=各部分联合意义。
  • L87: 训练语料里 England 与 France/US/China 出现在相似位置,capital 与 London/Paris 共现 → 所以能预测 London。
  • L89: prompt 定义;prompt engineering 最佳实践。
  • L91–99: pretrained base LLM:自监督(self-supervised),两种造对法:预测下一个词 / 挖掉中间一个词(MLM)。base 模型难用:要补全式提示而非问答式(L101)。
  • L103–115: instruction-tuned=在预训练之上继续训(fine-tuning):任务特定 QA 数据集(人工拼的、必然小得多)+ RLHF(用用户反馈增强偏好对)。「What is the capital of England?」可用。
  • L117–127: dialogue-tuned/chat:多轮对话数据 + chat format(system/user/assistant 角色)——防 jailbreak(用户输入与指令混淆,可能泄露系统提示里的机密)。
  • L129–131: fine-tuned LLM=开发者自己拿专有数据集再调;性能↑通用性↓。
  • L133: ★口径声明:本书后文说 LLM 都指 instruction-tuned,chat model 指 dialogue-tuned。
  • L137–149: prompting primer;OpenAI Playground 教程;L151 temperature 调到 0.00——温度越低越确定性。
  • L155–170: zero-shot:第 30 任总统 Calvin Coolidge 岳母去世时他几岁?gpt-3.5-turbo 答 48 岁(不对,答案差)。★全书贯穿例
  • L172–202: CoT:「Think step by step.」→ 输出 5 步推理但事实错(妻子母亲生死年份错),最终 85 也不对。
  • L204–230: RAG:往 prompt 里塞相关 context(四条 Coolidge 相关文字)→ 得 54 岁,差 3(算术不行)。注意书中用语 context=检索到的相关文本段。
  • L232–256: tool calling:在 prompt 前置工具清单+用法描述+如何标记要用工具;解析输出并调用。zero-shot 工具调用选了不合适的工具(calculator,2023-1892 和 search)。★要点:每种技术单独用都不行,组合才灵。
  • L258–292: 三合一(RAG+CoT+tool calling)→ calculator,1929 - 1872 → 正确答案 57 岁。(注:L290 书里有笔误写成 1827)
  • L294–305: few-shot:给例题学新任务,不需要再训练;比 fine-tuning 灵活但弱;先试 few-shot 再 fine-tuning。静态 vs 动态(按查询挑最相关例子)。
  • L308–338: LangChain 为什么重要:28M 月下载、99k GitHub stars、72k+ 开发者社区。抽象=封装各提示技术的函数和类。提供:模型集成(统一接口)、prompt template(可存 Hub)、第三方工具集成、RAG 所需 embedding 模型/vector store/vector index 集成、agent 抽象(LangGraph 提供,CoT+tool calling=ReAct 论文首创)、memory(ch4)、组装。LangSmith=调试测试部署监控平台;LangGraph Platform=部署扩展 agent 平台(ch9/ch10)。
  • L340–362: 全书路线图:plain English 定制 chatbot → 接自己的文档(RAG)→ 记忆 → 规划与行动(agent、迭代)→ 人机协作(HITL 中断/授权/澄清)→ 上线部署(latency/reliability/security)+ 监控持续改进。
  • L364+: 版式约定/代码许可/联系方式等导航内容,跳过。

Chapter 1(text/04-ch01…,963 行)

  • L5–9: 构建好 LLM 应用的挑战=怎么构造发给模型的 prompt+怎么处理模型输出。本章讲 LangChain 积木与 LLM 概念的对应。
  • L13–45 「Why LangChain?」:不用 LangChain 也能做(用 provider SDK)。两个理由:① 预置常见模式(CoT/tool calling 等参考实现)——起步最快;② 可互换组件——每个组件遵循同一规范,provider 换了应用不用重写。例:OpenAI 与 Anthropic 的 chat message 格式微妙不兼容,同一个对话里混用两家会出问题;LangChain 抹平差异(L45)。★互操作性=LangChain 存在的理由之二
  • L25–31: 本书代码用 OpenAI(LLM/chat model)+OpenAI(embeddings)+PGVector(vector store),都可换(Anthropic/Ollama/Cohere/Weaviate/OpenSearch)。vector store 例子里 PGVector=Postgres 的向量扩展。
  • L47–53: 编排能力三条:所有主组件被 callbacks 系统埋点(instrumented)以可观测(more ch8);所有主组件同一接口;长时运行应用可中断/恢复/重试(more ch6)。★callbacks 指向第 8 章,interrupt 指向第 6 章
  • L107–115: 两个接口:LLM 接口(string 进 string 出)和 chat model 接口(多轮、带角色)。
  • L123–137: OpenAI(model="gpt-3.5-turbo").invoke("The sky is")→"Blue!"。
  • L141–153: 常配参数:model 名(能力/成本/速度权衡);temperature(低=更可预测,如 0.1;高=更有创造性 0.9;结构化输出适合低温、创作适合高温);max_tokens(限输出大小和成本,太低会截断)。其余参数各家不同。
  • L155–169: 三角色 system(指令)/user(查询)/assistant(模型生成)。chat 接口存在的原因=provider 就是这么分的。
  • L195–247: 四种消息类型 HumanMessage/AIMessage/SystemMessage/ChatMessage(任意 role)。SystemMessage「回答带三个感叹号」→「Paris!!!」例子:模型遵守了用户问题里没有的指令→可预配置应用行为(L249)。
  • L251–269: 复用 prompt:RAG 式 prompt 样板「根据 context 回答,答不了就说 I don't know」;Challenge=context/question 动态化。
  • L273–341: PromptTemplate:f-string 语法的 {placeholder};模板=菜谱(recipe),格式化后得到静态 prompt。ChatPromptTemplate 按 role 给动态输入(from_messages [('system',…),('human','Context: {context}'),('human','Question: {question}')])。
  • L410–411: 输出:Hugging Face transformers / openai(openai 库)/ cohere(cohere 库)都提供 LLM。
  • L539–600: 结构化输出(JSON):先定义 schema;with_structured_output(schema) 两件事:把 schema 转 JSONSchema 发给 LLM(LangChain 按各模型挑最好方式,function calling 或 prompting)+ 用 schema 校验返回的输出。Pydantic(Python)/Zod(JS)。例:pound of bricks vs feathers → {answer:"They weigh the same", justification:…}。
  • L602–634: output parser 两大功能:往 prompt 注入格式指令;解析校验文本输出成结构格式(去杂、修不完整输出、校验值)。CommaSeparatedListOutputParser:"apple, banana, cherry"→['apple','banana','cherry']。
  • L636–656: 关键问题:怎么组装?Runnable 统一接口:invoke(单进单出)/batch(多进多出)/stream(流式产出部分输出);内置 retry/fallback/schema/运行时可配置;Python 有 asyncio 版。§ 所有组件行为一致,学会一个就都会了
  • L705–741: 两种组合方式:imperative(直接调用)/declarative(LCEL)。表1-1:LCEL 自动获得并行/streaming/async;imperative 要自己写(yield/threads/Promise.all)。
  • L742–861: imperative 组合:@chain 装饰器(Python)/RunnableLambda(JS)给任意函数加上同一 Runnable 接口;要 stream 得自己 yield token(AIMessageChunk(content="Hugging")…);要 async 自己改写(ainvoke)。
  • L863–955: LCEL:声明式语言,| 操作符连接(Python)/(JS .pipe())。chatbot = template | model。编译成优化执行计划:自动并行、streaming、tracing、async,无需改代码。★tracing 第一次出现,接 LangSmith
  • L956–963: 小结:LLM 应用=一条链(model+prompt+可选 output parser);两种组合;命令式适合大量自定义逻辑,声明式适合拼装现成组件。Ch2 讲给 chatbot 外部数据。

Chapter 3(text/06-ch03…,1376 行)RAG Part II: 检索与对话

Chapter 2 补记(细节)

  • L1034–1046 索引优化动机:朴素切分+嵌入在含图表数据源上检索不稳、幻觉偏多;三策略:
    • MultiVectorRetriever(L1040–1240):解耦「给 LLM 合成答案用的完整文档」与「给检索器用的引用」。LLM 批量总结每块(batch max_concurrency 5)→摘要带 doc_id 存向量库→原块存 docstore(InMemoryStore)→查询命中摘要后按 doc_id 取回完整原始块。适合表格:嵌表摘要保入口、整表送答案合成。
    • RAPTOR(L1242–1247):k-NN 只能答指向具体事实的低层问题,跨文档的高层问题答不了;递归聚类→总结簇→再聚类,长出摘要树;摘要+原文一起索引。脚注2 Sarthi et al. ICLR 2024。
    • ColBERT(L1250–1323):固定长度压缩丢细节、嵌进无关内容会引发幻觉;改为逐 token 上下文嵌入,query 每个 token 对所有文档 token 算相似取最大再求和。ColBERTv2 论文(脚注3),RAGatouille 库,Miyazaki 维基示例。
  • 小结预告 ch3。
  • L520 JS 笔误:await embeddings.embedDocuments 变量名错。L290 preface 笔误 1827 应为 1872。

Chapter 3(text/06-ch03…,1376 行)

Chapter 3 补记(细节)

  • L15: RAG 术语出自 Meta AI 研究者论文(脚注1 Lewis et al. 2021),发现带检索的模型更事实、更具体。
  • L17–48: ★FIFA 例:问「最新男足世界杯冠军?」ChatGPT 答 France 2018(错,出版时是 Argentina 2022);把维基导语贴进 prompt 当 context 后答对。人工贴不规模化→自动化系统。
  • L52–70: RAG 三阶段:indexing(ch2)/retrieval/generation。generation=把检索文档与原 prompt 合成一个最终 prompt 发给模型预测。
  • L120–128: 图3-2 检索三步:query 转嵌入;算向量库里最相似的;取回对应文本块。图注提到 HNSW(Hierarchical Navigable Small World)框=算相似度的实现。
  • L150–174: as_retriever() 抽象了「嵌 query+相似度计算」;k 参数控制取几篇。★低 k 反直觉但正确(L174):文档越多→越慢、prompt 越大成本越高、混进无关内容的概率越高→导致幻觉。
  • L193–245: 生成阶段:prompt「Answer the question based only on the following context…」,temperature=0(消除创造性),chain=prompt|llm。
  • L266–335: 封装成单个 qa 函数(@chain/RunnableLambda):先检索→格式化 prompt→生成;可返回 {answer, docs} 供检查。「把多步封装成单函数是构建有趣应用的关键」(L309)。
  • L339–349: 生产级四问:①用户输入质量参差怎么办;②多数据源怎么路由查询;③自然语言怎么翻译成目标数据源的查询语言;④索引过程(嵌入、切分)怎么优化。
  • L357–467: Query Transformation:
    • Rewrite-Retrieve-Read(Microsoft Research Asia 委托研究,脚注2 Ma et al. 2023):★走查例——串进无关信息的 query「Today I woke up…and forgot the food on the cooker. Who are some key figures…」直接检索失败答「no information provided」;加一个改写器 LLM(rewrite_prompt 要求产出更好的搜索查询、以**结尾)后,改写后的 query 送检索器,得到正确回答(Pythagoras、Plato 等)。代价:两次串行 LLM 调用增加延迟(L467)。可用于任何检索方法(向量库或 web search)。
    • Multi-Query(L469–592):单条 query 不够全面时,让 LLM 生成 5 个版本的问题,perspectives_prompt 说明目的是克服 distance-based 相似度搜索的局限;retriever.batch 并行检索全部;按 page_content 为字典键去重合并(get_unique_union)。
    • RAG-Fusion(L594–772):multi-query + 重排。RRF(reciprocal rank fusion):score += 1/(rank+k),k 默认 60;k 越大排名靠后的文档影响力越大。按融合分降序得最终列表。强项:捕捉用户意图表达、导航复杂查询、扩大召回面「serendipitous discovery」。脚注3 Rackauckas 2024。
    • HyDE(L774–888):让 LLM 先写一段假想的答案文本(passage),拿它(而非原 query)去嵌向量做相似检索——假想文档与真实相关文档在向量空间里比裸 query 更近。脚注4 Gao et al. 2022。
    • 改写四型小结(L878–886):去无关文本;用对话历史补全指代(and what about in LA ← SF 天气问题);广撒网取相关 query;拆复杂问题为多个简单问题全并进最终 prompt。
  • L890–1037: Query Routing:
    • Logical routing(L898–1039):给 LLM 数据源清单+function calling 分类(RouteQuery schema Literal["python_docs","js_docs"]),Python 代码提问被路由到 python_docs;下游 choose_route 用 toLowerCase()+子串匹配而非精确比较——对 LLM 输出「跑偏」有韧性;Tip:对 LLM 输出随机性的韧性是构建应用的重要主题(L1035–1037)。适用于数据源清单明确的场景(vector store/DB/API)。
    • Semantic routing(L1041–1139):不给清单;把代表各数据源的 prompt 模板(physics 教授 vs 数学家)预先嵌入,query 来了也算嵌入,cosine_similarity 取 argmax 选模板。"What's a black hole" → physics。
  • L1141–1357: Query Construction(自然语言→目标库的查询语言):
    • 动机(L1145–1151):生产中多数数据是结构化的(关系库);非结构数据的嵌入也带结构化 metadata。例句「What are movies about aliens in the year 1980?」:aliens 走语义、year==1980 走结构化过滤。
    • Text-to-Metadata Filter(L1155–1275):SelfQueryRetriever;预定义 AttributeInfo 字段表(genre/year/director/rating 及类型描述)进 prompt;LLM 拆出 metadata 过滤条件+语义 search query;翻译成向量库认识的过滤器再查(内部四步,L1269–1275)。例:「highly rated (above 8.5) science fiction film」。
    • Text-to-SQL(L1277–1357):光靠 LLM 直译 SQL 容错空间小。两招:给 CREATE TABLE 描述+几行示例行(grounding);few-shot 问题-SQL 对。create_sql_query_chain|QuerySQLDatabaseTool,Chinook.db「How many employees are there?」。★安全警告(L1349–1357):执行 LLM 生成的任意 SQL 在生产是危险的;建议只读权限账户、限表白名单、加超时防昂贵查询。「LLM 应用安全是仍在发展的领域」。
  • 小结(L1359–1367)+预告 ch4 记忆。

Chapter 4(text/07-ch04…,705 行)

Chapter 4 补记(text/07-ch04…)LangGraph 加记忆

  • L7: ★核心句:LLM 是无状态的(stateless)——每次生成新回复时对上一轮毫无记忆;历史信息必须每次随最终 prompt 重发。图4-1。
  • L15–19: 记忆系统两个设计决定:状态怎么存、状态怎么查。
  • L21–97: 最简方案=把全部对话史存成消息列表:每轮追加追加→插进 prompt 的 placeholder("{messages}")槽位。例:「Translate…I love programming.」→「J'adore programmer.」→「What did you just say?」答对了。
  • L89–99: 上生产的四个挑战:①原子更新(失败时不能只记了问题没记回答);②持久化存储(关系库);③控制存哪些、用哪些消息;④在 LLM 调用之外能检查和修改这个状态。
  • L103–147: LangGraph 定义拆词:multiactor(多个 actor 协作——LLM prompt 擅长生成/规划,搜索引擎擅长找新事实;Perplexity/Arc Search 例;需要协调层:定义 actor(node)与交接(edge)、以确定性结果调度执行可并行)、multistep(交互按离散时间步建模;一次交接触发下一步调度直到无人再交接)、stateful(跨步通信要有中央状态供所有 actor 共同更新;才能快照存储、暂停恢复容错、HITL——书里指到 Chapter 8)。graph 三件套:State(应用运行中收到的数据)/Nodes(Python 或 JS 函数:收当前状态返一个更新)/Edges(固定或条件边)。
  • L161–217: StateGraph 三步:①定义 State schema(TypedDict);messages 字段挂 add_messages reducer=追加不覆盖;没有注解的键默认整个被最新值覆盖;自定义 reducer=(当前状态,新值)→合并结果。②add_node("chatbot", fn)。③add_edge START→chatbot→END;compile() 成 runnable(invoke/stream);draw_mermaid_png 可视化。
  • L296–313: stream 输出形如 { "chatbot": { "messages": [AIMessage("How can I help you?")] } },且每一步都流出完整状态值。
  • L315–377: 持久化:checkpointer=存储适配器;官方带 in-memory(MemorySaver)/SQLite/Postgres,社区有 Redis/MySQL。compile(checkpointer=…) 后每步结束存状态;每次调用先取最近保存的状态并把新输入合并进来才执行第一个节点。thread_id 标识一段独立对话史(thread),首次使用自动创建,常用 UUID;多用户互不串线。★Jack 走查:第一次 invoke「hi, my name is Jack!」→「How can I help you, Jack?」两条消息存入;第二次同一 thread 问「what is my name?」,节点这次收到三条消息 →「Your name is Jack」。这就是记忆的本质(L377)。
  • L379–401: 可直接检查与修改状态:get_state(thread) 读;update_state(thread,[HumanMessage('I like LLMs!')]) 塞一条。
  • L403–502: 改聊天史三招之一 trimming:上下文窗口有限+过长信息会分散注意力致幻觉;只保留最近的消息。trim_messages 参数:strategy last/first(从尾部还是头部保);token_counter 用指定模型的分词器数 token;include_system=True 保住系统消息;allow_partial=False 超限整条删;start_on="human" 保证不会把 AI 回复留着而对应的人的问题被删掉(成对保留)。示例 max_tokens=65 从 10 条保到 7 条(具体输出见 L484–490)。
  • L504–617: 之二 filtering:filter_messages 按 include_types/exclude_names/exclude_ids 过滤;命令式或声明式(filter_ | model)皆可组合进链。
  • L619–700: 之三 merging:有的模型(如 Anthropic chat models)不接受连续同类型消息;merge_message_runs 把相邻同类合并(内容块列表保留列表;纯字符串用换行拼接);同样可进链。
  • L702–705: 小结说讲了 trim/filter/summarize——但正文没讲 summarize(书的自相矛盾,可当边界材料)。

Chapter 5(text/08-ch05…,724 行)

Chapter 5 补记(text/08-ch05…)认知架构

  • L13: 架构=材料的组合计划(泳池和单层房子用同样的砖)。「最重要的决定是怎么把手头组件(RAG/prompting/memory)组装成达成目的的东西」。
  • L15–23: 贯穿例子 email assistant:读你的邮件,归档无聊的/直接回一些/标记待看。两条约束:少打扰你;别发出你自己绝不会发的回复。→ 全书关键权衡:agency(自主行动能力)vs reliability(输出可信度)
  • L25–31: 自主性光谱三级:①LLM 决定某一步的输出(写草稿);②LLM 决定下一步做什么(对新邮件三选一:archive/reply/mark);③LLM 决定有哪些步可做(LLM 写代码实现没预编程的动作)。
  • L33: cognitive architecture 一词原本指人类推理模型;用在 LLM 上首见 Sumers et al.《Cognitive Architectures for Language Agents》(2023)。定义=「LLM 应用要走的步骤的配方」。一步=一次 RAG 检索或一次 CoT 提示的 LLM 调用。
  • L37–65: 四个架构:#0 Code(纯代码,不算);#1 LLM Call(单次调用;Notion 总结/翻译、简单 text-to-SQL);#2 Chain(预定义序列的多次调用;text-to-SQL 两连:生成 SQL+给非技术用户解释 SQL;作者注:有书叫它 flow engineering,脚注2 AlphaCodium 论文 Ridnik 2024);#3 Router(LLM 在预定义步骤间做选择;多索引 RAG 例)。本章只到 router,后面章讲 agentic。
  • L182–370: Chain 实现细节:text-to-SQL 用两个温度不同的模型——model_low_temp(0.1)生成 SQL,model_high_temp(0.7)生成自然语言解释(★按任务分配温度是可复用的判断)。State 五键:messages/user_query/sql_query/sql_explanation…Input/Output schema 独立定义——中间状态只出现在 stream 输出里不进最终返回(L370)。
  • L372–414: Router 动机:prompt 里混进无关信息会拖累模型,所以要选对索引只用那一个。Note(L378–386):LLM 之前要靠手标数据集+特征工程训分类器;LLM 天然编码了人类语言,可以零样本/极少样本当分类器。
  • L396–520: Router 实现:router_node(model_low_temp 选 records|insurance,prompt 要求只输出域名)→pick_retriever 条件边函数把 domain 映射到 retrieve_medical_records 或 retrieve_insurance_faqs→generate_answer 按域换 system prompt 并带上检索文档。add_conditional_edges。图5-4 条件边=虚线。
  • L663–716: ★streaming 走查:「Am I covered for COVID-19 treatment?」→chunk1 router(messages 更新+domain="insurance")→chunk2 retrieve_insurance_faqs(documents=[...])→chunk3 generate_answer(answer)。顶层键=节点名,值=节点返回的状态更新。走的是由 LLM 决定的路径。
  • L717–721: 小结:核心权衡 agency vs oversight;下一章 agent 架构。

Chapter 6(text/09-ch06…,773 行)

Chapter 6 补记(text/09-ch06…)Agent 架构

  • L7–15: agent 最简定义=Russell&Norvig《AIMA》(2020):「something that acts」。act 三层含义:①要有决定做什么的能力;②决定意味着有不止一个可选项(没有选项的决定不算决定);③决定需要外部环境信息。agentic 应用=用 LLM 从多个可选动作里挑,基于世界现状或期望状态。两种提示技术实现:tool calling(工具清单+输出格式指令)+ CoT(拆解成有序小步)。
  • L25–47: 手工 prompt 走查(问题:「第30任总统去世时几岁?」):Tools 清单+CSV 格式+「Think step by step;if you need to make multiple tool calls,return only the first one」;gpt-3.5-turbo 温度 0+换行符做 stop sequence→只产出一个动作「search,30th president of the United States」。★L45:新模型已为 tool calling/CoT 微调过,不再需要这些指令。★L47 有个编辑残留注释「add example prompt and output for tool-calling model」——正式出版书里的编辑瑕疵。
  • L49–100: ★Plan-Do 循环=agent 与 ch5 架构的唯一区别:LLM 驱动的循环。「人人都写过循环,关键是让 LLM 控制停止条件」。循环体=规划动作+执行动作。第 1 轮输出 search…;执行搜索(Coolidge 生卒)后把「工具名+文本结果」拼回 prompt 再问(L96–100:两处新增——output 工具作为停止信号+上一轮结果回填「我们拿到你要的结果了,下一步做什么?」)→ 第 2 轮输出 calculator,1933 - 1872;回填计算器结果 61 → 第 3 轮输出 output,61 停止。
  • L140: 这个架构叫 ReAct,Shunyu Yao et al. 首提。
  • L144–257: LangGraph 实现:@tool 装饰器(docstring 即工具描述)+DuckDuckGoSearchRun;model.bind_tools(tools);ToolNode=执行最新 AI 消息里的全部 tool_calls、逐个返回 ToolMessage,异常也包成 ToolMessage 传回模型由模型决定怎么办;tools_condition=条件边,有工具调用就去 tools 节点否则结束;graph 在 model↔tools 间循环——循环在 LangGraph 里靠条件边定义退出条件,何时结束由模型说了算(agent 的关键属性)。
  • L261–335: 基础版 stream 走查(call id 真实出现:call_ZWRbPmjvo0fYkwyo4HCYUsar):model 决定调 duckduckgo_search(query="30th president…age at death")→tools 返回搜索结果(里面其实有答案「aged 60」)→model 终答「died on January 5, 1933, at the age of 60。」无更多工具调用→结束。★注意:这个版本答 60(正确),手工循环版算出 61——because 1933−1872=61 没考虑生日没到。书自己没点破这个差一错误,可以当我们自己的判断素材。
  • L339–347: 扩展一(先强制调某工具):first_model 节点不调 LLM,直接构造 ToolCall(用户原话当查询)。好处:省一次 LLM 调用的延迟+防止模型误判「不需要调」。反过来若不存在「永远该先调它」的规则,硬加会变差。
  • L494–543: first_model 版走查:跳过首次 LLM 调用直接 search→回 model 终答时改成了写减法步骤并给 61(和基础版的 60 又不同)。★两次运行同题不同路径不同答案——温度 0.1 下 agent 的非确定性活例。
  • L545–551: 扩展二(工具太多):>10 个工具时规划性能开始劣化;方案=对工具描述做向量检索(InMemoryVectorStore 装 Document(tool.description, metadata name)),select_tools 节点先按 query 选出子集,再 bind_tools(selected_tools)。顺带省 prompt 钱;代价是检索步骤加延迟——只在加了工具性能下降后再上
  • L684–686: Note:与标准 agent 架构相比唯一区别是进循环前多停一站 select_tools。
  • L768–774: 小结+预告 ch7。

Chapter 7(text/10-ch07…,626 行)

Chapter 7 补记(text/10-ch07…)Agents II

  • L5–16: agent 架构=CoT+工具+循环的组合,潜力难以夸大。两个扩展:reflection(让应用分析自己过去的输出与选择,并记住历次反思)+multi-agent(团队胜过单人)。
  • L21–29: reflection 又名 self-critique:「生成提示」与「修订提示」之间建回路。人写书(作者↔审稿人↔编辑来回)类比。Kahneman《思考快与慢》System 1(反应式)/System 2(方法性反思);正确使用可接近 System 2 行为。
  • L29–97: 实现:generate+reflect 两节点,任务=三段式文章;固定转 N 次(should_continue:messages>6 即 END——3 轮、每轮 2 条消息);变体:让 reflect 决定何时结束。
  • L184: ★关键技巧:reflect 节点把消息角色反转(cls_map {AIMessage:HumanMessage, HumanMessage:AIMessage})——骗过对话微调模型:它以为自己是在批改「用户」写的文章,generate 则以为批评来自用户。不这样做不行,因为对话模型按「人-AI 消息对」训练,同一方连续多条消息会掉性能。
  • L186: 为什么在 generate 后终止而不是 revise 后:固定轮数下最后一轮的修改要被执行到。变体=reflect 无话可说时自己结束。
  • L188–245: 真实输出:《小王子》文章批评(五条建议:depth/analysis/length/style/conclusion)与终稿;token 数据真实:prompt 2501 tokens/completion 420 tokens(gpt-3.5-turbo)。
  • L247–255: 有效原因:给 LLM 多次打磨机会+批评者换一个人设;变体:把反思接在 agent 架构末尾(批评伪装成用户输入,代价是延迟);用外部信息落地的批评——代码生成 agent 在 reflect 前先跑 linter/编译器把报错喂给它。Tip:能这么做就强烈建议做,大概率提升最终质量。
  • L257–431: Subgraphs:图当节点用。三种用途:多 agent 系统;跨图复用节点组;团队边界(只需遵守输入输出 schema 接口)。两种接法:①直接当节点挂(要求共享状态键;多余的键进和出都被忽略——Note L283–286);②包一层函数做状态转换(schema 完全不同时:invoke({"bar": state["foo"]}) 进,{foo: response["bar"]} 出)。
  • L433–465: 多 agent 动机三信号(ch6 只讲了工具那一条):工具太多选不准;上下文复杂度超过单 agent 能力(prompt 大小与指代物数量超出所用模型容量);某领域需要专门子系统(planning/research/math)。独立 agent 可以简单到「一个 prompt+一次调用」,也可以是完整 ReAct agent。四种拓扑(Figure7-3):Network 全互连谁都能指派下一个;Supervisor 星型(特例:supervisor 就是带工具的 LLM 调用,即 ch6 的 agent);Hierarchical 监工的监工;Custom 混合(部分流程确定+只有部分节点有指派权)。作者偏好 supervisor:能力与易用平衡。
  • L467–502: supervisor 节点实现:SupervisorDecision(Pydantic, next: Literal[researcher|coder|FINISH])+gpt-4o 温度 0 结构化输出;prompt 两段:先介绍工人清单说完成时回 FINISH,再问「谁下一个?」。★Note(L537–539):子 agent 名字必须自解释且互异,叫 agent_1/agent_2 模型无从判断;必要时在 prompt 里加每个 agent 的描述。
  • L541–619: 整图:START→supervisor→条件边(lambda state.next)→researcher/coder→回 supervisor。两个 subagent 共享 messages 列表彼此看得见工作成果——不是唯一组织方式,子 agent 也可以自己持内部状态只交摘要;回 supervisor 不是硬规定,可以换成条件边让子 agent 自行决定直接返给用户。
  • L621–627: 小结忠告:这些扩展不该是新 agent 的第一选择,先用 ch6 的直白架构。ch8 回到 reliability vs agency 权衡。

Chapter 8(text/11-ch08…,632 行)

Chapter 8 补记(text/11-ch08…)把最多好处从 LLM 里挤出来的模式

  • L7–13: 核心:agency-reliability 权衡可视化成**前沿(frontier)**曲线(脚注1 借自金融的有效前沿/经济学生产可能性前沿/工程的 Pareto 前沿)。链=低自主高可靠;agent=高自主低可靠。
  • L15–33: 其它三个目标:latency(拿到最终答案的时间)/autonomy(少打断人)/variance(多次调用间差异小)。目标之间互斥:全权重下互相抵消——「延迟最小的应用是什么都不做的那个」。开发者要的是把前沿往外推:同等可靠度换更高自主,或反之。四个手段:streaming/中间输出、structured output、HITL、double texting 模式。
  • L51–142: 结构化输出三策略:prompting(好好求它——任何模型都行但只是建议不保证);tool calling(微调过的模型能按 schema 清单选并服从:name+description+JSONSchema);JSON mode(部分新 OpenAI 模型强制输出合法 JSON)。LangChain 统一接口 .with_structured_output(schema),Pydantic/Zod 对象兼做校验(不符则返回 validation error 而不是烂输出)。Joke 例:setup/punchline,字段 description 是模型决定哪段输出进哪个字段的依据(L90);低温适合结构化输出。
  • L144–253: 中间输出:架构越复杂调用越长(每见多个节点串行或成环=耗时增加的信号);用户期望几秒内有输出。stream_mode 三种:updates(默认,{节点名:更新});values(每次状态变化吐整个当前状态);debug(checkpoint/task/task_result 事件流);可用列表组合传参。逐 token:aSTREAM_events(version="v2") 过滤 on_chat_model_stream 事件。
  • L299–561: HITL 各模态(前提:接 checkpointer,否则没有「记忆」可中断):
    • interrupt(L319–327):用户看着流式输出手动打断(Python asyncio.Event+aclosing 保证正确关流;JS AbortController 的 abort 会抛异常要 try-catch);状态保存到最后一个完整步。
    • authorize(L387–434):预先声明 interrupt_before=['tools'],进入特定节点前暂停等人审批工具调用(interrupt_before 是列表、顺序无关,多节点逐个拦)。
    • Resume(L436–459):用 None/None 输入重新 invoke=继续上次的非空输入。
    • Restart(L461–500):带新输入 invoke=旧状态保留+合并新输入+从头跑;想丢掉现状就换 thread_id。
    • Edit state(L502–530):get_state 看一眼→update_state 打补丁→产生新 checkpoint→再 resume。
    • Fork(L532–559):get_state_history 迭代器列出全部历史状态(最近在前),拿历史里任何一个 config 重新 invoke=「换一种答案」,创意类应用好用。
    • 真正的力量在混用(L561)。
  • L563–621: 多任务/并发输入(double texting)五种策略:
    • 拒绝并发:最简单,把并发管理推给调用方。
    • 独立处理:每个输入开新线程;缺点两次调用对用户不可调和;优点任意扩展——两个不同用户本来就是这种。
    • 排队:数量不限+结果与到达时序无关;缺点队列可能无界增长、排队项看不到前一问的回答会变陈旧(依赖前答的新输入不适用)。
    • 打断:四个变体(什么都不留/留最后完成步/连未完成步的部分更新也抢救/等当前节点跑完存档再断)。好处即时响应;坏处仍是一次只处理一个+部分状态可能非法——例:OpenAI 要求请求工具调用的 AI 消息后面紧跟工具结果消息,打断在这两者之间就会卡死除非防御性清理+输出对到达时机敏感,设计不好很脆。
    • fork-and-merge:收到新输入时分叉线程状态并行处理完再合并;要求状态可无冲突合并(CRDT 等)或用户手工解冲突;两条满足其一就是综合最优:及时+时序无关+支持任意并发。
  • L621–631: 有些策略 LangGraph Platform 已实现(ch9);下一章上线生产。

「Prerequisites」章(text/12-fm-prerequisites.txt,665 行)

「Prerequisites」章补记——实为原书 Chapter 9(text/12-fm-prerequisites.txt)

注意:spine 把它标成 fm-prerequisites,但正文第一行写明「Chapter 9. Deployment: Launching Your AI Application into Production」。引用时用 text/12-fm-prerequisites.txt。

  • L5–29: 部署三件套选型:vector store=Supabase(PgSQL+pgvector);监控调试=LangSmith;backend API=LangGraph Platform。fork 官方模板仓库(retrieval agent)。.env:OPENAI_API_KEY/SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY/LANGCHAIN_TRACING_V2=true+LANGCHAIN_ENDPOINT(api.smith.langchain.com)+LANGCHAIN_API_KEY。
  • L60–222: Supabase 步骤:create extension vector;documents 表(id/content/metadata jsonb/embedding vector(1536)——1536 对应 OpenAI 嵌入维度要按需改);建 match_documents 函数:相似度=1−(embedding<=>query_embedding)(cosine),先按 metadata @> filter(JSON 包含符)过滤、按距离排序、limit match_count。SupabaseVectorStore(query_name="match_documents")。
  • L224–258: LangGraph Platform=托管服务:横向扩缩任务队列与服务器、健壮 Postgres checkpointer 扛并发与大人小状态(fault-tolerant scalability);支持 double texting/异步后台任务/cron。含 LangGraph Studio(可视化调试编辑测试共享 agent)。部署从 LangSmith UI 进(集成在里面),要 Plus 计划;免费自托管选项要求自己维护数据库和 Redis。
  • L260–310: ★Platform API 四个数据模型:Assistant=CompiledGraph 的配置实例——抽象了认知架构本身,同一张图可派生多个不同配置的 assistant(行为因此不同);Thread=一组 run 的累计状态容器,必须先建 thread 状态才持久化,某时点的状态叫 checkpoint;Run=assistant 的一次调用,可带自己的输入配置元数据,可选挂在某个 thread 上;Cron job=时刻表+assistant+输入,每次开新 thread 发输入。Feature 五项:streaming/HITL/double texting/stateless runs/webhooks。
  • L312–334: Platform 级流式五模式:values/messages(为聊天应用设计,节点结束时的完整消息+节点内逐 token;要求图里有 messages 键)/updates/events(全事件流,可实现 token-by-token)/debug。
  • L336–342: HITL 在平台侧的理由:自主跑的复杂 agent 会做意外动作「灾难性后果」;在涉及调用工具或访问特定文档的检查点插人工干预。
  • L340–358: 平台版 double texting 四策略:Reject/Enqueue(等首轮完成再单独发新轮)/Interrupt(打断但保留已完成工作,插入用户输入继续——你的图得能处理怪异边界情况)/Rollback(回滚全部已完成工作,当作用户刚跟在原始输入后发来的)。
  • L360–376: Stateless runs:建 thread 跑完即清、跳过所有 checkpoint 步骤的端点;stateless run 重试时保持 memory;后台任务 worker 中途死掉则整个从头重试。
  • L374–376: Webhooks:run 完成回调通知你的应用。
  • L380–427: 部署工程:langgraph.json(dependencies/graphs: graph id→compiled graph 或造图函数路径/env);项目结构 my_agent/{utils/tools.py,nodes.py,state.py}+agent.py;本地测试 langgraph-cli[inmem] (Python 3.11+)langgraph devhttp://localhost:2024 + /docs;cURL 示例带 multitask_strategy:"reject"、stream_mode:["values"];SDK get_client().threads.create()+client.runs.stream(stream_mode="updates")。
  • L534–576: 从 LangSmith UI 部署:GitHub OAuth(hosted-langserve 应用)→选 repo/langgraph.json 路径/git ref→Development type 选 Production(最高 500 requests/second、高可用存储、自动备份)→环境变量敏感值勾 Secret→等 build 完成出新 revision;Trace Count 图(successful/pending/error traces);New Revision 迭代。
  • L578–608: LangGraph Studio=agent IDE:能改 agent 结果或某节点的逻辑于轨迹中段,交互式操纵当时的状态形成迭代流程;也能改运行配置/建线程/中断/改代码/HITL;有 Apple silicon 桌面版可测本地应用。
  • L612–656: 安全三条原则:最小权限(read-only 凭据、禁碰敏感资源、沙箱/容器);预设滥用(假设凭据被允许的任何方式都会被用到——能删库的凭据就当会被要求删库);纵深防御(只读+沙箱叠加)。三个场景:文件系统/外部 API/数据库各自的风险与缓解。滥用与成本:账户验证(邮箱手机)、中间件限流(过去 X 分钟请求数,严重则 timeout/ban)、prompt injection 护栏(权限收窄+prompt 写得具体严格)。
  • L658–666: 小结+预告 ch10 评估监控基准改进。

Chapter 10(text/13-ch10…,1074 行)

Chapter 10 补记(text/13-ch10…)测试:评估监控持续改进

  • L7–11: 动机:底层 LLM 非确定性+幻觉,输出质量受 prompt/用户输入格式/检索上下文影响;错误输出伤害品牌与客户忠诚。需要测试-评估-监控-改进的系统。
  • L13–35: 三阶段:Design(运行时断言把失败喂回 LLM 自纠错——在影响用户前处理)Preproduction(上线前抓回归)Production(真实用户上的错误监测并回流到设计)。循环:design→test→deploy→monitor→fix→redesign。
  • L37–60: 设计阶段实例=Self-corrective RAG 控制流:①路由问题到向量库或 web search;②检索后 LLM 给文档相关性打分(binary yes/no);③相关则生成答案;④答案再做幻觉检查,准确且相关才展示给用户;⑤兜底:文档不相关或答非所问→web search 取上下文。
  • L63–102: 索引材料=三篇 LangChain 博客(from_tiktoken_encoder 切 250 字符无重叠);问题「What are 2 LangGraph agents used in production in 2024?」。
  • L161–252: 检索评分器:GradeDocuments(BaseModel binary_score 'yes'|'no',Field description 说明)+温度 0 的 gpt-3.5-turbo with_structured_output;判分规则写在 system prompt(含关键词或语义相关即算相关);「agent memory」query 实测输出 binary_score='yes'。Pydantic/Zod 把二元判断变成可编程分支的条件(L252)。
  • L254–264: 图10-3 LangSmith trace 可视化逻辑流;图不进索引的 out-of-context 问题会触发 web search 兜底(先经 transform_query 改写)。完整图定义在书 GitHub 仓库。
  • L268–289: Preproduction:数据集=输入+期望输出的样例集合。三种建法:人工精选(小型集 10–50 条高质量样例起步,随生产边角案例增加);应用日志(生产后的真实用户输入,保证真实覆盖常见问题);合成数据(从现有输入采样造边角案例,适合真数据不足)。
  • L296–336: LangSmith 数据集三型:kv(键值对,最通用默认)/llm(单 prompt 字符串进出)/chat(序列化消息列表)。
  • L338–366: 离线评估(batch evaluation on predetermined test suite),可选 ground truth 参考答案。三类评估器:Human(需求没法写成代码时打定性分;annotation queues 加速);Heuristic(硬编码函数与断言;reference-free 如「是否合法 JSON」/reference-based 如 accuracy;代码生成任务的 schema 检查单测很好用);LLM-as-a-judge(把人的评分规则写进 prompt 对着参考答案评;要审计调参才可靠)。推荐顺序:L360 先 heuristic→再 human→最后用 LLM-as-a-judge 自动化人审。Tip(L362–364):judge 的 prompt 要人类能复现;别让 LLM 做 0–10 模糊打分。
  • L370–402: few-shot 评估器:人工对 judge 结果的纠偏存成 few-shot 样例回灌进未来 prompt——自改进回路,减少反复调 prompt;步骤四条(L378–388)。Create Few-Shot Evaluator 选项自动建配套数据集,Schema 字段可切原始类型(integer/Boolean)。
  • L404–418: Pairwise evaluation:两两比较比绝对打分认知负担低(informative/specific/safe 等);RANKED_PREFERENCE 分数;可自定义 pairwise judge 标准、SDK 跑 UI 看。
  • L420–440: 回归测试:传统软件测试期望 100% 过,AI 因 model drift(数据分布变化或模型更新导致的退化)做不到满分→含义:①跨时间追踪性能防退化(regression testing=确保最新更新不劣于基线);②逐点对比两次实验找出变好变坏的样本。LangSmith comparison view 高亮回归/提升的 run,可定位版本+具体样例。
  • L444–466: agent 评测三层粒度:Response(终答,end-to-end,黑盒)/Single step(单步决策,输出是工具调用)/Trajectory(全部工具调用序列)。「agent 用 LLM 决定控制流,每次运行结果可以显著不同:不同工具、卡环、步数波动」(L448)。
  • L488–658: 实现(SQL agent over Chinook):
    • Response:5 条 QA 数据集($523.06 USA 最高消费、Hot Girl 2013 最热曲目、Led Zeppelin 14 张专辑、Big Ones 总价 14.85、Steve Johnson 2009 销冠);hub.pull("langchain-ai/rag-answer-vs-reference") 作 judge prompt;evaluate(num_repetitions=3)。
    • Single step:check_specific_tool_call 断言第一个 tool call==sql_db_list_tables 得分 1/0;配工具硬编码的专用 assistant。
    • Trajectory:预期五件套 [list_tables, schema, query_checker, query, check_result];三个评估器:any_order(set 包含)/in_order(迭代器子序列检查 all(elem in it))/exact_match(列表全等)。
    • 小结:这些测试是缓解 agent 成本与不可靠性的坚实起点(L1012)。
  • L1014–1067: 生产阶段:observability+在线评估当护栏(prompt injection/toxicity 防护)。
    • Tracing:trace=应用从输入到输出的步骤串;设两个环境变量即可全链埋点零改码;指标:trace volume/成功失败率/延迟/token 数与成本。
    • 在线评估(reference-free):没有参考答案实时打分。两类反馈来源:显式/隐式用户反馈(点赞点踩按钮,反馈挂任意 trace 或中间 span,annotation queue 批量看)+ judge 直接评 trace 抓幻觉与毒性。
    • 分类打标:没预设标签用 LLM-as-a-judge 分类;有 ground truth 标签用启发式评估器。
    • 监控修错:tracing 抓到的错误和边角案例回填离线数据集防复发;分阶段放量给 beta 用户攒 ground truth+估成本延迟质量。
  • L1069–1075: 小结。

Chapter 11(text/14-ch11…,168 行)