开发基于提示工程的大语言模型(LLM)应用
感谢NVIDIA开发者社区提供的学习机会,以下是个人在学习过程中的知识点总结。

课程链接:https://learn.nvidia.com/courses/course?course_id=course-v1:DLI+S-FX-12+V2-ZH
Part 1
该课程中提供了多种调用方式,分别是NVIDIA 推理微服务(NVIDIA Inference Microservice)、OpenAI 库、LangChain。
NVIDIA NIM 是一组易于使用的微服务,旨在安全、可靠地部署高性能 AI 模型推理,适用于云端、数据中心和工作站。它支持很多 AI 模型,包括开源社区和 NVIDIA AI Foundation 模型,确保在本地或云端无缝、可扩展地遵循行业标准 API 进行 AI 推理。https://build.nvidia.com/explore/discover
OpenAI 客户端配置
- 通过设置
base_url(如http://llama:8000/v1)和任意字符串api_key实例化客户端,适配本地模型服务。 - 本地 NIM(NVIDIA Inference Microservice)无需真实 API 密钥,但需确保端口和入口路径正确。
- 模型交互流程
- 使用
client.models.list()查看可用模型,确认目标模型 ID(如meta/llama-3.1-8b-instruct)。 - 通过
client.chat.completions.create()发起请求,需传入model和包含角色(如user)与内容的消息列表messages。
- 使用
- 响应解析与调试
- 从响应对象
response.choices[0].message.content提取模型生成内容。 - 可打印完整响应对象以观察元数据(如 token 使用量、模型版本等)。
- 从响应对象
- API 入口选择
chat.completions入口适用于对话场景,支持多轮上下文跟踪,生成简洁、连贯的回复。completions入口针对单条提示词生成响应,无上下文记忆,适合非对话任务。- 强调对指令调优模型(如 Llama 3.1 8B Instruct)应优先使用
chat.completions。
- 实践练习
- 提供自定义提示词(如
What is the OpenAI API?)的代码模板,引导用户独立完成请求发送与响应解析。
- 提供自定义提示词(如
通过 LangChain 框架简化与 Llama 3.1 8B Instruct 模型的交互
- LangChain 框架优势
- 提供统一接口抽象,支持多模型服务商,降低代码冗余。
- 简化提示工程与响应处理,便于快速迭代开发。
- 环境配置与模型初始化
- 使用
langchain_nvidia_ai_endpoints库的ChatNVIDIA类连接本地模型。 - 设置
base_url和model参数(与 OpenAI 库类似),并通过temperature=0控制生成结果的确定性。
- 使用
- 请求发起与结果解析
- 调用
llm.invoke(prompt)直接发送提示词,返回结果包含内容与元数据(如对话状态、token 统计)。 - 通过
result.content快速提取模型生成的文本。
- 调用
- 与 OpenAI 库的对比
- 代码简洁性:LangChain 的
invoke方法比 OpenAI 的多步调用更直观。 - 功能扩展性:LangChain 内置对话历史管理、批量处理等高级功能,为复杂应用提供基础。
- 代码简洁性:LangChain 的
- 实践练习
- 提供自定义提示词模板(如生成双关语),鼓励用户探索模型创造性,同时熟悉
temperature参数的影响。
- 提供自定义提示词模板(如生成双关语),鼓励用户探索模型创造性,同时熟悉
流式处理与批处理
- 流式处理:通过
stream方法逐步接收模型响应,适用于长响应或需要实时反馈的场景。流式处理可以提升用户体验,尤其是在用户界面应用中。 - 批处理:使用
batch方法处理多个输入,能够并行处理多个请求,提升性能和吞吐量。适用于需要同时处理多个提示词的场景。
提示词迭代开发
- 提示词迭代:通过不断调整和优化提示词,使模型输出更符合预期。提示词的具体性和清晰性对模型响应至关重要。
- 具体性:提示词越具体,模型响应越准确。可以通过多次迭代,逐步增加提示词的细节,以获得更好的结果。
- 长提示词:提示词的长度可以较长,尤其是当需要详细描述任务时。虽然长提示词可能会增加延迟和成本,但通常能带来更准确的响应。
提示模板
- 提示模板:通过模板化提示词,可以创建可复用的功能。LangChain 提供了强大的提示模板工具,能够帮助开发者构建复杂的提示词结构。
- 多值提示模板:提示模板可以接受多个输入值,通过字典形式传递参数,适用于需要处理多个变量的场景。
- ChatPromptTemplate:LangChain 提供的
ChatPromptTemplate可以用于创建聊天模型的提示模板,支持多轮对话和复杂的提示结构。
提示注入
- 提示注入漏洞:当提示词被恶意修改时,模型可能会输出不符合预期的结果。提示注入是一种安全漏洞,开发者需要采取措施防止提示词被篡改。
- 防范措施:在设计提示词时,应考虑如何防止提示注入,确保模型输出的完整性和安全性。
LangChain 的使用
- LangChain 工具:LangChain 提供了丰富的工具和模板,帮助开发者更高效地构建和管理提示词。通过 LangChain,开发者可以轻松创建复杂的提示模板,并批量处理多个请求。
- LCEL(LangChain Expression Language):LCEL 是一种表达语言,允许开发者创建简洁而强大的 LLM 应用功能链。通过 LCEL,开发者可以更高效地构建各种功能,提升开发效率。
多行字符串与空格处理
- 多行字符串:在编写长提示词时,使用多行字符串可以提高可读性,但需要注意避免引入不必要的空格和换行符。
- 转义换行符:通过转义换行符,可以避免在提示词中引入不必要的换行符,确保提示词的格式正确。
- 嵌套多行字符串:在函数或循环中使用多行字符串时,需要注意缩进问题,避免引入不必要的空格。
Part2
LangChain 表达语言(LCEL)与链
1. LangChain 运行时(Runnable)
- 定义:运行时是 LangChain 中的基本工作单元,可以被调用、批处理、流式处理,并与其他运行时组合。
- 核心组件:
- LLM 实例:如
ChatNVIDIA,用于调用模型生成响应。 - 提示模板:如
ChatPromptTemplate,用于生成动态提示词。 - 输出解析器:如
StrOutputParser,用于结构化模型输出。
- LLM 实例:如
- 方法:所有运行时均支持
invoke(单次调用)、batch(批量处理)、stream(流式处理)方法。
2. LangChain 表达语言(LCEL)
-
链(Chain):通过管道操作符
|将多个运行时组合成链,实现端到端的功能。 -
示例:
chain = ChatPromptTemplate.from_template("...") | llm | StrOutputParser() -
可视化:使用
chain.get_graph().draw_ascii()可查看链的结构。 -
输入输出:链的输入需符合提示模板的占位符要求(如
{question}),输出可通过解析器简化(如直接获取字符串)。
3. 输出解析器(Output Parser)
- 作用:将模型输出的复杂结构(如
AIMessage)转换为简单格式(如字符串)。 - 常用解析器:
StrOutputParser:提取content字段的文本。- 其他解析器:支持 JSON、列表等结构化输出(需配合特定提示词)。
4. 批处理与流式处理
-
批处理:通过
batch方法同时处理多个输入,提升效率。prompts = template.batch(questions) responses = llm.batch(prompts) -
流式处理:使用
stream方法逐步获取输出,适用于实时交互场景。
自定义运行时函数
1. RunnableLambda 的作用
-
功能:将普通 Python 函数转换为 LangChain 运行时,使其可集成到链中。
-
示例:
def double(x): return 2 * x runnable_double = RunnableLambda(double) runnable_double.invoke(3) # 输出 6
2. 数据预处理
-
应用场景:在调用 LLM 前对输入数据规范化(如文本清洗、格式转换)。
-
示例:
def normalize_text(text): text = text.lower() text = contractions.fix(text) # 扩展缩写(如 don't → do not) text = re.sub(r'\\\\s+', ' ', text).strip() return text runnable_normalize = RunnableLambda(normalize_text)
3. 链中集成自定义函数
-
流程示例:
- 数据规范化 → 2. 转换为模板输入 → 3. 生成提示词 → 4. 调用模型 → 5. 解析结果。
chain = ( RunnableLambda(normalize_text) | RunnableLambda(lambda text: {"text": text}) | sentiment_template | llm | StrOutputParser() ) -
批量处理:
results = chain.batch(["I loved it!", "Terrible service..."])
4. 复杂链的构建
- 多步骤处理:通过组合多个运行时实现复杂逻辑(如情感分析、翻译、摘要)。
- 调试工具:
- 使用
chain.get_graph().draw_ascii()可视化链的流程。 - 通过
chain.input_schema.schema()检查输入格式要求。
- 使用
实战示例
1. 翻译链
translate_template = ChatPromptTemplate.from_template("Translate from {from_lang} to {to_lang}: {text}")
translate_chain = translate_template | llm | StrOutputParser()
result = translate_chain.invoke({"from_lang": "English", "to_lang": "French", "text": "Hello!"})
2. 情感分析链
sentiment_chain = (
RunnableLambda(normalize_text) # 规范化输入
| RunnableLambda(lambda text: {"text": text}) # 转换为模板格式
| ChatPromptTemplate.from_template("Classify sentiment: {text}")
| llm
| StrOutputParser()
)
sentiments = sentiment_chain.batch(user_reviews)
组合链
核心知识点
- 链式组合原理
- 通过管道符
|实现链的串行组合 - 每个链的输出作为下一链的输入
- 支持创建多步骤处理流程(如:语法检查 → 段落生成)
- 链式开发流程
- 分步构建原子链(语法纠正链/段落生成链)
- 通过
.invoke()或.batch()测试单个链 - 使用管道符组合原子链形成复合链
# 构建语法纠正链
grammar_chain = (
ChatPromptTemplate.from_template("修复文本:{text}")
| llm
| StrOutputParser()
)
# 构建段落生成链
paragraph_chain = (
ChatPromptTemplate.from_template("以{text}开头写段落")
| llm
| StrOutputParser()
)
# 组合链
combined_chain = grammar_chain | paragraph_chain
- 使用
.get_graph().draw_ascii()可视化链结构 - 批量处理时通过
.batch()提升效率
并行链
核心知识点
- 并行执行原理
- 使用
RunnableParallel创建并行执行单元 - 独立任务可同时执行(如情感分析/主题提取/问题生成)
- 输出结果为字典结构整合各子任务结果
- 典型应用场景
- 需要多维度分析的文本处理
- 无数据依赖关系的独立任务
- 提升整体处理效率
parallel_chain = RunnableParallel({
"sentiment": 情感分析链,
"topic": 主题提取链,
"question": 问题生成链
})
# 结果整合
formatted_chain = parallel_chain | 格式化输出链
- 特殊语法技巧
- 字典字面量简写:
{"key": chain}代替显式RunnableParallel - 保留原始输入:通过Lambda函数传递原始文本
{"original": RunnableLambda(lambda x: x["text"])}
- 注意事项
- 并行链输出为字典结构,后续处理需适配
- 确保子任务间无数据依赖
- 批量处理时注意资源分配
总结
- LCEL 优势:通过声明式语法简化复杂流程的构建,支持链式组合、批处理和流式处理。
- 自定义函数:通过
RunnableLambda将任意函数集成到链中,灵活处理数据预处理、格式化等任务。 - 调试与优化:利用可视化工具和输入输出检查,确保链的每一步符合预期。
Part3
- 人类与 AI 消息:
- 聊天模型使用基于角色的消息传递系统,主要分为人类消息和 AI 消息。
- 使用
ChatPromptTemplate可以创建人类和 AI 消息的提示模板。 - 通过
HumanMessage和AIMessage类,可以明确地管理消息的角色。
- 少样本提示(Few-Shot Prompting):
- 少样本提示通过提供人类与 AI 的示例交互,影响模型的响应行为。
- 使用
FewShotChatMessagePromptTemplate可以动态地构建少样本提示。 - 少样本提示可以与主提示词结合使用,帮助模型更好地理解任务。
- 系统消息:
- 系统消息用于为聊天模型设定整体角色或上下文。
- 系统消息可以影响模型的响应风格和行为,尤其是在需要特定角色或任务的场景中。
- 系统消息通常用于定义模型的整体人格或任务框架。
- 思维链提示(Chain-of-Thought Prompting):
- 思维链提示通过让模型逐步分解复杂任务,提升其在复杂推理任务中的表现。
- 零样本思维链提示通过简单的提示词(如“让我们逐步思考”)引导模型进行分步推理。
- 思维链提示适用于需要逐步推理的任务,如数学计算或复杂问题解决。
- 聊天机器人(Chatbot):
- 聊天机器人通过保留对话历史来实现多轮对话。
- 使用占位符消息可以在提示模板中动态插入对话历史。
- 通过系统消息和少样本提示,可以为聊天机器人设定特定的角色或行为。
- 幻觉(Hallucination):
- LLM 可能会生成不正确的内容,这种现象被称为“幻觉”。
- 幻觉是 LLM 生成内容的一个常见问题,尤其是在没有足够上下文或明确指令的情况下。
总结
- 人类与 AI 消息:通过明确角色(人类或 AI)的消息传递系统,可以更好地控制聊天模型的交互方式。
ChatPromptTemplate和HumanMessage、AIMessage类是管理这些消息的关键工具。 - 少样本提示:通过提供示例交互,少样本提示可以显著影响模型的响应行为。
FewShotChatMessagePromptTemplate是一个强大的工具,可以动态地构建和管理这些示例。 - 系统消息:系统消息为模型设定了整体的上下文和角色,尤其是在需要特定风格或任务的场景中。系统消息可以极大地影响模型的响应风格和行为。
- 思维链提示:思维链提示通过引导模型逐步分解复杂任务,提升了模型在复杂推理任务中的表现。零样本思维链提示是一种简单但有效的方式,可以引导模型进行分步推理。
- 聊天机器人:通过保留对话历史和动态插入消息,聊天机器人可以实现多轮对话。系统消息和少样本提示可以帮助设定聊天机器人的特定角色或行为。
- 幻觉:LLM 可能会生成不正确的内容,尤其是在没有足够上下文或明确指令的情况下。理解并管理幻觉是构建可靠 LLM 应用的重要部分。
Part4
结构化输出
- 结构化输出的价值
- 允许LLM生成JSON等结构化格式,便于下游处理(如转换为字典、DataFrame)。
- 通过提示工程优化输出,例如明确字段名称、禁止非JSON文本。
- 实现方法
- 使用
ChatPromptTemplate构建参数化提示模板。 - 通过
StrOutputParser和SimpleJsonOutputParser解析模型响应。 - 批处理输入(
chain.batch())提高效率,支持多任务并行处理。
- 使用
- 关键工具
langchain_core.prompts.ChatPromptTemplate:动态生成提示词。langchain_core.output_parsers.SimpleJsonOutputParser:自动解析JSON到Python对象。
Pydantic
- Pydantic的核心作用
- 通过类定义结构化数据模型,强制字段类型和格式。
- 使用
Field描述字段含义,提升模型输出的准确性和一致性。
- LangChain集成
JsonOutputParser结合Pydantic类生成格式化指令。parser.get_format_instructions()自动生成模型所需的JSON结构描述。
- 改进效果
- 解决字段命名不一致(如
yearvsyear_of_publication)。 - 确保数据类型统一(如年份强制为整数而非字符串)。
- 解决字段命名不一致(如
Document-Tagging
- 复杂数据提取
- 从长文本中提取嵌套结构(如列表、多实体)。
- 定义复合Pydantic类(如包含
List[CrewMember]的Apollo11Details)。
- 链式处理设计
- 结合提示模板、LLM和解析器构建端到端流水线。
- 支持从自由文本提取多层次信息(人物、航天器、引用)。
- 实际应用示例
- 解析阿波罗11号任务描述,提取机组、航天器模块和名言。
- 使用
pprint美化输出,便于调试和数据分析。
总结
- 技术演进路径
- 从简单JSON生成(手动提示工程)→ 通过Pydantic强制结构化 → 复杂文本的多层级数据提取。
- LangChain工具(如
OutputParser)显著简化流程,提升代码可维护性。
- 核心优势
- 一致性:Pydantic模型消除字段歧义,确保输出符合预期结构。
- 扩展性:支持批量处理和复杂嵌套数据,适应真实场景需求。
- 自动化:
get_format_instructions()自动生成提示词,减少人工干预。
- 应用场景
- 数据标注:从非结构化文本提取结构化信息(如新闻事件、科研论文)。
- API集成:生成符合接口规范的JSON响应。
- 知识图谱构建:自动化实体关系提取。
- 后续方向
- 结合工具调用(Tool Use)实现动态决策(如根据结构化数据触发外部API)。
- 探索多模态输出(如生成结构化报告并自动生成可视化图表)。
Part5
工具调用(Tool Calling)
- 工具定义:
- 使用
@tool装饰器将普通函数转换为工具,工具的描述信息来自函数docstring - 通过Pydantic类定义参数结构,实现参数验证和说明(如
Multiply类定义乘法参数) - 工具调用需通过
invoke方法传入参数字典(如multiply.invoke({'a':3, 'b':5}))
- 使用
- 工具绑定与调用:
- 使用
bind_tools方法将工具列表绑定到LLM实例 - LLM通过
tool_calls属性返回工具调用建议(包含工具名称和参数) - 需要手动实现工具映射和执行逻辑(如
call_tools函数处理工具调用)
- 使用
- 典型应用场景:
- 数学计算(乘法工具)
- 外部数据查询(维基百科摘要工具)
- 实时数据获取(空气质量API工具)
智能体(Agents)
- 核心机制:
- 采用ReAct模式(推理与行动结合),通过LangGraph实现状态管理
- 消息系统包含
HumanMessage/AIMessage/ToolMessage三种类型 - 支持多轮工具调用(如连续回答多个乘法问题)
- 开发流程:
- 使用
create_react_agent创建智能体,绑定工具和系统消息 - 通过状态修改器(
state_modifier)添加行为约束(如"必须回答查询,不讨论思考过程") - 构建LCEL链实现输入输出标准化(
convert_to_agent_state处理输入格式)
- 使用
- 优化策略:
- 提示工程引导工具使用(如明确工具使用条件和响应要求)
- 错误处理机制(如API调用异常返回友好提示)
- 结果验证(对比工具计算结果与实际值)
总结
核心价值
- 能力扩展:通过工具调用机制,使LLM突破自身限制,能处理数学运算、实时数据查询等复杂任务。例如维基百科工具让模型能回答时效性问题,空气质量工具实现实时环境数据获取。
- 智能编排:智能体通过状态管理实现多步推理,将工具调用结果动态整合到响应中。如处理多轮乘法问题时,能自动执行多次工具调用并汇总结果。
关键挑战
- 可靠性问题:工具参数解析存在幻觉风险(如将成立年份问题错误映射到乘法参数),需通过参数校验和提示工程约束行为。
- 系统设计:需要构建工具映射表、状态解析器等组件,LangGraph简化了流程但仍需理解节点/边的运作机制。
Best Practice
- 工具设计:明确的参数描述(如`Field(…, description=“纬度”))和错误处理
- 智能体优化:通过系统消息(如"必须直接回答,不讨论思考过程")规范输出
- 链式开发:采用
RunnableLambda标准化输入输出,实现提示词→状态→响应的管道化处理
应用&spm=1001.2101.3001.5002&articleId=145994457&d=1&t=3&u=246aab7db9324bc69a0c513bef306f6c)
689

被折叠的 条评论
为什么被折叠?



