LangChain 实践2 增强版智能知识库&结构化输出问答器

项目3:增强版智能知识库

考察内容

知识点:语义分块、MMR去重、上下文压缩、多向量检索(摘要/假设问题)、Self-querying元数据检索

功能

  1. 支持语义分割,不按字数硬切
  2. 检索结果 MMR 去重,避免重复内容
  3. 上下文压缩精简冗余文档
  4. 多向量:摘要检索、假设问题检索优化匹配
  5. Self-querying 自动解析时间/分类/评分等元数据过滤

价值

完成此项目,RAG所有高级玩法全部吃透

项目框架

文档加载

3-enhanced-knowledge-base/utils/loader.py

python 复制代码
# 文档加载器:读取本地 txt 文档
from langchain_community.document_loaders import TextLoader, PyMuPDFLoader
import os

# 參考
# 文件路径
# DOC_PATH = "./docs/test.txt"
# loader = TextLoader(DOC_PATH, encoding="utf-8")
# # 读取全文
# documents = loader.load()

def load_file(file_path):
    """加载单份文档并挂载基础元数据"""
    ext = os.path.splitext(file_path)[-1]
    print("ext: ", ext)
    if ext == ".txt":
        loader = TextLoader(file_path, encoding="utf-8")
    elif ext == ".pdf":
        loader = PyMuPDFLoader(file_path)
    else:
        raise ValueError("暂只支持txt、pdf格式")

    docs = loader.load()
    print("docs:",docs)
    # 绑定基础元数据
    for doc in docs:
        print("doc:",doc)
        doc.metadata["source"] = file_path
        doc.metadata["category"] = "技术文档"
    return docs

入口3-enhanced-knowledge-base/main.py

python 复制代码
from utils.loader import load_file

if __name__ == "__main__":
    # 后续把文档放进data/raw_docs里,替换路径测试
    test_path = "data/docs/test.txt"
    try:
        res = load_file(test_path)
        print("文档加载成功,片段数量:", len(res))
        print("res:",res)
    except Exception as e:
        print("加载异常:", e)

运行结果

语义分块 Semantic Chunking

RAG 高级核心功能第一步------语义分块

这一步非常关键,它决定了知识库的 "颗粒度" 是否聪明。不按字数硬切,而是根据语义相似度来断开文档。

使用LangChain自带的SemanticChunker对中文内容进行切块,效果很垃圾,SemanticChunker的相似度计算逻辑 + 分块策略对中文极不友好,调不出好效果。

改用 BCE + 自定义语义分块,这是目前国内最稳、最准、最不抽风的分块方式。

真正可用的分块方法

python 复制代码
# ======================
# 中文语义分块
# ======================
def split_text_by_semantic(text_list_or_str, max_chunk_len=800, similarity_threshold=0.65):
    # 自动把列表转成完整文本
    if isinstance(text_list_or_str, list):
        text = "\n".join([str(t) for t in text_list_or_str if t])
    else:
        text = str(text_list_or_str)

    # 按句子/段落切分
    sentences = []
    for line in text.split("\n"):
        line = line.strip()
        if len(line) > 5:
            sentences.append(line)

    embedding_model = get_embedding()
    if not sentences:
        return [text]

    chunks = []
    current = [sentences[0]]

    for s in sentences[1:]:
        try:
            vec1 = embedding_model.embed_documents([current[-1]])
            vec2 = embedding_model.embed_documents([s])
            sim = cosine_similarity(vec1, vec2)[0][0]
        except:
            sim = 0

        # 不相似 or 超长 → 切分
        if sim < similarity_threshold or len("\n".join(current)) > max_chunk_len:
            chunks.append("\n".join(current))
            current = [s]
        else:
            current.append(s)

    if current:
        chunks.append("\n".join(current))

    return chunks

运行结果

返回是list,元素是Document对象,包含page_content等属性(找了两篇公众号的内容当做素材,但是平台审核不通过,就不放截图了)

