For AI agents: the complete documentation index is available at https://open-spaced-repetition.github.io/ts-fsrs/zh-TW/llms.txt, the full documentation bundle is available at https://open-spaced-repetition.github.io/ts-fsrs/zh-TW/llms-full.txt, and this page is available as Markdown at https://open-spaced-repetition.github.io/ts-fsrs/zh-TW/guide/model/index.md.
  • 繁體中文
  • 模型概覽

    FSRS 模型依據評分和經過時間計算新的記憶狀態,透過遺忘曲線估算回憶機率,並依據記憶狀態與目標保留率計算基礎間隔。

    本節的 FSRS-3 至 FSRS-6 術語與公式依據 awesome-fsrs Wiki 校對,主要參考其中的 The Algorithm。TypeScript API 與框架職責邊界以 ts-fsrs 本身為準。

    模型邊界

    模型只負責記憶狀態更新、遺忘曲線計算和基礎間隔計算。scheduler 負責組織複習流程,chrono 負責日期與經過時間,middleware 負責學習步驟、模糊處理、最大間隔限制和佇列狀態策略。

    For your Agent
    實作或說明 SRS 模型

    複製這段提示詞,讓 AI Agent 遵守 Model 契約,避免將排程策略混入模型。

    共用符號

    符號含義
    DD難度,範圍為 1(最簡單)至 10(最困難)。
    SSFSRS-3 至 FSRS-6 的穩定性,單位為天;當 t=St = S 時回憶機率為 90%。
    RR經過 tt 天後能夠回憶的估計機率。
    GG評分:1 為 Again、2 為 Hard、3 為 Good、4 為 Easy。
    tt距離上次複習經過的天數。
    rrnextInterval 使用的目標保留率。
    I(r,S)I(r, S)依據穩定性 SS 和目標保留率 rr 計算的基礎間隔,單位為天,尚未套用 middleware 策略。
    Smin⁡S_{\min}、Smax⁡S_{\max}模型允許的穩定性下限和上限。
    Dmin⁡D_{\min}、Dmax⁡D_{\max}模型允許的難度下限和上限。
    wiw_i訓練得到的模型權重。

    FSRS-7 依據其實作快照另行說明:它增加快穩定性,使用混合曲線並回傳小數天間隔。上表中穩定性的 90% 定義和 I(r, S) 記法適用於 FSRS-3 至 FSRS-6。

    使用版本化模型

    DefaultScheduler 預設使用 FSRS-7,可透過 version 選擇任一支援的 FSRS 版本。也可以直接建立並呼叫各版本模型;下例以 FSRS-6 為例:

    import { 
    type Rating = 0 | 1 | 2 | 3 | 4
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    } from 'ts-fsrs'
    import {
    const FSRS6_DEFAULT_WEIGHTS: number[]
    FSRS6_DEFAULT_WEIGHTS
    ,
    const FSRS6Model: Model<{
        readonly name: "fsrs-6";
        readonly config: SRSSchema<{
            input: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
            output: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
        }>;
        readonly memoryState: SRSSchema<{
            input: {
                stability: number;
                difficulty: number;
            };
            output: {
                stability: number;
                difficulty: number;
            };
        }>;
        readonly algorithm: FSRS6Algorithm;
    }>
    FSRS6Model
    } from 'ts-fsrs/models/fsrs-6'
    const
    const model: ModelCore<{
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly memoryState: {
            stability: number;
            difficulty: number;
        };
        readonly algorithm: FSRS6Algorithm;
    }>
    model
    =
    const FSRS6Model: Model<{
        readonly name: "fsrs-6";
        readonly config: SRSSchema<{
            input: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
            output: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
        }>;
        readonly memoryState: SRSSchema<{
            input: {
                stability: number;
                difficulty: number;
            };
            output: {
                stability: number;
                difficulty: number;
            };
        }>;
        readonly algorithm: FSRS6Algorithm;
    }>
    FSRS6Model
    .
    Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>.create: (ctx: {
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        } | {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly migrate?: boolean;
        readonly clip?: boolean;
        readonly check?: boolean;
        readonly bypass?: boolean;
    }) => ModelCore<{
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly memoryState: {
            stability: number;
            difficulty: number;
        };
        readonly algorithm: FSRS6Algorithm;
    }>
    create
    ({
    config: { weights:
    const FSRS6_DEFAULT_WEIGHTS: number[]
    FSRS6_DEFAULT_WEIGHTS
    ,
    enableShortTerm: true, numRelearningSteps: 1, // Constrains weights; middleware runs the steps. }, }) const memoryState =
    const model: ModelCore<{
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly memoryState: {
            stability: number;
            difficulty: number;
        };
        readonly algorithm: FSRS6Algorithm;
    }>
    model
    .
    ModelCore<{ readonly config: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; readonly memoryState: { stability: number; difficulty: number; }; readonly algorithm: FSRS6Algorithm; }>.step: (input: ModelStepInput<{
        stability: number;
        difficulty: number;
    }>) => {
        stability: number;
        difficulty: number;
    }
    step
    ({
    ModelStepInput<{ stability: number; difficulty: number; }>.memoryState: Readonly<{
        stability: number;
        difficulty: number;
    }> | null
    memoryState
    : null,
    ModelStepInput<{ stability: number; difficulty: number; }>.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelStepInput<{ stability: number; difficulty: number; }>.elapsedDays: number
    elapsedDays
    : 0,
    }) const desiredRetention = 0.9 const interval =
    const model: ModelCore<{
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly memoryState: {
            stability: number;
            difficulty: number;
        };
        readonly algorithm: FSRS6Algorithm;
    }>
    model
    .
    ModelCore<{ readonly config: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; readonly memoryState: { stability: number; difficulty: number; }; readonly algorithm: FSRS6Algorithm; }>.nextInterval: (memoryState: Readonly<{
        stability: number;
        difficulty: number;
    }>, desiredRetention: number) => number
    nextInterval
    (memoryState, desiredRetention)
    const retrievabilityAtInterval =
    const model: ModelCore<{
        readonly config: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        readonly memoryState: {
            stability: number;
            difficulty: number;
        };
        readonly algorithm: FSRS6Algorithm;
    }>
    model
    .
    ModelCore<{ readonly config: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; readonly memoryState: { stability: number; difficulty: number; }; readonly algorithm: FSRS6Algorithm; }>.forgettingCurve: (memoryState: Readonly<{
        stability: number;
        difficulty: number;
    }>, elapsedDays: number) => number
    forgettingCurve
    (memoryState, interval)
    console.log( JSON.stringify( { memoryState, desiredRetention, interval, retrievabilityAtInterval, }, null, 2 ) )

    範例先依據 90% 的目標保留率計算複習間隔,再透過 forgettingCurve 估算到達該間隔時的回憶機率。由於 FSRS-6 的 nextInterval 回傳整數天,結果可能與 90% 略有差異。

    在 FSRS-6 設定中,enableShortTerm 控制模型的同日穩定性更新。numRelearningSteps 只參與權重裁剪與檢查;重新學習步驟仍由 middleware 排程。

    定義模型

    defineModel 會保留具體的設定、記憶狀態和演算法型別,並將 schema、預設值與模型核心工廠組織為一個定義。它本身不會解析設定:

    以下透過實作一個 SM-2 變體,示範如何定義模型。

    import {
      
    function clamp(value: number, min: number, max: number): number
    clamp
    ,
    function defineModel<const Name extends string | symbol, const ConfigSchema extends AnySchema, const MemoryStateSchema extends AnyObjectSchema, const Algorithm>(definition: {
        readonly name: Name;
        readonly schema: ModelSchema<{
            readonly config: ConfigSchema;
            readonly memoryState: MemoryStateSchema;
        }>;
        readonly defaultValue: {
            readonly memoryState: (ctx: {
                readonly config: SchemaOutput<ConfigSchema>;
            }) => SchemaInput<MemoryStateSchema>;
        };
        readonly create: ModelCreate<{
            readonly config: ConfigSchema;
            readonly memoryState: MemoryStateSchema;
        }, Algorithm>;
    }): Model<{
        readonly name: Name;
        readonly config: ConfigSchema;
        readonly memoryState: MemoryStateSchema;
        readonly algorithm: Algorithm;
    }>
    defineModel
    ,
    type
    interface ModelForwardInput<MemoryState extends object>
    ModelForwardInput
    ,
    type
    interface ModelStepInput<MemoryState extends object>
    ModelStepInput
    ,
    type Rating = 0 | 1 | 2 | 3 | 4
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    ,
    } from 'ts-fsrs' import { z } from 'zod' const configSchema = z.object({ weights: z.tuple([ z.number().min(0.01).max(100), z.number().min(0.01).max(100), z.number().min(1.3).max(10), z.number().nonnegative(), z.number().min(5), z.number(), ]), }) const memoryStateSchema = z.object({ interval: z.number().min(0).max(36500), easeFactor: z.number().min(1.3).max(10), repetitions: z.number().int().nonnegative(), }) type
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    = z.output<typeof memoryStateSchema>
    const
    const SM2Model: Model<{
        readonly name: "sm2";
        readonly config: z.ZodObject<{
            weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
        }, z.core.$strip>;
        readonly memoryState: z.ZodObject<{
            interval: z.ZodNumber;
            easeFactor: z.ZodNumber;
            repetitions: z.ZodNumber;
        }, z.core.$strip>;
        readonly algorithm: null;
    }>
    SM2Model
    =
    defineModel<"sm2", z.ZodObject<{
        weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
    }, z.core.$strip>, z.ZodObject<{
        interval: z.ZodNumber;
        easeFactor: z.ZodNumber;
        repetitions: z.ZodNumber;
    }, z.core.$strip>, null>(definition: {
        readonly name: "sm2";
        readonly schema: ModelSchema<{
            readonly config: z.ZodObject<{
                weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
            }, z.core.$strip>;
            readonly memoryState: z.ZodObject<{
                ...;
            }, z.core.$strip>;
        }>;
        readonly defaultValue: {
            ...;
        };
        readonly create: ModelCreate<...>;
    }): Model<...>
    defineModel
    ({
    name: 'sm2',
    schema: ModelSchema<{
        readonly config: z.ZodObject<{
            weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
        }, z.core.$strip>;
        readonly memoryState: z.ZodObject<{
            interval: z.ZodNumber;
            easeFactor: z.ZodNumber;
            repetitions: z.ZodNumber;
        }, z.core.$strip>;
    }>
    schema
    : {
    ModelSchema<{ readonly config: ZodObject<{ weights: ZodTuple<[ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber], null>; }, $strip>; readonly memoryState: ZodObject<...>; }>.config: z.ZodObject<{
        weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
    }, z.core.$strip>
    config
    : configSchema,
    ModelSchema<{ readonly config: ZodObject<{ weights: ZodTuple<[ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber], null>; }, $strip>; readonly memoryState: ZodObject<...>; }>.memoryState: z.ZodObject<{
        interval: z.ZodNumber;
        easeFactor: z.ZodNumber;
        repetitions: z.ZodNumber;
    }, z.core.$strip>
    memoryState
    : memoryStateSchema },
    defaultValue: { memoryState: ({ config }) => ({ interval: 0, easeFactor: config.weights[2], repetitions: 0, }), },
    create: ModelCreate<{
        readonly config: z.ZodObject<{
            weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
        }, z.core.$strip>;
        readonly memoryState: z.ZodObject<{
            interval: z.ZodNumber;
            easeFactor: z.ZodNumber;
            repetitions: z.ZodNumber;
        }, z.core.$strip>;
    }, null>
    create
    ({ config }) {
    const parsed = configSchema.
    ZodType<any, any, $ZodObjectInternals<{ weights: ZodTuple<[ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber], null>; }, $strip>>.parse(data: unknown, params?: z.core.ParseContext<z.core.$ZodIssue>): {
        weights: [number, number, number, number, number, number];
    }
    parse
    (config)
    const w = parsed.weights const
    const step: ({ memoryState, rating }: ModelStepInput<MemoryState>) => {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    step
    = ({ memoryState, rating }:
    interface ModelStepInput<MemoryState extends object>
    ModelStepInput
    <
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    >) => {
    const previous = memoryState ?? { interval: 0, easeFactor: parsed.weights[2], repetitions: 0, } // Again / Hard / Good / Easy -> SM-2 quality 2 / 3 / 4 / 5. const quality = rating + 1 const repetitions = rating >
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Again ? previous.repetitions + 1 : 0
    const interval =
    function clamp(value: number, min: number, max: number): number
    clamp
    (
    repetitions <= 1 ? w[0] : repetitions === 2 ? w[1] : previous.interval * previous.easeFactor, 0.01, 36500 ) const easeFactor =
    function clamp(value: number, min: number, max: number): number
    clamp
    (
    previous.easeFactor - w[3] * (quality - w[4]) ** 2 + w[5], 1.3, 10 ) return { interval, easeFactor, repetitions } } const
    const forward: ({ history, initialState, }: ModelForwardInput<MemoryState>) => {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    forward
    = ({
    history: readonly ModelReview[]
    history
    ,
    initialState, }:
    interface ModelForwardInput<MemoryState extends object>
    ModelForwardInput
    <
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    >) => {
    const
    const states: {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    states
    :
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    [] = []
    let memoryState = initialState ?? null for (const
    const review: ModelReview
    review
    of
    history: readonly ModelReview[]
    history
    ) {
    memoryState =
    const step: ({ memoryState, rating }: ModelStepInput<MemoryState>) => {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    step
    ({
    ModelStepInput<{ interval: number; easeFactor: number; repetitions: number; }>.memoryState: Readonly<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }> | null
    memoryState
    ,
    ModelStepInput<{ interval: number; easeFactor: number; repetitions: number; }>.rating: 1 | 2 | 3 | 4
    rating
    :
    const review: ModelReview
    review
    .
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    ,
    ModelStepInput<{ interval: number; easeFactor: number; repetitions: number; }>.elapsedDays: number
    elapsedDays
    :
    const review: ModelReview
    review
    .
    ModelReview.deltaT: number
    deltaT
    ,
    })
    const states: {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    states
    .push(memoryState)
    } return
    const states: {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    states
    } return {
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.config: {
        weights: [number, number, number, number, number, number];
    }
    config
    : parsed,
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.bounds: ModelBoundsShape<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>
    bounds
    : {
    iMin: 0.01, iMax: 36500, eMin: 1.3, eMax: 10, rMin: 0, rMax: Infinity, },
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.algorithm: null
    algorithm
    : null,
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.step: (input: ModelStepInput<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>) => {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    step
    ,
    // Invert the benchmark curve to satisfy the Model interval contract.
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.nextInterval: (memoryState: Readonly<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>, desiredRetention: number) => number
    nextInterval
    : (state: Readonly<
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    >, desiredRetention: number) =>
    Math.max( 1, Math.round( (state.interval * Math.log(desiredRetention)) / Math.log(0.9) ) ),
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.forgettingCurve: (memoryState: Readonly<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>, elapsedDays: number) => number
    forgettingCurve
    : (state: Readonly<
    type MemoryState = {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }
    MemoryState
    >, elapsedDays: number) =>
    0.9 ** (elapsedDays / state.interval),
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.forward: (input: ModelForwardInput<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>) => readonly {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    forward
    ,
    } }, }) const
    const model: ModelCore<{
        readonly config: {
            weights: [number, number, number, number, number, number];
        };
        readonly memoryState: {
            interval: number;
            easeFactor: number;
            repetitions: number;
        };
        readonly algorithm: null;
    }>
    model
    =
    const SM2Model: Model<{
        readonly name: "sm2";
        readonly config: z.ZodObject<{
            weights: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
        }, z.core.$strip>;
        readonly memoryState: z.ZodObject<{
            interval: z.ZodNumber;
            easeFactor: z.ZodNumber;
            repetitions: z.ZodNumber;
        }, z.core.$strip>;
        readonly algorithm: null;
    }>
    SM2Model
    .
    Model<{ readonly name: "sm2"; readonly config: ZodObject<{ weights: ZodTuple<[ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber], null>; }, $strip>; readonly memoryState: ZodObject<...>; readonly algorithm: null; }>.create: (ctx: {
        readonly config: {
            weights: [number, number, number, number, number, number];
        } | {
            weights: [number, number, number, number, number, number];
        };
        readonly migrate?: boolean;
        readonly clip?: boolean;
        readonly check?: boolean;
        readonly bypass?: boolean;
    }) => ModelCore<{
        readonly config: {
            weights: [number, number, number, number, number, number];
        };
        readonly memoryState: {
            interval: number;
            easeFactor: number;
            repetitions: number;
        };
        readonly algorithm: null;
    }>
    create
    ({
    config: { weights: [1, 6, 2.5, 0.02, 7, 0.18] }, }) const
    const states: readonly {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    states
    =
    const model: ModelCore<{
        readonly config: {
            weights: [number, number, number, number, number, number];
        };
        readonly memoryState: {
            interval: number;
            easeFactor: number;
            repetitions: number;
        };
        readonly algorithm: null;
    }>
    model
    .
    ModelCore<{ readonly config: { weights: [number, number, number, number, number, number]; }; readonly memoryState: { interval: number; easeFactor: number; repetitions: number; }; readonly algorithm: null; }>.forward: (input: ModelForwardInput<{
        interval: number;
        easeFactor: number;
        repetitions: number;
    }>) => readonly {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    forward
    ({
    ModelForwardInput<{ interval: number; easeFactor: number; repetitions: number; }>.history: readonly ModelReview[]
    history
    : [
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelReview.deltaT: number
    deltaT
    : 0 },
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelReview.deltaT: number
    deltaT
    : 1 },
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelReview.deltaT: number
    deltaT
    : 6 },
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Again,
    ModelReview.deltaT: number
    deltaT
    : 15 },
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelReview.deltaT: number
    deltaT
    : 1 },
    {
    ModelReview.rating: 1 | 2 | 3 | 4
    rating
    :
    const Rating: Readonly<{
        readonly Manual: 0;
        readonly Again: 1;
        readonly Hard: 2;
        readonly Good: 3;
        readonly Easy: 4;
    }>
    Rating
    .Good,
    ModelReview.deltaT: number
    deltaT
    : 1 },
    ], }) console.log(JSON.stringify(
    const states: readonly {
        interval: number;
        easeFactor: number;
        repetitions: number;
    }[]
    states
    , null, 2)) // Intervals: 1, 6, 15, 1, 1, 6 days.

    Schema 描述公開邊界,並在 scheduler 組合 schema 時使用。直接呼叫 create 時,具體實作必須先完成所需的解析和正規化,再回傳 config、bounds、algorithm、step、nextInterval、forgettingCurve 和 forward。該核心應保持確定性,且不包含排程策略。

    bypass 僅用於已處理的設定

    只有呼叫方已明確完成該模型的 migrate、clip、schema 驗證和範圍檢查後,才能使用 FSRS6Model.create({ config, bypass: true })。它會略過這些 create 階段的處理以及凍結。不要對來自使用者、儲存空間或網路的原始設定使用 bypass。

    模型版本

    • FSRS-7 — 雙分量實作參考,34 個權重與數值求間隔。
    • FSRS-6 — 支援可訓練衰減與同日更新。
    • FSRS-5 — 使用固定冪函數遺忘曲線,並支援同日更新。
    • FSRS-4.5 — 使用固定冪函數曲線,不包含同日公式。
    • FSRS-4 — 本專案中第一個使用冪函數曲線的版本。
    • FSRS-3 — 使用較早的指數遺忘曲線。