初探轻量级LLM应用开发框架ell

初探轻量级LLM应用开发框架ell

📅 2024-09-19 | 🖱️
🔖 aigc

ell是一个全新的开发大语言模型应用框架。

ell的官方文档里将自己称为"大语言模型编程库", “ell是一个轻量级的提示工程库,将提示(prompt)视为函数”。

现在面向LLM的编程框架层出不穷,LangChain几乎为我们封装了所有,但是有些过于重了。ell的特点是"轻量化"。

使用LangChain或者直接面向OpenAI API的原生调用代码,除了Prompt之外还要写很多调用代码,而且对于Prompt缺乏版本控制管理,这成为了一个痛点 。ell轻量化的特点是它将是对LLM的操作简化到了只需写提示词。

ell的特点:

  • 将对LLM的调用简化到只需写提示词(Prompt),并且可以方便的对提示词进行版本跟踪
  • 有可视化的工具(ell studio),可以可视化监控跟踪每一次请求
  • 原生支持多模态

1.ell的设计原则 #

1.1 提示词不仅仅是字符串, 而是程序LMP #

提示词不仅仅是字符串, 而是程序LMP(Prompts are programs, not strings)。

1import ell
2
3@ell.simple(model="gpt-4o-mini")
4def hello(world: str):
5    """You are a helpful assistant""" # System prompt
6    name = world.capitalize()
7    return f"Say hello to {name}!" # User prompt
8
9hello("sam altman") # just a str, "Hello Sam Altman! ..."

在ell中提示词不仅仅是字符串;它们是所有将字符串发送到LLM的代码。

在ell中,我们将使用LLM的一种特定方式称为LMP(language model program)。

LMP是完全封装的函数,用于生成要发送到各种多模态语言模型的字符串提示或消息列表。这种封装为用户创建了一个干净的界面,用户只需指定给LMP的必需数据。

1.2 提示工程是一个优化过程 #

提示工程是一个优化过程(Prompt engineering is an optimization process)。

提示工程的过程涉及许多迭代,类似于机器学习中的优化过程。因为LMP只是函数,ell为此过程提供了丰富的工具。

ell通过静态和动态分析自动对提示进行版本控制和序列化,并直接将gpt-4o-mini自动生成的commit信息发送到本地存储。这个过程类似于机器学习训练循环中的检查点,但它不需要任何特殊的IDE或编辑器,这一切都是用常规的Python代码完成的。

1import ell
2
3ell.init(store='./logdir')  # Versions your LMPs and their calls
4
5# ... define your lmps
6
7hello("strawberry") # the source code of the LMP the call is saved to the store

1.3 监控、版本控制和可视化工具ell studio #

ell提供了监控、版本控制和可视化工具(Tools for monitoring, versioning, and visualization)。

对LLM的每一次调用都很重要, 所以要有日志的跟踪监控

1ell-studio --storage-dir ./logdir

有了合适的工具,提示工程从一门神秘的艺术转变为一门科学。Ell Studio是一个本地、开源的工具,用于Prompt的版本控制、监控、可视化。使用Ell Studio,您可以在实践中不断优化提示效果。

1.4 测试时的计算很重要 #

测试时的计算很重要(Test-time compute is important)

在将一个语言模型的演示转化为实际应用时,我们经常需要多次调用这个模型。ell这个工具通过把问题分解成更小的、更易于管理的部分,使得我们在模型使用过程中,能够灵活地调整和优化,从而提高模型的性能。

想象一下,你想要让LLM做很多事情。一开始,你可能会给它一些简单的指令,看看它能不能完成。但是,当你想让它做更复杂的事情时,你可能需要给它很多条指令,而且这些指令之间还有一些联系。ell就相当于一个工具箱,它能帮你把这些复杂的指令拆分成一个个小指令,然后按顺序执行。这样一来,你就可以更轻松地让LLM完成复杂的任务,而且还能够随时调整这些指令,让LLM做得更好。

 1import ell
 2@ell.simple(model="gpt-4o-mini", temperature=1.0, n=10)
 3def write_ten_drafts(idea : str):
 4  """You are an adept story writer. The story should only be 3 paragraphs"""
 5  return f"Write a story about {idea}."
 6
 7@ell.simple(model="gpt-4o", temperature=0.1)
 8def choose_the_best_draft(drafts : List[str]):
 9  """You are an expert fiction editor."""
10  return f"Choose the best draft from the following list: {'\n'.join(drafts)}."
11
12drafts = write_ten_drafts(idea)
13
14best_draft = choose_the_best_draft(drafts) # Best of 10 sampling.

除了存储每个LMP的源代码之外,ell还可选地将对LLM的每次调用保存到本地。这允许你生成调用数据集、按版本比较LMP输出,并充分利用提示工程的所有相关产物。

1.5 根据需要选择复杂性或简单性 #

使用LLM通常只需要传递字符串,但有时并非如此:

 1import ell
 2
 3@ell.tool()
 4def scrape_website(url : str):
 5   return requests.get(url).text
 6
 7@ell.complex(model="gpt-5-omni", tools=[scrape_website])
 8def get_news_story(topic : str):
 9   return [
10      ell.system("""Use the web to find a news story about the topic"""),
11      ell.user(f"Find a news story about {topic}.")
12   ]
13
14message_response = get_news_story("stock market")
15if message_response.tool_calls:
16   for tool_call in message_response.tool_calls:
17      #...
18if message_response.text:
19   print(message_response.text)
20if message_response.audio:
21   # message_response.play_audio() supprot for multimodal outputs will work as soon as the LLM supports it
22   pass

使用@ell.simple会让LMP生成简单的字符串输出。但当需要更复杂或多模态的输出时,可以使用@ell.complex,让语言模型返回Message对象的响应。

1.6 多模态是一等公民 #

LLM可以处理和生成各种类型的文本、图像、音频和视频等内容。针对各种类型的输出进行提示工程就像提示生成文本一样简单。

 1from PIL import Image
 2import ell
 3
 4
 5@ell.simple(model="gpt-4o", temperature=0.1)
 6def describe_activity(image: Image.Image):
 7   return [
 8      ell.system("You are VisionGPT. Answer <5 words all lower case."),
 9      ell.user(["Describe what the person in the image is doing:", image])
10   ]
11
12# Capture an image from the webcam
13describe_activity(capture_webcam_image()) # "they are holding a book"

ell支持丰富的多模态输入和输出类型转换。可以将PIL图像、音频和其他多模态输入内嵌在LMP返回的Message对象中。

1.7 提示工程库不应干扰您的工作流程 #

ell被设计为一个轻量级且不显眼的库。它不需要我们更的编码风格或使用特殊的编辑器。

我们可以继续在的IDE中使用常规Python代码来定义和修改的提示,同时利用ell的功能来可视化和分析提示。可以一次一个函数地从langchain迁移到ell。

2.开始使用ell #

2.1 安装 #

ellell studio都包含在ell-ai中。

1poetry add ell-ai -vv

pyproject.toml:

1[tool.poetry.dependencies]
2python = "^3.11"
3ell-ai = "^0.0.3"

2.2 设置环境变量OPENAI_API_KEYOPENAI_BASE_URL #

OPENAI_API_KEYOPENAI_BASE_URL放到.env文件中:

1OPENAI_API_KEY=<your key>
2OPENAI_BASE_URL=<your base url>

2.3 创建第一个大语言模型程序(LMP) #

在创建一个ell的LMP之前,先看一下使用OpenAI chat completions API的代码:

 1from dotenv import load_dotenv
 2import os
 3from openai import OpenAI
 4
 5assert load_dotenv()
 6assert os.environ.get("OPENAI_BASE_URL")
 7assert os.environ.get("OPENAI_API_KEY")
 8
 9messages = [
10    {
11        "role": "system",
12        "content": """你是用户的生活助理-小智。
13识别用户的意图并用简洁的语言给出合适的回应。
14        """,
15    },
16    {"role": "user", "content": "我想明天早上5点起床"},
17]
18
19client = OpenAI()
20
21response = client.chat.completions.create(
22    model="gpt-4o-mini", messages=messages, temperature=0.9, max_tokens=1000
23)
24
25print(response.choices[0].message.content)

运行上面的程序可能的输出如下:

1好的,明天早上5点起床。建议你今晚早点睡,确保充足的休息!需要我设置闹钟提醒吗?

现在,让我们看看如何使用 ell 来实现相同的结果:

 1from dotenv import load_dotenv
 2import os
 3from openai import OpenAI
 4import ell
 5
 6assert load_dotenv()
 7assert os.environ.get("OPENAI_BASE_URL")
 8assert os.environ.get("OPENAI_API_KEY")
 9
10client = OpenAI()
11
12
13@ell.simple(model="gpt-4o-mini", client=client, temperature=0.9, max_tokens=1000)
14def reconize_intent(user_input: str):
15    """你是用户的生活助理-小智。
16    识别用户的意图并用简洁的语言给出合适的回应。
17    """  # System prompt
18    return user_input  # User prompt
19
20
21content = reconize_intent("我想明天早上5点起床")
22print(content)

运行上面的程序可能的输出如下:

1好的,建议你今晚早点睡,设定一个闹钟在5点。确保把手机放在能听到的地方哦!