多向量检索(HyDE + 摘要向量)

  • 多向量检索:一个文档块 = 3 份向量

    • 原文向量
    • 摘要向量(LLM 总结这段话)
    • HyDE 假设问题向量(LLM 猜用户会怎么问)
  • 检索时 3 路同时搜 → 命中率暴涨

  • HyDE:假设文档嵌入,Hypothetical Document Embeddings

    • 用户问一个问题 → 先用 LLM 生成一段 "假设的答案文档" → 把这段假答案做 embedding → 用这个向量去库里搜最相似的真实文档。

核心代码

python 复制代码
# core/multi_vector_builder.py
from langchain_core.prompts import PromptTemplate
from langchain_core.documents import Document
from config.llm_config import get_llm


# 1. 生成摘要
def generate_summary(chunk):
    llm = get_llm()
    prompt = PromptTemplate(
        input_variables=["text"],
        template="请给下面的文本生成简洁摘要:\n{text}"
    )
    chain = prompt | llm
    return chain.invoke({"text": chunk.page_content})


# 2. 生成 HyDE 假设问题
def generate_hypothetical_questions(chunk):
    llm = get_llm()
    prompt = PromptTemplate(
        input_variables=["text"],
        template="根据文本,生成3个用户可能提出的问题:\n{text}"
    )
    chain = prompt | llm
    return chain.invoke({"text": chunk.page_content})


# 3. 构建多向量文档(原文+摘要+HyDE)
def build_multi_vector_docs(chunks):
    multi_docs = []

    for i, chunk in enumerate(chunks):
        # 基础元数据
        meta = chunk.metadata
        meta["chunk_id"] = f"chunk_{i}"

        # 原文文档
        multi_docs.append(Document(page_content=chunk.page_content, metadata=meta))

        # 摘要文档
        summary = generate_summary(chunk)
        multi_docs.append(Document(page_content=f"摘要:{summary}", metadata=meta))

        # 假设问题文档
        questions = generate_hypothetical_questions(chunk)
        multi_docs.append(Document(page_content=f"可能问题:{questions}", metadata=meta))

    print(f"构建完成,共生成 {len(multi_docs)} 个多向量条目")
    return multi_docs

结果结构分析

  • multi_docs = 纯列表 list[Document],里面每个元素都是带 page_content + metadata 的对象。

  • 多向量 = 1 个原文块 → 扩展成 3 个 Document

    • 第 1 个:原文
    • 第 2 个:摘要
    • 第 3 个:HyDE 假设问题
  • 向量库不知道它们是一组的,FAISS 只认一个个独立向量,不会自动分组。

  • 不管是去重、分组、还是合并结果,都要我们手动控制。

  • 真正工程上怎么用?

    • 存入时:给 metadata 打标记
    • 检索后:按 chunk_id 去重、合并、排序

运行失败

  1. 生成多向量失败,提示加载异常: 'str' object has no attribute 'metadata'。
    原因,生成多向量的方法semantic_chunks = split_text_by_semantic(raw_docs)返回的是字符串数组list,不是LangChain的Document对象,字符串没有metadata.
    解决:将字符串改回为Document.

运行结果

与分析的结构一致,list[Document],每个元素都是带 page_content + metadata 的对象。

向量库存储与多路检索

将向量存入FAISS.

  • 多向量检索 =1 个原文块 → 生成 3 条向量(原文 + 摘要 + HyDE)→ 一起存进 FAISS → 查询时 3 路同时匹配 → 最后按 chunk_id 去重拿回最相关的原文

    • 这就是大厂 RAG 系统的标准多路召回。
  • 本节要做的内容:

    • 给 multi_docs 打上类型标记(底层关键)
    • 存入向量库 + 实现多路检索 + 自动去重

核心代码

python 复制代码
# core/retriever.py
from langchain_community.vectorstores import FAISS
from config.embed_config import get_embedding


