当前位置: 首页 > news >正文

第2周学习笔记

学习时间:2026年5月(5天) |核心主题:Model I/O → RAG 系统构建 → Agent 与工具系统 → 中间件架构


一、本周学习路线总览

本周以"从单模型调用到构建完整的智能应用系统"为主线,按以下路径递进:

模型抽象与调优 → 文档加载与分割 → 向量嵌入与检索 → 工具定义与 ReAct → Agent 标准入口与中间件 Day1 Day2 Day3 Day4 Day5

如果说第 1 周是学会"如何用 LCEL 搭积木",那么第 2 周的核心命题是——如何让模型接入外部世界。RAG 给模型接上知识库,Agent 给模型接上工具,两者叠加让模型从"会说话的百科全书"进化为"能做事、能查资料的智能助手"。


二、Day 1:Model I/O 与模型抽象

2.1 BaseLanguageModel:一套接口,所有模型

本周第一个重要认知:LangChain 通过BaseLanguageModel将不同厂商的模型统一在同一套接口下。无论用 OpenAI、Anthropic,还是通过硅基流动接入的国产模型,上层的 Chain 和 Agent 只依赖invoke()/stream()/batch()这些方法签名,底层实现可以随意切换。

fromlangchain_openaiimportChatOpenAIfromlangchain_anthropicimportChatAnthropic# 两个不同厂商的模型,调用方式完全一致llm_openai=ChatOpenAI(model="gpt-5.5",temperature=0.1)llm_claude=ChatAnthropic(model="claude-3-5-sonnet")

这种设计的哲学是面向接口编程:切换模型只需改一行 import 和初始化参数,管道的其余代码一行不动。这是第 1 周"协议胜于继承"思想在模型层的延续。

2.2 参数调优:四个旋钮的艺术

理解四个核心参数对模型行为的影响,是写出高质量应用的前提:

参数作用建议值
temperature随机性控制RAG: 0.0, 创作: 0.7-0.9
max_tokens最大输出长度按场景设定(成本控制 + 安全兜底)
top_pnucleus sampling 候选词截断默认 1.0
frequency_penalty抑制重复需要时 0.1-0.3

关键理解:

  • temperaturetop_p二选一调优为主,不建议同时大幅偏离默认值
  • temperature=0在学习阶段尤为重要——确保每次运行结果可复现,调试时不会因为随机性而困惑
  • frequency_penalty是解决模型"复读机"问题的利器

2.3 多模型路由:RunnableBranch 的实战应用

RunnableBranch不仅是第 1 周学到的条件分支组件,它在模型路由场景中找到了最佳的用武之地——根据输入特征(文本长度、任务类型、复杂度)动态选择最合适的模型:

router=RunnableBranch((is_long_context,llm_powerful),# 长文本 → 强模型(is_code_task,llm_powerful),# 代码任务 → 强模型(is_complex_question,llm_powerful),# 复杂问题 → 强模型llm_fast# 默认 → 快速模型)

RunnableBranch本身也是Runnable,可以用|接入任何 LCEL 管道——这让原本线性的管道具备了"分叉"能力,是构建非平凡 Agent 的基础。


三、Day 2:RAG 系统构建(上)——文档加载与文本分割

3.1 RAG 六步流程

从今天开始进入 LangChain 最具工程价值的领域——RAG。一个完整的 RAG 系统可以概括为六个字:

Source → Load → Transform → Embed → Store → Retrieve 源 载 转 嵌 存 检

Day 2 聚焦在前两步 Load 和 Transform,这是整个 RAG 系统的地基。

3.2 文档加载:统一接口下的多格式支持

langchain_community.document_loaders提供了上百种加载器,覆盖 PDF、Markdown、CSV、HTML 等所有常见格式,共享同一套调用契约——load()返回List[Document]

加载器用途关键细节
TextLoader纯文本需要显式指定encoding="utf-8"
PyPDFLoaderPDF自动按页拆分,metadata 含页码
UnstructuredMarkdownLoaderMarkdown识别标题层级并写入 metadata
DirectoryLoader批量加载通过glob匹配文件,loader_cls指定加载器

3.3 文本分割:RecursiveCharacterTextSplitter 的设计智慧

分割的核心矛盾:chunk 太大会丢失检索精确性,chunk 太小会割裂语义。RecursiveCharacterTextSplitter通过递归降级分隔符的设计解决了这个问题:

splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50,separators=["\n\n","\n","。",",",";"," ",""],)

