LLM应用的标准开发框架LangChain(四)- 消息与提示词模板

1 消息

  • 大模型没有记忆,它的输出只和输入模型的内容有关(上下文)。很多大模型API服务也没有在服务端维护会话历史,是“ 无状态”的。因此,如果应用需要“记住”对话历史,需要在程序中维护消息列表。
    • 在这里插入图片描述
  • 在LangChain 中,Message(消息)是模型交互的最基本单元。它既代表模型接收到的输入(Input),也代表模型生成的输出(Output)。
    • 每一轮与大模型的对话,都由一条或多条Message 构成。每个Message 不仅包含,还携带描述上下文状态的元信息(metadata),用于保持对话的一致性和可追踪性。比如,模型文字内容在多轮交互中理解“谁在说话”、“说了什么”、“这条信息属于哪一轮对话”。
  • LangChain 在1.0 中提供了跨模型统一的Message 标准。无论你使用的是OpenAI、Anthropic、Gemini 还是本地模型,这一标准都能保持一致的行为。好处:
    • 兼容性强:不同模型的消息格式自动对齐。
    • 可扩展性高:方便添加多模态内容或自定义字段。
    • 可追踪性好:为LangSmith 等调试工具提供一致的上下文数据结构。

1.1 消息的内部结构

  • LangChain的消息(Message)对象包含三种字段
    • Role:消息所属的角色或类型,如system、user、assistant。
    • Content:消息内容
    • Metadata:(可选)元数据,存储额外信息。如:消息ID、响应时间、token消耗量、消息标签等。

1.2 消息的类型

  • LangChain定义了很多消息类型,通过role 区分。常用的有四种。
  • 系统消息
    • 也称为系统提示词,用于在对话开始时为模型设定角色、行为准则和上下文背景。它像是给AI助手的一份工作说明书,决定了其回答问题的风格、领域和专业范围。
    •   {"role": "system", "content": "你是个精通编程的软件架构师"}
      
  • 用户消息
    • 也称为用户提示词,在多轮对话中,它表示用户的一次输入。可以包含简单的文本问题,也可以是复杂的多模态内容(如图片、音频、文档等)。
    •   {"role": "user", "content": "你好啊~"}
      
  • 助手(AI)消息
    • 代表模型的回复,包括生成的文本、工具调用、元数据等。
    •   {"role": "assistant", "content": "我也很高兴认识你"}
        
        {
        	"role ": "assistant ",
        	"content ": "",
        	"tool_calls ": [{
        		"name ": "get_weather ",
        		"args ": {"location ": "北京"},
        		"id ": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s "
        		}]
        	}
      
  • 工具调用消息
    • 工具调用结果匹配的消息类型。将此消息返回给模型,让模型基于这个结果继续生成回复。
    •     {"role ": "tool ", "content ": "今天天气很好", "tool_call_id ":"call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s "}
      
  • 使用不同消息的作用
    • 明确角色:清晰区分系统提示、用户输入和AI 回复
    • 控制行为 :通过SystemMessage 精确控制AI 的行为
    • 对话历史 :构建完整的多轮对话上下文
    • 调试友好:更容易追踪和调试对话流程

1.3 消息格式

  • LangChain支持两种消息格式。
  • 格式1:JSON格式
    •   # 系统消息
        {"role": "system", "content": "你是个善解人意的助手"}
        
        # 用户消息
        {"role ": "user ", "content ": "你好啊~"}
        
        # 助手消息
        {"role": "assistant", "content": "我也很高兴认识你"}
        
        # 工具调用消息
        {"role": "tool", "content": "<工具输出>", "tool_call_id":"call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"}
      
  • 格式2:对象格式
    •   # 系统消息
        SystemMessage(content="你是个善解人意的助手")
        
        # 用户消息
        HumanMessage(content="你好啊~")
        
        # 助手消息
        AIMessage("我也很高兴认识你")
        
        # 工具调用消息
        ToolMessage(
        	content="<工具输出>",
        	tool_call_id="call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s "  # 一定要和AI消息中的调用ID匹配
        )
      
  • 总结
    • 角色字典格式对象格式用途示例
      System{"role": "system", ...}SystemMessage(...)设定 AI 的行为、角色、规则“你是一个专业的数学老师”
      User{"role": "user", ...}HumanMessage(...)用户输入“什么是微积分?”
      Assistant{"role": "assistant", ...}AIMessage(...)AI 的回复“微积分是研究变化率的数学分支…”
      Tool{"role": "tool", ...}ToolMessage(...)工具执行的结果“今天北京天气晴朗,万里无云”
  • 示例1:json格式消息
    •   import os
      
        from dotenv import load_dotenv
        from langchain_classic.chains.question_answering.map_reduce_prompt import messages
        from langchain_deepseek import ChatDeepSeek
        
        # 读取.env配置
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
        DEEPSEEK_BASE_URL = os.getenv('DEEPSEEK_BASE_URL')
        
        # 模型初始化
        llm_deepseek = ChatDeepSeek(
            api_key=DEEPSEEK_API_KEY,
            api_base=DEEPSEEK_BASE_URL,
            model="deepseek-v4-flash",
        )
        
        # 模型调用
        
        # json格式消息
        messages = [
            {"role":"system", "content":"你是一个科学家"},
            {"role":"user", "content":"世界上有永动机吗"},
            {"role":"assistant", "content":"没有"},
            {"role":"user", "content":"我刚才问了什么问题"},
        ]
        
        response = llm_deepseek.invoke(messages)
        print(response)
      
  • 示例2:消息对象列表
    •   import os
      
        from dotenv import load_dotenv
        from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
        from langchain_deepseek import ChatDeepSeek
        
        # 读取.env配置
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
        DEEPSEEK_BASE_URL = os.getenv('DEEPSEEK_BASE_URL')
        
        # 模型初始化
        llm_deepseek = ChatDeepSeek(
            api_key=DEEPSEEK_API_KEY,
            api_base=DEEPSEEK_BASE_URL,
            model="deepseek-v4-flash",
        )
        
        # 模型调用
        
        # json格式消息
        messages = [
            SystemMessage(content="你是一个科学家"),
            HumanMessage("世界上有永动机吗"),
            AIMessage("没有"),
            HumanMessage("我刚才问了什么问题"),
        ]
        
        response = llm_deepseek.invoke(messages)
        print(response)
      

