四. Langchain之Chain链

  • 为开发更复杂的应用程序,需要使用Chain来链接LangChain中的各个组件和功能,包括模型之间的链接以及模型与其他组件之间的链接

  • 链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一种基本功能单元。

  • API地址:https://python.langchain.com/api_reference/langchain/chains.html

1. 链的基本使用
  • LLMChain是最基础也是最常见的链。LLMChain结合了语言模型推理功能,并添加了PromptTemplate和Output Parser等功能,将模型输入输出整合在一个链中操作。

  • 它利用提示模板格式化输入,将格式化后的字符串传递给LLM模型,并返回LLM的输出。这样使得整个处理过程更加高效和便捷。

1.1 使用chain
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
import os
from dotenv import load_dotenv

load_dotenv()

# 原始字符串模板
template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"

# 创建模型实例
llm = ChatOpenAI(api_key=os.getenv("api_key"),
                 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                 model='qwen-max',
                 temperature=0)

# 创建LLMChain
llm_chain = LLMChain(
    llm=llm,
    prompt=PromptTemplate.from_template(template)
)

# 调用LLMChain,返回结果
result = llm_chain.invoke({"number": 2})
print(type(result))
print(result['text'])
1.3 使用表达式语言 (LCEL)
  • LangChain Expression Language 是一种以声明式方法,轻松地将链或组件组合在一起的机制。通过利用管道操作符,构建的任何链将自动具有完整的同步、异步和流式支持。

  • LangChain 表达式语言(LangChain Expression Language,简称 LCEL)是一种专为链组件(Chain)编排设计的声明式语法,其核心价值在于以统一的方式实现从简单到复杂的 AI 应用构建。从设计之初,LCEL 就致力于消除原型开发与生产部署间的鸿沟 —— 无论是基础的 "提示词 + LLM" 单链结构,还是包含 100 + 步骤的复杂工作流,均可通过同一套语法实现,无需修改代码逻辑。

  • python实现管道调用

    class Chain():
        def __init__(self, value):
            self.value = value
    
    
        def __or__(self, other):
            # 调用 | 运算符  触发的魔法方法
            return other(self.value)
    
    def prompt(text):
        return "请求回答问题:{}".format(text)
    
    aa = Chain('人工智能是什么?')
    
    res = aa | prompt
    print(res)
    

  • 普通调用

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
import os

load_dotenv()

# 创建提示词
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")

# 创建llm模型
model = ChatOpenAI(api_key=os.getenv("api_key"),
                   base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                   model="qwen-plus")
# 创建输出解释器
output_parser = StrOutputParser()
# 使用chain链在一起
chain = prompt | model | output_parser
print(chain.invoke({"topic": "ice cream"}))
  • 语言表达式语言(LCEL) 采用声明式方法从现有的 Runnable构建新的Runnable

2. Runnable是什么?
  • Runnable 接口是 LangChain 0.2 版本后推出的核心抽象层,旨在通过函数式编程模型统一各类 AI 组件的交互方式。它将语言模型(LLM)、链(Chain)、工具调用、数据处理等操作抽象为可组合的 "可运行单元"(Runnable),允许开发者以类似流水线(Pipeline)的方式编排复杂逻辑,而无需关注底层实现细节。

2.1 核心特性
2.2 主要实现类
2.3 案例
from langchain_openai import ChatOpenAI
from langchain.schema import SystemMessage
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.vectorstores import InMemoryVectorStore
from langchain.schema.runnable import RunnableMap, RunnableBranch, RunnableLambda
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_huggingface import HuggingFaceEmbeddings
from dotenv import load_dotenv
import os

load_dotenv()


