本页目录
LangChain v1Note 02

第 1 章:Prompt 与消息模板

学习目标

学习目标

ChatPromptTemplate 把稳定指令、对话历史和本次问题组合为消息列表,而不是在每个节点手写 f-string。

核心机制

ChatPromptTemplate 是一个 Runnable:输入变量字典,输出可交给 chat model 的 ChatPromptValuefrom_messages 接受角色和模板;MessagesPlaceholder 专门插入已有 HumanMessageAIMessageToolMessage 列表;partial 预填不会随调用变化的变量。

prompt = ChatPromptTemplate.from_messages([
    ("system", "你面向 {audience} 回答。"),
    MessagesPlaceholder("history", optional=True),
    ("human", "{question}"),
]).partial(audience="Python 初学者")

数据流是 dict -> ChatPromptValue(messages) -> AIMessage。变量缺失会在格式化阶段报错;placeholder 的 optional=True 只适用于确实允许没有历史的场景。

与当前项目的关系

src/open_deep_research/prompts.py 目前存放普通字符串模板,节点用 .format(...) 后再创建 HumanMessage/SystemMessage。这是可行的,但当模板要混入多轮消息、工具消息或复用 partial 变量时,ChatPromptTemplate 更不易漏角色或漏变量。不要把 Runtime.context.api_keys、token、完整用户档案放入模板变量。

最小真实调用

运行 01_prompt_templates.py

uv run python docs/langchain/examples/01_prompt_templates.py

它调用一次模型,验证 history 被保留为消息、audience/style 被 partial 预填,并通过 StrOutputParser 将最终 AIMessage.content 转成字符串。

常见误区

  • MessagesPlaceholder 当字符串插槽:它应接收消息序列,不能直接接未序列化的工具结果 dict。
  • 把所有用户资料拼进 system prompt:只传模型回答需要的脱敏字段;身份和凭据仍在 Runtime.context
  • 把模板当安全边界:模板只格式化数据,不能自动防 prompt injection 或数据泄露。

官方参考:ChatPromptTemplate

相关资源

  • 查看示例代码:docs/langchain/examples/01_prompt_templates.py
    """Chapter 1: prompt templates and message placeholders."""
    
    import asyncio
    
    from dotenv import load_dotenv
    from langchain.chat_models import init_chat_model
    from langchain_core.messages import AIMessage, HumanMessage
    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
    
    from open_deep_research.configuration import Configuration
    
    
    load_dotenv()
    
    
    async def main():
        settings = Configuration.from_env()
        prompt = ChatPromptTemplate.from_messages(
            [
                ("system", "你是面向 {audience} 的 LangChain 助手。回答要 {style}。"),
                MessagesPlaceholder("history", optional=True),
                ("human", "{question}"),
            ]
        ).partial(audience="Python 初学者", style="简洁且准确")
    
        chain = prompt | init_chat_model(
            model=settings.research_model,
            max_tokens=120,
        ) | StrOutputParser()
        answer = await chain.ainvoke(
            {
                "history": [
                    HumanMessage(content="我在学习 LangChain。"),
                    AIMessage(content="好的。"),
                ],
                "question": "ChatPromptTemplate 的作用是什么?",
            }
        )
        print("模型回答: " + answer)
    
    
    if __name__ == "__main__":
        asyncio.run(main())