1.4 消息对象字段说明

1.4.1 SystemMessage参数列表

  • content:消息内容,字段名可以省略
    • SystemMessage("你是个善解人意的助手")
  • 相当于
    • SystemMessage(content = "你是个善解人意的助手")

1.4.2 HumanMessage参数列表

  • content:消息内容,字段名可以省略
    • HumanMessage("你好啊~")
  • 相当于
    • HumanMessage(content = "你好啊~")
  • 还可以添加metadata,即元数据字段,比如
    •   HumanMessage(
        	content="Hello !",
        	name="alice ",  # 可选,用户名
        	id="msg_123 ",  # 可选,message的ID
        )
      
  • nameid都属于元数据字段,当消息类型相同,对消息进行区分。
  • 但不是所有模型都支持这一功能,是否支持取决于模型供应商,需要查看官方手册。
  • 举例
    •   from langchain_core.messages import SystemMessage, HumanMessage  
        from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        import os
        
        # 从.env文件中加载环境变量
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL
        )
        
        messages = [
            SystemMessage("你是一个信息抽取器。你会收到多条来自不同发言者的 user 消息。每条消息可能带有 name 字段。你的任务是:严格根据每条消息的 name 提取发言者及其观点,并输出 JSON。禁止使用“第一个人/第二个人”这种相对对称。若某条消息没有 name,则输出 unknown。输出格式:{\"speakers\":[{\"name\":\"...\",\"claim\":\"...\"}]}"),
            HumanMessage(
                content="我认为 1+1=2",
                name="Bob"
            ),
            HumanMessage(
                content="我认为 1+1>2",
                name="Tom"
            ),
            HumanMessage(
                content="请列出谁说了什么,不要判断对错。",
                name="audience"
            )
        ]
        
        response = model.invoke(messages)
        print(response.content)
      

1.4.3 AIMessage参数列表

  • content:模型输出的原始内容,字段名可以省略
    • AIMessage("你好~")
  • 相当于
    • AIMessage(content="你好~")
  • response_metadata:AIMessage特有属性,LLM的响应中附加元数据,根据不同模型会有不同,如可能会包含本次token使用量等信息。
  • tool_calls:AIMessage特有属性,表示工具调用信息。当LLM决定调用工具时,在AIMessage 中就会包含这个属性,没有工具调用则为空。
    •   AIMessage(
        	content="", 
        	tool_calls=[{
        		'name ': 'get_weather ',
        		'args ': {'city ': '北京'},
        		'id ': 'call_xxx '
        	}]
        )
      
  • usage_metadata:用量信息。

