codebee 0.1.95 → 0.1.97
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.
- package/CHANGELOG.md +733 -720
- package/LICENSE +21 -21
- package/README.md +516 -509
- package/app/core/acceptance.py +100 -100
- package/app/core/agent_context.py +118 -0
- package/app/core/aiflavor.py +197 -197
- package/app/core/analytics.py +38 -38
- package/app/core/artifacts.py +148 -148
- package/app/core/assets.py +110 -110
- package/app/core/attachments.py +623 -623
- package/app/core/audit.py +30 -30
- package/app/core/backup.py +470 -470
- package/app/core/beekeeper.py +183 -183
- package/app/core/benchstore.py +102 -95
- package/app/core/board.py +246 -209
- package/app/core/bookmeta.py +1036 -1036
- package/app/core/bookmeta_catalog.py +180 -180
- package/app/core/branching.py +137 -137
- package/app/core/builtin_agent.py +2055 -1680
- package/app/core/capability.py +144 -144
- package/app/core/chaptersafe.py +32 -32
- package/app/core/checkpoints.py +648 -0
- package/app/core/cleanup.py +303 -303
- package/app/core/compaction.py +201 -201
- package/app/core/contracts.py +255 -255
- package/app/core/covergen.py +356 -356
- package/app/core/defectretro.py +43 -43
- package/app/core/diagnostics.py +114 -114
- package/app/core/dispatch.py +198 -191
- package/app/core/dispatch_log.py +128 -128
- package/app/core/env_scrub.py +85 -85
- package/app/core/errorlog.py +137 -137
- package/app/core/eval_matrix.py +82 -0
- package/app/core/evalbench.py +184 -91
- package/app/core/evaluation.py +174 -174
- package/app/core/failure_notify.py +290 -290
- package/app/core/flow_graph.py +33 -0
- package/app/core/goal_service.py +167 -167
- package/app/core/health.py +389 -389
- package/app/core/history.py +38 -38
- package/app/core/hooks.py +162 -162
- package/app/core/jobs.py +25 -4
- package/app/core/knowledge_pipeline.py +86 -0
- package/app/core/manager.py +2232 -2232
- package/app/core/mcp_client.py +200 -29
- package/app/core/mocks.py +64 -64
- package/app/core/notify.py +201 -201
- package/app/core/novel_quality.py +181 -189
- package/app/core/openapi.py +177 -154
- package/app/core/operations.py +228 -228
- package/app/core/paihang.py +145 -145
- package/app/core/pipeline.py +319 -59
- package/app/core/planner.py +17 -11
- package/app/core/policy.py +84 -0
- package/app/core/portguard.py +111 -111
- package/app/core/presence.py +77 -77
- package/app/core/preview.py +293 -293
- package/app/core/project_memory.py +139 -0
- package/app/core/prompt_guard.py +78 -78
- package/app/core/publish/__init__.py +7 -7
- package/app/core/publish/auto.py +106 -22
- package/app/core/publish/browser.py +694 -655
- package/app/core/publish/chaprows.py +138 -0
- package/app/core/publish/fanqie.py +212 -165
- package/app/core/publish/flow.py +832 -640
- package/app/core/publish/flows-fanqie-calibrated.json +361 -336
- package/app/core/publish/ledger.py +51 -2
- package/app/core/publish/manager.py +1150 -1118
- package/app/core/publish/qimao.py +176 -176
- package/app/core/publish/volumes.py +108 -0
- package/app/core/publish/ws.py +139 -139
- package/app/core/quality_gate.py +194 -194
- package/app/core/redact.py +38 -38
- package/app/core/repeat_guard.py +129 -129
- package/app/core/retrieval.py +137 -137
- package/app/core/router.py +398 -398
- package/app/core/runner.py +2239 -2238
- package/app/core/selfupdate.py +384 -384
- package/app/core/session_log.py +161 -161
- package/app/core/sessions.py +312 -312
- package/app/core/settings.py +33 -5
- package/app/core/settings_schema.py +282 -271
- package/app/core/share_page.py +114 -114
- package/app/core/skill_scan.py +139 -139
- package/app/core/skillpacks/fanqie-novel.md +8 -6
- package/app/core/skillpacks/market/character-bible.md +66 -66
- package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
- package/app/core/skillpacks/market/git-workflow.md +57 -57
- package/app/core/skillpacks/market/release-notes.md +72 -72
- package/app/core/skillpacks/market/weekly-report.md +71 -71
- package/app/core/skillpacks/market/worldview-consistency.md +70 -70
- package/app/core/skillpacks/qimao-signing.md +14 -11
- package/app/core/skills.py +32 -21
- package/app/core/step_runner.py +132 -132
- package/app/core/store.py +24 -0
- package/app/core/story_tracking.py +275 -275
- package/app/core/task_compile.py +199 -199
- package/app/core/telemetry.py +292 -292
- package/app/core/tlsctx.py +64 -64
- package/app/core/token_meter.py +147 -147
- package/app/core/tool_outputs.py +107 -0
- package/app/core/tracing.py +207 -144
- package/app/core/upstream.py +41 -41
- package/app/core/usage.py +1038 -1038
- package/app/core/volumes.py +389 -389
- package/app/core/webhooks.py +89 -0
- package/app/core/wxdigest.py +865 -858
- package/app/core/zentao.py +2692 -2692
- package/app/main.py +179 -10
- package/app/mcp_server.py +244 -244
- package/app/pick_dialog.py +110 -110
- package/app/ui/app.js +563 -21
- package/app/ui/artdiff.js +74 -74
- package/app/ui/assets/community-qr.png +0 -0
- package/app/ui/assets/donate-alipay.jpg +0 -0
- package/app/ui/assets/donate-qr.jpg +0 -0
- package/app/ui/assets/workbench-reference.png +0 -0
- package/app/ui/board.css +451 -666
- package/app/ui/board.html +165 -162
- package/app/ui/board.js +509 -305
- package/app/ui/hive3d.js +242 -0
- package/app/ui/i18n.js +125 -9
- package/app/ui/icons/bee.svg +79 -79
- package/app/ui/index.html +60 -3
- package/app/ui/manifest.json +16 -16
- package/app/ui/qrcode.js +2297 -2297
- package/app/ui/style.css +189 -9
- package/bin/tutti.js +147 -147
- package/package.json +49 -48
- package/tools/wx_reader.py +261 -261
- package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +0 -88
- package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +0 -126
- package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +0 -210
- package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
- package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
- package/app/.playwright-cli/run-detail-after.png +0 -0
- package/app/output/_createbook_body.json +0 -1
- package/app/output/_probe_fq_click.py +0 -50
- package/app/output/_probe_fq_form.py +0 -42
- package/app/output/_probe_fq_modal.py +0 -55
- package/app/output/_probe_fq_points.py +0 -71
- package/app/output/_probe_fq_submit.py +0 -34
- package/app/output/_probe_fq_watch.py +0 -62
package/app/core/share_page.py
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""可分享报告页(借鉴 agency-orchestrator 的 ao report):把 run 成果渲染成
|
|
3
|
-
自包含单文件 HTML——零 JS、内联样式、离线可看,直接发群/发人。
|
|
4
|
-
|
|
5
|
-
纯函数无网络;数据全部来自调用方(store/paths 已读好的对象)。
|
|
6
|
-
"""
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import html as _html
|
|
10
|
-
import time
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _esc(s):
|
|
14
|
-
return _html.escape(str(s if s is not None else ""), quote=True)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _md_ish(text):
|
|
18
|
-
"""极简 Markdown 呈现:标题/列表/粗体/代码,够报告阅读即可,不做完整解析。"""
|
|
19
|
-
out = []
|
|
20
|
-
for ln in (text or "").splitlines():
|
|
21
|
-
e = _esc(ln)
|
|
22
|
-
if ln.startswith("# "):
|
|
23
|
-
out.append("<h1>%s</h1>" % e[2:])
|
|
24
|
-
elif ln.startswith("## "):
|
|
25
|
-
out.append("<h2>%s</h2>" % e[3:])
|
|
26
|
-
elif ln.startswith("### "):
|
|
27
|
-
out.append("<h3>%s</h3>" % e[4:])
|
|
28
|
-
elif ln.startswith("- "):
|
|
29
|
-
out.append("<li>%s</li>" % e[2:])
|
|
30
|
-
else:
|
|
31
|
-
out.append("<p>%s</p>" % e)
|
|
32
|
-
return "\n".join(out)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
_SHARE_TPL = """<!DOCTYPE html>
|
|
36
|
-
<html lang="zh"><head><meta charset="utf-8">
|
|
37
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
38
|
-
<title>__TITLE__ · CodeBee 任务报告</title>
|
|
39
|
-
<style>
|
|
40
|
-
body{margin:0;background:#f5f6f8;color:#1c2330;font:15px/1.65 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
|
|
41
|
-
.wrap{max-width:760px;margin:0 auto;padding:28px 18px 60px}
|
|
42
|
-
.head{border-bottom:3px solid #2b6cb0;padding-bottom:14px;margin-bottom:22px}
|
|
43
|
-
h1{margin:0 0 6px;font-size:24px}
|
|
44
|
-
.meta{color:#5a6472;font-size:13px}
|
|
45
|
-
.badge{display:inline-block;border-radius:999px;padding:2px 12px;font-size:13px;font-weight:600}
|
|
46
|
-
.badge.ok{background:#e6f6ec;color:#177245}.badge.bad{background:#fdeaea;color:#b42318}
|
|
47
|
-
.badge.off{background:#eef0f3;color:#5a6472}
|
|
48
|
-
.card{background:#fff;border:1px solid #e3e6ea;border-radius:12px;padding:18px 20px;margin-bottom:18px}
|
|
49
|
-
.card h2{font-size:16px;margin:0 0 12px;color:#2b6cb0}
|
|
50
|
-
table{border-collapse:collapse;width:100%}
|
|
51
|
-
td{padding:6px 10px;border-bottom:1px solid #eef0f3}
|
|
52
|
-
tr.sum td{font-weight:700;border-top:2px solid #e3e6ea}
|
|
53
|
-
.step{display:flex;gap:10px;padding:7px 0;border-bottom:1px dashed #eef0f3;font-size:13.5px;align-items:baseline}
|
|
54
|
-
.step .n{color:#8a93a0;font-weight:700}.step .role{font-weight:600;min-width:88px}
|
|
55
|
-
.step .who{color:#5a6472;min-width:90px}.step .sum{flex:1}.st{font-size:12px}
|
|
56
|
-
.st.done{color:#177245}.st.failed{color:#b42318}
|
|
57
|
-
h1,h2,h3{margin:14px 0 8px}li{margin:4px 0}.trow{padding:2px 0;border-bottom:1px dotted #eee}
|
|
58
|
-
p{margin:8px 0}.mut{color:#8a93a0}code{background:#f0f2f5;padding:1px 6px;border-radius:4px}
|
|
59
|
-
pre{background:#f0f2f5;padding:12px;border-radius:8px;overflow-x:auto;white-space:pre-wrap}
|
|
60
|
-
</style></head><body><div class="wrap">
|
|
61
|
-
<div class="head"><h1>__TITLE__</h1>
|
|
62
|
-
<div class="meta">__META__ · __ST__ · CodeBee 生成于 __NOW__</div></div>
|
|
63
|
-
<div class="card"><h2>结果</h2><span class="badge __STCLS__">__ST__</span>__OVERALL__</div>
|
|
64
|
-
<div class="card"><h2>步骤</h2>__STEPS__</div>
|
|
65
|
-
<div class="card"><h2>报告</h2>__REPORT__</div>
|
|
66
|
-
<div class="meta">由 <b>CodeBee</b> 多智能体编排台生成</div>
|
|
67
|
-
</div></body></html>"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def render_share_html(run, task, report_text):
|
|
71
|
-
"""渲染自包含分享页。run/task/report_text 缺失时优雅降级。"""
|
|
72
|
-
run = run or {}
|
|
73
|
-
task = task or {}
|
|
74
|
-
status = str(run.get("status") or "")
|
|
75
|
-
st_label = {"done": "已完成", "failed": "失败", "cancelled": "已取消",
|
|
76
|
-
"running": "进行中"}.get(status, status)
|
|
77
|
-
st_cls = {"done": "ok", "failed": "bad", "cancelled": "off"}.get(status, "off")
|
|
78
|
-
verdict = run.get("verdict") or {}
|
|
79
|
-
means = verdict.get("scores") or {}
|
|
80
|
-
|
|
81
|
-
score_rows = ""
|
|
82
|
-
for d, v in means.items():
|
|
83
|
-
score_rows += "<tr><td>%s</td><td>%.1f</td></tr>" % (_esc(d), float(v))
|
|
84
|
-
if verdict:
|
|
85
|
-
score_rows += ('<tr class="sum"><td>综合</td>'
|
|
86
|
-
'<td>%.1f</td></tr>' % float(verdict.get("overall") or 0))
|
|
87
|
-
|
|
88
|
-
steps_rows = ""
|
|
89
|
-
for s in run.get("steps") or []:
|
|
90
|
-
who = s.get("agent_label") or s.get("agent") or ""
|
|
91
|
-
if s.get("model"):
|
|
92
|
-
who += " · " + str(s["model"])
|
|
93
|
-
steps_rows += (
|
|
94
|
-
'<div class="step"><span class="n">%02d</span>'
|
|
95
|
-
'<span class="role">%s</span><span class="who">%s</span>'
|
|
96
|
-
'<span class="sum">%s</span><span class="st %s">%s</span></div>'
|
|
97
|
-
% (s.get("n") or 0, _esc(s.get("role") or ""),
|
|
98
|
-
_esc(who),
|
|
99
|
-
_esc((s.get("summary") or "")[:220]), _esc(s.get("status") or ""),
|
|
100
|
-
_esc(s.get("status") or "")))
|
|
101
|
-
|
|
102
|
-
report_html = _md_ish(report_text) if report_text else "<p class='mut'>(暂无报告)</p>"
|
|
103
|
-
overall_note = ("综合 %.1f 分" % float(verdict.get("overall"))
|
|
104
|
-
if verdict.get("overall") is not None else "")
|
|
105
|
-
|
|
106
|
-
return (_SHARE_TPL
|
|
107
|
-
.replace("__TITLE__", _esc(task.get("title") or run.get("title") or "任务报告"))
|
|
108
|
-
.replace("__META__", _esc(task.get("goal") or ""))
|
|
109
|
-
.replace("__ST__", _esc(st_label))
|
|
110
|
-
.replace("__STCLS__", st_cls)
|
|
111
|
-
.replace("__NOW__", _esc(time.strftime("%Y-%m-%d %H:%M")))
|
|
112
|
-
.replace("__OVERALL__", _esc(overall_note))
|
|
113
|
-
.replace("__STEPS__", steps_rows or "<p class='mut'>(无步骤)</p>")
|
|
114
|
-
.replace("__REPORT__", report_html))
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""可分享报告页(借鉴 agency-orchestrator 的 ao report):把 run 成果渲染成
|
|
3
|
+
自包含单文件 HTML——零 JS、内联样式、离线可看,直接发群/发人。
|
|
4
|
+
|
|
5
|
+
纯函数无网络;数据全部来自调用方(store/paths 已读好的对象)。
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import html as _html
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _esc(s):
|
|
14
|
+
return _html.escape(str(s if s is not None else ""), quote=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _md_ish(text):
|
|
18
|
+
"""极简 Markdown 呈现:标题/列表/粗体/代码,够报告阅读即可,不做完整解析。"""
|
|
19
|
+
out = []
|
|
20
|
+
for ln in (text or "").splitlines():
|
|
21
|
+
e = _esc(ln)
|
|
22
|
+
if ln.startswith("# "):
|
|
23
|
+
out.append("<h1>%s</h1>" % e[2:])
|
|
24
|
+
elif ln.startswith("## "):
|
|
25
|
+
out.append("<h2>%s</h2>" % e[3:])
|
|
26
|
+
elif ln.startswith("### "):
|
|
27
|
+
out.append("<h3>%s</h3>" % e[4:])
|
|
28
|
+
elif ln.startswith("- "):
|
|
29
|
+
out.append("<li>%s</li>" % e[2:])
|
|
30
|
+
else:
|
|
31
|
+
out.append("<p>%s</p>" % e)
|
|
32
|
+
return "\n".join(out)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_SHARE_TPL = """<!DOCTYPE html>
|
|
36
|
+
<html lang="zh"><head><meta charset="utf-8">
|
|
37
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
38
|
+
<title>__TITLE__ · CodeBee 任务报告</title>
|
|
39
|
+
<style>
|
|
40
|
+
body{margin:0;background:#f5f6f8;color:#1c2330;font:15px/1.65 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
|
|
41
|
+
.wrap{max-width:760px;margin:0 auto;padding:28px 18px 60px}
|
|
42
|
+
.head{border-bottom:3px solid #2b6cb0;padding-bottom:14px;margin-bottom:22px}
|
|
43
|
+
h1{margin:0 0 6px;font-size:24px}
|
|
44
|
+
.meta{color:#5a6472;font-size:13px}
|
|
45
|
+
.badge{display:inline-block;border-radius:999px;padding:2px 12px;font-size:13px;font-weight:600}
|
|
46
|
+
.badge.ok{background:#e6f6ec;color:#177245}.badge.bad{background:#fdeaea;color:#b42318}
|
|
47
|
+
.badge.off{background:#eef0f3;color:#5a6472}
|
|
48
|
+
.card{background:#fff;border:1px solid #e3e6ea;border-radius:12px;padding:18px 20px;margin-bottom:18px}
|
|
49
|
+
.card h2{font-size:16px;margin:0 0 12px;color:#2b6cb0}
|
|
50
|
+
table{border-collapse:collapse;width:100%}
|
|
51
|
+
td{padding:6px 10px;border-bottom:1px solid #eef0f3}
|
|
52
|
+
tr.sum td{font-weight:700;border-top:2px solid #e3e6ea}
|
|
53
|
+
.step{display:flex;gap:10px;padding:7px 0;border-bottom:1px dashed #eef0f3;font-size:13.5px;align-items:baseline}
|
|
54
|
+
.step .n{color:#8a93a0;font-weight:700}.step .role{font-weight:600;min-width:88px}
|
|
55
|
+
.step .who{color:#5a6472;min-width:90px}.step .sum{flex:1}.st{font-size:12px}
|
|
56
|
+
.st.done{color:#177245}.st.failed{color:#b42318}
|
|
57
|
+
h1,h2,h3{margin:14px 0 8px}li{margin:4px 0}.trow{padding:2px 0;border-bottom:1px dotted #eee}
|
|
58
|
+
p{margin:8px 0}.mut{color:#8a93a0}code{background:#f0f2f5;padding:1px 6px;border-radius:4px}
|
|
59
|
+
pre{background:#f0f2f5;padding:12px;border-radius:8px;overflow-x:auto;white-space:pre-wrap}
|
|
60
|
+
</style></head><body><div class="wrap">
|
|
61
|
+
<div class="head"><h1>__TITLE__</h1>
|
|
62
|
+
<div class="meta">__META__ · __ST__ · CodeBee 生成于 __NOW__</div></div>
|
|
63
|
+
<div class="card"><h2>结果</h2><span class="badge __STCLS__">__ST__</span>__OVERALL__</div>
|
|
64
|
+
<div class="card"><h2>步骤</h2>__STEPS__</div>
|
|
65
|
+
<div class="card"><h2>报告</h2>__REPORT__</div>
|
|
66
|
+
<div class="meta">由 <b>CodeBee</b> 多智能体编排台生成</div>
|
|
67
|
+
</div></body></html>"""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_share_html(run, task, report_text):
|
|
71
|
+
"""渲染自包含分享页。run/task/report_text 缺失时优雅降级。"""
|
|
72
|
+
run = run or {}
|
|
73
|
+
task = task or {}
|
|
74
|
+
status = str(run.get("status") or "")
|
|
75
|
+
st_label = {"done": "已完成", "failed": "失败", "cancelled": "已取消",
|
|
76
|
+
"running": "进行中"}.get(status, status)
|
|
77
|
+
st_cls = {"done": "ok", "failed": "bad", "cancelled": "off"}.get(status, "off")
|
|
78
|
+
verdict = run.get("verdict") or {}
|
|
79
|
+
means = verdict.get("scores") or {}
|
|
80
|
+
|
|
81
|
+
score_rows = ""
|
|
82
|
+
for d, v in means.items():
|
|
83
|
+
score_rows += "<tr><td>%s</td><td>%.1f</td></tr>" % (_esc(d), float(v))
|
|
84
|
+
if verdict:
|
|
85
|
+
score_rows += ('<tr class="sum"><td>综合</td>'
|
|
86
|
+
'<td>%.1f</td></tr>' % float(verdict.get("overall") or 0))
|
|
87
|
+
|
|
88
|
+
steps_rows = ""
|
|
89
|
+
for s in run.get("steps") or []:
|
|
90
|
+
who = s.get("agent_label") or s.get("agent") or ""
|
|
91
|
+
if s.get("model"):
|
|
92
|
+
who += " · " + str(s["model"])
|
|
93
|
+
steps_rows += (
|
|
94
|
+
'<div class="step"><span class="n">%02d</span>'
|
|
95
|
+
'<span class="role">%s</span><span class="who">%s</span>'
|
|
96
|
+
'<span class="sum">%s</span><span class="st %s">%s</span></div>'
|
|
97
|
+
% (s.get("n") or 0, _esc(s.get("role") or ""),
|
|
98
|
+
_esc(who),
|
|
99
|
+
_esc((s.get("summary") or "")[:220]), _esc(s.get("status") or ""),
|
|
100
|
+
_esc(s.get("status") or "")))
|
|
101
|
+
|
|
102
|
+
report_html = _md_ish(report_text) if report_text else "<p class='mut'>(暂无报告)</p>"
|
|
103
|
+
overall_note = ("综合 %.1f 分" % float(verdict.get("overall"))
|
|
104
|
+
if verdict.get("overall") is not None else "")
|
|
105
|
+
|
|
106
|
+
return (_SHARE_TPL
|
|
107
|
+
.replace("__TITLE__", _esc(task.get("title") or run.get("title") or "任务报告"))
|
|
108
|
+
.replace("__META__", _esc(task.get("goal") or ""))
|
|
109
|
+
.replace("__ST__", _esc(st_label))
|
|
110
|
+
.replace("__STCLS__", st_cls)
|
|
111
|
+
.replace("__NOW__", _esc(time.strftime("%Y-%m-%d %H:%M")))
|
|
112
|
+
.replace("__OVERALL__", _esc(overall_note))
|
|
113
|
+
.replace("__STEPS__", steps_rows or "<p class='mut'>(无步骤)</p>")
|
|
114
|
+
.replace("__REPORT__", report_html))
|
package/app/core/skill_scan.py
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""skill 装前危险模式扫描(借鉴 NVIDIA SkillSpector 的静态扫描思路)。
|
|
3
|
-
|
|
4
|
-
研究数据(SkillSpector 对 31,132 个 skill 的分析):26.1% 含漏洞、5.2% 疑似
|
|
5
|
-
恶意。我们在「白名单闸门 + SSRF 防护 + 剥离式检查」之外补一道**内容静态扫描**:
|
|
6
|
-
装前扫 skill 正文里的危险模式,给风险提示——提示不是拦阻(用户仍可装),
|
|
7
|
-
但危险必须被看见。
|
|
8
|
-
|
|
9
|
-
纯内存函数:输入是文本,输出是发现列表,无网络无落盘。
|
|
10
|
-
"""
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
|
-
import re
|
|
14
|
-
|
|
15
|
-
# 危险模式(静态正则;命中即在装前提示行展示)
|
|
16
|
-
# —— 每条:(模式, 类别, 中文说明)
|
|
17
|
-
_PATTERNS = [
|
|
18
|
-
# 代码执行
|
|
19
|
-
(r"\beval\s*\(", "代码执行", "动态 eval 执行任意代码"),
|
|
20
|
-
(r"\bexec\s*\(", "代码执行", "exec 执行任意代码"),
|
|
21
|
-
(r"subprocess|os\.system|Popen", "代码执行", "起子进程执行命令"),
|
|
22
|
-
(r"child_process", "代码执行", "Node.js 子进程(可能执行任意命令)"),
|
|
23
|
-
(r"(?:curl|wget)[^\n|]*\|\s*(?:ba|z|da)?sh\b", "代码执行",
|
|
24
|
-
"下载内容直接管道进 shell 执行(dropper 特征)"),
|
|
25
|
-
(r"Invoke-Expression|\biex\s*\(|DownloadString", "代码执行",
|
|
26
|
-
"PowerShell 下载执行(dropper 特征)"),
|
|
27
|
-
# 数据外发
|
|
28
|
-
(r"https?://(?!api\.|docs\.|github\.com|raw\.githubusercontent)[a-z0-9.-]+/(upload|collect|track|ingest|webhook|callback)",
|
|
29
|
-
"数据外发", "向非常规端点上传/回调数据"),
|
|
30
|
-
(r"requests\.post|urllib\.request|fetch\s*\(", "网络请求", "发起网络请求(确认目标可信)"),
|
|
31
|
-
(r"base64.{0,10}(decode|b64decode|atob)", "数据外发",
|
|
32
|
-
"base64 解码(可能隐藏混淆载荷)"),
|
|
33
|
-
(r"pastebin\.com|webhook\.site|requestbin|pipedream\.net|"
|
|
34
|
-
r"ngrok\.(io|app|dev)|trycloudflare\.com", "数据外发",
|
|
35
|
-
"常见外传/中转信道(pastebin、ngrok 隧道、webhook.site 等)"),
|
|
36
|
-
# 持久化与挖矿(治理巡检 09-24 补:装后驻留与资源盗用的静态特征)
|
|
37
|
-
(r"\bcrontab\b|schtasks|LaunchAgents|CurrentVersion\\Run|StartupItems",
|
|
38
|
-
"持久化", "注册定时任务/自启动项(持久化驻留特征)"),
|
|
39
|
-
(r"stratum\+tcp|xmrig|cryptonight", "可疑意图", "加密货币挖矿特征"),
|
|
40
|
-
# 云元数据/内网端点探测(批4 治理 09-25:169.254.169.254 窃取云凭据是
|
|
41
|
-
# 真实攻击面,此前零覆盖)。只认高信号形态:链路本地元数据地址/云元数据
|
|
42
|
-
# 主机名,或私网地址搭配凭据路径——本地起服务提 127.0.0.1:3000 不误报。
|
|
43
|
-
(r"169\.254\.169\.254|169\.254\.\d{1,3}\.\d{1,3}|metadata\.google\.internal",
|
|
44
|
-
"可疑意图", "触碰云元数据端点(窃取云凭据特征)"),
|
|
45
|
-
(r"(?:192\.168\.\d{1,3}\.\d{1,3}|10\.\d{1,3}\.\d{1,3}\.\d{1,3})[^。\n]{0,40}"
|
|
46
|
-
r"(?:meta-data|credentials|secret|token|\.ssh)",
|
|
47
|
-
"可疑意图", "私网地址搭配凭据路径(内网探测特征)"),
|
|
48
|
-
# 敏感信息读取
|
|
49
|
-
(r"os\.environ|process\.env|getenv", "环境读取", "读取环境变量(可能带走密钥)"),
|
|
50
|
-
(r"\.ssh/|\.aws/|\.npmrc|\.gitconfig|credentials|\.env\b", "敏感文件", "触碰凭据/密钥文件路径"),
|
|
51
|
-
(r"keychain|credential manager|dpapi", "敏感文件", "访问系统凭据库"),
|
|
52
|
-
(r"\.claude/|\.zcode/|\.kimi-code/|\.codebee/", "敏感文件",
|
|
53
|
-
"触碰 AI 助手配置目录(可能篡改系统提示词或模型绑定)"),
|
|
54
|
-
# 提示注入特征
|
|
55
|
-
(r"(ignore|disregard|forget).{0,30}(previous|above|prior|all).{0,20}(instruction|prompt|rule)",
|
|
56
|
-
"提示注入", "指令覆盖话术(试图无视既有规则)"),
|
|
57
|
-
(r"(?:无视|忽略|跳过|忘记)[^。\n]{0,8}(?:之前|以上|前面|上述)[^。\n]{0,6}(?:指令|规则|要求|设定|约束)",
|
|
58
|
-
"提示注入", "中文指令覆盖话术(试图无视既有规则)"),
|
|
59
|
-
(r"system prompt|开发者指令|隐藏指令|系统提示词", "提示注入", "提及系统提示词/隐藏指令"),
|
|
60
|
-
(r"do not (tell|reveal|mention).{0,20}(user|human|player)", "提示注入", "要求对用户隐瞒行为"),
|
|
61
|
-
(r"(?:不要|切勿|别)[^。\n]{0,6}(?:告诉|透露|提及|说明|隐瞒)[^。\n]{0,8}(?:用户|主人|玩家)"
|
|
62
|
-
r"|(?:不要|切勿|别)[^。\n]{0,6}(?:用户|主人|玩家)[^。\n]{0,6}(?:隐瞒|透露|告诉|说明)"
|
|
63
|
-
r"|对(?:用户|主人|玩家)隐瞒",
|
|
64
|
-
"提示注入", "中文话术要求对用户隐瞒行为"),
|
|
65
|
-
# 反拒答/越权
|
|
66
|
-
(r"(you (are|must) (now|act as)|从此你是|你现在必须"
|
|
67
|
-
r"|从现在起你(?:是|扮演|成为|要[^。\n]{0,4}服从)|请你扮演)",
|
|
68
|
-
"越权人格", "试图重设助手人格"),
|
|
69
|
-
# SkillSpector 规则对表补充(0929 第四轮:AR/OH/TR 三族缺口)
|
|
70
|
-
(r"(do not|don't|never|不用|无需|不必)[^\n]{0,14}"
|
|
71
|
-
r"(refuse|decline|reject|reject)|(?:拒绝|推辞)[^\n]{0,6}(?:是)?(?:禁止|不允许|违规)"
|
|
72
|
-
r"|you (?:will|must) (?:always|never fail to) comply",
|
|
73
|
-
"提示注入", "反拒答抑制(试图关掉模型的拒绝能力)"),
|
|
74
|
-
(r"(omit|skip|remove)[^\n]{0,16}(disclaimer|warning|safety)"
|
|
75
|
-
r"|(?:省略|跳过|不要加|去掉)[^\n]{0,10}(?:免责|声明|安全(?:提示|警告|策略))",
|
|
76
|
-
"提示注入", "免责/安全声明抑制"),
|
|
77
|
-
(r"(?:your|the) (?:output|response|answer)[^\n]{0,30}"
|
|
78
|
-
r"(?:will be|is) (?:executed|run|evaluated as (?:code|commands?))"
|
|
79
|
-
r"|(?:输出|回答)(?:会|将)[^\n]{0,12}(?:被)?(?:直接)?(?:执行|当作命令|作为代码)",
|
|
80
|
-
"数据外发", "输出即执行指示(作答将被当命令跑——OH1 输出注入)"),
|
|
81
|
-
(r"(?:keyword|trigger|关键词|触发)[^\n]{0,50}"
|
|
82
|
-
r"(?:any|every|all|任何|所有|一切)|"
|
|
83
|
-
r"(?:any|every|all|任何|所有|一切)[^\n]{0,30}"
|
|
84
|
-
r"(?:keyword|trigger|关键词|触发)",
|
|
85
|
-
"可疑意图", "过宽触发条件(任何输入都激活——TR1 触发滥用)"),
|
|
86
|
-
(r"exfiltrat|渗透|后门|backdoor|keylog", "可疑意图", "包含可疑渗透/后门词汇"),
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
_COMPILED = [(re.compile(p, re.I), cat, desc) for p, cat, desc in _PATTERNS]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def scan_text(text, max_findings=12):
|
|
93
|
-
"""扫描一段 skill 文本。返回 [{category, detail, line}];空列表=干净。
|
|
94
|
-
|
|
95
|
-
line 是 1 起的行号(供装前提示定位)。同一类别只报首个命中
|
|
96
|
-
(提示行是给人看的,重复刷屏没有信息量)。"""
|
|
97
|
-
findings, seen_cats = [], set()
|
|
98
|
-
if not text:
|
|
99
|
-
return []
|
|
100
|
-
lines = text.splitlines()
|
|
101
|
-
for rx, cat, desc in _COMPILED:
|
|
102
|
-
if cat in seen_cats:
|
|
103
|
-
continue
|
|
104
|
-
for i, ln in enumerate(lines, 1):
|
|
105
|
-
if rx.search(ln):
|
|
106
|
-
findings.append({"category": cat, "detail": desc, "line": i})
|
|
107
|
-
seen_cats.add(cat)
|
|
108
|
-
break
|
|
109
|
-
if len(findings) >= max_findings:
|
|
110
|
-
break
|
|
111
|
-
return findings
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def risk_label(findings):
|
|
115
|
-
"""发现列表 → 风险标签(装前提示行用)。
|
|
116
|
-
|
|
117
|
-
三级:高危(代码执行/敏感文件/可疑意图/数据外发/持久化)任一
|
|
118
|
-
命中 = 高风险;中危类(网络请求/环境读取/提示注入/越权人格)≥2 类
|
|
119
|
-
同时命中 = 中风险;有发现 = 注意;空 = 干净。"""
|
|
120
|
-
if not findings:
|
|
121
|
-
return ""
|
|
122
|
-
cats = {f["category"] for f in findings}
|
|
123
|
-
if cats & {"代码执行", "敏感文件", "可疑意图", "数据外发", "持久化"}:
|
|
124
|
-
return "⚠ 高风险"
|
|
125
|
-
medium_cats = cats & {"网络请求", "环境读取", "提示注入", "越权人格"}
|
|
126
|
-
if len(medium_cats) >= 2:
|
|
127
|
-
return "⚠ 中风险"
|
|
128
|
-
return "△ 注意"
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def scan_summary(text):
|
|
132
|
-
"""一步到位:扫描+汇总成一行提示文案(空=干净返回空串)。"""
|
|
133
|
-
fs = scan_text(text)
|
|
134
|
-
if not fs:
|
|
135
|
-
return ""
|
|
136
|
-
label = risk_label(fs)
|
|
137
|
-
top = "、".join("%s(行%d)" % (f["category"], f["line"]) for f in fs[:4])
|
|
138
|
-
more = " 等 %d 项" % len(fs) if len(fs) > 4 else ""
|
|
139
|
-
return "%s:%s%s" % (label, top, more)
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""skill 装前危险模式扫描(借鉴 NVIDIA SkillSpector 的静态扫描思路)。
|
|
3
|
+
|
|
4
|
+
研究数据(SkillSpector 对 31,132 个 skill 的分析):26.1% 含漏洞、5.2% 疑似
|
|
5
|
+
恶意。我们在「白名单闸门 + SSRF 防护 + 剥离式检查」之外补一道**内容静态扫描**:
|
|
6
|
+
装前扫 skill 正文里的危险模式,给风险提示——提示不是拦阻(用户仍可装),
|
|
7
|
+
但危险必须被看见。
|
|
8
|
+
|
|
9
|
+
纯内存函数:输入是文本,输出是发现列表,无网络无落盘。
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
# 危险模式(静态正则;命中即在装前提示行展示)
|
|
16
|
+
# —— 每条:(模式, 类别, 中文说明)
|
|
17
|
+
_PATTERNS = [
|
|
18
|
+
# 代码执行
|
|
19
|
+
(r"\beval\s*\(", "代码执行", "动态 eval 执行任意代码"),
|
|
20
|
+
(r"\bexec\s*\(", "代码执行", "exec 执行任意代码"),
|
|
21
|
+
(r"subprocess|os\.system|Popen", "代码执行", "起子进程执行命令"),
|
|
22
|
+
(r"child_process", "代码执行", "Node.js 子进程(可能执行任意命令)"),
|
|
23
|
+
(r"(?:curl|wget)[^\n|]*\|\s*(?:ba|z|da)?sh\b", "代码执行",
|
|
24
|
+
"下载内容直接管道进 shell 执行(dropper 特征)"),
|
|
25
|
+
(r"Invoke-Expression|\biex\s*\(|DownloadString", "代码执行",
|
|
26
|
+
"PowerShell 下载执行(dropper 特征)"),
|
|
27
|
+
# 数据外发
|
|
28
|
+
(r"https?://(?!api\.|docs\.|github\.com|raw\.githubusercontent)[a-z0-9.-]+/(upload|collect|track|ingest|webhook|callback)",
|
|
29
|
+
"数据外发", "向非常规端点上传/回调数据"),
|
|
30
|
+
(r"requests\.post|urllib\.request|fetch\s*\(", "网络请求", "发起网络请求(确认目标可信)"),
|
|
31
|
+
(r"base64.{0,10}(decode|b64decode|atob)", "数据外发",
|
|
32
|
+
"base64 解码(可能隐藏混淆载荷)"),
|
|
33
|
+
(r"pastebin\.com|webhook\.site|requestbin|pipedream\.net|"
|
|
34
|
+
r"ngrok\.(io|app|dev)|trycloudflare\.com", "数据外发",
|
|
35
|
+
"常见外传/中转信道(pastebin、ngrok 隧道、webhook.site 等)"),
|
|
36
|
+
# 持久化与挖矿(治理巡检 09-24 补:装后驻留与资源盗用的静态特征)
|
|
37
|
+
(r"\bcrontab\b|schtasks|LaunchAgents|CurrentVersion\\Run|StartupItems",
|
|
38
|
+
"持久化", "注册定时任务/自启动项(持久化驻留特征)"),
|
|
39
|
+
(r"stratum\+tcp|xmrig|cryptonight", "可疑意图", "加密货币挖矿特征"),
|
|
40
|
+
# 云元数据/内网端点探测(批4 治理 09-25:169.254.169.254 窃取云凭据是
|
|
41
|
+
# 真实攻击面,此前零覆盖)。只认高信号形态:链路本地元数据地址/云元数据
|
|
42
|
+
# 主机名,或私网地址搭配凭据路径——本地起服务提 127.0.0.1:3000 不误报。
|
|
43
|
+
(r"169\.254\.169\.254|169\.254\.\d{1,3}\.\d{1,3}|metadata\.google\.internal",
|
|
44
|
+
"可疑意图", "触碰云元数据端点(窃取云凭据特征)"),
|
|
45
|
+
(r"(?:192\.168\.\d{1,3}\.\d{1,3}|10\.\d{1,3}\.\d{1,3}\.\d{1,3})[^。\n]{0,40}"
|
|
46
|
+
r"(?:meta-data|credentials|secret|token|\.ssh)",
|
|
47
|
+
"可疑意图", "私网地址搭配凭据路径(内网探测特征)"),
|
|
48
|
+
# 敏感信息读取
|
|
49
|
+
(r"os\.environ|process\.env|getenv", "环境读取", "读取环境变量(可能带走密钥)"),
|
|
50
|
+
(r"\.ssh/|\.aws/|\.npmrc|\.gitconfig|credentials|\.env\b", "敏感文件", "触碰凭据/密钥文件路径"),
|
|
51
|
+
(r"keychain|credential manager|dpapi", "敏感文件", "访问系统凭据库"),
|
|
52
|
+
(r"\.claude/|\.zcode/|\.kimi-code/|\.codebee/", "敏感文件",
|
|
53
|
+
"触碰 AI 助手配置目录(可能篡改系统提示词或模型绑定)"),
|
|
54
|
+
# 提示注入特征
|
|
55
|
+
(r"(ignore|disregard|forget).{0,30}(previous|above|prior|all).{0,20}(instruction|prompt|rule)",
|
|
56
|
+
"提示注入", "指令覆盖话术(试图无视既有规则)"),
|
|
57
|
+
(r"(?:无视|忽略|跳过|忘记)[^。\n]{0,8}(?:之前|以上|前面|上述)[^。\n]{0,6}(?:指令|规则|要求|设定|约束)",
|
|
58
|
+
"提示注入", "中文指令覆盖话术(试图无视既有规则)"),
|
|
59
|
+
(r"system prompt|开发者指令|隐藏指令|系统提示词", "提示注入", "提及系统提示词/隐藏指令"),
|
|
60
|
+
(r"do not (tell|reveal|mention).{0,20}(user|human|player)", "提示注入", "要求对用户隐瞒行为"),
|
|
61
|
+
(r"(?:不要|切勿|别)[^。\n]{0,6}(?:告诉|透露|提及|说明|隐瞒)[^。\n]{0,8}(?:用户|主人|玩家)"
|
|
62
|
+
r"|(?:不要|切勿|别)[^。\n]{0,6}(?:用户|主人|玩家)[^。\n]{0,6}(?:隐瞒|透露|告诉|说明)"
|
|
63
|
+
r"|对(?:用户|主人|玩家)隐瞒",
|
|
64
|
+
"提示注入", "中文话术要求对用户隐瞒行为"),
|
|
65
|
+
# 反拒答/越权
|
|
66
|
+
(r"(you (are|must) (now|act as)|从此你是|你现在必须"
|
|
67
|
+
r"|从现在起你(?:是|扮演|成为|要[^。\n]{0,4}服从)|请你扮演)",
|
|
68
|
+
"越权人格", "试图重设助手人格"),
|
|
69
|
+
# SkillSpector 规则对表补充(0929 第四轮:AR/OH/TR 三族缺口)
|
|
70
|
+
(r"(do not|don't|never|不用|无需|不必)[^\n]{0,14}"
|
|
71
|
+
r"(refuse|decline|reject|reject)|(?:拒绝|推辞)[^\n]{0,6}(?:是)?(?:禁止|不允许|违规)"
|
|
72
|
+
r"|you (?:will|must) (?:always|never fail to) comply",
|
|
73
|
+
"提示注入", "反拒答抑制(试图关掉模型的拒绝能力)"),
|
|
74
|
+
(r"(omit|skip|remove)[^\n]{0,16}(disclaimer|warning|safety)"
|
|
75
|
+
r"|(?:省略|跳过|不要加|去掉)[^\n]{0,10}(?:免责|声明|安全(?:提示|警告|策略))",
|
|
76
|
+
"提示注入", "免责/安全声明抑制"),
|
|
77
|
+
(r"(?:your|the) (?:output|response|answer)[^\n]{0,30}"
|
|
78
|
+
r"(?:will be|is) (?:executed|run|evaluated as (?:code|commands?))"
|
|
79
|
+
r"|(?:输出|回答)(?:会|将)[^\n]{0,12}(?:被)?(?:直接)?(?:执行|当作命令|作为代码)",
|
|
80
|
+
"数据外发", "输出即执行指示(作答将被当命令跑——OH1 输出注入)"),
|
|
81
|
+
(r"(?:keyword|trigger|关键词|触发)[^\n]{0,50}"
|
|
82
|
+
r"(?:any|every|all|任何|所有|一切)|"
|
|
83
|
+
r"(?:any|every|all|任何|所有|一切)[^\n]{0,30}"
|
|
84
|
+
r"(?:keyword|trigger|关键词|触发)",
|
|
85
|
+
"可疑意图", "过宽触发条件(任何输入都激活——TR1 触发滥用)"),
|
|
86
|
+
(r"exfiltrat|渗透|后门|backdoor|keylog", "可疑意图", "包含可疑渗透/后门词汇"),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
_COMPILED = [(re.compile(p, re.I), cat, desc) for p, cat, desc in _PATTERNS]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def scan_text(text, max_findings=12):
|
|
93
|
+
"""扫描一段 skill 文本。返回 [{category, detail, line}];空列表=干净。
|
|
94
|
+
|
|
95
|
+
line 是 1 起的行号(供装前提示定位)。同一类别只报首个命中
|
|
96
|
+
(提示行是给人看的,重复刷屏没有信息量)。"""
|
|
97
|
+
findings, seen_cats = [], set()
|
|
98
|
+
if not text:
|
|
99
|
+
return []
|
|
100
|
+
lines = text.splitlines()
|
|
101
|
+
for rx, cat, desc in _COMPILED:
|
|
102
|
+
if cat in seen_cats:
|
|
103
|
+
continue
|
|
104
|
+
for i, ln in enumerate(lines, 1):
|
|
105
|
+
if rx.search(ln):
|
|
106
|
+
findings.append({"category": cat, "detail": desc, "line": i})
|
|
107
|
+
seen_cats.add(cat)
|
|
108
|
+
break
|
|
109
|
+
if len(findings) >= max_findings:
|
|
110
|
+
break
|
|
111
|
+
return findings
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def risk_label(findings):
|
|
115
|
+
"""发现列表 → 风险标签(装前提示行用)。
|
|
116
|
+
|
|
117
|
+
三级:高危(代码执行/敏感文件/可疑意图/数据外发/持久化)任一
|
|
118
|
+
命中 = 高风险;中危类(网络请求/环境读取/提示注入/越权人格)≥2 类
|
|
119
|
+
同时命中 = 中风险;有发现 = 注意;空 = 干净。"""
|
|
120
|
+
if not findings:
|
|
121
|
+
return ""
|
|
122
|
+
cats = {f["category"] for f in findings}
|
|
123
|
+
if cats & {"代码执行", "敏感文件", "可疑意图", "数据外发", "持久化"}:
|
|
124
|
+
return "⚠ 高风险"
|
|
125
|
+
medium_cats = cats & {"网络请求", "环境读取", "提示注入", "越权人格"}
|
|
126
|
+
if len(medium_cats) >= 2:
|
|
127
|
+
return "⚠ 中风险"
|
|
128
|
+
return "△ 注意"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def scan_summary(text):
|
|
132
|
+
"""一步到位:扫描+汇总成一行提示文案(空=干净返回空串)。"""
|
|
133
|
+
fs = scan_text(text)
|
|
134
|
+
if not fs:
|
|
135
|
+
return ""
|
|
136
|
+
label = risk_label(fs)
|
|
137
|
+
top = "、".join("%s(行%d)" % (f["category"], f["line"]) for f in fs[:4])
|
|
138
|
+
more = " 等 %d 项" % len(fs) if len(fs) > 4 else ""
|
|
139
|
+
return "%s:%s%s" % (label, top, more)
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
- **C级:单个案例、固定字数或传闻算法权重**:不作硬门槛,不宣称推荐、签约或收入因果;没有样本就保留为待验证。
|
|
13
13
|
- 质量报告按“文本质量 → 平台适配 → 当前政策”分层。每个结论都给出证据位置、未知项和可逆改动;完读率、追读率等数字是定位线索,不是文学评分。
|
|
14
14
|
|
|
15
|
+
先确认“不过”指内容/合规审核、建书校验、签约评估、章节发布还是读者数据表现。没有平台明确原文时,报告只能叫内部文本诊断;经验不能证明审核原因或推荐算法。大纲和修改建议不得把固定字数、黄金三章、每章爽点/钩子、危机开局等当硬条件。按具体作品与目标读者评估承诺兑现、人物选择、因果、情绪和叙事节奏,允许有功能的慢热、群像、安静章节和多种情绪回报。
|
|
16
|
+
|
|
15
17
|
### 番茄与通用质量底线
|
|
16
18
|
|
|
17
19
|
先检查具体事件是否触发主角主动选择,并产生可见后果、阶段性兑现和新的问题;再检查人物动机、因果连续性、文风一致、移动端可读性与内容安全。算法指标不能替代逐章阅读,也不能把“数据暂时好”写成质量已证实。
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
- 世界观说明文、慢热日常、多视角群像和倒叙解谜可列为待验证风险;不能直接把某种写法归因成数据崩溃。
|
|
41
43
|
|
|
42
44
|
## 三、节奏与爽点
|
|
43
|
-
-
|
|
45
|
+
- 逐章检查叙事功能与阅读体验;“变化、情绪波动或兑现”是常见观察角度,不要求每章同时满足,也不要求章章以疑问/悬念收尾。1800-2500 字可作为节奏实验起点,按题材和章节目标调整,不作硬门槛。
|
|
44
46
|
- 建议先用一句话说明主线目标和下一步;多线叙事、慢热铺垫可列为待验证风险,
|
|
45
47
|
先用章节目标、读者反馈和后台分段数据复核,不预设结果。
|
|
46
48
|
- 早期长线应安排一次完整的大兑现(打脸/升级/身份跃迁等),具体章节不固定;连续多个章节无兑现时,先回到事件、选择、后果链查找原因,不把数据变化直接归因于某个数字。
|
|
@@ -72,8 +74,8 @@
|
|
|
72
74
|
文白/网络梗/书面长句不混用。
|
|
73
75
|
- **流畅**:逐段读出声——长定语、多重倒装、连续同主语开头(她…她…她…)、动作与反应拆碎,
|
|
74
76
|
卡顿处就地删改;一句一动作,段尾留半句承接。
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
+
- **细节刻画力度**:重要情绪/转折是否按其叙事分量呈现。可使用特写、对白、动作、内心直述或留白;
|
|
78
|
+
不要求所有爽点都扩写,也不为每章制造高潮。检查问题必须由实际文本支持。
|
|
77
79
|
|
|
78
80
|
## 八、后台数据诊断卡(仅在样本足够时使用)
|
|
79
81
|
|
|
@@ -88,9 +90,9 @@
|
|
|
88
90
|
|
|
89
91
|
## 九、开书/投稿前自检清单
|
|
90
92
|
- [ ] 书名/标签/简介一致表达题材,分类挂对
|
|
91
|
-
- [ ]
|
|
92
|
-
- [ ]
|
|
93
|
-
- [ ]
|
|
93
|
+
- [ ] 开篇是否尽早兑现作品承诺(具体位置随题材调整);前三章是否让读者理解故事方向并得到相称回报,不强制同一节拍模板
|
|
94
|
+
- [ ] 每章叙事功能清楚,章节收束方式适合当前节奏;字数仅作节奏实验,不作硬门槛
|
|
95
|
+
- [ ] 主线和近期方向可复述;多线/慢热铺垫是否有清楚功能,避免无效延宕
|
|
94
96
|
- [ ] 早期长线有一次大兑现闭环的规划(章节位置可调整)
|
|
95
97
|
- [ ] 存稿和日更承诺可持续,并已核对当前合同/后台(不套用固定数字)
|
|
96
98
|
- [ ] 文笔三词(风格统一/流畅/细节力度)逐章自查过
|