ell通过将提示定义为函数单元来简化提示。在此示例中,reconize_intent函数通过文档字符串定义系统提示,并通过返回字符串定义用户提示。

最终的的提示可以简单地使用的参数调用该函数,而不是手动构建消息。这种使提示更具可读性、可维护性和可重用性。

@ell.simple装饰器是ell中的一个关键概念。它将一个常规的Python函数转换为一个大语言模型程序(LMP):

  • 函数的文档字符串成为系统消息
  • 函数的返回值成为用户消息
  • 装饰器处理API调用并以字符串形式返回模型的响应

这种封装允许更简洁、更可重用的代码。可以像调用任何其他Python 函数一样调用您LMP。

可以启用ell的verbose模式,这样可以了解后台的细节。启用verbose模式只需要在开始调用ell.init(verbose=True)

1ell.init(verbose=True)

启用verbose后,可以看到有关的LLM调用的输入和输出的详细信息。

 1╔══════════════════════════════════════════════════════════════════════════════════════╗
 2║ reconize_intent(我想明天早上5点..)
 3╠══════════════════════════════════════════════════════════════════════════════════════╣
 4║ Prompt:
 5╟──────────────────────────────────────────────────────────────────────────────────────╢
 6│      system: 你是用户的生活助理-小智。
 7│                  识别用户的意图并用简洁的语言给出合适的回应。
 8 9│        user: 我想明天早上5点起床
10╟──────────────────────────────────────────────────────────────────────────────────────╢
11║ Output:
12╟──────────────────────────────────────────────────────────────────────────────────────╢
13│   assistant: 好的,建议你今晚早点睡,这样明天早上能更好地起床。要我帮你设定闹钟吗?
14╚══════════════════════════════════════════════════════════════════════════════════════╝
15好的,建议你今晚早点睡,这样明天早上能更好地起床。要我帮你设定闹钟吗?

2.4 Prompt的版本控制 #

ell为LMP提供强大的版本控制功能。要启用此功能,只需在代码开始的地方添加以下代码:

1ell.init(store='./logdir', autocommit=True, verbose=True)

这行代码时将在 ./logdir 目录中设置一个存储库并启用自动提交。ell将在./logdir/ell.db中存储所有的提示及其版本。

启用版本控制生成./logdir/ell.db后,可以使用ell-studio来查看提示。在终端中,运行下面的命令:

1ell-studio --storage-dir ./logdir

服务默认监听本机8080端口,在浏览器打开可以看到下面的界面:

091305

点击inocations还可以看到调用历史:

091306

让我们接下来修改一下reconize_intent这个LMP:

 1from dotenv import load_dotenv
 2import os
 3from openai import OpenAI
 4import ell
 5
 6assert load_dotenv()
 7assert os.environ.get("OPENAI_BASE_URL")
 8assert os.environ.get("OPENAI_API_KEY")
 9
10client = OpenAI()
11
12
13@ell.simple(model="gpt-4o-mini", client=client, temperature=0.9, max_tokens=1000)
14def reconize_intent(user_input: str):
15    """你是用户的生活助理-小智。
16    识别用户的意图并用简洁的语言给出合适的回应。
17    回应的格式是一个json数组,每个对象json包含字段intent和parameters
18    如果无法识别用户的意图, json对象中intent字段的值固定为unknown, 并将user_input字段设置为用户的输入
19    """  # System prompt
20    return user_input  # User prompt
21
22
23ell.init(store="./logdir", autocommit=True, verbose=True)
24content = reconize_intent("我想明天早上5点起床")
25print(content)

运行上面的程序可能的输出如下:

1[
2    {
3        "intent": "set_alarm",
4        "parameters": {
5            "time": "5:00 AM"
6        }
7    }
8]

此时再次查看ell studio,可以看到reconize_intent这个LMP更新到了Version 2:

091307

注意

当前ell在做版本控制时,写入的commit messages使用了其内部LMP的write_commit_message_for_diff,这个LMP默认使用的gpt-4o-mini,即commit message也利用了大模型的能力。 前面使用load_dotenv()文件加载.env中的环境变量,发现目前无法对write_commit_message_for_diff生效,会报"ERROR: No API key found for model gpt-4o-mini used by LMP write_commit_message_for_diff using client"的错误。 目前的解决方法是在运行程序前使用export OPENAI_BASE_URL=<base url>export OPENAI_API_KEY=<api key>来手动将环境变量设置上

3.总结 #

ell的目前还是一个新的框架, API还不够全面和稳定,一些特殊场景未必能很好满足,未来增加和改动的可能性比较大。

还不建议将其用到生产环境,建议个人本地学习或在一些小项目上尝尝鲜。

参考链接 #

© 2025 青蛙小白 | 总访问量 | 总访客数