1.4.4 ToolMessage参数列表

  • content:文件内容
  • name:工具名称
  • tool_call_id:工具调用唯一ID,ToolMessage必须紧邻匹配的AIMessage,和前者tool_calls中的id一致。
    •   ToolMessage(
        	content="<工具输出>", 
        	name="get_weather "
        	tool_call_id="call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s "  # 一定要和AI消息中的调用ID匹配
        )
      
  • 举例1:json格式
    •   from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        import os
        
        # 从.env文件中加载环境变量
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
            # v4-flash默认开启思考模式,要求带tool_calls的assistant消息必须回传reasoning_content,
            # 而langchain_openai序列化时不支持该字段,这里直接关闭思考模式
            extra_body={"thinking": {"type": "disabled"}},
        )
        
        def get_weather(city: str) -> str:
            return "不晴哦~"
        
        # 根据模型绑定工具
        model_with_tools = model.bind_tools([get_weather])
        
        ai_message = {
            "role": "assistant",
            "content": "",
            "tool_calls": [{
                "name": "get_weather",
                "args": {"location": "北京"},
                "id": "call_00_nU02nC9QRN5G9IGao1kBJQ4s"
            }]
        }
        
        tool_message = {
            "role": "tool",
            "content": "今天北京天气晴朗,万里无云~",
            "tool_call_id": "call_00_nU02nC9QRN5G9IGao1kBJQ4s"
        }
        
        messages = [
            {"role": "user", "content": "北京天气如何"},
            ai_message,
            tool_message
        ]
        
        response = model.invoke(messages)
        print(response)
      
  • 示例2:对象格式
    •   from langchain_core.messages import AIMessage, ToolMessage, HumanMessage
        from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        import os
        
        # 从.env文件中加载环境变量
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
            # v4-flash默认开启思考模式,要求带tool_calls的assistant消息必须回传reasoning_content,
            # 而langchain_openai序列化时不支持该字段,这里直接关闭思考模式
            extra_body={"thinking": {"type": "disabled"}},
        )
        
        def get_weather(city: str) -> str:
            return "不晴哦~"
        
        # 根据模型绑定工具
        model_with_tools = model.bind_tools([get_weather])
        
        ai_message = AIMessage(
            content=[],
            tool_calls=[{
                "name": "get_weather",
                "args": {"location": "北京"},
                "id": "call_00_nU02nC9QRN5G9IGao1kBJQ4s"
            }]
        )
        
        
        tool_message = ToolMessage(
            content="今天北京天气晴朗,万里无云~",
            tool_call_id="call_00_nU02nC9QRN5G9IGao1kBJQ4s"
        )
        
        messages = [
            {"role": "user", "content": "北京天气如何"},
            HumanMessage(content="北京天气如何"),
            ai_message,
            tool_message
        ]
        
        response = model.invoke(messages)
        print(response)
      

1.5 实战

1.5.1 对话历史管理

  • 关键规则:每次调用必须传递完整的对话历史!
  •   第 1 轮:
      [system, user] → AI回复 → 保存回复
      第 2 轮:
      [system, user, assistant, user] → AI回复 → 保存回复
      第 3 轮:
      [system, user, assistant, user, assistant, user] → AI回复
    
  • 每次对话都要在原有的消息列表中添加新消息,不可重新创建新的列表。
  • 错误示例1
    •   # 第一次
        response1 = model .invoke("我叫张三")
        # 第二次(没传历史)
        response2 = model .invoke("我叫什么?")  # AI 不记得!
      
  • 错误示例2
    •   conversation = [{"role ": "user ", "content ": "问题1 "}]
        response1 = model .invoke(conversation)
        conversation = [{"role ": "user ", "content ": "问题2 "}]  # 重新创建! 
        response2 = model .invoke(conversation)  # 丢失了历史
      
  • 错误示例3
    •   conversation = []
        conversation.append({"role": "user", "content": "问题1"})
        response1 = model.invoke(conversation)
        # 忘记保存 response1.content!
        conversation.append({"role": "user", "content": "问题2"})
        response2 = model.invoke(conversation) # AI 不知道之前的回答
      
  • 正确做法
    •   conversation = []
        # 第一次
        conversation .append({"role ": "user ", "content ": "我叫张三"})
        response1 = model .invoke(conversation)
        # 关键:保存AI 回复
        conversation .append({"role ": "assistant ", "content ": response1 .content})
        # 第二次(传递完整历史)
        conversation .append({"role ": "user ", "content ": "我叫什么?"})
        response2 = model .invoke(conversation)  # AI 记得!
      

1.5.2 对话历史优化

  • 问题:对话历史会越来越长,消耗大量tokens 和成本。
  • 解决方案:只保留最近N 轮对话。具体的:
    • 总是保留system 消息(定义角色)
    • 只保留最近N 轮对话,丢弃更早的历史
  • 示例
    •   from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        import os
        
        def keep_recent_messages(messages , max_pairs=3):
            """
            保留最近的N 轮对话
            max_pairs: 保留的对话轮数(每轮= user + assistant)
            """
            # 分离system 和对话
            system_msgs = [m for m in messages if m.get("role") == "system"]
            conversation_msgs = [m for m in messages if m.get("role") != "system"]
            # 只保留最近的
            recent_msgs = conversation_msgs [-(max_pairs * 2):]
            # 返回:system + 最近对话
            return system_msgs + recent_msgs
        
        
        # 从.env文件中加载环境变量
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
        )
        
        
        # 初始化
        long_conversation = [
            {"role": "system", "content": "你是 Python 导师"}
        ]
        
        # 第 1 轮
        long_conversation.append({"role": "user", "content": "什么是列表?用一句解释"})
        r1 = model.invoke(long_conversation)
        long_conversation.append({"role": "assistant", "content": r1.content})
        
        # 第 2 轮
        long_conversation.append({"role": "user", "content": "列表和元组有什么区别?用一句解释"})
        r2 = model.invoke(long_conversation)
        long_conversation.append({"role": "assistant", "content": r2.content})
        
        # 第 3 轮
        long_conversation.append({"role": "user", "content": "什么是字典呢?用一句解释"})
        r3 = model.invoke(long_conversation)
        long_conversation.append({"role": "assistant", "content": r3.content})
        
        print(f"原始消息数:{len(long_conversation)}")
        
        # 优化:只保留最近 2 轮
        optimized = keep_recent_messages(long_conversation, max_pairs=2)
        
        print(f"优化后消息数:{len(optimized)}")
        print(f"保留的内容:system + 最近2轮对话")
        
        # 添加新的用户问题
        optimized.append({"role": "user", "content": "我第一个问题问的是什么?"})
        # 使用优化后的历史
        response = model.invoke(optimized)
        print(f"\nAI 回复:{response.content}")
      
    • 在这里插入图片描述