# 构建并保存向量库
def create_multi_vector_store(docs, save_path="faiss_multi_db"):
    embeddings = get_embedding()
    store = FAISS.from_documents(docs, embeddings)
    store.save_local(save_path)
    print("✅ 多路向量库存储完成")
    return store


# 核心:多路检索 + 自动去重
def multi_retrieve(query, vector_store, top_k=3):
    # 1. 多路召回(原文、摘要、问题一起匹配)
    retriever = vector_store.as_retriever(search_kwargs={"k": top_k * 3})
    docs = retriever.get_relevant_documents(query)

    # 2. 按 chunk_id 去重(同一组只保留一个)
    unique_chunks = []
    seen_ids = set()

    for doc in docs:
        cid = doc.metadata["chunk_id"]
        if cid not in seen_ids:
            seen_ids.add(cid)
            unique_chunks.append(doc)

            # 数量达标就终止
            if len(unique_chunks) >= top_k:
                break

    print(f"\n🎯 多路检索完成,去重后结果:{len(unique_chunks)} 条")
    return unique_chunks

运行结果

返回向量库的3个最相近的向量。

运行越来越慢。

look下各段的耗时情况。

  1. 加载文档,耗时:0:00:00.000999
  2. 语义分块,耗时:0:00:39.051776
  3. 生成多向量,耗时:0:04:56.142265
  4. 向量库存储,耗时:0:01:41.750813
  5. 多路检索,耗时:0:00:00.203046

优化思路

直接加载本地已保存的向量库,跳过加载文档、语义分块、生成多向量和存储向量库的操作。

python 复制代码
# 加载向量库
def load_multi_vector_store(save_path="faiss_multi_db"):
    embeddings = get_embedding()
    return FAISS.load_local(save_path, embeddings, allow_dangerous_deserialization=True)

开始判断本地是否存在向量库,存在则不加载,不存在则创建。

python 复制代码
if os.path.exists("faiss_multi_db"):
    store = load_multi_vector_store()
    step0 = datetime.now()
    print(f"向量库加载,耗时:{(step0 - start)}")
else:
	# 创建向量库

优化结果

  1. 向量库加载,耗时:0:00:00.339462
  2. 向量库存储/加载,耗时:0:00:00.339462
  3. 多路检索,耗时:0:00:00.517187

元数据检索 Self‑Querying

  • 元数据检索
    • 普通检索只能搜内容,Self‑Querying 能让大模型自动把自然语言转换成过滤规则。
    • 例如:"2024年之后发布的关于AI的文档",AI自动解析为 内容关键词:AI,元数据过滤:year > 2024

核心代码

python 复制代码
from config.llm_config import get_llm
from langchain_classic.chains.query_constructor.schema import AttributeInfo
from langchain_classic.retrievers import SelfQueryRetriever

# 定义:元数据字段说明(告诉AI每个字段是干嘛的)
metadata_field_info = [
    AttributeInfo(
        name="chunk_id",
        description="文本块的唯一编号",
        type="string"
    ),
    AttributeInfo(
        name="vector_type",
        description="向量类型:original=原文, summary=摘要, hyde=假设问题",
        type="string"
    ),
    AttributeInfo(
        name="source",
        description="文档来源",
        type="string"
    )
]

# 文档内容描述(帮助AI理解我们在检索什么)
document_content_description = "知识库文档内容"


def build_self_query_retriever(vector_store):
    """
    构建 Self-Query 智能检索器
    功能:自动把自然语言 → 过滤条件
    """
    llm = get_llm()

    # 创建官方自带的智能检索器
    self_query_retriever = SelfQueryRetriever.from_llm(
        llm,
        vector_store,
        document_content_description,
        metadata_field_info,
        verbose=True  # 打印解析过程(方便你看原理)
    )
    return self_query_retriever

