使用 Instructor + Burr 的抽认卡生成器¶
抽认卡有助于分解复杂主题,学习从生物学到新语言或戏剧台词的任何内容。这篇博客将展示如何使用 LLM 生成抽认卡,开启你的学习之旅!
Instructor 使我们能够可靠地从 LLM 获取结构化输出,而 Burr 有助于创建易于理解和调试的 LLM 应用。它附带 Burr UI,一个免费、开源、本地优先的工具,用于可观测性、标注等!
信息
本文是对之前一篇博文的扩展:使用 Instructor 分析 YouTube 字幕。
使用 Instructor 通过 LLM 生成抽认卡¶
1. 定义 LLM 响应模型¶
使用 instructor
,你可以定义 Pydantic 模型,这些模型将作为 LLM 填充的模板。
在这里,我们定义了 QuestionAnswer
模型,它将存储问题、答案和一些元数据。没有默认值的属性将由 LLM 生成。
import uuid
from pydantic import BaseModel, Field
from pydantic.json_schema import SkipJsonSchema
class QuestionAnswer(BaseModel):
question: str = Field(description="Question about the topic")
options: list[str] = Field(
description="Potential answers to the question.", min_items=3, max_items=5
)
answer_index: int = Field(
description="Index of the correct answer options (starting from 0).", ge=0, lt=5
)
difficulty: int = Field(
description="Difficulty of this question from 1 to 5, 5 being the most difficult.",
gt=0,
le=5,
)
youtube_url: SkipJsonSchema[str | None] = None
id: uuid.UUID = Field(description="Unique identifier", default_factory=uuid.uuid4)
这个例子展示了 instructor
的几个特性
Field
可以有一个default
或default_factory
值,以防止 LLM 虚构值id
生成唯一的 id (uuid
)
- 类型注解
SkipJsonSchema
也阻止 LLM 生成该值。youtube_url
在应用中通过编程设置。我们不希望 LLM 虚构它。
Field
可以对 LLM 生成的内容设置约束。min_items=3, max_items=5
将潜在答案的数量限制在 3 到 5 之间ge=0, lt=5
将难度限制在 0 到 5 之间,其中 5 是最难的
2. 获取 YouTube 字幕¶
我们使用 youtube-transcript-api
来获取视频的完整字幕。
from youtube_transcript_api import YouTubeTranscriptApi
youtube_url = "https://www.youtube.com/watch?v=hqutVJyd3TI"
_, _, video_id = youtube_url.partition("?v=")
segments = YouTubeTranscriptApi.get_transcript(video_id)
transcript = " ".join([s["text"] for s in segments])
3. 生成问答对¶
现在,生成问答对
- 通过包装 OpenAI 客户端创建一个
instructor
客户端 - 在
instructor_client
上使用.create_iterable()
从输入生成多个输出 - 指定
response_model=QuestionAnswer
以确保输出是QuestionAnswer
对象 - 使用
messages
通过system
消息传递任务指令,并通过user
消息传递输入字幕。
import instructor
import openai
instructor_client = instructor.from_openai(openai.OpenAI())
system_prompt = """Analyze the given YouTube transcript and generate question-answer pairs
to help study and understand the topic better. Please rate all questions from 1 to 5
based on their difficulty."""
response = instructor_client.chat.completions.create_iterable(
model="gpt-4o-mini",
response_model=QuestionAnswer,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": transcript},
],
)
这将返回一个生成器,你可以迭代它来访问单个 QuestionAnswer
对象。
print("Preview:\n")
count = 0
for qna in response:
if count > 2:
break
print(qna.question)
print(qna.options)
print()
count += 1
"""
Preview:
What is the primary purpose of the new OpenTelemetry instrumentation released with Burr?
['To reduce code complexity', 'To provide full instrumentation without changing code', 'To couple the project with OpenAI', 'To enhance customer support']
What do you need to install to use the OpenTelemetry instrumentation with Burr applications?
['Only OpenAI package', 'Specific OpenTelemetry instrumentation module', 'All available packages', 'No installation needed']
What advantage does OpenTelemetry provide in the context of instrumentation?
['It is vendor agnostic', 'It requires complex integration', 'It relies on specific vendors', 'It makes applications slower']
"""
使用 Burr 创建抽认卡应用¶
Burr 使用 actions
和 transitions
来定义复杂应用,同时保持流程图的简洁性,便于理解和调试。
1. 定义 actions
¶
Actions 是你的应用可以执行的操作。@action
装饰器指定可以从 State
读取或写入哪些值。装饰后的函数将 State
作为第一个参数,并返回一个更新后的 State
对象。
接下来,我们定义三个 actions
- 处理用户输入以获取 YouTube URL
- 获取与该 URL 相关的 YouTube 字幕
- 为字幕生成问答对
请注意,这只是对之前代码片段的轻微重构。
from burr.core import action, State
@action(reads=[], writes=["youtube_url"])
def process_user_input(state: State, user_input: str) -> State:
"""Process user input and update the YouTube URL."""
youtube_url = (
user_input # In practice, we would have more complex validation logic.
)
return state.update(youtube_url=youtube_url)
@action(reads=["youtube_url"], writes=["transcript"])
def get_youtube_transcript(state: State) -> State:
"""Get the official YouTube transcript for a video given it's URL"""
youtube_url = state["youtube_url"]
_, _, video_id = youtube_url.partition("?v=")
transcript = YouTubeTranscriptApi.get_transcript(video_id)
full_transcript = " ".join([entry["text"] for entry in transcript])
# store the transcript in state
return state.update(transcript=full_transcript, youtube_url=youtube_url)
@action(reads=["transcript", "youtube_url"], writes=["question_answers"])
def generate_question_and_answers(state: State) -> State:
"""Generate `QuestionAnswer` from a YouTube transcript using an LLM."""
# read the transcript from state
transcript = state["transcript"]
youtube_url = state["youtube_url"]
# create the instructor client
instructor_client = instructor.from_openai(openai.OpenAI())
system_prompt = (
"Analyze the given YouTube transcript and generate question-answer pairs"
" to help study and understand the topic better. Please rate all questions from 1 to 5"
" based on their difficulty."
)
response = instructor_client.chat.completions.create_iterable(
model="gpt-4o-mini",
response_model=QuestionAnswer,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": transcript},
],
)
# iterate over QuestionAnswer, add the `youtube_url`, and append to state
for qna in response:
qna.youtube_url = youtube_url
# `State` is immutable, so `.append()` returns a new object with the appended value
state = state.append(question_answers=qna)
return state
2. 构建 Application
¶
要创建 Burr Application
,我们使用 ApplicationBuilder
对象。
至少需要
- 使用
.with_actions()
定义所有可能的 actions。只需传递用@action
装饰的函数。 - 使用
.with_transitions()
定义 actions 之间的可能转换。这是通过元组(from_action, to_action)
完成的。 - 使用
.with_entrypoint()
指定首先运行哪个 action。
from burr.core import ApplicationBuilder
app = (
ApplicationBuilder()
.with_actions(
process_user_input,
get_youtube_transcript,
generate_question_and_answers,
)
.with_transitions(
("process_user_input", "get_youtube_transcript"),
("get_youtube_transcript", "generate_question_and_answers"),
("generate_question_and_answers", "process_user_input"),
)
.with_entrypoint("process_user_input")
.build()
)
app.visualize()
你随时可以可视化应用图以了解逻辑流。
3. 启动应用¶
使用 Application.run()
将使应用执行 actions 直到达到停止条件。在这种情况下,我们在 process_user_input
之前停止,以便从用户获取 YouTube URL。
方法 .run()
返回一个元组 (action_name, result, state)
。在这种情况下,我们只使用 state 来检查生成的问答对。
action_name, result, state = app.run(
halt_before=["process_user_input"],
inputs={"user_input": "https://www.youtube.com/watch?v=hqutVJyd3TI"},
)
print(state["question_answers"][0])
你可以在 while
循环中使用 .run()
创建一个简单的本地体验
while True:
user_input = input("Enter a YouTube URL (q to quit): ")
if user_input.lower() == "q":
break
action_name, result, state = app.run(
halt_before=["process_user_input"],
inputs={"user_input": user_input},
)
print(f"{len(state['question_answers'])} question-answer pairs generated")
下一步¶
既然你了解了如何使用 Instructor 获取可靠的 LLM 输出以及使用 Burr 构建应用结构,那么根据你的目标,有很多途径可以探索!
1. 构建复杂智能体¶
Instructor 通过提供结构来改进 LLM 的推理能力。嵌套模型和添加约束可以在几行代码中实现 获取带引用的事实 或 提取知识图谱。此外,重试 使 LLM 能够自我纠正。
Burr 定义了用户、LLMs 和系统其余部分之间的边界。你可以在转换中添加 Condition
来创建易于理解的复杂工作流。
2. 将 Burr 添加到你的产品中¶
你的 Burr Application
是一个轻量级的 Python 对象。你可以在 notebook、脚本、Web 应用(如 Streamlit, Gradio 等)或作为 Web 服务(例如 FastAPI)中运行它。
ApplicationBuilder
提供了许多用于将应用生产化的特性
- 持久化:保存和恢复
State
(例如,存储对话历史记录) - 可观测性:记录和监控应用遥测数据(例如,LLM 调用、使用的 token 数量、错误和重试)
- 流式处理和异步:通过流式传输 LLM 响应和异步运行 actions 来创建流畅的用户界面。
例如,你可以用几行代码将遥测数据记录到 Burr UI 中。首先,对 OpenAI 库进行插桩。然后,在 ApplicationBuilder
中添加 .with_tracker()
,指定项目名称并启用 use_otel_tracing=True
。
from burr.core import ApplicationBuilder
from opentelemetry.instrumentation.openai import OpenAIApiInstrumentor
# instrument before importing instructor or creating the OpenAI client
OpenAIApiInstrumentor().instrument()
app = (
ApplicationBuilder()
.with_actions(
process_user_input,
get_youtube_transcript,
generate_question_and_answers,
)
.with_transitions(
("process_user_input", "get_youtube_transcript"),
("get_youtube_transcript", "generate_question_and_answers"),
("generate_question_and_answers", "process_user_input"),
)
.with_tracker(project="youtube-qna", use_otel_tracing=True)
.with_entrypoint("process_user_input")
.build()
)
使用 Instructor 进行 OpenAI API 调用的遥测数据。我们看到提示、响应模型和响应内容。
3. 标注应用日志¶
Burr UI 内置了标注工具,允许你对记录的数据(例如,用户输入、LLM 响应、为 RAG 检索的内容)进行标注、评分或评论。这对于创建测试用例和评估数据集非常有用。
结论¶
我们展示了 Instructor 如何帮助从 LLM 获取可靠的输出,以及 Burr 如何提供构建应用的正确工具。现在轮到你开始构建了!