1.5.3 多轮对话聊天机器人

  • 基于模型初始化、流式响应以及消息列表的拼接来创建多轮聊天机器人。
  • 示例
    •   from langchain.chat_models import init_chat_model
        import os
        from dotenv import load_dotenv
        
        
        def keep_recent_messages(messages , max_pairs=3):
            """
            保留最近的N 轮对话
            max_pairs: 保留的对话轮数(每轮= user + assistant)
            """
            # 分离system 和对话
            system_msgs = [m for m in messages if m.get("role") == "system"]
            conversation_msgs = [m for m in messages if m.get("role") != "system"]
            # 只保留最近的
            recent_msgs = conversation_msgs [-(max_pairs * 2):]
            # 返回:system + 最近对话
            return system_msgs + recent_msgs
        
        load_dotenv(override=True)
        
        # 1. 基础配置
        MAX_PAIRS_HISTORY = 10
        EXIT_WORD = "quit"
        
        # 2. 初始化模型
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
            # v4-flash默认开启思考模式,多轮对话时要求回传reasoning_content,
            # 关闭思考模式以避免该协议约束,同时响应更快
            extra_body={"thinking": {"type": "disabled"}},
        )
        
        # 3. 初始化消息列表
        messages = [
            {
                "role": "system",
                "content": "你是小灰灰, 是一个人工智能助手"
            }
        ]
        
        # 4. 启动提示
        print(f"请输入问题,输入 {EXIT_WORD} 结束对话")
        
        # 5. 多轮对话主循环
        i = 1
        while True:
            print(f"\n", "=" * 6, f"-> 第 {i} 轮对话开始 <-", "=" * 10, "\n")
            user_input = input("你: ").strip()
        
            # 退出判断
            if user_input.lower() == EXIT_WORD:
                print("对话已结束,欢迎下次再来!")
                break
        
            # 添加用户消息
            messages.append({"role": "user", "content": user_input})
        
            # 流式输出模型回复
            print("小灰灰: ", end="", flush=True)
            reply_content = ""
        
            # 优化历史记忆
            memory_messages = keep_recent_messages(messages, max_pairs=MAX_PAIRS_HISTORY)
        
            # 控制流输出模型的历史长度
            for chunk in model.stream(memory_messages):
                if chunk.content:
                    print(chunk.content, end="", flush=True)
                    reply_content += chunk.content
        
            print(f"\n", "=" * 10, f"-> 第 {i} 轮对话结束 <-", "=" * 10, "\n")
            i += 1
        
            # 添加 AI 回复
            messages.append({"role": "assistant", "content": reply_content})
      
    • 在这里插入图片描述

1.5.4 消息属性

1.5.4.1 content
  • 消息的content 可以理解为数据内容,它是弱类型的,支持字符串和列表(列表元素通常为字典)。
  • 举例1:存储字符串
    • 如果只是纯文本内容,直接传递字符串即可。
      •   from LangChain.messages import HumanMessage
          
          msg1 = HumanMessage(content = "你好啊")
          
          # 当content内容只有字符串时,可以省略参数名称。
          msg2 = HumanMessage("你好啊")
        
  • 举例2:存储字典列表
    • 如果需要发送的不只是文本,如多模态内容,则需要content的字典列表形式。
    • 字典内容遵循模型供应商的API规范。
      •   import base64
          from langchain.chat_models import init_chat_model
          from langchain.messages import HumanMessage
          from dotenv import load_dotenv
          import os
          
          load_dotenv(override=True)
          
          DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
          DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
          
          model = init_chat_model(
              model="deepseek-v4-flash",
              model_provider="openai",
              api_key=DEEPSEEK_API_KEY,
              base_url=DEEPSEEK_BASE_URL,
          )
          
          
          def encode_image(img_path, img_type='jpeg'):
              """将一张本地图片转换成 Base64 编码的 Data URI 字符串,方便在文本中嵌入图片数据"""
              with open(img_path, "rb") as img_file:
                  return f"data:image/{img_type};base64,{base64.b64encode(img_file.read()).decode('utf-8')}"
          
          # 图像路径
          img_path = "image1.jpg"
          
          # 获取图像base64编码字符串
          base64_image = encode_image(img_path)
          
          response = model.invoke(
              [
                  HumanMessage(
                      content=[
                          {'type': 'text', 'text': '这张图里有什么?'},
                          {
                              'type': 'image_url',
                              "image_url": {"url": base64_image},
                          }
                      ]
                  )
              ]
          )
          
          print(response.content)
        
