happy path#

# 启动之前需要在命令行执行
 pip install -r requirements.txt

做什么?#

让LLM做翻译

使用LLM#

import os
from langchain_openai import ChatOpenAI

# os.environ["OPENAI_API_KEY"] = "sk-********"  去掉注释
# os.environ["OPENAI_API_BASE"] = "https://ai-yyds.com/v1"

model = ChatOpenAI(model="gpt-4")

到此,创建了ChatOpenAI的model,他继承了langchain的RunnableSerializable接口,RunnableSerializable在langchain的表达式中很重要,他重写了 __or__方法,使得可以用 | 来实现管道符,并且规定了一系列标准方法,langchain的核心组件都实现了这个方法。之后细讲。在这里,就可以直接通过invoke方法来调用了。

model.invoke("hi,你是谁")
AIMessage(content='你好,我是OpenAI的人工智能助手。我可以帮助回答问题,提供信息,或者进行各种对话。你有什么需要帮助的吗?', response_metadata={'token_usage': {'completion_tokens': 55, 'prompt_tokens': 13, 'total_tokens': 68}, 'model_name': 'gpt-4', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-8bd37862-d050-4058-aa74-e168c96e44d1-0', usage_metadata={'input_tokens': 13, 'output_tokens': 55, 'total_tokens': 68})

如上所属,这种方式和之前直接调用openAI的接口是一样的,但是需要注意的是,返回值langchain做了包装,并且将入参也包装了, 将str变为PromptValue 在和LLM交互的时候需要角色扮演,这里也支持,用下面的方式

from langchain_core.messages import HumanMessage, SystemMessage
messages = [
    SystemMessage(content="将下面的英文歌词翻译为中文"),
    HumanMessage(content="""
Look how they shine for you
And everything you do
Yeah' they were all Yellow
I came along
I wrote a song for you
And all the things you do
It was called Yellow
So then I took my turn
Oh what a thing to have done
""")]
# 传递给LLM
res = model.invoke(messages)
print(res)
content='看它们如何为你闪耀\n和你做的一切\n是的,它们都是黄色的\n我来了\n我为你写了一首歌\n和你做的所有事情\n那首歌被叫做黄色\n然后我轮到我了\n哦,这是多么伟大的事情\n' response_metadata={'token_usage': {'completion_tokens': 96, 'prompt_tokens': 87, 'total_tokens': 183}, 'model_name': 'gpt-4', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-74b64201-7eda-41ee-8f3a-42a105be083c-0' usage_metadata={'input_tokens': 87, 'output_tokens': 96, 'total_tokens': 183}

返回值解析#

现在需要从返回的AIMessage中拿到返回值,langchain已经帮我们封装好了StrOutputParser,可以直接读取到content属性

from langchain_core.output_parsers import StrOutputParser

parser = StrOutputParser()
parser.invoke(res)
'看它们如何为你闪耀\n和你做的一切\n是的,它们都是黄色的\n我来了\n我为你写了一首歌\n和你做的所有事情\n那首歌被叫做黄色\n然后我轮到我了\n哦,这是多么伟大的事情\n'

如你看到的一样,StrOutputParser也继承了RunnableSerializable类,所以也可以通过invoke来调用。 到这里已经拿到了结果了。前面说了,继承于RunnableSerializable的类,都可以通过 | 符号来实现管道流。 上面的代码就可以改写为

chain = model | parser
chain.invoke(messages)
'看他们如何为你闪耀\n以及你做的一切\n是的,他们都是黄色的\n我出现了\n我为你写了一首歌\n和你做的所有事情\n这首歌叫黄色\n那么我轮到我了\n哦,做这件事真是太棒了'

体会到chain的意义了吗!! 不需要像之前一样,先组装prompt,在去调用llm的接口,自己做解析。langchain已经做好这样的功能了,我们做的事情是将组件拼接在一块,形成一个chain。快捷开发

但是现在还有个问题,prompt不够灵活,如果想翻译为韩文的话,要怎么做

Prompt Templates#

PromptTemplates 是 LangChain 中的一个重要概念,他可以做格式化模板,将消息分类(系统message,人类的message,ai的message) 对于上面,需要有两个模板变量

  • language: 翻译为什么语言

  • input:要翻译的文本

下面用ChatPromptTemplate来做,他是BasePromptTemplate的子类,用于创建灵活的聊天模型模板prompt

from langchain_core.prompts import ChatPromptTemplate

# 定义system message
system_template = "将下面的文本翻译为{language}:"
# 创建 ChatPromptTemplate,分为user和system
prompt_template = ChatPromptTemplate.from_messages(
    [("system", system_template), ("user", "{input}")]
)   

ChatPromptTemplate继承了RunnableSerializable,可以直接调用invoke方法

prompt_format_res = prompt_template.invoke({"language":"韩文","input":"""
Look how they shine for you
And everything you do
Yeah' they were all Yellow
I came along
I wrote a song for you
And all the things you do
It was called Yellow
So then I took my turn
Oh what a thing to have done
"""})
print(prompt_format_res)
type(prompt_format_res)
messages=[SystemMessage(content='将下面的文本翻译为韩文:'), HumanMessage(content="\nLook how they shine for you\nAnd everything you do\nYeah' they were all Yellow\nI came along\nI wrote a song for you\nAnd all the things you do\nIt was called Yellow\nSo then I took my turn\nOh what a thing to have done\n")]
langchain_core.prompt_values.ChatPromptValue

如上所示,prompt_template 调用 invoke之后,得到的类型是ChatPromptValue,调用messages拿到messages,如下所示,这就和👆🏻是一样的。不过,灵活性大大提升

prompt_format_res.messages
[SystemMessage(content='将下面的文本翻译为韩文:'),
 HumanMessage(content="\nLook how they shine for you\nAnd everything you do\nYeah' they were all Yellow\nI came along\nI wrote a song for you\nAnd all the things you do\nIt was called Yellow\nSo then I took my turn\nOh what a thing to have done\n")]

它也实现了RunnableSerializable接口,所以可以用|拼接

llm = prompt_template | model | parser
invoke_res = llm.invoke({"language":"韩文","input":"""
Look how they shine for you
And everything you do
Yeah' they were all Yellow
I came along
I wrote a song for you
And all the things you do
It was called Yellow
So then I took my turn
Oh what a thing to have done
"""})
print(invoke_res)
And it was all Yellow
Your

韩文看不懂,把韩文翻译为中文

llm.invoke({"language":"中文","input":invoke_res})
'而所有的都是黄色的\n你的'

!!看到了么,一个完整的chain出现了,prompt -> llm -> result parse。并且用| 连接了起来,实现了管道流。(是因为他们都继承了RunnableSerializable类),和原始的openai的chat api相比,代码简单了很多,灵活性也很高。

到这里happy path 结束了

上面支持开发,langchain是一站式的llm开发框架,下面介绍langsmith和langserver

lang server#

就是将chain通过REST api的形式暴露出来,并且提供了swagger文档和playground来调试chain

# ! pip install "langserve[all]"
# 我已经在requirements.txt中已经安装过了,这里不需要了

他是基于FAST API的,这里需要创建fast的app,绑定路由,启动。

import uvicorn
from fastapi import FastAPI
from langserve import add_routes

# 创建FastAPi app
app = FastAPI(
  title="LangChain Server",
  version="1.0",
  description="A simple API server using LangChain's Runnable interfaces",
)

# 创建chain
def translate_chain():
    # 将上面的代码包装在这里
    import os
    from langchain_openai import ChatOpenAI
    # os.environ["OPENAI_API_KEY"] = "sk-********"
    # os.environ["OPENAI_API_BASE"] = "https://ai-yyds.com/v1"
    
    model = ChatOpenAI(model="gpt-4")
    from langchain_core.output_parsers import StrOutputParser
    parser = StrOutputParser()
    from langchain_core.prompts import ChatPromptTemplate

    # 定义system message
    system_template = "将下面的文本翻译为{language}:"
    # 创建 ChatPromptTemplate,分为user和system
    prompt_template = ChatPromptTemplate.from_messages(
        [("system", system_template), ("user", "{input}")]
    ) 
    return prompt_template | model | parser

# 绑定chain
add_routes(
    app,
    translate_chain(),
    path="/chain",
)
print("这里的代码需要新建一个py文件来跑,因为jupyter-book里面不能再启动event loop了")
# if __name__ == '__main__':
#     # 启动
#     uvicorn.run(app, host="localhost", port=8000)
这里的代码需要新建一个py文件来跑,因为jupyter-book里面不能再启动event loop了

上面的代码需要新建一个py文件来跑,因为jupyter-book里面不能再启动event loop了,具体可以看 src/section_1_lang_server.py

他提供了功能如下

将chain暴露为了REST API并且提供了swagger文档#

服务地址: http://localhost:8000 接口文档地址:http://localhost:8000/docs

提供了playground来调试#

地址:http://localhost:8000/chain/playground/

运行调试:

lang smith#

官网: https://docs.smith.langchain.com/ lang Smith来监控上面的chain。在使用之前需要安装,配置api_key(api_key 在lang smith官网中配置就行)

export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=<your-api-key>

配置之后,再次运行上面的结果,可以很清晰的看到chain的运行过程,可以如下

到此,happy path已经完成了,回顾一下,在这个例子中,了解到

  • LangChain Expression Language(LangChain的表达式)

  • Prompt Template

  • Chat Models

  • Output Parsers

  • Lang Server

  • Lang Smith

在之后的学习中,我会按照上面的顺序来学习分享,此外还会增加一些例子。💪🏻