Prompt 管理噩梦? 你的 Prompt 是硬编码在代码里,还是散落在各个文件中?修改一个 Prompt 需要改动多个地方? 上下文处理繁琐? 如何在多轮对话中优雅地管理历史信息,避免信息丢失或冗余? 应用逻辑混乱? 想让 LLM 完成一个复杂任务(比如先检索文档再总结),代码写出来像一堆“屎山”,流程难以追踪? 模型切换成本高? 如果老板突然说要换个模型提供商,你是不是要改动大量的调用代码?
根据用户提问,先从公司内部知识库中检索相关文档。 将检索到的文档和用户提问一起作为 Prompt 发送给 LLM。 LLM 生成答案后,可能需要对答案进行结构化解析(例如提取关键实体)。 如果 LLM 无法回答,还需要调用外部工具(如搜索引擎)获取信息。 在整个过程中,还要保持对用户对话历史的记忆。
模块化 (Modularity):将 LLM 调用、Prompt 构建、信息检索、内存管理、工具使用等功能,拆分为独立的、可复用的组件。就像把一整块代码拆分成一个个微服务。 可组合性 (Composability):提供灵活的机制(尤其是 Chains 和 LCEL),让你能像搭积木一样,将这些独立的模块串联起来,形成复杂的业务逻辑流。
python -m venv venv# Windows.\venv\Scripts\activate# macOS/Linuxsource venv/bin/activate
pip install langchain langchain-openai # Langchain 核心库和 OpenAI 模块
# 在终端中运行,Windows 用户使用 set 而非 exportexport OPENAI_API_KEY="你的OpenAI API Key"
import osos.environ["OPENAI_API_KEY"] = "你的OpenAI API Key"
LLM (旧式接口):主要用于那些接收一个纯文本字符串作为输入,并返回一个纯文本字符串输出的模型(通常是文本补全任务)。例如,OpenAI 早期的一些 text-davinci-003 模型。在实际开发中,由于主流模型都已转向对话模式,这个接口的使用场景逐渐减少。 ChatModel (推荐使用):这是目前主流且更强大的接口。它接收一个 结构化的消息列表 作为输入(由 SystemMessage、HumanMessage、AIMessage 组成),并返回一个 结构化的 AI 消息对象。这完美契合了现代对话型模型的特点,更利于复杂的上下文管理和角色扮演。
import osfrom langchain_openai import ChatOpenAIfrom langchain_core.messages import HumanMessage, SystemMessage, AIMessage# 确保 OPENAI_API_KEY 已设置print("--- 2.1 LLM 与 Chat Model 接口演示 ---")# 初始化一个 ChatModel - 推荐使用 gpt-3.5-turbo 或 gpt-4# temperature 控制模型生成文本的随机性,0 表示确定性最高,1 表示创造性最高。chat_model = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)# 场景一:简单的用户提问print("\n--- 场景一:简单的用户提问 ---")user_query = "你好,请用一句话介绍一下 Langchain。"response = chat_model.invoke([HumanMessage(content=user_query)])print(f"用户提问: {user_query}")print(f"ChatModel 响应: {response.content}") # response 是 AIMessage 对象,内容在 .content 属性中# 场景二:结合系统消息,为模型设定角色或行为print("\n--- 场景二:结合系统消息,为模型设定角色或行为 ---")messages_with_system = [SystemMessage(content="你是一个专业的编程语言导师,擅长简洁明了地解释概念。"),HumanMessage(content="请解释一下 Python 中的装饰器。")]response_system_role = chat_model.invoke(messages_with_system)print(f"设定角色后 ChatModel 响应: {response_system_role.content}")# 场景三:模拟多轮对话的结构(实际应用中会结合 Memory 模块)print("\n--- 场景三:模拟多轮对话结构 ---")# 这里的 messages 列表代表了对话历史dialog_history = [HumanMessage(content="我最喜欢的编程语言是 Python。"),AIMessage(content="Python 是一个非常流行的语言!你喜欢它的哪些特性呢?"),HumanMessage(content="我喜欢它的简洁性和丰富的库生态。")]response_multi_turn = chat_model.invoke(dialog_history)print(f"ChatModel 响应 (模拟多轮): {response_multi_turn.content}")
统一接口:无论你将来切换到其他 ChatModel(如 GoogleGenerativeAI),调用 invoke 方法的格式基本不变,极大降低了模型迁移成本。 结构化输入输出:ChatModel 接收的是 BaseMessage 列表,这比简单的字符串更容易管理复杂的上下文,比如区分用户说的、AI 说的、系统设定的。
Prompt 即代码:将 Prompt 文本从业务逻辑中分离,像配置一样管理。 版本控制友好:Prompt 内容的变化可以像代码一样进行版本管理。 协作效率:不同团队成员可以独立维护和优化不同的 Prompt 模板。 测试性:更容易针对不同输入测试 Prompt 的效果。 一致性:确保在不同场景下生成的 Prompt 格式和质量一致。
PromptTemplate:适用于 LLM 类型的模型,它接收字符串变量,最终生成一个纯文本 Prompt 字符串。 ChatPromptTemplate:强烈推荐 用于 ChatModel 类型模型。它允许你以更细粒度的方式定义 Prompt,包括 SystemMessage(系统指令)、HumanMessage(用户输入)和 AIMessage(AI 回复)的模板。
from langchain_core.prompts import PromptTemplate, ChatPromptTemplatefrom langchain_core.messages import HumanMessage, SystemMessageprint("\n--- 2.2 Prompt Templates 演示 ---")# 1. 使用 PromptTemplate (适用于旧式LLM,了解即可)# 作用:将输入变量填充到模板字符串中poem_template = PromptTemplate(input_variables=["topic", "style"],template="请给我写一首关于 {topic} 的诗歌,风格是 {style}。",)# 格式化 Prompt,传入变量formatted_poem_prompt = poem_template.format(topic="秋天的落叶", style="伤感")print(f"格式化后的 Prompt (PromptTemplate):\n{formatted_poem_prompt}")# 2. 使用 ChatPromptTemplate (推荐,适用于 ChatModel)# 作用:构建结构化的消息列表,每个消息可以有自己的模板coding_assistant_template = ChatPromptTemplate.from_messages([SystemMessage(content="你是一个经验丰富的Python程序员,擅长解答编程问题,并给出代码示例。"),HumanMessage(content="请用 {language} 语言,实现一个 {functionality} 的函数。"),])# 格式化 ChatPromptTemplate,传入变量,返回一个消息对象列表messages_for_coding = coding_assistant_template.format_messages(language="Python",functionality="计算斐波那契数列的第 n 项")print(f"\n格式化后的 Chat Messages (ChatPromptTemplate):")for msg in messages_for_coding:print(f" {type(msg).__name__}: {msg.content[:50]}...") # 打印部分内容# 将格式化后的消息发送给 ChatModel# chat_model 在 2.1 节已初始化response_code = chat_model.invoke(messages_for_coding)print(f"\nChatModel 生成的代码解释:\n{response_code.content[:300]}...") # 打印部分响应# 另一个更复杂的 ChatPromptTemplate 示例:需要JSON格式输出json_prompt = ChatPromptTemplate.from_messages([SystemMessage(content="你是一个数据提取专家,请将用户信息提取为 JSON 格式。"),HumanMessage(content="请从以下文本中提取用户的姓名和年龄:\n\n用户资料:我叫李明,今年28岁。"),HumanMessage(content="请确保输出为严格的 JSON 格式,例如: `{{\"name\": \"\", \"age\": 0}}`")])json_messages = json_prompt.format_messages() # 无需额外变量,直接格式化response_json = chat_model.invoke(json_messages)print(f"\nChatModel 生成的 JSON:\n{response_json.content}")
模板的本质:Prompt 模板的本质是实现了 BasePromptTemplate 接口的类,它们负责将传入的变量字典转换为模型能理解的输入格式(字符串或 BaseMessage 列表)。 参数化 Prompt 的威力:在实际项目中,Prompt 往往非常复杂,包含大量的指示、示例。通过模板化,你可以将这些复杂性封装起来,只暴露需要动态填充的参数,大大降低了业务逻辑代码的耦合度。
极佳的可读性:数据流向一目了然。 强大的灵活性:支持复杂的输入/输出处理、并行执行、分支逻辑等。 性能优化:LCEL 组件原生支持异步(ainvoke)和流式传输(stream),提高应用响应速度。 易于测试:每个组件都是独立的,方便进行单元测试。
from langchain_core.prompts import ChatPromptTemplatefrom langchain_openai import ChatOpenAIfrom langchain_core.output_parsers import StrOutputParser # 用于将模型输出解析成字符串print("\n--- 2.3 LCEL (Chains) 演示 ---")# 1. 定义 Prompt Templatecode_gen_prompt = ChatPromptTemplate.from_messages([SystemMessage(content="你是一个专业的 Python 编程助手,擅长清晰地解释概念并提供简洁的代码示例。"),HumanMessage(content="请为我解释并提供一个关于 Python '{concept}' 的代码示例。"),HumanMessage(content="请确保解释和代码都清晰易懂。")])# 2. 初始化 Chat Model# chat_model 在 2.1 节已初始化# 3. 定义输出解析器# StrOutputParser 会把 ChatModel 返回的 AIMessage 对象中的 .content 部分提取出来output_parser = StrOutputParser()# 4. 构建 LCEL 链# 链的输入是一个字典,例如 {"concept": "装饰器"}# 1. `code_gen_prompt` 接收 {"concept": "..."},生成 Chat Messages 列表# 2. `chat_model` 接收 Chat Messages 列表,返回 AIMessage 对象# 3. `output_parser` 接收 AIMessage 对象,解析成纯字符串code_explanation_chain = code_gen_prompt | chat_model | output_parser# 5. 调用链并传入输入变量input_concept = "生成器"print(f"正在生成关于 '{input_concept}' 的代码示例和解释...")result = code_explanation_chain.invoke({"concept": input_concept})print(f"\n链的输出:\n{result}")# 另一个链的例子:先将用户输入转换为英文,再进行解释from langchain_core.runnables import RunnablePassthrough # 用于传递输入# 步骤1:翻译 Prompttranslate_prompt = ChatPromptTemplate.from_messages([SystemMessage(content="你是一个专业的翻译家,将用户输入的中文短语翻译成英文。"),HumanMessage(content="请将 '{text}' 翻译成英文。")])# 步骤2:英文解释 Promptexplain_prompt = ChatPromptTemplate.from_messages([SystemMessage(content="你是一个专业词汇解释器,用简洁的英文解释词语。"),HumanMessage(content="请解释一下 '{english_text}' 这个词汇的含义。")])# 构建一个更复杂的链:# 1. 接收一个包含 "text" 的字典,例如 {"text": "并行计算"}# 2. 通过 RunnablePassthrough 将 "text" 传给 translate_prompt# 3. translate_chain 翻译中文到英文,其输出是英文文本# 4. assign() 将翻译结果添加到字典中,键为 "english_text"# 5. explain_prompt 接收 "english_text",生成解释 Prompt# 6. chat_model 和 output_parser 处理最终的解释translation_and_explanation_chain = ({"text": RunnablePassthrough()} # 接收原始输入并传递| translate_prompt| chat_model| output_parser.assign(english_text=lambda x: x) # 将翻译结果赋值给 english_text 键| explain_prompt| chat_model| output_parser)# 注意:上面的 .assign() 只是一个示意,实际复杂的链可能需要更精妙的 RunnableParallel/RunnableMap 来处理多输入# 这里简化为串行处理,只传递一个主要输入。# 假设我们需要一个更清晰的多步骤链,来演示输入传递# Step 1: 翻译translator_chain = (translate_prompt | chat_model | output_parser)# Step 2: 解释 (需要上一步的翻译结果)# 这里的{"english_text": translator_chain} 表示将 translator_chain 的输出作为 english_text 的值full_pipeline = {"english_text": translator_chain,"original_text": RunnablePassthrough() # 传递原始的 "text" 输入,供后续步骤使用(如果需要)} | explain_prompt | chat_model | output_parserinput_text_for_pipeline = "并发编程"print(f"\n正在处理概念:'{input_text_for_pipeline}'")explanation_result = full_pipeline.invoke({"text": input_text_for_pipeline})print(f"解释结果:\n{explanation_result}")
LCEL :LCEL 的核心是 Runnable 接口。所有能被 | 连接的组件都实现了这个接口。它们定义了如何接收输入、如何处理、以及如何生成输出。 类型匹配:组件之间通过管道连接时,需要确保左侧组件的输出类型与右侧组件的预期输入类型匹配。例如,ChatPromptTemplate 输出的是 BaseMessage 列表,正好是 ChatModel 的输入。 RunnablePassthrough:这是一个非常有用的 LCEL 组件,它会将输入原封不动地传递下去。在构建需要保留原始输入,或将输入传递给链中多个组件的复杂链时非常有用。 StrOutputParser:虽然简单,但它扮演着重要的角色。LLM 返回的是一个 AIMessage 对象,而我们通常需要其中的 content 字符串进行后续处理或展示,StrOutputParser 恰好完成了这个转换。
LLMs & Chat Models:作为与大模型交互的统一抽象层,让我们轻松切换模型。 Prompt Templates:让你的 Prompt 变得可管理、可复用、可协作,是高效 Prompt Engineering 的关键。 LCEL & Chains:这是 Langchain 的灵魂!它以声明式、可读性极高的方式,将各种组件串联起来,构建出复杂而健壮的 LLM 业务逻辑流水线。
文章转载自AI云枢,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