1.5.4.2 content_blocks
  • 在LangChain 1.x 中,content_blocks 是消息对象(BaseMessage)的一项重大升级。它的核心目标是提供一种跨模型供应商、标准化的多模态数据结构。
  • 过去,处理图片、音频、甚至是模型生成的“思维链(Reasoning)”内容时,不同供应商(OpenAI, Anthropic, Google 等)的API 格式各异,导致开发者需要写大量的适配代码。content_blocks 的出现终结了这种混乱。
  • 在LangChain 1.2 版本中,消息对象的 content 属性依然存在(为了向前兼容),但新增了content_blocks属性,可以将content 解析为标准、类型安全的表示。
    • 数据结构:它是一个
    • 统一格式:每个block 都有一个type 字段,用于区分内容类型。
    • 支持类型:包括text(文本)、 image(图片)、 audio(音频)、video(视频)、tool_call(工具调用)以及 reasoning(推理/思维链)。
  • 支持的字段类型详见https://docs.langchain.com/oss/python/langchain/messages#openai
  • 输入格式化
    • 对于复杂的对话(带图片或工具结果),建议使用content_blocks列表形式构建HumanMessage或AIMessage。
    • 借助content_blocks,我们可以用一套标准代码,无缝地在不同厂商的模型之间切换。
    •   import base64
        from langchain.chat_models import init_chat_model
        from langchain.messages import HumanMessage
        from dotenv import load_dotenv
        import os
        
        load_dotenv(override=True)
        
        DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
        DASHSCOPE_BASE_URL = os.getenv("DASHSCOPE_BASE_URL")
        
        model = init_chat_model(
            #model="deepseek-v4-flash",
            model="qwen3.7-plus",
            model_provider="openai",
            api_key=DASHSCOPE_API_KEY,
            base_url=DASHSCOPE_BASE_URL,
        )
        
        
        def encode_image(img_path):
            """将本地图片读取为纯base64编码字符串(不带data URI前缀)
        
            注意:标准content block {'type':'image','base64':...} 中的base64
            要的是纯编码,LangChain会自动拼接 data:{mime_type};base64, 前缀
            """
            with open(img_path, "rb") as img_file:
                return base64.b64encode(img_file.read()).decode('utf-8')
        
        # 图像路径
        img_path = "test01.png"
        
        # 获取图像纯base64编码
        base64_image = encode_image(img_path)
        
        response = model.invoke(
            [
                HumanMessage(
                      content_blocks=[
                            {'type': 'text', 'text': '这张图里有什么'},
                            {
                                "type": "image",
                                "base64": base64_image,
                                "mime_type": "image/png",
                            },
                      ]
                )
            ]
        )
        
        print(response.content)
      
  • 输出格式化
    • 不同的模型其输出格式可能不同,仅为提取思考内容,切换模型都可能需要更改代码,非常不方便。
    • content_blocks提供了统一的输出格式,可以将不同格式的响应统一为标准格式。
    •   from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        import os
        
        # 从.env文件中加载环境变量
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
        DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL")
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="deepseek",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL
        )
        
        
        
        response = model.invoke("1+1等于几")
        print("response.content: ", response.content)
        
        print("response.content_blocks: ", response.content_blocks)
      
    • 在这里插入图片描述

2 提示词模板

2.1 为什么推荐提示词模板

  • 在LangChain 开发中,构造提示词既可以直接使用Python 字符串拼接(如f-string、format() 或+),也可以使用LangChain 提供的PromptTemplateChatPromptTemplate
  • 举例1:字符串拼接方式
    •   # 字符串拼接
        topic = "Python "
        difficulty = "初学者"
        
        # 难以维护,容易出错
        prompt_str = f "你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
        response = model .invoke(prompt_str)
        print(f "AI 回复:{response .content}  \n")
      
    • 优点✅:
      • 简单直接,上手快
      • 适合临时demo
      • 无额外学习成本
    • 缺点❌:
      • 可读性差(变量多时混乱)
      • 不易维护(修改容易出错)
      • 无变量校验(容易漏/拼错)
      • 难以支持复杂场景(多轮对话/ RAG / Few-shot)
  • 举例2:提示词模板
    •   from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        from langchain_core.prompts import ChatPromptTemplate
        
        import os
        
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
        DEEPSEEK_BASE_URL = os.getenv('DEEPSEEK_BASE_URL')
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
        )
        
        prompt_template = ChatPromptTemplate([
            ("system", "你是一个AI开发工程师. 你的名字是{name}."),
            ("human", "{user_input}")
        ])
        # 调用format()方法,返回字符串
        prompt = prompt_template.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})
        print(prompt)
      
      
    • 优点✅:
      • 结构清晰(变量占位)
      • 易维护、可复用
      • 自动变量校验(更安全)
      • 支持复杂场景(对话/ RAG /Agent)
      • 可与LangChain 生态无缝集成
      • 便于调试与日志追踪
    • 缺点❌:
      • 有一定学习成本
      • 初期写法略复杂
      • 对极简单场景略“重”
    • 开发建议:
      • 小项目/ 临时用→ 字符串拼接
      • 正式开发/ AI应用→ 提示词模板(必选)

2.2 提示词机制演进

  • LangChain 1.0的架构变革中,核心的演进之一体现在Prompt 机制上:一个结构化的、富含元数据的消息列表已经取代单一字符串,成为与模型交互的标准数据格式。

