跳转至

Few-shot 示例

演示如何通过少量示例引导模型按期望格式与风格输出,包括示例选择、 模板拼接与去重。Few-shot 是提升输出稳定性的常用手段。 对应教程:第4卷《提示工程》第2章 Few-shot 与模板。

文件路径: examples/volume-04-prompt-engineering\few_shot.py


源代码

"""Few-shot 提示示例。

演示如何通过少量示例引导模型按期望格式与风格输出,包括示例选择、
模板拼接与去重。Few-shot 是提升输出稳定性的常用手段。

对应教程:第4卷《提示工程》第2章 Few-shot 与模板。
"""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass(frozen=True)
class Example:
    """单个 few-shot 示例。

    Attributes:
        input: 示例输入。
        output: 期望输出。
    """

    input: str
    output: str


@dataclass
class FewShotTemplate:
    """Few-shot 提示模板。

    Attributes:
        instruction: 任务说明。
        examples: 示例列表。
        input_label: 输入行的前缀标签。
        output_label: 输出行的前缀标签。
    """

    instruction: str
    examples: list[Example] = field(default_factory=list)
    input_label: str = "输入"
    output_label: str = "输出"

    def add_example(self, ex: Example) -> FewShotTemplate:
        """添加示例。

        Args:
            ex: 示例。

        Returns:
            ``self``。
        """
        self.examples.append(ex)
        return self

    def render_example(self, ex: Example) -> str:
        """渲染单个示例为文本。

        Args:
            ex: 示例。

        Returns:
            形如 ``输入: ...\n输出: ...`` 的文本。
        """
        return f"{self.input_label}: {ex.input}\n{self.output_label}: {ex.output}"

    def build(self, query: str) -> str:
        """构建完整 prompt。

        Args:
            query: 当前用户查询。

        Returns:
            拼接好的完整 prompt。
        """
        sections = [self.instruction]
        for ex in self.examples:
            sections.append(self.render_example(ex))
        sections.append(f"{self.input_label}: {query}\n{self.output_label}:")
        return "\n\n".join(sections)


def select_examples_by_keyword(
    pool: list[Example],
    query: str,
    k: int = 2,
) -> list[Example]:
    """根据关键词重叠从示例池中选择最相关的 k 个示例。

    Args:
        pool: 示例池。
        query: 用户查询。
        k: 返回数量。

    Returns:
        按相关度降序排列的至多 k 个示例。
    """
    query_tokens = set(query.lower())
    scored: list[tuple[int, Example]] = []
    for ex in pool:
        overlap = len(query_tokens & set(ex.input.lower()))
        scored.append((overlap, ex))
    scored.sort(key=lambda x: x[0], reverse=True)
    return [ex for _, ex in scored[:k]]


def deduplicate_examples(examples: list[Example]) -> list[Example]:
    """按 (input, output) 去重,保持顺序。

    Args:
        examples: 示例列表。

    Returns:
        去重后的列表。
    """
    seen: set[tuple[str, str]] = set()
    result: list[Example] = []
    for ex in examples:
        key = (ex.input, ex.output)
        if key not in seen:
            seen.add(key)
            result.append(ex)
    return result


def build_sentiment_template() -> FewShotTemplate:
    """构建一个情感分类的 few-shot 模板。

    Returns:
        含正/负/中三个示例的模板。
    """
    return FewShotTemplate(
        instruction="请对用户评论进行情感分类,只输出:正面、负面 或 中性。",
        examples=[
            Example(input="这个产品太好用了,强烈推荐!", output="正面"),
            Example(input="用了两天就坏了,非常失望。", output="负面"),
            Example(input="功能一般,价格还行。", output="中性"),
        ],
    )


def main() -> None:
    """演示 few-shot 提示构建。"""
    template = build_sentiment_template()
    prompt = template.build("质量超出预期,很满意!")
    print(prompt)

    print("\n--- 关键词示例选择 ---")
    pool = [
        Example("退货流程是什么", "请进入订单页点击申请退货。"),
        Example("如何修改密码", "在设置-账号安全中修改密码。"),
        Example("退款多久到账", "退款一般 3-5 个工作日到账。"),
    ]
    for ex in select_examples_by_keyword(pool, "退款什么时候到", k=2):
        print(f"  {ex.input} -> {ex.output}")


if __name__ == "__main__":
    main()