👦抠腚男孩的AI学习之旅 | 5、玩转 LangChain (一)

1. 概念相关

1.1. LangChain 定义

LangChain 是一个旨在帮助开发者更轻松、更灵活地构建基于 LLM 的应用程序的 开源框架

1.2. 传统 AI 应用开发的痛点

🤡 只有经历过 "手撕脚本 " 调 LLM 的人才知道 LangChain 这套 "工具集 " 有多香,Talk is cheap,show you the Code,通过代码的对比来展示这个库开发 LLM APP 的高效性。【完整可运行源码:c1\d1\traditional_llm_app_problem.py】

上下文管理:需手动管理对话历史,代码示例:

python 复制代码
# 初始化OpenAI客户端
ai_client = openai.OpenAI(
    api_key="sk-xxx",
    base_url="https://xxx.xxx.xxx/v1"
)

# ====== 痛点1:上下文管理 ======
def traditional_conversation_management():
    print("=== 传统方式:多轮对话 ===")
    # 💡 初始对话历史
    conversation_history = []
    # 第一轮对话
    user_input1 = "我正在学习LangChain,推荐一些入门书籍"
    conversation_history.append({"role": "user", "content": user_input1})
    response1 = ai_client.chat.completions.create(
        model="gemini-2.5-flash-lite",
        messages=conversation_history,
    )
    assistant_response1 = response1.choices[0].message.content
    # 💡 区分角色,手动追加
    conversation_history.append({"role": "assistant", "content": assistant_response1})
    print(f"助手: {assistant_response1}")
    
    # 第二轮对话 - 手动管理上下文
    user_input2 = "这些书籍中哪本最适合零基础的人?"
    conversation_history.append({"role": "user", "content": user_input2})
    response2 = ai_client.chat.completions.create(
        model="gemini-2.5-flash-lite",
        messages=conversation_history  # 必须手动传递完整历史
    )
    assistant_response2 = response2.choices[0].message.content
    # 💡 区分角色,手动追加
    conversation_history.append({"role": "assistant", "content": assistant_response2})
    print(f"助手: {assistant_response2}")
    
    # 第三轮对话 - 继续手动管理,容易出错
    user_input3 = "能给我制定一个学习计划吗?"
    conversation_history.append({"role": "user", "content": user_input3})
    response3 = ai_client.chat.completions.create(
        model="gemini-2.5-flash-lite",
        messages=conversation_history
    )
    assistant_response3 = response3.choices[0].message.content
    print(f"助手: {assistant_response3}")
    print(f"对话历史长度: {len(conversation_history)} 条消息")

多步骤任务:需要手动处理每个步骤,错误处理复杂,定位具体失败步骤麻烦,代码示例:

python 复制代码
# ====== 痛点2:多步骤任务 ======
def traditional_multi_step_task():
    print("\n=== 传统方式:文档分析任务 ===")
    document_content = """
    公司2023年第三季度财报显示,营收增长15%,达到500万美元。
    主要增长来源于新产品线的推出和海外市场拓展。
    但是,运营成本也上升了12%,主要由于人员扩张和营销投入增加。
    净利润为50万美元,同比增长8%。
    """
    try:
        # 步骤1:文档摘要
        print("步骤1:生成文档摘要...")
        summary_prompt = f"请为以下文档生成简洁摘要:\n{document_content}"
        summary_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": summary_prompt}]
        )
        summary = summary_response.choices[0].message.content
        print(f"摘要: {summary}")
        
        # 步骤2:关键指标提取
        print("\n步骤2:提取关键财务指标...")
        metrics_prompt = f"从以下文档中提取所有数值指标,格式为JSON:\n{document_content}"
        metrics_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": metrics_prompt}]
        )
        metrics = metrics_response.choices[0].message.content
        print(f"关键指标: {metrics}")
        
        # 步骤3:趋势分析
        print("\n步骤3:进行趋势分析...")
        analysis_prompt = f"""
        基于以下摘要和指标,分析公司发展趋势:
        摘要: {summary}
        指标: {metrics}
        """
        analysis_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": analysis_prompt}]
        )
        analysis = analysis_response.choices[0].message.content
        print(f"趋势分析: {analysis}")
        
        # 步骤4:生成建议
        print("\n步骤4:生成改进建议...")
        recommendation_prompt = f"""
        基于以下分析,提供具体的改进建议:
        {analysis}
        """
        recommendation_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": recommendation_prompt}]
        )
        recommendations = recommendation_response.choices[0].message.content
        print(f"改进建议: {recommendations}")
        
    except Exception as e:
        print(f"错误发生在某个步骤: {e}")

外部工具集成:需大量样板代码,工具选择逻辑复杂,代码示例:

python 复制代码
# ====== 痛点3:外部工具集成 (天气查询+推荐系统) ======
def traditional_tool_integration():
    print("\n=== 传统方式:工具集成 ===")

    def get_weather(city: str) -> str:
        """获取天气信息的模拟函数"""
        weather_data = {
            "北京": "晴天,温度25°C,湿度60%",
            "上海": "多云,温度22°C,湿度75%",
            "广州": "雨天,温度28°C,湿度85%"
        }
        return weather_data.get(city, "未找到该城市天气信息")
     
    def get_restaurant_recommendations(city: str, weather: str) -> List[str]:
        """根据天气推荐餐厅的模拟函数"""
        if "晴天" in weather:
            return ["露天BBQ餐厅", "花园咖啡厅", "屋顶酒吧"]
        elif "雨天" in weather:
            return ["温馨火锅店", "室内日料店", "舒适茶餐厅"]
        else:
            return ["中式餐厅", "西式简餐", "快餐连锁"]
    
    # 用户询问
    user_query = "我在北京,今天适合去哪些餐厅?"
    
    # 手动解析用户意图
    print("步骤1:解析用户意图...")
    intent_prompt = f"用户询问:{user_query}\n请判断用户想查询哪个城市的信息,只返回城市名称。"
    
    try:
        intent_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": intent_prompt}]
        )
        
        city = intent_response.choices[0].message.content.strip()
        print(f"识别城市: {city}")
        
        # 手动调用工具1:获取天气
        print("步骤2:获取天气信息...")
        weather = get_weather(city)
        print(f"天气信息: {weather}")
        
        # 手动调用工具2:获取推荐
        print("步骤3:获取餐厅推荐...")
        restaurants = get_restaurant_recommendations(city, weather)
        print(f"推荐餐厅: {restaurants}")
        
        # 手动整合结果
        print("步骤4:整合并生成回复...")
        final_prompt = f"""
        基于以下信息为用户生成友好的回复:
        用户询问: {user_query}
        城市: {city}
        天气: {weather}
        推荐餐厅: {', '.join(restaurants)}
        """
        final_response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=[{"role": "user", "content": final_prompt}]
        )
        result = final_response.choices[0].message.content
        print(f"最终回复: {result}")
        
    except Exception as e:
        print(f"工具集成失败: {e}")

应用扩展性:添加新功能需要修改大量代码,扩展性差,代码示例:

python 复制代码
# ====== 痛点4:应用扩展性差 (聊天机器人) ======
class TraditionalChatBot:
    def __init__(self):
        """初始化聊天机器人"""
        self.conversation_history = []
        self.supported_functions = ["天气查询", "餐厅推荐", "新闻搜索"]
    
    def process_message(self, user_input: str) -> str:
        """处理用户消息的主要逻辑"""
        try:
            # 硬编码的意图识别
            if "天气" in user_input:
                return self._handle_weather_query(user_input)
            elif "餐厅" in user_input or "吃饭" in user_input:
                return self._handle_restaurant_query(user_input)
            elif "新闻" in user_input:
                return self._handle_news_query(user_input)
            else:
                return self._handle_general_chat(user_input)
                
        except Exception as e:
            return f"抱歉,处理您的请求时出现错误:{e}"
    
    def _handle_weather_query(self, user_input: str) -> str:
        """处理天气查询"""
        # 需要手动实现所有逻辑
        return "天气查询功能需要大量代码实现..."
    
    def _handle_restaurant_query(self, user_input: str) -> str:
        """处理餐厅推荐"""
        # 又是一大堆硬编码逻辑
        return "餐厅推荐功能需要大量代码实现..."
    
    def _handle_news_query(self, user_input: str) -> str:
        """处理新闻搜索"""
        # 每个功能都要重复写相似的代码
        return "新闻搜索功能需要大量代码实现..."
    
    def _handle_general_chat(self, user_input: str) -> str:
        """处理普通聊天"""
        # 手动管理上下文
        self.conversation_history.append({"role": "user", "content": user_input})
        
        response = ai_client.chat.completions.create(
            model="gemini-2.5-flash-lite",
            messages=self.conversation_history
        )
        assistant_response = response.choices[0].message.content
        self.conversation_history.append({"role": "assistant", "content": assistant_response})
        return assistant_response
    
    def add_new_function(self, function_name: str):
        """添加新功能(展示扩展困难)"""
        print(f"要添加'{function_name}'功能,需要:")
        print("1. 修改 process_message 方法添加新的条件判断")
        print("2. 实现新的 _handle_xxx_query 方法")
        print("3. 可能需要修改意图识别逻辑")
        print("4. 添加相应的错误处理")
        print("5. 更新支持功能列表")
        print("扩展一个功能就要改动这么多地方,太痛苦了! (╯°□°)╯︵ ┻━┻")

1.3. 基于 LangChain 实现

接着用 LangChain 来实现相同的功能【完整源码:c1\d1\langchain_solutions_examples.py】

上下文管理:自动管理对话历史,代码示例:

python 复制代码
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain, LLMChain
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.runnables import ConfigurableFieldSpec


# ====== 解决方案1:自动上下文管理  ======
def langchain_conversation_management():
    print("=== LangChain方式:多轮对话 ===")
    # 初始化LLM和内存
    llm = ChatOpenAI(
        temperature=0.7,
        openai_api_key="sk-xxx",
        base_url="https://xxx.xxx.xxx/v1",
        model = "gemini-2.5-flash-lite"
    )
    memory = ConversationBufferMemory(return_messages=True)
    # 创建对话链
    conversation = ConversationChain(
        llm=llm, memory=memory, verbose=True  # 显示内部处理过程
    )
    # 第一轮对话
    response1 = conversation.predict(input="我正在学习LangChain ,推荐一些入门书籍")
    print(f"助手: {response1}")

    # 第二轮对话 - 自动维护上下文
    response2 = conversation.predict(input="这些书籍中哪本最适合零基础的人?")
    print(f"助手: {response2}")

    # 第三轮对话 - 继续自动维护上下文
    response3 = conversation.predict(input="能给我制定一个学习计划吗?")
    print(f"助手: {response3}")

多步骤任务:链式处理,错误自动传播,代码示例:

python 复制代码
# ====== 解决方案2:多步骤任务 - 链式处理 ======
class DocumentAnalysisChain:
    """
    LangChain方式:用链式处理实现复杂文档分析
    每个步骤都是独立的链,易于维护和测试
    """

    def __init__(self):
        """初始化分析链"""
        self.llm = ChatOpenAI(
        temperature=0.3,
        openai_api_key="sk-xxx",
        base_url="https://xxx.xxx.xxx/v1",
        model = "gemini-2.5-flash-lite"
        )
        self._setup_chains()
    
    def _setup_chains(self):
        """设置各个处理链"""
        # 文档摘要链
        summary_prompt = PromptTemplate(
            input_variables=["document"],
            template="请为以下文档生成简洁摘要,突出关键信息:\n{document}"
        )
        self.summary_chain = LLMChain(
            llm=self.llm,
            prompt=summary_prompt,
            output_key="summary"
        )
        
        # 指标提取链
        metrics_prompt = PromptTemplate(
            input_variables=["document"],
            template="""
            从以下文档中提取所有数值指标,以JSON格式返回:
            {document}
            
            请按以下格式返回:
            {{"营收": "具体数值", "增长率": "具体数值", "成本": "具体数值"}}
            """
        )
        self.metrics_chain = LLMChain(
            llm=self.llm,
            prompt=metrics_prompt,
            output_key="metrics"
        )
        
        # 趋势分析链
        analysis_prompt = PromptTemplate(
            input_variables=["summary", "metrics"],
            template="""
            基于以下信息分析公司发展趋势:
            
            文档摘要:{summary}
            关键指标:{metrics}
            
            请从以下角度分析:
            1. 财务健康状况
            2. 增长趋势
            3. 潜在风险
            """
        )
        self.analysis_chain = LLMChain(
            llm=self.llm,
            prompt=analysis_prompt,
            output_key="analysis"
        )
        
        # 建议生成链
        recommendation_prompt = PromptTemplate(
            input_variables=["analysis"],
            template="""
            基于以下趋势分析,提供3-5条具体的改进建议:
            {analysis}
            
            每条建议请包含:
            - 具体行动
            - 预期效果
            - 实施难度
            """
        )
        self.recommendation_chain = LLMChain(
            llm=self.llm,
            prompt=recommendation_prompt,
            output_key="recommendations"
        )
        
        # 组合成顺序链
        self.full_chain = SequentialChain(
            chains=[
                self.summary_chain,
                self.metrics_chain,
                self.analysis_chain,
                self.recommendation_chain
            ],
            input_variables=["document"],
            output_variables=["summary", "metrics", "analysis", "recommendations"],
            verbose=True
        )
    
    def analyze_document(self, document_content: str) -> Dict[str, str]:
        """分析文档并返回完整结果"""
        try:
            result = self.full_chain({"document": document_content})
            return result
        except Exception as e:
            print(f"分析过程中出现错误: {e}")
            return {"error": str(e)}


def langchain_multi_step_task():
    """演示LangChain的多步骤任务处理"""
    print("\n=== LangChain方式:文档分析任务 ===")
    
    document_content = """
    公司2023年第三季度财报显示,营收增长15%,达到500万美元。
    主要增长来源于新产品线的推出和海外市场拓展。
    但是,运营成本也上升了12%,主要由于人员扩张和营销投入增加。
    净利润为50万美元,同比增长8%。
    """
    
    # 创建文档分析器
    analyzer = DocumentAnalysisChain()
    
    # 一行代码完成复杂的多步骤分析
    results = analyzer.analyze_document(document_content)
    
    # 输出结果
    for key, value in results.items():
        if key != "error":
            print(f"\n{key.title()}: {value}")

外部工具集成:Agent自动选择和组合工具,无需手动编排,代码示例:

python 复制代码
# ====== 解决方案3:工具集成 - Agent架构 ======
def create_weather_tool():
    """创建天气查询工具"""

    def get_weather(city: str) -> str:
        """获取指定城市的天气信息"""
        weather_data = {
            "北京": "晴天,温度25°C,湿度60%",
            "上海": "多云,温度22°C,湿度75%",
            "广州": "雨天,温度28°C,湿度85%",
        }
        return weather_data.get(city, "未找到该城市天气信息")

    return Tool(
        name="天气查询",
        description="获取指定城市的当前天气信息。输入应该是城市名称。",
        func=get_weather,
    )


def create_restaurant_tool():
    """创建餐厅推荐工具"""

    def get_restaurant_recommendations(query: str) -> str:
        """根据城市和天气推荐餐厅"""
        # 简化的推荐逻辑
        if "晴天" in query:
            restaurants = ["露天BBQ餐厅", "花园咖啡厅", "屋顶酒吧"]
        elif "雨天" in query:
            restaurants = ["温馨火锅店", "室内日料店", "舒适茶餐厅"]
        else:
            restaurants = ["中式餐厅", "西式简餐", "快餐连锁"]

        return f"推荐餐厅:{', '.join(restaurants)}"

    return Tool(
        name="餐厅推荐",
        description="根据天气情况推荐合适的餐厅。输入应该包含天气信息。",
        func=get_restaurant_recommendations,
    )


def langchain_tool_integration():
    """LangChain方式:智能工具集成"""
    print("\n=== LangChain方式:工具集成 ===")

    # 创建工具列表
    tools = [create_weather_tool(), create_restaurant_tool()]

    # 创建LLM
    llm = ChatOpenAI(
        temperature=0.3,
        openai_api_key="sk-xxx",
        base_url="https://xxx.xxx.xxx/v1",
        model="gemini-2.5-flash-lite",
    )

    # 创建Agent
    agent_prompt = """
    你是一个智能助手,可以帮助用户查询天气和推荐餐厅。

    可用工具:{tool_names}

    工具描述:
    {tools}

    用户问题:{input}

    请按以下格式思考和行动:

    Thought: 我需要分析用户的需求
    Action: [工具名称]
    Action Input: [工具输入]
    Observation: [工具输出]
    ... (重复 Thought/Action/Action Input/Observation)
    Final Answer: [最终回答]

    {agent_scratchpad}
    """
    # 创建提示模板
    prompt = PromptTemplate(
        input_variables=["tool_names", "tools", "input", "agent_scratchpad"],
        template=agent_prompt,
    )

    # 创建ReAct Agent
    agent = create_react_agent(llm, tools, prompt)
    agent_executor = AgentExecutor(
        agent=agent, tools=tools, verbose=True, max_iterations=3
    )

    # 用户查询
    user_query = "我在北京,今天适合去哪些餐厅?"

    try:
        result = agent_executor.invoke({"input": user_query})
        print(f"\n最终回复: {result['output']}")
    except Exception as e:
        print(f"Agent执行错误: {e}")

应用扩展性:模块化架构,添加功能只需改动少量代码,代码示例:

python 复制代码
# ====== 解决方案4:应用扩展性 - 模块化架构 ======
class LangChainChatBot:
    """
    LangChain方式:模块化聊天机器人
    添加新功能只需注册新工具,无需修改核心逻辑
    """
    def __init__(self):
        """初始化聊天机器人"""
        self.llm = ChatOpenAI(temperature=0.7,
        openai_api_key="sk-xxx",
        base_url="https://xxx.xxx.xxx/v1",
        model="gemini-2.5-flash-lite",
        )
        self.memory = ConversationBufferMemory(return_messages=True)
        self.tools = []
        self.agent_executor = None
        
        # 初始化基础工具
        self._register_default_tools()
        self._setup_agent()
    
    def _register_default_tools(self):
        """注册默认工具"""
        self.add_tool(create_weather_tool())
        self.add_tool(create_restaurant_tool())
    
    def add_tool(self, tool: Tool):
        """添加新工具(扩展功能的唯一入口)"""
        self.tools.append(tool)
        print(f"✅ 已添加工具:{tool.name}")
        # 重新设置Agent以包含新工具
        if len(self.tools) > 0:
            self._setup_agent()
    
    def _setup_agent(self):
        """设置Agent(自动包含所有已注册工具)"""
        if not self.tools:
            return
        agent_prompt = """
        你是一个智能助手,可以使用以下工具帮助用户:

        可用工具:{tool_names}

        工具描述:
        {tools}

        对话历史:
        {chat_history}

        用户问题:{input}

        {agent_scratchpad}
        """

        prompt = PromptTemplate(
            input_variables=["tool_names", "tools", "chat_history", "input", "agent_scratchpad"],
            template=agent_prompt
        )
        
        agent = create_react_agent(self.llm, self.tools, prompt)
        self.agent_executor = AgentExecutor(
            agent=agent,
            tools=self.tools,
            memory=self.memory,
            verbose=True,
            max_iterations=3
        )
    
    def chat(self, user_input: str) -> str:
        """处理用户消息"""
        try:
            if self.agent_executor:
                result = self.agent_executor.invoke({"input": user_input})
                return result['output']
            else:
                # 如果没有工具,使用基础对话
                conversation = ConversationChain(
                    llm=self.llm,
                    memory=self.memory
                )
                return conversation.predict(input=user_input)
        except Exception as e:
            return f"抱歉,处理您的请求时出现错误:{e}"
    
    def list_capabilities(self) -> List[str]:
        """列出当前支持的功能"""
        capabilities = [tool.name for tool in self.tools]
        capabilities.append("普通对话")
        return capabilities


def create_stock_tool():
    """创建股票查询工具(演示扩展性)"""
    def get_stock_info(symbol: str) -> str:
        """获取股票信息"""
        # 模拟股票数据
        stock_data = {
            "AAPL": "苹果公司 - 当前价格: $150.25, 涨幅: +2.3%",
            "GOOGL": "谷歌 - 当前价格: $2,750.80, 涨幅: +1.8%",
            "TSLA": "特斯拉 - 当前价格: $850.45, 跌幅: -0.9%"
        }
        return stock_data.get(symbol.upper(), f"未找到股票 {symbol} 的信息")
    
    return Tool(
        name="股票查询",
        description="查询指定股票的当前价格和涨跌情况。输入应该是股票代码,如AAPL、GOOGL等。",
        func=get_stock_info
    )


def demonstrate_langchain_extensibility():
    """演示LangChain的扩展性"""
    print("\n=== LangChain方式:应用扩展性 ===")
    
    # 创建聊天机器人
    bot = LangChainChatBot()
    
    print("初始功能列表:", bot.list_capabilities())
    
    # 轻松添加新功能
    print("\n🔧 添加股票查询功能...")
    stock_tool = create_stock_tool()
    bot.add_tool(stock_tool)
    
    print("更新后功能列表:", bot.list_capabilities())
    
    # 测试新功能
    print("\n💬 测试对话...")
    response1 = bot.chat("北京今天天气怎么样?")
    print(f"用户: 北京今天天气怎么样?")
    print(f"助手: {response1}")
    
    response2 = bot.chat("AAPL股票现在多少钱?")
    print(f"\n用户: AAPL股票现在多少钱?")
    print(f"助手: {response2}")

😄 自动管理对话历史和记忆,通过链式调用实现复杂推理,支持工具集成和外部数据访问,少量代码即可实现Agent自主决策,LangChain 0.1.x 后还进行了重大重构,如:从传统 Chain 到 LCEL 。开发 LLM 应用 LangChain 是正解啊 ❗️

1.4. 安装与环境配置

python 复制代码
# ==============================
# 💡 ① 强烈建议创建虚拟环境,在项目根目录下执行:
# ==============================
python -m venv .venv
# 激活虚拟环境
# Windows (PowerShell)
..venv\Scripts\Activate.ps1
# macOS/Linux
source .venv/bin/activate

# ==============================
# 💡 ② 相关依赖库安装,(🔥标识的是必须的!)
# ==============================
# ✨入门学习项目直接下面这句一把梭就好,其它库按需选择
pip install langchain langchain-openai python-dotenv

# 【核心框架包】
pip install langchain            # 🔥 主要功能包 
pip install langchain-core        # 基础抽象层
pip install langchain-community  # 第三方集成
pip install langchain-experimental  # 实验性功能

# 【LLM提供商集成包】
pip install langchain-openai     # 🔥专用的 OpenAI 集成包
pip install langchain-anthropic  # Claude 模型
pip install langchain-huggingface  # Hugging Face 模型
pip install langchain-google-vertexai  # Google 模型

# 【向量数据库集成包】
# 本地向量数据库
pip install chromadb           # 轻量级向量数据库
pip install faiss-cpu          # Facebook AI 相似性搜索
# 云端向量数据库  
pip install pinecone-client     # Pinecone 向量数据库
pip install weaviate-client     # Weaviate 向量数据库

# 【工具和实用包】
# 文本处理和分割
pip install tiktoken           # 🔥 OpenAI 的 tokenizer,用于文本分词和编码
pip install unstructured       # 文档解析
# 环境配置
pip install python-dotenv      # 🔥 环境变量管理,方便配置 API Key 等敏感信息
# 网页处理
pip install beautifulsoup4     # 网页解析
pip install requests          # HTTP 请求

# 【部署与服务包】
# LangChain 服务化
pip install langserve          # REST API 部署
pip install langchain-cli      # 命令行工具
pip install langgraph          # 多智能体应用构建
# 监控和调试
pip install langsmith          # LangSmith SDK

# ==============================
# 💡 ③ 环境变量配置
# ==============================
# 大多数 LangChain 功能依赖外部 LLM 服务,需设置对应的API密钥,除了在代码调用时动态设置外,
# 还可以配置对应的环境变量,如 (OpenAI):
# macOS/Linux
export OPENAI_API_KEY="your_openai_api_key"
# Windows(PowerShell)
$Env:OPENAI_API_KEY="your_openai_api_key"
# 也可通过代码动态设置,其它 LLM 可参考各自文档,将对应环境变量添加到系统中。
# 如:Hugging Face: export HUGGINGFACEHUB_API_TOKEN="your_token"
# 😄还可以通过【python-dotenv】库将变量存放在【.env】文件中,在程序运行时
# 自动加载到系统环境中,这样可以避免在代码中硬编码敏感信息

python-dotenv 库的用法非常简单:

① 在项目根目录下新建一个名为 .env 的文本文件