关键参数理解:

  • separators的优先级顺序:先尝试段落边界\n\n→ 行边界\n→ 中文标点。,;→ 空格 → 最终兜底逐字符切割。中文文档必须加入中文标点,否则会退化到字符级切割。
  • chunk_overlap:相邻 chunk 之间的重叠部分(推荐值为chunk_size的 10%~20%),解决了"关键信息恰好落在切割边界上"的问题。
  • chunk_size:500 是工程上的常用起点,在精度和上下文之间取得平衡。

四、Day 3:RAG 系统构建(下)——嵌入、存储与检索

4.1 向量嵌入:让机器"理解"文本

向量嵌入的本质是将自然语言映射到高维向量空间,语义相近的文本在高维空间中几何上彼此靠近。LangChain 中所有 embedding 模型遵循统一的Embeddings接口,提供两个核心方法:

  • embed_documents(texts)— 批量嵌入待存储的文档
  • embed_query(text)— 嵌入用户查询

重要踩坑:使用硅基流动等非 OpenAI 服务商时,必须设置check_embedding_ctx_length=False。默认True会让 OpenAIEmbeddings 使用tiktoken将文本转为 token ID 数组发送,但硅基流动不支持这种格式,会返回 20015 “参数无效” 错误。

4.2 向量存储与检索策略

三种检索策略各有适用的场景:

策略search_type原理适用场景
相似度similarity(默认)余弦相似度排序,返回 Top-K通用场景
MMRmmrfetch_k候选中做多样性筛选避免结果同质化
阈值过滤similarity_score_threshold只返回相似度超过score_threshold的结果生产环境质量兜底

VectorStore本身不实现Runnable接口,需通过as_retriever()包装为Retriever对象后才能接入 LCEL 管道。这个设计体现了检索器是比向量存储更通用的抽象——无论数据来自向量库、搜索引擎还是外部 API,都可以统一封装。

4.3 完整 RAG Chain:一条管道串联全部环节

rag_chain=({"context":retriever,"question":RunnablePassthrough()}|prompt|llm|StrOutputParser())

数据流分析:

  1. RunnablePassthrough()将用户输入同时传给retriever(执行检索)和原样透传(保留原始问题)
  2. prompt模板将检索到的上下文注入 system 消息,原始问题注入 user 消息
  3. llm在上下文辅助下推理回答
  4. StrOutputParser提取纯文本

关键习惯:把上下文放在 system 消息中而非 user 消息中,让 system 承载"背景信息",user 保持用户的原始意图不被干扰。


五、Day 4:Agent 系统(上)——工具与 ReAct

5.1 工具定义:给模型装上手脚

工具是 Agent 系统中最基础的单元。@tool装饰器把一个普通 Python 函数自动转化为模型可理解的结构化接口:

@tooldefsearch_database(query:str,limit:int=10)->str:"""搜索客户数据库中的匹配记录。"""returnf"在数据库中找到了{limit}条与 '{query}' 相关的结果。"

@tool做了三件事:类型标注 → JSON Schema;docstring → 用途描述;函数名 → 全局唯一工具名。

对于复杂参数场景,通过args_schema传入 Pydantic 模型可以获得更精细的控制:

classWeatherInput(BaseModel):city:str=Field(description="城市名称")units:Literal["celsius","fahrenheit"]=Field(default="celsius",description="温度单位")@tool(args_schema=WeatherInput)defget_weather(city:str,units:str="celsius")->str:"""获取指定城市的当前天气信息。"""...

每个字段的Field(description=...)是模型决定如何填充参数的关键依据——描述模糊会导致模型在错误的情境下调用工具或填入不合理的参数。

5.2 ReAct 模式:推理与行动交替进行

这是 Agent 系统最经典的思想框架。它的执行流程是一条优雅的循环链条:

Question → Thought → Action → Observation → Thought → ... → Final Answer

