跳转至

Self-Discovery:先选推理策略,再解题

有些失败不是能力问题,而是策略问题。

旅游助手接到"带老人的雨天少步行美食之旅"——它应该先分解约束?先验证路线可行性?先类比以前的成功案例?如果选错了策略,答案从一开始就走偏了。

Self-Discovery 让模型先从一个策略库里挑选合适的推理模块,然后按选定的模块解题。

一句话说清楚

Self-Discovery 把"直接解题"改成"先从策略库选模块、再按模块解题",让模型在动手之前先承诺一个方法。

解决什么问题

问题 表面看起来 实际风险
直接回答 用了错误的策略
策略全写在 prompt 里 全面 模型可能无视
策略库太大 灵活 选择变成随机

旅游助手收到一个复杂请求时,如果 prompt 里塞了 10 条策略("先查天气""先查预算""先分解约束"...),模型很可能只用其中一两条,剩下的形同虚设。Self-Discovery 强制模型在一个显式的"选择"步骤里做出承诺。

引入什么复杂度

  • 需要维护一个策略模块库(module library)。
  • 模块如果太抽象("逻辑思考""仔细分析"),选了也没用。
  • 模型可能选了模块但解题时不用——需要在每个模块绑定检查点。

和其他模式的关系

模式 谁决定下一步 什么时候用
Plan & Solve 模型写步骤 任务需要拆步骤
Self-Discovery 模型选策略模块 问题在于选错方法
LATS 搜索控制器比较候选 多个方案需要评分
Routing 路由选外部流程 不同输入走不同 pipeline

Self-Discovery 和 Routing 有相似之处——都是"先选再做"。区别是 Routing 选的是外部流程(由代码实现),Self-Discovery 选的是推理策略(由模型内部执行)。

这个模式改变了什么

负责什么
模块库(Python) 提供可选策略列表
模型(选择步骤) 从库中选出本次任务要用的模块
Python 校验模块名合法
模型(解题步骤) 按选定模块解题
  • 谁决定下一步:两阶段——先选模块,再解题。
  • 谁拥有状态:Python 持有可用模块列表和被选中的模块列表。
  • 什么时候停止:解题步骤返回答案。
  • trace 记录什么self_discovery.select(选了哪些模块)、self_discovery.solve

走一遍真实轨迹

用户请求:"帮我规划杭州老人雨天行程,少步行,以美食为主。"

可用模块库:["decompose_constraints", "verify_route", "analogy", "check_walking_load", "weather_first"]

阶段 输出
选模块 ["decompose_constraints", "check_walking_load", "weather_first"]
校验 三个名称都在合法列表里
解题 先按 weather_first 检查天气 → 按 decompose_constraints 分解约束(老人、雨天、少步行、美食)→ 按 check_walking_load 验证步行量 → 输出路线

如果模型没有 Self-Discovery,它可能直接输出一条步行量大的路线,根本不检查"少步行"这个约束。

流程图

flowchart TD
  T["用户任务"] --> L["读模块库"]
  L --> S["模型选模块"]
  S --> V["Python 校验模块名"]
  V --> A["模型按选定模块解题"]
  A --> O["答案"]

完整实现

from __future__ import annotations
from pathlib import Path
from agent_patterns_lab.patterns.self_discovery import self_discovery
from agent_patterns_lab.runtime import MockLLM, Tracer


