分析方法定稿(ANALYSIS_METHOD_PROMPT,guide id 升到 analysis.method@2):在第一版六条上写清按问题选基期(环比比上一个整期、同比比去年同期并写 lag、和目标比要库里有目标,没说就比上一个整期,本期取最近一个过完的整期);周序列的期次在 SQL 里由日期列算出来,写每周第一天的日期(附 SQLite 写法)或 ISO 周,SQLite 的 %W、MySQL 的 %u 不是 ISO 周,直接取现成的周号文本本期齐不齐核对不了;先可比性与基线、趋势,再拆因子与结构、按维度由粗到细,找到主要来源再下钻一层;每次写明在验证什么,拆了不集中也是结论;不作因果断言;阈值不由模型定、算不了照实说、调用写错照说明改;最后交给写作者的文字里列出做过的分析(An)和判定原话。测试守这几件事都在、点名的工具是那五个、五个工具共用这一份

This commit is contained in:
ThearchyHelios
2026-10-21 18:08:25 +08:00
parent 040040d23a
commit 5617deb80b
2 changed files with 57 additions and 8 deletions
+10 -8
View File
@@ -22,20 +22,22 @@ from app.tools.registry import ToolContext, ToolGuide, register
CATEGORY = "分析"
#: 「分析方法」:五个分析工具共用,绑了任一个就注入 system(第一版,第 2 波定稿)。改了方法就改 id 里的版本号:评测的
#: 「分析方法」:五个分析工具共用,绑了任一个就注入 system(第 2 波定稿为第二版)。改了方法就改 id 里的版本号:评测的
#: prompt_fingerprint 认得出提示词变了
ANALYSIS_METHOD_PROMPT = """\
# 分析方法(平台内置,用分析工具时照这个做)
1. 先定现象:取本期和问题对应的基期,用 analysis_comparable 确认两期能直接比;历史够长(缺省至少 8 期)就用 analysis_baseline 放回历史看——是常规波动就照实说,不硬找原因。
2. 指标是乘积或比率时,先用 analysis_decompose 的 factor、ratio 模式拆因子、拆结构。
3. 按数据目录里的候选维度由粗到细拆(additive 模式),每次用 headline 给出总数核对拆开后是否闭合;找到主要来源再用 parent 下钻一层:下钻的 SQL 由你写(只查那一段),系统按上一层那一段的值核对闭合。
4. 每次调用在 hypothesis 里写明这次在验证什么;拆了没发现集中也是结论,照实写。
5. 不作因果断言:数据只说明变化来自哪里;原因要有知识库原话或输入作依据,否则写成待核实的推测。
6. 阈值和方法由平台定,工具参数里没有阈值;算不了时照实说算不了的原因,不要换个说法硬下判断。
1. 先按问题定基期:「这周怎么样」「环比」比上一个整期(上周、上月);「同比」「和去年比」比去年同期(波动基线的 lag 周数据写 52、月数据写 12,历史要相应够长);「和目标比」比库里的目标或预算(目标和实际查进同一份结果,用一列区分两者当期次,label 写清是和目标比;库里没有目标就照实说,不自己定目标)。问题没说时比上一个整期,并写明比的是哪一期。本期取最近一个已经过完的整期:这周没过完,就比上周和上上周。
2. 期次在 SQL 里由日期列算出来:按周汇总写每周第一天的日期(SQLite 写 date(日期列, '-6 days', 'weekday 1')),或 ISO 周「YYYY-Www」——SQLite 的 %W、MySQL 的 %u 算的不是 ISO 周,同一个「2026-W42」落在别的日子上。直接取表里现成的周号文本时,系统认不出它的算法,本期齐不齐核对不了。
3. 先定现象:用 analysis_comparable 确认两期能直接比;历史够长(缺省至少 8 期)就用 analysis_baseline 放回历史看,要看是不是持续在变用 analysis_trend。是常规波动就照实说,不硬找原因。
4. 再拆:指标是乘积(销售额 = 订单数 × 客单价)先用 analysis_decompose 的 factor 拆因子,是比率(退款率、客单价)先用 ratio 拆结构和各段自身的变化;可加的量按数据目录里的候选维度由粗到细用 additive 拆,每次用 headline 给出总数,核对拆开后是否闭合。找到主要来源再用 parent 下钻一层:下钻的 SQL 由你写(只查那一段),系统按上一层那一段的值核对闭合。
5. 每次调用在 hypothesis 里写明这次在验证什么。拆了没发现集中(分散、持平)也是结论,照实写,不换着维度硬凑「主要来自」。两组、两期的差异要说站不站得住时用 analysis_differ。
6. 不作因果断言:数据只说明变化来自哪里,说明不了为什么。原因要有知识库原话或运行输入作依据,否则写成待核实的推测。
7. 阈值和方法由平台定,工具参数里没有阈值。算不了(历史不够、本期不完整、截断、不可比)时照实说原因,不要换个说法硬下判断;调用写错时照交回的说明改了再调。
8. 最后交给写作者的文字里列出做过的分析:「【分析 An】」的编号、分析的是什么、判定(判定语原话)或算不了的原因,没做的分析不要写成做过。
分析结论的编号「【分析 An】」只在本节点有效:下钻时 parent 写它(如 {"of": "A1", "key": ["华东"]})。判定语由系统写好了,结论里照它的意思说,不要改写判断的强弱。"""
METHOD_GUIDE = ToolGuide(id="analysis.method@1", text=ANALYSIS_METHOD_PROMPT)
METHOD_GUIDE = ToolGuide(id="analysis.method@2", text=ANALYSIS_METHOD_PROMPT)
#: 交回 agent 的说法:这个节点里没有可分析的查询容器(调用工具节点、工具库试用、没记台账的老运行)
NO_SCOPE = ("分析工具只能由 Agent 在本节点查过数之后调用:它分析的是本节点交回过的查询结果(【证据 Qn】的编号)。"
@@ -0,0 +1,47 @@
"""平台内置的分析方法(tools/builtin/analysis.py 的 ANALYSIS_METHOD_PROMPT,第 2 波定稿,guide id analysis.method@2)。
方法随代码升版、绑了任一个分析工具就注入 Agent 的 system(注入本身见 tests/test_analysis_tools.py)。这里守方法里该写清的几件事
都在:按问题选基期、周序列的期次怎么写、先现象后拆解再下钻的顺序、写明在验证什么、拆了不集中也是结论、不作因果断言、阈值
不由模型定、算不了照实说、最后列出做过的分析;方法里点名的工具都是真有的那五个。
"""
from __future__ import annotations
import re
from app.analysis import contract
from app.tools.builtin.analysis import ANALYSIS_METHOD_PROMPT, METHOD_GUIDE
from app.tools.registry import all_specs
def test_the_guide_is_the_second_version_and_shared_by_all_five_tools():
assert METHOD_GUIDE.id == "analysis.method@2" and METHOD_GUIDE.text == ANALYSIS_METHOD_PROMPT
specs = all_specs()
assert {name for name, spec in specs.items() if spec.guide is METHOD_GUIDE} == set(contract.TOOL_NAMES.values())
def test_the_method_says_how_to_pick_the_base_period():
first = ANALYSIS_METHOD_PROMPT.split("\n2.")[0]
for words in ("上周", "去年同期", "和目标比", "上一个整期", "lag", "52", "库里没有目标就照实说", "已经过完的整期"):
assert words in first, words
def test_the_method_says_how_to_write_weekly_periods():
for words in ("每周第一天的日期", "ISO 周「YYYY-Www」", "SQLite 的 %W", "不是 ISO 周", "date(日期列, '-6 days', 'weekday 1')",
"核对不了"):
assert words in ANALYSIS_METHOD_PROMPT, words
def test_the_method_orders_the_steps_and_names_only_real_tools():
text = ANALYSIS_METHOD_PROMPT
# 先可比性和基线,再拆因子与结构,再按维度由粗到细,找到主要来源再下钻一层
order = [text.index(w) for w in ("analysis_comparable", "analysis_baseline", "factor", "ratio", "additive", "parent")]
assert order == sorted(order), order
named = set(re.findall(r"\banalysis_[a-z]+\b", text))
assert named == set(contract.TOOL_NAMES.values()), named
assert "由粗到细" in text and "下钻一层" in text and "headline" in text
def test_the_method_keeps_the_round_rules():
for words in ("hypothesis", "拆了没发现集中", "也是结论", "不作因果断言", "待核实的推测", "工具参数里没有阈值",
"算不了", "照实说原因", "【分析 An】", "判定语原话", "没做的分析不要写成做过"):
assert words in ANALYSIS_METHOD_PROMPT, words