百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

【AI大模型框架—Langchain】构建简单的LLM的聊天机器人

ccwgpt 2024-11-20 13:15 46 浏览 0 评论

在本文中,我们将带你一步步设计并实现一个基于大语言模型(LLM)的聊天机器人。这个机器人不仅可以与用户进行对话,还能够记住之前的互动记录。

先决条件

在开始之前,建议你熟悉以下概念:

  • 聊天模型(Chat Models)
  • 提示模板(Prompt Templates)
  • 聊天记录(Chat History)

概述

我们将通过一个示例来展示如何设计和实现一个LLM驱动的聊天机器人。需要注意的是,这个机器人只使用语言模型进行对话。你可能还需要了解以下相关概念:

  • Conversational RAG:在外部数据源上启用聊天机器人体验
  • Agents:构建可以执行操作的聊天机器人

本教程将介绍一些基础知识,这些知识对上述更高级的主题也会有所帮助。

环境搭建

安装 LangChain

要安装LangChain,请运行以下命令:

pip install langchain

有关更多详细信息,请参阅我们的安装指南。

快速入门

使用语言模型

LangChain支持许多不同的语言模型,你可以根据需要选择使用。这里以OpenAI的gpt-4o-mini为例:

pip install -qU langchain-openai

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
from langchain_openai import ChatOpenAI

model = ChatOpenAI(model="gpt-4o-mini").bind(logprobs=True)

直接使用模型与之交互:

from langchain_core.messages import HumanMessage

response = model.invoke([HumanMessage(content="Hi! I'm Bob")])
print(response)

响应的结果:

content='Hi Bob! How can I assist you today?' response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 11, 'total_tokens': 21}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_ba606877f9', 'finish_reason': 'stop', 'logprobs': {'content': [{'token': 'Hi', 'bytes': [72, 105], 'logprob': -0.023249088, 'top_logprobs': []}, {'token': ' Bob', 'bytes': [32, 66, 111, 98], 'logprob': -0.01416727, 'top_logprobs': []}, {'token': '!', 'bytes': [33], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' How', 'bytes': [32, 72, 111, 119], 'logprob': -0.00035596156, 'top_logprobs': []}, {'token': ' can', 'bytes': [32, 99, 97, 110], 'logprob': -0.00011450992, 'top_logprobs': []}, {'token': ' I', 'bytes': [32, 73], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' assist', 'bytes': [32, 97, 115, 115, 105, 115, 116], 'logprob': -0.03804183, 'top_logprobs': []}, {'token': ' you', 'bytes': [32, 121, 111, 117], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' today', 'bytes': [32, 116, 111, 100, 97, 121], 'logprob': 0.0, 'top_logprobs': []}, {'token': '?', 'bytes': [63], 'logprob': 0.0, 'top_logprobs': []}]}} id='run-c623dce5-1b6a-45ee-ad27-84ddbee0ebad-0' usage_metadata={'input_tokens': 11, 'output_tokens': 10, 'total_tokens': 21}

如果问后续问题,模型默认没有状态概念:

response = model.invoke([HumanMessage(content="What's my name?")])
print(response)

响应结果:

content="I'm sorry, but I don't have access to personal information about users unless it has been shared in the conversation. If you'd like, you can tell me your name!" response_metadata={'token_usage': {'completion_tokens': 33, 'prompt_tokens': 11, 'total_tokens': 44}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_ba606877f9', 'finish_reason': 'stop', 'logprobs': {'content': [{'token': "I'm", 'bytes': [73, 39, 109], 'logprob': -0.47415686, 'top_logprobs': []}, {'token': ' sorry', 'bytes': [32, 115, 111, 114, 114, 121], 'logprob': -0.0006115251, 'top_logprobs': []}, {'token': ',', 'bytes': [44], 'logprob': -4.3202e-07, 'top_logprobs': []}, {'token': ' but', 'bytes': [32, 98, 117, 116], 'logprob': -0.00012356207, 'top_logprobs': []}, {'token': ' I', 'bytes': [32, 73], 'logprob': -7.703444e-06, 'top_logprobs': []}, {'token': " don't", 'bytes': [32, 100, 111, 110, 39, 116], 'logprob': -0.019258404, 'top_logprobs': []}, {'token': ' have', 'bytes': [32, 104, 97, 118, 101], 'logprob': -0.47407743, 'top_logprobs': []}, {'token': ' access', 'bytes': [32, 97, 99, 99, 101, 115, 115], 'logprob': -0.017442156, 'top_logprobs': []}, {'token': ' to', 'bytes': [32, 116, 111], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' personal', 'bytes': [32, 112, 101, 114, 115, 111, 110, 97, 108], 'logprob': -0.047323395, 'top_logprobs': []}, {'token': ' information', 'bytes': [32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110], 'logprob': -0.20144111, 'top_logprobs': []}, {'token': ' about', 'bytes': [32, 97, 98, 111, 117, 116], 'logprob': -0.19552712, 'top_logprobs': []}, {'token': ' users', 'bytes': [32, 117, 115, 101, 114, 115], 'logprob': -0.9391852, 'top_logprobs': []}, {'token': ' unless', 'bytes': [32, 117, 110, 108, 101, 115, 115], 'logprob': -0.1827523, 'top_logprobs': []}, {'token': ' it', 'bytes': [32, 105, 116], 'logprob': -1.0404304, 'top_logprobs': []}, {'token': ' has', 'bytes': [32, 104, 97, 115], 'logprob': -0.0007059985, 'top_logprobs': []}, {'token': ' been', 'bytes': [32, 98, 101, 101, 110], 'logprob': -5.5577775e-06, 'top_logprobs': []}, {'token': ' shared', 'bytes': [32, 115, 104, 97, 114, 101, 100], 'logprob': -0.007847821, 'top_logprobs': []}, {'token': ' in', 'bytes': [32, 105, 110], 'logprob': -1.5318099, 'top_logprobs': []}, {'token': ' the', 'bytes': [32, 116, 104, 101], 'logprob': -0.15039976, 'top_logprobs': []}, {'token': ' conversation', 'bytes': [32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105, 111, 110], 'logprob': -1.3203849, 'top_logprobs': []}, {'token': '.', 'bytes': [46], 'logprob': -2.2961513e-05, 'top_logprobs': []}, {'token': ' If', 'bytes': [32, 73, 102], 'logprob': -0.82898766, 'top_logprobs': []}, {'token': " you'd", 'bytes': [32, 121, 111, 117, 39, 100], 'logprob': -0.252001, 'top_logprobs': []}, {'token': ' like', 'bytes': [32, 108, 105, 107, 101], 'logprob': -1.504853e-06, 'top_logprobs': []}, {'token': ',', 'bytes': [44], 'logprob': -1.0768048, 'top_logprobs': []}, {'token': ' you', 'bytes': [32, 121, 111, 117], 'logprob': -0.014669579, 'top_logprobs': []}, {'token': ' can', 'bytes': [32, 99, 97, 110], 'logprob': -0.00018411019, 'top_logprobs': []}, {'token': ' tell', 'bytes': [32, 116, 101, 108, 108], 'logprob': -0.00955621, 'top_logprobs': []}, {'token': ' me', 'bytes': [32, 109, 101], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' your', 'bytes': [32, 121, 111, 117, 114], 'logprob': -5.5122365e-07, 'top_logprobs': []}, {'token': ' name', 'bytes': [32, 110, 97, 109, 101], 'logprob': 0.0, 'top_logprobs': []}, {'token': '!', 'bytes': [33], 'logprob': -0.00811096, 'top_logprobs': []}]}} id='run-ec33c381-a4db-4bb9-9ccb-d1720a8214a6-0' usage_metadata={'input_tokens': 11, 'output_tokens': 33, 'total_tokens': 44}

模型无法回答,体验不佳。为解决这个问题,我们需要传递整个对话历史:

response = model.invoke(
    [
        HumanMessage(content="Hi! I'm Bob"),
        AIMessage(content="Hello Bob! How can I assist you today?"),
        HumanMessage(content="What's my name?"),
    ]
)

print(response)

此时响应的结果可以看出,LLM已经能够识别到我是谁了?

content='Your name is Bob! How can I help you today?' response_metadata={'token_usage': {'completion_tokens': 12, 'prompt_tokens': 33, 'total_tokens': 45}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_ba606877f9', 'finish_reason': 'stop', 'logprobs': {'content': [{'token': 'Your', 'bytes': [89, 111, 117, 114], 'logprob': -0.003211819, 'top_logprobs': []}, {'token': ' name', 'bytes': [32, 110, 97, 109, 101], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' is', 'bytes': [32, 105, 115], 'logprob': -4.3202e-07, 'top_logprobs': []}, {'token': ' Bob', 'bytes': [32, 66, 111, 98], 'logprob': 0.0, 'top_logprobs': []}, {'token': '!', 'bytes': [33], 'logprob': -0.25192946, 'top_logprobs': []}, {'token': ' How', 'bytes': [32, 72, 111, 119], 'logprob': -0.015417111, 'top_logprobs': []}, {'token': ' can', 'bytes': [32, 99, 97, 110], 'logprob': -0.00081379723, 'top_logprobs': []}, {'token': ' I', 'bytes': [32, 73], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' help', 'bytes': [32, 104, 101, 108, 112], 'logprob': -0.023248974, 'top_logprobs': []}, {'token': ' you', 'bytes': [32, 121, 111, 117], 'logprob': 0.0, 'top_logprobs': []}, {'token': ' today', 'bytes': [32, 116, 111, 100, 97, 121], 'logprob': -0.01769961, 'top_logprobs': []}, {'token': '?', 'bytes': [63], 'logprob': -0.6931476, 'top_logprobs': []}]}} id='run-122d5e43-1c05-4bb2-a3cd-1507f7167f4d-0' usage_metadata={'input_tokens': 33, 'output_tokens': 12, 'total_tokens': 45}

管理对话历史

我们可以使用Message History类来记录对话,使模型具有状态。安装必要依赖:

pip install langchain_community

然后设置会话历史记录存储:

def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

with_message_history = RunnableWithMessageHistory(model, get_session_history)

config = {"configurable": {"session_id": "abc2"}}
response = with_message_history.invoke([HumanMessage(content="Hi! I'm Bob")], config=config)
print(response.content)

response = with_message_history.invoke([HumanMessage(content="What's my name?")], config=config)
print(response.content)

运行结果:

Hi Bob! How can I assist you today?
Your name is Bob! How can I help you today?

使用提示模板

提示模板帮助将用户输入转换为LLM可以处理的格式。首先,我们添加系统消息来定制指令:

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

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

chain = prompt | model

response = chain.invoke({"messages": [HumanMessage(content="hi! I'm bob")]})
print(response.content)

响应结果为:

Hi Bob! How can I assist you today?

将其包装在Message History对象中:

# 包装在Message History对象
with_message_history = RunnableWithMessageHistory(chain, get_session_history)
config = {"configurable": {"session_id": "abc5"}}

response = with_message_history.invoke([HumanMessage(content="Hi! I'm Jim")], config=config)
print(response.content)

response = with_message_history.invoke([HumanMessage(content="What's my name?")], config=config)
print(response.content)
Hi Jim! How can I assist you today?
Your name is Jim! How can I help you today, Jim?

管理对话历史的大小

为了防止对话历史过长,我们可以使用LangChain的消息修剪功能:

from langchain_core.messages import trim_messages, AIMessage, BaseMessage, HumanMessage, SystemMessage

messages = [
    HumanMessage(content="hi! I'm bob"),
    SystemMessage(content="you're a good assistant"),
    AIMessage(content="hi!"),
    HumanMessage(content="I like vanilla ice cream"),
    AIMessage(content="nice"),
    HumanMessage(content="whats 2 + 2"),
    AIMessage(content="4"),
    HumanMessage(content="thanks"),
    AIMessage(content="no problem!"),
    HumanMessage(content="having fun?"),
    AIMessage(content="yes!"),
]
trimmer = trim_messages(
   # messages,
    max_tokens=65,
    strategy="last",
    token_counter=model,
    include_system=True,
    allow_partial=False,
    start_on="human",
)
print(trimmer)
response = trimmer.invoke(messages)
print(response)


在我们的链中使用修剪功能:

# 在langchain的链中使用
from operator import itemgetter
from langchain_core.runnables import RunnablePassthrough

chain = (
    RunnablePassthrough.assign(messages=itemgetter("messages") | trimmer)
    | prompt
    | model
)
response = chain.invoke(
    {
        "messages": messages + [HumanMessage(content="what's my name?")],
        "language": "English",
    }
)
print(response.content)

将其包装在Message History中:

# 包装在Message History
with_message_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="messages",
)

config = {"configurable": {"session_id": "abc20"}}

response = with_message_history.invoke(
    {
        "messages": messages + [HumanMessage(content="whats my name?")],
        "language": "English",
    },
    config=config,
)
print(response.content)

流式响应

为了改善用户体验,我们可以使用流式响应:

config = {"configurable": {"session_id": "abc15"}}
for r in with_message_history.stream(
    {
        "messages": [HumanMessage(content="hi! I'm todd. tell me a joke")],
        "language": "English",
    },
    config=config,
):
 print(r.content, end="|")

通过本文的讲解,你应该能够构建一个简单但功能强大的LLM聊天机器人。随着对LangChain的深入了解,你可以不断扩展和优化你的机器人,实现更多复杂功能。

相关推荐

Spring WebFlux vs. Spring MVC(springboot是什么)

背景随着异步I/O和Netty等框架的流行,响应式编程逐渐走入大众的视野。但是,响应式编程本身并不是太新的概念,这个术语最早出现在1985年DavidHarel和AmirPnue...

深度解析微服务高并发:适配SpringMVC框架适配模块及实现原理

适配主流框架如果不借助Sentinel提供的适配主流框架的模块,则在使用Sentinel时需要借助try-catchfinally将要保护的资源(方法或代码块)包起来,在目标方法或代码块执行之前,调...

Spring MVC 底层原理深度解析:从请求到响应的全链路拆解

一、Servlet容器与DispatcherServlet的启动博弈1.Tomcat初始化阶段java//Tomcat初始化流程StandardContext#startInterna...

改造总结之传统SpringMVC架构转换为SpringBoot再到集群

改造出发点,是基于现在服务都在向上云的目标前进,传统SpringMVC难以满足项目持续构建、服务节点任意扩展的需求,所以开始了历史项目的改造。项目改造考虑的主要是兼容以前的业务代码,以及session...

SpringBoot3 整合 Spring MVC 全解析:开启高效 Web 开发之旅

在当今的JavaWeb开发领域,Spring框架家族无疑占据着重要的地位。其中,SpringBoot3和SpringMVC更是开发者们构建强大、高效Web应用的得力工具。今天,...

一文读懂SpringMVC(一文读懂!残疾人低保边缘家庭能领的超实用福利政策)

1.SpringMVC定义1.1.MVC定义Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据View(视图):是应用程序中处理数据显示的部分。通常...

69 个Spring mvc 全部注解:真实业务使用案例说明(必须收藏)

SpringMVC框架的注解为Web开发提供了一种简洁而强大的声明式方法。从控制器的定义、请求映射、参数绑定到异常处理和响应构建,这些注解涵盖了Web应用程序开发的各个方面。它们不仅简化了编码工作,...

Spring MVC工作原理:像拼积木一样构建Web应用

SpringMVC工作原理:像拼积木一样构建Web应用在Java的Web开发领域,SpringMVC无疑是一个让人又爱又恨的存在。它像一位神通广大的积木搭建大师,将一个个分散的功能模块巧妙地拼接在...

5千字的SpringMVC总结,我觉得你会需要

思维导图文章已收录到我的Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary概述SpringMVC再熟悉不过的框架了,因为现在最火的...

SpringMVC工作原理与优化指南(springmvc工作原理和工作流程)

SpringMVC工作原理与优化指南在现代Java开发中,SpringMVC无疑是构建Web应用程序的首选框架之一。它以其优雅的设计和强大的功能吸引了无数开发者。那么,SpringMVC究竟是如何工作...

Spring MVC框架源码深度剖析:从入门到精通

SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...

3000字搞明白SpringMVC工作流程、DispatcherServlet类、拦截器!

SpringMVC基础虽然SpringBoot近几年发展迅猛,但是SpringMVC在Web开发领域仍然占有重要的地位。本章主要讲解SpringMVC的核心:DispatcherServlet类...

多年经验大佬用2000字透彻解析SpringMVC的常用注解及相关示例

SpringMVC注解SpringMVC框架提供了大量的注解,如请求注解、参数注解、响应注解及跨域注解等。这些注解提供了解决HTTP请求的方案。本节主要讲解SpringMVC的常用注解及相关示例...

知乎热议:如何成为前端架构师,赚百万年薪?

作者|慕课网精英讲师双越最近有一条知乎热议:从一个前端工程师,如何根据目标,制定计划,才能快速进阶成为前端架构师?不久之前我参与了一次直播,讲到了自己对于Web前端架构师的理解。架构师这个角色...

学习笔记-前端开发架构设计(前端架构设计方案)

前端开发的技术选项主要包含以下几点,下面对一些名词概念的解释做了笔记:1、分层架构:把功能相似,抽象级别相近的实现进行分层隔离优势:松散耦合(易维护,易复用,易扩展)常见分层方式:MVC,MVVM2、...

取消回复欢迎 发表评论: