• LangChain入门学习笔记(五)—— Model I/O之Chat Models


    在LangChain的组件当中,Chat Models可以说跟LLMs占据着相同的“生态位”,它也是根据用户输入的提示内容,调用底层的大模型产生内容。与LLMs不同的是,LangChain的Chat Models主要有如下一些不同:

    • 输入输出格式不同:LLMs通常接受单一的文本提示语,而Chat Models则接受一系列的Messages作为提示输入上下文,里面除了内容外还区分输入的不同角色。
    • 应用的场景不同:LLMs通常用于文本处理,比如问答、总结和创作;而Chat Models更强调的是上下文的理解和跟踪对话,引入了更多的对话逻辑。
    • 使用成本不同:针对Chat Models的使用,大模型提供商在内部结构上做了优化,这使得它的推理成本会比通用的LLMs更高。

    Message类型

    如前所述,Messages是Chat Models的主要输入类型,通过Messages我们可以给后端的大模型提供必要的角色和信息内容。在介绍Chat Models的使用前,先学习一下Messages的类型,便于后面的使用理解。

    所有的Message都有两个属性:

    • Role:role表示这条message是谁“说”的,不同类型的Message具有不同的role。
    • Content:表示这条message的内容,即消息的文本。

    根据这2个属性可以构造不同类型的Message。

    Messages的主要类型见下表:

    Message类型作用说明
    HumanMessage表示“人”的消息,可以认为是使用者输入的信息,是对话的起点。
    AIMessage表示模型的消息,是语言模型生成的响应消息,可以认为是对HumanMessage的回复。
    SystemMessage

    表示给予语言模型的系统指令,告知大模型该以怎样的行为响应。一般用来设置语言模型的身份、任务和语气等。

    并不是每个LangChain支持的大模型都支持这个类型。

    FunctionMessage表示一个可执行函数或工具的调用结果。除了role和content,本类型消息还有一个name参数,用来传达用于生成此结果的函数名称。
    ToolMessage与FunctionMessage类似,表示工具调用的结果。此类型的message携带tool_call_id参数表示生成该结果的工具id。

    Chat Models基本使用

    作为Runnable的子类,Chat Models的使用和LLMs大同小异,主要的不同就是构造Messages对象,如下面代码所示:

    1. from langchain_community.chat_models import ChatOllama
    2. from langchain_core.messages import SystemMessage, HumanMessage
    3. # 构造Messages对象,提供系统信息(SystemMessage)和用户信息(HumanMessage)。
    4. messages = [
    5. SystemMessage(content="You're a helpful assistant"),
    6. HumanMessage(content="What is the purpose of model regularization?"),
    7. ]
    8. # 使用的是ChatOllama类,而非Ollama
    9. chat = ChatOllama(model="llama3")
    10. # 返回的是AIMessage对象
    11. response = chat.invoke(messages)
    12. # 打印AIMessage对象的content内容
    13. print(response.content)

    当传入messages给到Chat Model的invoke方法后,返回结果在AIMessage的response对象里。打印content的内容结果如下:

    与前面介绍的LLMs直接返回str类型的结果不同,Chat Models输出的结果是Chat Message,因此,如果只想要大模型回答的结果内容,需要访问content字段。

    流式传输

    所有实现了Runnable接口的Chat Models都默认支持ainvoke/batch/abatch/stream/astream方法。流式传输支持返回结果的iterator,这样可以用迭代的方式输出结果:

    比如上面的代码调用invoke部分改成:

    1. for trunk in chat.stream(messages):
    2. print(trunk.content, end="", flush=True)

    最终结果一致,只是输出是迭代的流式输出。

    自定义Chat Model

    与自定义LLM类似,自定义Chat Model需要继承BaseChatModel,并实现方法:

    • _generate:用于根据输入的提示语生成结果。
    • _llm_type:返回属性字符串,用来唯一标志模型类型名称。

    还有一些可选接口方法:

    • _agenerate:_generate的异步版本。
    • _stream / _astream:流式传输的同步/异步版本。
    • _identifying_params:用于追踪目的的模型参数属性值。

    下面定义一个自定义的Chat Model叫做MyCustomChatModel,为了简单示例,这里对任何输入都返回固定的字符串:

    1. from typing import Any, List, Optional
    2. from langchain_core.callbacks import (
    3. CallbackManagerForLLMRun,
    4. )
    5. from langchain_core.language_models import BaseChatModel
    6. from langchain_core.messages import BaseMessage, AIMessage
    7. from langchain_core.outputs import ChatGeneration, ChatResult
    8. class MyCustomChatModel(BaseChatModel):
    9. def _generate(
    10. self,
    11. messages: List[BaseMessage],
    12. stop: Optional[List[str]] = None,
    13. run_manager: Optional[CallbackManagerForLLMRun] = None,
    14. **kwargs: Any,
    15. ) -> ChatResult:
    16. # 这里实现自己的生成逻辑,此处仅作举例所以输出固定字符串
    17. tokens = "I don't want to answer any question from you"
    18. message = AIMessage(
    19. content=tokens,
    20. additional_kwargs={}, # Used to add additional payload (e.g., function calling request)
    21. response_metadata={ # Use for response metadata
    22. "time_in_seconds": 3,
    23. },
    24. )
    25. generation = ChatGeneration(message=message)
    26. return ChatResult(generations=[generation])
    27. @property
    28. def _llm_type(self) -> str:
    29. return "my_custom_chat_model"
    30. # 使用自定义的Chat Model
    31. chat = MyCustomChatModel()
    32. response = chat.invoke("tell me a joke about bear")
    33. print(response.content)

    结果打印如下: 

    响应元数据(Response metadata)

    在模型返回AIMessage中,还有一些额外的信息,模型提供商会将它们记录在AIMessage的response_metadata属性中。

    使用很简单,如下直接调用ChatModel返回的AIMessage对象的response_metadata属性:

    print(response.response_metadata)

    在ChatOllama中返回如下(json格式化处理过):

    {
      "model": "llama3",
      "created_at": "2024-06-19T13:02:40.297019706Z",
      "message": {
        "role": "assistant",
        "content": ""
      },
      "done_reason": "stop",
      "done": true,
      "total_duration": 13890899022,
      "load_duration": 7291480563,
      "prompt_eval_count": 27,
      "prompt_eval_duration": 124457000,
      "eval_count": 355,
      "eval_duration": 6327830000
    }

    不同提供商具体提供了哪些内容,需要查阅对应厂家的文档。通过response_metadata,有些厂家提供了Token使用的统计数据,有些提供了日志相关信息。

    缓存(Caching)

    具体使用和LLMs类似,具体使用方法可以参看《LangChain入门学习笔记(四)—— Model I/O之LLMs》的缓存部分。

  • 相关阅读:
    DataBinding 高级用法
    flink技术总结待续
    世界杯——手动为梅西标名
    【开源】物联网智慧消防云平台系统,前后端分离,微服务框架带文档,源码分享
    信息学奥赛初赛天天练-89-CSP-S2023基础题1-linux常用命令、完全平方数、稀疏图、队列、散列表、二叉树、哈夫曼树
    Java之运算符(4)
    IDEA同步代码到Gitee
    巨控GRM530,GRM230远程模块教你如何实现手机(电脑)无线远程监控西门子S7-smart200 plc
    这些包括我在内都有的Python编程陋习,趁早改掉
    一文详解Docker容器(Container)
  • 原文地址:https://blog.csdn.net/stingfire/article/details/139800490