引言
在上一篇LangChain入门中,我们已经了解到了可以使用LangChain的langchain_deepseek组件与DeepSeek进行对话。 但DeepSeek并没有提供如OpenAI一样的诸多参数来解决AI使用的问题,如控制随机性的temperature,控制重复(质量和新颖度)的presence_penalty和frequency_penalty。
关于参数在之前常见AI参数讲解有介绍过。
那AI的诸多问题要怎么解决呢?要怎么控制大模型(AI)呢? 常见问题如下:

我们应该使用"直接修改大模型本身"之外的方案来解决问题(不现实)。 比如,LangChain。 LangChain不仅仅通过简单传递参数给 LLM API来解决问题,它还抽象了一系列组件来提供问题的解决方案。使用LangChain,可以讲不同大模型API封装成统一、易于调用的形式。
需要快速回顾和看懂Python的Java程序员可以看一下我之前写的Python快速入门
基础代码
上一篇入门中有与DeepSeek联网搜索对话的代码,这里放一个对话基础代码。
我本地是没有问题的(狗头)。有问题随时交流。
在DeepSeek官网购买获取API-KEY后替换到代码中。
python
import os
# 基础对话所需
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import SystemMessage, HumanMessage
# 环境key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xx'
def get_deepseek_key():
key = os.getenv('DEEPSEEK_API_KEY')
if key is None:
raise ValueError("DEEPSEEK_API_KEY not found in environment variables.")
return key
# --- LLM 初始化 ---
def create_deepseek_llm():
api_key = get_deepseek_key()
if not api_key:
raise ValueError("没有ds key")
return ChatDeepSeek(
model = "deepseek-chat",
temperature=0.1, # 低温度,更具备确定性
max_tokens=1024,
timeout=None,
max_retries=2,
api_key=api_key
)
def test_simple_llm_call():
llm = create_deepseek_llm()
# 封装消息
# LangChain 支持的消息类型如下:
# - 'human': 人类消息
# - 'user': 用户消息
# - 'ai': AI 消息
# - 'assistant': 助手消息
# - 'function': 函数消息
# - 'tool': 工具消息
# - 'system': 系统消息
# - 'developer': 开发者消息
messages = [
("system","你是一个乐于助人的LangChain专家。"),
("human","你好,请用一句话介绍LangChain。")
]
# 流式响应
for chunk in llm.stream(messages):
print(chunk.text(), end="")
print("\n AI回答完了")
# 单词响应
# response = llm.invoke(messages)
# print("AI:", response.content)
if __name__ == '__main__':
test_simple_llm_call()
LangChain python官方文档
输出可控性
结构化输入输出:提示模板Prompt Templates
LangChain提供了组件 Prompt Templates(提示模板)用来"让你的指令更规范、灵活,并且可以动态地根据用户输入或上下文调整"。
python
# 导入提示模板
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
场景:我们创建一个LangChain专家的提示模板
python
import os
# 基础对话所需
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import SystemMessage, HumanMessage
# 提示模板
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
# 环境key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xx'
def get_deepseek_key():
key = os.getenv('DEEPSEEK_API_KEY')
if key is None:
raise ValueError("DEEPSEEK_API_KEY not found in environment variables.")
return key
# --- LLM 初始化 ---
def create_deepseek_llm():
api_key = get_deepseek_key()
if not api_key:
raise ValueError("没有ds key")
return ChatDeepSeek(
model = "deepseek-chat",
temperature=0.1, # 低温度,更具备确定性
max_tokens=1024,
timeout=None,
max_retries=2,
api_key=api_key
)
# --- 使用模板对话限定输入输出 ---
def create_langchain_teacher_template():
# 创建基础对话模板
"""
我们能在一些消息的构建上看到这种使用消息对象的写法。
但是实际上这不是推荐的简洁写法,应该直接使用元组。
是的,这个写法不会被视为一个s-string模板,而是一个string
return ChatPromptTemplate.from_messages([
SystemMessage(content="你是一个乐于助人的LangChain专家。"),
HumanMessage(content="你好,我想问一个关于LangChain的问题: {actual_user_input}")
])
"""
return ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的LangChain专家。"),
("human", "你好,我想问一个关于LangChain的问题: {actual_user_input}") # 确保这里是占位符
])
def test_simple_template_call():
llm = create_deepseek_llm()
user_question = "你好,请问LangChain组件Prompt Templates可以做什么?"
prompt_template = create_langchain_teacher_template()
messages = prompt_template.format_messages(actual_user_input=user_question)
print("最终发送给 LLM 的消息:")
for msg in messages:
print(f"- 类型: {msg.type}, 内容: {msg.content}")
print("\nAI开始回答")
for chunk in llm.stream(messages):
print(chunk.content, end="")
print("\nAI回答完了")
if __name__ == '__main__':
test_simple_template_call()
Prompt Templates可以告诉LLM它应该扮演什么角色、提供上下文、引导输出。
这里需要注意的是,在使用ChatPromptTemplate.from_messages()方法创建ChatPromptTemplate实例时,使用元组消息而是不是使用消息对象。 ("human", "...") 语法糖在幕后为你做了更多的工作,将字符串自动识别和处理为模板。而直接使用 HumanMessage(content="...") 时,content 被视为最终内容,而不是一个待格式化的模板。
应对不同场景的不同模板
LangChain提供了多种模板工具。
PromptTemplate
用于通用提示,适合单一角色的输入(如单一系统指令或用户输入)。 结构简单,适用于生成文本、回答问题、翻译等任务。
python
template = """
你是{persona},一个{description},以{tone}的语气回应以下问题:
问题:{query}
回答:
"""
prompt = PromptTemplate(input_variables=["persona", "description", "tone", "query"], template=template)
formatted_prompt = prompt.format(persona="福尔摩斯", description="天才侦探", tone="机智", query="你能破这个案子吗?")
# 输出:以福尔摩斯机智风格的回答。
ChatPromptTemplates
代码中使用的模板就是这个,专为对话场景设计,支持多角色消息(如系统、用户、助手)。适合模拟聊天、角色扮演或多轮对话,结构为消息列表。
MessagePlaceholder
MessagePlaceholder 是 LangChain 中用于 ChatPromptTemplate 的特殊占位符,允许在对话模板中动态插入消息列表(如对话历史、用户输入或其他角色消息)。 适合需要灵活处理多轮对话或多角色交互的场景,例如聊天机器人、带上下文的问答。
前面的代码加入MessagePlaceholder后如下:
python
import os
from langchain_deepseek import ChatDeepSeek
# 提示模板
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
# 环境key
# 请确保你已经设置了你的 API Key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xx'
def get_deepseek_key():
key = os.getenv('DEEPSEEK_API_KEY')
if key is None:
raise ValueError(
"DEEPSEEK_API_KEY 未在环境变量中找到。")
return key
# --- LLM 初始化 ---
def create_deepseek_llm():
api_key = get_deepseek_key()
# get_deepseek_key 已经检查了 key 是否存在,这里无需重复检查
return ChatDeepSeek(
model="deepseek-chat",
temperature=0.1, # 低温度,更具确定性
max_tokens=1024,
timeout=None,
max_retries=2,
api_key=api_key
)
# --- 创建带上下文的模板 ---
def create_langchain_teacher_template():
return ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的LangChain专家,基于对话历史提供准确、简洁的回答。"),
MessagesPlaceholder(variable_name="history"), # optional=True 也可以,但如果历史总是存在,则非必需
("human", "你好,我想问一个关于LangChain的问题: {actual_user_input}")
])
# --- 测试带上下文的对话 ---
def test_context_aware_template_call():
llm = create_deepseek_llm()
prompt_template = create_langchain_teacher_template()
# 初始化记忆机制
# 告诉记忆模块人类输入对应的键名是什么
memory = ConversationBufferMemory(
return_messages=True,
memory_key="history", # 这是默认值,与 MessagesPlaceholder 匹配
input_key="actual_user_input" # 关键:告诉记忆模块当前人类输入对应的键名
)
# 告诉 ConversationChain 它的主要输入键是什么
chain = ConversationChain(
llm=llm,
prompt=prompt_template,
memory=memory,
input_key="actual_user_input", # 关键:告诉链,run()方法中的哪个参数是主要的用户输入
verbose=True # 开启详细模式,方便调试,会打印完整的提示
)
# 第一次提问
user_question_1 = "你好,请问LangChain组件Prompt Templates可以做什么?"
print(f"\n用户提问: {user_question_1}")
# 当调用 run 时,使用在 ConversationChain 的 input_key 中指定的键
response_1 = chain.run(actual_user_input=user_question_1)
print("AI回答:", response_1)
# 第二次提问,依赖上下文
user_question_2 = "那它支持多轮对话吗?"
print(f"\n用户提问: {user_question_2}")
response_2 = chain.run(actual_user_input=user_question_2)
print("AI回答:", response_2)
# 你可以检查记忆中的内容
print("\n--- 记忆内容 ---")
print(memory.load_memory_variables({}))
if __name__ == '__main__':
# 请确保你设置了 API Key
if os.getenv('DEEPSEEK_API_KEY') == 'sk-6d65661014664acd8267025af3bfe925' or not os.getenv('DEEPSEEK_API_KEY'):
print("警告: DEEPSEEK_API_KEY 正在使用占位符或未设置。请输入你真实的 API Key。")
# 如果没有设置,可以临时在这里设置一个(仅为示例结构,请替换为你的真实Key)
if not os.getenv('DEEPSEEK_API_KEY'):
os.environ["DEEPSEEK_API_KEY"] = 'sk-6d65661014664acd8267025af3bfe925' # 临时使用占位符
print("临时使用占位符API Key进行演示。请务必替换它!")
test_context_aware_template_call()
输出解析器 Output Parsers(输出解析器)
现在,我们已经了解了基本对话和使用提示词模板Prompt Templates来限制输出。有时候我们需要结构化的输出数据,比如JSON、列表等... LangChain提供了Output Parsers(输出解析器)来解析大模型返回文本。
以Prompt Templates的代码为基础,我们导入类库,询问LangChain的3个优点
python
import os
# 基础对话所需
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import SystemMessage, HumanMessage
# 提示模板
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
# 输出解析器
from langchain.output_parsers import CommaSeparatedListOutputParser
# 链条
from langchain.chains import LLMChain, SimpleSequentialChain, SequentialChain
# 环境key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xx'
def get_deepseek_key():
key = os.getenv('DEEPSEEK_API_KEY')
if key is None:
raise ValueError("DEEPSEEK_API_KEY not found in environment variables.")
return key
# --- LLM 初始化 ---
def create_deepseek_llm():
api_key = get_deepseek_key()
if not api_key:
raise ValueError("没有ds key")
return ChatDeepSeek(
model = "deepseek-chat",
temperature=0.1, # 低温度,更具备确定性
max_tokens=1024,
timeout=None,
max_retries=2,
api_key=api_key
)
# --- 使用模板对话限定输入输出 ---
def create_langchain_teacher_template():
return ChatPromptTemplate.from_messages([
("system", "你是一个乐于助人的LangChain专家。"),
("human", "你好,我想问一个关于LangChain的问题: {actual_user_input} \n\n {format_instructions}") # 确保这里是占位符
])
# --- 使用输出解析器解析输出 ---
def create_output_parser():
return CommaSeparatedListOutputParser()
def test_output_parser_with_template():
llm = create_deepseek_llm()
output_parser = create_output_parser()
"""
1.获取格式化指令
对于 CommaSeparatedListOutputParser,它会是类似 "Your response should be a list of comma separated values, eg: `foo, bar, baz`"
"""
format_instructions = output_parser.get_format_instructions()
print(f"输出解析器的格式化指令: {format_instructions}")
prompt_template = create_langchain_teacher_template()
user_task = "请列出 LangChain 的3个主要优点。"
# 2.格式化完整提示,包含用户任务和格式指令
messages_for_llm = prompt_template.format_messages(
actual_user_input=user_task,
format_instructions=format_instructions
)
print("\n最终发送给 LLM 的消息 (包含格式指令):")
for msg in messages_for_llm:
print(f"- 类型: {msg.type}, 内容: {msg.content}")
# 3. 调用LLM并获取原始文本输出
print("\nAI开始生成原始文本 (等待 LLM 响应)...")
ai_response_message = llm.invoke(messages_for_llm)
raw_llm_output = ai_response_message.content # AIMessage对象的content属性是字符串
print(f"LLM 返回的原始文本: '{raw_llm_output}'")
# 4. 解析原始文本输出
try:
parsed_output = output_parser.parse(raw_llm_output)
print("\n解析后的输出 (Python列表):")
print(parsed_output)
if isinstance(parsed_output, list):
print("LangChain 的3个优点是:")
for i,advantage in enumerate(parsed_output):
print(f"{i}. {advantage.strip()}")
except Exception as e:
print(f"解析输出时出错: {e}")
print("这通常意味着 LLM 的输出没有严格遵循格式化指令。")
print("你可以尝试调整提示,或者使用更鲁棒的解析器/重试机制。")
print("--- 结束测试:带输出解析器的模板调用 ---\n")
if __name__ == '__main__':
test_output_parser_with_template()