class TravelQASystem:
    def __init__(self, openai_api_key, serpapi_api_key, embed_path):
        """初始化旅游问答系统核心组件"""

        # 初始化语言模型
        self.llm = ChatOpenAI(api_key=openai_api_key,
                              base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                              model="qwen-plus")

        # 初始化搜索工具
        self.search = TavilySearchResults(tavily_api_key=serpapi_api_key)

        # 初始化嵌入模型
        self.embeddings = HuggingFaceEmbeddings(model_name=embed_path)

        # 构建景点知识库
        self.attraction_data = [
            "故宫:北京地标,明清皇宫,开放时间8:30-17:00",
            "颐和园:皇家园林,昆明湖、长廊等景点",
            "八达岭长城:距离市区70公里,建议游览3-4小时"
        ]

        # 使用内存型向量存储类
        self.vector_store = InMemoryVectorStore.from_texts(
            self.attraction_data, self.embeddings, k=1
        )

    def setup_runnable_pipeline(self):
        """定义Runnable流程管道"""
        # 3.1 问题解析模块:识别地点与查询类型
        parse_prompt = ChatPromptTemplate.from_messages([
            SystemMessage(content="你是旅游助手,需从用户问题中提取地点和查询类型(天气/景点介绍/行程规划)"),
            ("user", """问题:{user_question}请以JSON格式返回:{{"location": "地点", "type": "查询类型"}}""")
        ])
        parse_module = parse_prompt | self.llm | JsonOutputParser()  # Output JSON string

        # 3.2 并行数据获取:天气查询+景点信息检索
        weather_query = RunnableLambda(
            lambda x: self.search.invoke(f"{x['location']} 今日天气")
        )
        attraction_retrieval = (lambda x: x["location"]) | self.vector_store.as_retriever() | (
            lambda x: x[0].page_content)
        # RunnableMap:并行执行天气查询和景点检索
        data_acquisition = RunnableMap({
            "weather": weather_query,
            "attraction": attraction_retrieval,
            "location": (lambda x: x["location"])
        })

        # 3.3 回答生成模块:整合信息并格式化
        generate_prompt = ChatPromptTemplate.from_messages([
            SystemMessage(content="你是专业旅游顾问,需结合景点信息和天气生成建议"),
            ("user", """地点:{location}
                景点信息:{attraction}
                天气情况:{weather}
                请生成1条行程建议,包含注意事项(如天气相关准备)""")
        ])
        generate_module = generate_prompt | self.llm | (lambda x: x.content.strip())

        """
        RunnableBranch实现
        RunnableBranch(
            (lambda x: 条件1, 执行的代码),
            (lambda x: 条件2, 执行的代码2),
            lambda x: {"location": x["location"], "attraction": attraction_retrieval.invoke(x)}
        )
        
        """
        # 3.4 全流程串联
        self.travel_qa_pipeline = (
            # 阶段1:解析问题
                parse_module
                | (lambda x: {"location": x["location"], "type": x["type"]})
                # 阶段2:并行获取数据(仅当查询类型为天气或行程时触发)
                # RunnableBranch:根据查询类型选择数据获取路径
                | RunnableBranch(
            (lambda x: "天气" in x["type"], data_acquisition),
            lambda x: {"location": x["location"], "attraction": attraction_retrieval.invoke(x)}
        )
                # 阶段3:生成回答
                | generate_module
        )

    def process_user_question(self, user_question):
        """处理用户提问并返回回答"""
        input_data = {"user_question": user_question}
        # try:
        response = self.travel_qa_pipeline.invoke(input_data)
        return response


# 示例用法
if __name__ == "__main__":
    # 替换为实际API密钥
    OPENAI_API_KEY = os.getenv("DASHSCOPE_API_KEY")
    # https://www.tavily.com/
    SERPAPI_API_KEY = os.getenv("TAVILY_API_KEY")
    embed_path = r"D:\LLM\Local_model\BAAI\bge-large-zh-v1___5"

    # 初始化系统
    travel_qa = TravelQASystem(OPENAI_API_KEY, SERPAPI_API_KEY, embed_path)
    travel_qa.setup_runnable_pipeline()

    # 测试1:查询天气与景点建议
    question1 = "今天故宫的天气怎么样?"
    answer1 = travel_qa.process_user_question(question1)
    print(f"User Question: {question1}\nAI Answer: {answer1}\n")
链的调用方式

Agent代理的核心思想是使用语言模型来选择要采取的一系列动作。在链中,动作序列是硬编码的。

在代理中,语言模型用作推理引擎来确定要采取哪些动作以及按什么顺序进行。