格式为 KEY=VALUE,示例如下 (每行一个,使用#添加注释,不要在等号两侧添加空格):

python 复制代码
OPENAI_API_KEY=sk-xxxxxxx
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
DEBUG=True

② 使用 load_dotenv 加载,之后就可以通过 os.getenv()os.environ() 读取变量:

python 复制代码
from dotenv import load_dotenv
import os

# 从项目根目录或指定路径加载 .env
load_dotenv()  

# 之后就可以通过 os.getenv 或 os.environ 读取变量
openai_key = os.getenv("OPENAI_API_KEY")
db_url      = os.environ.get("DATABASE_URL")
debug_mode  = os.getenv("DEBUG", "False") == "True"

print("OpenAI Key:", openai_key)
print("Database URL:", db_url)
print("Debug Mode:", debug_mode)

# Tips:如果 .env 文件不在当前工作目录,可以设置自定义路径
load_dotenv(dotenv_path="/path/to/your/.env")
# 还可以调用 load_dotenv() 可以递归向上查找,直到找到 .env 为止
load_dotenv(find_dotenv())

#【强制覆盖】默认情况下,已存在的环境变量不会被 .env 覆盖,如需强制覆盖,可设置属性
load_dotenv(override=True)

#【只加载指定变量】可通过 dotenv_values 读取并过滤
from dotenv import dotenv_values
config = dotenv_values(".env")
# config 是一个 dict,可按需使用
api_key = config.get("OPENAI_API_KEY")

1.5. LangChain 生态系统

① langchain-core (核心抽象和LCEL)

  • 统一接口设计:提供Runnable协议,让所有组件都能无缝组合。
  • LCEL语言:声明式的链组合语言,类似于Unix管道操作。
  • 异步支持:原生支持async/await,提升性能。

langchain-community (第三方集成)

  • 丰富的集成:支持200+种LLM供应商、向量数据库 (如Pinecone、Chroma、FAISS、Weaviate等)、工具 (搜索引擎、API调用、数据库查询等)
  • 开源生态:社区贡献的各种集成组件。
  • 快速试验:新技术的试验场地。

langchain (主要链、代理和检索策略)

  • 预构建链:RAG链、总结链、问答链。
  • Agent系统:ReAct、Plan-and-Execute等智能代理。
  • 检索策略:多查询检索、混合检索、上下文压缩。

LangGraph (有状态多Agent应用构建)

  • 状态管理:支持复杂的工作流状态。
  • Agent协作:多个专业Agent的协同工作。
  • 视化工作流:图形化的状态机设计。

LangSmith (开发者平台-调试、测试、监控)

  • 全链路追踪:从输入到输出的完整调用链
  • 性能监控:延迟、成本、准确率等关键指标
  • A/B测试:不同模型和策略的对比测试
  • 数据集评估:自动化的评估流程

LangServe (将链部署为REST API)

  • 一键部署:将LangChain链转换为REST API。
  • 自动文档:生成OpenAPI规范和交互界面。
  • 性能优化:支持批处理、流式响应。
  • 监控集成:与LangSmith无缝集成。

1.6. 文档 & 资讯

😄 概念相关的东西就了解到这,接着系统学习 LangChain 的 "七大核心组件"。

2. Models - LLM模型配置

🤔 Models 组件是与大型语言模型 (LLMs ) 进行交互的核心,它为 不同类型的AI模型 提供了一个 "标准化的接口"。 这使得开发者可以轻松地在不同的模型之间切换,而无需重写大量的代码。

该组件的主要作用是 "简化和标准化与 AI 模型的交互",其核心优势包括:

  • 接口统一:为所有支持的模型提供了一致的调用方式,降低了学习成本。
  • 模型切换灵活:可以轻松地更换底层的语言模型,便于测试和选择最适合特定任务的模型。
  • 简化模型管理:将模型的实例化和配置过程变得简单明了。

LangChainModels 组件主要分为三种类型,每种都有其特定的应用场景:

  • LLMs (Large Language Models):接受一个字符串作为输入,并返回一个字符串作为输出。它们是文本补全模型,适用于各种自然语言处理任务。
  • Chat Models (聊天模型):与 LLMs 不同,聊天模型以一系列消息作为输入,并返回一个消息作为输出。这种基于消息的交互方式非常适合构建对话式应用,如:聊天机器人。
  • Text Embedding Models (文本嵌入模型):将文本转换为密集的向量表示 (即 "嵌入 "),这些向量可以被存储在 向量数据库 中,用于进行 相似性搜索 ,这在 检索增强生成(RAG) 等应用中至关重要。

2.1. 多模型支持

2.1.1. 不同LLM厂商的API调用

安装核心的 langchain-core 包 + 想要使用的模型的具体集成包 (如 langchain_openai) 即可调用对应LLM提供商的模型,梳理下常见 LLM 供应商的依赖的包和核心类:

OpenAIlangchain-openai

  • OpenAI - GPT-3.5/GPT-4 文本补全模型
  • ChatOpenAI - GPT-3.5/GPT-4 聊天模型
  • OpenAIEmbeddings - 嵌入模型
  • AzureChatOpenAI - Azure OpenAI 聊天模型
  • AzureOpenAI - Azure OpenAI 文本模型
  • AzureOpenAIEmbeddings - Azure OpenAI 嵌入模型

Googlelangchain-google-genai、langchain-google-vertexai

  • ChatGoogleGenerativeAI - Gemini 聊天模型
  • GoogleGenerativeAI - Gemini 文本模型
  • GoogleGenerativeAIEmbeddings - Google 嵌入模型
  • ChatVertexAI - Vertex AI 聊天模型
  • VertexAI - Vertex AI 文本模型
  • VertexAIEmbeddings - Vertex AI 嵌入模型

Anthropiclangchain-anthropic

  • ChatAnthropic - Claude 聊天模型
  • AnthropicLLM - Claude 文本模型

Hugging Faceangchain-huggingface

  • ChatHuggingFace - Hugging Face 聊天模型
  • HuggingFacePipeline - Hugging Face Pipeline 模型
  • HuggingFaceEmbeddings - Hugging Face 嵌入模型
  • HuggingFaceEndpoint - Hugging Face 推理端点

Ollama (本地模型) → langchain-ollama

  • ChatOllama - Ollama 聊天模型
  • OllamaLLM - Ollama 文本模型
  • OllamaEmbeddings - Ollama 嵌入模型

DeepSeeklangchain-deepseek

  • ChatDeepSeek - DeepSeek 聊天模型

😄 其它LLM 厂商可移步至官方文档查询:主站集成页面模型集成聊天模型嵌入模型

💡 Tips :每个LLM厂商都有自己的 专属API KeyLangChain集成类API端点三者必须配套使用 ❗️ 但我上面 ChatOpenAI 用的 Google Gemini 模型,却能正确使用,没有报错,是因为用的不是 "官方服务器 ",而是 "第三方代理中转" (base_url 设置)。原理如下:

  • ChatOpenAI 这个类被设计用来创建符合 OpenAI 官方 API 规范的 HTTP 请求。它会打包一个包含 {"model": "gemini-2.5-flash-lite", "messages": [...]} 这样的 JSON 数据包。
  • 代理服务器收到这个 OpenAI 格式的请求,内部路由逻辑识别到 "gemini" 应该由 Google 的 API 来处理,于是它开始 "翻译 ",将 OpenAI 格式的 messages 数组,转换成 Gemini API 需要的 contents 格式,将 temperature 等通用参数进行映射,最后使用 Google Gemini API 需要的凭证 (google_api_key) 向真正的 Google AI Platform API 发起一个 Gemini 能听懂的请求。
  • Google 的服务器处理这个请求,并生成一个 Gemini 格式的响应,中间代理服务器接收到响应,再次进行 "翻译",将 Gemini 的响应格式转换回 OpenAI 的标准响应格式,例如 {"choices": [{"message": {"role": "assistant", "content": "..."}}]}。
  • 代理服务器将这个标准 OpenAI 格式的响应发回给你的 LangChain 应用,ChatOpenAI 这样格式的响应,成功解析并返回最终结果。

🤔 在企业环境中,这种 "代理层" 是必不可少的,它可以:

  • 统一管理 API 密钥: 公司只需要管理少数几个 (如 Google, Anthropic) 主密钥,然后给开发者分发代理服务的密钥。
  • 集中式日志和监控: 所有 LLM 的调用都会经过一个地方,便于审计、分析成本和监控性能。
  • 实现高级路由和回退 : 代理层可以实现 "智能路由",如:Gemini 挂了,它可以自动将请求转发给 Claude,而前端应用代码无需任何改动。
  • 统一缓存: 可以在代理层实现对所有模型调用的缓存,进一步节省成本。

😶 这种 "中转平台 " 还挺多,如 OpenRouter、OneChat 等,有 "免费 " 也有 "付费 " 的,使用时注意保护 "隐私安全 ",充钱的话不建议一次性充太多,平台跑路见太多了,这种基本申诉无门 ❗️ 分享一个能轻松将多种 LLM API 转换为 OpenAI 兼容接口的流行 "开源项目 ":BerriAI/litellm

2.1.2. LLM 核心类-可配置属性

LLM核心类 在 "实例化 " 时可以配置各种属性,这里以最常用的 ChatOpenAI 为例,其它类也是类似,具体可参见对应的文档或源码 (😄 其实,这些参数在《3、AI-概念名词 & LLM-模型微调》已经涉猎过):

①【核心身份参数

  • model :str,必需,模型名称,用于指定要使用的具体模型,如:"gpt-4o",不同模型的性能、价格和上下文窗口大小都不同。
  • api_key :str,可选,API 密钥 ,如果不在实例化时提供,LangChain 会自动从名为 OPENAI_API_KEY 的环境变量中读取。
  • base_url :str,可选,API 请求的服务器基地址,当你需要使用代理、兼容 OpenAI 的端点(如 LiteLLM)或 Azure OpenAI 服务时,可以设置这个参数。
  • organization :str,可选,OpenAI 组织 ID,主要用于区分不同组织下的用量和计费。

② 【生成控制参数

  • temperature:float,可选,范围[0,2],温度/控制输出的随机性,值越低 (如 0.1),输出越确定、重复性越高,值越高 (如 1.0),输出越具创造性和多样性。
  • max_tokens :int,可选,限制单次调用生成文本的最大长度 (词元数量),一般用于控制API成本和确保输出简洁。
  • top_p :float,可选,范围[0,1],核心采样,与 temperature 类似,但通过另一种方式控制随机性。它让模型只考虑概率总和达到 top_p 值的那些词汇。一般建议只使用 temperature 或 top_p 中的一个。
  • stop :list[str],可选,停止序列,当模型生成其中任何一个字符串时,会立即停止生成,且停止的字符串不会包含在最终输出中。
  • presence_penalty :float,可选,范围[-2.0, 2.0],存在惩罚,正值会惩罚那些已经出现在文本中的词汇,鼓励模型谈论新主题,降低重复性。
  • frequency_penalty :float,可选,范围 [-2.0, 2.0],频率惩罚,正值会根据词汇在文本中出现的频率来惩罚它们,进一步降低模型重复相同词语的概率。
  • seed :int,可选,随机种子,配合较低的 temperature 使用时,提供此参数可以使模型的输出在多次调用中保持确定性和可复现性,对于测试和调试非常有用。
  • response_format :dict,可选,强制模型输出特定格式。如:设置为 {"type": "json_object"} 可以启用 JSON 模式,确保模型输出一个有效的 JSON 对象。
  • n :int,可选,默认为 1,生成数量,让模型为每个输入消息生成 n 个不同的备选输出(choices)。

③ 【工具调用参数

  • tools :list,可选,工具列表 ,用于向模型提供它可以调用的外部工具 (如函数、API),是实现 Function Calling 功能的核心。
  • tool_choice :str 或 dict,可选,控制工具选择策略 ,可以设置为 none-不使用工具auto-模型自行决定,或强制模型调用某个特定工具。

④ 【高级与网络参数

  • streaming :bool,可选,流式输出,默认为 False,如果设为 True,当你使用 .stream() 或 .astream() 方法时,模型会以数据块的形式实时返回结果,而不是等待全部生成完毕。
  • timeout :float,可选,请求超时,等待 OpenAI API 响应的最长时间 (单位秒)。
  • max_retries :int,可选,默认为 2,最大重试次数,当 API 请求失败 (如网络问题、速率限制) 时,自动重试的最大次数。
  • model_kwargs :dict,可选,额外模型参数,用于传递 OpenAI API 支持但 ChatOpenAI 类没有直接暴露的任何其他参数。如:通过这里传递 logit_bias 来调整特定词汇的出现概率。

⑤ 【元数据与回调参数

  • tags :list[str],可选,为此次模型调用附加标签,便于在 LangSmith 等追踪工具中进行筛选和分组。
  • metadata :dict,可选,元数据,附加一个包含任意键值对的字典,为调用提供更丰富的上下文信息,同样便于在 LangSmith 中追踪。
  • callbacks :list,可选,回调管理器 ,用于在模型调用的不同生命周期阶段 (如开始、结束、出错时) 执行自定义代码,是 LangChain 中实现 自定义逻辑和监控 的重要机制。

简单代码调用示例:

python 复制代码
from langchain_openai import OpenAI

# ==============================
# 💡 语言模型
# ==============================
llm = OpenAI(
    model="gpt-4",
    temperature=0.7,
    max_tokens=1000,
    timeout=30,
    max_retries=3
)

# 调用模型
text = "请为我写一篇关于LangChain的简短介绍"
response = llm.invoke(text)
print(response)

# 流式生成
for chunk in llm.stream("请详细介绍机器学习的基本概念"):
    print(chunk, end="", flush=True)

# 批量调用
texts = ["介绍Python", "介绍Java", "介绍JavaScript"]
responses = llm.batch(texts)
for response in responses:
    print(response)

# ==============================
# 💡 聊天模型
# ==============================
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage

# 初始化聊天模型
chat_model = ChatOpenAI(
    model="gpt-4",
    temperature=0.7,
    max_tokens=1000
)

# 单轮对话
messages = [HumanMessage(content="你好,请介绍一下自己")]
response = chat_model.invoke(messages)
print(response.content)

# 多轮对话
conversation = [
    SystemMessage(content="你是一个专业的Python编程助手"),
    HumanMessage(content="什么是列表推导式?"),
    AIMessage(content="列表推导式是Python中创建列表的简洁方式..."),
    HumanMessage(content="能给我一个具体的例子吗?")
]

response = chat_model.invoke(conversation)
print(response.content)

# 支持多模态输入,如:包含图像的消息
from langchain_core.messages import HumanMessage
message = HumanMessage(
    content=[
        {"type": "text", "text": "这张图片中有什么?"},
        {"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}}
    ]
)

response = chat_model.invoke([message])
print(response.content)

💡 顺带介绍下上面涉及到的几个 LangChain 中的 "消息类型":

python 复制代码
#【SystemMessage-system】系统信息,用于:
# 设置AI的角色和性格、定义回答的风格和规则、
# 提供上下文背景信息、设置安全和伦理约束
system_msg = SystemMessage(content="你是一个专业的Python编程助手,请用简洁明了的方式回答问题")

#【HumanMessage-user】用户消息,用于表示用户的输入
human_msg = HumanMessage(content="什么是列表推导式?")

#【AIMessage-assistant】AI消息,用于表示 AI 的回复
ai_msg = AIMessage(content="列表推导式是Python中创建列表的简洁方式...")
# 带有工具调用的 AI 信息
ai_msg_with_tools = AIMessage(
    content="我需要查询天气信息",
    tool_calls=[{
        "id": "call_123",
        "function": {"name": "get_weather", "arguments": '{"city": "北京"}'},
        "type": "function"
    }]
)

#【ToolMessage-tool】工具消息,表示工具调用的结果
tool_msg = ToolMessage(
    content='{"temperature": 22, "condition": "sunny"}',
    tool_call_id="call_123"
)

#【ChatMessage】通用聊天信息。可以指定任意角色
custom_msg = ChatMessage(
    content="这是一个自定义角色的消息",
    role="moderator"  # 自定义角色
)

# 共同的属性和方法
msg = HumanMessage(content="Hello")

# 基本属性
print(f"类型: {msg.type}")           # 'human'
print(f"内容: {msg.content}")        # 'Hello'
print(f"ID: {msg.id}")              # 自动生成的唯一ID

# 附加数据
msg_with_metadata = HumanMessage(
    content="Hello",
    additional_kwargs={"user_id": "123", "timestamp": "2024-01-01"},
    name="用户张三"
)

# 序列化和反序列化
msg_dict = msg.dict()
print(msg_dict)

# 从字典创建消息
recreated_msg = HumanMessage(**msg_dict)

#【自定消息类型】
from langchain_core.messages import BaseMessage
from typing import List, Dict, Any

class CustomMessage(BaseMessage):
    def __init__(self, content: str, metadata: Dict[str, Any] = None, **kwargs):
        super().__init__(content=content, **kwargs)
        self.metadata = metadata or {}
    
    @property
    def type(self) -> str:
        return "custom"

# 使用自定义消息
custom_msg = CustomMessage(
    content="这是一个自定义消息",
    metadata={"priority": "high", "category": "urgent"}
)

print(f"消息类型: {custom_msg.type}")
print(f"元数据: {custom_msg.metadata}")

2.2. 自定义模型类

LangChain 提供了两类核心模型接口:BaseLLM (纯文本生成) 和 BaseChatModel (需处理对话历史),继承实现特定的抽象方法和属性,添加自定义逻辑即可 (如API调用、数据预处理等)。简单代码示例如下:

python 复制代码
from langchain_core.language_models.llms import BaseLLM
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from langchain_core.outputs import LLMResult, ChatResult, ChatGeneration
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
from typing import Any, List, Optional

class CustomLLM(BaseLLM):
    """自定义LLM模型类示例"""
    
    # 定义模型参数
    api_key: str = ""
    base_url: str = ""
    model_name: str = "custom-model"
    temperature: float = 0.7
    
    @property
    def _llm_type(self) -> str:
        """返回模型类型标识"""
        return "custom_llm"
    
    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """核心调用方法"""
        # 实现你的自定义API调用逻辑
        response = self._call_custom_api(prompt, **kwargs)
        return response
    
    def _call_custom_api(self, prompt: str, **kwargs) -> str:
        """具体的API调用实现"""
        # 这里可以调用任何你想要的模型API
        # 例如:本地模型、其他云服务等
        import requests
        
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        data = {
            "prompt": prompt,
            "temperature": self.temperature,
            "max_tokens": kwargs.get("max_tokens", 100)
        }
        
        try:
            response = requests.post(
                f"{self.base_url}/generate",
                headers=headers,
                json=data,
                timeout=30
            )
            response.raise_for_status()
            result = response.json()
            return result.get("text", "")
        except Exception as e:
            return f"API调用失败: {str(e)}"
    
    @property
    def _identifying_params(self) -> dict:
        """返回模型标识参数"""
        return {
            "model_name": self.model_name,
            "temperature": self.temperature,
        }

class CustomChatModel(BaseChatModel):
    """自定义ChatModel - 处理对话历史"""
    
    api_key: str = ""
    base_url: str = ""
    
    @property
    def _llm_type(self) -> str:
        return "custom_chat"
    
    def _generate(
        self,
        messages: List[BaseMessage],  # 接受消息列表
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:  # 返回结构化结果
        """处理消息历史,返回对话回复"""
        print(f"💬 ChatModel收到 {len(messages)} 条消息")
        
        # 解析消息历史
        conversation_history = []
        for msg in messages:
            if isinstance(msg, SystemMessage):
                conversation_history.append(f"系统: {msg.content}")
            elif isinstance(msg, HumanMessage):
                conversation_history.append(f"用户: {msg.content}")
            elif isinstance(msg, AIMessage):
                conversation_history.append(f"助手: {msg.content}")
        
        print(f"📜 对话历史: {conversation_history}")
        
        # 基于对话历史生成回复
        last_user_msg = ""
        for msg in reversed(messages):
            if isinstance(msg, HumanMessage):
                last_user_msg = msg.content
                break
        
        response_text = f"[Chat回复] 理解你的问题:'{last_user_msg}'。这是基于对话历史的智能回复。"
        
        # 返回结构化的消息对象
        message = AIMessage(content=response_text)
        generation = ChatGeneration(message=message)
        return ChatResult(generations=[generation])
    
    @property
    def _identifying_params(self) -> dict:
        return {"model_type": "chat"}

from custom_llm import CustomLLM
from custom_chat_model import CustomChatModel
from langchain_core.prompts import PromptTemplate

def demonstrate_difference():
    """使用自定义模型"""
    
    llm = CustomLLM()
    chat_model = CustomChatModel()
    
    print("=== LLM vs ChatModel 对比 ===\n")
    
    # LLM的使用方式
    print("🤖 LLM模式:")
    llm_result = llm.invoke("解释什么是人工智能")
    print(f"输入: 纯文本字符串")
    print(f"输出: {llm_result}\n")
    
    # ChatModel的使用方式
    print("💬 ChatModel模式:")
    from langchain_core.messages import HumanMessage, SystemMessage
    
    messages = [
        SystemMessage(content="你是一个AI助手"),
        HumanMessage(content="今天天气怎么样"),
        AIMessage(content="我需要知道你的位置才能准确告诉你天气"),
        HumanMessage(content="我在北京")
    ]
    
    chat_result = chat_model.invoke(messages)
    print(f"输入: {len(messages)} 条消息对象")
    print(f"输出: {chat_result.content}\n")
    
    print("🎯 核心区别:")
    print("• LLM: 文本 → 文本")
    print("• ChatModel: 消息历史 → 结构化回复")

if __name__ == "__main__":
    demonstrate_difference()

2.3. 嵌入模型

嵌入模型 (Embedding Models) 是 RAG (检索增强生成) 等许多高级应用的基石,它的作用是 将文本转化为一串数字(向量) ,这个向量可以捕捉文本的语义信息。语义相近的文本,其向量在空间中的距离也相近。🤔 "嵌入" 这个概念,之前没提到,这里先展开讲讲。

2.3.1. 嵌入 (Embedding)

😄 对于一台计算机,你可以告诉它:A=1,B=2,它能理解,但你怎么告诉它 "国王 " 这个词是什么意思呢?一个最原始、最笨的方法,可能是给字典里的每个词都分配一个 独一无二的编号 ,如:国王(5494)、女王(7168)、苹果 (45)。😶 虽然现在有了 数字,但这些数字毫无意义,计算机看不出5494和7168间有任何关系,更不知道"国王"和"女王"的关系,对计算机来说,这些数字跟随机分配的门牌号没任何区别。

🤔 想象有一种方法,它能把世界上所有词语、句子和概念,都标注在一张巨大的、多维度的地图上。这张地图不止有东西南北两个方向,而是有成百上千个 "方向" (维度 )。在这张 "意义地图 " 上,意思相近的东西,在空间中的位置也相互靠近。通过一个简单的二维地图来打比方:

  • 嵌入模型 阅读了互联网上万亿的句子,学习到 "国王" 经常和 "皇家"、"权利"、"王座"、"男人" 这些词一起出现。基于这些上下文信息,它把 "国王" 放在了地图上的一个坐标点,如 [10, 12].
  • 接着它拿到 "女王 " 这个词,它发现 "女王" 也经常和 "皇家"、"权力"、"王座" 一起出现,但更多是和 "女人" 关联。于是,它把 "女王" 放在一个离 "国王" 非常近的坐标点,如 [10.1, 11.9]。
  • 然后它拿到 "苹果" 这个词,它学到这个词总是和"水果"、"吃"、"树"、"红色" 一起出现。这些语境和上面完全不同!于是把"苹果"放在一个离"国王"和"女王"十万八千里远的坐标点,如[-50,45]

💁‍♂️ 这个代表坐标点的一串数字 (如[10, 12]),就是 "嵌入向量 " (Embedding Vector),然后地图上点与点间的 "方向 " 和 "距离 " 也蕴含着深层含义 (关系),这就引出AI领域最经典、最著名的例子:

向量("国王") - 向量("男人") + 向量("女人") ≈ 向量("女王")

😳 简单的数学运算,竟然得出了符合逻辑的结果!这意味着,嵌入模型 已经从海量文本中,自主学习并理解了"性别"、"皇权" 这类抽象概念,并把它们编码进了地图的空间几何关系中,它知道了从"男性"到"女性"在地图上是一个固定的 "方向向量"!

早期模型 (如 Word2Vec) 擅长为 单个词语 (词嵌入) 创建地图坐标,现代模型 (如 OpenAIEmbeddings) 则要先进得多,它们可以阅读 一整句话/一整段文字 ,然后为这一 "整段内容 " 生成一个 唯一的坐标/向量 (文本嵌入),这个坐标代表了整段话的核心意思。如:"国王统治着他的土地" 会得到一个坐标,"女王陛下发表了演说" 会得到一个离上面那句话非常近的坐标,"我午饭吃了一个美味的苹果" 则会得到一个离前两句话非常远的坐标。

2.3.2. 语义搜索

嵌入技术目前最核心、最普遍的应用就是 语义搜索 (Semantic Search),就是 "按意思 " 来搜索,而非 "关键词" 匹配,它的工作流程非常简单:

  • 知识入库与向量化 :你把你公司所有的文档 (知识库、PDF、网站文章等) 都拿出来,用 嵌入模型 把每一篇/段文档都计算出它在 "意义地图" 上的坐标 (向量)。
  • 存储 :把所有这些算好的坐标 (向量),存入一个特殊的数据库 → 向量数据库 (Vector Store)。
  • 用户提问:如 "我们公司的产假政策是怎样的?"
  • 问题向量化与比较 :系统不会去傻傻地搜索 "产假 "、"政策 " 这两个关键词,而是用同一个 嵌入模型,把用户这句问话也计算出它在地图上的坐标。
  • 寻找"近邻" :然后,系统去向量数据库里问:"快帮我找找,数据库里存的那些文档坐标,哪些离我这个问题的坐标最近?"。
  • 检索与生成:数据库会返回语义上最接近的几篇文档原文。然后,你把这几篇原文和用户的原始问题一起,喂给一个强大的大语言模型 (如 GPT-4),并对它说:"请参考这几份资料,回答用户下面这个问题。"

2.3.3. 代码示例

LangChain 支持多种嵌入模型,如:

  • OpenAI Embeddings:质量高,适用于生产环境
  • 本地嵌入模型:如 Sentence Transformers,隐私友好。
  • 云服务嵌入:如Azure OpenAI、Cohere等。
  • 自定义嵌入:继承BaseEmbeddings实现。

这里只演示下 OpenAI Embeddings 的用法,可用模型名称:

  • text-embedding-3-small:高性价比检索/RAG、相似度/去重,维度默认1536。
  • text-embedding-3-large:高精度检索、聚类、复杂排序、多语种强鲁棒 (跨语言检索召回稳定),维度默认3072。
  • text-embedding-ada-002:旧版兼容过渡,不建议新项目起步。

支持通过 dimensions 参数自定义输出向量的维度,在优化存储和计算效率时非常有用,直接上代码:

python 复制代码
def demonstrate_openai_embeddings():
    """OpenAI嵌入模型的使用"""

    # 初始化OpenAI嵌入模型
    embeddings = OpenAIEmbeddings(
        model="text-embedding-3-small",  
        openai_api_key=openai_api_key,
        openai_api_base=openai_base_url,
        dimensions=1536,    # 维度
    )

    # 测试文本
    texts = [
        "人工智能是计算机科学的一个分支",
        "机器学习是AI的重要组成部分",
        "深度学习使用神经网络进行学习",
        "自然语言处理帮助计算机理解人类语言",
    ]

    print("=== OpenAI嵌入模型演示 ===\n")

    # 1. 批量生成嵌入向量
    print("📊 生成嵌入向量...")
    vectors = embeddings.embed_documents(texts)
    print(f"生成了 {len(vectors)} 个向量,每个向量维度: {len(vectors[0])}")

    # 2. 为单个查询生成嵌入
    query = "什么是人工智能?"
    query_vector = embeddings.embed_query(query)
    print(f"查询向量维度: {len(query_vector)}")

    # 3. 计算相似度
    print("\n🔍 计算相似度...")
    from sklearn.metrics.pairwise import cosine_similarity
    import numpy as np
    
    # 向量越相似,夹角越小 (余弦相似度越大,1.0-完全相同方向,
    最相似、0.0-垂直,不相关、-1.0完全相反方向,最不相似)
    similarities = cosine_similarity([query_vector], vectors)[0]

    print(f"查询: {query}")
    for i, (text, similarity) in enumerate(zip(texts, similarities)):
        print(f"文本: {text}, 相似度: {similarity:.4f}")

    # 4. 找到最相似的文本
    best_match_idx = np.argmax(similarities)
    print(f"\n🎯 最相似的内容: {texts[best_match_idx]}")


if __name__ == "__main__":
    openai_api_key = os.getenv("OPENAI_API_KEY")
    openai_base_url = os.getenv("OPENAI_API_BASE")
    demonstrate_openai_embeddings()

运行输出结果:

3. Prompts - 提示词模板

实际应用中,我们很少使用完全写死 (静态) 的 Prompt,而是根据用户输入或其它动态数据来生成Prompt。如:为一个电商客服机器人涉及一个查询订单状态的Prompt,其中的订单号是动态变化的。

😄 这时,LangChain的Prompts组件 就派上了用场,它的核心是 Prompt Templates (提示词模板) ------ 一种预先定义好的、可复用的Prompt结构,它包含了 占位符 (变量) ,可在 运行时动态填充信息,主要由两部分构成:

  • 模板字符串 :包含自然语言文本和用花括号 {} 标记的占位符。
  • 输入变量: 定义了模板中需要填充的变量名称。

使用提示词模板的好处:

  • 复用性与效率:一次创建,多次使用,极大地提高了开发效率,避免了代码重复。
  • 灵活性与动态性:可以根据不同的场景和输入,动态生成定制化的Prompt。
  • 一致性与可维护性:确保了应用中同类任务的Prompt结构一致,便于团队协作和后期维护。
  • 代码解耦:将Prompt的逻辑与应用的其他逻辑分离,使代码更加清晰、模块化。

3.1. PromptTemplate - 字符串模板

LangChain 中最基础也是最常用的模板,适用于 "生成单个字符串Prompt" 的场景,使用代码示例:

python 复制代码
from langchain_core.prompts import PromptTemplate

# ==============================
# 💡 基础用法
# ==============================
# 方式1:使用from_template(推荐)
template1 = PromptTemplate.from_template(
    "你是一个{role},请回答关于{topic}的问题:{question}"
)

# 方式2:使用构造函数
template2 = PromptTemplate(
    input_variables=["role", "topic", "question"],
    template="你是一个{role},请回答关于{topic}的问题:{question}",
)

# 使用模板
formatted_prompt1 = template1.format(
    role="Python专家", topic="数据结构", question="什么是字典?"
)
formatted_prompt2 = template2.format(
    role="Python专家", topic="数据结构", question="什么是字典?"
)
print(formatted_prompt1)
print(formatted_prompt2)


# ==============================
# 💡 高级用法:部分变量
# ==============================

# 创建模板
template = PromptTemplate.from_template("你是{company}的{role},用户问:{question}")

# 部分填充(预设某些变量)
partial_template = template.partial(company="OpenAI")

# 只需要填充剩余变量
final_prompt = partial_template.format(role="AI研究员", question="GPT是如何工作的?")
print(final_prompt)

3.2. ChatPromptTemplate - 对话模板

LangChain 中用于 快速构建结构化消息列表 的模板,使用代码示例:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate

# 消息类型
#【system】系统消息,设定AI的行为和角色,
#【human】人类用户的消息, 
#【ai】AI助手的消息,
#【function】函数调用结果消息,
#【tool】工具调用消息"

# 创建对话模板
chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的{domain}助手。"),
    ("human", "请解释{concept}是什么?"),
    ("ai", "我很乐意为你解释{concept}。"),
    ("human", "{question}")
])

