You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/zh/10-ai-framework-project/README.md

13 KiB

AI框架

市面上有许多AI框架可以显著加快项目开发的速度。在本项目中我们将重点了解这些框架解决的问题并亲自构建一个这样的项目。

为什么选择框架

在使用AI时有不同的方式和选择这些方式的不同原因以下是一些例子

  • 无SDK大多数AI模型允许通过例如HTTP请求直接与模型交互。这种方式可行并且如果没有SDK选项时可能是唯一选择。
  • SDK使用SDK通常是推荐的方式因为它可以减少与模型交互时需要编写的代码量。通常SDK仅限于特定模型如果使用不同的模型可能需要编写新的代码来支持这些额外的模型。
  • 框架框架通常将事情提升到另一个层次。如果需要使用不同的模型框架通常提供一个统一的API区别通常在于初始设置。此外框架还引入了有用的抽象例如在AI领域它们可以处理工具、记忆、工作流、代理等同时减少代码编写量。由于框架通常具有一定的主观性如果你接受它的工作方式它会非常有帮助但如果你尝试做一些框架未设计的定制化内容可能会有所不足。有时框架可能过于简化导致你无法学习某些重要主题这可能会在后续影响性能。

总的来说,选择适合工作的工具。

介绍

在本课程中,我们将学习:

  • 使用常见的AI框架。
  • 解决常见问题,例如聊天对话、工具使用、记忆和上下文。
  • 利用这些知识构建AI应用。

第一个提示

在我们的第一个应用示例中我们将学习如何连接到AI模型并使用提示进行查询。

使用Python

在这个示例中我们将使用Langchain连接到GitHub模型。我们可以使用一个名为ChatOpenAI的类,并为其提供api_keybase_urlmodel字段。令牌会自动在GitHub Codespaces中填充如果你在本地运行应用则需要设置个人访问令牌才能正常工作。

from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

# works
response  = llm.invoke("What's the capital of France?")
print(response.content)

在这段代码中,我们:

  • 调用ChatOpenAI创建一个客户端。
  • 使用llm.invoke和提示生成响应。
  • 使用print(response.content)打印响应。

你应该会看到类似以下的响应:

The capital of France is Paris.

聊天对话

在上一节中,你看到了我们如何使用通常称为零次提示的方法,即单个提示后跟一个响应。

然而通常你会发现自己需要维护一个由多个消息组成的对话这些消息在你和AI助手之间交换。

使用Python

在Langchain中我们可以将对话存储在一个列表中。HumanMessage表示用户的消息,而SystemMessage是用于设置AI“个性”的消息。在下面的示例中你会看到我们指示AI扮演皮卡德舰长的角色并让用户提出“告诉我关于你的信息”作为提示。

messages = [
    SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
    HumanMessage(content="Tell me about you"),
]

完整的代码示例如下:

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

messages = [
    SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
    HumanMessage(content="Tell me about you"),
]


# works
response  = llm.invoke(messages)
print(response.content)

你应该会看到类似以下的结果:

I am Captain Jean-Luc Picard, the commanding officer of the USS Enterprise (NCC-1701-D), a starship in the United Federation of Planets. My primary mission is to explore new worlds, seek out new life and new civilizations, and boldly go where no one has gone before. 

I believe in the importance of diplomacy, reason, and the pursuit of knowledge. My crew is diverse and skilled, and we often face challenges that test our resolve, ethics, and ingenuity. Throughout my career, I have encountered numerous species, grappled with complex moral dilemmas, and have consistently sought peaceful solutions to conflicts.

I hold the ideals of the Federation close to my heart, believing in the importance of cooperation, understanding, and respect for all sentient beings. My experiences have shaped my leadership style, and I strive to be a thoughtful and just captain. How may I assist you further?

为了保持对话的状态,你可以将聊天的响应添加到对话中,这样对话就会被记住,以下是实现方式:

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

messages = [
    SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
    HumanMessage(content="Tell me about you"),
]


# works
response  = llm.invoke(messages)

print(response.content)

print("---- Next ----")

messages.append(response)
messages.append(HumanMessage(content="Now that I know about you, I'm Chris, can I be in your crew?"))

response  = llm.invoke(messages)

print(response.content)

从上述对话中可以看到我们两次调用了LLM第一次对话仅包含两条消息第二次对话则添加了更多消息。

实际上,如果你运行这段代码,你会看到第二次响应类似于:

Welcome aboard, Chris! It's always a pleasure to meet those who share a passion for exploration and discovery. While I cannot formally offer you a position on the Enterprise right now, I encourage you to pursue your aspirations. We are always in need of talented individuals with diverse skills and backgrounds. 

If you are interested in space exploration, consider education and training in the sciences, engineering, or diplomacy. The values of curiosity, resilience, and teamwork are crucial in Starfleet. Should you ever find yourself on a starship, remember to uphold the principles of the Federation: peace, understanding, and respect for all beings. Your journey can lead you to remarkable adventures, whether in the stars or on the ground. Engage!