运行失败

  • 输出:加载异常: Self query retriever with Vector Store type <class 'langchain_community.vectorstores.faiss.FAISS'> not supported.

  • 原因

    • 之前构建的向量库 store 是 FAISS → sq_retriever = build_self_query_retriever(store) → 传入的 store 就是 FAISS
    • SelfQueryRetriever 官方不支持 FAISS
  • 总结:LangChain 框架限制

    • FAISS = 不支持 Self-Query
    • Chroma / Pinecone / Milvus / PGVector = 支持 Self-Query
    • 没写错代码,是工具不支持 ✅
  • 解决

    • 继续用 FAISS ✅️
      • 把 MMR、多路检索学完
      • 元数据原理已经懂了,环境不支持暂时跳过,完全不影响掌握高级 RAG
    • 1 分钟把向量库从 FAISS 换成 Chroma然后立刻就能跑元数据检索

MMR 多样性检索

  • 普通检索:只返回复合度最高、但可能很重复、很相似的内容
  • MMR 检索
    • 最大边际相关性 Maximal Marginal Relevance

    • 在相关的基础上,保证结果多样性,不重复、覆盖面更广

    • 在 "和问题相关" 与 "结果之间不重复" 之间做平衡,既准确又多样。

    • MMR = λ× 相关性 − (1−λ)× 与已选结果的相似度

      • λ=1 → 退化为普通相似度(最相关、易重复)
      • λ=0 → 完全去重(最多样、易跑偏)
      • 常用 λ=0.5(均衡)
    • 搜索参数

      • k:最终输出结果条数
      • fetch_k:首轮筛选出的候选文档总数
      • 逻辑:先筛选足量高相关候选,再从中择优选出指定条数,平衡相关性与内容多样性

核心代码

python 复制代码
from config.embed_config import get_embedding


def mmr_retriever(vector_store, k=3):
    retriever = vector_store.as_retriever(search_type="mmr", search_kwargs={"k": k, "fetch_k":k * 3})
    return retriever

main.py测试

python 复制代码
# 7 mmr检索
retriever = mmr_retriever(store)
results = retriever.invoke("你要提问的问题")

运行结果

返回向量库的3个最相近的结果

上下文压缩 Contextual Compression

  • 上下文压缩
    • 检索回来的文档很长、很冗余
    • 压缩器会自动把 "和问题无关的废话删掉",只保留关键句子
    • → 让给 LLM 的内容更短、更精、更便宜、更快
    • 上下文压缩 = 给检索器套了一层壳,不是先检索完再手动压缩

伪代码示意图

python 复制代码
# 1. 先有基础检索器
retriever = store.as_retriever()

# 2. 套上压缩器 → 变成压缩检索器
compressed_retriever = 压缩器(retriever)

# 3. 用这个新的去检索 → 回来就是裁剪过的
result = compressed_retriever.invoke("问题")

核心代码

python 复制代码
from langchain_classic.retrievers import ContextualCompressionRetriever
from config.llm_config import get_llm
from langchain_classic.retrievers.document_compressors import LLMChainExtractor


def create_compression_retriever(retriever):
    # 定义压缩器
    extractor = LLMChainExtractor.from_llm(get_llm())
    # 把 基础retriever + 压缩器 包装在一起
    compressed_retriever = ContextualCompressionRetriever(base_retriever=retriever, base_compressor=extractor)
    # 返回 压缩后的检索器
    return compressed_retriever

main.py测试

python 复制代码
 # 8 上下文压缩(mmr检索)
 retriever = create_compression_retriever(mmr_retriever(store))
 results = retriever.invoke("输入你的问题")

运行结果

返回向量库最相近的2条向量。

为什么mmr检索 + 上下文压缩只返回了2条内容,而mmr检索返回了3条

检索数量由底层检索器参数控制,压缩层不改变文档条数,只裁剪内容。在生成 MMR 检索器时指定 k 值即可限定返回数量。