2.2.1 旧时代

  • LLM + PromptTemplate(输入与输出均为字符串)
    • 模型接口:对应于LangChain 中的LLM类,主要面向早期的文本补全模型。
    • 工作方式:模型接受一个单一的字符串作为输人,基于此预测并生成后续的文本内容(文本补全)。
    • Prompt 工具:核心工具是 PromptTemplate。它的职责是接收一组变量,并通过模板渲染,最终输出一个完整的字符串。
    • 局限性:当我们需要用这种方式模拟多轮聊天时,开发者必须在字符串中手动拼接和伪造对话角色
  • 这种方式不仅导致Prompt 的结构混乱、难以维护,也极易让模型混淆对话的边界与上下文,影响生成质量。

2.2.2 新时代

  • ChatModel+ChatPromptTemplate(输入与输出均为消息列表)
    • 模型接口:对应LangChain 1.0 的主流接口ChatModel。
    • 工作方式:现代聊天模型API 已原生支持角色概念。它们不再接受单一字符串,而是要求输入一个结构化的消息列表。为构建复杂、可靠的多轮对话智能体系统奠定了坚实的基础。
    • Prompt 工具:ChatPromptTemplate 因此成为LangChain 1.0 中最核心的Prompt工具。它的职责是接收变量,并输出一个List「BaseMessage](消息列表),该列表可直接传递给聊天模型。
  • 对比
    • 特性PromptTemplateChatPromptTemplate
      输出格式纯文本字符串消息列表
      角色支持❌ 无☑️ system/user/assistant
      对话历史❌ 不支持☑️ 支持
      适用场景简单提示聊天、对话、多轮交互
  • 因此,用于生成消息列表的ChatPromptTemplate,也自然取代了生成字符串的PromptTemplate,成为构建现代LangChain 应用的首选工具。
  • 示例
    •   from langchain.chat_models import init_chat_model
        from dotenv import load_dotenv
        
        from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
        
        import os
        
        load_dotenv(override=True)
        
        DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
        DEEPSEEK_BASE_URL = os.getenv('DEEPSEEK_BASE_URL')
        
        model = init_chat_model(
            model="deepseek-v4-flash",
            model_provider="openai",
            api_key=DEEPSEEK_API_KEY,
            base_url=DEEPSEEK_BASE_URL,
        )
        
        # 方式一
        prompt_template1 = PromptTemplate.from_template("请给我一个关于{topic}的{type}解释。")
        prompt1 = prompt_template1.format(type="简单", topic="人工智能")
        response1 = model.invoke(prompt1)
        print(response1.content)
        
        # 方式二
        prompt_template2 = ChatPromptTemplate([
            ("system", "你是一个AI开发工程师. 你的名字是{name}."),
            ("human", "{user_input}")
        ])
        prompt2 = prompt_template2.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})
        print(prompt2)
      

2.3 ChatPromptTemplate的使用

  • 在LangChain 1.0中,ChatPromptTemplate 是用于生成消息列表的核心组件。
  • ChatPromptTemplate是创建聊天消息列表的提示模板。它比普通PromptTemplate 更适合处理多角色、多轮次的对话场景。支持System/Human/AI 等不同角色的消息模板。
  • 消息类型
    • 角色字符串含义用途
      "system"系统消息设定 AI 的行为、角色、规则
      "user" / "human"用户消息用户的输入/问题
      "assistant" / "ai"AI 消息AI 的回复(用于对话历史)

2.3.1 两种实例化方式

  • ChatPromptTemplate 可以通过初始化方法或from_messages 方法来实例化提示词模板。实例化时需要传入 messages参数。

  • 方式1(推荐):调用from_messages()

    • 该方法允许传入一个由元组(Tuple)构成的列表,列表中的每一个元组都代表一条具有特定角色的消息。
    •   from langchain_core.prompts import ChatPromptTemplate
      
        chat_template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个AI机器人,你的名字是{name}。"),
                ("human", "你好,最近怎么样?"),
                ("ai", "我很好,谢谢!"),
                ("human", "{user_input}"),
            ]
        )
        
        # 格式化聊天提示词模版中的变量
        prompt = chat_template.invoke({"name": "小明", "user_input": "你叫什么名字?"})
        # 打印格式化后的聊天提示词模版内容
        print(prompt)
      
    • 在这里插入图片描述
  • 方式2:使用实例初始化方法

    •   from langchain_core.prompts import ChatPromptTemplate
      
        chat_template = ChatPromptTemplate(
            [
                ("system", "你是一个AI机器人,你的名字是{name}。"),
                ("human", "你好,最近怎么样?"),
                ("ai", "我很好,谢谢!"),
                ("human", "{user_input}"),
            ]
        )
        
        # 格式化聊天提示词模版中的变量
        prompt = chat_template.invoke({"name": "小明", "user_input": "你叫什么名字?"})
        # 打印格式化后的聊天提示词模版内容
        print(prompt)		
      
    • 在这里插入图片描述

