NVIDIA DLI课程——开发基于提示工程的大语言模型(LLM)应用

Python3.8

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

开发基于提示工程的大语言模型(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 密钥,但需确保端口和入口路径正确。
  1. 模型交互流程
    • 使用 client.models.list() 查看可用模型,确认目标模型 ID(如 meta/llama-3.1-8b-instruct)。
    • 通过 client.chat.completions.create() 发起请求,需传入 model 和包含角色(如 user)与内容的消息列表 messages
  2. 响应解析与调试
    • 从响应对象 response.choices[0].message.content 提取模型生成内容。
    • 可打印完整响应对象以观察元数据(如 token 使用量、模型版本等)。
  3. API 入口选择
    • chat.completions 入口适用于对话场景,支持多轮上下文跟踪,生成简洁、连贯的回复。
    • completions 入口针对单条提示词生成响应,无上下文记忆,适合非对话任务。
    • 强调对指令调优模型(如 Llama 3.1 8B Instruct)应优先使用 chat.completions
  4. 实践练习
    • 提供自定义提示词(如 What is the OpenAI API?)的代码模板,引导用户独立完成请求发送与响应解析。

通过 LangChain 框架简化与 Llama 3.1 8B Instruct 模型的交互

  1. LangChain 框架优势
    • 提供统一接口抽象,支持多模型服务商,降低代码冗余。
    • 简化提示工程与响应处理,便于快速迭代开发。
  2. 环境配置与模型初始化
    • 使用 langchain_nvidia_ai_endpoints 库的 ChatNVIDIA 类连接本地模型。
    • 设置 base_urlmodel 参数(与 OpenAI 库类似),并通过 temperature=0 控制生成结果的确定性。
  3. 请求发起与结果解析
    • 调用 llm.invoke(prompt) 直接发送提示词,返回结果包含内容与元数据(如对话状态、token 统计)。
    • 通过 result.content 快速提取模型生成的文本。
  4. 与 OpenAI 库的对比
    • 代码简洁性:LangChain 的 invoke 方法比 OpenAI 的多步调用更直观。
    • 功能扩展性:LangChain 内置对话历史管理、批量处理等高级功能,为复杂应用提供基础。
  5. 实践练习
    • 提供自定义提示词模板(如生成双关语),鼓励用户探索模型创造性,同时熟悉 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,用于结构化模型输出。
  • 方法:所有运行时均支持 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. 链中集成自定义函数

  • 流程示例

    1. 数据规范化 → 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)


组合链

核心知识点

  1. 链式组合原理
  • 通过管道符 | 实现链的串行组合
  • 每个链的输出作为下一链的输入
  • 支持创建多步骤处理流程(如:语法检查 → 段落生成)
  1. 链式开发流程
  • 分步构建原子链(语法纠正链/段落生成链)
  • 通过.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()提升效率

并行链

核心知识点

  1. 并行执行原理
  • 使用RunnableParallel创建并行执行单元
  • 独立任务可同时执行(如情感分析/主题提取/问题生成)
  • 输出结果为字典结构整合各子任务结果
  1. 典型应用场景
  • 需要多维度分析的文本处理
  • 无数据依赖关系的独立任务
  • 提升整体处理效率
parallel_chain = RunnableParallel({
    "sentiment": 情感分析链,
    "topic": 主题提取链,
    "question": 问题生成链
})

# 结果整合
formatted_chain = parallel_chain | 格式化输出链

  1. 特殊语法技巧
  • 字典字面量简写:{"key": chain} 代替显式RunnableParallel
  • 保留原始输入:通过Lambda函数传递原始文本
{"original": RunnableLambda(lambda x: x["text"])}

  1. 注意事项
  • 并行链输出为字典结构,后续处理需适配
  • 确保子任务间无数据依赖
  • 批量处理时注意资源分配

总结

  • LCEL 优势:通过声明式语法简化复杂流程的构建,支持链式组合、批处理和流式处理。
  • 自定义函数:通过 RunnableLambda 将任意函数集成到链中,灵活处理数据预处理、格式化等任务。
  • 调试与优化:利用可视化工具和输入输出检查,确保链的每一步符合预期。