# 格式化消息
messages = chat_template.format_messages(
    domain="编程",
    concept="函数",
    question="能给个具体例子吗?"
)

for message in messages:
    print(f"{message.type}: {message.content}")

💁‍♂️ 其中有一个特殊的占位符 "MessagesPlaceholder ",它允许我们在 ChatPromptTemplate 中动态插入一个 "消息列表",使用代码示例:

python 复制代码
from langchain_core.prompts import MessagesPlaceholder

chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个有趣的编程老师,喜欢用比喻来解释概念。"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "什么是{concept}?")
])

messages = chat_template.format_messages(
    chat_history=[
        ("human", "什么是函数?"),
        ("ai", "函数是一种编程概念,用于将输入转换为输出。")
    ],
    concept="函数"
)

for message in messages:
    print(f"{message.type}: {message.content}")

3.3. FewShotPromptTemplate - 少样本学习模板

LangChain 中用于 "创建包含少量示例" 的模板,核心参数:

  • example_prompt: PromptTemplate,用于格式化单个示例的模板。
  • suffix: str,示例后的提示模板字符串。
  • example_separator: str = "\n\n",示例分隔符,默认双换行。
  • prefix: str = "",示例前的提示模板字符串。
  • template_format: Literal["f-string", "jinja2"] = "f-string",模板格式
  • validate_template: bool = False,是否验证模板。

