深入LangChain:模型封装与输出解析器

核心知识点讲解

模型封装(Model Wrappers)

LangChain提供了统一的模型封装接口,使开发者能够轻松集成各种语言模型。这些封装不仅提供了一致的API,还添加了一些实用功能,如重试机制、速率限制处理等。

1. 语言模型类型

LangChain支持多种类型的语言模型:

  • LLM:基础语言模型,如GPT-3.5、GPT-4等,接收文本输入并返回文本输出
  • ChatModel:聊天模型,如ChatGPT,使用消息格式进行交互
  • TextEmbeddingModel:文本嵌入模型,将文本转换为向量表示

2. 常见模型封装

LangChain内置了对多种模型提供商的支持:

  • OpenAI:GPT系列模型
  • Hugging Face:各种开源模型
  • Google Vertex AI:PaLM模型
  • Anthropic:Claude模型
  • Azure OpenAI:Azure上的OpenAI模型
  • 国内模型:如智谱AI、百度文心一言、阿里通义千问等

输出解析器(Output Parsers)

输出解析器用于将模型的原始输出转换为更结构化的格式,使后续处理更加容易。

1. 解析器类型

LangChain提供了多种类型的输出解析器:

  • 基础解析器:简单的文本处理,如分隔符解析
  • 结构化解析器:将输出转换为JSON、字典等结构化格式
  • 列表解析器:将输出转换为列表
  • Pydantic解析器:使用Pydantic模型进行类型安全的解析
  • 多模态解析器:处理文本和其他模态的输出

实用案例分析

案例1:使用不同类型的语言模型

from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings

# 1. 使用基础LLM
llm = OpenAI(temperature=0.7)
response = llm("什么是人工智能?")
print("LLM响应:", response)

# 2. 使用ChatModel
chat_model = ChatOpenAI(temperature=0.7)
from langchain.schema import HumanMessage, SystemMessage
messages = [
    SystemMessage(content="你是一个专业的AI助手"),
    HumanMessage(content="什么是人工智能?")
]
chat_response = chat_model(messages)
print("ChatModel响应:", chat_response.content)

# 3. 使用嵌入模型
embedding_model = OpenAIEmbeddings()
embedding = embedding_model.embed_query("什么是人工智能?")
print("嵌入向量长度:", len(embedding))
print("前5个值:", embedding[:5])

案例2:自定义语言模型封装

from langchain.llms.base import LLM
from typing import Optional, List, Mapping, Any
import requests

class CustomLLM(LLM):
    api_url: str
    api_key: str
    
    @property
    def _llm_type(self) -> str:
        return "custom"
    
    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        data = {
            "prompt": prompt,
            "max_tokens": 1000
        }
        if stop:
            data["stop"] = stop
        
        response = requests.post(self.api_url, headers=headers, json=data)
        response.raise_for_status()
        return response.json()["text"]
    
    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        return {
            "api_url": self.api_url
        }

# 使用自定义模型
custom_llm = CustomLLM(
    api_url="https://api.example.com/llm",
    api_key="your-api-key"
)

response = custom_llm("什么是人工智能?")
print(response)

案例3:使用不同类型的输出解析器

from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI

# 1. 基础解析器:使用分隔符
from langchain.output_parsers import DelimitedListOutputParser

output_parser = DelimitedListOutputParser(separator=",")

prompt = PromptTemplate(
    template="列出5个{topic}相关的概念,用逗号分隔。\n{format_instructions}",
    input_variables=["topic"],
    partial_variables={"format_instructions": output_parser.get_format_instructions()}
)

llm = OpenAI(temperature=0)

chain = prompt | llm | output_parser
result = chain.invoke({"topic": "人工智能"})
print("分隔符解析器结果:", result)

# 2. 结构化解析器:JSON格式
from langchain.output_parsers import ResponseSchema, StructuredOutputParser

response_schemas = [
    ResponseSchema(name="definition", description="术语的定义"),
    ResponseSchema(name="applications", description="应用领域列表"),
    ResponseSchema(name="advantages", description="优点列表"),
    ResponseSchema(name="disadvantages", description="缺点列表")
]

output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

prompt = PromptTemplate(
    template="请提供{term}的详细信息,包括定义、应用领域、优点和缺点。\n{format_instructions}",
    input_variables=["term"],
    partial_variables={"format_instructions": output_parser.get_format_instructions()}
)

chain = prompt | llm | output_parser
result = chain.invoke({"term": "人工智能"})
print("JSON解析器结果:")
print("定义:", result["definition"])
print("应用领域:", result["applications"])
print("优点:", result["advantages"])
print("缺点:", result["disadvantages"])

# 3. Pydantic解析器:类型安全
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List

class AIInfo(BaseModel):
    definition: str = Field(description="术语的定义")
    applications: List[str] = Field(description="应用领域列表")
    advantages: List[str] = Field(description="优点列表")
    disadvantages: List[str] = Field(description="缺点列表")

output_parser = PydanticOutputParser(pydantic_object=AIInfo)

