首页 > 教程攻略 > ai资讯 >​认识 PromptPoet人人都在讨论的新提示工程框架

​认识 PromptPoet人人都在讨论的新提示工程框架

来源:互联网 时间:2026-08-26 14:18:21

提示工程,某种程度上说,是件“让人又爱又恨”的事。对于很多计算机科学家而言,这种“输入正确词语,祈祷它产生正确结果”的模式,总显得有些别扭。但不可否认的是,在今天的大语言模型应用里,高质量的提示词是不可或缺的,也因此,我们需要一些趁手的工具和框架来简化这件事。最近,在这个方向上,一个很有意思的新面孔出现了——来自 Character.ai 的 PromptPoet。这家与 Google 达成战略合作的 AI 初创公司,带来了一个旨在设计和集成高质量提示的灵活框架。

​认识 PromptPoet人人都在讨论的新提示工程框架

PromptPoet 的目标很明确:帮助开发者和非技术用户,在不与复杂字符串操作打交道的前提下,高效地设计和管理提示。它的核心思路,是让用户能专注于“如何写出有效的提示”本身,而不是被编码的技术细节所困扰。

这个框架的精髓,一部分在于它对 Python f-strings 的使用。这对很多开发者来说并不陌生,但问题在于,当f-strings变得复杂时,构建一个复杂的提示依然需要大量手动工作。这种复杂性对非技术用户来说,无疑是一道门槛。那么,PromptPoet 是怎么解决这个问题的呢?

一个简单的例子就能说明问题:

- name: system instructions
role: system
content: |
  Your name is {{ character_name }} and you are meant to be helpful and never harmful to humans.

- name: user query
role: user
content: |
  {{ username}}: {{ user_query }}

- name: response
role: user
content: |
  {{ character_name }}:

这里的核心思路是,从 UI 设计中汲取灵感,把提示视为其运行时状态的动态函数——模板、数据、令牌限制这些元素,都成为了它的“构建块”。

提示模板

PromptPoet 真正想做的,是把关注点从“提示工程”转移到“提示设计”上来。用户可以在模板上反复迭代,而不是在代码上反复调试。它采用 YAML 和 Jinja2 的组合,让模板既灵活又易于适应。整体处理分两个阶段:

  • 渲染

    :这个初级阶段由 Jinja2 负责。它处理输入数据,执行控制逻辑,验证数据,绑定变量,并在模板内调用函数。
  • 加载

    :渲染完成后,输出会被组织成 YAML 文件。这个文件由重复的段落构成,每个段落最终都会变成一个 Python 数据结构。每个段落拥有几个关键属性:
    • 名称

      :段落的清晰标识。
    • 内容

      :构成提示核心的字符串内容。
    • 角色(可选)

      :指定参与者的角色,用于区分用户和系统组件。
    • 截断优先级(可选)

      :在需要截断时,定义段落的先后顺序。优先级相同的段落会按顺序被截断。

这个库还提供了不少实用功能,比如分词和截断,这些都是为了提升缓存效率和响应速度。来看几个具体用法:

prompt.tokenize()
prompt.truncate(token_limit=TOKEN_LIMIT, truncation_step=TRUNCATION_STEP)

# 将提示内容输出为纯文本字符串
prompt.string: str
>>>> "..."

# 查看提示的原生 token 序列
prompt.tokens: list[int]
>>>> [...]

# 将提示内容转为 LLM API 所需的消息字典格式
prompt.messages: list[dict]
>>>> [...]

# 将提示内容以段落(PromptPart)的形式展开
prompt.parts: list[PromptPart]
>>>> [...]

模板语言

将 Jinja2 和 YAML 结合起来,效果拔群。Jinja2 允许你在模板内直接绑定数据、调用函数、执行基本的控制流;而 YAML 则负责提供结构,让那些超出 token 限制时的复杂截断成为可能。这种方式,和 Ansible 这类工具中用的思路如出一辙。

提示可移植性

在 Character.AI,模型需要持续迭代,以更好地匹配用户偏好。这就要求提示能在离线环境中被复现,以便进行模型评估和后训练任务。通过模板化提示,团队之间可以无缝共享这些内容,不再需要集成零散的代码段。

模板内的函数调用

Jinja2 一个很值得关注的能力是,能在运行时直接在模板内调用 Python 函数。这对于实时数据检索、操作和验证来说,至关重要。比如,extract_user_query_topic 这个函数可以直接对用户查询进行复杂处理,并用来控制模板的逻辑流:

{% if extract_user_query_topic(user_query) == "homework_help" %}
{% for homework_example in fetch_few_shot_homework_examples(username, character_name) %}
- name: homework_example_{{ loop.index }}
role: user
content: |
  {{ homework_example }}
{% endfor %}
{% endif %}

自定义编码选项

默认情况下,PromptPoet 使用 TikToken 的“o200k_base”分词器。但如果你有特殊需求,也可以通过 tiktoken_encoding_name 参数指定其他编码,或者通过 encode_func: Callable[[str], list[int]] 参数直接提供一个自定义的编码函数。

from tiktoken import get_encoding
encode_func = get_encoding("o200k_base")

prompt = Prompt(
  raw_template=raw_template,
  template_data=template_data,
  encode_func=encode_func
)
prompt.tokenize()
prompt.tokens
>>>> [...]

解释缓存感知截断

聊完模板,再来看另一个关键点:截断策略。在 Character.AI 的实践中,截断的核心目标是通过优化消息截断,来最大化 GPU 的前缀缓存命中率。简单来说,就是通过调整截断步骤和 token 限制,让缓存提示 token 占总提示 token 的比例尽可能高。但这里有个平衡:增加截断步骤,可能会带来更多的 token 被截掉。

传统做法的弊端很明显。

简单截断问题

想象一个包含 M1 到 M10 消息的聊天场景。如果每次对话都简单地在某一轮截断到固定的 token 限制,那么截断点会频繁移动。这直接导致缓存效率降低,计算成本飙升。

缓存感知截断的好处

Character.AI 的方法是,每隔几轮才移动一次截断点,让它稳定在一个位置上。这样,就能保持一条从开头到最新消息的连续 token 序列。最关键的是,这个方案允许复用前几轮已经完成的缓存计算结果,从而大幅提升效率。里面的参数 k,实际上反映了截断步长和平均 token 数量之间的平衡,当然,你无法直接控制它。

总的来说,PromptPoet 现在已经开源,这应该会吸引不少开发者在它的基础上进行构建。至少,它已经把 Character.ai 在提示设计领域积累的经验教训,很好地封装了起来。无论如何,它对提示工程这个领域来说,都是一个让人乐于见到的补充。