以一个具体场景为例:用户问"北京今天多少度?华氏度是多少?"

  1. Thought:模型意识到需要先获取温度 → 决定调用get_weather
  2. Action:调用get_weather(city="北京")
  3. Observation:收到 “北京:晴,25°C”
  4. Thought:模型判断还需要换算华氏度 → 决定调用calculate
  5. Action:调用calculate(expression="25 * 9/5 + 32")
  6. Observation:收到 “77.0”
  7. Final Answer:“北京今天 25°C,约 77°F”

ReAct 的美妙之处在于——把"推理"和"行动"统一为可迭代、可观测的过程。每一步思考都有文本记录,每一次工具调用都有可追溯的输入输出。

5.3 错误处理:当工具出错时

生产环境中的 Agent 不可能一帆风顺。@wrap_tool_call中间件像一个透明的拦截器,将工具异常转化为模型可理解的ToolMessage

@wrap_tool_calldefhandle_tool_errors(request,handler):try:returnhandler(request)exceptExceptionase:returnToolMessage(content=f"工具执行出错,请检查输入参数后重试。错误详情:{e}",tool_call_id=request.tool_call["id"],)

这贯彻了 ReAct 的核心理念——把一切(包括失败)都转化为可推理的信息。模型收到错误消息后会像处理正常结果一样分析它,然后决定重试、换参数还是告知用户。


六、Day 5:Agent 系统(下)——Function Calling 与 create_agent

6.1 Function Calling:模型与工具的通信协议

bind_tools()是连接模型和工具世界的桥梁。在create_agent内部,框架自动完成工具绑定——你不需要显式调用bind_tools()

ReAct vs Function Calling 的本质区别:

维度ReActFunction Calling
实现方式提示词工程,模型输出"带格式的文本"模型原生能力,输出结构化 JSON
解析方式正则/解析器从文本中提取确定性 JSON 解析
调用准确率依赖提示词质量,有格式偏差风险通常是训练内置能力,更稳定
适用场景无原生 FC 支持的模型;学术追溯思考过程有 FC 支持的模型(生产首选)

在 LangChain v1 中,create_agent自动根据模型能力选择最优策略——支持 tool calling 就走 Function Calling 路径,否则退回文本推理模式。你不需要改变构建代码。

6.2 create_agent:LangChain v1 的 Agent 标准入口

create_agent统一了过去分散在create_react_agentAgentExecutorAgentAction中的功能,把所有复杂性封装进由 LangGraph 驱动的高层抽象。核心参数:

参数作用
model模型标识字符串或已初始化的聊天模型实例
tools工具列表,框架自动完成绑定和注册
system_promptAgent 的行为基调
response_formatPydantic 模型约束最终输出格式
checkpointer持久化对话状态(InMemorySaver用于开发,PostgresSaver用于生产)
context_schema定义每次调用的不可变上下文数据类型
middleware可插拔的中间件列表

checkpointer + thread_id是实现多轮对话的关键:

agent=create_agent(model=llm,tools=[...],checkpointer=InMemorySaver())# 同一 thread_id 下的多次调用自动累积对话历史result_1=agent.invoke({"messages":[{"role":"user","content":"北京天气怎么样?"}]},config={"configurable":{"thread_id":"conversation-001"}})result_2=agent.invoke({"messages":[{"role":"user","content":"我刚才问了什么?"}]},config={"configurable":{"thread_id":"conversation-001"}}# 同一个 thread_id)# Agent 能记住之前的对话!

6.3 Middleware:Agent 的可插拔扩展层

这是create_agent最令人惊艳的设计。中间件分为节点式和包裹式两种风格:

节点式钩子(在特定时间点运行):

  • @before_agent— Agent 调用开始前(全局状态初始化)
  • @before_model— 每次模型调用前(注入动态上下文,如当前时间戳)
  • @after_model— 每次模型响应后(日志记录、响应校验)
  • @after_agent— Agent 调用结束后(清理、汇总)

包裹式钩子(环绕调用,控制执行零次/一次/多次):

  • @wrap_model_call— 环绕模型调用(重试、缓存、动态切换模型)
  • @wrap_tool_call— 环绕工具调用(错误转换、日志、限流)

一个经典的@before_model中间件——为 Agent 注入时间感知能力:

@before_modeldefadd_timestamp(state:AgentState,runtime:Runtime):now=datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")state["messages"].append(HumanMessage(content=f"[系统信息] 当前时间:{now}"))returnNone

