|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
import json
|
|
|
import random
|
|
|
from dataclasses import dataclass
|
|
|
+from itertools import product
|
|
|
from pathlib import Path
|
|
|
from typing import Any
|
|
|
|
|
|
@@ -22,8 +23,8 @@ DEFAULT_ACTIVE_FAMILY_KEYS = ("f1", "f2")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
class QueryBuildOptions:
|
|
|
- per: int = 30
|
|
|
- batch_n: int = 30
|
|
|
+ per: int = 0
|
|
|
+ batch_n: int = 0
|
|
|
seed: int = 7
|
|
|
dry: bool = False
|
|
|
active_family_keys: tuple[str, ...] = DEFAULT_ACTIVE_FAMILY_KEYS
|
|
|
@@ -52,7 +53,7 @@ def _leaves(idx: list[dict[str, Any]], source_type: str, under: str | None = Non
|
|
|
return out
|
|
|
|
|
|
|
|
|
-def _nonleaf(
|
|
|
+def _nodes_at_depth(
|
|
|
idx: list[dict[str, Any]],
|
|
|
source_type: str,
|
|
|
depths: tuple[int, ...] = (3, 4),
|
|
|
@@ -61,21 +62,24 @@ def _nonleaf(
|
|
|
paths = [(_segs(n.get("path")), n.get("name")) for n in idx if n.get("source_type") == source_type]
|
|
|
if under:
|
|
|
paths = [(s, nm) for s, nm in paths if under in s]
|
|
|
- all_paths = {"/".join(s) for s, _ in paths}
|
|
|
out: list[str] = []
|
|
|
seen: set[str] = set()
|
|
|
for segs, name in paths:
|
|
|
if len(segs) not in depths:
|
|
|
continue
|
|
|
- full = "/".join(segs)
|
|
|
- is_nonleaf = any(other != full and other.startswith(full + "/") for other in all_paths)
|
|
|
value = name or (segs[-1] if segs else "")
|
|
|
- if is_nonleaf and value and value not in seen:
|
|
|
+ if value and value not in seen:
|
|
|
seen.add(value)
|
|
|
out.append(value)
|
|
|
return out
|
|
|
|
|
|
|
|
|
+def _sample_axis(values: list[str], limit: int, rng: random.Random) -> list[str]:
|
|
|
+ if limit <= 0 or limit >= len(values):
|
|
|
+ return values
|
|
|
+ return rng.sample(values, limit)
|
|
|
+
|
|
|
+
|
|
|
def build_creation_query_batch(
|
|
|
settings: Settings,
|
|
|
*,
|
|
|
@@ -86,20 +90,34 @@ def build_creation_query_batch(
|
|
|
opts = options or QueryBuildOptions()
|
|
|
rng = random.Random(opts.seed)
|
|
|
idx = json.loads(tree_path.read_text("utf-8"))
|
|
|
- shi = _nonleaf(idx, "实质", depths=(3, 4), under="理念")
|
|
|
- xing = _nonleaf(idx, "形式", depths=(3, 4), under="架构")
|
|
|
+ shi = _nodes_at_depth(idx, "实质", depths=(3, 4))
|
|
|
+ xing = _nodes_at_depth(idx, "形式", depths=(3, 4))
|
|
|
purpose_pool = _leaves(idx, "作用") + _leaves(idx, "感受") + _leaves(idx, "意图")
|
|
|
- shi_batch = rng.sample(shi, min(opts.batch_n, len(shi)))
|
|
|
- xing_batch = rng.sample(xing, min(opts.batch_n, len(xing)))
|
|
|
- purpose_batch = rng.sample(purpose_pool, min(opts.batch_n, len(purpose_pool)))
|
|
|
+ shi_batch = _sample_axis(shi, opts.batch_n, rng)
|
|
|
+ xing_batch = _sample_axis(xing, opts.batch_n, rng)
|
|
|
+ purpose_batch = _sample_axis(purpose_pool, opts.batch_n, rng)
|
|
|
f6_zy = _nonleaf_d4("作用", 10, tree_path=tree_path)
|
|
|
f6_stage_act = [(s, a) for s in STAGES for a in ACTIONS] + [("", "")]
|
|
|
|
|
|
if not shi_batch or not xing_batch or not purpose_batch or not f6_zy:
|
|
|
raise RuntimeError("scope tree does not contain enough creation query axes")
|
|
|
|
|
|
+ product_pools = {
|
|
|
+ "实质": shi_batch,
|
|
|
+ "形式": xing_batch,
|
|
|
+ "目的": purpose_batch,
|
|
|
+ "模态": MODALITY,
|
|
|
+ "业务阶段": INTENT,
|
|
|
+ "知识类型": KTYPE,
|
|
|
+ }
|
|
|
+
|
|
|
+ def gen_product(keys: list[str]):
|
|
|
+ for values in product(*(product_pools[key] for key in keys)):
|
|
|
+ yield {"parts": dict(zip(keys, values, strict=True))}
|
|
|
+
|
|
|
+ legacy_per = opts.per if opts.per > 0 else 30
|
|
|
master: list[dict[str, str]] = []
|
|
|
- for i in range(opts.per):
|
|
|
+ for i in range(legacy_per):
|
|
|
stage, action = f6_stage_act[i % len(f6_stage_act)]
|
|
|
master.append(
|
|
|
{
|
|
|
@@ -117,10 +135,6 @@ def build_creation_query_batch(
|
|
|
}
|
|
|
)
|
|
|
|
|
|
- def project(i: int, keys: list[str]) -> dict[str, Any]:
|
|
|
- row = master[i]
|
|
|
- return {"parts": {key: row[key] for key in keys}}
|
|
|
-
|
|
|
def gen_old(
|
|
|
i: int,
|
|
|
*,
|
|
|
@@ -152,11 +166,11 @@ def build_creation_query_batch(
|
|
|
return {"parts": parts, "query": query}
|
|
|
|
|
|
families = [
|
|
|
- {"key": "f1", "axes": ["实质", "模态", "业务阶段", "知识类型"], "gen": lambda i: project(i, ["实质", "模态", "业务阶段", "知识类型"])},
|
|
|
- {"key": "f2", "axes": ["形式", "模态", "业务阶段", "知识类型"], "gen": lambda i: project(i, ["形式", "模态", "业务阶段", "知识类型"])},
|
|
|
- {"key": "f4", "axes": ["作用/感受/意图", "模态", "业务阶段", "知识类型"], "gen": lambda i: project(i, ["目的", "模态", "业务阶段", "知识类型"])},
|
|
|
- {"key": "f3", "axes": ["实质", "形式", "模态", "业务阶段", "知识类型"], "gen": lambda i: project(i, ["实质", "形式", "模态", "业务阶段", "知识类型"])},
|
|
|
- {"key": "f5", "axes": ["模态", "业务阶段", "知识类型"], "gen": lambda i: project(i, ["模态", "业务阶段", "知识类型"])},
|
|
|
+ {"key": "f1", "axes": ["实质", "模态", "业务阶段", "知识类型"], "items": lambda: gen_product(["实质", "模态", "业务阶段", "知识类型"])},
|
|
|
+ {"key": "f2", "axes": ["形式", "模态", "业务阶段", "知识类型"], "items": lambda: gen_product(["形式", "模态", "业务阶段", "知识类型"])},
|
|
|
+ {"key": "f4", "axes": ["作用/感受/意图", "模态", "业务阶段", "知识类型"], "items": lambda: gen_product(["目的", "模态", "业务阶段", "知识类型"])},
|
|
|
+ {"key": "f3", "axes": ["实质", "形式", "模态", "业务阶段", "知识类型"], "items": lambda: gen_product(["实质", "形式", "模态", "业务阶段", "知识类型"])},
|
|
|
+ {"key": "f5", "axes": ["模态", "业务阶段", "知识类型"], "items": lambda: gen_product(["模态", "业务阶段", "知识类型"])},
|
|
|
{"key": "a_shi", "axes": ["实质", "阶段", "动作", "作用", "知识类型"], "gen": lambda i: gen_old(i, shi_axis=True)},
|
|
|
{"key": "a_xing", "axes": ["形式", "阶段", "动作", "作用", "知识类型"], "gen": lambda i: gen_old(i, xing_axis=True)},
|
|
|
{"key": "a_both", "axes": ["实质", "形式", "阶段", "动作", "作用", "知识类型"], "gen": lambda i: gen_old(i, shi_axis=True, xing_axis=True)},
|
|
|
@@ -202,14 +216,16 @@ def build_creation_query_batch(
|
|
|
name = " × ".join(family["axes"])
|
|
|
seen: set[str] = set()
|
|
|
items: list[dict[str, Any]] = []
|
|
|
- for i in range(opts.per):
|
|
|
- generated = family["gen"](i)
|
|
|
+ generated_items = family["items"]() if "items" in family else (family["gen"](i) for i in range(legacy_per))
|
|
|
+ for generated in generated_items:
|
|
|
parts = generated["parts"]
|
|
|
query = generated.get("query") or " ".join(parts[k] for k in order if k in parts)
|
|
|
if query in seen:
|
|
|
continue
|
|
|
seen.add(query)
|
|
|
items.append({"query": query, "parts": parts})
|
|
|
+ if opts.per > 0 and len(items) >= opts.per:
|
|
|
+ break
|
|
|
verdicts = (
|
|
|
[
|
|
|
{"keep": True, "valid": None, "relevant": True, "reason": "(dry:未过筛)"}
|