概述

本章节我们通过一个示例讲解如何设计和实现一个基于大语言模型(LLM)的聊天机器人。这个聊天机器人能够进行对话,并且记住之前与聊天模型的交互内容。

准备

Jupyter Notebook

本教程(以及LangChain系列中的大多数其他章节教程)使用的是 Jupyter Notebook。Jupyter Notebook 非常适合学习如何与 LLM 系统协作,因为很多时候可能会出现问题(如输出异常、API 故障等),而在交互式环境中学习和操作可以更好理解这些内容。

python 库安装

本教程需要使用 langchain-core 和 langgraph。要求 langgraph 的版本为 0.2.28 或更高

pip install langchain-core langgraph>0.2.27

LangSmith

使用 LangChain 构建的许多应用程序通常包含多个步骤,并多次调用 LLM。随着应用的复杂度不断增加,能够深入了解链(chain)或代理(agent)内部发生了什么帮助我们更好的理解和定位问题。最好的方法就是使用LangSmith.。

在你通过上面的链接注册之后,需要先设置环境变量,才能使用记录跟踪信息:

export LANGSMITH_TRACING="true"export LANGSMITH_API_KEY="..."

如果实在Notebook里面,可以像下面这样设置​​​​​​​

import getpassimport os
os.environ["LANGSMITH_TRACING"] = "true"os.environ["LANGSMITH_API_KEY"] = getpass.getpass()

快速开始

首先,让我们学习如何单独使用语言模型。LangChain支持多种语言模型,你可以根据需要互换使用——在下面选择你想要使用的模型即可!

pip install -qU "langchain[openai]"
import getpassimport os
if not os.environ.get("OPENAI_API_KEY"):  os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4o-mini", model_provider="openai")

我们先直接使用模型。ChatModels 是 LangChain 中 “Runnable” 的实例,它们提供了一个标准接口用于模型交互。
如果只是简单地调用模型,可以将一组消息列表传递给 .invoke 方法。​​​​​​​

from langchain_core.messages import HumanMessage
model.invoke([HumanMessage(content="Hi! I'm Bob")])