压缩仅精简单篇文本内容,不会增减文档条目数。

mmr检索 + 上下文压缩的底层检索器是mmr检索器,而mmr检索器实际返回了3条,因此还是与实际结果对不上。

真实原因:LLMChainExtractor 自己会删文档。

提取器觉得某条文档和问题无关,它会直接整条丢掉,不告诉你,不报错 → 所以数量变少

因此实际现象符合组件特性:MMR 检索固定取回 3 条候选文档,上下文压缩会判定剔除无关文档,最终留存 2 条有效内容。文本做精简裁剪,无效条目直接舍弃。

项目4:结构化输出问答器

项目4:结构化输出问答器(Pydantic + 自定义OutputParser)

知识点:Pydantic结构化输出、自定义解析器、类NER信息抽取、RunnableLambda

功能

  • 输入自然语言,自动抽取:天气/时间/地址/商品信息
  • 强制固定JSON结构输出,失败自动校验重试

项目分析

模型必须严格按固定 JSON 格式输出。

例如:输入一句自然话,"明天北京会下雨吗?我想买件雨衣。"

输出

python 复制代码
{
  "weather": "下雨",
  "time": "明天",
  "address": "北京",
  "goods": "雨衣"
}

核心能力

  • 信息抽取(类 NER):抽天气、时间、地址、商品
  • 强制结构化输出:用 Pydantic 定义格式
  • 自定义解析器:把 LLM 输出转成标准对象
  • 失败重试:解析失败自动重新问 LLM
  • 链式调用:用 RunnableLambda 组装流程

Pydantic 定义结构化数据模型

  • 实体:Pydantic 是第三方 Python 库,属于项目依赖。
  • 用法:用它定义数据结构、做数据校验、约束格式的写法,就是一种结构化编程模式。
  • 作用:强制输出格式、自动校验、防止乱输出

核心代码

python 复制代码
from pydantic import BaseModel, Field

class  CustomModel(BaseModel):
    weather: str = Field(default="", description="抽取的天气信息,如晴天、下雨、多云")
    time: str = Field(default="", description="抽取的时间信息,如今天、明天、周一")
    address: str = Field(default="", description="抽取的地址信息,如北京、上海、小区")
    goods: str = Field(default="", description="抽取的商品信息,如雨衣、手机、奶茶")

# default=""
# 意思是:没抽到信息就默认为空字符串
# description="..."
# 意思是:给大模型看的说明,告诉它该抽什么内容

拓展:选型分析

模型选择

是选择llm还是chatmodel?

结论

这个项目必须用 ChatModel(对话模型),普通 LLM(补全模型)不是不能用,但非常不推荐!

原因

  1. 结构更标准
    • ChatModel 用 system + human + ai 三段式对话
    • 大模型最吃这套提示词,抽取准确率最高
  2. 输出 JSON 更稳定
    • 普通 LLM 是续写文本,容易乱输出
      • 普通 LLM(比如 LLM 类、OpenAI 类)是文本补全模型,输入:今天天气...输出:...很好,适合出去玩
      • 不是为 "对话" 设计的,很难强制输出标准 JSON,会经常失败
    • ChatModel 专门用来 "回答问题",更听话
  3. LangChain 官方最佳实践
    • 结构化输出、自定义解析、Agent 开发全部都是基于 ChatModel 设计的

编写提示词模板

写提示词 ChatPromptTemplate,作用:告诉大模型:你必须严格按照定义的 Pydantic 格式输出 JSON,不许说废话。

python 复制代码
# 提示词模板:构造给大模型的提问格式
from langchain_core.prompts import ChatPromptTemplate

if __name__ == "__main__":
    chat_prompt_template = ChatPromptTemplate.from_messages([
        ("system", """你必须严格遵守以下规则:
    1. 从用户输入中抽取4个字段:天气(weather)、时间(time)、地址(address)、商品(goods)
    2. 只输出纯净的JSON,不要解释、不要废话
    3. 没有信息的字段填空字符串"""),
        ("user", "{user_input}")
    ])

    chat_prompt = chat_prompt_template.invoke("今天天气不错")
    print(chat_prompt)