我就当你是可能吧 ;)

流式响应

TODO

提示模板

TODO

结构化输出

TODO

工具调用

工具是我们赋予LLM额外技能的方式。其核心思想是告诉LLM它可以使用哪些函数如果提示与某个工具的描述匹配就会调用该工具。

使用Python

让我们添加一些工具,如下所示:

from typing_extensions import Annotated, TypedDict

class add(TypedDict):
    """Add two integers."""

    # Annotations must have the type and can optionally include a default value and description (in that order).
    a: Annotated[int, ..., "First integer"]
    b: Annotated[int, ..., "Second integer"]

tools = [add]

functions = {
    "add": lambda a, b: a + b
}

在这里,我们创建了一个名为add的工具的描述。通过继承TypedDict并添加像ab这样的成员(类型为Annotated这可以转换为LLM可以理解的模式。函数的创建是一个字典用于确保当识别到特定工具时我们知道该做什么。

接下来我们看看如何使用这个工具调用LLM

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

llm_with_tools = llm.bind_tools(tools)

在这里,我们使用bind_tools绑定我们的tools数组因此LLM llm_with_tools现在知道这个工具。

为了使用这个新的LLM我们可以输入以下代码

query = "What is 3 + 12?"

res = llm_with_tools.invoke(query)
if(res.tool_calls):
    for tool in res.tool_calls:
        print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)

现在当我们调用具有工具的新的LLM的invoke时,可能会填充tool_calls属性。如果是这样,任何识别到的工具都会有一个nameargs属性,用于标识应该调用哪个工具以及参数是什么。完整代码如下:

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
from typing_extensions import Annotated, TypedDict

class add(TypedDict):
    """Add two integers."""

    # Annotations must have the type and can optionally include a default value and description (in that order).
    a: Annotated[int, ..., "First integer"]
    b: Annotated[int, ..., "Second integer"]

tools = [add]

functions = {
    "add": lambda a, b: a + b
}

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

llm_with_tools = llm.bind_tools(tools)

query = "What is 3 + 12?"

res = llm_with_tools.invoke(query)
if(res.tool_calls):
    for tool in res.tool_calls:
        print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)

运行这段代码,你应该会看到类似以下的输出:

TOOL CALL:  15
CONTENT: 

这个输出的意思是LLM分析了提示“3 + 12是多少”认为应该调用add工具并且它知道这一点是因为工具的名称、描述和成员字段描述。答案是15因为我们的代码使用了字典functions来调用它:

print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))

一个更有趣的工具调用Web API

添加两个数字的工具很有趣因为它说明了工具调用的工作原理但通常工具会做一些更有趣的事情例如调用Web API。让我们用以下代码实现这一点

class joke(TypedDict):
    """Tell a joke."""

    # Annotations must have the type and can optionally include a default value and description (in that order).
    category: Annotated[str, ..., "The joke category"]

def get_joke(category: str) -> str:
    response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
    if response.status_code == 200:
        return response.json().get("value", f"Here's a {category} joke!")
    return f"Here's a {category} joke!"

functions = {
    "add": lambda a, b: a + b,
    "joke": lambda category: get_joke(category)
}

query = "Tell me a joke about animals"

# the rest of the code is the same

现在,如果你运行这段代码,你会得到类似以下的响应:

TOOL CALL:  Chuck Norris once rode a nine foot grizzly bear through an automatic car wash, instead of taking a shower.
CONTENT:  

以下是完整代码:

from langchain_openai import ChatOpenAI
import requests
import os
from typing_extensions import Annotated, TypedDict

class add(TypedDict):
    """Add two integers."""

    # Annotations must have the type and can optionally include a default value and description (in that order).
    a: Annotated[int, ..., "First integer"]
    b: Annotated[int, ..., "Second integer"]

class joke(TypedDict):
    """Tell a joke."""

    # Annotations must have the type and can optionally include a default value and description (in that order).
    category: Annotated[str, ..., "The joke category"]

tools = [add, joke]

def get_joke(category: str) -> str:
    response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
    if response.status_code == 200:
        return response.json().get("value", f"Here's a {category} joke!")
    return f"Here's a {category} joke!"

functions = {
    "add": lambda a, b: a + b,
    "joke": lambda category: get_joke(category)
}

llm = ChatOpenAI(
    api_key=os.environ["GITHUB_TOKEN"],
    base_url="https://models.github.ai/inference",
    model="openai/gpt-4o-mini",
)

llm_with_tools = llm.bind_tools(tools)

query = "Tell me a joke about animals"

res = llm_with_tools.invoke(query)
if(res.tool_calls):
    for tool in res.tool_calls:
        # print("TOOL CALL: ", tool)
        print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)

嵌入

向量化内容,通过余弦相似性进行比较

https://python.langchain.com/docs/how_to/embed_text/

文档加载器

支持PDF和CSV

构建应用

TODO

作业

总结


免责声明
本文档使用AI翻译服务Co-op Translator进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档为权威来源。对于关键信息,建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。