使用代码示例:

python 复制代码
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

def basic_usage_demo():
    examples = [
        {"question": "3+2=?", "answer": "4", "category": "math"},
        {"question": "3+3=?", "answer": "6", "category": "math"},
        {
            "question": "法国的首都是什么?",
            "answer": "巴黎",
            "category": "geography",
        },
        {
            "question": "日本的首都是什么?",
            "answer": "东京",
            "category": "geography",
        },
        {
            "question": "如何煮意大利面?",
            "answer": "煮水, 加意大利面, 煮10分钟",
            "category": "cooking",
        },
        {
            "question": "如何煮咖啡?",
            "answer": "磨豆, 用热水煮",
            "category": "cooking",
        },
    ]
    # 创建示例模板
    example_template = PromptTemplate(
        input_variables=["question", "answer"],
        template="问题: {question}\n答案: {answer}",
    )

    # 创建FewShot模板
    few_shot_template = FewShotPromptTemplate(
        examples=examples[:3],  # 使用前3个示例
        example_prompt=example_template,
        prefix="请根据以下示例回答问题:\n",
        suffix="问题: {input}\n答案:",
        input_variables=["input"],
    )

    # 生成prompt
    prompt = few_shot_template.format(input="5+5=?")
    print(f"生成的prompt: {prompt}")

3.4. 动态示例选择

😄 让AI根据 问题的相似度 自动选择最相关的示例,代码示例:

python 复制代码
def prompt_dynamic_selector():
    # 创建示例数据
    examples = [
        {"input": "如何学习Python?", "output": "从基础语法开始,多做练习项目。"},
        {"input": "什么是机器学习?", "output": "让计算机从数据中学习模式的技术。"},
        {"input": "如何优化代码性能?", "output": "使用分析工具找瓶颈,选择合适的算法和数据结构。"},
    ]
    # 创建语义相似度示例选择器
    example_selector = SemanticSimilarityExampleSelector.from_examples(
        examples,
        OpenAIEmbeddings(), # 使用嵌入模型来计算语义相似度
        InMemoryVectorStore, # 使用内存向量存储来存储示例
        k=2 # 选择最相似的2个示例 
    )

    # 创建动态少样本模板
    dynamic_template = FewShotPromptTemplate(
        example_selector=example_selector,
        example_prompt=PromptTemplate.from_template(
            "问题: {input}\n回答: {output}"
        ),
        prefix="根据以下示例回答问题:",
        suffix="问题: {question}\n回答:",
        input_variables=["question"]
    )
    return dynamic_template.format(question="如何学习Python?")

运行输出结果:

3.5. 条件化提示词

😄 就是根据 "条件变量" 的切换动态生成不同的提示模板,代码示例:

python 复制代码
from langchain_core.prompts import PromptTemplate

def create_conditional_prompt(user_level):
    """根据用户水平创建不同的提示"""
    
    if user_level == "beginner":
        template = """
        你是一个耐心的编程入门导师。请用最简单的语言解释:
        
        问题:{question}
        
        要求:
        1. 使用通俗易懂的比喻
        2. 避免技术术语
        3. 提供简单示例
        """
    elif user_level == "intermediate":
        template = """
        你是一个专业的技术导师。请详细解释:
        
        问题:{question}
        
        要求:
        1. 包含技术细节
        2. 提供代码示例
        3. 说明最佳实践
        """
    else:  # advanced
        template = """
        作为技术专家,请从深度技术角度分析:
        
        问题:{question}
        
        要求:
        1. 深入原理分析
        2. 性能优化建议
        3. 架构设计考虑
        """
    
    return PromptTemplate.from_template(template)

# 使用条件化提示
beginner_prompt = create_conditional_prompt("beginner")
expert_prompt = create_conditional_prompt("advanced")

3.6. 组合式提示词

😄 就是将 复杂提示词 拆解成多个 "职责单一的独立组件" (角色、情境、任务等),然后通过 "组合函数" 将组件组装成完整的提示词,以实现 "模块化复用" 和 "灵活定制"。代码示例:

python 复制代码
from langchain_core.prompts import PromptTemplate

# 创建可复用的提示组件
role_template = PromptTemplate.from_template("你是一个{role}。")
context_template = PromptTemplate.from_template("当前情境:{context}")
task_template = PromptTemplate.from_template("请完成以下任务:{task}")

# 组合提示
def combine_prompts(role, context, task):
    role_part = role_template.format(role=role)
    context_part = context_template.format(context=context)
    task_part = task_template.format(task=task)
    
    return f"{role_part}\n\n{context_part}\n\n{task_part}"

# 使用组合式提示
final_prompt = combine_prompts(
    role="Python专家",
    context="在代码审查会议中",
    task="分析这段代码的性能问题"
)

4. Tools - 工具函数

🤔 Tools 组件的本质是将 Python函数 与其 元数据 (名称、描述、参数架构) 关联起来的抽象层,它让LLM模型具备了 "动手能力",如:执行具体的操作、获取外部信息,从而不仅仅局限于它自身训练数据中的静态知识。

4.1. @tool 装饰器

被修饰的函数会转换成一个 LangChain Tool,函数签名 (a: int, b: int) 和 类型提示 (-> int) 非常重要,LangChain会利用它们自动推断出工具的 参数模式 (schema) 。函数的 文档字符串 (docstring) 也很重要,它会成为 工具的描述,这个描述是给LLM看的,LLM会根据这个描述来判断在何种情况下应该使用这个工具。一个用于计算两数乘积的简单工具定义示例:

python 复制代码
import os
from langchain_core.tools import tool
from langchain_openai.chat_models import ChatOpenAI

# ================================
# 💡 ① 定义自定义Tool
# ================================
@tool
def multiply(a: int, b: int) -> int:
    """计算两个整数的乘积。"""
    return a * b


if __name__ == "__main__":
    print("=" * 50)
    # ================================
    # 💡 ② 检查自动生成的Tool 属性,可以帮助我们理解它是如何被结构化的
    # ================================
    print("Tool属性")
    print("=" * 50)
    print(f"工具名称: {multiply.name}")
    print(f"工具描述: {multiply.description}")
    print(f"参数模式: {multiply.args}")
    print("=" * 50)
    print(f"直接调用Tool,输出结果:{multiply.invoke({'a': 2, 'b': 3})}")
    print("=" * 50)
    # ================================
    # 💡 ③ 让LLM模型决定是否调用工具
    # ================================
    openai_api_key = os.getenv("OPENAI_API_KEY")
    openai_base_url = os.getenv("OPENAI_BASE_URL")
    openai_api_model = os.getenv("DEFAULT_LLM_MODEL")
    llm = ChatOpenAI(
        openai_api_key=openai_api_key,
        openai_api_base=openai_base_url,
        model=openai_api_model,
    )
    # ================================
    # 💡 ⑤ 将工具列表绑定到LLM模型上,😄 可以指定【tool_choice】参数强制使用某个工具
    # ================================
    available_tools = [multiply]
    llm_with_tool = llm.bind_tools(available_tools)
    # ================================
    # 💡 ⑥ 让LLM模型决定调用哪个Tool,模型本身不执行工具,它只是"请求"执行,这就是所谓的"工具调用"
    # ================================
    response = llm_with_tool.invoke('计算2和3的乘积')
    print(f"返回包含「工具调用信息」的特殊消息对象:{response}")
    print("=" * 50)
    # ================================
    # 💡 ⑦ 根据工具调用信息,调用工具执行
    # ================================
    if not response.tool_calls:
        print("没有工具调用")
    else:
        for tool_call in response.tool_calls:
            tool_name = tool_call['name']
            tool_args = tool_call['args']
            # 查找对应的工具
            tool = next((t for t in available_tools if t.name == tool_name), None)
            if tool:
                result = tool.invoke(tool_args)
                print(f"工具【{tool_name}】的执行结果:{result}")
                print("=" * 50)
            else:
                print(f"未找到工具【{tool_name}】")
                print("=" * 50)

运行输出结果:

😄 更优雅的方式是使用 Agent 组件,它可以自动处理多轮对话和复杂的工具调用场景,这个后面会讲~

4.2. StructuredTool

专为 "结构化输入 " 设计,简化了工具创建过程,特别适合处理多参数、复杂输入的场景。通过 类方法 from_function() 快速构建工具,同时支持同步和异步版本,能自动从函数签名推断参数模式,还支持自定义名称、描述、模式。使用代码示例:

python 复制代码
from pydantic import BaseModel, Field
from langchain_core.tools import StructuredTool
import asyncio

class AreaInput(BaseModel):
    length: float = Field(description="长度(米)")
    width: float = Field(description="宽度(米)")

def calculate_area(length: float, width: float) -> float:
    """计算矩形面积"""
    return length * width

async def async_calculate_area(length: float, width: float) -> float:
    """异步计算矩形面积"""
    return length * width

area_tool = StructuredTool.from_function(
    func=calculate_area,            # 同步版本
    coroutine=async_calculate_area,  # 异步版本
    name="area_calculator", # 工具名称
    description="计算矩形面积的专用工具", # 工具描述
    args_schema=AreaInput, # 工具参数   
    return_direct=False, # 是否直接返回结果
    handle_tool_error=True,  # 启用错误处理
)

async def main():
    """主函数:演示同步和异步工具调用"""
    print("=" * 50)
    print("同步调用")
    print("=" * 50)
    print(area_tool.invoke({"length": 5.0, "width": 3.0}))
    print("=" * 50)
    print("异步调用")
    print("=" * 50)
    print(await area_tool.ainvoke({"length": 5.0, "width": 3.0}))

if __name__ == "__main__":
    asyncio.run(main())

4.3. BaseTool

所有工具类的抽象父类,当你需要完全的、底层的控制时,才会选择继承 BaseTool,通常适用于下述高级场景:

  • 需要分别定义工具的同步 (_run) 和异步 (_arun) 实现。
  • 工具需要管理复杂的状态或连接 (如:数据库连接池)。
  • 参数的解析逻辑非常特殊,无法通过 Pydantic 自动推断。

简单代码示例:

python 复制代码
import asyncio
from typing import Type, Optional
from pydantic import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun, AsyncCallbackManagerForToolRun

# 1. 定义参数的Pydantic模型
class DatabaseQuerySchema(BaseModel):
    query: str = Field(description="需要执行的SQL查询语句。")