def main() -> None:
    tracer = Tracer()

    # 可用模块库
    available_modules = [
        "decompose_constraints",   # 分解约束条件
        "verify_route",            # 验证路线可行性
        "analogy",                 # 类比以前成功案例
        "check_walking_load",      # 检查步行量
        "weather_first",           # 先查天气再规划
    ]

    model = MockLLM(
        [
            # 第一次调用:选模块
            '{"modules":["decompose_constraints","check_walking_load","weather_first"]}',
            # 第二次调用:按模块解题
            (
                "【策略:weather_first】先查天气 → 杭州明天全天小雨。\n"
                "【策略:decompose_constraints】约束分解:\n"
                "  - 老人 → 避免爬坡、台阶\n"
                "  - 雨天 → 室内为主\n"
                "  - 少步行 → 打车或公交\n"
                "  - 美食 → 安排特色餐厅\n"
                "【策略:check_walking_load】步行量检查:\n"
                "  全天步行 < 3000 步 ✓\n\n"
                "推荐行程:\n"
                "上午:知味观(早茶)→ 打车到中国茶叶博物馆\n"
                "中午:龙井村午餐(龙井虾仁)\n"
                "下午:宋城室内演出\n"
                "晚上:外婆家(西湖醋鱼)"
            ),
        ]
    )

    result = self_discovery(
        model,
        task="帮我规划杭州老人雨天行程,少步行,以美食为主。",
        available_modules=available_modules,
        tracer=tracer,
    )

    print("=== 选中的模块 ===")
    print(result.selected_modules)
    print("\n=== 答案 ===")
    print(result.answer)

    trace_path = tracer.export_jsonl(Path(".traces") / "55_self_discovery_zh.jsonl")
    print(f"\n[trace] {trace_path}")


if __name__ == "__main__":
    main()

运行:

PYTHONPATH=src python examples/55_self_discovery.py

完整运行记录

=== 选中的模块 ===
['decompose_constraints', 'check_walking_load', 'weather_first']

=== 答案 ===
【策略:weather_first】先查天气 → 杭州明天全天小雨。
【策略:decompose_constraints】约束分解:
  - 老人 → 避免爬坡、台阶
  - 雨天 → 室内为主
  - 少步行 → 打车或公交
  - 美食 → 安排特色餐厅
【策略:check_walking_load】步行量检查:
  全天步行 < 3000 步 ✓

推荐行程:
上午:知味观(早茶)→ 打车到中国茶叶博物馆
中午:龙井村午餐(龙井虾仁)
下午:宋城室内演出
晚上:外婆家(西湖醋鱼)

[trace] .traces/55_self_discovery_zh.jsonl

trace 事件:

self_discovery.select  modules=["decompose_constraints","check_walking_load","weather_first"]
self_discovery.solve

踩坑与诊断

现象 trace 信号 修法
模块是口号 "逻辑思考""仔细分析" 选了但答案没变化 每个模块定义清楚:输入/输出/检查清单
选了但不用 答案里看不到策略痕迹 trace 有 select 但 solve 输出和模块无关 每个模块绑检查点,prompt 要求引用模块名
模块太多 选择漂移 选了 5+ 模块 限制库大小(5-8 个)和最大选择数(3 个)
选错模块 方向一开始就不对 最终答案和约束不匹配 允许 quick check 后重新选择

关键诊断技巧:检查答案里是否有每个选中模块的输出。如果选了 check_walking_load 但答案里没有步行量数字,说明模型"选了但没执行"。

工程备忘

成本公式

模型调用 = 2(选模块 + 解题)

最便宜的规划模式之一。成本和 Plan & Solve 差不多,但解决的问题不同:Plan & Solve 解决"没有计划",Self-Discovery 解决"用错策略"。

生产注意事项

  • 模块库建议 5-8 个,每个模块需要有:名称、描述、输入/输出、检查清单。
  • "analogy"这种抽象模块效果不稳定——具体模块(如 check_walking_load)更可靠。
  • Self-Discovery 可以和其他模式组合:先用 Self-Discovery 选策略,再用 Plan & Solve 执行。
  • 如果模块库需要动态更新(新任务类型出现),考虑把模块库放在检索系统里。

读完以后

Self-Discovery 适合"方法选择比执行更关键"的场景。

  • 如果任务需要具体步骤规划,读 Plan & Solve
  • 如果需要在多个候选之间搜索,读 LATS

参考资料