AIMessage(content='Hi Bob! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 11, 'total_tokens': 21, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-5211544f-da9f-4325-8b8e-b3d92b2fc71a-0', usage_metadata={'input_tokens': 11, 'output_tokens': 10, 'total_tokens': 21, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

模型本身没有任何“状态”概念。例如,如果你接着再问问题:

model.invoke([HumanMessage(content="What's my name?")])
    AIMessage(content="I'm sorry, but I don't have access to personal information about users unless it has been shared with me in the course of our conversation. How can I assist you today?", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 34, 'prompt_tokens': 11, 'total_tokens': 45, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-a2d13a18-7022-4784-b54f-f85c097d1075-0', usage_metadata={'input_tokens': 11, 'output_tokens': 34, 'total_tokens': 45, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

    我们可以看到,模型没有将之前的对话轮次纳入上下文,因此无法回答这个问题。这会导致非常糟糕的聊天体验!

    为了解决这个问题,我们需要将整个对话历史传递给模型。
    让我们看看这样做会发生什么:​​​​​​​

    from langchain_core.messages import AIMessage
    model.invoke(    [        HumanMessage(content="Hi! I'm Bob"),        AIMessage(content="Hello Bob! How can I assist you today?"),        HumanMessage(content="What's my name?"),    ])
    
    AIMessage(content='Your name is Bob! How can I help you today, Bob?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 14, 'prompt_tokens': 33, 'total_tokens': 47, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_0705bf87c0', 'finish_reason': 'stop', 'logprobs': None}, id='run-34bcccb3-446e-42f2-b1de-52c09936c02c-0', usage_metadata={'input_tokens': 33, 'output_tokens': 14, 'total_tokens': 47, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

    现在我们可以看到,模型给出了一个更好的回应!

    这就是支持聊天机器人进行对话交互的基本原理。那么,我们该如何更好地实现这一点呢?

    消息持久化(Message persistence)

    LangGraph 实现了内置的持久化层,非常适合支持多轮对话的聊天应用。

    将我们的聊天模型封装在一个最小化的 LangGraph 应用中,可以自动持久化消息历史,从而简化多轮对话应用的开发。

    LangGraph自带一个简单的内存检查点工具(in-memory checkpointer),向下面这样使用。​​​​​​​

    from langgraph.checkpoint.memory import MemorySaverfrom langgraph.graph import START, MessagesState, StateGraph
    # Define a new graphworkflow = StateGraph(state_schema=MessagesState)
    
    # Define the function that calls the modeldef call_model(state: MessagesState):    response = model.invoke(state["messages"])    return {"messages": response}
    
    # Define the (single) node in the graphworkflow.add_edge(START, "model")workflow.add_node("model", call_model)
    # Add memorymemory = MemorySaver()app = workflow.compile(checkpointer=memory)

    现在我们需要创建一个配置(config),在每次调用 runnable 时传入。这个配置包含一些虽然不是输入信息但有用的信息。

    在本例中,我们希望包含一个 thread_id。配置应如下所示:

    config = {"configurable": {"thread_id": "abc123"}}

    这使我们能够在单个应用中支持多个对话线程,这在应用有多个用户时是一个常见需求。

    接着,我们就可以调用这个应用了:​​​​​​​

    query = "Hi! I'm Bob."
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()  # output contains all messages in state

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    Hi Bob! How can I assist you today?

    ​​​​​​​

    query = "What's my name?"
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    Your name is Bob! How can I help you today, Bob?

    我们的聊天机器人现在能记住关于我们的信息了。如果我们把配置里的 thread_id 改成另一个不同的值,就会看到它从新的对话开始,不带任何之前的上下文。​​​​​​​

    config = {"configurable": {"thread_id": "abc234"}}
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    I'm sorry, but I don't have access to personal information about you unless you've shared it in this conversation. How can I assist you today?

    不过,由于我们将对话持久化到了数据库中,随时都可以回到最初的那场对话继续交流。

    ​​​​​​​

    config = {"configurable": {"thread_id": "abc123"}}
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    Your name is Bob. What would you like to discuss today?

    这就是我们如何支持聊天机器人与多个用户进行多场对话的方式!

    对于异步支持,需要将 call_model 节点更新为异步函数,并在调用应用程序时使用 .ainvoke

    ​​​​​​​

    # Async function for node:async def call_model(state: MessagesState):    response = await model.ainvoke(state["messages"])    return {"messages": response}
    
    # Define graph as before:workflow = StateGraph(state_schema=MessagesState)workflow.add_edge(START, "model")workflow.add_node("model", call_model)app = workflow.compile(checkpointer=MemorySaver())
    # Async invocation:output = await app.ainvoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()

    目前,我们所做的只是给模型加了一个简单的持久化层。接下来,我们可以通过加入提示模板,让聊天机器人变得更复杂、更个性化。

    提示模板(Prompt Templates)

    提示模板将原始用户信息转换成 LLM 可以处理的格式。上面,原始用户输入只是一个消息,我们直接传给了LLM。现在让我们把它做得更复杂一些。
    首先,加入一个带有自定义指令的系统消息(但仍以消息作为输入)。然后,除了消息之外,我们还会加入更多输入内容。

    要添加系统消息,我们将创建一个ChatPromptTemplate,并利用 MessagesPlaceholder 来传入所有消息。​​​​​​​

    from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
    prompt_template = ChatPromptTemplate.from_messages(    [        (            "system",            "You talk like a pirate. Answer all questions to the best of your ability.",        ),        MessagesPlaceholder(variable_name="messages"),    ])

    我们修改之前的代码,使用提示模板的方式​​​​​​​

    workflow = StateGraph(state_schema=MessagesState)
    
    def call_model(state: MessagesState):    prompt = prompt_template.invoke(state)    response = model.invoke(prompt)    return {"messages": response}
    
    workflow.add_edge(START, "model")workflow.add_node("model", call_model)
    memory = MemorySaver()app = workflow.compile(checkpointer=memory)

    使用同样的方式调用应用​​​​​​​

    config = {"configurable": {"thread_id": "abc345"}}query = "Hi! I'm Jim."
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()==================================[1m Ai Message [0m==================================
    Ahoy there, Jim! What brings ye to these waters today? Be ye seekin' treasure, knowledge, or perhaps a good tale from the high seas? Arrr!
    query = "What is my name?"
    input_messages = [HumanMessage(query)]output = app.invoke({"messages": input_messages}, config)output["messages"][-1].pretty_print()

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    Ye be called Jim, matey! A fine name fer a swashbuckler such as yerself! What else can I do fer ye? Arrr!

    很好,现在让我们把提示模板弄得更复杂一点。

    假设提示模板现在看起来像这样:

    ​​​​​​​

    prompt_template = ChatPromptTemplate.from_messages(    [        (            "system",            "You are a helpful assistant. Answer all questions to the best of your ability in {language}.",        ),        MessagesPlaceholder(variable_name="messages"),    ])

    需要注意的事,我们已经在提示中添加了一个新的输入参数 language。我们的应用程序现在有两个参数——输入消息和language。我们修改下程序

    ​​​​​​​

    from typing import Sequence
    from langchain_core.messages import BaseMessagefrom langgraph.graph.message import add_messagesfrom typing_extensions import Annotated, TypedDict
    
    class State(TypedDict):    messages: Annotated[Sequence[BaseMessage], add_messages]    language: str
    
    workflow = StateGraph(state_schema=State)
    
    def call_model(state: State):    prompt = prompt_template.invoke(state)    response = model.invoke(prompt)    return {"messages": [response]}
    
    workflow.add_edge(START, "model")workflow.add_node("model", call_model)
    memory = MemorySaver()app = workflow.compile(checkpointer=memory)

    ​​​​​​​

    config = {"configurable": {"thread_id": "abc456"}}query = "Hi! I'm Bob."language = "Spanish"
    input_messages = [HumanMessage(query)]output = app.invoke(    {"messages": input_messages, "language": language},    config,)output["messages"][-1].pretty_print()

    ​​​​​​​

    ==================================[1m Ai Message [0m==================================
    ¡Hola, Bob! ¿Cómo puedo ayudarte hoy?

    请注意,整个状态都是持久化的,因此如果不需要更改,我们可以省略像 language 这样的参数:

    ​​​​​​​

    query = "What is my name?"
    input_messages = [HumanMessage(query)]output = app.invoke(    {"messages": input_messages},    config,)output["messages"][-1].pretty_print()
    ==================================[1m Ai Message [0m==================================
    Tu nombre es Bob. ¿Hay algo más en lo que pueda ayudarte?

    通过LangSmith,可以帮助你了解内部的过程。

    未完待续,下一节,我们继续深入探索该例子。

     如何系统的去学习大模型LLM ?

    大模型时代,火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业?”“谁的饭碗又将不保了?”等问题热议不断。

    事实上,抢你饭碗的不是AI,而是会利用AI的人。

    科大讯飞、阿里、华为等巨头公司发布AI产品后,很多中小企业也陆续进场!超高年薪,挖掘AI大模型人才! 如今大厂老板们,也更倾向于会AI的人,普通程序员,还有应对的机会吗?

    与其焦虑……

    不如成为「掌握AI工具的技术人」,毕竟AI时代,谁先尝试,谁就能占得先机!

    但是LLM相关的内容很多,现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学,学习成本和门槛很高。

    基于此,我用做产品的心态来打磨这份大模型教程,深挖痛点并持续修改了近70次后,终于把整个AI大模型的学习门槛,降到了最低!

    在这个版本当中:

    第一您不需要具备任何算法和数学的基础
    第二不要求准备高配置的电脑
    第三不必懂Python等任何编程语言

    您只需要听我讲,跟着我做即可,为了让学习的道路变得更简单,这份大模型教程已经给大家整理并打包,现在将这份 LLM大模型资料 分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓

    一、LLM大模型经典书籍

    AI大模型已经成为了当今科技领域的一大热点,那以下这些大模型书籍就是非常不错的学习资源。

    在这里插入图片描述

    二、640套LLM大模型报告合集

    这套包含640份报告的合集,涵盖了大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。(几乎涵盖所有行业)
    在这里插入图片描述

    三、LLM大模型系列视频教程

    在这里插入图片描述

    四、LLM大模型开源教程(LLaLA/Meta/chatglm/chatgpt)

    在这里插入图片描述

    五、AI产品经理大模型教程

    在这里插入图片描述

    LLM大模型学习路线 

    阶段1:AI大模型时代的基础理解

    • 目标:了解AI大模型的基本概念、发展历程和核心原理。

    • 内容

      • L1.1 人工智能简述与大模型起源
      • L1.2 大模型与通用人工智能
      • L1.3 GPT模型的发展历程
      • L1.4 模型工程
      • L1.4.1 知识大模型
      • L1.4.2 生产大模型
      • L1.4.3 模型工程方法论
      • L1.4.4 模型工程实践
      • L1.5 GPT应用案例

    阶段2:AI大模型API应用开发工程

    • 目标:掌握AI大模型API的使用和开发,以及相关的编程技能。

    • 内容

      • L2.1 API接口
      • L2.1.1 OpenAI API接口
      • L2.1.2 Python接口接入
      • L2.1.3 BOT工具类框架
      • L2.1.4 代码示例
      • L2.2 Prompt框架
      • L2.3 流水线工程
      • L2.4 总结与展望

    阶段3:AI大模型应用架构实践

    • 目标:深入理解AI大模型的应用架构,并能够进行私有化部署。

    • 内容

      • L3.1 Agent模型框架
      • L3.2 MetaGPT
      • L3.3 ChatGLM
      • L3.4 LLAMA
      • L3.5 其他大模型介绍

    阶段4:AI大模型私有化部署

    • 目标:掌握多种AI大模型的私有化部署,包括多模态和特定领域模型。

    • 内容

      • L4.1 模型私有化部署概述
      • L4.2 模型私有化部署的关键技术
      • L4.3 模型私有化部署的实施步骤
      • L4.4 模型私有化部署的应用场景

    这份 LLM大模型资料 包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓

     

    Logo

    火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

    更多推荐