psyclaw 0.25.2 → 0.26.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 (271) hide show
  1. package/README.md +111 -60
  2. package/{apps → dist/apps}/panel/index.html +50 -28
  3. package/dist/src/adapters/pi/extension.d.ts +19 -0
  4. package/dist/src/adapters/pi/extension.js +735 -0
  5. package/dist/src/adapters/pi/extension.js.map +1 -0
  6. package/dist/src/adapters/pi/model.d.ts +72 -0
  7. package/dist/src/adapters/pi/model.js +152 -0
  8. package/dist/src/adapters/pi/model.js.map +1 -0
  9. package/dist/src/adapters/pi/resources.d.ts +23 -0
  10. package/dist/src/adapters/pi/resources.js +54 -0
  11. package/dist/src/adapters/pi/resources.js.map +1 -0
  12. package/dist/src/adapters/pi/rpc.d.ts +58 -0
  13. package/dist/src/adapters/pi/rpc.js +253 -0
  14. package/dist/src/adapters/pi/rpc.js.map +1 -0
  15. package/dist/src/adapters/pi/session.d.ts +30 -0
  16. package/dist/src/adapters/pi/session.js +64 -0
  17. package/dist/src/adapters/pi/session.js.map +1 -0
  18. package/dist/src/agents/catalog.d.ts +30 -0
  19. package/dist/src/agents/catalog.js +118 -0
  20. package/dist/src/agents/catalog.js.map +1 -0
  21. package/dist/src/agents/discover.d.ts +28 -0
  22. package/dist/src/agents/discover.js +127 -0
  23. package/dist/src/agents/discover.js.map +1 -0
  24. package/dist/src/agents/import.d.ts +67 -0
  25. package/dist/src/agents/import.js +789 -0
  26. package/dist/src/agents/import.js.map +1 -0
  27. package/dist/src/analysis/hooks.d.ts +81 -0
  28. package/dist/src/analysis/hooks.js +139 -0
  29. package/dist/src/analysis/hooks.js.map +1 -0
  30. package/dist/src/analysis/pre-registration.d.ts +18 -0
  31. package/dist/src/analysis/pre-registration.js +86 -0
  32. package/dist/src/analysis/pre-registration.js.map +1 -0
  33. package/dist/src/branding.d.ts +44 -0
  34. package/dist/src/branding.js +128 -0
  35. package/dist/src/branding.js.map +1 -0
  36. package/dist/src/chat.d.ts +16 -0
  37. package/dist/src/chat.js +95 -0
  38. package/dist/src/chat.js.map +1 -0
  39. package/dist/src/cli.d.ts +2 -0
  40. package/dist/src/cli.js +316 -0
  41. package/dist/src/cli.js.map +1 -0
  42. package/dist/src/core/citations.d.ts +44 -0
  43. package/dist/src/core/citations.js +48 -0
  44. package/dist/src/core/citations.js.map +1 -0
  45. package/dist/src/core/contracts.d.ts +119 -0
  46. package/dist/src/core/contracts.js +2 -0
  47. package/dist/src/core/contracts.js.map +1 -0
  48. package/dist/src/core/docx.d.ts +11 -0
  49. package/dist/src/core/docx.js +46 -0
  50. package/dist/src/core/docx.js.map +1 -0
  51. package/dist/src/core/doi.d.ts +41 -0
  52. package/dist/src/core/doi.js +82 -0
  53. package/dist/src/core/doi.js.map +1 -0
  54. package/dist/src/core/evidence-policy.d.ts +24 -0
  55. package/dist/src/core/evidence-policy.js +308 -0
  56. package/dist/src/core/evidence-policy.js.map +1 -0
  57. package/dist/src/core/filekind.d.ts +15 -0
  58. package/dist/src/core/filekind.js +82 -0
  59. package/dist/src/core/filekind.js.map +1 -0
  60. package/dist/src/core/hash.d.ts +2 -0
  61. package/dist/src/core/hash.js +17 -0
  62. package/dist/src/core/hash.js.map +1 -0
  63. package/dist/src/core/pricing.d.ts +38 -0
  64. package/dist/src/core/pricing.js +110 -0
  65. package/dist/src/core/pricing.js.map +1 -0
  66. package/dist/src/core/redact.d.ts +12 -0
  67. package/dist/src/core/redact.js +48 -0
  68. package/dist/src/core/redact.js.map +1 -0
  69. package/dist/src/core/references.d.ts +55 -0
  70. package/dist/src/core/references.js +102 -0
  71. package/dist/src/core/references.js.map +1 -0
  72. package/dist/src/core/schemas.d.ts +162 -0
  73. package/dist/src/core/schemas.js +160 -0
  74. package/dist/src/core/schemas.js.map +1 -0
  75. package/dist/src/extension.d.ts +1 -0
  76. package/dist/src/extension.js +2 -0
  77. package/dist/src/extension.js.map +1 -0
  78. package/dist/src/index.d.ts +72 -0
  79. package/dist/src/index.js +73 -0
  80. package/dist/src/index.js.map +1 -0
  81. package/dist/src/install/installer.d.ts +147 -0
  82. package/dist/src/install/installer.js +721 -0
  83. package/dist/src/install/installer.js.map +1 -0
  84. package/dist/src/integrations/contracts.d.ts +35 -0
  85. package/dist/src/integrations/contracts.js +27 -0
  86. package/dist/src/integrations/contracts.js.map +1 -0
  87. package/dist/src/integrations/mcp.d.ts +155 -0
  88. package/dist/src/integrations/mcp.js +826 -0
  89. package/dist/src/integrations/mcp.js.map +1 -0
  90. package/dist/src/knowledge-map/contracts.d.ts +20 -0
  91. package/dist/src/knowledge-map/contracts.js +2 -0
  92. package/dist/src/knowledge-map/contracts.js.map +1 -0
  93. package/dist/src/knowledge-map/index.d.ts +4 -0
  94. package/dist/src/knowledge-map/index.js +39 -0
  95. package/dist/src/knowledge-map/index.js.map +1 -0
  96. package/dist/src/literature/contracts.d.ts +106 -0
  97. package/dist/src/literature/contracts.js +2 -0
  98. package/dist/src/literature/contracts.js.map +1 -0
  99. package/dist/src/literature/fulltext.d.ts +7 -0
  100. package/dist/src/literature/fulltext.js +24 -0
  101. package/dist/src/literature/fulltext.js.map +1 -0
  102. package/dist/src/literature/knowledge-map.d.ts +2 -0
  103. package/dist/src/literature/knowledge-map.js +20 -0
  104. package/dist/src/literature/knowledge-map.js.map +1 -0
  105. package/dist/src/literature/ledger.d.ts +3 -0
  106. package/dist/src/literature/ledger.js +17 -0
  107. package/dist/src/literature/ledger.js.map +1 -0
  108. package/dist/src/literature/metadata.d.ts +6 -0
  109. package/dist/src/literature/metadata.js +32 -0
  110. package/dist/src/literature/metadata.js.map +1 -0
  111. package/dist/src/memory/store.d.ts +22 -0
  112. package/dist/src/memory/store.js +73 -0
  113. package/dist/src/memory/store.js.map +1 -0
  114. package/dist/src/orchestration/contracts.d.ts +61 -0
  115. package/dist/src/orchestration/contracts.js +2 -0
  116. package/dist/src/orchestration/contracts.js.map +1 -0
  117. package/dist/src/orchestration/pi-executor.d.ts +28 -0
  118. package/dist/src/orchestration/pi-executor.js +169 -0
  119. package/dist/src/orchestration/pi-executor.js.map +1 -0
  120. package/dist/src/orchestration/runner.d.ts +196 -0
  121. package/dist/src/orchestration/runner.js +991 -0
  122. package/dist/src/orchestration/runner.js.map +1 -0
  123. package/dist/src/orchestration/scheduler.d.ts +8 -0
  124. package/dist/src/orchestration/scheduler.js +335 -0
  125. package/dist/src/orchestration/scheduler.js.map +1 -0
  126. package/dist/src/panel/events.d.ts +20 -0
  127. package/dist/src/panel/events.js +93 -0
  128. package/dist/src/panel/events.js.map +1 -0
  129. package/dist/src/panel/extension.d.ts +8 -0
  130. package/dist/src/panel/extension.js +115 -0
  131. package/dist/src/panel/extension.js.map +1 -0
  132. package/dist/src/panel/literature-map.d.ts +26 -0
  133. package/dist/src/panel/literature-map.js +34 -0
  134. package/dist/src/panel/literature-map.js.map +1 -0
  135. package/dist/src/panel/projection.d.ts +51 -0
  136. package/dist/src/panel/projection.js +288 -0
  137. package/dist/src/panel/projection.js.map +1 -0
  138. package/dist/src/panel/server.d.ts +46 -0
  139. package/dist/src/panel/server.js +1610 -0
  140. package/dist/src/panel/server.js.map +1 -0
  141. package/dist/src/panel/usage.d.ts +53 -0
  142. package/dist/src/panel/usage.js +150 -0
  143. package/dist/src/panel/usage.js.map +1 -0
  144. package/dist/src/project/bootstrap.d.ts +10 -0
  145. package/dist/src/project/bootstrap.js +83 -0
  146. package/dist/src/project/bootstrap.js.map +1 -0
  147. package/dist/src/project/hitl.d.ts +12 -0
  148. package/dist/src/project/hitl.js +43 -0
  149. package/dist/src/project/hitl.js.map +1 -0
  150. package/dist/src/project/jsonl.d.ts +16 -0
  151. package/dist/src/project/jsonl.js +93 -0
  152. package/dist/src/project/jsonl.js.map +1 -0
  153. package/dist/src/project/manuscript.d.ts +21 -0
  154. package/dist/src/project/manuscript.js +74 -0
  155. package/dist/src/project/manuscript.js.map +1 -0
  156. package/dist/src/project/paths.d.ts +38 -0
  157. package/dist/src/project/paths.js +166 -0
  158. package/dist/src/project/paths.js.map +1 -0
  159. package/dist/src/project/versions.d.ts +10 -0
  160. package/dist/src/project/versions.js +14 -0
  161. package/dist/src/project/versions.js.map +1 -0
  162. package/dist/src/psyclaw-theme.d.ts +88 -0
  163. package/dist/src/psyclaw-theme.js +89 -0
  164. package/dist/src/psyclaw-theme.js.map +1 -0
  165. package/dist/src/research/brief.d.ts +11 -0
  166. package/dist/src/research/brief.js +81 -0
  167. package/dist/src/research/brief.js.map +1 -0
  168. package/dist/src/research/ledger.d.ts +12 -0
  169. package/dist/src/research/ledger.js +43 -0
  170. package/dist/src/research/ledger.js.map +1 -0
  171. package/dist/src/run.d.ts +20 -0
  172. package/dist/src/run.js +34 -0
  173. package/dist/src/run.js.map +1 -0
  174. package/dist/src/setup.d.ts +54 -0
  175. package/dist/src/setup.js +221 -0
  176. package/dist/src/setup.js.map +1 -0
  177. package/dist/src/skills/contracts.d.ts +105 -0
  178. package/dist/src/skills/contracts.js +3 -0
  179. package/dist/src/skills/contracts.js.map +1 -0
  180. package/dist/src/skills/preflight.d.ts +10 -0
  181. package/dist/src/skills/preflight.js +39 -0
  182. package/dist/src/skills/preflight.js.map +1 -0
  183. package/dist/src/skills/registry.d.ts +28 -0
  184. package/dist/src/skills/registry.js +540 -0
  185. package/dist/src/skills/registry.js.map +1 -0
  186. package/dist/src/style/artifact.d.ts +6 -0
  187. package/dist/src/style/artifact.js +61 -0
  188. package/dist/src/style/artifact.js.map +1 -0
  189. package/dist/src/style/cli-ui.d.ts +41 -0
  190. package/dist/src/style/cli-ui.js +119 -0
  191. package/dist/src/style/cli-ui.js.map +1 -0
  192. package/dist/src/style/contracts.d.ts +34 -0
  193. package/dist/src/style/contracts.js +2 -0
  194. package/dist/src/style/contracts.js.map +1 -0
  195. package/dist/src/style/registry.d.ts +16 -0
  196. package/dist/src/style/registry.js +50 -0
  197. package/dist/src/style/registry.js.map +1 -0
  198. package/dist/src/trust/manifest.d.ts +42 -0
  199. package/dist/src/trust/manifest.js +53 -0
  200. package/dist/src/trust/manifest.js.map +1 -0
  201. package/dist/src/trust/sbom.d.ts +54 -0
  202. package/dist/src/trust/sbom.js +77 -0
  203. package/dist/src/trust/sbom.js.map +1 -0
  204. package/dist/src/tui/app.d.ts +3 -0
  205. package/dist/src/tui/app.js +103 -0
  206. package/dist/src/tui/app.js.map +1 -0
  207. package/dist/src/tui/model.d.ts +13 -0
  208. package/dist/src/tui/model.js +25 -0
  209. package/dist/src/tui/model.js.map +1 -0
  210. package/dist/src/updates/check.d.ts +43 -0
  211. package/dist/src/updates/check.js +68 -0
  212. package/dist/src/updates/check.js.map +1 -0
  213. package/dist/src/updates/manifest.d.ts +19 -0
  214. package/dist/src/updates/manifest.js +58 -0
  215. package/dist/src/updates/manifest.js.map +1 -0
  216. package/dist/src/updates/registry.d.ts +21 -0
  217. package/dist/src/updates/registry.js +82 -0
  218. package/dist/src/updates/registry.js.map +1 -0
  219. package/dist/src/updates/skills.d.ts +15 -0
  220. package/dist/src/updates/skills.js +39 -0
  221. package/dist/src/updates/skills.js.map +1 -0
  222. package/dist/src/updates/status.d.ts +7 -0
  223. package/dist/src/updates/status.js +37 -0
  224. package/dist/src/updates/status.js.map +1 -0
  225. package/dist/src/updates/update.d.ts +43 -0
  226. package/dist/src/updates/update.js +128 -0
  227. package/dist/src/updates/update.js.map +1 -0
  228. package/dist/src/wizard.d.ts +7 -0
  229. package/dist/src/wizard.js +90 -0
  230. package/dist/src/wizard.js.map +1 -0
  231. package/dist/src/workflows/analysis-delegation.d.ts +3 -0
  232. package/dist/src/workflows/analysis-delegation.js +96 -0
  233. package/dist/src/workflows/analysis-delegation.js.map +1 -0
  234. package/dist/src/workflows/expert-review-runner.d.ts +29 -0
  235. package/dist/src/workflows/expert-review-runner.js +35 -0
  236. package/dist/src/workflows/expert-review-runner.js.map +1 -0
  237. package/dist/src/workflows/expert-review.d.ts +140 -0
  238. package/dist/src/workflows/expert-review.js +111 -0
  239. package/dist/src/workflows/expert-review.js.map +1 -0
  240. package/dist/src/workflows/institutional-fulltext.d.ts +16 -0
  241. package/dist/src/workflows/institutional-fulltext.js +110 -0
  242. package/dist/src/workflows/institutional-fulltext.js.map +1 -0
  243. package/dist/src/workflows/literature-review.d.ts +3 -0
  244. package/dist/src/workflows/literature-review.js +76 -0
  245. package/dist/src/workflows/literature-review.js.map +1 -0
  246. package/dist/src/workflows/meta-analysis.d.ts +38 -0
  247. package/dist/src/workflows/meta-analysis.js +185 -0
  248. package/dist/src/workflows/meta-analysis.js.map +1 -0
  249. package/dist/src/workflows/publish.d.ts +51 -0
  250. package/dist/src/workflows/publish.js +153 -0
  251. package/dist/src/workflows/publish.js.map +1 -0
  252. package/dist/src/workflows/spec.d.ts +51 -0
  253. package/dist/src/workflows/spec.js +157 -0
  254. package/dist/src/workflows/spec.js.map +1 -0
  255. package/dist/src/workflows/style-contract.d.ts +24 -0
  256. package/dist/src/workflows/style-contract.js +23 -0
  257. package/dist/src/workflows/style-contract.js.map +1 -0
  258. package/dist/src/workflows/writing-review.d.ts +15 -0
  259. package/dist/src/workflows/writing-review.js +82 -0
  260. package/dist/src/workflows/writing-review.js.map +1 -0
  261. package/package.json +20 -8
  262. package/scripts/rebrand-pi.mjs +3 -3
  263. package/scripts/__pycache__/build_dist.cpython-314.pyc +0 -0
  264. package/scripts/__pycache__/build_whitepaper_docx.cpython-314.pyc +0 -0
  265. package/scripts/build_preprint_docx.py +0 -729
  266. package/scripts/build_preprint_figures.py +0 -238
  267. package/scripts/build_three_line_table_fixture.py +0 -56
  268. package/scripts/build_whitepaper_docx.py +0 -768
  269. package/scripts/check-branding.mjs +0 -159
  270. package/scripts/copy-panel.mjs +0 -11
  271. package/scripts/verify_docx.py +0 -41