中间件执行顺序遵循洋葱模型before_*按列表正序执行,after_*按列表反序执行,wrap_*形成嵌套结构(列表前面的包裹在最外层)。

官方预置中间件覆盖了完整场景:ModelRetryMiddleware(模型重试)、ToolRetryMiddleware(工具重试)、ModelFallbackMiddleware(模型降级)、HumanInTheLoopMiddleware(人工审批)、SummarizationMiddleware(对话历史压缩)、TodoListMiddleware(任务规划追踪)等。


七、核心概念的个性化理解

7.1 对"文档 → 向量 → 检索 → 生成"管道的感悟

RAG 本质上就是在 LLM 管道的前端插入了一个检索步骤:retriever | prompt | llm | parser。第 1 周学到的 LCEL 知识在这里被完整复用——管道的可组合性让"插入新组件"极其自然。这印证了一个设计理念:好的抽象能经得起场景扩展的考验

7.2 对 Agent = Model + Harness 的理解

create_agent的官方定义 “Agent = Model + Harness” 精准概括了 Agent 的本质。Model 负责思考(推理是否调用工具、如何填充参数、何时给出最终答案),Harness(包括工具绑定、状态管理、中间件、循环控制)负责为模型提供正确的上下文和行动框架。这种分离让 Agent 的"大脑"和"身体"可以独立演进而互不影响。

7.3 ReAct 与 Function Calling 的关系

两者不是"替代"关系,而是分层关系。ReAct 定义的是逻辑范式(推理-行动-观察的循环),Function Calling 提供的是底层实现(用结构化 JSON 替代文本解析)。在 LangChain v1 中,create_agent为上层提供了统一的抽象,底层的选择只影响执行效率而不影响功能接口——这正是优秀的框架设计。

7.4 Middleware 与 Runnable 的设计共性

中间件系统和 LCEL 的Runnable接口共享同一个设计哲学:协议胜于继承Runnable只要实现invoke就能接入管道,Middleware 只要实现@before_model@wrap_tool_call就能插入 Agent 生命周期。两者都通过"定义协议 + 自由组合"的方式实现了极高的扩展性。


八、踩坑记录与经验

  1. check_embedding_ctx_length=False的重要性:使用硅基流动等非 OpenAI 服务商时,必须显式设置此参数为False。默认True会让OpenAIEmbeddings使用tiktoken将文本转换为 token ID 数组发送给 API,但硅基流动不支持 token 化输入,会返回 20015 “参数无效” 错误。这个问题 debug 了很久才定位到。

  2. langchain-community被标记为 deprecated:在使用langchain_community.document_loaderslangchain_community.vectorstores时,会收到 DeprecationWarning,提示该包正在被逐步淘汰。官方建议迁移到独立的集成包(如langchain-chromalangchain-pdf等)。学习阶段不影响使用,但生产项目需要注意迁移路径。

  3. 中文文档的 separators 必须定制RecursiveCharacterTextSplitter默认分隔符是英文导向的(\n\n,\n, ,"")。处理中文文档时,如果不在 separators 中加入,分割器会跳过所有中文标点直接退化到空格或字符级切割,导致语义碎片化。

  4. VectorStore不是Runnable:向量存储对象不能直接用|接入管道,必须通过as_retriever()包装。这个细节容易忽略,直接写vectorstore | prompt会报类型错误。

  5. 工具名应使用 snake_case:部分模型提供商会拒绝包含空格或特殊字符的函数名。@tool装饰器默认使用函数名作为工具名,保持 Python 原生命名习惯即可。

  6. @before_model返回 None 的含义:当中间件通过修改state["messages"]原地生效后,返回None表示不需要额外更新 state。如果返回一个 dict,框架会用它来部分更新 state。


九、下周展望

第 3 周的主题是高级特性与生产级应用。从本周的认知出发:

  • RAG 的检索质量可以通过多路检索融合、重排序(Re-ranking)、HyDE 假设文档嵌入等高级策略进一步提升
  • Agent 的可靠性可以通过更多的中间件组合(如SummarizationMiddleware自动压缩长对话历史、HumanInTheLoopMiddleware在关键操作前暂停审批)来保障
  • checkpointer+thread_id的多轮对话能力将在实际应用中发挥核心作用
  • context_schema让 Agent 能感知用户身份、权限级别等运行时上下文,是构建多租户应用的基础