运行结果

python 复制代码
messages=[SystemMessage(content='你必须严格遵守以下规则:\n    1. 从用户输入中抽取4个字段:天气(weather)、时间(time)、地址(address)、商品(goods)\n    2. 只输出纯净的JSON,不要解释、不要废话\n    3. 没有信息的字段填空字符串', additional_kwargs={}, response_metadata={}), HumanMessage(content='今天天气不错', additional_kwargs={}, response_metadata={})]

初始化ChatModel

核心代码

python 复制代码
from langchain_community.chat_models import ChatTongyi

chat_model = ChatTongyi(
    model="qwen-plus",  # 模型:qwen-turbo(轻量、快、便宜)/plus(均衡(RAG 首选))/max(最强、慢、贵)
    dashscope_api_key=tongyi_api_key,  # 阿里云 API Key(或环境变量)
    temperature=0.7,  # 随机性
    max_tokens=2048,
    streaming=True,  # 流式返回(RAG 生成常用)
    verbose=False
)

res = chat_model.invoke(chat_prompt)
print(res)

运行结果

python 复制代码
content='{"weather":"不错","time":"今天","address":"","goods":""}' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'request_id': '3fe9965a-9243-91ef-8062-1cb5a08b22a3', 'token_usage': {'input_tokens': 84, 'output_tokens': 14, 'total_tokens': 98, 'prompt_tokens_details': {'cached_tokens': 0}}} id='lc_run--019e54c4-f4ab-7c50-9138-fde38656ebb2' tool_calls=[] invalid_tool_calls=[]

搭建基础调用链路

  1. 用管道符拼接 提示词模板 + 模型,组成基础可执行链
  2. 先简单测试调用逻辑,暂不做解析

核心代码

python 复制代码
chain = chat_prompt_template | chat_model
# chain.invoke("今天天气不错")  # ❌ 报错,原因:LangChain 链要求:必须传键值对 dict
res = chain.invoke({"user_input": "今天天气不错"})
print(res)

运行结果

python 复制代码
content='{"weather":"不错","time":"今天","address":"","goods":""}' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'request_id': '072bb037-34c2-9c58-84a9-1da4be03f720', 'token_usage': {'input_tokens': 84, 'output_tokens': 14, 'total_tokens': 98, 'prompt_tokens_details': {'cached_tokens': 0}}} id='lc_run--019e54ce-6ec7-78f0-ab04-33c37d229ab4' tool_calls=[] invalid_tool_calls=[]

自定义解析器

目前ChatModel和LangChain链输出的都是

python 复制代码
AIMessage(content='{"weather":"下雨", ...}')

我们希望输出的格式为

python 复制代码
CustomModel(weather='下雨', time='明天', address='北京', goods='')

这就是解析器的作用。

核心代码

python 复制代码
from langchain_core.output_parsers import BaseOutputParser
import json

class MyParser(BaseOutputParser):
    def parse(self, output: str):
        # 字符串转字典
        obj = json.loads(output)
        # 字典拆包给Pydantic
        res = InfoExtraction(**obj)
        return res
strParse = MyParser()
chain = chat_prompt_template | chat_model | strParse

运行结果

python 复制代码
weather='不错' time='今天' address='' goods=''