因此,在LangChain中,Agent代理就是使用语言模型作为推理引擎,让模型自主判断、调用工具和决定下一步行动。

Agent代理像是一个多功能接口,能够使用多种工具,并根据用户输入决定调用哪些工具,同时能够将一个工具的输出数据作为另一个工具的输入数据。

1. Agent的基本使用
1.1 Tavily在线搜索

  • 通过invoke方法

    from langchain_core.prompts import PromptTemplate
    from langchain_openai import ChatOpenAI
    from dotenv import load_dotenv
    import os
    
    load_dotenv()
    
    # 原始字符串模板
    template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"
    prompt = PromptTemplate.from_template(template)
    
    # 创建模型实例
    llm = ChatOpenAI(api_key=os.getenv("api_key"),
                     base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                     model='qwen-plus',
                     temperature=0)
    
    # 创建Chain
    chain = prompt | llm
    
    # 调用Chain,返回结果
    result = chain.invoke({"number": "3"})
    print(result)

    通过batch方法(原apply方法):batch方法允许输入列表运行链,一次处理多个输入。

    from langchain.chains.llm import LLMChain
    from langchain_core.prompts import PromptTemplate
    from langchain_openai import ChatOpenAI
    from dotenv import load_dotenv
    import os
    
    load_dotenv()
    
    
    # 创建模型实例
    template = PromptTemplate(
        input_variables=["role", "fruit"],
        template="{role}喜欢吃{fruit}?",
    )
    
    # 创建LLM
    llm = ChatOpenAI(api_key=os.getenv("api_key"),
                     base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                     model='qwen-plus',
                     temperature=0)
    
    # 创建LLMChain     0.1.17 开始被标记为弃用,并计划在未来的 1.0 版本中移除
    # llm_chain = LLMChain(llm=llm, prompt=template)
    llm_chain = template | llm
    
    # 输入列表
    input_list = [
        {"role": "猪八戒", "fruit": "人参果"}, {"role": "孙悟空", "fruit": "仙桃"}
    ]
    
    # 调用LLMChain,返回结果
    result = llm_chain.batch(input_list)
    print(result[0].content)
    print(result[1].content)

    五. Agent代理

    在LangChain框架中,Agents是一种利用大型语言模型(Large Language Models,简称LLMs)来执行任务和做出决策的系统

    在 LangChain 的世界里,Agent 是一个智能代理,它的任务是听取你的需求(用户输入)和分析当前的情境(应用场景),然后从它的工具箱(一系列可用工具)中选择最合适的工具来执行操作

  • 使用工具(Tool):LangChain中的Agents可以使用一系列的工具(Tools)实现,这些工具可以是API调用、数据库查询、文件处理等,Agents通过这些工具来执行特定的功能。

  • 推理引擎(Reasoning Engine):Agents使用语言模型作为推理引擎,以确定在给定情境下应该采取哪些行动,以及这些行动的执行顺序。

  • 可追溯性(Traceability):LangChain的Agents操作是可追溯的,这意味着可以记录和审查Agents执行的所有步骤,这对于调试和理解代理的行为非常有用。

  • 自定义(Customizability):开发者可以根据需要自定义Agents的行为,包括创建新的工具、定义新的Agents类型或修改现有的Agents。

  • 交互式(Interactivity):Agents可以与用户进行交互,响应用户的查询,并根据用户的输入采取行动。

  • 记忆能力(Memory):LangChain的Agents可以被赋予记忆能力,这意味着它们可以记住先前的交互和状态,从而在后续的决策中使用这些信息。

  • 执行器(Agent Executor):LangChain提供了Agent Executor,这是一个用来运行代理并执行其决策的工具,负责协调代理的决策和实际的工具执行。

  • 构建一个具有两种工具的代理:一种用于在线查找,另一种用于查找加载到索引中的特定数据。

  • 在LangChain中有一个内置的工具,可以方便地使用Tavily搜索引擎作为工具。

  • 访问Tavily(用于在线搜索)注册账号并登录,获取API 密钥

  • TAVILY_API_KEY申请:https://tavily.com/

  • # 加载所需的库
    import os
    from langchain_community.tools.tavily_search import TavilySearchResults
    from dotenv import load_dotenv
    load_dotenv()
    
    # 查询 Tavily 搜索 API 并返回 json 的工具
    search = TavilySearchResults(tavily_api_key=os.getenv("tavily_key"))
    # 执行查询
    res = search.invoke("目前市场上苹果手机16的售价是多少?")
    print(res)

    创建检索器

    • 根据上述查询结果中的某个URL中,获取一些数据创建一个检索器。