# 2. 继承BaseTool
class DatabaseQueryTool(BaseTool):
    name: str = "execute_sql_query"
    description: str = "连接到数据库并执行只读的SQL查询。"
    args_schema: Type[BaseModel] = DatabaseQuerySchema

    def _run(
        self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        """执行同步查询"""
        print("--- 正在执行同步查询 ---")
        # 伪代码:db.connect().execute(query)
        return f"查询 '{query}' 的结果是:[数据...]"

    async def _arun(
        self, query: str, run_manager: Optional[AsyncCallbackManagerForToolRun] = None
    ) -> str:
        """执行异步查询"""
        print("--- 正在执行异步查询 ---")
        await asyncio.sleep(1) # 模拟异步IO操作
        # 伪代码:await db.async_connect().execute(query)
        return f"异步查询 '{query}' 的结果是:[数据...]"

# 实例化工具
db_tool = DatabaseQueryTool()
print(db_tool.invoke({"query": "SELECT * FROM users;"}))

4.4. 内置工具库

LangChain 团队和社区已经为我们预先构建了大量可以直接使用的工具,覆盖了搜索、计算、API交互等最常见的应用场景。使用这些内置工具,可以让我们避免重复造轮子,专注于应用的业务逻辑。梳理下三类常用库:

python 复制代码
# ================================
# 💡 一、搜索
# ================================

#【DuckDuckGo Search】无需API密钥,开箱即用,非常适合学习和快速原型开发。
# 需安装依赖「duckduckgo-search、ddgs」
from langchain_community.tools import DuckDuckGoSearchRun

search_tool = DuckDuckGoSearchRun()
result = search_tool.invoke("LangChain的最新版本是多少?")
print(result)

#【Google Search】功能强大,搜索结果质量高,但需要设置API密钥。
# 需安装依赖「google-api-python-client」,配置环境变量 GOOGLE_API_KEY 和 GOOGLE_CSE_ID
from langchain_community.tools import GoogleSearchRun

google_search_tool = GoogleSearchRun()
print(google_search_tool.invoke("2024年奥运会在哪里举办?"))

# 【Tavily Search】一个专门为LLM应用优化的搜索引擎,它返回的结果格式更简洁、更适合Agent处理,
# 是目前LangChain官方文档中比较推荐的工具。需安装依赖「tavily-python」,配置环境变量「TAVILY_API_KEY」
from langchain_community.tools.tavily_search import TavilySearchResults

tavily_tool = TavilySearchResults(max_results=3) # 可以指定返回结果数量
print(tavily_tool.invoke("介绍一下谷歌最新的图像模型nano banana"))

# ================================
# 💡 二、计算与代码执行
# 大型语言模型本质上是基于概率的文本生成器,它们并不擅长精确的数学计算和严谨的逻辑执行
# ================================

#【Python REPL Tool】一个极其强大但也需要谨慎使用的工具。它允许LLM生成并执行Python代码。
# ⚠️ 安全警告:绝对不能在不加任何防护的生产环境中直接暴露此工具。请务必在沙箱环境
# (如Docker容器)中运行,以隔离其文件系统和网络访问权限。
from langchain_experimental.tools import PythonREPLTool

python_repl_tool = PythonREPLTool()

# LLM可以生成并执行代码
code = "print(len('Hello World')**2)"
print(python_repl_tool.invoke(code))

#【Wolfram Alpha】一个强大的计算知识引擎,擅长自然科学、数学、符号计算等。
# 需安装依赖「wolframalpha」和设置环境变量「WOLFRAM_ALPHA_APP_ID」
from langchain_community.utilities.wolfram_alpha import WolframAlphaAPIWrapper

wolfram_wrapper = WolframAlphaAPIWrapper()
wolfram_tool = Tool(
    name="wolfram_alpha",
    func=wolfram_wrapper.run,
    description="当你需要回答关于数学、科学、技术、文化、社会和日常生活的问题时很有用。"
)
print(wolfram_tool.invoke("derivative of x^4 sin(x)"))

# ================================
# 💡 三、与外部系统交互工具 (如数据库、内部API、SaaS服务等)
# ================================

#【SQL Database Toolkit】提供了一整套与SQL数据库交互的工具,包括查询数据、查看表结构等的工具包
# 需安装依赖「langchain-community、sqlalchemy」
from langchain_community.utilities import SQLDatabase
from langchain_experimental.sql import SQLDatabaseToolkit

# 连接到数据库 (这里用SQLite做示例)
db = SQLDatabase.from_uri("sqlite:///mydatabase.db")
toolkit = SQLDatabaseToolkit(db=db, llm=ChatOpenAI())

# 工具包里包含多个工具
sql_tools = toolkit.get_tools()
# 例如: sql_tools[0].name -> 'sql_db_query'
# 例如: sql_tools[1].name -> 'sql_db_schema'
# Agent可以利用这些工具先查看有哪些表 (sql_db_list_tables),再看表的结构 (sql_db_schema),
# 最后构造并执行查询 (sql_db_query)。

#【Requests Tools】用于发出HTTP请求,让LLM可以与任何RESTful API进行交互。
# 需安装依赖「requests」
from langchain_community.tools.requests.tool import RequestsGetTool
from langchain_community.utilities.requests import TextRequestsWrapper

# 创建网络请求工具
requests_tool = RequestsGetTool(
    requests_wrapper=TextRequestsWrapper(),
    allow_dangerous_requests=True  # 允许危险请求(仅用于学习和测试)
)

response = requests_tool.invoke("https://httpbin.org/json")
print(response)

5. Chains - 链式调用

🤔 Chains (链) 是 LangChain 框架中用于 "将多个组件串联起来 " 的核心概念,就像一条 "流水线",将不同的处理步骤连接在一起,如:提示模板 → LLM → 输出解析器。每个环节都有特定的功能,数据在链中依次流转,还可以嵌套其他链,形成更复杂的处理逻辑。

5.1. 传统Chains类型

💡 在新版本中,传统Chains 已经被 LCEL (LangChain Expression Language) 所替代,大部分在 LangChain 0.1.17 版本后被标记为废弃,0.3.0 版本中已移除,笔者用的 0.3.32 版本,使用直接报错,所以这里只对 传统Chains 做下简单介绍,主要还是以 LCEL 为主。

  • LLMChain:最基础的 Chain,用于连接 PromptTemplate 和 LLM,按模板格式化输入并调用模型,适用于简单的文本生成任务。
  • SimpleSequentialChain顺序执行链,当任务需要多个步骤,且上一步的输出是下一步的输入时使用。
  • RouterChain智能路由链,支持条件分支逻辑,根据输入内容选择不同的处理路径,适用于多场景应用,如:客服机器人的意图识别。
  • StuffDocumentsChain文档填充链,将所有文档内容直接填入提示模板,简单直接,但受token限制,不适合长文档。
  • MapReduceDocumentsChain映射归约链,先对每个文档单独处理,再汇总结果,可处理大量文档,支持并行,但可能丢失文档间的关联信息。
  • RefineDocumentsChain迭代优化链,逐步处理文档,不断优化结果,优点是保持上下文连贯性,缺点是处理时间较长。
  • ConversationalRetrievalChain对话检索链,结合对话历史和知识检索,适用于需要支持多轮对话上下文的场景,如:智能问答系统。
  • RetrievalQA:检索问答链,基于文档检索回答问题,结合向量搜索和语言模型,适用于知识库问答的场景。

🤔 传统Chains 对于初学者来说,面向对象的方式很直观,能明确知道在创建一个什么类型的链,每个链对象都封装了特定的逻辑,开箱即用。但缺乏 "灵活性 ",如果想创建一个官方没有提供的、自定义的逻辑流,就需要继承基类并编写 大量模板代码 ,非常繁琐。而且 "功能受限 ",对 流式(Streaming)异步(Async)批量 (Batch) 操作的支持不是原生的,需额外的配置。

5.2. LCEL

LCELLangChain 的一种 "声明式编程范式 ",用于构建和组合可运行的链 (Chains),不是一个具体的 " ",而是一套使用 Python运算符 (主要是 管道符| ) 来组合组件的 "语法 "。其核心是 "Runnable 接口",它定义了一个标准的接口,使得组件能够:

  • 调用 (invoke): 单个输入转换为输出。
  • 批处理 (batch): 多个输入高效转换为输出。
  • 流式处理 (stream): 输出在生成时进行流式传输。
  • 检查 (inspect): 访问 Runnable 的输入、输出和配置的模式信息。
  • 组合 (compose): 多个 Runnables 可以使用 LCEL 组合成复杂管道。

管道操作符 | ,通过 "运算符重载" 实现了链式组合:

python 复制代码
# 管道操作符写法:

chain = prompt | model | output_parser

# 等同于「显式创建」
chain = RunnableSequence([prompt, model, output_parser])

LCEL的优势

  • 优化的并行执行:使用 RunnableParallel 或 Runnable Batch API 并行运行多个 Runnables,显著减少延迟。
  • 保证的异步支持:任何用 LCEL 构建的链都可以使用 Runnable Async API 异步运行,适用于需要处理大量并发请求的服务器环境。
  • 简化流式处理:LCEL 链可以进行流式处理,允许在链执行时增量输出,优化流式输出以最小化首个令牌时间。
  • 无缝的 LangSmith 追踪:所有步骤自动记录到 LangSmith,提供最大的可观测性和可调试性。
  • 标准 API 和部署能力:所有链都使用 Runnable 接口构建,可以用 LangServe 部署到生产环境。

😄 接着介绍下它的几个核心组件~

5.2.1. RunnableSequence - 序列组合

允许你按顺序链接多个 runnables,一个 runnable 的输出作为下一个的输入。简单代码示例:

python 复制代码
import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

# 创建组件
prompt = ChatPromptTemplate.from_template("请解释{concept}的概念")
model = ChatOpenAI(
    api_key=openai_api_key,
    base_url=openai_base_url,
    model=openai_api_model,
    temperature=1,
)
parser = StrOutputParser()	# 将LLM输出(通常是AIMessage对象)转换为纯字符串

# 组合成序列
chain = prompt | model | parser
# 等价于:chain = RunnableSequence([prompt, model, parser])

result = chain.invoke({"concept": "LECL"})
print(result)

5.2.2. RunnableParallel - 并行组合

允许同时执行多个处理分支,每个分支可以独立处理输入数据,最后将所有结果合并返回。简单代码示例:

python 复制代码
from langchain_core.runnables import RunnableParallel, RunnableLambda

# 创建并行处理链
# 文本分析器,同时对输入文本进行多种数据分析
analyzer = RunnableParallel({
    "summary": RunnableLambda(lambda x: f"总结:{x[:50]}..."),
    "length": RunnableLambda(lambda x: f"长度:{len(x)}字符"),
    "word_count": RunnableLambda(lambda x: f"词数:{len(x.split())}个"),
    "uppercase": RunnableLambda(lambda x: x.upper())
})

# 执行并行处理
text = "人工智能正在改变我们的生活方式"
result = analyzer.invoke(text)

# 输出结果
print(result)
# {
#     "summary": "总结:人工智能正在改变我们的生活方式...",
#     "length": "长度:16字符",
#     "word_count": "词数:1个",
#     "uppercase": "人工智能正在改变我们的生活方式"
# }

# RAG 应用中的多路检索
retrieval_chain = RunnableParallel({
    "vector_results": vector_store.as_retriever(),
    "keyword_results": keyword_search,
    "semantic_results": semantic_search
})

# 多模型集成
multi_model_chain = RunnableParallel({
    "gpt_response": gpt_chain,
    "claude_response": claude_chain,
    "gemini_response": gemini_chain
})

5.2.3. RunnablePassthrough - 数据透传

一个特殊的组件,它接收输入数据后不做任何处理,直接将数据透传到下一个组件。简单代码示例:

python 复制代码
# 💡 ① 保留原始数据
from langchain_core.runnables import (
    RunnableLambda,
    RunnableParallel,
    RunnablePassthrough,
)

# 在并行处理中保留原始输入
processing_chain = RunnableParallel(
    {
        "original": RunnablePassthrough(),
        "processed": RunnableLambda(lambda x: x.upper()),
    }
)

result = processing_chain.invoke("hello world")
print(result)
# 输出:{"original": "hello world", "processed": "HELLO WORLD"}

# 💡 ② RAG 应用中的上下文传递
from langchain_core.runnables import RunnablePassthrough

# 在 RAG 链中传递上下文
rag_chain = RunnableParallel({
    "context": retriever,
    "question": RunnablePassthrough()
}) | prompt | model | parser

# 💡 ③ 数据预处理和后处理
# 数据预处理 + 透传 + 后处理
data_pipeline = (
    RunnableLambda(lambda x: preprocess(x))  # 预处理
    | RunnablePassthrough()                   # 透传处理后的数据
    | RunnableLambda(lambda x: postprocess(x))  # 后处理
)

5.2.4. RunnableLambda - 函数包装器

将普通 Python 函数转换为 Runnable 对象的包装器,让任何函数都能集成到 LCEL 链中。简单代码示例:

python 复制代码
# 💡 ① 简单数据转换
double = RunnableLambda(lambda x: x * 2)
result = double.invoke(5)  # 输出:10
# 字符串处理
uppercase = RunnableLambda(lambda x: x.upper())
result = uppercase.invoke("hello")  # 输出:"HELLO"

# 💡 ② 复杂数据处理
def analyze_text(text: str) -> dict:
    """分析文本的复杂函数"""
    return {
        "original": text,
        "length": len(text),
        "words": len(text.split()),
        "uppercase": text.upper(),
        "reversed": text[::-1]
    }

# 包装为 Runnable
text_analyzer = RunnableLambda(analyze_text)

# 使用
result = text_analyzer.invoke("Hello World")
print(result)
# {
#     "original": "Hello World",
#     "length": 11,
#     "words": 2,
#     "uppercase": "HELLO WORLD",
#     "reversed": "dlroW olleH"
# }

# 💡 ③ 在链中的使用
processing_chain = (
    RunnableLambda(lambda x: f"输入:{x}")
    | RunnableLambda(lambda x: x.upper())
    | RunnableLambda(lambda x: f"结果:{x}")
)

result = processing_chain.invoke("test")
print(result)  # 输出:"结果:输入:TEST"

5.2.5. 异步支持

python 复制代码
import asyncio
from langchain_core.runnables import RunnableLambda

async def async_process(text: str) -> str:
    """异步处理函数"""
    await asyncio.sleep(0.1)  # 模拟异步操作
    return f"处理完成:{text.upper()}"

# 创建异步 Runnable
async_processor = RunnableLambda(async_process)

# 异步调用
result = await async_processor.ainvoke("hello world")
print(result)  # 输出:"处理完成:HELLO WORLD"

5.2.6. 高级组合模式

python 复制代码
#================================
# 💡 嵌套组合,并行+序列组合
#================================
complex_chain = RunnableParallel({
    "analysis": RunnableParallel({
        "sentiment": sentiment_analyzer,
        "keywords": keyword_extractor,
        "summary": text_summarizer
    }),
    "metadata": RunnableParallel({
        "timestamp": RunnableLambda(lambda x: datetime.now()),
        "source": RunnableLambda(lambda x: "api"),
        "version": RunnableLambda(lambda x: "1.0")
    }),
    "original": RunnablePassthrough()
})

#================================
# 💡 基于条件的分支处理
#================================
from langchain_core.runnables import RunnableBranch

branch_chain = RunnableBranch(
    (lambda x: len(x) < 10, short_text_processor),
    (lambda x: len(x) < 100, medium_text_processor),
    long_text_processor  # 默认分支
)

#================================
# 💡 配置管理,配置化链
#================================
from langchain_core.runnables import RunnableConfig

configurable_chain = chain.with_config(
    RunnableConfig(
        max_concurrency=5,
        max_retries=3,
        timeout=30.0
    )
)

#================================
# 💡 错误处理
#================================
from langchain_core.runnables import RunnableWithFallbacks

# 带回退的链
robust_chain = RunnableWithFallbacks(
    primary=primary_chain,
    fallbacks=[backup_chain1, backup_chain2]
)

# 带重试的执行
result = await robust_chain.ainvoke(input, config={"max_retries": 3})

5.2.7. 完整应用示例-简单文档分析器

AI推理简单计算 分开处理,三个AI任务同时进行,而非串行等待,单个链失败不影响其他链的执行,使用RunnablePassthrough()避免重复数据拷贝,原始文档引用传递,节省内存,具体代码实现:

python 复制代码
import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough, RunnableLambda
from langchain_openai import ChatOpenAI
from typing import Dict, Any

openai_api_key = os.getenv("OPENAI_API_KEY")
openai_base_url = os.getenv("OPENAI_BASE_URL")
openai_api_model = os.getenv("DEFAULT_LLM_MODEL")

class DocumentAnalyzer:
    """文档分析器"""

    def __init__(self):
        self.llm = ChatOpenAI(
            api_key=openai_api_key,
            base_url=openai_base_url,
            model=openai_api_model,
            temperature=0.1,
        )

        # 摘要生成链
        summary_prompt = ChatPromptTemplate.from_template(
            "请为以下文档生成一个简洁的摘要:\n\n{document}"
        )
        self.summary_chain = summary_prompt | self.llm | StrOutputParser()

        # 关键词提取链
        keywords_prompt = ChatPromptTemplate.from_template(
            "从以下文档中提取5-8个关键词:\n\n{document}"
        )
        self.keywords_chain = keywords_prompt | self.llm | StrOutputParser()

        # 情感分析链
        sentiment_prompt = ChatPromptTemplate.from_template(
            "分析以下文档的情感倾向(积极/消极/中性):\n\n{document}"
        )
        self.sentiment_chain = sentiment_prompt | self.llm | StrOutputParser()

    def create_analysis_chain(self):
        """创建完整的分析链"""

        # 并行分析链
        analysis_chain = RunnableParallel({
            "summary": self.summary_chain,
            "keywords": self.keywords_chain,
            "sentiment": self.sentiment_chain,
            "metadata": RunnableParallel({
                "word_count": RunnableLambda(lambda x: len(x["document"].split())),
                "char_count": RunnableLambda(lambda x: len(x["document"])),
                "language": RunnableLambda(lambda x: "中文" if any("\u4e00" <= c <= "\u9fff" for c in x["document"]) else "英文")
            }),
            "original": RunnablePassthrough()     # 原始输入
        })

        return analysis_chain

    def analyze(self, document: str) -> Dict[str, Any]:
        """分析文档"""
        analysis_chain = self.create_analysis_chain()

        input_data = {"document": document}
        result = analysis_chain.invoke(input_data)

        return result

def main():
    """主函数演示"""

    # 创建分析器
    analyzer = DocumentAnalyzer()

    # 测试文档
    test_document = """
    人工智能技术正在快速发展。在过去的几年中,我们看到了机器学习和深度学习技术的重大突破。
    从图像识别到自然语言处理,AI正在改变各个行业的工作方式。

    然而,随着AI技术的普及,也带来了许多挑战和伦理问题。我们需要在技术创新的同时,
    确保AI的发展符合人类的价值观和利益。

    展望未来,AI将继续在医疗、教育、交通等多个领域发挥重要作用。
    """

    print("🔍 正在分析文档...")
    result = analyzer.analyze(test_document)

    print("\n📊 分析结果:")
    print("=" * 50)

    print("📝 摘要:")
    print(result["summary"])
    print()

    print("🏷️ 关键词:")
    print(result["keywords"])
    print()

    print("😊 情感分析:")
    print(result["sentiment"])
    print()

    print("📊 元数据:")
    for key, value in result["metadata"].items():
        print(f"  {key}: {value}")
    print()

    print("✅ 文档分析完成!")

if __name__ == "__main__":
    main()

运行输出结果:

bash 复制代码
🔍 正在分析文档...

📊 分析结果:
==================================================
📝 摘要:
人工智能技术发展迅速,在机器学习和深度学习方面取得突破,正在改变各行各业。然而,AI的普及也带来了挑战和伦理 
问题,需要确保其发展符合人类价值观。未来,AI将在医疗、教育、交通等领域发挥重要作用。

🏷️ 关键词:
以下是从文档中提取的5-8个关键词:

1.  **人工智能 (AI)**
2.  **机器学习**
3.  **深度学习**
4.  **技术发展**
5.  **行业应用**
6.  **挑战与伦理**
7.  **未来展望**
8.  **价值观与利益**

😊 情感分析:
文档的情感倾向分析如下:

*   **整体情感倾向:中性偏积极**

**分析过程:**

1.  **积极方面:**
    *   "人工智能技术正在快速发展。" - 描述了技术进步,通常是积极的。
    *   "我们看到了机器学习和深度学习技术的重大突破。" - 强调了积极的进展和成就。
    *   "AI正在改变各个行业的工作方式。" - 暗示了效率提升和创新,是积极的影响。
    *   "展望未来,AI将继续在医疗、教育、交通等多个领域发挥重要作用。" - 描绘了AI在未来社会中的积极应用前景
。

2.  **消极方面(或挑战):**
    *   "然而,随着AI技术的普及,也带来了许多挑战和伦理问题。" - 明确指出了潜在的负面影响和需要解决的问题。
    *   "我们需要在技术创新的同时,确保AI的发展符合人类的价值观和利益。" - 这是一个警示性的陈述,强调了风险
和责任。

3.  **中性方面:**
    *   文档整体上是在陈述事实、描述现状和展望未来,并没有强烈的个人情感表达。

**结论:**

文档在描述AI的快速发展和未来潜力时,带有明显的积极色彩。但同时,它也客观地指出了AI发展带来的挑战和伦理问题 
,并强调了负责任发展的必要性。因此,虽然存在对挑战的提及,但整体基调更偏向于对AI技术进步的肯定和对其未来积 
极作用的展望。

所以,最准确的描述是**中性偏积极**。

📊 元数据:
  word_count: 5
  char_count: 194
  language: 中文

✅ 文档分析完成!
相关推荐
飞哥数智坊12 小时前
国外AI限制,国内工具欠佳?我用GLM-4.5+Claude Code搞了个国内平替
ai编程·claude·chatglm (智谱)
墨风如雪12 小时前
月之暗面Kimi K2-0905:代码与创意的新篇章?
aigc
猫头虎13 小时前
猫头虎AI分享:无需OCR,基于ColQwen2、Qwen2.5和Weaviate对PDF进行多模态RAG的解决方案
microsoft·ai·pdf·aigc·ocr·ai编程·ai-native
AiTop10014 小时前
腾讯混元翻译模型Hunyuan-MT-7B开源:小参数量大能量,获得30项国际冠军
人工智能·ai·自然语言处理·aigc·机器翻译
小妖同学学AI16 小时前
百度发布Comate AI IDE,我要把Cursor卸载了!
ide·人工智能·百度·ai编程
302AI16 小时前
翻车救星:谷歌官方Nano Banana提示词教程详解,附实测案例对比
google·aigc·gemini
子昕16 小时前
不想续费Kiro?这个 MCP 让 Claude Code 秒变 Spec 开发神器
ai编程
酥饼_i16 小时前
你的自动化脚本又双叒叕崩了?
前端·人工智能·ai编程
后端小肥肠17 小时前
做家常菜美食号别瞎折腾!Coze+Nano Banana 2 分钟出一周菜谱 + 配图,小白可学
人工智能·aigc·coze
三翼鸟数字化技术团队18 小时前
大模型应用开发框架 LangChain
langchain·llm