对于前面提到的本地部署或通过API以代码的形式调用,使用Langchain是一种非常流行的方式。下面是对之前课上学习的简单汇总回顾,目前本地是一台比较老的中端游戏本,配着GTX 1650的显卡和8GB左右内存,所以许多内容来自线上的运行记录,也存在一些纸上谈兵的情况,也当时为未来本地实操留下一些记录。
1.创建一个模型实例:
LangChain 集成的模型主要有两种类型:LLM(来自OpenAI类,将字符串提示作为输入并输出字符串) 和聊天模型(来自ChatOpenAI类,不是使用单个字符串,而是将聊天消息列表作为输入,并返回 AI 消息)。不过仅从定义来看,不太清楚二者的具体区别,或许是后者存在“user”、“模型自己”这样的概念?
llm = ChatOpenAI(model=os.environ.get("LLM_MODELEND")) #可添加temperature-值越大越随机(0、0.5、1);max_tokens;max_retries
2.直接使用模型:
打印这个结果,还会出现所使用的模型的其他信息
text = llm.invoke("How's the weather tomorrow?")
#输出结果:
{'completion_tokens': 239, 'prompt_tokens': 18, 'total_tokens': 257}, 'model_name': 'Doubao-pro-32k', 'system_fingerprint': '', 'finish_reason': 'stop', 'logprobs': None} id='run-53e7857f-bb23-49ba-a42b-c4fb54e5a12e-0' usage_metadata={'input_tokens': 18, 'output_tokens': 239, 'total_tokens': 257, ...}
3.包装输入:
在目前的理解中,这也是基于Langchain使用大语言模型的核心,也就是通过预置的各种提示词模板包装提示词并输出,如课上提到的创建基础提示词模板:
# 创建原始模板
template = """您是一位专业的鲜花店文案撰写员。\n 对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗? """
prompt = PromptTemplate.from_template(template)
#生成输出
input = prompt.format(flower_name=["玫瑰"], price='50')
output = model.invoke(input)
将两个变量以列表的形式定义,或者之前从外部文件读取,再通过循环的形式调用生成输出的过程,就可以实现批量的问答。这个交给模型的模板实际的模样是这样的:
input_variables=['flower_name', 'price']
output_parser=None partial_variables={}
template='/\n您是一位专业的鲜花店文案撰写员。 \n对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?\n'
template_format='f-string'
validate_template=True
之后涉及的各种链、智能体,看起来也是通过内置模板来包装输入提示词,并获得模型的回答,长长的链也是将一个个阶段性模型的输出再次当作输入放给下一环节的提示词模板之中(不过这样的token开销似乎也很大)。
4.包装输出:
也就是输出的格式化,看起来也是通过内置的提示词模板实现的,如课上最开始的输出解析,也就是在之前的prompt基础上再套一个结构化的prompt。解析出来的输出可以进行选择性输出或者写入pandas的Dataframe并格式化进行输出:
template = """... {format_instructions}"""
response_schemas = [
ResponseSchema(name="description", description="鲜花的描述文案"),
ResponseSchema(name="reason", description="为什么要这样写这个文案")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = output_parser.get_format_instructions()
#将其传入实例化的prompt
prompt = PromptTemplate.from_template(template, partial_variables={"format_instructions": format_instructions})
#在得到输出后进行输出解析
parsed_output = output_parser.parse(output)
parsed_output['flower'] = flower
parsed_output['price'] = price
之后的json解析器也有着类似的原理,先定义需要的输出格式,再进一步创建输出解析器
class FlowerDescription(BaseModel):
flower_type: str = Field(description="鲜花的种类")
price: int = Field(description="鲜花的价格")
description: str = Field(description="鲜花的描述文案")
reason: str = Field(description="为什么要这样写这个文案")
output_parser = PydanticOutputParser(pydantic_object=FlowerDescription)
format_instructions = output_parser.get_format_instructions()
而它的本质,也依然是提示词工程:
The output should be formatted as a JSON instance that conforms to the JSON schema below.
As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}} the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.
Here is the output schema: {"properties": {"flower_type": {"title": "Flower Type", "description": "\u9c9c\u82b1\u7684\u79cd\u7c7b", "type": "string"}, "price": {"title": "Price", "description": "\u9c9c\u82b1\u7684\u4ef7\u683c", "type": "integer"}, "description": {"title": "Description", "description":"\u9c9c\u82b1\u7684\u63cf\u8ff0\u6587\u6848", "type": "string"}, "reason": {"title": "Reason", "description": "\u4e3a\u4ec0\u4e48\u8981\u8fd9\u6837\u5199\u8fd9\u4e2a\u6587\u6848", "type": "string"}}, "required": ["flower_type", "price", "description", "reason"]}