@@ -1,729 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """生成 PsyClaw 预印本的 Word 版(版式对齐 MNE-MCP 那篇:中文正文 + 双语前置 +
4
- APA7 参考文献)。内容来源为 docs/PsyClaw预印本_draft.md 所依据的实测数据;
5
- 本脚本只负责排版。图取自 figures/psyclaw_fig{1,2}_*.png(由
6
- scripts/build_preprint_figures.py 生成)。
7
-
8
- 用法:
9
- UV_DEFAULT_INDEX=https://mirrors.aliyun.com/pypi/simple/ \
10
- uv run --python 3.12 --with python-docx python scripts/build_preprint_docx.py
11
- """
12
- from __future__ import annotations
13
-
14
- import sys
15
- import zipfile
16
- from pathlib import Path
17
-
18
- from docx import Document
19
- from docx.enum.table import WD_TABLE_ALIGNMENT
20
- from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
21
- from docx.oxml import OxmlElement
22
- from docx.oxml.ns import qn
23
- from docx.shared import Cm, Pt, RGBColor
24
-
25
- ROOT = Path(__file__).resolve().parents[1]
26
- FIGDIR = ROOT / "figures"
27
-
28
- CN_BODY = "Songti SC" # 正文中文(宋体系)
29
- CN_HEAD = "Heiti SC" # 标题中文(黑体系)
30
- LATIN = "Times New Roman" # 西文
31
- MONO = "Menlo"
32
- INK = RGBColor(0x1A, 0x1A, 0x1A)
33
- GREY = RGBColor(0x55, 0x55, 0x55)
34
- DXA_PER_CM = 1440 / 2.54
35
-
36
-
37
- # ── 排版助手 ───────────────────────────────────────────────────────────────
38
- def _fonts(run, cn=CN_BODY, latin=LATIN):
39
- """同时设西文字体与 eastAsia 中文字体,避免 Word 里中文回落。"""
40
- run.font.name = latin
41
- rpr = run._element.get_or_add_rPr()
42
- rfonts = rpr.find(qn("w:rFonts"))
43
- if rfonts is None:
44
- rfonts = OxmlElement("w:rFonts")
45
- rpr.append(rfonts)
46
- rfonts.set(qn("w:ascii"), latin)
47
- rfonts.set(qn("w:hAnsi"), latin)
48
- rfonts.set(qn("w:eastAsia"), cn)
49
-
50
-
51
- def _shade(cell, hex_color):
52
- tcPr = cell._tc.get_or_add_tcPr()
53
- shd = OxmlElement("w:shd")
54
- shd.set(qn("w:val"), "clear")
55
- shd.set(qn("w:fill"), hex_color)
56
- tcPr.append(shd)
57
-
58
-
59
- def _cm_to_dxa(value):
60
- return int(round(float(value) * DXA_PER_CM))
61
-
62
-
63
- def _append_or_replace(parent, child):
64
- tag = child.tag
65
- for old in list(parent):
66
- if old.tag == tag:
67
- parent.remove(old)
68
- parent.append(child)
69
-
70
-
71
- def _remove_children(parent, tag):
72
- for old in list(parent):
73
- if old.tag == tag:
74
- parent.remove(old)
75
-
76
-
77
- def _insert_after_last(parent, child, tags):
78
- idx = -1
79
- tag_set = {qn(tag) for tag in tags}
80
- for pos, node in enumerate(list(parent)):
81
- if node.tag in tag_set:
82
- idx = pos
83
- parent.insert(idx + 1, child)
84
-
85
-
86
- def _insert_before_first(parent, child, tags):
87
- tag_set = {qn(tag) for tag in tags}
88
- for pos, node in enumerate(list(parent)):
89
- if node.tag in tag_set:
90
- parent.insert(pos, child)
91
- return
92
- parent.append(child)
93
-
94
-
95
- def _set_row_flags(row, header=False):
96
- tr_pr = row._tr.get_or_add_trPr()
97
- cant_split = OxmlElement("w:cantSplit")
98
- _append_or_replace(tr_pr, cant_split)
99
- if header:
100
- tbl_header = OxmlElement("w:tblHeader")
101
- _append_or_replace(tr_pr, tbl_header)
102
-
103
-
104
- def _set_cell_width(cell, width_dxa):
105
- tc_pr = cell._tc.get_or_add_tcPr()
106
- _remove_children(tc_pr, qn("w:tcW"))
107
- _remove_children(tc_pr, qn("w:tcMar"))
108
- tc_w = OxmlElement("w:tcW")
109
- tc_w.set(qn("w:w"), str(width_dxa))
110
- tc_w.set(qn("w:type"), "dxa")
111
- _insert_after_last(tc_pr, tc_w, ["w:cnfStyle"])
112
- margins = OxmlElement("w:tcMar")
113
- for side, width in (("top", "80"), ("left", "120"), ("bottom", "80"), ("right", "120")):
114
- node = OxmlElement(f"w:{side}")
115
- node.set(qn("w:w"), width)
116
- node.set(qn("w:type"), "dxa")
117
- margins.append(node)
118
- _insert_before_first(tc_pr, margins, ["w:textDirection", "w:tcFitText", "w:vAlign"])
119
-
120
-
121
- def _set_table_fixed_layout(t, width_dxa, col_widths_dxa):
122
- """Apply docx skill table invariants: tblW + tblGrid + matching tcW."""
123
- t.autofit = False
124
- tbl = t._tbl
125
- tbl_pr = tbl.tblPr
126
-
127
- _remove_children(tbl_pr, qn("w:tblW"))
128
- _remove_children(tbl_pr, qn("w:tblLayout"))
129
- _remove_children(tbl_pr, qn("w:tblCellMar"))
130
-
131
- tbl_w = OxmlElement("w:tblW")
132
- tbl_w.set(qn("w:w"), str(width_dxa))
133
- tbl_w.set(qn("w:type"), "dxa")
134
- _insert_after_last(
135
- tbl_pr,
136
- tbl_w,
137
- ["w:tblStyle", "w:tblpPr", "w:tblOverlap", "w:bidiVisual",
138
- "w:tblStyleRowBandSize", "w:tblStyleColBandSize"],
139
- )
140
-
141
- layout = OxmlElement("w:tblLayout")
142
- layout.set(qn("w:type"), "fixed")
143
- _insert_before_first(tbl_pr, layout, ["w:tblCellMar", "w:tblLook"])
144
-
145
- tbl_mar = OxmlElement("w:tblCellMar")
146
- for side, width in (("top", "80"), ("left", "120"), ("bottom", "80"), ("right", "120")):
147
- node = OxmlElement(f"w:{side}")
148
- node.set(qn("w:w"), width)
149
- node.set(qn("w:type"), "dxa")
150
- tbl_mar.append(node)
151
- _insert_before_first(tbl_pr, tbl_mar, ["w:tblLook"])
152
-
153
- for old_grid in tbl.findall(qn("w:tblGrid")):
154
- tbl.remove(old_grid)
155
- grid = OxmlElement("w:tblGrid")
156
- for width in col_widths_dxa:
157
- col = OxmlElement("w:gridCol")
158
- col.set(qn("w:w"), str(width))
159
- grid.append(col)
160
- tbl.insert(1, grid)
161
-
162
- for row_idx, row in enumerate(t.rows):
163
- _set_row_flags(row, header=row_idx == 0)
164
- for col_idx, cell in enumerate(row.cells):
165
- _set_cell_width(cell, col_widths_dxa[col_idx])
166
-
167
-
168
- def P(doc, text="", size=10.5, bold=False, italic=False, color=None,
169
- align=None, space_after=6, first_indent=0.74, line=1.5, cn=CN_BODY):
170
- p = doc.add_paragraph()
171
- pf = p.paragraph_format
172
- pf.space_after = Pt(space_after)
173
- pf.line_spacing = line
174
- if first_indent:
175
- pf.first_line_indent = Cm(first_indent)
176
- if align is not None:
177
- p.alignment = align
178
- if align == WD_ALIGN_PARAGRAPH.CENTER:
179
- pf.first_line_indent = Cm(0)
180
- r = p.add_run(text)
181
- r.font.size = Pt(size)
182
- r.bold = bold
183
- r.italic = italic
184
- if color:
185
- r.font.color.rgb = color
186
- _fonts(r, cn)
187
- return p
188
-
189
-
190
- def H(doc, text, level=1):
191
- p = doc.add_paragraph()
192
- pf = p.paragraph_format
193
- pf.space_before = Pt(16 if level == 1 else 10)
194
- pf.space_after = Pt(6)
195
- pf.line_spacing = 1.3
196
- r = p.add_run(text)
197
- r.font.size = Pt(15 if level == 1 else 12.5)
198
- r.bold = True
199
- r.font.color.rgb = INK
200
- _fonts(r, CN_HEAD)
201
- return p
202
-
203
-
204
- def caption(doc, text):
205
- p = doc.add_paragraph()
206
- p.alignment = WD_ALIGN_PARAGRAPH.CENTER
207
- p.paragraph_format.space_before = Pt(4)
208
- p.paragraph_format.space_after = Pt(10)
209
- p.paragraph_format.line_spacing = 1.3
210
- r = p.add_run(text)
211
- r.font.size = Pt(9.5)
212
- r.font.color.rgb = GREY
213
- _fonts(r, CN_BODY)
214
- return p
215
-
216
-
217
- def figure(doc, path, cap, width_cm=16.0):
218
- p = doc.add_paragraph()
219
- p.alignment = WD_ALIGN_PARAGRAPH.CENTER
220
- p.paragraph_format.space_before = Pt(8)
221
- p.paragraph_format.space_after = Pt(2)
222
- p.add_run().add_picture(str(path), width=Cm(width_cm))
223
- caption(doc, cap)
224
-
225
-
226
- def table(doc, headers, rows, widths=None, fontsize=9.0, caption_text=None):
227
- if caption_text:
228
- cp = doc.add_paragraph()
229
- cp.paragraph_format.space_before = Pt(8)
230
- cp.paragraph_format.space_after = Pt(3)
231
- r = cp.add_run(caption_text)
232
- r.bold = True
233
- r.font.size = Pt(9.5)
234
- _fonts(r, CN_HEAD)
235
- rows = [list(row[:len(headers)]) + [""] * max(0, len(headers) - len(row)) for row in rows]
236
- t = doc.add_table(rows=1, cols=len(headers))
237
- t.style = "Table Grid"
238
- t.alignment = WD_TABLE_ALIGNMENT.CENTER
239
- for i, h in enumerate(headers):
240
- c = t.rows[0].cells[i]
241
- _shade(c, "E8EDF3")
242
- c.paragraphs[0].text = ""
243
- r = c.paragraphs[0].add_run(h)
244
- r.bold = True
245
- r.font.size = Pt(fontsize)
246
- _fonts(r, CN_HEAD)
247
- for row in rows:
248
- cells = t.add_row().cells
249
- for i, v in enumerate(row):
250
- cells[i].paragraphs[0].text = ""
251
- cells[i].paragraphs[0].paragraph_format.line_spacing = 1.2
252
- r = cells[i].paragraphs[0].add_run(str(v))
253
- r.font.size = Pt(fontsize)
254
- _fonts(r, CN_BODY)
255
- if widths:
256
- col_widths_dxa = [_cm_to_dxa(w) for w in widths]
257
- col_widths_dxa[-1] += _cm_to_dxa(sum(widths)) - sum(col_widths_dxa)
258
- else:
259
- table_width_dxa = _cm_to_dxa(16.0)
260
- base = table_width_dxa // len(headers)
261
- col_widths_dxa = [base] * len(headers)
262
- col_widths_dxa[-1] += table_width_dxa - sum(col_widths_dxa)
263
- _set_table_fixed_layout(t, sum(col_widths_dxa), col_widths_dxa)
264
- doc.add_paragraph().paragraph_format.space_after = Pt(2)
265
- return t
266
-
267
-
268
- def reference(doc, text):
269
- """APA7 参考文献条目:悬挂缩进。"""
270
- p = doc.add_paragraph()
271
- pf = p.paragraph_format
272
- pf.left_indent = Cm(0.9)
273
- pf.first_line_indent = Cm(-0.9)
274
- pf.space_after = Pt(6)
275
- pf.line_spacing = 1.3
276
- r = p.add_run(text)
277
- r.font.size = Pt(9.5)
278
- _fonts(r, CN_BODY)
279
- return p
280
-
281
-
282
- def page_break(doc):
283
- doc.add_paragraph().add_run().add_break(WD_BREAK.PAGE)
284
-
285
-
286
- def rule(doc):
287
- p = doc.add_paragraph()
288
- p.alignment = WD_ALIGN_PARAGRAPH.CENTER
289
- r = p.add_run("— · —")
290
- r.font.color.rgb = GREY
291
- _fonts(r, CN_BODY)
292
-
293
-
294
- # ── 正文内容 ───────────────────────────────────────────────────────────────
295
- def front_matter(doc):
296
- P(doc, "PsyClaw:面向社会科学研究的智能体助手",
297
- size=18, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=3, cn=CN_HEAD)
298
- P(doc, "把 Claude Code 的智能体范式引入研究流程,并内建学术诚信核查",
299
- size=11.5, italic=True, color=GREY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=10)
300
- P(doc, "[作者一]¹²,[作者二]¹²,[通讯作者]¹²*", size=11.5,
301
- align=WD_ALIGN_PARAGRAPH.CENTER, space_after=2)
302
- P(doc, "¹ [单位一];² [单位二]", size=10, color=GREY,
303
- align=WD_ALIGN_PARAGRAPH.CENTER, space_after=1)
304
- P(doc, "* 通讯作者:[邮箱]", size=10, color=GREY,
305
- align=WD_ALIGN_PARAGRAPH.CENTER, space_after=14)
306
-
307
- P(doc, "摘要", size=11, bold=True, first_indent=0, space_after=3, cn=CN_HEAD)
308
- P(doc, ABSTRACT_CN, size=10, first_indent=0, line=1.4, space_after=6)
309
- P(doc, "关键词:智能体研究助手;社会科学;学术诚信;可复现性;大语言模型;Claude Code",
310
- size=10, bold=False, first_indent=0, space_after=14)
311
-
312
- P(doc, "PsyClaw: An Agentic Assistant for Social-Science Research—Bringing "
313
- "the Claude Code Paradigm to the Research Workflow with Built-in "
314
- "Academic-Integrity Gates",
315
- size=13, bold=True, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=8, cn=CN_HEAD)
316
- P(doc, "[Author One]¹², [Author Two]¹² & [Corresponding Author]¹²*",
317
- size=10.5, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=2)
318
- P(doc, "¹ [Affiliation One]; ² [Affiliation Two]", size=9.5, color=GREY,
319
- align=WD_ALIGN_PARAGRAPH.CENTER, space_after=12)
320
- P(doc, "Abstract", size=11, bold=True, first_indent=0, space_after=3, cn=CN_HEAD)
321
- P(doc, ABSTRACT_EN, size=10, first_indent=0, line=1.4, space_after=6)
322
- P(doc, "Keywords: Agentic research assistant; Social science; Academic "
323
- "integrity; Reproducibility; Large Language Model; Claude Code",
324
- size=10, first_indent=0, space_after=8)
325
- rule(doc)
326
- page_break(doc)
327
-
328
-
329
- ABSTRACT_CN = (
330
- "近年来,以 Claude Code 为代表的智能体编程助手改变了软件工程的工作方式:它常驻终端、理解整个项目、以自然语言接受指令,并通过调用工具与执行命令真正地完成工作,而非仅作代码补全。本文提出 PsyClaw,把这一范式引入社会科学研究。它是一个开源的智能体研究助手:常驻终端、理解研究项目,以自然语言驱动文献检索、研究澄清与预注册、统计分析、论文写作与同行评审。正如 Claude Code 之于软件工程,PsyClaw 意在成为社会科学研究者的智能体助手。"
331
- "然而,研究与编程有一处根本不同:代码写得对不对,有编译器和测试来判定;研究做得规不规范,却缺一道现成的自动关卡。这正是 PsyClaw 的着力点——它在流程编排之外专设一层学术诚信核查:效应量与置信区间必报、区分探索与确证、确证研究须预注册、不把相关当因果、图件不得缺失等十四条规则,由机器逐条把关,产出一旦不合规就拦下并提示修改。Claude Code 靠测试与持续集成兜底,PsyClaw 就靠这层核查兜底,防止模型把存疑的分析包装成可信的结论。架构上,系统采用“四层加两横切”的可组合结构,并守一条铁律:不自行实现任何统计,统计一律交由 pingouin、statsmodels、SciPy 等成熟库或专用 MCP 后端(如 MNE-MCP)完成。本文介绍其设计与实现,报告 2228 项自动化测试全部通过,并以一个真实案例——一份草稿因插图缺失被核查当场拦下——印证这层把关的实效。PsyClaw 现阶段主要在心理学中打磨,但同一套核查与流程亦适用于其他定量社会科学,意在降低研究门槛,同时让成果更经得起复现与检验。"
332
- )
333
-
334
- ABSTRACT_EN = (
335
- "Agentic coding assistants such as Claude Code have recently changed how software is built: they live in the terminal, understand the whole project, take natural-language instructions, and get real work done by calling tools and running commands rather than merely autocompleting code. This paper presents PsyClaw, which brings that paradigm to social-science research—an open-source agentic research assistant that likewise lives in the terminal, understands your research project, and drives the entire pipeline in natural language, from literature search, study clarification, and preregistration to statistical analysis, manuscript writing, and peer review. Research, however, differs from coding in one basic respect: whether code is correct is settled by compilers and tests, whereas whether research is sound has no such ready-made, automatic checkpoint. This is where PsyClaw concentrates. Alongside orchestration it adds a layer of academic-integrity checks—effect sizes and confidence intervals are mandatory, exploratory and confirmatory work must be kept apart, confirmatory studies must be preregistered, correlation is not passed off as causation, figures may not be missing, and so on, across fourteen machine-checkable rules—that vet each output and, whenever something is non-compliant, block it on the spot and suggest a fix. Where Claude Code relies on tests and continuous integration as its safety net, PsyClaw relies on this layer of checks, keeping the model from dressing up questionable analyses as trustworthy conclusions. Architecturally the system is organized as composable layers and holds to one rule: it computes no statistics itself, delegating all of them to mature libraries (pingouin, statsmodels, SciPy) or dedicated MCP backends such as MNE-MCP. We describe the design and its rationale, report that all 2228 automated tests pass, and use a real case—a manuscript draft blocked on the spot because a figure was missing—to show that the checks work in practice. PsyClaw is being refined primarily in psychology, yet the same checks and workflows carry over to other quantitative social sciences, with the aim of making research less laborious and more reproducible."
336
- )
337
-
338
-
339
- def sec1(doc):
340
- H(doc, "1 引言")
341
- P(doc, "社会科学研究是一条环节众多、规范密集的长链条。一项典型的实证研究——无论出自心理学、教育学、"
342
- "传播学、社会学还是管理学——都需要研究者依次完成:文献检索与综述、研究问题澄清与假设界定、研究"
343
- "设计与功效分析、(视情况)预注册、数据采集与清洗、统计分析、结果可视化、论文撰写与投稿格式化、"
344
- "同行评审与修回。每一环都要求研究者同时具备方法学知识、软件操作能力与规范意识;任一环的疏漏都"
345
- "可能在最终结论中被放大。过去十余年,社会科学经历了严重的可复现性危机:对心理学已发表效应的大"
346
- "规模重复研究发现,仅约三分之一至半数能被稳健复现(Open Science Collaboration, 2015);对发表"
347
- "于 Nature 与 Science 的社会科学实验的系统重复亦得到相近的结论(Camerer et al., 2018)。危机的"
348
- "根源之一是研究者自由度(researcher degrees of freedom)在缺乏约束时被系统性地滥用——包括未"
349
- "声明的多重比较、择优报告、事后假设伪装成事前(hypothesizing after the results are known, "
350
- "HARKing; Kerr, 1998),以及各类可疑研究实践(questionable research practices, QRPs),后者"
351
- "在匿名调查中被自我报告为相当普遍(John et al., 2012; Simmons et al., 2011)。为回应危机,"
352
- "学界确立了一系列规范:期刊论文报告标准(JARS; Appelbaum et al., 2018)、“新统计学”对效应量"
353
- "与置信区间而非孤立 p 值的强调(Cumming, 2014)、系统综述的 PRISMA 流程(Page et al., 2021),"
354
- "以及预注册作为区分确证性与探索性研究的制度手段(Nosek et al., 2018)。然而,这些规范多以"
355
- "“研究者应当遵守”的形式存在,其执行仍高度依赖个人自律与评审者的事后把关。")
356
- P(doc, "与此同时,另一个领域正经历一场由智能体(agent)驱动的工作方式变革——软件工程。以 Anthropic "
357
- "的 Claude Code 为代表的智能体编程助手(Anthropic, 2025),把大语言模型(Large Language "
358
- "Models, LLMs)从“代码补全”推进到“真正完成工作”:它常驻于终端,理解整个项目的上下文,以自然"
359
- "语言接受指令,并通过读写文件、执行命令、调用外部工具(经模型上下文协议接入)来实际推进任务;"
360
- "其对具有副作用的操作采用逐条审批、可回退的权限机制。这一范式的要义不在于“生成一段代码”,而在于"
361
- "把一个专业工作者的完整工作流——理解现状、规划、执行、验证——交由一个可对话、可审批、工具在手的"
362
- "智能体来编排。一个自然的问题是:这套已在软件工程中被验证的范式,能否同样服务于社会科学研究?")
363
- P(doc, "将现有的通用 LLM 数据工具直接用于研究,会暴露两项结构性缺口。其一是编排缺口。以 ChatGPT Code "
364
- "Interpreter(后更名 Advanced Data Analysis)为代表的通用数据分析范式,聚焦于“给定一份数据、"
365
- "产出一段分析”(Rahman et al., 2025; Hong et al., 2024);而社会科学研究是一条跨阶段、有前后"
366
- "依赖的流程:文献要先于设计、设计要先于数据、分析结论要回流到写作、写作又要接受评审。通用范式"
367
- "难以把这些环节串成一条状态连续、可复现、可审计的工作流,更难以把“这一步做之前必须先完成上一步”"
368
- "的方法学约束显式地表达出来。其二是规范缺口,也是更为要害的一点。软件工程中,代码的对错由编译器、"
369
- "测试与持续集成客观裁决;而研究背负着软件所没有的正确性与规范要求,且 LLM 恰恰倾向于以自信、"
370
- "流畅的措辞给出结论。当模型被用于选择检验方法、剔除异常值、解释显著性或撰写结果段落时,它可能在"
371
- "无人审查的情况下,把一个存疑的分析自由度“洗白”成看似权威的表述——把相关当作因果、把大样本下的"
372
- "微小显著当作重要、把探索性发现表述为确证、或在报告中悄然省略未通过的检验。这正是自动化偏误"
373
- "(automation bias)在科研场景中的具体表现:越是流畅的自动化输出,越容易被不加审查地采纳。")
374
- P(doc, "模型上下文协议(Model Context Protocol, MCP)是 Anthropic 于 2024 年发布的开放标准,采用"
375
- "客户端—服务器架构,定义了工具(Tools)、资源(Resources)与提示词(Prompts)三类能力,使 "
376
- "LLM 应用能以标准化方式接入外部工具与数据源(Anthropic, 2024)。MCP 生态已经孕育出面向特定"
377
- "科研领域的服务器,例如将神经电生理分析平台 MNE-Python 接入 AI 编程助手的 MNE-MCP(Xue et "
378
- "al., 2025)。这类服务器出色地解决了“把一个专业工具交给智能体”的问题,但从整条研究链条来看,"
379
- "统计分析仍只是其中一环——它的上游是文献与设计,下游是写作与评审,而把这些环节编排为一条守规矩"
380
- "的可复现流程,尚缺一个专门的编排框架层。")
381
- P(doc, "本文提出 PsyClaw:把 Claude Code 所代表的智能体范式引入社会科学研究。它是一个开源的智能体研究"
382
- "助手,常驻终端、理解研究项目,以自然语言驱动文献、澄清、分析、写作与评审。正如 Claude Code 之于"
383
- "软件工程,PsyClaw 意在成为社会科学研究者的智能体助手。但针对上述规范缺口,PsyClaw 做了一项"
384
- "关键的加法:在流程编排之上内建一层贯穿全程、失败即阻断的学术诚信核查——如果说 Claude Code 以"
385
- "测试、类型检查与持续集成为护栏,那么 PsyClaw 就以效应量必报、探索确证之分、预注册、相关非因果、"
386
- "图件完整性等学术诚信核查为护栏。为坚守“编排”而非“重造轮子”的定位,PsyClaw 秉持一条铁律:自身"
387
- "不实现任何统计算法,一切统计计算外移至成熟库(pingouin、statsmodels、SciPy)或专用 MCP 后端"
388
- "(如上述 MNE-MCP)。系统围绕三项核心设计展开:声明式的研究流程定义、失败即阻断的学术诚信核查,"
389
- "以及统计外移的分层委托。需要说明的是,PsyClaw 以心理学为主要试验场(其内置的期刊画像与 JARS "
390
- "检查根植于此),但其核查与流程可推广至定量社会科学的诸多分支。本文给出关键实现细节、验证结果、"
391
- "局限分析与使用建议,并以宽松许可证开源发布,便于复用与扩展。")
392
-
393
-
394
- def sec2(doc):
395
- H(doc, "2 设计与原理")
396
- H(doc, "2.1 为什么这样分层", 2)
397
- P(doc, "PsyClaw 的设计出发点是一个判断:研究流程之所以难以交给通用工具,是因为它同时要求两件常相拉扯"
398
- "的事——既要“听得懂人话、能自主推进”,又要“步步可复现、处处守规范”。太偏前者,就成了一个流畅却"
399
- "难以复现、也不守规范的黑箱;太偏后者,又退回成僵硬的脚本,失去了自然语言协作的意义。为同时容纳"
400
- "这两种诉求,PsyClaw 把职责分成四层,再用两条横切贯穿其间(见图 1)。")
401
- P(doc, "四层自上而下,各回答一个问题。路由层(L0)回答“以什么方式介入”:研究者可以只是对话(chat,"
402
- "随需而动),可以指定一条明确、可复现的流程(run),也可以让系统感知项目状态、自主推进(auto)。"
403
- "流程层(L1)回答“一类研究该怎么走”:每一类研究被写成一份声明式的步骤清单,而不是一段写死的脚本,"
404
- "因而既能被完整复现,也能被重新拼装。子功能层(L2)回答“每一步能否独立成立”:每个步骤既能单独当"
405
- "一条命令用、能拆出来单独测试,也能拼进别的流程——同一份能力,多种用法。实现层(L3)回答“活到底"
406
- "由谁干”:真正的计算不由 PsyClaw 亲自完成,而是委托给外部的成熟工具。")
407
- P(doc, "两条横切则回答“如何始终守住底线”。其一是核查:每一步动手之前先过一道前置检查,不合规就当场拦下;"
408
- "整条流程跑完,还留下一份机器可读的总验收。其二是记忆:研究者的方法学偏好、目标期刊的惯例、过往的"
409
- "教训被沉淀下来并贯穿始终,使系统在多次交互中保持一致的前提。这套分层的意义不在于工程上的整洁,"
410
- "而在于把“自主”与“守规矩”这两种看似矛盾的诉求,各自安放在了合适的位置上。")
411
- figure(doc, FIGDIR / "psyclaw_fig1_architecture.png",
412
- "图 1 PsyClaw 的四层加两横切架构与数据流。研究者以自然语言经三入口(chat/run/auto)进入;"
413
- "L0–L3 自上而下逐层委托、结果与证据回流;Harness 横切(左)为每层附加失败即阻断的前置检查,"
414
- "Memory 横切(右)以三层记忆贯穿全程;统计计算整体外移至 L3 之下的成熟库与 MCP 后端,"
415
- "本仓不实现任何统计算法。")
416
-
417
- H(doc, "2.2 核心设计理念:声明式流程、失败即阻断的核查、统计外移", 2)
418
- P(doc, "社会科学研究流程与无状态、一次性的代码执行范式存在本质差异,这决定了 PsyClaw 的三项设计取向。")
419
- P(doc, "第一,声明式的研究流程。一个流程是“数据”而非“脚本”——它只声明“要走哪几步、每步动手前要满足什么"
420
- "前提”,具体怎么执行交给统一的引擎。以文献综述为例,它就是“研究准备检查 → 文献检索 → 筛选 → "
421
- "结构化合成 → 同行评审”这样一串带前提的步骤。引擎对每一步的处理是固定的:先验前提,不满足就当场"
422
- "拦下;再执行;再记录产物与可恢复的检查点;必要时在步间征求人工确认;最后给出一份机器可读的总验收。"
423
- "这样做的好处不在于代码优雅,而在于研究流程因此变成可被完整重放、也可被自由重组的对象——同一份"
424
- "分析,别人能照着复算,自己也能改几步复用。")
425
- P(doc, "第二,把学术规范做成不可绕过的核查。这是 PsyClaw 区别于通用 LLM 数据分析方案的核心。学术规范——"
426
- "效应量、探索确证之分、预注册、相关与因果之别——历来以“研究者应当遵守”的形式存在,执行全靠自律。"
427
- "PsyClaw 把这些要求凝练成一份规范:它既写给人看,也作为系统提示注入模型,让模型在生成阶段就受约束;"
428
- "同时把其中可机器判定的部分做成一张规则表,在每个产出点强制核查。核查默认“不合规即拦下”:不达标时"
429
- "不是给个警告然后放行,而是拦住产出、要求修复(详见 2.4 节)。用意很简单——把“该不该这么做、报告"
430
- "是否合规”从依赖自律的软约束,变成流程内绕不过去的硬约束。")
431
- P(doc, "第三,统计外移的分层委托。PsyClaw 秉持一条明确的铁律:不在本仓实现任何统计算法(分布函数、"
432
- "参数估计、各类检验、因子分析、生存分析等一概不写)。需要统计时,一律委托给实现层的成熟库或 MCP "
433
- "后端。这一取舍有三重理由:其一,成熟统计库经过长期社区检验,其数值正确性远非一个编排工具重新"
434
- "实现所能企及;其二,把统计留在外部,使 PsyClaw 的职责边界清晰——它只负责“编排得对不对、报告"
435
- "合不合规”,而非“数值算得准不准”;其三,同一份外部后端既可被智能体在多步推理中直接调用,也可被"
436
- "声明式流程在分析步中委托,两条路径落到同一实现,避免重复。作为两处样板,元分析流程生成委托 "
437
- "statsmodels 的脚本(随机效应 DerSimonian–Laird 估计、I²/τ²/Q 异质性、Egger 检验;"
438
- "DerSimonian & Laird, 1986; Egger et al., 1997),实证分析流程据数据画像推荐分析并生成委托 "
439
- "pingouin 与 SciPy 的脚本(Vallat, 2018; Virtanen et al., 2020)——仓内不计算任何一个统计量"
440
- "(见图 2)。")
441
- figure(doc, FIGDIR / "psyclaw_fig2_principles.png",
442
- "图 2 三项核心设计理念在一个流程步骤上的协同。声明式流程定义并驱动每个步骤(①);步骤执行前经"
443
- "失败即阻断的核查把关,通过则放行、不过则阻断并给出修复(②);步骤把统计计算委托给外部成熟库或 MCP "
444
- "后端,数值正确性由外部库承担(③);三者合力产出“工作流可复现 × 内容合规 × 数值可信”的成果。")
445
-
446
- H(doc, "2.3 能力的组织方式:一份能力,两种用法", 2)
447
- P(doc, "PsyClaw 覆盖研究的完整链条——研究准备、文献、分析、写作、评审与质量检查。但比“覆盖了多少功能”"
448
- "更值得说的,是这些能力被组织的方式:每一份能力既能被单独使用,又同时是某条流程里的一步。赶时间时,"
449
- "研究者可以只用其中一环(比如只做一次文献检索、或只跑一遍格式核查);要严谨、可复现时,就让同一批"
450
- "能力按声明式流程自动串起来。二者背后是同一套实现,因此“单独用”和“进流程”不会给出两种不同的行为"
451
- "——这正是可复现性的前提之一。表 1 按研究阶段勾勒了这些能力所承担的角色。")
452
- table(doc, ["研究阶段", "PsyClaw 承担的事"],
453
- [["研究准备", "澄清研究问题与假设、登记预注册、功效与样本量估算、伦理核对"],
454
- ["文献", "多库检索与引文滚雪球、生成规范引用、核查引用是否真实存在"],
455
- ["分析", "据数据画像推荐合适的分析,生成可复现脚本并委托外部库执行"],
456
- ["写作", "按目标期刊规范起草与润色、稿件标注、多格式导出"],
457
- ["评审", "模拟同行评审、按 JARS 等规范自检"],
458
- ["贯穿全程", "学术诚信核查与三层记忆(见 2.4、2.5)"]],
459
- widths=[3.0, 13.0], caption_text="表 1 PsyClaw 的能力按研究阶段划分")
460
- P(doc, "无论单独使用还是纳入流程,每一环都遵循同一种约定:所需能力不可用时,先给出可诊断的提示或一份可"
461
- "自行运行的降级方案,而不是中途报错;凡涉及统计结论,都附上可复现脚本与数据指纹。正是这种一致的"
462
- "约定,让“单用”与“入流程”之间没有行为落差。目前,文献综述、元分析、实证分析、质性分析四类研究被"
463
- "固化为稳定的声明式流程,其余能力则可按需拼装。")
464
-
465
- H(doc, "2.4 学术诚信核查与质量检查", 2)
466
- P(doc, "学术诚信核查是 PsyClaw 的招牌设计——它把“研究做得规不规范”这件事,从“把活干完”里单独拎出来"
467
- "专门保障,而不是混在流程里听天由命。")
468
- P(doc, "这层核查有两种形态。一是一份人读的规范,把研究诚信原则讲清楚——可复现优先、不 p-hacking、效应量"
469
- "与不确定性并报、区分探索与确证、诚实可视化、相关不等于因果、尊重原始数据、遵循 APA7;这份规范同时"
470
- "作为系统提示注入模型,使它在生成阶段就受约束。二是把其中可机器判定的部分,落成十四条带“触发点—"
471
- "判据—不合规怎么办”的规则,在每个产出点自动核查(见表 2)。阻断类规则不达标就中止产出并给出修复"
472
- "路径(如自动补算效应量与置信区间、提示稳健替代检验、把确证改标为探索);告警类规则则记入审计日志,"
473
- "供人工复核。")
474
- table(doc, ["规则", "触发点", "要求", "不合规时"],
475
- [["STAT.effect_size", "统计产出", "显著性检验必含效应量 + CI", "阻断,自动补算 d/η²/r 及 95% CI"],
476
- ["STAT.assumptions", "跑检验前", "正态/方差齐性/独立性已诊断", "阻断,提示稳健替代"],
477
- ["STAT.no_phack", "分析过程", "无未声明多重比较/择优/HARKing", "告警 + 审计日志"],
478
- ["DESIGN.power", "实验设计", "样本量来自先验功效分析", "阻断"],
479
- ["DESIGN.prereg", "确证性研究", "有预注册或明确标注探索性", "阻断或改标探索性"],
480
- ["WRITE.apa7", "论文产出", "引用/数字/表格符合 APA7", "阻断,自动修正"],
481
- ["LIT.prisma", "文献综述", "检索/筛选符合 PRISMA", "提示补全"],
482
- ["REPRO.script", "任何统计结论", "附复现脚本 + 数据指纹", "阻断"],
483
- ["FIG.honest", "出图", "坐标轴诚实、误差棒含义、色盲友好", "阻断"],
484
- ["DATA.careless", "数据筛查", "已跑粗心作答筛查、剔除经人工批准", "阻断"],
485
- ["MEASURE.reliability", "使用量表分", "合成量表分前必报信度", "阻断"],
486
- ["CLARIFY.complete", "研究开始", "研究准备卡全部项已解决", "阻断"],
487
- ["DESIGN.evidence", "设计决策", "每条决策有方法学文献背书", "阻断"],
488
- ["STAT.rigor", "分析流水线", "数据质量→描述→诊断→稳健性→限定措辞", "阻断"]],
489
- widths=[3.2, 2.2, 5.4, 5.2], fontsize=8.0,
490
- caption_text="表 2 PsyClaw 的机器可核查学术诚信规则(共十四项)")
491
- P(doc, "这里要区分两类把关:一类是价值判断——什么时候停下、异常值该不该剔、这个取舍值不值,这类事没有"
492
- "唯一正确答案,交给人;另一类是合规判断——产出本身有没有违反明确的规范,这类事有客观标准,交给"
493
- "机器。核查负责后者并给评审提供客观依据,前者始终留给研究者。二者互补,谁也不越俎代庖。")
494
- P(doc, "核查不只看统计与文字,也看图。它会拦下两类问题:其一,图注自称“待生成/占位”却仍随稿交付的占位图;"
495
- "其二,正文里的本地图片链接指向的文件在稿件里根本不存在的断链。这条规则来自一次真实的教训(详见 "
496
- "3.4 节):图是论文内容的一部分,不是可选项;一张主图从未生成、却在交付稿里留着占位符,这样的稿子"
497
- "不该被悄悄放过。")
498
- P(doc, "还有一种兜底:当某项具体依据(如某本期刊的字数与版块要求)不在系统覆盖范围内时,它不把“查无匹配”"
499
- "说成失败或死路,而是明确套用通用高标准(效应量加 CI、相关不等于因果、区分探索与确证、图件不得"
500
- "缺失等),并指清哪些细节需研究者对照官方指南自行确认。这样,覆盖盲区就不会变成规避规范的缺口。")
501
-
502
- H(doc, "2.5 记忆、澄清与预注册", 2)
503
- P(doc, "系统维护贯穿路由与各步的三层记忆:方法学偏好(研究者惯用的检验取向、报告格式)、期刊惯例(目标"
504
- "刊的画像与要求)、教训卡(跨会话累积的具体教训)。记忆影响路由默认值与各步行为,使模型在多会话"
505
- "交互中自动遵循一致的前提与参数设定,并为过程审计提供可追溯依据。与“先跑起来再说”的取向相反,"
506
- "PsyClaw 把研究准备前置为硬性核查:CLARIFY.complete 核查要求在研究开始前完成研究准备卡的全部项,"
507
- "DESIGN.prereg 核查要求确证性研究在分析前具备预注册或明确标注为探索性。这把“区分探索与确证”这一"
508
- "研究方法学的核心规范,从事后自述变成了流程内不可绕过的前置步骤(Nosek et al., 2018)。")
509
-
510
- H(doc, "2.6 把关键决定留给人", 2)
511
- P(doc, "自动化程度越高,越要想清楚哪些事不能让机器自己拍板。PsyClaw 的原则很简单:凡是有外部影响的操作"
512
- "——写文件、执行命令、调用外部工具——默认都要先经研究者批准,而非先斩后奏;批准的松紧档位可随时"
513
- "调整。更要紧的是,删数据、改测量口径、改核查判据这类既不可逆、又直接关系结论的操作,系统绝不自作"
514
- "主张,而是写成一份“决策请求”交给人来定。再加上“能力缺失时给可运行的降级方案、不碰原始数据、不在"
515
- "产出里写入任何密钥”,这些合起来保证了一件事:效率可以由自动化来提,但关键的、不可逆的、涉及诚信的"
516
- "判断,始终握在研究者手里。")
517
-
518
- H(doc, "2.7 验证", 2)
519
- P(doc, "作为一个以编排与核查为主的系统,PsyClaw 的验证重点是“流程走得对、核查拦得住”,而不是统计数值"
520
- "本身。测试覆盖路由、声明式流程引擎、各子功能与核查规则等;在本文写作环境下的一次全量运行中,"
521
- "2228 项测试全部通过。")
522
- P(doc, "有一点必须讲清楚:测试不校验统计量的数值精度——这与“统计外移”的铁律一致,数值对不对由被委托的"
523
- "外部库负责。因此本文所称“可复现性”主要指工作流层面:每个统计结论都附带可复现脚本与数据指纹,"
524
- "流程末尾还有一份机器可读的总验收,使同一分析可被审计、复算与共享。作为统计确已“外移到位”的证据,"
525
- "元分析与实证分析流程生成的委托脚本,都能在装好相应库的环境中独立跑到正常退出——也就是说,PsyClaw "
526
- "产出的编排是可执行的,真正做统计的是外部库,而非它自己。")
527
- table(doc, ["层面", "内容", "结果"],
528
- [["流程与接口", "路由、配置、命令行、核查规则", "全部通过"],
529
- ["子功能", "文献筛选、效应量校验、分析推荐、元分析脚本生成、转录本加载", "全部通过"],
530
- ["流程集成", "四类声明式流程引擎 + 总验收", "全部通过"],
531
- ["统计外移", "meta/analysis 生成的委托脚本在装库环境独立运行", "正常退出"],
532
- ["全量", "约 2200 个测试", "2228 通过"]],
533
- widths=[3.0, 9.0, 3.0], caption_text="表 3 PsyClaw 的验证概览")
534
-
535
- H(doc, "2.8 与传统工作流的对比", 2)
536
- P(doc, "表 4 从多个维度比较了 PsyClaw 辅助的工作流与三种传统方式(手写分析脚本、图形界面统计软件、通用 "
537
- "LLM 数据分析)的差异。需要强调的是,对比旨在刻画取舍而非否定其他方式:手写脚本在确定性与精确复现"
538
- "上具有优势,图形界面在零编程上手上具有优势,通用 LLM 在灵活性上具有优势;PsyClaw 的独特之处在于"
539
- "把研究编排与学术规范护栏一并纳入。")
540
- table(doc, ["维度", "手写脚本", "图形界面软件", "通用 LLM 分析", "PsyClaw"],
541
- [["覆盖范围", "单环节", "单环节(统计)", "单环节(分析)", "全链条编排"],
542
- ["交互方式", "编程", "菜单", "自然语言", "自然语言"],
543
- ["流程可复现", "高(依赖规范)", "低", "中", "高(脚本+指纹+归档)"],
544
- ["学术规范护栏", "依赖自律", "无", "无", "失败即阻断核查"],
545
- ["统计数值来源", "自选库", "内置", "模型生成代码", "外部成熟库/MCP"],
546
- ["上手门槛", "高", "低", "低", "低"],
547
- ["确定性", "高", "高", "低", "中(编排非确定)"]],
548
- widths=[2.8, 3.0, 3.2, 3.2, 3.8], fontsize=8.5,
549
- caption_text="表 4 PsyClaw 与传统工作流的对比")
550
-
551
-
552
- def sec3(doc):
553
- H(doc, "3 面向社会科学研究者的使用建议")
554
- H(doc, "3.1 系统要求与安装", 2)
555
- P(doc, "PsyClaw 以 Python 标准库为主实现,需 Python 3.11 及以上版本;唯一的第三方运行依赖是交互基础"
556
- "设施 prompt_toolkit(缺失时降级至 readline),因此可跨平台运行于 Windows、macOS 与 Linux。"
557
- "统计外移所依赖的成熟库(pingouin、statsmodels、SciPy 等)按需安装:不装则相关命令返回可运行的"
558
- "降级脚本,装了则可直接执行或经 MCP 后端委托运行。安装提供在线脚本、包管理器与离线整包三种方式,"
559
- "并对网络受限环境做了适配(自动探测 GitHub 可达性,不通时切换至镜像与国内 PyPI 索引)。装好后运行 "
560
- "psyclaw doctor 进行环境自检,运行 psyclaw start 进入交互式向导。若需接入外部统计或领域后端,"
561
- "可用 psyclaw mcp 配置并启用 MCP 服务器(系统内置对多种统计后端的委托封装,并可接入如 MNE-MCP "
562
- "等领域服务器)。")
563
-
564
- H(doc, "3.2 典型使用场景", 2)
565
- P(doc, "PsyClaw 面向四类具备稳定契约的研究流程,可用 run <类型> 一次性、可复现地执行,也可在 chat 中"
566
- "以自然语言分步驱动:文献综述(literature)从研究准备检查开始,经 PRISMA 识别的多库检索、PRISMA "
567
- "筛选、结构化合成,到可选的同行评审;元分析(meta)校验各研究的效应量与不确定性后,生成委托 "
568
- "statsmodels 的随机效应元分析脚本,仓内不计算任何统计量;实证分析(analysis)据数据画像推荐合适的"
569
- "分析(t/方差/相关/回归/描述),生成委托 pingouin/SciPy 的脚本,全程受 STAT.* 核查约束;质性分析"
570
- "(qualitative)为解释性的 LLM 辅助编码与主题分析,研究者复核(HITL)。无论走哪条流程,学术诚信"
571
- "核查都在相应产出点自动生效。对于拟用于发表的确证性研究,建议在项目根目录以记忆形式集中记录项目级"
572
- "约定(默认数据目录、目标期刊、报告格式、量表信度口径等),使模型在跨会话交互中自动遵循一致前提。")
573
-
574
- H(doc, "3.3 自然语言交互示例", 2)
575
- P(doc, "以下示例展示了 PsyClaw 在接收自然语言请求后,如何自动编排流程、执行核查并在不合规时阻断。为聚焦"
576
- "交互逻辑,示例对系统返回内容做了精简;其中的数值均为示例性说明,而非来自真实数据的测量结果。")
577
- table(doc, ["轮次", "用户(自然语言)", "PsyClaw(编排 + 核查)"],
578
- [["1", "我想比较两组被试的反应时。",
579
- "CLARIFY.complete 触发:研究准备卡尚有未决项(这是确证还是探索?有无预注册?)。请先完成澄清,否则阻断开工。"],
580
- ["2", "确证性,已在 OSF 预注册,组间独立样本 t 检验。",
581
- "澄清完成,DESIGN.prereg 通过。据数据画像推荐独立样本 t(先诊断正态与方差齐性)。已生成委托 pingouin 的脚本,将在你本地执行——本系统不自算统计。"],
582
- ["3", "跑吧。",
583
- "方差齐性不满足 → STAT.assumptions 提示改用 Welch t。结果:t(88.3)=2.41,p=.018。STAT.effect_size 触发:缺效应量与 CI → 自动补算 Cohen's d=0.51, 95% CI [0.09, 0.93]。已附复现脚本与数据指纹。"],
584
- ["4", "帮我写进结果段落,就说 A 组更快导致了差异。",
585
- "生成 APA7 结果段落(WRITE.apa7)。但“导致”属因果断言,此为非实验的组间比较 → 核查改写为相关性表述并提示:如需因果结论须满足识别假设。"],
586
- ["5", "配一张组均值柱状图。",
587
- "出图并过 FIG.honest:坐标轴自零起、误差棒标注为 95% CI、采用色盲友好配色;图件完整性校验确认图片文件已生成且链接可达,方才随稿内嵌。"]],
588
- widths=[1.2, 5.2, 9.6], fontsize=8.5,
589
- caption_text="表 5 自然语言交互示例:一次“分析到写作”的逐轮对话")
590
- P(doc, "该示例表明,研究者只需用自然语言描述目标,系统即可自动完成“澄清—推荐—委托执行—合规写作—诚实"
591
- "出图”的完整循环,并在每一处规范要点上自动把关;与此同时,统计计算始终在外部库中完成,关键的价值"
592
- "判断仍由研究者确认。")
593
-
594
- H(doc, "3.4 真实案例:图件完整性核查拦截一份缺陷手稿", 2)
595
- P(doc, "为在真实产出上展示核查的把关,本节给出一次真实发生、并据此加固系统的案例。在一次以某高影响力"
596
- "期刊为目标的论文写作中,系统交付的手稿存在两处图件缺陷:其一,一张核心路径图从未被生成,交付稿里"
597
- "却赫然留着替代文本为“路径图——待生成”(Path diagram — to be generated)的占位图;其二,一张"
598
- "次要的调节效应图虽已生成,却未被内嵌,且其保存路径与稿中链接指向的目录不一致,导致该链接落空。")
599
- P(doc, "问题在于,当时的质量检查(JARS 与诚信启发式)完全不看图:手稿全过、静默交付——而这恰是“可视化"
600
- "不足、内容缺失”的直接来源。诊断显示,图是论文内容的一部分,而非可选项;一份主图从未兑现、却以"
601
- "占位符充数的稿件,不应被放行。据此,系统在质量检查中新增了图件完整性核查:一条规则拦截替代文本自认"
602
- "“待生成/将补充/占位”(中英文均覆盖)的占位图,另一条校验每个本地图片链接指向的文件是否真实存在"
603
- "(远程与内嵌数据链接跳过)。为高精度、避免误伤正文中谈论“占位符”这一概念的行文,占位检测仅扫描"
604
- "替代文本,且沿用参考文献区截断规则不予扫描。")
605
- P(doc, "在那份真实手稿上复核的结果是:修复前,零阻断、静默通过;修复后,同时命中两条图件完整性规则,"
606
- "手稿被阻断并给出具体的待修项。这一案例与 MNE-MCP 中方法学审查代理对“伪重复”返回修订裁定的情形"
607
- "同类(Xue et al., 2025):它表明核查能对系统自身的产出提出实质性质疑,而非形式化附和,从而把 "
608
- "2.4 节所述的自动化偏误风险落到一次具体的真实拦截上。相应的图件完整性校验已随新增测试固化进测试"
609
- "套件,且遵循“质量检查只增不减”的原则——核查一旦确立,只会加严、不会悄然放松。")
610
-
611
-
612
- def sec4(doc):
613
- H(doc, "4 结论")
614
- P(doc, "本文介绍了 PsyClaw:把 Claude Code 所代表的智能体范式引入社会科学研究的开源智能体研究助手。"
615
- "针对社会科学研究“流程长、规范密”的本质,系统以“四层加两横切”的可组合架构,把文献、澄清预注册、"
616
- "统计分析、论文写作与同行评审编排为自然语言驱动、可复现的工作流。其三项核心设计——声明式的研究"
617
- "流程、失败即阻断的学术诚信核查、统计外移的分层委托——共同构成了它区别于通用 LLM 数据分析方案的特征。系统通过约 2200 个"
618
- "自动化测试(本文环境下 2228 项全部通过)、四类声明式流程的集成测试、统计外移生成脚本的独立可执行"
619
- "性,以及核查自检完成质量保证,并以一次真实手稿被图件完整性核查拦截的案例,展示了核查的实际把关。")
620
- P(doc, "PsyClaw 的核心贡献有二。其一,它把在软件工程中被验证的智能体范式(Claude Code)迁移并适配到"
621
- "社会科学研究的完整链条:由 LLM 依用户意图分派研究类型、逐步执行并回流结果,声明式流程与等效"
622
- "脚本、数据指纹、归档机制保障了工作流层面的可复现与可审计。其二,也是更关键的,它把散落于 JARS、PRISMA、预注册等规范中的学术诚信要求,"
623
- "凝练为一层失败即阻断的机器核查,前置到每一个产出点——从而把可复现性与规范性,从依赖个人自律的"
624
- "软约束,转变为流程内不可绕过的硬约束。同时,通过坚持“不在本仓实现任何统计算法”的铁律,PsyClaw "
625
- "把数值正确性交给成熟库、把规范正确性留给自己,两相分离而各司其职;作为编排框架,它天然地"
626
- "能够托管如 MNE-MCP 这样的领域 MCP 服务器,把专业统计接入同一条守规矩的研究流程。")
627
- P(doc, "局限与风险。本研究存在若干局限,使用时需特别注意。其一,学术诚信核查是基于规范的启发式检查,"
628
- "能拦截可机器判定的违规(缺效应量、占位图、无预注册的确证分析等),但不能替代人类的方法学判断:"
629
- "许多深层的效度问题(构念效度、混淆变量、因果识别假设的合理性)仍需研究者与评审者把关,核查是"
630
- "辅助而非替代。其二,把“看图—判断—写作”自动化仍存在自动化偏误风险:模型以自信措辞给出的建议,"
631
- "可能在缺乏审查时被不加辨别地采纳,因此人在回路不可或缺,涉及价值判断与不可逆操作的环节始终保留"
632
- "人工审批。其三,各统计量的数值精度继承自外部库,而非由本系统保证;本文的验证确立的是编排的功能"
633
- "正确性与流程集成,而非统计数值的准确性。其四,相较于确定性的手写脚本,经由 LLM 的编排存在非"
634
- "确定性,并带来延迟与调用成本。")
635
- P(doc, "未来工作。可在以下方向继续推进:在真实研究场景中对核查的拦截准确率(真阳性与假阳性)进行系统"
636
- "评估;把预注册与投稿格式化进一步一键化;扩充可委托的 MCP 统计与领域后端;以及将质性编码升级为"
637
- "专用技能。随着 MCP 生态的成长与 LLM 能力的提升,专业科研规范与 AI 助手的集成将进一步深入。"
638
- "作为这一方向的早期实践,PsyClaw 尝试证明:AI 辅助的科研自动化,不应以牺牲学术规范为代价——恰恰"
639
- "相反,规范可以被编码为流程内不可绕过的护栏。其架构与核查设计,亦可为其他规范密集学科的研究编排"
640
- "所借鉴。")
641
-
642
-
643
- REFS = [
644
- "Anthropic. (2024). Model Context Protocol: An open standard for connecting AI assistants to data systems. https://modelcontextprotocol.io",
645
- "Anthropic. (2025). Claude Code [Computer software]. https://www.anthropic.com/claude-code",
646
- "Appelbaum, M., Cooper, H., Kline, R. B., Mayo-Wilson, E., Nezu, A. M., & Rao, S. M. (2018). Journal article reporting standards for quantitative research in psychology: The APA Publications and Communications Board task force report. American Psychologist, 73(1), 3–25. https://doi.org/10.1037/amp0000191",
647
- "Camerer, C. F., Dreber, A., Holzmeister, F., Ho, T.-H., Huber, J., Johannesson, M., Kirchler, M., Nave, G., Nosek, B. A., Pfeiffer, T., Altmejd, A., Buttrick, N., Chan, T., Chen, Y., Forsell, E., Gampa, A., Heikensten, E., Hummer, L., Imai, T., … Wu, H. (2018). Evaluating the replicability of social science experiments in Nature and Science between 2010 and 2015. Nature Human Behaviour, 2(9), 637–644. https://doi.org/10.1038/s41562-018-0399-z",
648
- "Cumming, G. (2014). The new statistics: Why and how. Psychological Science, 25(1), 7–29. https://doi.org/10.1177/0956797613504966",
649
- "DerSimonian, R., & Laird, N. (1986). Meta-analysis in clinical trials. Controlled Clinical Trials, 7(3), 177–188. https://doi.org/10.1016/0197-2456(86)90046-2",
650
- "Egger, M., Davey Smith, G., Schneider, M., & Minder, C. (1997). Bias in meta-analysis detected by a simple, graphical test. BMJ, 315(7109), 629–634. https://doi.org/10.1136/bmj.315.7109.629",
651
- "Gramfort, A., Luessi, M., Larson, E., Engemann, D. A., Strohmeier, D., Brodbeck, C., Goj, R., Jas, M., Brooks, T., Parkkonen, L., & Hämäläinen, M. (2013). MEG and EEG data analysis with MNE-Python. Frontiers in Neuroscience, 7, 267. https://doi.org/10.3389/fnins.2013.00267",
652
- "Harris, C. R., Millman, K. J., van der Walt, S. J., Gommers, R., Virtanen, P., Cournapeau, D., … Oliphant, T. E. (2020). Array programming with NumPy. Nature, 585, 357–362. https://doi.org/10.1038/s41586-020-2649-2",
653
- "Hong, S., Lin, Y., Liu, B., Liu, B., Wu, B., Zhang, C., … Wu, C. (2024). Data Interpreter: An LLM agent for data science (arXiv:2402.18679). arXiv. https://doi.org/10.48550/arXiv.2402.18679",
654
- "John, L. K., Loewenstein, G., & Prelec, D. (2012). Measuring the prevalence of questionable research practices with incentives for truth telling. Psychological Science, 23(5), 524–532. https://doi.org/10.1177/0956797611430953",
655
- "Kerr, N. L. (1998). HARKing: Hypothesizing after the results are known. Personality and Social Psychology Review, 2(3), 196–217. https://doi.org/10.1207/s15327957pspr0203_4",
656
- "Meade, A. W., & Craig, S. B. (2012). Identifying careless responses in survey data. Psychological Methods, 17(3), 437–455. https://doi.org/10.1037/a0028085",
657
- "Nosek, B. A., Ebersole, C. R., DeHaven, A. C., & Mellor, D. T. (2018). The preregistration revolution. Proceedings of the National Academy of Sciences, 115(11), 2600–2606. https://doi.org/10.1073/pnas.1708274114",
658
- "Open Science Collaboration. (2015). Estimating the reproducibility of psychological science. Science, 349(6251), aac4716. https://doi.org/10.1126/science.aac4716",
659
- "Page, M. J., McKenzie, J. E., Bossuyt, P. M., Boutron, I., Hoffmann, T. C., Mulrow, C. D., … Moher, D. (2021). The PRISMA 2020 statement: An updated guideline for reporting systematic reviews. BMJ, 372, n71. https://doi.org/10.1136/bmj.n71",
660
- "Rahman, M., Bhuiyan, A., Islam, M. S., Laskar, M. T. R., Mahbub, R., Masry, A., Joty, S., & Hoque, E. (2025). LLM-based data science agents: A survey of capabilities, challenges, and future directions (arXiv:2510.04023). arXiv. https://doi.org/10.48550/arXiv.2510.04023",
661
- "Simmons, J. P., Nelson, L. D., & Simonsohn, U. (2011). False-positive psychology: Undisclosed flexibility in data collection and analysis allows presenting anything as significant. Psychological Science, 22(11), 1359–1366. https://doi.org/10.1177/0956797611417632",
662
- "Vallat, R. (2018). Pingouin: Statistics in Python. Journal of Open Source Software, 3(31), 1026. https://doi.org/10.21105/joss.01026",
663
- "Virtanen, P., Gommers, R., Oliphant, T. E., Haberland, M., Reddy, T., Cournapeau, D., … SciPy 1.0 Contributors. (2020). SciPy 1.0: Fundamental algorithms for scientific computing in Python. Nature Methods, 17, 261–272. https://doi.org/10.1038/s41592-019-0686-2",
664
- "Xue, J., Wei, S., & Zhu, T. (2025). MNE-MCP: An automated neurophysiological analysis system for MCP-compatible AI coding assistants [Manuscript/preprint].",
665
- ]
666
-
667
-
668
- def references(doc):
669
- page_break(doc)
670
- H(doc, "参考文献")
671
- for r in REFS:
672
- reference(doc, r)
673
-
674
-
675
- def footer(doc, text):
676
- for sec in doc.sections:
677
- p = sec.footer.paragraphs[0]
678
- p.alignment = WD_ALIGN_PARAGRAPH.CENTER
679
- r = p.add_run(text)
680
- r.font.size = Pt(8)
681
- r.font.color.rgb = RGBColor(0x88, 0x88, 0x88)
682
- _fonts(r, CN_BODY)
683
-
684
-
685
- def _patch_docx_settings_zoom(path):
686
- """python-docx emits bestFit zoom without percent; docx skill validation requires it."""
687
- tmp = Path(str(path) + ".tmp")
688
- with zipfile.ZipFile(path, "r") as src, zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as dst:
689
- for item in src.infolist():
690
- data = src.read(item.filename)
691
- if item.filename == "word/settings.xml":
692
- text = data.decode("utf-8")
693
- text = text.replace(
694
- '<w:zoom w:val="bestFit"/>',
695
- '<w:zoom w:val="bestFit" w:percent="100"/>',
696
- )
697
- data = text.encode("utf-8")
698
- dst.writestr(item, data)
699
- tmp.replace(path)
700
-
701
-
702
- def main():
703
- if not (FIGDIR / "psyclaw_fig1_architecture.png").exists():
704
- sys.exit("缺少图件,请先运行 scripts/build_preprint_figures.py")
705
- doc = Document()
706
- st = doc.styles["Normal"]
707
- st.font.name = LATIN
708
- st.font.size = Pt(10.5)
709
- st.element.rPr.rFonts.set(qn("w:eastAsia"), CN_BODY)
710
- for sec in doc.sections:
711
- sec.top_margin = sec.bottom_margin = Cm(2.4)
712
- sec.left_margin = sec.right_margin = Cm(2.6)
713
- front_matter(doc)
714
- sec1(doc)
715
- sec2(doc)
716
- sec3(doc)
717
- sec4(doc)
718
- references(doc)
719
- footer(doc, "PsyClaw 预印本 · 草稿")
720
- out = ROOT / "docs" / "PsyClaw预印本_draft.docx"
721
- out.parent.mkdir(parents=True, exist_ok=True)
722
- doc.save(out)
723
- _patch_docx_settings_zoom(out)
724
- print(f"✓ {out}")
725
- return out
726
-
727
-
728
- if __name__ == "__main__":
729
- main()