〇门槛 快速上手

一分钟速成提示工程!

直接 pip install 安装 🌟

PyPI 安装:

pip install promplate[openai]

你也可以用任何你喜欢的源(比如以清华源为例):

pip install promplate[openai] -i https://pypi.tuna.tsinghua.edu.cn/simple

我们只是用 OpenAI 来做演示。实际上,你可以使用任何你想要的LLM

让LLM生成文本!OpenAI 启动!

首先,打开一个python REPL 💻 (或者ipython或者jupyter都可以。任何你喜欢的python终端都可以

下面的所有代码都可以直接运行,只要你是一行行复制粘贴过去的,都可以正常得出结果。

>>> from promplate.llm.openai import ChatComplete  # 这个类只是对OpenAI的官方SDK的封装而已
>>> complete = ChatComplete(api_key="...")

api_key 填你的 API Key 就行

然后像这样调用:

>>> complete("你好", model="gpt-4o-mini")
'你好!有什么可以帮助你的吗?'

你可能懒得每次调用 complete 的时候都要填写 model 参数,那你可以把参数绑定到 complete 上面:

>>> complete = ChatComplete(api_key="...").bind(model="gpt-4o-mini")

然后只需用一个字符串来调用它:

>>> complete("你是谁")
'我是一位聊天机器人,可以回答你的问题和进行对话。有什么可以帮助你的吗?'
😭😭 如果你没有 API Key 我有个教育目的的免费代理站,你不用 API Key 也能免费使用。像这样填写 base_url 字段即可:
>>> from promplate.llm.openai import ChatComplete
>>> complete = ChatComplete(base_url="https://promplate.dev", api_key="").bind(model="gpt-4o-mini")
>>> complete("随便说点什么")
'今天的天气真是美好,阳光明媚,让人心情也变得愉快起来。希望每个人都能在这样的日子里感受到幸福和快乐。无论遇到什么困难和挑战,都要坚持积极向前,相信明天会更好。愿大家都拥有一个美好的一天!'
如果你想使用 instruct 模型 🤔 只需简单地将 ChatComplete 替换为 TextComplete
>>> from promplate.llm.openai import TextComplete
>>> complete = TextComplete(api_key="...").bind(model="gpt-3.5-turbo-instruct")
>>> complete("我是")
'人也像你\n缘起在那个年代,'
当调用 Complete 实例时,你可以传递参数,比如 temperature,也可以在初始化时修改参数:
>>> complete("1 + 1 = ", temperature=0, max_tokens=1)
'2'
想迭代流式响应也很简单 👀 只需使用 ChatGenerate,然后用 for 循环去迭代它:
>>> from promplate.llm.openai import ChatGenerate
>>> generate = ChatGenerate(api_key="...").bind(model="gpt-4o-mini")
>>> for i in generate("为什么 1 + 1 = 2"):
...     print(i, end="", flush=True)  # 这会逐渐打印生成的token
...
1 + 1 = 2 是数学中的基本原理,这个等式符合加法运算规则。当我们将一个物体与另一个相同的物体放在一起时,我们就会有两个物体。所以,1个物体加上另一个物体等于2个物体。这是数学中的基本概念之一。

模板!如何使用 Promplate 的模板表示动态的 prompt

在你的提示中必然有一些动态的内容,比如用户输入的问题从数据库中检索到的数据网上搜索的结果等。 在 promplate 中,只需使用 {{ }} 就可以插入动态数据。

>>> import time
>>> from promplate import Template
>>> greet = Template("结合当前时间向我请安。现在是 {{ time.asctime() }}")
>>> greet.render(locals())
'结合当前时间向我请安。现在是 Sat Feb 24 19:57:36 2024'

借助刚刚创建的 complete 对象来执行它:

>>> complete(_)  # _ 在 REPL 中就是上一个表达式的值
'您好!现在是2024年2月24日周六晚上7点58分50秒,祝您身体健康,心情愉快!有什么可以帮助您的吗?祝您一切顺利!'

可以看到,它根据我们的当前时间给出了合适的问候语。

实际上,你可以在 {{ }} 里面使用任何python表达式。

问题有点复杂?分解成子问题逐个解决!

有时候需要组合多个 prompt 来完成任务:

  • 如果任务比较复杂,可能很难在一个 prompt 中描述清楚
  • 将大任务分解成小任务分别执行有可能可以减少 token 消耗量
  • 如果你要让模型输出结构化的数据(通过XML或者JSON),往往先生成正常结果再
  • 将任务分解成部分来完成是符合人类直觉的
  • 将大任务分解成子任务可能会增强可解释性,debug 时更容易定位错误

prompate 中,我们使用 Node 来表示一个单一的”任务”。你可以用一个字符串来初始化一个”任务”:

>>> from promplate import Node
>>> greet = Node("{{ time.asctime() }} 是上午还是下午?请用英文回答。", locals())
>>> greet.render()  # 你已经在这个 Node 初始化时传递了上下文
'Sat Feb 24 19:59:41 2024 是上午还是下午?请用英文回答。'

一个 Node 就是一个加了许多实用功能Template

像魔法一样,你可以直接将两个节点相加,表示先后连接的顺序:

>>> translate = Node('将 """{{ __result__ }}""" 翻译成 {{ target_language }}')
>>> chain = greet + translate  # 这表示了"用中文打招呼"的流程

然后像这样调用:

>>> chain.invoke({"target_language": "zh_CN"}, complete)
>>> _.result
'早上好!'
一些细节

注意 .run() 的返回类型是 ChainContext,这是一个 dict 的子类,但是比 dict 多了一些东西:

  • __result__ 键在 .run() 时被自动分配为上一个 Node 的输出,你可以在模板中用 {{ __result__ }} 访问它;
  • 在模板外部,你可以使用 ChainContext.result 来获取最后的输出。

以下三个表达式应该返回相同的字符串:

>>> template = Template("...")
>>> complete(template.render())
>>> Node("...").invoke()["__result__"]
>>> Node("...").invoke().result

这部分可能有点难理解。但这些都是为了这个框架的灵活性。其实,等你的工程逐渐变大了,你会发现这种封装就是最佳实践。

Agent = LLM + Tools! 给 Chain 注册回调!

有些事情LLM做不了,更适合用代码来完成。例如:

  • LLM只会返回字符串,但我们有时需要将其解析为结构化的数据格式,如 dictlist
  • Chain 默认只保留最新的 __result__,而可能也需要保留下来中间的 Node 的输出
  • 后一个 Node 可能不能直接使用前一个 Node 的结果,而是需要进行一些处理

promplate 中,你可以在 LLM 运行前后分别注册一个或多个回调,作为”预处理”和”后处理”

除了手动实现 Callback 接口,你可以直接用装饰器语法来注册回调:

>>> @greet.end_process
... @translate.end_process
... def log_greet_result(context):
...     print(context.result)
...
>>> chain.invoke({"target_language": "zh_CN"}, complete)
It is in the afternoon.
现在是下午。
ChainContext({'target_language': 'zh_CN', '__result__': '现在是下午。'})

上面这是一个将第一个 Node 的结果 print 出来的回调。事实上,你可以在回调中随意读取和写入 context


恭喜你 🎉 你已经学会了使用 promplate 进行提示工程的基本范式。感谢你的阅读!还有很多没有提到的特性在这个速成教程中没有提到。你可以前往其它页面了解更多 🤗 有任何问题,都欢迎在 GitHub Discussions 提问!