eduevidence 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +506 -0
  3. package/README.zh-CN.md +434 -0
  4. package/SKILL.md +195 -0
  5. package/bin/eduevidence.js +127 -0
  6. package/domains/education/manifest.json +183 -0
  7. package/domains/education/outcome_taxonomy.json +127 -0
  8. package/domains/manifest.json +26 -0
  9. package/domains/policy/frame.schema.json +234 -0
  10. package/domains/policy/manifest.json +10 -0
  11. package/domains/policy/methodology_checklist.json +109 -0
  12. package/domains/policy/outcome_taxonomy.json +53 -0
  13. package/domains/policy/references/causal-identification.md +45 -0
  14. package/domains/policy/references/cost-evidence.md +44 -0
  15. package/domains/policy/references/equity.md +42 -0
  16. package/domains/policy/references/evidence-hierarchy.md +41 -0
  17. package/domains/policy/references/implementation-evidence.md +47 -0
  18. package/eduevidence_cli.py +26 -0
  19. package/engine/__init__.py +11 -0
  20. package/engine/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/engine/__pycache__/analysis.cpython-312.pyc +0 -0
  22. package/engine/__pycache__/bias.cpython-312.pyc +0 -0
  23. package/engine/__pycache__/briefs.cpython-312.pyc +0 -0
  24. package/engine/__pycache__/capabilities.cpython-312.pyc +0 -0
  25. package/engine/__pycache__/citation_check.cpython-312.pyc +0 -0
  26. package/engine/__pycache__/contracts.cpython-312.pyc +0 -0
  27. package/engine/__pycache__/datasets.cpython-312.pyc +0 -0
  28. package/engine/__pycache__/events.cpython-312.pyc +0 -0
  29. package/engine/__pycache__/evidence_graph.cpython-312.pyc +0 -0
  30. package/engine/__pycache__/evidence_review.cpython-312.pyc +0 -0
  31. package/engine/__pycache__/evidencecore.cpython-312.pyc +0 -0
  32. package/engine/__pycache__/gap_lens.cpython-312.pyc +0 -0
  33. package/engine/__pycache__/gaps.cpython-312.pyc +0 -0
  34. package/engine/__pycache__/graph_store.cpython-312.pyc +0 -0
  35. package/engine/__pycache__/graph_validate.cpython-312.pyc +0 -0
  36. package/engine/__pycache__/ids.cpython-312.pyc +0 -0
  37. package/engine/__pycache__/library.cpython-312.pyc +0 -0
  38. package/engine/__pycache__/library_builtin.cpython-312.pyc +0 -0
  39. package/engine/__pycache__/living.cpython-312.pyc +0 -0
  40. package/engine/__pycache__/log.cpython-312.pyc +0 -0
  41. package/engine/__pycache__/meta_analysis.cpython-312.pyc +0 -0
  42. package/engine/__pycache__/meta_synthesis.cpython-312.pyc +0 -0
  43. package/engine/__pycache__/migration.cpython-312.pyc +0 -0
  44. package/engine/__pycache__/mode_router.cpython-312.pyc +0 -0
  45. package/engine/__pycache__/paths.cpython-312.pyc +0 -0
  46. package/engine/__pycache__/pilot.cpython-312.pyc +0 -0
  47. package/engine/__pycache__/planner.cpython-312.pyc +0 -0
  48. package/engine/__pycache__/project.cpython-312.pyc +0 -0
  49. package/engine/__pycache__/projections.cpython-312.pyc +0 -0
  50. package/engine/__pycache__/robustness.cpython-312.pyc +0 -0
  51. package/engine/__pycache__/run.cpython-312.pyc +0 -0
  52. package/engine/__pycache__/semantics.cpython-312.pyc +0 -0
  53. package/engine/__pycache__/study_design.cpython-312.pyc +0 -0
  54. package/engine/__pycache__/synthesis.cpython-312.pyc +0 -0
  55. package/engine/__pycache__/tribunal.cpython-312.pyc +0 -0
  56. package/engine/__pycache__/update.cpython-312.pyc +0 -0
  57. package/engine/__pycache__/versions.cpython-312.pyc +0 -0
  58. package/engine/analysis.py +308 -0
  59. package/engine/bias.py +178 -0
  60. package/engine/briefs.py +106 -0
  61. package/engine/capabilities.py +99 -0
  62. package/engine/citation_check.py +192 -0
  63. package/engine/contracts.py +117 -0
  64. package/engine/datasets.py +165 -0
  65. package/engine/events.py +67 -0
  66. package/engine/evidence_graph.py +571 -0
  67. package/engine/evidence_review.py +88 -0
  68. package/engine/evidencecore.py +182 -0
  69. package/engine/gap_lens.py +132 -0
  70. package/engine/gaps.py +169 -0
  71. package/engine/graph_store.py +335 -0
  72. package/engine/graph_validate.py +87 -0
  73. package/engine/ids.py +77 -0
  74. package/engine/library.py +268 -0
  75. package/engine/library_builtin.py +301 -0
  76. package/engine/living.py +671 -0
  77. package/engine/log.py +39 -0
  78. package/engine/meta_analysis.py +333 -0
  79. package/engine/meta_synthesis.py +111 -0
  80. package/engine/migration.py +397 -0
  81. package/engine/mode_router.py +72 -0
  82. package/engine/paths.py +15 -0
  83. package/engine/pilot.py +368 -0
  84. package/engine/planner.py +126 -0
  85. package/engine/project.py +118 -0
  86. package/engine/projections.py +240 -0
  87. package/engine/robustness.py +109 -0
  88. package/engine/run.py +85 -0
  89. package/engine/semantics.py +135 -0
  90. package/engine/study_design.py +87 -0
  91. package/engine/synthesis.py +187 -0
  92. package/engine/tribunal.py +408 -0
  93. package/engine/update.py +113 -0
  94. package/engine/versions.py +12 -0
  95. package/install.sh +510 -0
  96. package/integrations/__init__.py +1 -0
  97. package/integrations/__pycache__/__init__.cpython-312.pyc +0 -0
  98. package/integrations/__pycache__/agent_mcp.cpython-312.pyc +0 -0
  99. package/integrations/__pycache__/smart_web_fetch.cpython-312.pyc +0 -0
  100. package/integrations/agent_mcp.py +856 -0
  101. package/integrations/smart_web_fetch.py +59 -0
  102. package/package.json +50 -0
  103. package/pyproject.toml +55 -0
  104. package/references/applicability-policy.md +88 -0
  105. package/references/education-framing.md +132 -0
  106. package/references/effect_size_formulas.md +35 -0
  107. package/references/evaluation-design.md +111 -0
  108. package/references/evidence-quality.md +79 -0
  109. package/references/grade_framework.md +29 -0
  110. package/references/intervention-design.md +98 -0
  111. package/references/methodology-audit.md +103 -0
  112. package/references/outcome-taxonomy.md +106 -0
  113. package/references/retrieval-protocol.md +142 -0
  114. package/references/skeptic-protocol.md +93 -0
  115. package/references/social_science_pitfalls.md +48 -0
  116. package/references/source-validity.md +140 -0
  117. package/references/tribunal-policy.md +112 -0
  118. package/references/wwc_standards.md +29 -0
  119. package/retrieval/__init__.py +1 -0
  120. package/retrieval/__pycache__/__init__.cpython-312.pyc +0 -0
  121. package/retrieval/__pycache__/corpus_store.cpython-312.pyc +0 -0
  122. package/retrieval/__pycache__/dedupe.cpython-312.pyc +0 -0
  123. package/retrieval/__pycache__/failures.cpython-312.pyc +0 -0
  124. package/retrieval/__pycache__/fetch.cpython-312.pyc +0 -0
  125. package/retrieval/__pycache__/search.cpython-312.pyc +0 -0
  126. package/retrieval/__pycache__/source.cpython-312.pyc +0 -0
  127. package/retrieval/__pycache__/validate.cpython-312.pyc +0 -0
  128. package/retrieval/corpus_store.py +181 -0
  129. package/retrieval/dedupe.py +127 -0
  130. package/retrieval/failures.py +90 -0
  131. package/retrieval/fetch.py +435 -0
  132. package/retrieval/search.py +493 -0
  133. package/retrieval/source.py +160 -0
  134. package/retrieval/validate.py +257 -0
  135. package/schemas/agent-mcp-approval.schema.json +57 -0
  136. package/schemas/chart-spec.schema.json +88 -0
  137. package/schemas/cross-model-review.schema.json +28 -0
  138. package/schemas/education-frame.schema.json +106 -0
  139. package/schemas/evaluation.schema.json +35 -0
  140. package/schemas/evidence.schema.json +81 -0
  141. package/schemas/fetch-result.schema.json +119 -0
  142. package/schemas/intervention.schema.json +46 -0
  143. package/schemas/methodology.schema.json +102 -0
  144. package/schemas/report-result.schema.json +381 -0
  145. package/schemas/report-spec.schema.json +130 -0
  146. package/schemas/source.schema.json +311 -0
  147. package/schemas/v2/analysis-plan.schema.json +28 -0
  148. package/schemas/v2/analysis-run.schema.json +33 -0
  149. package/schemas/v2/claim.schema.json +26 -0
  150. package/schemas/v2/dataset-asset.schema.json +40 -0
  151. package/schemas/v2/decision-snapshot.schema.json +53 -0
  152. package/schemas/v2/evidence-link.schema.json +38 -0
  153. package/schemas/v2/finding.schema.json +47 -0
  154. package/schemas/v2/graph-revision.schema.json +30 -0
  155. package/schemas/v2/knowledge-gap.schema.json +40 -0
  156. package/schemas/v2/methodology-audit.schema.json +30 -0
  157. package/schemas/v2/outcome.schema.json +18 -0
  158. package/schemas/v2/project.schema.json +31 -0
  159. package/schemas/v2/research-intent.schema.json +24 -0
  160. package/schemas/v2/run.schema.json +43 -0
  161. package/schemas/v2/source.schema.json +24 -0
  162. package/schemas/v2/study-design.schema.json +67 -0
  163. package/schemas/v2/study.schema.json +37 -0
  164. package/schemas/v3/pilot-outcome.schema.json +132 -0
  165. package/schemas/v3/run-manifest.schema.json +193 -0
  166. package/schemas/v3/synthesis.schema.json +86 -0
  167. package/schemas/v4/drift-report.schema.json +66 -0
  168. package/schemas/v4/evidence-library.schema.json +42 -0
  169. package/schemas/v4/living-subscription.schema.json +55 -0
  170. package/schemas/v4/meta-analysis.schema.json +152 -0
  171. package/schemas/verdict.schema.json +56 -0
  172. package/scripts/__init__.py +0 -0
  173. package/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  174. package/scripts/__pycache__/benchmark.cpython-312.pyc +0 -0
  175. package/scripts/__pycache__/benchmark_evaluator.cpython-312.pyc +0 -0
  176. package/scripts/__pycache__/benchmark_judge.cpython-312.pyc +0 -0
  177. package/scripts/__pycache__/benchmark_routing.cpython-312.pyc +0 -0
  178. package/scripts/__pycache__/benchmark_v2.cpython-312.pyc +0 -0
  179. package/scripts/__pycache__/benchmark_v3.cpython-312.pyc +0 -0
  180. package/scripts/__pycache__/build_result.cpython-312.pyc +0 -0
  181. package/scripts/__pycache__/claim_audit.cpython-312.pyc +0 -0
  182. package/scripts/__pycache__/complexity_gate.cpython-312.pyc +0 -0
  183. package/scripts/__pycache__/compute_confidence.cpython-312.pyc +0 -0
  184. package/scripts/__pycache__/dashboard_server.cpython-312.pyc +0 -0
  185. package/scripts/__pycache__/did_regression.cpython-312.pyc +0 -0
  186. package/scripts/__pycache__/effect_calculator.cpython-312.pyc +0 -0
  187. package/scripts/__pycache__/evidence_matrix.cpython-312.pyc +0 -0
  188. package/scripts/__pycache__/evidence_score.cpython-312.pyc +0 -0
  189. package/scripts/__pycache__/evidence_semantics.cpython-312.pyc +0 -0
  190. package/scripts/__pycache__/fetch_benchmark.cpython-312.pyc +0 -0
  191. package/scripts/__pycache__/lint_report_layout.cpython-312.pyc +0 -0
  192. package/scripts/__pycache__/orchestrator.cpython-312.pyc +0 -0
  193. package/scripts/__pycache__/pre_verdict_gate.cpython-312.pyc +0 -0
  194. package/scripts/__pycache__/recompute_demo_quality.cpython-312.pyc +0 -0
  195. package/scripts/__pycache__/render_report.cpython-312.pyc +0 -0
  196. package/scripts/__pycache__/render_report_html.cpython-312.pyc +0 -0
  197. package/scripts/__pycache__/run_workspace.cpython-312.pyc +0 -0
  198. package/scripts/__pycache__/skill_lint.cpython-312.pyc +0 -0
  199. package/scripts/__pycache__/startup_probe.cpython-312.pyc +0 -0
  200. package/scripts/__pycache__/sync_killer_demo_report.cpython-312.pyc +0 -0
  201. package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.0.2.pyc +0 -0
  202. package/scripts/__pycache__/test_adversarial_empirical.cpython-312-pytest-9.1.1.pyc +0 -0
  203. package/scripts/__pycache__/validate_schema.cpython-312.pyc +0 -0
  204. package/scripts/audit_dois.py +292 -0
  205. package/scripts/bake_pack.sh +37 -0
  206. package/scripts/benchmark.py +183 -0
  207. package/scripts/benchmark_evaluator.py +371 -0
  208. package/scripts/benchmark_judge.py +535 -0
  209. package/scripts/benchmark_routing.py +120 -0
  210. package/scripts/benchmark_v2.py +304 -0
  211. package/scripts/benchmark_v3.py +552 -0
  212. package/scripts/build_esl_artifacts.py +1921 -0
  213. package/scripts/build_evidence_library.py +307 -0
  214. package/scripts/build_killer_demo.py +295 -0
  215. package/scripts/build_result.py +311 -0
  216. package/scripts/check_version_consistency.py +96 -0
  217. package/scripts/citation_check.py +123 -0
  218. package/scripts/claim_audit.py +157 -0
  219. package/scripts/complexity_gate.py +180 -0
  220. package/scripts/compute_confidence.py +176 -0
  221. package/scripts/dashboard_server.py +536 -0
  222. package/scripts/did_regression.py +315 -0
  223. package/scripts/effect_calculator.py +99 -0
  224. package/scripts/enrich_projects_human_and_lieflat.py +315 -0
  225. package/scripts/evidence_matrix.py +129 -0
  226. package/scripts/evidence_score.py +234 -0
  227. package/scripts/evidence_semantics.py +87 -0
  228. package/scripts/fetch_benchmark.py +177 -0
  229. package/scripts/generate_metrics.py +99 -0
  230. package/scripts/generate_new_projects.py +686 -0
  231. package/scripts/generate_promo.py +192 -0
  232. package/scripts/lint_report_layout.py +182 -0
  233. package/scripts/orchestrator.py +1456 -0
  234. package/scripts/pre_verdict_gate.py +513 -0
  235. package/scripts/quickstart.py +121 -0
  236. package/scripts/rebake_all_5themes.py +88 -0
  237. package/scripts/recompute_demo_quality.py +205 -0
  238. package/scripts/render_report.py +270 -0
  239. package/scripts/render_report_html.py +356 -0
  240. package/scripts/retraction_watch.py +110 -0
  241. package/scripts/run_workspace.py +337 -0
  242. package/scripts/serve_web.py +54 -0
  243. package/scripts/skill_lint.py +150 -0
  244. package/scripts/startup_probe.py +265 -0
  245. package/scripts/sync_killer_demo_report.py +270 -0
  246. package/scripts/test_adversarial_empirical.py +541 -0
  247. package/scripts/validate_schema.py +256 -0
  248. package/skill/agents/education-planner.md +80 -0
  249. package/skill/agents/evaluation-designer.md +74 -0
  250. package/skill/agents/evidence-analyst.md +106 -0
  251. package/skill/agents/evidence-judge.md +111 -0
  252. package/skill/agents/evidence-retriever.md +80 -0
  253. package/skill/agents/intervention-designer.md +82 -0
  254. package/skill/agents/method-reviewer.md +104 -0
  255. package/skill/agents/skeptic.md +89 -0
  256. package/skill/sub-skills/aihot-trend-analysis/SKILL.md +31 -0
  257. package/skill/sub-skills/contradiction-analysis/SKILL.md +17 -0
  258. package/skill/sub-skills/data-analysis/SKILL.md +23 -0
  259. package/skill/sub-skills/ethics-review/SKILL.md +25 -0
  260. package/skill/sub-skills/evidence-extraction/SKILL.md +19 -0
  261. package/skill/sub-skills/evidence-review/SKILL.md +18 -0
  262. package/skill/sub-skills/gap-analysis/SKILL.md +25 -0
  263. package/skill/sub-skills/literature-review/SKILL.md +21 -0
  264. package/skill/sub-skills/methodology-audit/SKILL.md +20 -0
  265. package/skill/sub-skills/report-generation/SKILL.md +51 -0
  266. package/skill/sub-skills/research-planning/SKILL.md +21 -0
  267. package/skill/sub-skills/study-design/SKILL.md +16 -0
  268. package/skill/task-briefs/adjudicate.md +17 -0
  269. package/skill/task-briefs/audit.md +15 -0
  270. package/skill/task-briefs/challenge.md +15 -0
  271. package/skill/task-briefs/evaluate.md +13 -0
  272. package/skill/task-briefs/extract.md +16 -0
  273. package/skill/task-briefs/frame.md +17 -0
  274. package/skill/task-briefs/intervene.md +14 -0
  275. package/skill/task-briefs/present.md +16 -0
  276. package/skill/task-briefs/retrieve.md +15 -0
  277. package/visualization/eduevidence-report/assets/base.css +337 -0
  278. package/visualization/eduevidence-report/motion/motion.css +157 -0
  279. package/visualization/eduevidence-report/motion/motion.js +107 -0
  280. package/visualization/eduevidence-report/references/bilingual-style.md +7 -0
  281. package/visualization/eduevidence-report/references/component-catalog.md +145 -0
  282. package/visualization/eduevidence-report/references/evidence-expansion.md +65 -0
  283. package/visualization/eduevidence-report/references/full-report-outline.md +86 -0
  284. package/visualization/eduevidence-report/references/layout-constraints.md +63 -0
  285. package/visualization/eduevidence-report/references/lieflat-composition.md +79 -0
  286. package/visualization/eduevidence-report/references/motion-system.md +31 -0
  287. package/visualization/eduevidence-report/schemas/adapter-envelope.schema.json +22 -0
  288. package/visualization/eduevidence-report/schemas/visual-layout.schema.json +87 -0
  289. package/visualization/eduevidence-report/scripts/__pycache__/adapter_contract.cpython-312.pyc +0 -0
  290. package/visualization/eduevidence-report/scripts/__pycache__/build_artifact_manifest.cpython-312.pyc +0 -0
  291. package/visualization/eduevidence-report/scripts/__pycache__/build_charts.cpython-312.pyc +0 -0
  292. package/visualization/eduevidence-report/scripts/__pycache__/build_figures.cpython-312.pyc +0 -0
  293. package/visualization/eduevidence-report/scripts/__pycache__/build_infographics.cpython-312.pyc +0 -0
  294. package/visualization/eduevidence-report/scripts/__pycache__/build_report.cpython-312.pyc +0 -0
  295. package/visualization/eduevidence-report/scripts/__pycache__/charts_data.cpython-312.pyc +0 -0
  296. package/visualization/eduevidence-report/scripts/__pycache__/lieflat_engine.cpython-312.pyc +0 -0
  297. package/visualization/eduevidence-report/scripts/__pycache__/zh_labels.cpython-312.pyc +0 -0
  298. package/visualization/eduevidence-report/scripts/adapter_contract.py +72 -0
  299. package/visualization/eduevidence-report/scripts/build_artifact_manifest.py +70 -0
  300. package/visualization/eduevidence-report/scripts/build_charts.py +283 -0
  301. package/visualization/eduevidence-report/scripts/build_figures.py +515 -0
  302. package/visualization/eduevidence-report/scripts/build_infographics.py +268 -0
  303. package/visualization/eduevidence-report/scripts/build_report.py +3211 -0
  304. package/visualization/eduevidence-report/scripts/charts_data.py +617 -0
  305. package/visualization/eduevidence-report/scripts/check_mobile_layout.js +220 -0
  306. package/visualization/eduevidence-report/scripts/lieflat_engine.py +852 -0
  307. package/visualization/eduevidence-report/scripts/zh_labels.py +245 -0
  308. package/visualization/eduevidence-report/themes/academic.css +94 -0
  309. package/visualization/eduevidence-report/themes/claude.css +96 -0
  310. package/visualization/eduevidence-report/themes/datalab-dark.css +147 -0
  311. package/visualization/eduevidence-report/themes/datalab.css +151 -0
  312. package/visualization/eduevidence-report/themes/presentation.css +140 -0
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python3
2
+ """complexity_gate.py — Deterministic complexity grading gate (plan section 9, Phase 6).
3
+
4
+ Decides whether an EduEvidence task should run in single-agent mode (S),
5
+ with one independent check (M), or enter the full 8-role workflow (L), and
6
+ whether delegation to Agent MCP is warranted at all.
7
+
8
+ Pure local heuristics — zero tokens, no spawn. The result is a suggestion:
9
+ the main agent's judgment of the actual task always wins ("宁可不拆").
10
+
11
+ Rules (per EduEvidence 实施方案 §9 and agent-mcp 编排 skill):
12
+ - S: single question, single outcome, few sources, no obvious conflict
13
+ -> Frame → Retrieve → Extract → Verify → Answer (0 spawn)
14
+ - M: multiple studies, 2-3 outcomes, partial conflict, one independent check
15
+ -> Primary Analysis + Independent Check (<=2-3 roles)
16
+ - L: multiple outcomes, multiple learner groups, strong conflict,
17
+ needs teaching deployment plan -> full 8-role workflow
18
+
19
+ Do-NOT-delegate list (hit any -> delegate=False):
20
+ quick Q&A, single-source check, formatting-only task, trivial edit,
21
+ strong sequential dependency chain.
22
+
23
+ Usage:
24
+ python scripts/complexity_gate.py --question "..." --depth standard \\
25
+ --target teaching_decision --outcomes retention transfer \\
26
+ --multi-learner --needs-pilot
27
+ """
28
+ from __future__ import annotations
29
+
30
+ import argparse
31
+ import json
32
+ import re
33
+ from typing import Any
34
+
35
+ # Signals that push complexity up (from plan §9 / §13)
36
+ L_SIGNALS = [
37
+ "长期", "迁移", "保持", "retention", "transfer", "长期效果",
38
+ "多学习者", "多群体", "不同学生", "全面部署", "全校", "试点", "pilot",
39
+ "风险", "依赖", "over-reliance", "ai dependency", "冲突", "矛盾",
40
+ ]
41
+ M_SIGNALS = [
42
+ "是否有效", "提高", "影响", "比较", "对比", "vs", "与", "不同研究",
43
+ "证据", "研究", "结论",
44
+ ]
45
+
46
+ # Do-not-delegate triggers
47
+ NO_DELEGATE_SIGNALS = [
48
+ "一句话", "简要", "快速回答", "解释一下", "这个文件", "格式化",
49
+ "重命名", "改个名字", "简单回答",
50
+ ]
51
+
52
+
53
+ def _signal_count(text: str, signals: list[str]) -> int:
54
+ low = text.lower()
55
+ return sum(1 for s in signals if s.lower() in low)
56
+
57
+
58
+ def _count_outcomes(outcomes: list[str] | None) -> int:
59
+ if not outcomes:
60
+ return 0
61
+ return len([o for o in outcomes if o])
62
+
63
+
64
+ def grade(
65
+ question: str,
66
+ *,
67
+ depth: str = "standard",
68
+ target: str = "evidence_review",
69
+ outcomes: list[str] | None = None,
70
+ multi_learner: bool = False,
71
+ needs_pilot: bool = False,
72
+ needs_evaluation: bool = False,
73
+ conflict_hint: bool = False,
74
+ ) -> dict[str, Any]:
75
+ """Grade a task S/M/L and decide whether delegation is warranted.
76
+
77
+ Returns {level, rationale, delegate, suggestion}.
78
+ """
79
+ question = question or ""
80
+ l_score = _signal_count(question, L_SIGNALS)
81
+ m_score = _signal_count(question, M_SIGNALS)
82
+ n_outcomes = _count_outcomes(outcomes)
83
+ no_delegate = _signal_count(question, NO_DELEGATE_SIGNALS) > 0
84
+
85
+ # --- do-not-delegate gate (hit any -> never spawn) ---
86
+ if no_delegate:
87
+ return {
88
+ "level": "S",
89
+ "rationale": "do-not-delegate signal in question (quick/simple/formulaic task)",
90
+ "delegate": False,
91
+ "suggestion": "主 Agent 直接执行,禁止 spawn。",
92
+ }
93
+
94
+ # --- explicit inputs push complexity ---
95
+ score = 0
96
+ reasons: list[str] = []
97
+
98
+ if depth == "deep":
99
+ score += 2
100
+ reasons.append("depth=deep")
101
+ elif depth == "standard":
102
+ score += 1
103
+ reasons.append("depth=standard")
104
+
105
+ if target in ("teaching_decision", "pilot_design", "evaluation_design"):
106
+ score += 1
107
+ reasons.append(f"target={target}")
108
+ if multi_learner:
109
+ score += 1
110
+ reasons.append("multi-learner")
111
+ if needs_pilot or needs_evaluation:
112
+ score += 1
113
+ reasons.append("needs pilot/evaluation")
114
+ if conflict_hint:
115
+ score += 1
116
+ reasons.append("conflict hinted")
117
+ if n_outcomes >= 4:
118
+ score += 2
119
+ reasons.append(f"{n_outcomes} outcomes")
120
+ elif n_outcomes >= 2:
121
+ score += 1
122
+ reasons.append(f"{n_outcomes} outcomes")
123
+
124
+ # --- question-language signals ---
125
+ score += min(2, l_score)
126
+ score += min(1, m_score)
127
+ if l_score:
128
+ reasons.append(f"L-signals x{l_score}")
129
+ if m_score:
130
+ reasons.append(f"M-signals x{m_score}")
131
+
132
+ if score >= 4:
133
+ level = "L"
134
+ elif score >= 2:
135
+ level = "M"
136
+ else:
137
+ level = "S"
138
+
139
+ delegate = level in ("M", "L") and not no_delegate
140
+ suggestion = {
141
+ "S": "Frame → Retrieve → Extract → Verify → Answer(单 Agent 串行,0 spawn)",
142
+ "M": "Primary Analysis + Independent Check(增强模式 ≤2-3 角色)",
143
+ "L": "完整 8 角色工作流(Planner/Retriever/Analyst/Skeptic/Method Reviewer/Judge/Intervention Designer/Evaluation Designer)",
144
+ }[level]
145
+
146
+ rationale = f"complexity score {score} ({', '.join(reasons) if reasons else 'no signals'})"
147
+ return {"level": level, "rationale": rationale, "delegate": delegate, "suggestion": suggestion}
148
+
149
+
150
+ def main() -> int:
151
+ parser = argparse.ArgumentParser(description="EduEvidence deterministic complexity gate")
152
+ parser.add_argument("--question", required=True, help="education question text")
153
+ parser.add_argument("--depth", choices=["quick", "standard", "deep"], default="standard")
154
+ parser.add_argument("--target", choices=["evidence_review", "teaching_decision",
155
+ "pilot_design", "evaluation_design"],
156
+ default="evidence_review")
157
+ parser.add_argument("--outcomes", nargs="*", default=None)
158
+ parser.add_argument("--multi-learner", action="store_true")
159
+ parser.add_argument("--needs-pilot", action="store_true")
160
+ parser.add_argument("--needs-evaluation", action="store_true")
161
+ parser.add_argument("--conflict-hint", action="store_true")
162
+ args = parser.parse_args()
163
+
164
+ result = grade(
165
+ args.question,
166
+ depth=args.depth,
167
+ target=args.target,
168
+ outcomes=args.outcomes,
169
+ multi_learner=args.multi_learner,
170
+ needs_pilot=args.needs_pilot,
171
+ needs_evaluation=args.needs_evaluation,
172
+ conflict_hint=args.conflict_hint,
173
+ )
174
+ print(json.dumps(result, ensure_ascii=False, indent=2))
175
+ return 0
176
+
177
+
178
+ if __name__ == "__main__":
179
+ import sys
180
+ sys.exit(main())
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """compute_confidence.py — Deterministic confidence computation for final verdicts.
3
+
4
+ Pipeline entry for the Evidence Integrity Report (P0-05): reads the raw model
5
+ verdict plus the evidence registry and OVERRIDES the model's confidence values
6
+ with the deterministic rule-based computation, so the final verdict's
7
+ confidence is reproducible and auditable rather than model-generated.
8
+
9
+ Formula (identical policy to evidence_score.confidence, v2 — D-1/D-2):
10
+
11
+ score = 0.30 * Evidence Quality + 0.25 * Consistency + 0.20 * Directness
12
+ + 0.25 * Evidence Count (independent-study weighted)
13
+ - Conflict Penalty - Unsupported Penalty
14
+
15
+ where Evidence Count = min(1.0, independent_studies / 4) — independent_samples
16
+ is reported separately instead of being added to the count term —,
17
+ Consistency is computed over decision_relation (support_adoption /
18
+ oppose_adoption / conditional / neutral) rather than relation_to_claim,
19
+ Conflict Penalty = 0.15 if any evidence opposes adoption, and
20
+ Unsupported Penalty = min(0.20, 0.05 * n_unsupported).
21
+
22
+ IMPORTANT: confidence_score is a rule-based index in [0, 1], NOT a
23
+ probability. It must never be presented as a percentage probability (e.g. "85%
24
+ confidence"); consumers should phrase it as an index/band (High | Moderate |
25
+ Low | Insufficient) with the policy version.
26
+
27
+ Usage:
28
+ python3 scripts/compute_confidence.py --verdict raw_verdict.json \
29
+ --evidence evidence.jsonl --out final_verdict.json
30
+
31
+ Output (final_verdict.json) carries the deterministic fields:
32
+ confidence / confidence_score / confidence_policy_version /
33
+ independent_studies / independent_samples / confidence_breakdown, plus the
34
+ raw model values preserved as raw_model_confidence* for audit comparison.
35
+ """
36
+ from __future__ import annotations
37
+
38
+ import argparse
39
+ import json
40
+ import sys
41
+ from pathlib import Path
42
+
43
+ from evidence_score import (CONFIDENCE_POLICY_VERSION,
44
+ decision_consistency_score, directness_score,
45
+ independent_samples, independent_studies)
46
+ from evidence_semantics import decision_relation
47
+
48
+
49
+ def compute_confidence(evidence_list: list[dict], *, target_outcome: str | None = None) -> dict:
50
+ """Deterministic confidence over an evidence list (independent-study weighted).
51
+
52
+ Returns a breakdown dict plus the final label (High | Moderate | Low |
53
+ Insufficient). This is the P0-05 policy engine; the legacy
54
+ evidence_score.confidence() remains for backward-compatible callers.
55
+ """
56
+ if not evidence_list:
57
+ return {"confidence": "Insufficient",
58
+ "confidence_policy_version": CONFIDENCE_POLICY_VERSION,
59
+ "independent_studies": 0,
60
+ "independent_samples": 0,
61
+ "confidence_breakdown": {"evidence_count": 0}}
62
+
63
+ # 1. Evidence Quality (mean of quality scores, scaled to 0-1)
64
+ quality_values = [e.get("quality_score") for e in evidence_list]
65
+ numeric = [q for q in quality_values if isinstance(q, (int, float))]
66
+ avg_quality = sum(numeric) / len(numeric) if numeric else 0.0
67
+ quality_term = avg_quality / 10.0
68
+
69
+ # 2. Consistency (decision_relation based, D-2): claim-level evidence is
70
+ # usually extracted to support its claim, so relation_to_claim would
71
+ # overstate agreement about the final teaching decision.
72
+ decisions = [decision_relation(e) for e in evidence_list]
73
+ consistency = decision_consistency_score(decisions)
74
+
75
+ # 3. Directness (0-2 -> 0-1)
76
+ directness = directness_score(evidence_list) / 2.0
77
+
78
+ # 4. Evidence count weighted by independent studies only (D-1): a typical
79
+ # study is 1 study + 1 sample, so adding independent_samples would count
80
+ # the same study twice. independent_samples is reported separately.
81
+ studies = independent_studies(evidence_list)
82
+ samples = independent_samples(evidence_list)
83
+ count_term = min(1.0, studies / 4.0)
84
+
85
+ # 5. Conflict penalty (0.15 when any evidence opposes adoption)
86
+ conflict_penalty = 0.15 if "oppose_adoption" in decisions else 0.0
87
+
88
+ # 6. Unsupported penalty (capped at 0.20)
89
+ unsupported = [e for e in evidence_list if e.get("status") in ("UNSUPPORTED", "DOWNGRADE_CONFIDENCE")]
90
+ unsupported_penalty = min(0.20, len(unsupported) * 0.05)
91
+
92
+ score = (0.30 * quality_term + 0.25 * consistency + 0.20 * directness
93
+ + 0.25 * count_term - conflict_penalty - unsupported_penalty)
94
+ score = max(0.0, min(1.0, score))
95
+
96
+ if target_outcome:
97
+ relevant = [e for e in evidence_list if e.get("outcome_type") == target_outcome]
98
+ if relevant:
99
+ score *= 0.5 + 0.5 * (len(relevant) / max(1, len(evidence_list)))
100
+
101
+ label = _confidence_label(score)
102
+ return {
103
+ "confidence": label,
104
+ "confidence_policy_version": CONFIDENCE_POLICY_VERSION,
105
+ "independent_studies": studies,
106
+ "independent_samples": samples,
107
+ "confidence_breakdown": {
108
+ "score": round(score, 3),
109
+ "evidence_quality": round(quality_term, 3),
110
+ "consistency": round(consistency, 3),
111
+ "directness": round(directness, 3),
112
+ "evidence_count": len(evidence_list),
113
+ "independent_studies": studies,
114
+ "independent_samples": samples,
115
+ "count_term": round(count_term, 3),
116
+ "conflict_penalty": conflict_penalty,
117
+ "unsupported_penalty": round(unsupported_penalty, 3),
118
+ },
119
+ }
120
+
121
+
122
+ def _confidence_label(score: float) -> str:
123
+ if score >= 0.72:
124
+ return "High"
125
+ if score >= 0.45:
126
+ return "Moderate"
127
+ if score >= 0.2:
128
+ return "Low"
129
+ return "Insufficient"
130
+
131
+
132
+ def main() -> int:
133
+ parser = argparse.ArgumentParser(
134
+ description="Compute deterministic confidence and merge it into the final verdict")
135
+ parser.add_argument("--verdict", required=True, help="Raw (model) verdict JSON file")
136
+ parser.add_argument("--evidence", required=True, help="Evidence JSONL file")
137
+ parser.add_argument("--out", required=True, help="Output final verdict JSON path")
138
+ args = parser.parse_args()
139
+
140
+ raw_verdict = json.loads(Path(args.verdict).read_text(encoding="utf-8"))
141
+ evidence_list = []
142
+ for lineno, line in enumerate(Path(args.evidence).read_text(encoding="utf-8").splitlines(), 1):
143
+ line = line.strip()
144
+ if not line:
145
+ continue
146
+ try:
147
+ evidence_list.append(json.loads(line))
148
+ except json.JSONDecodeError as exc:
149
+ print(f"{args.evidence}:{lineno}: invalid JSON line: {exc}", file=sys.stderr)
150
+ return 1
151
+
152
+ target_outcome = raw_verdict.get("target_outcome")
153
+ computed = compute_confidence(evidence_list, target_outcome=target_outcome)
154
+
155
+ # Override model-generated confidence fields with the deterministic values.
156
+ final = dict(raw_verdict)
157
+ final["confidence"] = computed["confidence"]
158
+ final["confidence_score"] = computed["confidence_breakdown"].get("score")
159
+ final["confidence_policy_version"] = computed["confidence_policy_version"]
160
+ final["independent_studies"] = computed["independent_studies"]
161
+ final["independent_samples"] = computed["independent_samples"]
162
+ final["confidence_breakdown"] = computed["confidence_breakdown"]
163
+ # 保留原始模型输出,供审计比对(模型值被覆盖而非丢弃)。
164
+ final["raw_model_confidence"] = raw_verdict.get("confidence")
165
+ final["raw_model_confidence_breakdown"] = raw_verdict.get("confidence_breakdown") or {}
166
+
167
+ out_path = Path(args.out)
168
+ out_path.write_text(json.dumps(final, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
169
+ print(f"confidence={computed['confidence']} score={computed['confidence_breakdown']['score']} "
170
+ f"studies={computed['independent_studies']} samples={computed['independent_samples']} "
171
+ f"policy={CONFIDENCE_POLICY_VERSION} -> {out_path}")
172
+ return 0
173
+
174
+
175
+ if __name__ == "__main__":
176
+ sys.exit(main())