prompt = PromptTemplate(
    template="请提供{term}的详细信息,包括定义、应用领域、优点和缺点。\n{format_instructions}",
    input_variables=["term"],
    partial_variables={"format_instructions": output_parser.get_format_instructions()}
)

chain = prompt | llm | output_parser
result = chain.invoke({"term": "人工智能"})
print("Pydantic解析器结果:")
print("类型:", type(result))
print("定义:", result.definition)
print("应用领域:", result.applications)
print("优点:", result.advantages)
print("缺点:", result.disadvantages)

代码解析

模型封装

  1. 基础LLMOpenAI类封装了OpenAI的文本补全API,提供了简单的文本输入输出接口。

  2. ChatModelChatOpenAI类封装了OpenAI的聊天API,使用消息格式进行交互,更适合多轮对话场景。

  3. 嵌入模型OpenAIEmbeddings类封装了OpenAI的嵌入API,用于将文本转换为向量表示,常用于检索和相似度计算。

  4. 自定义模型:通过继承LLM基类并实现_call方法,可以创建自定义的模型封装,集成任何语言模型API。

输出解析器

  1. 分隔符解析器DelimitedListOutputParser使用指定的分隔符将模型输出分割成列表,适用于需要多个项目的场景。

  2. 结构化解析器StructuredOutputParser将模型输出解析为JSON格式,需要定义响应模式来指导模型生成结构化输出。

  3. Pydantic解析器PydanticOutputParser使用Pydantic模型进行解析,提供类型安全的输出,自动处理类型转换和验证。

  4. LCEL语法:使用|操作符可以简洁地构建处理链,将提示词模板、模型和解析器连接在一起。

高级技巧

1. 自定义输出解析器

对于复杂的输出格式,可以创建自定义的输出解析器:

from langchain.output_parsers import BaseOutputParser
from typing import List

class CustomOutputParser(BaseOutputParser[dict]):
    def parse(self, text: str) -> dict:
        # 自定义解析逻辑
        lines = text.strip().split('\n')
        result = {}
        for line in lines:
            if ':' in line:
                key, value = line.split(':', 1)
                result[key.strip()] = value.strip()
        return result
    
    def get_format_instructions(self) -> str:
        return "请按照'键: 值'的格式输出,每行一个键值对。"

# 使用自定义解析器
parser = CustomOutputParser()
prompt = PromptTemplate(
    template="请提供{term}的信息。\n{format_instructions}",
    input_variables=["term"],
    partial_variables={"format_instructions": parser.get_format_instructions()}
)

chain = prompt | llm | parser
result = chain.invoke({"term": "人工智能"})
print("自定义解析器结果:", result)

2. 模型配置优化

可以通过配置模型参数来优化性能和成本:

# 配置模型参数
llm = OpenAI(
    temperature=0.7,          # 创造性
    max_tokens=1000,           # 最大输出 tokens
    top_p=0.95,                # 采样阈值
    frequency_penalty=0,       # 频率惩罚
    presence_penalty=0,        # 存在惩罚
    model_name="gpt-3.5-turbo-instruct",  # 模型名称
    request_timeout=30,         # 请求超时
    max_retries=3              # 最大重试次数
)

# 配置聊天模型
chat_model = ChatOpenAI(
    temperature=0.7,
    model_name="gpt-4",
    max_tokens=2000,
    request_timeout=60,
    max_retries=3
)

3. 批量处理

对于多个输入,可以使用批量处理提高效率:

# 批量处理多个输入
inputs = ["什么是人工智能?", "什么是机器学习?", "什么是深度学习?"]
responses = llm.generate(inputs)

for i, generation in enumerate(responses.generations):
    print(f"输入: {inputs[i]}")
    print(f"输出: {generation[0].text}")
    print()

总结

LangChain的模型封装和输出解析器提供了强大的工具,使开发者能够:

  1. 轻松集成各种语言模型:统一的接口和内置的封装使集成不同模型变得简单
  2. 自定义模型集成:通过继承基类可以集成任何语言模型API
  3. 结构化输出:使用各种解析器将模型输出转换为结构化格式
  4. 类型安全:Pydantic解析器提供类型安全的输出处理
  5. 灵活的处理链:LCEL语法使构建处理流程变得简洁明了

这些功能为我们构建复杂的AI智能体系统提供了坚实的基础。在接下来的几集中,我们将学习如何使用这些组件构建更复杂的应用,如问答系统、对话机器人等。

课后思考

  1. 如何选择合适的语言模型类型(LLM vs ChatModel)?
  2. 什么时候应该创建自定义的输出解析器?
  3. 如何优化模型配置以平衡性能、成本和质量?
  4. 如何处理模型输出的不确定性和错误?

在下一集中,我们将学习如何构建第一条链:简单的问答链,将模型封装和输出解析器应用到实际场景中。

« 上一篇 LangChain核心概念:Model I/O、Retrieval、Chains 下一篇 » 构建第一条链:简单的问答链