2.3.2 模板调用的3种方式

  • 方式1:使用invoke()
    •   from langchain_core.prompts import ChatPromptTemplate
      
        chat_template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个AI机器人,你的名字是{name}。"),
                ("human", "你好,最近怎么样?"),
                ("ai", "我很好,谢谢!"),
                ("human", "{user_input}"),
            ]
        )
        
        # 传入参数:字典列表。返回参数:ChatPromptValue
        prompt = chat_template.invoke({"name": "小明", "user_input": "你叫什么名字?"})
        # 打印格式化后的聊天提示词模版内容
        print(prompt)
        print(type(prompt))
      
    • 在这里插入图片描述
  • 方式2:使用format()
    •   from langchain_core.prompts import ChatPromptTemplate
      
        chat_template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个AI机器人,你的名字是{name}。"),
                ("human", "你好,最近怎么样?"),
                ("ai", "我很好,谢谢!"),
                ("human", "{user_input}"),
            ]
        )
        
        # 传入参数:字典列表。返回参数:ChatPromptValue
        prompt = chat_template.format(name="小明", user_input="你叫什么名字?")
        # 打印格式化后的聊天提示词模版内容
        print(prompt)
        print(type(prompt))
      
    • 在这里插入图片描述
  • 方式3:使用format_messages()
    •   from langchain_core.prompts import ChatPromptTemplate
      
        chat_template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个AI机器人,你的名字是{name}。"),
                ("human", "你好,最近怎么样?"),
                ("ai", "我很好,谢谢!"),
                ("human", "{user_input}"),
            ]
        )
        
        # 传入参数:字典列表。返回参数:ChatPromptValue
        prompt = chat_template.format_messages(name="小明", user_input="你叫什么名字?")
        # 打印格式化后的聊天提示词模版内容
        print(prompt)
        print(type(prompt))
      
    • 在这里插入图片描述

2.3.3 结合大模型调用

  •   from langchain.chat_models import init_chat_model
      from dotenv import load_dotenv
      from langchain_core.prompts import ChatPromptTemplate
      import os
      
      load_dotenv(override=True)
      
      DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
      DEEPSEEK_BASE_URL = os.getenv('DEEPSEEK_BASE_URL')
      
      model = init_chat_model(
          model="deepseek-v4-flash",
          model_provider="openai",
          api_key=DEEPSEEK_API_KEY,
          base_url=DEEPSEEK_BASE_URL,
      )
      
      # 提供提示词模板
      chat_template = ChatPromptTemplate.from_messages(
          [
              ("system", "你是一个AI机器人,你的名字是{name}。"),
              ("human", "你好,最近怎么样?"),
              ("ai", "我很好,谢谢!"),
              ("human", "{user_input}"),
          ]
      )
      
      # 格式化聊天提示词模版中的变量
      prompt_value = chat_template.invoke({"name": "小明", "user_input": "你叫什么名字?"})
      
      # 模型调用
      response = model.invoke(prompt_value)
      print(response.content)
    

2.3.4 更丰富的初始化参数类型

  • 前面讲了ChatPromptTemplate的两种创建方式。我们看到不管使用实例初始化方法,还是使用from_messages(),参数类型都是列表类型。列表中的元素可以是多种类型,前面我们主要测试了元组类型。

  • 参数是列表类型,列表的元素可以是字符串、字典、字符串构成的元组、消息类型、提示词模板类型、消息提示词模板类型等

  • 类型1:str列表类型

    • 列表参数格式是str类型(不推荐),因为默认角色都是human
    •   
        from langchain_core.prompts import ChatPromptTemplate
        
        chat_template = ChatPromptTemplate.from_messages(
            [
                # 会理解为是一个用户消息
                "你好,我是{name}"
            ]
        )
        
        prompt = chat_template.invoke({"name": "小明"})
        
        print(prompt)
        print(type(prompt))
      
    • 在这里插入图片描述
  • 类型2:元组类型

    • 列表参数格式是元组类型。
    •   
        from langchain_core.prompts import ChatPromptTemplate
        
        chat_template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个AI助手"),
                ("human", "你好,我是{name}")
            ]
        )
        
        prompt = chat_template.invoke({"name": "小明"})
        
        print(prompt)
        print(type(prompt))
      
    • 在这里插入图片描述
  • 类型3:dict列表类型

    •   from langchain_core.prompts import ChatPromptTemplate
        
        chat_template = ChatPromptTemplate.from_messages(
            [
                {"role": "system", "content": "你是一个AI助手"},
                {"role": "human", "content":"你好,我是{name}"}
            ]
        )
        
        prompt = chat_template.invoke({"name": "小明"})
        
        print(prompt)
        print(type(prompt))
      
  • 类型4:Message列表类型

    •   
        from langchain_core.prompts import ChatPromptTemplate
        from langchain_core.messages import SystemMessage,HumanMessage
        
        chat_template = ChatPromptTemplate.from_messages(
            [
                SystemMessage(content="你是一个AI助手"),
                HumanMessage(content="你好,我是小明")
            ]
        )
        
        prompt = chat_template.invoke({})
        
        print(prompt)
        print(type(prompt))
      
  • 类型5:MessagePromptTemplate列表类型

    • LangChain提供不同类型的MessagePromptTemplate。最常用的是SystemMessagePromptTemplate、HumanMessagePromptTemplate 和AIMessagePromptTemplate,分别创建系统消息、人工消息和AI消息。
    •   # 导入聊天消息类模板
        from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate
        
        # 创建消息模板
        system_message_prompt = SystemMessagePromptTemplate.from_template("你是一个{role}")
        human_message_prompt = HumanMessagePromptTemplate.from_template("你好,我是{name}")
        
        # 组合成聊天提示模板
        chat_prompt = ChatPromptTemplate.from_messages([
            system_message_prompt,
            human_message_prompt
        ])
        
        # 格式化提示
        formatted_messages = chat_prompt.invoke({"role": "AI助手", "name": "小明"})
        print(formatted_messages)
      
  • 类型6:BaseChatPromptTemplate列表类型

    • 使用BaseChatPromptTemplate,可以理解为ChatPromptTemplate里嵌套了ChatPromptTemplate。
    • 举例1:带参数
      •   from langchain_core.prompts import ChatPromptTemplate
        
          # 使用BaseChatPromptTemplate(嵌套的ChatPromptTemplate)
          nested_prompt_template1 = ChatPromptTemplate.from_messages([
              ("system", "你是一个{role}")
          ])
          nested_prompt_template2 = ChatPromptTemplate.from_messages([
              ("human", "你好,我是{name}")
          ])
          
          prompt_template = ChatPromptTemplate.from_messages([
              nested_prompt_template1, nested_prompt_template2
          ])
          
          result = prompt_template.invoke({"role": "AI助手", "name": "小明"})
          print(result)
        
    • 举例2:不带参数
      •   from langchain_core.prompts import ChatPromptTemplate
        
          # 使用BaseChatPromptTemplate(嵌套的ChatPromptTemplate)
          nested_prompt_template1 = ChatPromptTemplate.from_messages([("system", "你是一个AI助手")])
          nested_prompt_template2 = ChatPromptTemplate.from_messages([("human", "我是小明")])
          prompt_template = ChatPromptTemplate.from_messages([
              nested_prompt_template1, nested_prompt_template2
          ])
          
          result = prompt_template.invoke({})
          print(result)
        

