dsh-math-calculator 2.0.0 → 2.1.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.
- package/CHANGELOG.md +73 -0
- package/README.md +52 -10
- package/dsh.plugin.json +15 -8
- package/lib/awareness.js +16 -13
- package/lib/body-catalog.js +488 -0
- package/lib/body-scaling.js +288 -0
- package/lib/body-taxonomy.js +719 -0
- package/lib/detector.js +361 -16
- package/lib/empirical-decomposition.js +321 -0
- package/lib/format.js +113 -5
- package/lib/formula-library-generic.js +226 -0
- package/lib/formulas.js +71 -13
- package/lib/index.js +613 -77
- package/lib/ops/bodies-op.js +368 -0
- package/lib/ops/shared.js +64 -4
- package/lib/ops/workspace-op.js +19 -1
- package/lib/ops/world-op.js +22 -0
- package/lib/playbook.js +6 -1
- package/lib/precision-report.js +80 -0
- package/lib/result-cache.js +144 -0
- package/lib/types/awareness.d.ts +10 -4
- package/lib/types/body-catalog.d.ts +156 -0
- package/lib/types/body-scaling.d.ts +123 -0
- package/lib/types/body-taxonomy.d.ts +95 -0
- package/lib/types/detector.d.ts +18 -0
- package/lib/types/empirical-decomposition.d.ts +107 -0
- package/lib/types/format.d.ts +33 -4
- package/lib/types/formula-library-generic.d.ts +37 -0
- package/lib/types/formulas.d.ts +35 -7
- package/lib/types/index.d.ts +16 -0
- package/lib/types/ops/bodies-op.d.ts +35 -0
- package/lib/types/ops/shared.d.ts +94 -3
- package/lib/types/precision-report.d.ts +59 -0
- package/lib/types/result-cache.d.ts +78 -0
- package/lib/types/workspace.d.ts +24 -0
- package/lib/types/world.d.ts +8 -0
- package/lib/workspace.js +22 -0
- package/lib/world.js +38 -3
- package/package.json +1 -1
- package/src/awareness.ts +16 -13
- package/src/body-catalog.ts +619 -0
- package/src/body-scaling.ts +357 -0
- package/src/body-taxonomy.ts +806 -0
- package/src/detector.ts +403 -15
- package/src/empirical-decomposition.ts +411 -0
- package/src/format.ts +123 -5
- package/src/formula-library-generic.ts +242 -0
- package/src/formulas.ts +79 -13
- package/src/index.ts +642 -64
- package/src/ops/bodies-op.ts +415 -0
- package/src/ops/shared.ts +141 -4
- package/src/ops/workspace-op.ts +21 -1
- package/src/ops/world-op.ts +22 -0
- package/src/playbook.ts +6 -1
- package/src/precision-report.ts +87 -0
- package/src/result-cache.ts +157 -0
- package/src/workspace.ts +34 -0
- package/src/world.ts +51 -4
package/lib/types/format.d.ts
CHANGED
|
@@ -19,12 +19,41 @@ export declare function safeNumber(value: number): JsonValue;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare function isIntegerLike(value: any): boolean;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* 格式化输出值:**整数完整,小数按「小数点后几位」截断**。
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* ## 计算精度 vs 显示精度
|
|
25
|
+
*
|
|
26
|
+
* `precision` 是计算精度(BigNumber 的有效位数),拿它直接当显示位数有两个毛病:
|
|
27
|
+
* · 64 位全吐出来,一行里六十个数字,人和 AI 都读不下去
|
|
28
|
+
* · 真调低 precision 又会**算错大数** —— 实测 `(10^20+1)-(10^20)`
|
|
29
|
+
* 在 precision=20 时得 0(应为 1)
|
|
30
|
+
*
|
|
31
|
+
* 所以拆成两层:计算永远按 `precision`(默认 64)走,显示用 `displayPrecision`。
|
|
32
|
+
*
|
|
33
|
+
* ## displayPrecision 的语义是「小数点后几位」,不是「有效数字」
|
|
34
|
+
*
|
|
35
|
+
* 这个区别很重要,实测对比:
|
|
36
|
+
*
|
|
37
|
+
* 数值 有效数字=8(旧) 小数位=8(现在)
|
|
38
|
+
* 12345.678901234 12345.679 12345.67890123
|
|
39
|
+
* 1/7 0.14285714 0.14285714
|
|
40
|
+
* 2^0.5 1.4142136 1.41421356
|
|
41
|
+
*
|
|
42
|
+
* 说「保留 8 位」时想的是小数位 —— 而有效数字语义下,
|
|
43
|
+
* 数字越大得到的小数位越少,`12345.678901234` 只给 3 位小数,与直觉不符。
|
|
44
|
+
*
|
|
45
|
+
* ## 整数不做截断
|
|
46
|
+
*
|
|
47
|
+
* `10^20+5`、`50!` 这类整数照原样输出。把 `10^20+5` 写成 `1e20` 是**丢信息**,
|
|
48
|
+
* 不是简洁 —— 作者问「等于多少」,答案就该精确到个位。
|
|
49
|
+
*
|
|
50
|
+
* @param math - mathjs 实例
|
|
51
|
+
* @param value - 待格式化的值
|
|
52
|
+
* @param precision - 计算精度(BigNumber 有效位数)
|
|
53
|
+
* @param displayPrecision - **小数点后保留几位**;省略则输出完整精度
|
|
54
|
+
* @returns 显示串
|
|
26
55
|
*/
|
|
27
|
-
export declare function formatValue(math: any, value: any, precision: number): string;
|
|
56
|
+
export declare function formatValue(math: any, value: any, precision: number, displayPrecision?: number): string;
|
|
28
57
|
/**
|
|
29
58
|
* 把 mathjs 的任意返回值转成可 JSON 化的值。
|
|
30
59
|
* Matrix / Complex / Unit / Fraction / BigNumber / Range / ResultSet 分别处理。
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公式库第八批:**普适版经验式**(把烘焙的常数拆出来,让它们随世界变)。
|
|
3
|
+
*
|
|
4
|
+
* ## 这一批解决什么问题
|
|
5
|
+
*
|
|
6
|
+
* 前七批里有一批经验公式把常数**烘焙进了系数**:
|
|
7
|
+
*
|
|
8
|
+
* 船舶极限航速 = 1.34 × sqrt(L) ← 1.34 里藏着 g = 9.81
|
|
9
|
+
* 宜居带内边界 = sqrt(L / 1.1) ← 1.1 是「地球温室条件下的辐照阈值」
|
|
10
|
+
* 硬度估强度 = 3 × H × 9.80665e6 ← 9.8 是地球重力换算
|
|
11
|
+
*
|
|
12
|
+
* 它们在**地球**上对,但作者写 1.5 倍重力的星球时照抄就会错,而且错得隐蔽
|
|
13
|
+
* (数字看着正常,只是用了地球的常数)。
|
|
14
|
+
*
|
|
15
|
+
* 本批把这些式子重写成**显式暴露物理量**的形式:
|
|
16
|
+
*
|
|
17
|
+
* hull_speed_generic = Fr × sqrt(g × L) × 1.944 ← g 由 world 给出
|
|
18
|
+
* habitable_inner_generic = sqrt(L / (flux × S0)) ← S0 由 L☉/AU 推出
|
|
19
|
+
* hardness_yield_generic = 3 × H × g / 1e-6 × 1e6 ← g 可替换
|
|
20
|
+
*
|
|
21
|
+
* ## 与旧版并存,不是替换
|
|
22
|
+
*
|
|
23
|
+
* 旧的 `hull_speed`(1.34)**保留**:作者要写地球上的船时它更快更省事,
|
|
24
|
+
* 而且那是船舶工程界的标准式。新版只是给「非地球重力」的场景一个正确入口。
|
|
25
|
+
* 两版的 desc 都写明适用域,让作者知道该用哪个。
|
|
26
|
+
*
|
|
27
|
+
* ## 单位约定
|
|
28
|
+
*
|
|
29
|
+
* 同前几批:**SI 基本单位**,但保留各村行业惯用单位(节、kgf/mm²)并在参数说明里写明。
|
|
30
|
+
*
|
|
31
|
+
* @module dsh-math-calculator/formula-library-generic
|
|
32
|
+
*/
|
|
33
|
+
import type { FormulaDef } from './formula-library.js';
|
|
34
|
+
/**
|
|
35
|
+
* 第八批公式:把烘焙常数拆出来的普适形式。
|
|
36
|
+
*/
|
|
37
|
+
export declare const GENERIC_FORMULAS: FormulaDef[];
|
package/lib/types/formulas.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { CONSTANTS, type FormulaDef } from './formula-library.js';
|
|
14
14
|
/**
|
|
15
|
-
* 全部公式 = 主批次 + 扩展 + 感知层 + 故事 + 势力动力学 +
|
|
15
|
+
* 全部公式 = 主批次 + 扩展 + 感知层 + 故事 + 势力动力学 + 通用计算 + 普适经验式。
|
|
16
16
|
* 分批组织而不是单文件拼接,避免 formula-library.ts 无限膨胀。
|
|
17
17
|
*/
|
|
18
18
|
export declare const FORMULAS: FormulaDef[];
|
|
@@ -63,21 +63,49 @@ export declare function resolveParams(formula: FormulaDef, overrides: Overrides,
|
|
|
63
63
|
*/
|
|
64
64
|
export declare function buildFormulaScope(engine: any, values: Record<string, unknown>): Record<string, any>;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* 在一组公式里按相关度检索(**检索逻辑的唯一实现**)。
|
|
67
|
+
*
|
|
68
|
+
* 为什么要单独抽出来:这段逻辑曾经被**复制成两份** ——
|
|
69
|
+
* `formulas.ts` 里的 `findFormulas` 和 `index.ts` 里的 `findInCurrentWorkspace`。
|
|
70
|
+
* 于是修好了一份、另一份还是旧的:`formulas` 操作走 `findInCurrentWorkspace`,
|
|
71
|
+
* 真实工具里查「贷款月供」仍然是 0 条,而单元测试调 `findFormulas` 全部通过。
|
|
72
|
+
* **测试绿了,工具没修**。现在两边都调这个函数,物理上不可能再不一致。
|
|
73
|
+
*
|
|
74
|
+
* @param formulas - 待检索的公式集(内置全量,或「工作区叠加后的当前集」)
|
|
75
|
+
* @param query - 查询串
|
|
76
|
+
* @param limit - 最多返回条数
|
|
77
|
+
* @returns 按相关度排序的公式
|
|
78
|
+
*/
|
|
79
|
+
export declare function scoreFormulas(formulas: readonly FormulaDef[], query: string, limit?: number): FormulaDef[];
|
|
80
|
+
/**
|
|
81
|
+
* 查找公式(内置全量集)。支持 id 精确、名称子串、关键词、分类、同义词与中文子词。
|
|
68
82
|
*
|
|
69
83
|
* 排序优先级:整串精确 > id 精确 > 关键词精确 > id 子串 > 名称精确 > 名称子串 > 描述 > 分类。
|
|
70
84
|
* 注意:分类命中给的分要低,否则搜「重力」会把整个 physics/gravity
|
|
71
85
|
* 分类的公式都挤到最前面,把真正相关的 gravitation 埋掉。
|
|
72
86
|
*
|
|
73
|
-
* ⚠️ 词元匹配的分数按**词长加权**:命中 4 个字比命中 2 个字更相关。
|
|
74
|
-
* 否则「粮食产量」会把所有含「量」的公式都召回到同一档。
|
|
75
|
-
*
|
|
76
87
|
* @param query - 查询串(关键词 / 同义词 / 中文复合词都行)
|
|
77
|
-
* @param limit -
|
|
88
|
+
* @param limit - 最多返回条数
|
|
78
89
|
* @returns 按相关度排序的公式
|
|
79
90
|
*/
|
|
80
91
|
export declare function findFormulas(query: string, limit?: number): FormulaDef[];
|
|
92
|
+
/**
|
|
93
|
+
* 严格解析单个公式(不做子词/同义词模糊匹配)。
|
|
94
|
+
*
|
|
95
|
+
* 为什么必须与 `scoreFormulas` 分开:
|
|
96
|
+
* 检索(`formulas` 操作)追求「想得到」,模糊匹配是对的;
|
|
97
|
+
* 求值(`formula` 操作)追求「指哪儿打哪儿」,模糊匹配是灾难 ——
|
|
98
|
+
* 传 `'完全不存在的公式xyz'` 时,子词「公式」会命中别的公式,
|
|
99
|
+
* 于是本该报 `unknown_formula` 的地方变成 `missing_params`,
|
|
100
|
+
* 模型会以为「公式找到了,只是缺参数」,然后去补一堆无关参数。
|
|
101
|
+
*
|
|
102
|
+
* 这里只认四种关系:id 精确、名称精确、id 包含、名称包含。
|
|
103
|
+
*
|
|
104
|
+
* @param formulas - 待解析的公式集
|
|
105
|
+
* @param query - 公式 id 或名称
|
|
106
|
+
* @returns 唯一命中的公式;没有命中返回 `undefined`
|
|
107
|
+
*/
|
|
108
|
+
export declare function resolveFormulaStrict(formulas: readonly FormulaDef[], query: string): FormulaDef | undefined;
|
|
81
109
|
/** 某条公式的读者视角频次。 */
|
|
82
110
|
export declare function formulaFrequency(formula: FormulaDef): 'high' | 'medium' | 'low';
|
|
83
111
|
/** 按频次筛选公式。 */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -44,6 +44,22 @@ export interface MathResult {
|
|
|
44
44
|
message: string;
|
|
45
45
|
hint?: string;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* 本次结果来自缓存(未重新计算)。
|
|
49
|
+
*
|
|
50
|
+
* 为什么要暴露:AI 需要知道「这个数是刚算的还是回忆的」——
|
|
51
|
+
* 若它以为自己刚验证过而实际只是缓存,会把「没改过设定」误当成「验证通过」。
|
|
52
|
+
* 改动常数或切换工作区后缓存键会变,所以不会出现「改了设定还拿旧值」。
|
|
53
|
+
*/
|
|
54
|
+
cached?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 附加提示(渲染时展示给模型,**不进入 `result`**)。
|
|
57
|
+
*
|
|
58
|
+
* 为什么要分开:`result` 是数据通道,调用方会做精确相等比较;
|
|
59
|
+
* 往里拼提示会让 `0.1+0.2` 从 `0.3` 变成两行,破坏所有精确断言。
|
|
60
|
+
* 提示属于「给人/模型看的说明」,走这个字段即可。
|
|
61
|
+
*/
|
|
62
|
+
notice?: string;
|
|
47
63
|
}
|
|
48
64
|
/** 更新检测器配置。 */
|
|
49
65
|
export declare function configureDetector(config: Partial<DetectorConfig>): Partial<DetectorConfig>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* operation="bodies" 的实现:天体目录查询与常数传导。
|
|
3
|
+
*
|
|
4
|
+
* ## 三种用法
|
|
5
|
+
*
|
|
6
|
+
* ```jsonc
|
|
7
|
+
* // 1. 列出全部天体(按类别分组)
|
|
8
|
+
* { "operation": "bodies" }
|
|
9
|
+
*
|
|
10
|
+
* // 2. 查某个天体 / 按类别筛
|
|
11
|
+
* { "operation": "bodies", "bodyName": "mars" }
|
|
12
|
+
* { "operation": "bodies", "bodyKind": "planet-terrestrial" }
|
|
13
|
+
*
|
|
14
|
+
* // 3. 改常数后,看天体数据哪些该重算(**这是最容易出错的一步**)
|
|
15
|
+
* { "operation": "bodies", "bodyConstant": "alpha_fs", "bodyFactor": 2 }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## 第 3 种为什么重要
|
|
19
|
+
*
|
|
20
|
+
* 作者改了 α 之后,最容易犯两个**静默**的错:
|
|
21
|
+
* · 把行星质量也跟着改(错:质量与 α 无关,那是篡改设定)
|
|
22
|
+
* · 忘了恒星光度要变(错:气候结论还是旧的)
|
|
23
|
+
*
|
|
24
|
+
* 这里把「该变的」和「不该动的」分成两块明确列出来。
|
|
25
|
+
*
|
|
26
|
+
* @module dsh-math-calculator/ops/bodies-op
|
|
27
|
+
*/
|
|
28
|
+
import { type MathArgs, type Outcome } from './shared.js';
|
|
29
|
+
/**
|
|
30
|
+
* `bodies`:天体目录、类型学与常数传导。
|
|
31
|
+
*
|
|
32
|
+
* @param args - 工具参数
|
|
33
|
+
* @returns 结果文本与结构化值
|
|
34
|
+
*/
|
|
35
|
+
export declare function opBodies(args: MathArgs): Outcome;
|
|
@@ -21,7 +21,7 @@ import type { WorldParams } from '../world.js';
|
|
|
21
21
|
* "27 个 operation" 断言都直接依赖它。新增 operation 必须同步改三处,
|
|
22
22
|
* 否则模型看得到名字却调不通,或测试直接变红。
|
|
23
23
|
*/
|
|
24
|
-
export type MathOperation = 'auto' | 'evaluate' | 'solve' | 'system' | 'derivative' | 'integral' | 'simplify' | 'expand' | 'roots' | 'matrix' | 'eigen' | 'stats' | 'dist' | 'regression' | 'formula' | 'formulas' | 'ode' | 'world' | 'constants' | 'workspace' | 'impact' | 'quantities' | 'formula-table' | 'feasibility' | 'derivation' | 'custom' | 'units' | 'balance' | 'help';
|
|
24
|
+
export type MathOperation = 'auto' | 'evaluate' | 'solve' | 'system' | 'derivative' | 'integral' | 'simplify' | 'expand' | 'roots' | 'matrix' | 'eigen' | 'stats' | 'dist' | 'regression' | 'formula' | 'formulas' | 'ode' | 'world' | 'constants' | 'workspace' | 'impact' | 'quantities' | 'formula-table' | 'feasibility' | 'derivation' | 'custom' | 'units' | 'balance' | 'bodies' | 'help';
|
|
25
25
|
/**
|
|
26
26
|
* 所有 operation 共用的参数对象。
|
|
27
27
|
*
|
|
@@ -38,6 +38,49 @@ export interface MathArgs {
|
|
|
38
38
|
matrix?: number[][];
|
|
39
39
|
vector?: number[];
|
|
40
40
|
precision?: number;
|
|
41
|
+
/**
|
|
42
|
+
* **显示**精度:**小数点后几位**(与计算精度分开)。
|
|
43
|
+
*
|
|
44
|
+
* ## 为什么要分开
|
|
45
|
+
*
|
|
46
|
+
* `precision` 是**计算**精度:它决定 BigNumber 的有效位数,
|
|
47
|
+
* 调低会真的把大数算错 —— 实测 `(10^20+1)-(10^20)`:
|
|
48
|
+
*
|
|
49
|
+
* precision=64 → 1(正确)
|
|
50
|
+
* precision=20 → 0(算错了)
|
|
51
|
+
*
|
|
52
|
+
* 所以它不能为了让输出好看而调低。而 `displayPrecision` 只影响**写出来的位数**,
|
|
53
|
+
* 计算仍然按 `precision`(默认 64)进行 —— 大数运算照样精确,只是显示得短些。
|
|
54
|
+
*
|
|
55
|
+
* ## 语义是「小数点后几位」,不是「有效数字」
|
|
56
|
+
*
|
|
57
|
+
* 这个区别很大:
|
|
58
|
+
* 8 位小数 → 12345.678901234 得 12345.67890123
|
|
59
|
+
* 8 位有效数字 → 同一个数得 12345.679(只剩 3 位小数)
|
|
60
|
+
*
|
|
61
|
+
* 说「保留 8 位」时想的是小数位,所以按小数位实现。
|
|
62
|
+
*
|
|
63
|
+
* ## 怎么用
|
|
64
|
+
*
|
|
65
|
+
* 一次设定即固化到当前工作区(后续调用自动沿用),也可每次覆盖:
|
|
66
|
+
* { operation:'workspace', action2:'set', displayPrecision:8 } ← 固化
|
|
67
|
+
* { operation:'evaluate', expression:'1/7', displayPrecision:6 } ← 临时
|
|
68
|
+
*
|
|
69
|
+
* 默认 8 位小数;工程核对可给 12—16;整数永远完整,不受此参数影响。
|
|
70
|
+
*/
|
|
71
|
+
displayPrecision?: number;
|
|
72
|
+
/**
|
|
73
|
+
* 批量表达式(operation=evaluate 的批量模式)。
|
|
74
|
+
*
|
|
75
|
+
* 一次算多条,省掉多次工具往返的开销。每条独立报错,
|
|
76
|
+
* 不会因为一条失败就中断整批。
|
|
77
|
+
*
|
|
78
|
+
* 例:`{operation:'evaluate', expressions:['1/7','2^0.5','log(10)']}`
|
|
79
|
+
*
|
|
80
|
+
* 为什么值得:每次工具调用都有固定开销(往返 + 结果文本进入上下文),
|
|
81
|
+
* 算 5 条就多付 5 次。批量把 5 次往返压成 1 次。
|
|
82
|
+
*/
|
|
83
|
+
expressions?: string[];
|
|
41
84
|
data?: number[];
|
|
42
85
|
dataY?: number[];
|
|
43
86
|
distribution?: string;
|
|
@@ -174,6 +217,30 @@ export interface MathArgs {
|
|
|
174
217
|
supply: number;
|
|
175
218
|
unit?: string;
|
|
176
219
|
}>;
|
|
220
|
+
/** 查单个天体:id(mars)或中文名(火星)。 */
|
|
221
|
+
bodyName?: string;
|
|
222
|
+
/** 按类别筛:star / planet-terrestrial / moon / dwarf-planet … */
|
|
223
|
+
bodyKind?: string;
|
|
224
|
+
/**
|
|
225
|
+
* 改常数后看天体数据哪些该重算。
|
|
226
|
+
*
|
|
227
|
+
* 例:`bodyConstant:"alpha_fs", bodyFactor:2` 表示 α 变成两倍,
|
|
228
|
+
* 输出会分成「必须重算的量」与「绝不该动的量」两块——
|
|
229
|
+
* 后者尤其重要:行星质量、半径、轨道与 α 无关,改了就是篡改设定。
|
|
230
|
+
*/
|
|
231
|
+
bodyConstant?: string;
|
|
232
|
+
/** 常数变化倍数(默认 2)。 */
|
|
233
|
+
bodyFactor?: number;
|
|
234
|
+
/**
|
|
235
|
+
* 查天体**类型**(operation=bodies)。
|
|
236
|
+
*
|
|
237
|
+
* 与 bodyName 的区别:bodyName 查具名天体(火星、木卫二),
|
|
238
|
+
* bodyType 查类型学条目(中子星、热木星、戴森球)——
|
|
239
|
+
* 面向宇宙科幻创作,含理论预言与纯思辨类。
|
|
240
|
+
*/
|
|
241
|
+
bodyType?: string;
|
|
242
|
+
/** 按证据等级筛类型:observed / theoretical / speculative。 */
|
|
243
|
+
bodyEvidence?: string;
|
|
177
244
|
/** 叙事锚点 id(如 creatureHeight)。 */
|
|
178
245
|
anchor?: string;
|
|
179
246
|
/** 锚点想要的值(如 50 米)。 */
|
|
@@ -328,7 +395,31 @@ export declare function fast(): any;
|
|
|
328
395
|
* 换行会被 mathjs 当成语句结束而报错。
|
|
329
396
|
*/
|
|
330
397
|
export declare function normalizeSource(input: string): string;
|
|
331
|
-
/**
|
|
332
|
-
|
|
398
|
+
/**
|
|
399
|
+
* 设置本次调用的显示精度(在工具入口调用)。
|
|
400
|
+
*
|
|
401
|
+
* @param value - 显示有效位数;undefined = 不截断
|
|
402
|
+
*/
|
|
403
|
+
export declare function setActiveDisplayPrecision(value: number | undefined): void;
|
|
404
|
+
/**
|
|
405
|
+
* 取当前显示精度。
|
|
406
|
+
*
|
|
407
|
+
* @returns 显示有效位数;未设置返回 undefined
|
|
408
|
+
*/
|
|
409
|
+
export declare function currentDisplayPrecision(): number | undefined;
|
|
410
|
+
/**
|
|
411
|
+
* 数字修剪:去掉浮点噪声,按**小数点后几位**保留。
|
|
412
|
+
*
|
|
413
|
+
* 语义与 `formatValue` 的 `displayPrecision` 保持一致:**小数位**,不是有效数字。
|
|
414
|
+
*
|
|
415
|
+
* 为什么统一成小数位:说「保留 8 位」时想的是小数位。
|
|
416
|
+
* 有效数字语义下 `12345.678901234` 只给 3 位小数(12345.679),与直觉不符。
|
|
417
|
+
*
|
|
418
|
+
* @param value - 数值
|
|
419
|
+
* @param precision - 计算精度(未设显示精度时作为退路)
|
|
420
|
+
* @param displayPrecision - **小数点后保留几位**;省略则用模块级当前值
|
|
421
|
+
* @returns 显示串
|
|
422
|
+
*/
|
|
423
|
+
export declare function trimNumber(value: number, precision: number, displayPrecision?: number): string;
|
|
333
424
|
/** 把数字安全地塞进 JsonValue(非有限值转字符串)。 */
|
|
334
425
|
export declare function safeNumberForJson(value: number): JsonValue;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 显示精度的损失追踪:判断「当前位数够不够用」。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么需要这个模块
|
|
5
|
+
*
|
|
6
|
+
* 显示精度截断是**悄悄发生**的,作者与模型都不会察觉:
|
|
7
|
+
*
|
|
8
|
+
* 1/7 8 位小数 → 0.14285714 损失约 1e-9(无感)
|
|
9
|
+
* 123456.789012345 8 位小数 → 123456.78901235 损失约 5e-9(无感)
|
|
10
|
+
* 0.00000123456789 8 位小数 → 0.00000123 相对损失约 0.3%(有感!)
|
|
11
|
+
*
|
|
12
|
+
* 最后一个例子是关键:数值很小时,8 位小数只留下 3 位有效数字,
|
|
13
|
+
* 相对误差 0.3% —— 作者以为拿到的是精确值,实际已经偏了。
|
|
14
|
+
*
|
|
15
|
+
* 本模块只做一件事:记住本轮格式化过程中最大的相对误差,
|
|
16
|
+
* 让工具层能据此提醒「该调高 displayPrecision 了」。
|
|
17
|
+
*
|
|
18
|
+
* ## 为什么不写在 format.ts
|
|
19
|
+
*
|
|
20
|
+
* `format.ts` 不依赖任何其它模块,而 `shared.ts` 已经导入它。
|
|
21
|
+
* 把状态放进 `format.ts` 就得反向导入,形成循环。
|
|
22
|
+
* 单独一个模块两边都能用,依赖方向干净。
|
|
23
|
+
*
|
|
24
|
+
* ## 阈值
|
|
25
|
+
*
|
|
26
|
+
* 取 1e-4(万分之一):低于它的损失对创作与工程核对都无影响;
|
|
27
|
+
* 高于它说明显示值已经明显偏离真值,值得提醒一次。
|
|
28
|
+
*
|
|
29
|
+
* @module dsh-math-calculator/precision-report
|
|
30
|
+
*/
|
|
31
|
+
/** 允许的最大相对误差(万分之一)。超过就提醒。 */
|
|
32
|
+
export declare const PRECISION_LOSS_THRESHOLD = 0.0001;
|
|
33
|
+
/**
|
|
34
|
+
* 记录一次截断造成的相对误差。
|
|
35
|
+
*
|
|
36
|
+
* @param relativeLoss - |显示值−真值| / |真值|
|
|
37
|
+
*/
|
|
38
|
+
export declare function reportPrecisionLoss(relativeLoss: number): void;
|
|
39
|
+
/**
|
|
40
|
+
* 取出并清空本轮的最大相对误差。
|
|
41
|
+
*
|
|
42
|
+
* 清空是必要的:每次工具调用开始都要重新统计,
|
|
43
|
+
* 否则上一轮的损失会一直挂在本轮结果上。
|
|
44
|
+
*
|
|
45
|
+
* @returns 最大相对误差;0 表示没有可感知的损失
|
|
46
|
+
*/
|
|
47
|
+
export declare function consumePrecisionLoss(): number;
|
|
48
|
+
/**
|
|
49
|
+
* 标记本轮显示精度是否来自显式参数。
|
|
50
|
+
*
|
|
51
|
+
* @param explicit - 是否显式
|
|
52
|
+
*/
|
|
53
|
+
export declare function setDisplayPrecisionExplicit(explicit: boolean): void;
|
|
54
|
+
/**
|
|
55
|
+
* 当前显示精度是否来自显式参数。
|
|
56
|
+
*
|
|
57
|
+
* @returns 是否显式
|
|
58
|
+
*/
|
|
59
|
+
export declare function isDisplayPrecisionExplicit(): boolean;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 计算结果缓存:同样的输入不再算第二遍。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么值得做(而不只是优化)
|
|
5
|
+
*
|
|
6
|
+
* 反复算同一个式子在真实使用里很常见:
|
|
7
|
+
* · AI 校验自己的结论(「再算一次确认」)——输入完全相同
|
|
8
|
+
* · 同一批数据换着角度问(同一条 `1/7` 在几个不同问题里出现)
|
|
9
|
+
* · 推演时反复引用同一个常量式
|
|
10
|
+
*
|
|
11
|
+
* 每次重算的代价不只是 CPU,还有**结果文本再次进入上下文**:
|
|
12
|
+
* 一个 64 位小数的返回值约 65 字符,重复十次就是 650 字符的 token。
|
|
13
|
+
* 缓存命中时仍要返回结果(AI 需要看到值),但省掉了重算,
|
|
14
|
+
* 更重要的是对**批量和重复调用**能显著降低总开销。
|
|
15
|
+
*
|
|
16
|
+
* ## 缓存键必须包含什么
|
|
17
|
+
*
|
|
18
|
+
* 少放一个就会返回**错的结果**——这比不缓存危险得多:
|
|
19
|
+
* · 规范化后的表达式(`1/7` 与 `1 / 7` 应算同一条,但不能把 `1/7` 和 `2/7` 撞车)
|
|
20
|
+
* · operation(`evaluate "1+1"` 与 `solve "1+1"` 结果不同)
|
|
21
|
+
* · precision(计算精度不同,结果不同)
|
|
22
|
+
* · displayPrecision(只影响显示,但也进键——否则命中后位数不对)
|
|
23
|
+
* · **当前常数状态**(改了 c/G 之后同样的式子结果不同)
|
|
24
|
+
* · **工作区**(不同世界的公式表/变量可能不同)
|
|
25
|
+
*
|
|
26
|
+
* 最后两项最容易漏。实测过:改常数后若缓存不失效,
|
|
27
|
+
* 会返回「改之前」的数——那是静默错误。
|
|
28
|
+
*
|
|
29
|
+
* ## 容量与淘汰
|
|
30
|
+
*
|
|
31
|
+
* 固定容量 + LRU。不设 TTL:常数或工作区一变,键就变了,旧条目自然不再命中;
|
|
32
|
+
* 保留它们没有危害,只是占内存,所以用容量上限控制。
|
|
33
|
+
*
|
|
34
|
+
* @module dsh-math-calculator/result-cache
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* 计算缓存键。
|
|
38
|
+
*
|
|
39
|
+
* @param parts - 参与键的各个部分(按固定顺序传入)
|
|
40
|
+
* @returns 键字符串
|
|
41
|
+
*/
|
|
42
|
+
export declare function cacheKey(parts: readonly unknown[]): string;
|
|
43
|
+
/**
|
|
44
|
+
* 取缓存。
|
|
45
|
+
*
|
|
46
|
+
* @param key - 缓存键
|
|
47
|
+
* @returns 命中则返回缓存值;未命中返回 undefined
|
|
48
|
+
*/
|
|
49
|
+
export declare function getCached(key: string): unknown;
|
|
50
|
+
/**
|
|
51
|
+
* 存缓存。
|
|
52
|
+
*
|
|
53
|
+
* @param key - 缓存键
|
|
54
|
+
* @param value - 要缓存的值
|
|
55
|
+
*/
|
|
56
|
+
export declare function setCached(key: string, value: unknown): void;
|
|
57
|
+
/**
|
|
58
|
+
* 清空缓存(改常数、切工作区时应当调用)。
|
|
59
|
+
*
|
|
60
|
+
* 为什么需要显式清空:缓存的键里已经含常数与工作区,
|
|
61
|
+
* 理论上不会串;但**清空是个安全网**——
|
|
62
|
+
* 万一某条路径漏把常数放进键里,清空能兜住,而不是静默返回旧值。
|
|
63
|
+
*/
|
|
64
|
+
export declare function clearCache(): void;
|
|
65
|
+
/**
|
|
66
|
+
* 缓存统计(供诊断与测试)。
|
|
67
|
+
*
|
|
68
|
+
* @returns 条目数、命中、未命中
|
|
69
|
+
*/
|
|
70
|
+
export declare function cacheStats(): {
|
|
71
|
+
size: number;
|
|
72
|
+
hits: number;
|
|
73
|
+
misses: number;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* 重置统计(不影响已存条目)。
|
|
77
|
+
*/
|
|
78
|
+
export declare function resetCacheStats(): void;
|
package/lib/types/workspace.d.ts
CHANGED
|
@@ -61,6 +61,19 @@ export interface WorkspaceData {
|
|
|
61
61
|
* 用发行前的量纲检查发现「公式算出来的不是想要的量」。
|
|
62
62
|
*/
|
|
63
63
|
customConstants?: CustomConstant[];
|
|
64
|
+
/**
|
|
65
|
+
* 本工作区的**显示**有效位数(不影响计算精度)。
|
|
66
|
+
*
|
|
67
|
+
* 为什么存在这里:AI 在对话里读到的每个数字都带着几十位小数,
|
|
68
|
+
* 逐个自己截断既费 token 又容易出错。定一次、存下来、后续沿用,
|
|
69
|
+
* 是「让 AI 估计一个够用的精度并固化」的实现方式。
|
|
70
|
+
*
|
|
71
|
+
* 计算仍然按 `precision`(默认 64)走,只有输出被截断 ——
|
|
72
|
+
* 所以 `(10^20+1)-(10^20)` 照样得 1,不会因为显示短了就算错。
|
|
73
|
+
*
|
|
74
|
+
* 未设置时沿用计算精度(即完整输出,旧行为)。
|
|
75
|
+
*/
|
|
76
|
+
displayPrecision?: number;
|
|
64
77
|
/** 创建时间。 */
|
|
65
78
|
createdAt: string;
|
|
66
79
|
/** 最后修改时间。 */
|
|
@@ -313,3 +326,14 @@ export declare function bindHostWorkspace(rawId: unknown, rawTitle: unknown, mak
|
|
|
313
326
|
created: boolean;
|
|
314
327
|
workspace: WorkspaceData;
|
|
315
328
|
};
|
|
329
|
+
/**
|
|
330
|
+
* 读取当前工作区的显示精度(未设置返回 undefined = 沿用计算精度)。
|
|
331
|
+
*
|
|
332
|
+
* 为什么单独做一个函数:工具入口每轮都要问一次「现在显示几位」,
|
|
333
|
+
* 而这里要吸收掉「工作区文件损坏 / 不存在」等各种异常 ——
|
|
334
|
+
* 读取失败不该让整个计算挂掉,退回完整输出即可。
|
|
335
|
+
*
|
|
336
|
+
* @param name - 工作区名;省略用当前活动工作区
|
|
337
|
+
* @returns 显示有效位数;未设置返回 undefined
|
|
338
|
+
*/
|
|
339
|
+
export declare function displayPrecisionOf(name?: string): number | undefined;
|
package/lib/types/world.d.ts
CHANGED
|
@@ -229,6 +229,14 @@ export interface WorldProfile {
|
|
|
229
229
|
warnings: string[];
|
|
230
230
|
/** 读者能直接感知的要点(供主模型二创)。 */
|
|
231
231
|
highlights: string[];
|
|
232
|
+
/**
|
|
233
|
+
* 恒星假设说明:哪些地方**替代了用户设定**或**补了默认值**。
|
|
234
|
+
*
|
|
235
|
+
* 与 `warnings` 的区别:`warnings` 说「你给的世界有物理问题」,
|
|
236
|
+
* 这里说「我动了你的输入」。后者更要紧——整份结论都建立在它之上,
|
|
237
|
+
* 所以必须显式返回、由上层放到输出最前面。
|
|
238
|
+
*/
|
|
239
|
+
starNotes: string[];
|
|
232
240
|
}
|
|
233
241
|
/**
|
|
234
242
|
* 世界推演主函数。
|
package/lib/workspace.js
CHANGED
|
@@ -739,3 +739,25 @@ export function bindHostWorkspace(rawId, rawTitle, makeCurrent = true) {
|
|
|
739
739
|
}
|
|
740
740
|
return { bound: true, name, workspaceId: hostId, created: !existed, workspace: data };
|
|
741
741
|
}
|
|
742
|
+
/**
|
|
743
|
+
* 读取当前工作区的显示精度(未设置返回 undefined = 沿用计算精度)。
|
|
744
|
+
*
|
|
745
|
+
* 为什么单独做一个函数:工具入口每轮都要问一次「现在显示几位」,
|
|
746
|
+
* 而这里要吸收掉「工作区文件损坏 / 不存在」等各种异常 ——
|
|
747
|
+
* 读取失败不该让整个计算挂掉,退回完整输出即可。
|
|
748
|
+
*
|
|
749
|
+
* @param name - 工作区名;省略用当前活动工作区
|
|
750
|
+
* @returns 显示有效位数;未设置返回 undefined
|
|
751
|
+
*/
|
|
752
|
+
export function displayPrecisionOf(name) {
|
|
753
|
+
try {
|
|
754
|
+
const data = loadWorkspace(name ?? activeWorkspaceName);
|
|
755
|
+
const value = data.displayPrecision;
|
|
756
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
757
|
+
return undefined;
|
|
758
|
+
return value;
|
|
759
|
+
}
|
|
760
|
+
catch {
|
|
761
|
+
return undefined;
|
|
762
|
+
}
|
|
763
|
+
}
|
package/lib/world.js
CHANGED
|
@@ -143,6 +143,14 @@ export function generateWorld(params, options = {}) {
|
|
|
143
143
|
// a) starClass='M5V' → 按光谱型基准表填充(省去查表)
|
|
144
144
|
// b) 直接给 starMass/starRadius/starTemp
|
|
145
145
|
// 显式参数优先于 starClass —— 用户想微调时不会被基准值覆盖。
|
|
146
|
+
//
|
|
147
|
+
// ⚠️ 目录里**只有主序星(V)**:O5V…M5V 共 9 种,没有巨星(III)、次巨星、白矮星。
|
|
148
|
+
// 同样光谱字母的巨星比主序星亮 20—100 倍,用主序值顶替会让平衡温度差出几百开。
|
|
149
|
+
//
|
|
150
|
+
// 实测踩到过:传 starClass='F5III'(某设定里的 F5-IIIa 巨星)时,旧实现静默回退到
|
|
151
|
+
// 最近的主序型 G2V(太阳),而那句提示被推到最后一百多行之后——读前 20 行的人永远看不到。
|
|
152
|
+
// 现在这类替换会记进 starNotes,并被放到输出的**最前面**。
|
|
153
|
+
const starNotes = [];
|
|
146
154
|
let baseStar;
|
|
147
155
|
if (typeof params.starClass === 'string' && params.starClass.trim() !== '') {
|
|
148
156
|
const requested = STAR_INDEX.get(params.starClass.trim());
|
|
@@ -152,16 +160,35 @@ export function generateWorld(params, options = {}) {
|
|
|
152
160
|
// 允许简写(如 'G2' → 'G2V')
|
|
153
161
|
const upper = params.starClass.trim().toUpperCase();
|
|
154
162
|
const prefixed = STAR_CLASSES.find((s) => s.id.startsWith(upper));
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
if (prefixed) {
|
|
164
|
+
baseStar = prefixed;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
baseStar = closestStarClass(num(params.starTemp, 5778));
|
|
168
|
+
starNotes.push(`未找到光谱型「${params.starClass}」,已按温度推断为 ${baseStar.id}(主序星)。` +
|
|
158
169
|
`可选值:${STAR_CLASSES.map((s) => s.id).join(', ')}`);
|
|
170
|
+
// 亮度级 I/II/III/IV = 巨/次巨星:同样的光谱字母,亮度高出一个数量级
|
|
171
|
+
const lumMatch = params.starClass.trim().match(/(III|II|IV|I)\b/i);
|
|
172
|
+
const lum = lumMatch ? lumMatch[1].toUpperCase() : undefined;
|
|
173
|
+
if (lum !== undefined && lum !== 'V') {
|
|
174
|
+
starNotes.push(`⚠ 你给的是「${lum}」级(巨/次巨星),而本模型只支持主序星(V)。` +
|
|
175
|
+
'同光谱字母的巨星比主序星亮 20—100 倍,因此温度、宜居带、日照量都会显著偏低——' +
|
|
176
|
+
'这个结果不能直接采信。要接近真实,请显式给 starMass 与 starRadius。');
|
|
177
|
+
}
|
|
159
178
|
}
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
else {
|
|
163
182
|
// 没给 starClass:按温度反查最接近的光谱型(用于补全半径/质量)
|
|
164
183
|
baseStar = closestStarClass(num(params.starTemp, 5778));
|
|
184
|
+
// 完全没给恒星信息 = 默认太阳,这必须说在最前面
|
|
185
|
+
const gaveAnyStar = params.starMass !== undefined ||
|
|
186
|
+
params.starRadius !== undefined ||
|
|
187
|
+
params.starTemp !== undefined;
|
|
188
|
+
if (!gaveAnyStar) {
|
|
189
|
+
starNotes.push('没有给任何恒星参数,已默认成太阳(G2V,1 L☉,5778 K)。' +
|
|
190
|
+
'若这颗行星绕的不是太阳型恒星(例如 K 型橙星或 F 型巨星),温度与宜居带会差很多。');
|
|
191
|
+
}
|
|
165
192
|
}
|
|
166
193
|
const starMassSun = num(params.starMass, baseStar.massSun);
|
|
167
194
|
const starRadiusSun = num(params.starRadius, baseStar.radiusSun);
|
|
@@ -604,5 +631,13 @@ export function generateWorld(params, options = {}) {
|
|
|
604
631
|
},
|
|
605
632
|
warnings,
|
|
606
633
|
highlights,
|
|
634
|
+
/**
|
|
635
|
+
* 恒星假设说明(替代了用户设定、或用了默认值)。
|
|
636
|
+
*
|
|
637
|
+
* 与 `warnings` 分开:`warnings` 是「你给的世界有个物理问题」,
|
|
638
|
+
* `starNotes` 是「我改了/补了你的输入」。两者性质不同,呈现位置也不同——
|
|
639
|
+
* `starNotes` 必须出现在输出最前面,因为整份结论都建立在它之上。
|
|
640
|
+
*/
|
|
641
|
+
starNotes,
|
|
607
642
|
};
|
|
608
643
|
}
|
package/package.json
CHANGED