十、本周学习成果自检

  • 理解BaseLanguageModel的统一抽象设计,能对比不同模型在同一 prompt 下的输出差异
  • 掌握 temperature / max_tokens / top_p / frequency_penalty 四个参数的作用与调优哲学
  • 能使用RunnableBranch实现基于任务特征的模型路由器
  • 能使用至少 3 种文档加载器加载不同格式的文档
  • 理解RecursiveCharacterTextSplitter的递归降级分割机制,能为中文文档定制 separators
  • 掌握 embedding 模型的使用,理解check_embedding_ctx_length参数的含义
  • 能构建完整的 RAG Chain(Load → Split → Embed → Store → Retrieve → Generate)
  • 理解三种检索策略(similarity / MMR / threshold)的原理与适用场景
  • 能使用@tool装饰器和args_schema定义结构化的工具接口
  • 理解 ReAct 循环(Thought → Action → Observation)的运作机制
  • 能使用create_agent()构建完整的 Agent 系统
  • 理解checkpointer+thread_id实现多轮对话的原理
  • 能编写自定义 middleware(@before_model/@wrap_tool_call
  • 理解 ReAct 与 Function Calling 的异同及create_agent的自动适配策略
http://www.jsqmd.com/news/964207/

相关文章:

  • Agent S3:让AI像人类一样操作电脑的智能助手
  • YOLO26自适应注意力魔改:让模型在训练中自动决定选用通道还是空间注意力
  • 在线查询IP归属地攻略:三步锁定精确地理位置,新手也能用(2026版)
  • 百草枯农药残留检测卡快速检测果蔬中的百草枯农药残留
  • 2026流程图工具选型:5款产品深度对比,帮你找到最适合团队的方案
  • 新手入门:通过快马生成的代码轻松理解timed_out编程概念
  • Xilinx Virtex-5 FPGA DDR2 SDRAM接口调试全流程与避坑指南
  • 5分钟找回十年青春:GetQzonehistory一键备份QQ空间完整记忆
  • 特朗普手机号称美国制造却困难重重,真能实现目标吗?
  • 如何永久免费使用IDM:一键激活脚本完整指南
  • 终极Sketch标注插件:Sketch MeaXure完整指南,让设计交付效率提升300%
  • 深度修复:Flow Launcher文件搜索失效的3步诊断与解决方案
  • 2026甄选:涉密资质服务公司核心能力与适配性分析 - 品牌企业推荐师(官方)
  • 2026.06.06 6666666
  • 实测5种Prompt模板对比100次查询性能
  • 利用快马平台快速生成串口调试助手原型,十分钟搞定嵌入式通信测试工具
  • JS详解:Boolean()与!!双感叹号的区别、用法、底层原理(前端必看)
  • 从DAG到值编码:手把手教你用Python可视化编译原理中的表达式优化过程
  • QQ截图独立版:从零开始打造Windows最强截图工作流
  • 南京微短剧产业迎来“高光时刻”:“百部真人短剧集群”盛大开机 - 资讯速览
  • 零基础学全栈:借助快马AI生成‘面具公社’源码,轻松入门网页开发
  • 工程师招聘:从应试筛选到双向技术对话的实践与思考
  • 2026年免费在线抠图工具推荐:一看就会的网页版详细教程
  • PDF转Excel/PPT/图片及压缩,2026年度免费工具横评:速度、精度、隐私安全全对比 - 时时资讯
  • 2026年想去成都电竞网咖,哪家性价比高能让我玩得值
  • ai辅助开发:如何用快马平台的kimi模型迭代出理想中的跳转页面样式
  • OmenSuperHub终极指南:如何为惠普OMEN游戏本实现专业级性能控制
  • Linux串口工具不止minicom:CuteCom、Screen、Putty横向对比与选型指南
  • 挂耳式耳机什么牌子的好音质最好?本篇十款音质好的开放式耳机
  • CSDN AI数字营销究竟谁在用?:2024年覆盖12大行业的客户画像、预算分配与效果衰减阈值首次公开