Part3

  1. 人类与 AI 消息
    • 聊天模型使用基于角色的消息传递系统,主要分为人类消息和 AI 消息。
    • 使用 ChatPromptTemplate 可以创建人类和 AI 消息的提示模板。
    • 通过 HumanMessageAIMessage 类,可以明确地管理消息的角色。
  2. 少样本提示(Few-Shot Prompting)
    • 少样本提示通过提供人类与 AI 的示例交互,影响模型的响应行为。
    • 使用 FewShotChatMessagePromptTemplate 可以动态地构建少样本提示。
    • 少样本提示可以与主提示词结合使用,帮助模型更好地理解任务。
  3. 系统消息
    • 系统消息用于为聊天模型设定整体角色或上下文。
    • 系统消息可以影响模型的响应风格和行为,尤其是在需要特定角色或任务的场景中。
    • 系统消息通常用于定义模型的整体人格或任务框架。
  4. 思维链提示(Chain-of-Thought Prompting)
    • 思维链提示通过让模型逐步分解复杂任务,提升其在复杂推理任务中的表现。
    • 零样本思维链提示通过简单的提示词(如“让我们逐步思考”)引导模型进行分步推理。
    • 思维链提示适用于需要逐步推理的任务,如数学计算或复杂问题解决。
  5. 聊天机器人(Chatbot)
    • 聊天机器人通过保留对话历史来实现多轮对话。
    • 使用占位符消息可以在提示模板中动态插入对话历史。
    • 通过系统消息和少样本提示,可以为聊天机器人设定特定的角色或行为。
  6. 幻觉(Hallucination)
    • LLM 可能会生成不正确的内容,这种现象被称为“幻觉”。
    • 幻觉是 LLM 生成内容的一个常见问题,尤其是在没有足够上下文或明确指令的情况下。

总结

  • 人类与 AI 消息:通过明确角色(人类或 AI)的消息传递系统,可以更好地控制聊天模型的交互方式。ChatPromptTemplateHumanMessageAIMessage 类是管理这些消息的关键工具。
  • 少样本提示:通过提供示例交互,少样本提示可以显著影响模型的响应行为。FewShotChatMessagePromptTemplate 是一个强大的工具,可以动态地构建和管理这些示例。
  • 系统消息:系统消息为模型设定了整体的上下文和角色,尤其是在需要特定风格或任务的场景中。系统消息可以极大地影响模型的响应风格和行为。
  • 思维链提示:思维链提示通过引导模型逐步分解复杂任务,提升了模型在复杂推理任务中的表现。零样本思维链提示是一种简单但有效的方式,可以引导模型进行分步推理。
  • 聊天机器人:通过保留对话历史和动态插入消息,聊天机器人可以实现多轮对话。系统消息和少样本提示可以帮助设定聊天机器人的特定角色或行为。
  • 幻觉:LLM 可能会生成不正确的内容,尤其是在没有足够上下文或明确指令的情况下。理解并管理幻觉是构建可靠 LLM 应用的重要部分。

Part4

结构化输出

  1. 结构化输出的价值
    • 允许LLM生成JSON等结构化格式,便于下游处理(如转换为字典、DataFrame)。
    • 通过提示工程优化输出,例如明确字段名称、禁止非JSON文本。
  2. 实现方法
    • 使用ChatPromptTemplate构建参数化提示模板。
    • 通过StrOutputParserSimpleJsonOutputParser解析模型响应。
    • 批处理输入(chain.batch())提高效率,支持多任务并行处理。
  3. 关键工具
    • langchain_core.prompts.ChatPromptTemplate:动态生成提示词。
    • langchain_core.output_parsers.SimpleJsonOutputParser:自动解析JSON到Python对象。

Pydantic

  1. Pydantic的核心作用
    • 通过类定义结构化数据模型,强制字段类型和格式。
    • 使用Field描述字段含义,提升模型输出的准确性和一致性。
  2. LangChain集成
    • JsonOutputParser结合Pydantic类生成格式化指令。
    • parser.get_format_instructions()自动生成模型所需的JSON结构描述。
  3. 改进效果
    • 解决字段命名不一致(如year vs year_of_publication)。
    • 确保数据类型统一(如年份强制为整数而非字符串)。

