通过 System Prompt 控制模型行为,用 Few-shot 示例引导风格,用 Pydantic 强制模型输出结构化 JSON——这是构建可靠 AI 应用的核心技能。
提示工程(Prompt Engineering)是影响 LLM 输出质量的最直接手段。本节从最基础到最严格,介绍四种递进的提示技巧:
通过 system_prompt 参数定义 Agent 的角色和行为准则。最简单但效果显著。
在 system prompt 里加入 2-5 个输入/输出示例,引导模型模仿特定风格或格式。
在 prompt 里用模板明确定义输出的字段名称和格式,让自然语言输出更规整。
用 Pydantic 定义数据模型,通过 response_format 强制 LLM 输出严格的 JSON,可直接作为 Python 对象使用。
可靠性和可解析性依次提升:基础 → Few-shot → 结构化提示 → 结构化输出。生产环境中,凡是需要程序解析 LLM 输出的场景,都应优先考虑结构化输出(Pydantic),它是最可靠的方式。
最简单的提示方式:在 create_agent 时传入 system_prompt 参数,告诉模型它是谁、应该怎么做。
没有 system prompt 时,模型会给出"诚实"的回答——月亮没有首都:
agent = create_agent(model=deepseek_model())
question = HumanMessage(content="What's the capital of the moon?")
response = agent.invoke({"messages": [question]})
print(response['messages'][1].content)
system_prompt = "You are a science fiction writer, create a capital city at the users request."
scifi_agent = create_agent(
model=deepseek_model(temperature=1), # temperature=1 增加创意性
system_prompt=system_prompt
)
response = scifi_agent.invoke({"messages": [question]})
print(response['messages'][1].content)
temperature=1 让模型输出更具创意性和多样性(每次运行结果不同);temperature=0 使输出更确定、可重复(每次运行结果相同)。创意型任务用高 temperature,需要一致性的任务(如代码生成、数据提取)用低 temperature。
Few-shot(少样本)提示是一种强大的技巧:在 system prompt 里直接给出几个"输入 → 输出"的示例,LLM 会模仿这些示例的风格和格式来生成新的响应。
system_prompt = """
You are a science fiction writer, create a space capital city at the users request.
User: What is the capital of mars?
Scifi Writer: Marsialis
User: What is the capital of Venus?
Scifi Writer: Venusovia
"""
scifi_agent = create_agent(
model=deepseek_model(),
system_prompt=system_prompt
)
question = HumanMessage(content="What is the capital of The Moon?")
response = scifi_agent.invoke({"messages": [question]})
print(response['messages'][1].content)
注意输出风格的变化:模型学习了示例里"Scifi Writer: [单词]"的格式,给出了一个简洁的单词答案,而不是长篇描述。
LLM 通过训练学会了"续写"文本。Few-shot 示例相当于给模型展示了一段"对话脚本",模型会按照这个脚本的风格续写。示例的质量和一致性直接决定了 Few-shot 效果——示例要有代表性,格式要统一。
结构化提示(Structured Prompting)是在 system prompt 里定义明确的字段名称和输出格式,让模型的自然语言输出更规整、更易解析。
system_prompt = """
You are a science fiction writer, create a space capital city at the users request.
Please keep to the below structure.
Name: The name of the capital city
Location: Where it is based
Vibe: 2-3 words to describe its vibe
Economy: Main industries
"""
scifi_agent = create_agent(
model=deepseek_model(),
system_prompt=system_prompt
)
response = scifi_agent.invoke({"messages": [question]})
print(response['messages'][1].content)
结构化提示的输出仍然是自然语言字符串,字段分隔只是约定,模型可能不严格遵守(偶尔会增减字段、改变顺序)。如果你需要用代码可靠地解析每个字段,必须用下一节的结构化输出(Pydantic)。
结构化输出(Structured Output)是最可靠的方式:用 Pydantic 定义数据模型,通过 response_format 参数告诉 LLM 必须按这个 schema 输出。LangChain 会在底层把模型的 JSON 输出自动解析为 Pydantic 对象。
from pydantic import BaseModel
class CapitalInfo(BaseModel):
name: str # 城市名称
location: str # 地理位置
vibe: str # 氛围描述
economy: str # 经济支柱
agent = create_agent(
model=deepseek_model(),
system_prompt="You are a science fiction writer, create a capital city at the users request.",
response_format=CapitalInfo # 关键:传入 Pydantic 类
)
question = HumanMessage(content="What is the capital of The Moon?")
response = agent.invoke({"messages": [question]})
# structured_response 是 Pydantic 对象,不是字符串
print(response["structured_response"])
当你传入 response_format=CapitalInfo 时,LangChain 会:
CapitalInfo 的工具,参数严格符合 schemaresponse["structured_response"]现代 LLM(GPT-4、DeepSeek、Claude)都支持工具调用(Function Calling / Tool Use),这要求模型输出严格符合 JSON Schema 的参数,可靠性远高于"在普通文本里嵌入 JSON"。response_format 参数正是利用了这一机制。
拿到 Pydantic 对象后,可以像访问普通 Python 对象属性一样读取每个字段:
capital_info = response["structured_response"]
# 访问具体字段
print(capital_info.name) # → 'Lunar Prime'
print(capital_info.location) # → 'Mare Imbrium, near the lunar equator'
print(capital_info.vibe) # → 'futuristic, serene, industrious'
print(capital_info.economy) # → 'Helium-3 mining...'
# 组合使用
print(f"{capital_info.name} is located at {capital_info.location}")
使用 response_format 时,messages 列表里除了 HumanMessage 和 AIMessage,还会有一条 ToolMessage:
for msg in response["messages"]:
pprint(msg)
用结构化输出时,response['messages'][-1].content 不再是你要的内容(它可能是空字符串或工具调用消息)。应该通过 response["structured_response"] 来获取 Pydantic 对象,而不是 messages[-1].content。
| 技巧 | 输出格式 | 可靠性 | 适用场景 |
|---|---|---|---|
| 基础提示 | 自然语言(自由格式) | 低 | 开放式对话、创意写作、简单问答 |
| Few-shot | 自然语言(模仿示例格式) | 中 | 需要特定风格/格式,有样例可参考 |
| 结构化提示 | 自然语言(有字段约定) | 中 | 需要大致结构,允许偶发偏差 |
| 结构化输出 | Pydantic 对象(严格 JSON) | 高 | 需要程序解析输出、数据管道、表单填写 |