2.4 高级特性

2.4.1 部分变量预填充:partial

  • 预填充某些固定不变的变量,创建模板的变体。
  • 使用场景
    • 某些变量在所有调用中都相同
    • 需要为不同用户/场景创建定制模板
  • 举例1
    •   from langchain_core.prompts import ChatPromptTemplate
      
        # 原始模板
        template = ChatPromptTemplate.from_messages([
            ("system", "你是{role},目标用户是{audience}"),
            ("user", "{task}")
        ])
        
        # 部分填充
        customer_support_template = template.partial(
            role="客服专员",
            audience="普通用户"
        )
        # 现在只需要提供task
        messages = customer_support_template.invoke({"task": "解释退款政策"})
        print(messages)
      
  • 举例2
    •   from langchain_core.prompts import ChatPromptTemplate
      
        # 场景:为不同部门创建专用模板
        base_template = ChatPromptTemplate.from_messages([
            ("system", "你是{department}的{role}"),
            ("user", "{task}")
        ])
        # IT 部门
        it_template = base_template.partial(
            department="IT 部门",
            role="技术支持"
        )
        
        # 销售部门
        sales_template = base_template.partial(
            department="销售部门",
            role="销售顾问"
        )
        
        message = sales_template.invoke({"task": "为什么每年年底汽车会促销"})
        print(message)
      

2.4.2 消息占位符

  • 当你不确定消息提示模板使用什么角色,或者希望在格式化过程中插入消息列表时,该怎么办?这就需要使用消息占位符,负责在特定位置添加消息列表。
  • 使用场景:多轮对话系统存储历史消息以及Agent的中间步骤处理此功能非常有用。
  • 方式1:JSON形式
    •   from langchain_core.prompts import ChatPromptTemplate
      
        template = ChatPromptTemplate.from_messages(
            [
                ("system", "你是一个有用的AI助手"),
                ("placeholder", "{conversation}"),
            ]
        )
        prompt_value = template.invoke(
            {
                "conversation": [
                    ("human", "你好!"),
                    ("ai", "今天我能帮你做什么?"),
                    ("human", "你能给我做一个冰激凌吗?"),
                    ("ai", "抱歉,我没有这样的能力"),
                ]
            })
        
        print(prompt_value)
      
  • 方式2:MessagesPlaceholder实例
    • 举例1
      •   from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
          from langchain_core.messages import HumanMessage
          
          prompt_template = ChatPromptTemplate.from_messages([
              ("system", "You are a helpful assistant"),
              MessagesPlaceholder("msgs")
          ])
          
          result = prompt_template.invoke({"msgs": [HumanMessage(content="hi !")]})
          print(result)
        
    • 举例2:存储历史对话
      •   from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
        
          prompt_template = ChatPromptTemplate.from_messages(
              [
                  ("system", "你是一个非常友好的AI助手"),
                  MessagesPlaceholder(variable_name="history"),
                  ("human", "{question}")
              ]
          )
          prompt_template.invoke(
              {
                  "history": [
                      ("human", "5 + 2 = ?"),
                      ("ai", "5 + 2 = 7 ")
                  ],
                  "question": "结果再乘以4呢?"
              }
          )
        
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大草原的小灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值