Document-Tagging

  1. 复杂数据提取
    • 从长文本中提取嵌套结构(如列表、多实体)。
    • 定义复合Pydantic类(如包含List[CrewMember]Apollo11Details)。
  2. 链式处理设计
    • 结合提示模板、LLM和解析器构建端到端流水线。
    • 支持从自由文本提取多层次信息(人物、航天器、引用)。
  3. 实际应用示例
    • 解析阿波罗11号任务描述,提取机组、航天器模块和名言。
    • 使用pprint美化输出,便于调试和数据分析。

总结

  1. 技术演进路径
    • 从简单JSON生成(手动提示工程)→ 通过Pydantic强制结构化 → 复杂文本的多层级数据提取。
    • LangChain工具(如OutputParser)显著简化流程,提升代码可维护性。
  2. 核心优势
    • 一致性:Pydantic模型消除字段歧义,确保输出符合预期结构。
    • 扩展性:支持批量处理和复杂嵌套数据,适应真实场景需求。
    • 自动化get_format_instructions()自动生成提示词,减少人工干预。
  3. 应用场景
    • 数据标注:从非结构化文本提取结构化信息(如新闻事件、科研论文)。
    • API集成:生成符合接口规范的JSON响应。
    • 知识图谱构建:自动化实体关系提取。
  4. 后续方向
    • 结合工具调用(Tool Use)实现动态决策(如根据结构化数据触发外部API)。
    • 探索多模态输出(如生成结构化报告并自动生成可视化图表)。

Part5

工具调用(Tool Calling)

  1. 工具定义
    • 使用@tool装饰器将普通函数转换为工具,工具的描述信息来自函数docstring
    • 通过Pydantic类定义参数结构,实现参数验证和说明(如Multiply类定义乘法参数)
    • 工具调用需通过invoke方法传入参数字典(如multiply.invoke({'a':3, 'b':5})
  2. 工具绑定与调用
    • 使用bind_tools方法将工具列表绑定到LLM实例
    • LLM通过tool_calls属性返回工具调用建议(包含工具名称和参数)
    • 需要手动实现工具映射和执行逻辑(如call_tools函数处理工具调用)
  3. 典型应用场景
    • 数学计算(乘法工具)
    • 外部数据查询(维基百科摘要工具)
    • 实时数据获取(空气质量API工具)

智能体(Agents)

  1. 核心机制
    • 采用ReAct模式(推理与行动结合),通过LangGraph实现状态管理
    • 消息系统包含HumanMessage/AIMessage/ToolMessage三种类型
    • 支持多轮工具调用(如连续回答多个乘法问题)
  2. 开发流程
    • 使用create_react_agent创建智能体,绑定工具和系统消息
    • 通过状态修改器(state_modifier)添加行为约束(如"必须回答查询,不讨论思考过程")
    • 构建LCEL链实现输入输出标准化(convert_to_agent_state处理输入格式)
  3. 优化策略
    • 提示工程引导工具使用(如明确工具使用条件和响应要求)
    • 错误处理机制(如API调用异常返回友好提示)
    • 结果验证(对比工具计算结果与实际值)

总结

核心价值

  1. 能力扩展:通过工具调用机制,使LLM突破自身限制,能处理数学运算、实时数据查询等复杂任务。例如维基百科工具让模型能回答时效性问题,空气质量工具实现实时环境数据获取。
  2. 智能编排:智能体通过状态管理实现多步推理,将工具调用结果动态整合到响应中。如处理多轮乘法问题时,能自动执行多次工具调用并汇总结果。

关键挑战

  1. 可靠性问题:工具参数解析存在幻觉风险(如将成立年份问题错误映射到乘法参数),需通过参数校验和提示工程约束行为。
  2. 系统设计:需要构建工具映射表、状态解析器等组件,LangGraph简化了流程但仍需理解节点/边的运作机制。

Best Practice

  • 工具设计:明确的参数描述(如`Field(…, description=“纬度”))和错误处理
  • 智能体优化:通过系统消息(如"必须直接回答,不讨论思考过程")规范输出
  • 链式开发:采用RunnableLambda标准化输入输出,实现提示词→状态→响应的管道化处理

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值