失败自动重试

  • 目标

    • 成功:模型输出合法 JSON → 解析成功
    • 失败:LLM 输出格式错误 / 解析异常
      → 不崩溃,自动重试调用模型,直到解析成功
  • LangChain重试机制

    • LangChain 链路默认不会重试,抛异常 = 直接终止程序
    • ✅ 真正重试:必须使用 tenacity 重试库 + 装饰器 @retry
  • 实现重试的两个关键

    • try/except:捕获解析错误(JSON 错误、Pydantic 校验错误)
    • raise 主动抛错:告诉重试库 "本次失败,需要重试"
    • @retry 装饰器:控制重试次数、间隔(真正执行重试)
  • 完整流程

    python 复制代码
    调用模型
    ↓
    获取输出
    ↓
    try 尝试解析
        成功 → 返回结果
        失败 → except 捕获 → 主动抛错 → 触发重试库重新执行整条链
  • 两种必捕获的异常

    • json.JSONDecodeError

      • 原因:模型返回的不是标准 JSON
      • 位置:json.loads() 阶段
    • ValidationError(Pydantic)

      • 原因:JSON 格式正确,但字段缺失 / 类型错误
      • 位置:InfoExtraction(**data) 阶段
    • 可统一捕获:

      python 复制代码
      except Exception as e:
          raise ValueError("解析失败,请重试")
  • RunnableLambda 包装

    • 管道符 | 只接受 Runnable 类型组件,不认普通函数
    • LangChain 里的管道 chain = A | B | C,A、B、C 必须都是 Runnable 类型
    • 什么是 Runnable?
      • prompt
      • llm
      • parser
      • 工具
      • RunnableLambda (普通函数)
    • def retry_parser (output): 普通函数 ≠ Runnable → 直接放进管道会报错
    • RunnableLambda
      • chain = prompt | llm | RunnableLambda(retry_parser)
      • 作用:把普通 Python 函数 → 变成管道能识别的 "可运行节点"

核心代码(含模拟异常代码)

python 复制代码
from langchain_core.runnables import RunnableLambda
from tenacity import retry, stop_after_attempt, wait_fixed

retry_count = 0

@retry(stop=stop_after_attempt(2), wait=wait_fixed(0.5))
def retry_parser(output):
    try:
        global retry_count
        retry_count += 1
        print(f"第 {retry_count} 次解析执行")
        # 强制失败,必重试
        raise ValueError("手动模拟失败!")
        # 自定义 BaseOutputParser LangChain 会自动调用 .content
        # RunnableLambda (普通函数) 不会自动取 content,直接把 AIMessage 丢过来
        str = output.content
        res = InfoExtraction(**json.loads(str))
        return res
    except Exception as e:
        raise ValueError("解析失败,请重试")

    chain = chat_prompt_template | chat_model | RunnableLambda(retry_parser)

运行结果

第1次解析失败,自动进行第2次。第2次失败不会触发重试。

  • 重试次数
    • stop_after_attempt(2)
    • = 首次执行 + 重试 1 次 = 总共跑 2 次 = 异常后只再试 1 次 ✅
相关推荐
Artech11 小时前
[对比学习LangChain和MAF-04]针对消息的设计
ai·langchain·agent·message·maf
Rsingstarzengjx11 小时前
LangChain 是什么
langchain
wuxinyan12312 小时前
工业级大模型学习之路025:问题解决-检索质量全为0
人工智能·python·学习·langchain
wuxinyan1231 天前
工业级大模型学习之路023:LangChain零基础入门教程(第六篇):重排序与高级检索策略
人工智能·python·学习·langchain
abigale031 天前
LangChain 实践 极简个人知识库问答机器人&自定义大模型封装+通用对话链
langchain·prompt·rag·lcel
njsgcs1 天前
那langchain的reAct是怎么实现的
人工智能·langchain
swipe1 天前
Elasticsearch 全文检索工程教程:倒排索引、IK 分词器与 BM25 从原理到落地
面试·langchain·llm
SuniaWang1 天前
《Agentx专栏》02-技术选型:预算有限时如何做出正确的技术决策
java·spring·架构·langchain·milvus·agenx·opl
唐璜Taro1 天前
LangChain与LangGraph多Agent实战:从工具链到工作流编排(上)
langchain·agent·langgraph