# 加载所需的库
import os

from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv

load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
# search = TavilySearchResults()
# # 执行查询
# res = search.invoke("目前市场上苹果手机16的售价是多少?")
# print(res)


# 创建索引器根据上述查询的结果

# 加载HTML内容为一个文档对象
loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
# 读取文档
docs = loader.load()
# print(docs)

# 分割文档
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)

# 向量化
vector = FAISS.from_documents(documents, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))

# 创建检索器
retriever = vector.as_retriever()

# 测试检索结果
print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))
          • 得到工具列表

          • from langchain.tools.retriever import create_retriever_tool
            # 创建一个工具来检索文档
            retriever_tool = create_retriever_tool(
                retriever,
                "iPhone_price_search",
                "搜索有关 iPhone 16 的价格信息。对于iPhone 16的任何问题,您必须使用此工具!",
            )
            
            # 创建将在下游使用的工具列表
            tools = [search, retriever_tool]

            对接大模型

          • 创建Agent,这里使用LangChain中一个叫OpenAI functions的代理,然后得到一个AgentExecutor代理执行器

          # 加载所需的库
          import os
          
          from langchain import hub
          from langchain_community.tools.tavily_search import TavilySearchResults
          from langchain_community.document_loaders import WebBaseLoader
          from langchain_community.vectorstores import FAISS
          from langchain_community.embeddings import DashScopeEmbeddings
          from langchain_openai import ChatOpenAI
          from langchain_text_splitters import RecursiveCharacterTextSplitter
          from dotenv import load_dotenv
          from langchain.tools.retriever import create_retriever_tool
          load_dotenv()
          
          # 查询 Tavily 搜索 API 并返回 json 的工具
          search = TavilySearchResults(tavily_api_key=os.getenv("tavily_key"))
          # # 执行查询
          # res = search.invoke("目前市场上苹果手机16的售价是多少?")
          # print(res)
          
          
          # 创建索引器根据上述查询的结果
          
          # 加载HTML内容为一个文档对象
          loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
          # 读取文档
          docs = loader.load()
          # print(docs)
          
          # 分割文档
          documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)
          
          # 向量化
          vector = FAISS.from_documents(documents, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))
          
          # 创建检索器
          retriever = vector.as_retriever()
          
          # 测试检索结果
          # print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))
          
          
          # 创建一个工具来检索文档
          retriever_tool = create_retriever_tool(
              retriever,
              "iPhone_price_search",
              "搜索有关 iPhone 16 的价格信息。对于iPhone 16的任何问题,您必须使用此工具!",
          )
          
          # 创建将在下游使用的工具列表
          tools = [search, retriever_tool]
          
          # 初始化大模型
          llm = ChatOpenAI(api_key=os.getenv("api_key"),
                           base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                           model='qwen-plus', temperature=0)
          
          
          
          # https://smith.langchain.com/hub
          # 获取要使用的提示
          prompt = hub.pull("hwchase17/openai-functions-agent")
          # 打印Prompt
          # print(prompt)
          
          # 使用OpenAI functions代理
          from langchain.agents import create_openai_functions_agent
          
          # 构建OpenAI函数代理:使用 LLM、提示模板和工具来初始化代理
          agent = create_openai_functions_agent(llm, tools, prompt)
          
          from langchain.agents import AgentExecutor
          # 将代理与AgentExecutor工具结合起来
          agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
          # 执行代理 进行对比
          # agent_executor.invoke({"input": "目前市场上苹果手机16的各个型号的售价是多少?如果我在此基础上加价5%卖出,应该如何定价?"})
          agent_executor.invoke({"input": "美国2024年谁胜出了总统的选举?"})
          

            2. OpenAI Functions Agent
            
            from langchain.agents import create_openai_functions_agent, AgentExecutor
            from langchain_community.tools.tavily_search import TavilySearchResults
            from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
            from langchain_openai import ChatOpenAI
            from langchain_core.tools import Tool
            import os
            from dotenv import load_dotenv
            
            load_dotenv()
            
            
            # 定义查询订单状态的函数
            def query_order_status(order_id):
                if order_id == "1024":
                    return "订单 1024 的状态是:已发货,预计送达时间是 3-5 个工作日。"
                else:
                    return f"未找到订单 {order_id} 的信息,请检查订单号是否正确。"
            
            
            # 定义退款政策说明函数
            def company_refund_policy(company_name):
                print(company_name)
                if company_name == "tom公司":
                    return "tom公司的退款政策是:在购买后7天内可以申请全额退款,需提供购买凭证。"
                else:
                    print('输入有误')
            
            
            # 查询年龄
            def get_age(name):
                if name == "tom":
                    print(name)
                    return "我的年龄是56岁!"
                else:
                    print('输入有误')
            
            
            # 初始化工具
            tools = [
                TavilySearchResults(max_results=1, tavily_api_key=os.getenv("tavily_key")),
                Tool(
                    name="queryOrderStatus",
                    func=query_order_status,
                    description="根据订单ID查询订单状态",
                    args={"order_id": "订单的ID"}
                ),
                Tool(
                    name="companyRefundPolicy",
                    func=company_refund_policy,
                    description="查询某某公司退款政策详细内容",
                    args={"company_name": "公司名称"}
                ),
                Tool(
                    name="getAge",
                    func=get_age,
                    description="查询tom年龄大小",
                    args={"name": "查询tom年龄大小"}
                ),
            ]
            
            # 获取使用的提示
            prompt = ChatPromptTemplate.from_messages([
                ("system",
                    "你是一个客服助手,使用工具回答问题。传递给工具的内容必须是准确的json数据不结尾的括号多加一个,如果是字符串数据必须和输入的保持一致要完整,不是篡改**重要规则**, "
                ),
                # 用户输入变量
                ("user", "{input}"),
                # 关键点:添加代理中间步骤占位符
                MessagesPlaceholder(variable_name="agent_scratchpad")
            ])
            # print(prompt)
            # 选择将驱动代理的LLM
            llm = ChatOpenAI(api_key=os.getenv("api_key"),
                             base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                             model='qwen-plus')
            
            # 构建OpenAI函数代理
            agent = create_openai_functions_agent(llm, tools, prompt)
            
            # 通过传入代理和工具创建代理执行器
            agent_executor = AgentExecutor(agent=agent, tools=tools, handle_parsing_errors=True, verbose=True)
            
            # 定义一些测试询问
            queries = [
                "请问订单1024的状态是什么?",
                "请问tom公司退款政策是什么?",
                "2024年谁胜出了美国总统的选举"
            ]
            
            # 运行代理并输出结果
            for input in queries:
                response = agent_executor.invoke({"input": input})
                print(f"客户提问:{input}")
                print(f"代理回答:{response}\n")
            
            # response = agent_executor.invoke({"input": "2024年谁胜出了美国总统的选举"})
            # print(f"代理回答:{response}\n")
            

            • 六. LangChain之Tools工具

              1. 工具Tools

              工具是代理、链或LLM可以用来与世界互动的接口。它们结合了几个要素

            • 工具的名称

            • 工具的描述

            • 该工具输入的JSON模式

            • 要调用的函数

            • 是否应将工具结果直接返回给用户
               

            1.1 工具的初步认识
            
            from langchain_community.tools.tavily_search import TavilySearchResults
            from dotenv import load_dotenv
            import os
            
            load_dotenv()
            
            # 初始化工具 可以根据需要进行配置
            tool = TavilySearchResults(top_k_results=1, doc_content_chars_max=100)
            
            # 工具默认名称
            print("name:", tool.name)
            # 工具默认的描述
            print("description:", tool.description)
            # 输入内容 默认JSON模式
            print("args:", tool.args)
            # 是否直接返回工具的输出。
            print("return_direct:", tool.return_direct)
            
            # 可以用字典输入来调用这个工具
            print(tool.run({"query": "langchain"}))
            # 使用单个字符串输入来调用该工具。
            print(tool.run("langchain"))
            # 需要科学上网
            1.2 自定义工具
            • 在LangChain中,自定义工具有多种方法

            • @tool装饰器

            • @tool装饰器是定义自定义工具的最简单方法。装饰器默认使用函数名称作为工具名称,但可以通过传递字符串作为第一个参数来覆盖此设置。此外,装饰器将使用函数的文档字符串作为工具的描述 - 因此必须提供文档字符串。

            
            from langchain.tools import tool
            
            @tool
            def add_number(a: int, b: int) -> int:
                """add two numbers."""
                return a + b
            
            
            print(add_number.name)
            print(add_number.description)
            print(add_number.args)
            
            res = add_number.run({"a": 10, "b": 20})
            print(res)
            

            七. LangChain之Memory

            • 大多数的 LLM 应用程序都会有一个会话接口,允许我们和 LLM 进行多轮的对话,并有一定的上下文记忆能力。但实际上,模型本身是不会记忆任何上下文的,只能依靠用户本身的输入去产生输出。而实现这个记忆功能,就需要额外的模块去保存我们和模型对话的上下文信息,然后在下一次请求时,把所有的历史信息都输入给模型,让模型输出最终结果。

            from dotenv import load_dotenv
            from langchain_openai import ChatOpenAI
            import os
            load_dotenv()
            
            llm = ChatOpenAI(api_key=os.getenv("api_key"),
                             base_url=os.getenv("base_url"),
                             model_name="qwen-plus")
            
            # 直接提供问题,并调用llm
            response = llm.invoke("你好我是宝宝")
            # print(response)
            # print("=" * 50)
            print(response.content)
            
            response = llm.invoke("我是谁?")
            
            print(response.content)
            
            • 而在 LangChain 中,提供这个功能的模块就称为 Memory,用于存储用户和模型交互的历史信息。

            • 记忆系统需要支持两种基本操作:读取和写入。

              • 在接收到初始用户输入之后但在执行核心逻辑之前,链将从其内存系统中读取并增强用户输入。

              • 在执行核心逻辑之后但在返回答案之前,链会将当前运行的输入和输出写入内存,以便在将来的运行中引用它们。

            • 对该图的解释:

              1、输入问题: ({"question": ...})

              2、读取历史消息: 从Memory中READ历史消息({"past_messages": [...]})

              3、构建提示(Prompt): 读取到的历史消息和当前问题会被合并,构建一个新的Prompt

              4、模型处理: 构建好的提示会被传递给语言模型进行处理。语言模型根据提示生成一个输出。

              5、解析输出: 输出解析器通过正则表达式 regex("Answer: (.*)")来解析,返回一个回答({"answer": ...})给用户

              6、得到回复并写入Memory: 新生成的回答会与当前的问题一起写入Memory,更新对话历史。Memory会存储最新的对话内容,为后续的对话提供上下文支持。

            1. Chat Messages
            • Chat Messages: 最基础的记忆管理方法,是用于管理和存储对话历史的具体实现。它们通常用于构建对话系统,帮助系统保持对话的连续性和上下文。这些消息通常包含了对话的每一轮,包括用户的输入和系统的响应。

            2. RunnableWithMessageHistory

            • RunnableWithMessageHistory 包装另一个 Runnable 并为其管理聊天消息历史记录;它负责读取和更新聊天消息历史记录。

            2.1 本地内存存储
            • 可以将聊天记录进行本地存储

            import json
            from langchain_core.runnables.history import RunnableWithMessageHistory
            from langchain_community.chat_message_histories import ChatMessageHistory
            from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
            from langchain_openai import ChatOpenAI
            from langchain.schema import messages_from_dict, messages_to_dict
            from dotenv import load_dotenv
            import os
            
            # 加载环境变量(需要包含API_KEY)
            load_dotenv()
            
            # 初始化大语言模型(通义千问)
            llm = ChatOpenAI(
                api_key=os.getenv("api_key"),  # 从环境变量读取API密钥
                base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 阿里云兼容端点
                model="qwen-turbo"  # 使用qwen-turbo模型
            )
            
            # 创建对话提示模板
            prompt = ChatPromptTemplate.from_messages([
                # 系统角色设定
                ("system", "你是一个友好的助手"),
                # 历史消息占位符(变量名必须与链配置中的history_messages_key一致)
                MessagesPlaceholder(variable_name="history"),
                # 用户输入占位符
                ("user", "{input}")
            ])
            
            # 构建基础对话链(组合提示模板和语言模型)
            base_chain = prompt | llm
            
            # 全局会话存储字典(Key: session_id, Value: ChatMessageHistory实例)
            store = {}
            
            
            def get_session_history(session_id):
                """获取或创建会话历史存储对象
                Args:
                    session_id: 会话唯一标识(用于多会话隔离)
                Returns:
                    对应会话的聊天历史记录对象
                """
                if session_id not in store:
                    store[session_id] = ChatMessageHistory()  # 初始化空历史记录
                return store[session_id]
            
            
            # 创建支持历史记录的对话链
            conversation = RunnableWithMessageHistory(
                base_chain,  # 基础对话链
                get_session_history=get_session_history,  # 历史记录获取方法
                input_messages_key="input",  # 输入文本的键名
                history_messages_key="history"  # 历史记录的键名(需与提示模板中的变量名一致)
            )
            
            
            def save_memory(filepath, session_id):
                """保存指定会话的历史记录到文件
                Args:
                    filepath: 文件保存路径(建议使用.json扩展名)
                    session_id: 要保存的会话ID(默认"default")
                """
                history = get_session_history(session_id)
                # 将消息对象列表转换为字典格式
                dicts = messages_to_dict(history.messages)
                # 写入JSON文件(UTF-8编码)
                with open(filepath, "w", encoding='utf-8') as f:
                    json.dump(dicts, f, ensure_ascii=False)
            
            
            def load_memory(filepath, session_id):
                """从文件加载历史记录到指定会话
                Args:
                    filepath: 历史记录文件路径
                    session_id: 要加载到的会话ID(默认"default")
                """
                with open(filepath, "r", encoding='utf-8') as f:
                    dicts = json.load(f)
                # 将字典转换回消息对象列表
                messages = messages_from_dict(dicts)
                # 更新全局存储中的会话历史
                store[session_id] = ChatMessageHistory(messages=messages)
            
            
            def legacy_predict(input_text: str, session_id: str = "default") -> str:
                """模拟旧版predict方法的调用接口
                Args:
                    input_text: 用户输入文本
                    session_id: 会话ID(默认"default")
                Returns:
                    AI生成的回复文本
                """
                return conversation.invoke(
                    {"input": input_text},  # 输入参数
                    # 配置参数(必须包含session_id来关联历史记录)
                    config={"configurable": {"session_id": session_id}}
                ).content
            
            
            if __name__ == "__main__":
                # 使用默认会话ID
                SESSION_ID = "default"
            
                # 模拟连续对话(4轮)
                legacy_predict("你好", SESSION_ID)  # 问候
                legacy_predict("你是谁,我是宝宝", SESSION_ID)  # 身份确认
                legacy_predict("你的背后实现原理是什么", SESSION_ID)  # 技术原理询问
            
                # 查询对话历史(第4轮)
                last_response = legacy_predict('截止到现在我们聊了什么?', SESSION_ID)
                print("最后一次回答:", last_response)
            
                # 持久化保存对话历史(JSON格式)
                save_memory("./memory_new.json", SESSION_ID)
            
                # 模拟重新加载历史记录(清空当前会话后重新加载)
                load_memory("./memory_new.json", SESSION_ID)
            
                # 验证历史恢复效果(第5轮)
                reload_response = legacy_predict("我回来了,我们之前都聊了一些什么?", SESSION_ID)
                print("\n恢复后的回答:", reload_response)

            八. LangSmith使用

            1. 什么是 LangSmith

            LangSmith 是一个用于构建、调试和监控大型语言模型 (LLM) 应用的平台,由 LangChain 团队开发。它帮助开发者跟踪 LLM 的调用、性能和输出,优化提示词(Prompt)设计,并管理数据集和评估。

            主要功能:

            • 跟踪(Tracing):记录 LLM 调用、输入输出和元数据。

            • 调试(Debugging):分析模型行为,识别问题。

            • 数据集管理:创建和维护测试数据集。

            • 评估(Evaluation):运行测试用例,评估模型性能。

            • 监控(Monitoring):实时监控生产环境中的 LLM 应用。

            2. 配置 API 密钥
            1. 注册 LangSmith 账号。

            2. LangSmith 仪表板中获取 API 密钥。

            3. 设置环境变量:.env

              LANGSMITH_API_KEY = 'Langsmith-key'   LANGCHAIN_TRACING_V2="true"   LANGCHAIN_PROJECT="在Langsmith存储的名称"   LANGCHAIN_ENDPOINT="https://api.smith.langchain.com" # 任务发布站点

            4. 执行代码不需要有任何的改动,会自动把执行的内容上传到Langsmith进行管理,在平台把任务已树结构展示项目的内容,展示项目的耗时,加载的提示词内容等等

            from langchain.prompts import PromptTemplate
            from langchain_openai import ChatOpenAI
            from dotenv import load_dotenv
            load_dotenv()
            
            import os
            
            
            
            
            # 定义小红书文案提示词
            prompt = PromptTemplate.from_template("""
            你是一位小红书内容创作者,擅长撰写简洁、吸引人的种草文案。目标是创作100-150字的小红书风格文案,面向18-35岁用户,激发兴趣和互动。
            
            **输入**:
            - 产品/主题:{product}
            - 核心特点:{features}
            - 目标情绪:{emotion}
            - 目标行动:{action}
            
            **要求**:
            1. 风格:亲切、口语化,带小幽默或生活场景,融入“种草”“安利”等流行词。
            2. 结构:吸睛开头(问题/场景),中间突出特点,结尾引导互动(提问/号召)。
            3. 使用1-2个emoji,保持自然。
            4. 标题:10字以内。
            
            **输出**:
            标题:
            文案正文:分2-3段,每段2-3句,结尾带互动引导
            """)
            
            llm = ChatOpenAI(
                api_key=os.getenv("DASHSCOPE_API_KEY"),
                base_url=os.getenv("DASHSCOPE_BASE_URL"),
                model='qwen-plus'
            )
            
            # 创建 LangChain 链
            chain = prompt | llm
            
            # 输入示例
            input_data = {
                "product": "无线耳机",
                "features": "音质清晰、佩戴舒适、续航长",
                "emotion": "科技感、轻松",
                "action": "分享体验"
            }
            response = chain.invoke(input_data)
            # 输出生成的小红书文案
            print(response.content)
            
            3. 提示词优化
            • Langsmith 提供了提示词优化和导出提示词模版,可以用Langsmith 优化自己的提示词得到满意的输出,在将提示词导出使用

            from langchain.prompts import PromptTemplate
            from langchain_openai import ChatOpenAI
            from dotenv import load_dotenv
            load_dotenv()
            from langsmith import Client
            
            import os
            
            client = Client(api_key=os.getenv("LANGSMITH_API_KEY"))
            prompt = client.pull_prompt("test")#test是存储的prompt
            
            print(prompt)
            llm = ChatOpenAI(
                api_key=os.getenv("DASHSCOPE_API_KEY"),
                base_url=os.getenv("DASHSCOPE_BASE_URL"),
                model='qwen-plus'
            )
            
            # 创建 LangChain 链
            chain = prompt | llm
            
            # 输入示例
            input_data = {
                "product": "无线耳机",
                "features": "音质清晰、佩戴舒适、续航长",
                "emotion": "科技感、轻松",
                "action": "分享体验"
            }
            
            response = chain.invoke(input_data)
            
            # 输出生成的小红书文案
            print(response.content)
            

            Logo

            AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

            更多推荐