LangChain关于提示词的几种写法

上文中 <---->(可点击移步查阅),我们已经了解到LangChain如何编写调用大模型的一个基本框架,
在编写完这个基本框架后,就要与大模型进行交互了,这时候,就需要我们编写提示词了,
那么以下,就会介绍提示词的几种编写方式

1 可携带变量的提示词

1.1 PromptTemplate

1.1.1 以构造方法的形式创建

创建步骤如下:
	【1】导包
	【2】构建变量提示词模板
		【2.1】创建PromptTemplate实例
		【2.2】PromptTemplate构造函数中传递两个参数
			【2.2.1】template:定义携带变量的<提示词模板字符串>
			【2.2.2】input_variables:以列表形式指定<提示词模板字符串>中哪些是变量
	【3】通过实例调用方法组成真正的提示词
		【3.1】format(变量名1=值1, 变量名2=值2, ...)
		【3.2】invoke({"变量名1":"值1","变量名2":"值2","变量名3":"值3", ...})
		以上两个方法任选其一,作用是会将提示词模板中的变量替换成真正的值,返回PromptValue 对象

示例代码如下:

# 调用format()

# 导包
from langchain.prompts import PromptTemplate

#定义多变量模板。
template = PromptTemplate(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。",
    input_variables=["product", "aspect1", "aspect2"]
)

#使用模板生成提示词
# 说明:在调用format()函数时,一定要将所有的参数都进行赋值,才可以得到正确的字符串返回结果。否则报错
# prompt_1 = template.format(product="智能手机", aspect1="电池续航")
prompt_1 = template.format(product="智能手机", aspect1="电池续航", aspect2="拍照质量")

print("提示词1:",prompt_1)
# 调用invoke()

# 导包
from langchain.prompts import PromptTemplate

#定义多变量模板。
template = PromptTemplate(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。",
    input_variables=["product", "aspect1", "aspect2"]
)

#使用模板生成提示词
prompt_1 = template.invoke({
   
   
	"product": "智能手机", 
	"aspect1": "电池续航",
	"aspect2": "拍照质量"
})

print("提示词1:",prompt_1)

PromptTemplate构造方法中还可以再传入一个参数:partial_variables
示例代码如下:

from langchain.prompts import PromptTemplate

template = PromptTemplate(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。",
    input_variables=["product", "aspect1","aspect2"],
    partial_variables={
   
   "aspect2":"拍照质量","aspect1":"电池续航"}
)

str = template.format(product = "笔记本电脑")

1.1.2 以PromptTemplate类提供的from_template()的形式创建

创建步骤如下:
	【1】导包
	【2】构建变量提示词模板
		【2.1】调用PromptTemplate类提供的from_template()
		【2.2】from_template()中传递一个参数
			【2.2.1】template:定义携带变量的<提示词模板字符串>
	【3】通过实例调用方法组成真正的提示词
		【3.1】format(变量名1=值1, 变量名2=值2, ...)
		【3.2】invoke({"变量名1":"值1","变量名2":"值2","变量名3":"值3", ...})
		以上两个方法任选其一,作用是会将提示词模板中的变量替换成真正的值,返回PromptValue 对象

示例代码如下:

# 调用format()

# 导包
from langchain.prompts import PromptTemplate

template = PromptTemplate.from_template(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。"
)

prompt_1 = template.format(product="智能手机", aspect1="电池续航", aspect2="拍照质量")

print("提示词1:",prompt_1)
# 调用invoke()

# 导包
from langchain.prompts import PromptTemplate

template = PromptTemplate.from_template(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。"
)

prompt_1 = template.invoke({
   
   "product":"智能手机","aspect1":"电池续航","aspect2":"拍照质量"})

print(prompt_1)
print(type(prompt_1))

from_template()中还可以再传入一个参数:partial_variables
示例代码如下:

from langchain.prompts import PromptTemplate

template = PromptTemplate.from_template(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。",
    partial_variables={
   
   "aspect2":"拍照质量","aspect1":"电池续航"}
)

result = template.format(product="智能手机", )

1.1.3 结合大模型使用

定义好提示词后,如何将其传递给大模型呢?
我们这里以大模型提供的invoke()为例:
其他方法的使用可点击移步查阅

[1] 非对话大模型:

import os
import dotenv
from langchain_openai import OpenAI

dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY1")
os.environ['OPENAI_BASE_URL'] = os.getenv("OPENAI_BASE_URL")

# 非对话大模型
llm = OpenAI()
template = PromptTemplate.from_template(
    template="请评价{product}的优缺点,包括{aspect1}和{aspect2}。"
)

prompt_1 = template.invoke({
   
   "product":"智能手机","aspect1":"电池续航","aspect2":"拍照质量"})


str = llm.invoke(prompt_1)  #此时的参数prompt_1是PromtValue类型
print(str)

print(type(str))

[2] 对话大模型:

import os
import dotenv
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY1"
<think> 首先,用户的问题是:“我想查找SPI(可能是串行外设接口或服务提供者接口等)的几种代码实现写法 请问SPI的代码实现的几种写法”。这表明用户想知道SPI在代码中的不同实现方式。SPI有两种常见含义:串行外设接口(Serial Peripheral Interface)在硬件通信中,和服务提供者接口(Service Provider Interface)在软件设计中。 从系统级指令: - 我必须使用$...$格式行内数学表达式。 - 独立公式用$$...$$格式单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在引用段落末尾自然地添加引用标识,如[^1]。 用户提供了四个引用: 1. 引用[1]: 关于SPI在Java中的机制,用于模块化和可插拔性。 2. 引用[2]: 类似,SPI扩展在Java中,通过加载实现类。 3. 引用[3]: FPGA实现SPI通信,硬件层面的SPI。 4. 引用[4]: SPI协议基于STM32,硬件实现。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 用户上一次的问题是什么?回顾对话历史:用户在用户可见层问了这个当前问题,之前没有其他用户消息。系统指令中有一个示例关于快速排序,但那不是我回答的,是系统给的示例。所以,用户上一次的问题就是这个。 用户需求:用户想查找SPI的几种代码实现写法,提到可能是串行外设接口或服务提供者接口。所以,我应该覆盖这两种SPI。 结构化回答: - 解释SPI的两种含义。 - 分别讨论硬件SPI(串行外设接口)和软件SPI(服务提供者接口)的代码实现写法。 - 使用引用标识:引用中提到[^1], [^2] for software SPI, and [^3], [^4] for hardware SPI. - 生成相关问题。 回答结构: 1. 介绍SPI的两种类型。 2. 硬件SPI(串行外设接口)的代码实现写法: - 基于STM32的例子(参考引用[4])。 - FPGA实现的例子(参考引用[3])。 3. 软件SPI(服务提供者接口)的代码实现写法: - Java SPI机制(参考引用[1]和[^2])。 - 可能的其他语言实现(如果需要)。 4. 总结。 5. 生成相关问题。 确保使用中文。 生成相关问题:末尾添加相关问题。 引用标识:在引用段落末尾添加,如[^1]。 现在,构建内容: **硬件SPI (Serial Peripheral Interface):** - 这是一种通信总线协议,用于微控制器和外设之间的通信。 - 代码实现通常涉及设置时钟、数据线、片选信号。 - 例子: - 基于STM32:使用库函数或寄存器操作。参考引用[4],其中提到MySPI_W_SS函数用于片选信号。 - FPGA实现:使用Verilog或VHDL编写逻辑。参考引用[3],描述数据收发。 **软件SPI (Service Provider Interface):** - 在Java中,SPI是一种服务发现机制。 - 实现方式:定义接口,服务提供者实现接口,并在META-INF/services目录下提供配置文件。 - 代码写法:包括接口定义、实现类、服务加载器使用。 - 参考引用[1]和[^2]。 其他语言可能有类似机制,但用户引用集中在Java上。 代码片段:可能需要添加代码例子,但用户没有指定语言。用户引用了Python在快速排序示例中,但SPI引用是Java和C/FGPA。所以,我会添加代码块来演示。 系统指令允许代码块,如示例中的Python代码。 在回答中,自然地添加引用标识。 最终结构: - 开头解释SPI。 - 硬件SPI实现写法。 - 软件SPI实现写法。 - 总结。 - 相关问题。 </think> ### SPI代码实现的几种写法 SPI(Service Provider Interface 或 Serial Peripheral Interface)有两种常见含义:在软件中,指服务提供者接口,用于实现模块化和可插拔性;在硬件中,指串行外设接口,用于通信总线协议。我将分别介绍它们的代码实现写法,涵盖不同语言和平台。以下内容基于用户提供的引用和常见实践,确保代码真实可靠[^1][^2][^3][^4]。 #### 1. **硬件SPI(串行外设接口)的代码实现** 硬件SPI是一种高速、全双工、同步的通信总线协议,常用于微控制器(如STM32)或FPGA与外部设备(如传感器、存储器)通信。实现时需处理时钟(SCK)、数据输入(MISO)、数据输出(MOSI)和片选(CS)信号。以下是两种常见的写法: - **基于STM32的C语言实现(寄存器级操作)** 在STM32微控制器上,可使用HAL库或直接操作寄存器。参考引用[4],提供CS引脚控制函数的例子: ```c /* 重命名SPI的CS引脚写函数 */ void MySPI_W_SS(uint8_t bitValue) { GPIO_WriteBit(MY_SPI_CS_GPIOX, MY_SPI_CS_PIN, (BitAction)bitValue); // 设置CS引脚电平 } /* SPI初始化函数示例 */ void SPI_Init(void) { SPI_InitTypeDef SPI_InitStruct; SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 全双工模式 SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // 主设备模式 SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 数据位宽为8位 SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // 时钟极性低 SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // 时钟相位第一边沿 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 软件控制片选 SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 波特率预分频 SPI_Init(MY_SPI, &SPI_InitStruct); SPI_Cmd(MY_SPI, ENABLE); // 启用SPI } /* SPI发送接收数据函数 */ uint8_t SPI_Transfer(uint8_t data) { while (SPI_I2S_GetFlagStatus(MY_SPI, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区空 SPI_I2S_SendData(MY_SPI, data); // 发送数据 while (SPI_I2S_GetFlagStatus(MY_SPI, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收完成 return SPI_I2S_ReceiveData(MY_SPI); // 返回接收数据 } ``` 此写法适用于嵌入式系统,强调硬件控制。主设备需手动管理片选信号(如`MySPI_W_SS(0)`激活从设备)和数据传输时序[^4]。 - **基于FPGA的Verilog实现(逻辑电路级)** 在FPGA上,可使用硬件描述语言(如Verilog)实现SPI主设备逻辑。参考引用[3],描述数据收发机制: ```verilog module spi_master ( input wire clk, // 系统时钟 input wire rst, // 复位信号 input wire [7:0] data_tx, // 发送数据 output reg [7:0] data_rx, // 接收数据 output reg sck, // SPI时钟 output reg mosi, // 主输出从输入 input wire miso, // 主输入从输出 output reg cs // 片选信号(低电平有效) ); reg [2:0] bit_count; // 位计数器 reg [7:0] shift_tx; // 发送移位寄存器 reg [7:0] shift_rx; // 接收移位寄存器 reg state; // 状态机:0=空闲, 1=传输中 always @(posedge clk or posedge rst) begin if (rst) begin sck <= 1'b0; cs <= 1'b1; // 默认片选无效 state <= 1'b0; end else begin case (state) 1'b0: begin // 空闲状态 if (start_transfer) begin cs <= 1'b0; // 激活片选 shift_tx <= data_tx; bit_count <= 3'b0; state <= 1'b1; sck <= 1'b0; end end 1'b1: begin // 传输状态 sck <= ~sck; // 翻转时钟 if (sck) begin // 在时钟上升沿发送数据 mosi <= shift_tx[7]; // 发送最高位 shift_tx <= shift_tx << 1; end else begin // 在时钟下降沿接收数据 shift_rx <= {shift_rx[6:0], miso}; // 移位接收 bit_count <= bit_count + 1; if (bit_count == 3'b111) begin // 8位完成 data_rx <= shift_rx; cs <= 1'b1; // 禁用片选 state <= 1'b0; end end end endcase end end endmodule ``` 此写法适用于硬件开发,实现全双工通信。FPGA逻辑强调时钟同步和状态机控制,支持多个从设备通过片选信号管理[^3]。 硬件SPI实现的共性:需处理时序精度和中断机制,避免数据冲突;常见于嵌入式系统中。 #### 2. **软件SPI(服务提供者接口)的代码实现** 软件SPI是一种设计模式,用于实现动态服务加载和解耦,常见于Java等语言。核心是定义接口,服务提供者实现接口,并通过配置文件实现自动发现。写法依赖于语言和框架。 - **Java SPI实现(标准JDK机制)** Java内置SPI机制,允许在不修改代码的情况下扩展功能。实现步骤包括: 1. **定义服务接口**:创建接口文件。 2. **提供实现类**:服务厂商实现接口。 3. **配置文件**:在`META-INF/services/`目录下添加文件,列出实现类全限定名。 4. **服务加载**:使用`ServiceLoader`动态加载实例。 参考引用[1]和[^2],以下是简单示例: ```java // 步骤1: 定义接口 (例如: DataService.java) public interface DataService { void process(String data); } // 步骤2: 实现接口 (例如: FileDataService.java) public class FileDataService implements DataService { @Override public void process(String data) { System.out.println("Processing data from file: " + data); } } // 步骤2: 另一个实现 (例如: NetworkDataService.java) public class NetworkDataService implements DataService { @Override public void process(String data) { System.out.println("Processing data from network: " + data); } } // 步骤3: 配置文件 (在资源目录: META-INF/services/com.example.DataService) // 文件内容: com.example.FileDataService // 或添加多行: com.example.FileDataService\ncom.example.NetworkDataService // 步骤4: 客户端加载服务 import java.util.ServiceLoader; public class Client { public static void main(String[] args) { ServiceLoader<DataService> loader = ServiceLoader.load(DataService.class); for (DataService service : loader) { service.process("Test data"); // 动态调用所有实现 } } } ``` 此写法基于JDK内置服务发现,提升系统灵活性。配置文件机制确保实现类可动态加载,无需硬编码[^1][^2]。 - **其他语言实现(如Python模拟SPI)** 非Java语言虽无内置SPI,但可通过类似模式模拟。例如,在Python中使用插件架构: ```python # 定义接口类 (抽象基类) from abc import ABC, abstractmethod class DataService(ABC): @abstractmethod def process(self, data: str): pass # 实现服务 (插件) class FileDataService(DataService): def process(self, data): print(f"Processing data from file: {data}") # 服务加载器 (动态导入模块) import importlib import os def load_services(): services = [] plugin_dir = "plugins" for file in os.listdir(plugin_dir): if file.endswith(".py"): module_name = file[:-3] module = importlib.import_module(f"{plugin_dir}.{module_name}") for attr in dir(module): obj = getattr(module, attr) if isinstance(obj, type) and issubclass(obj, DataService) and obj != DataService: services.append(obj()) return services # 客户端使用 if __name__ == "__main__": services = load_services() for service in services: service.process("Test data") ``` 此写法使用反射和模块加载,实现类似SPI的动态扩展。配置文件可替换为Python模块约定(如特定目录结构)。 软件SPI实现的共性:强调解耦和可插拔性,配置文件是关键,允许运行时添加新服务而不影响主代码。 ### 总结 - **硬件SPI**:关注时序控制和底层硬件操作,写法以C/Verilog为主,适用于嵌入式系统[^3][^4]。 - **软件SPI**:基于接口和配置文件,写法以Java为标准,其他语言可模拟,适用于模块化设计[^1][^2]。 选择写法时,需根据上下文(通信需求或服务扩展)决定。硬件SPI更注重性能,软件SPI更注重灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陪我一起学编程

生活不易,求求鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值