soulbridge 0.0.100 → 0.0.102
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/dist/packages/core/src/config/status.d.ts +15 -15
- package/dist/packages/core/src/config/status.js +15 -15
- package/dist/packages/core/src/extract.js +15 -2
- package/dist/packages/core/src/index.d.ts +1 -1
- package/dist/packages/core/src/index.js +8 -3
- package/dist/packages/core/src/prompts.generated.d.ts +1 -1
- package/dist/packages/core/src/prompts.generated.js +1 -1
- package/dist/packages/core/src/roles.d.ts +8 -9
- package/dist/packages/core/src/roles.js +4 -10
- package/dist/packages/core/src/sqlite.d.ts +2 -1
- package/dist/packages/core/src/sqlite.js +29 -15
- package/dist/packages/core/src/store.d.ts +5 -1
- package/dist/packages/core/src/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fact lifecycle vocabulary — declared once, imported everywhere.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* These four replaced none/open/kept/broken/superseded. The older set mixed "what kind of
|
|
5
|
+
* fact" with "how far it has come", so every reader had to remember which values counted as
|
|
6
|
+
* live. The four below are the only ones written now: `raw` is the exchange itself,
|
|
7
|
+
* `candidate` is held but not injected, `active` is in use, `retracted` is kept for the
|
|
8
|
+
* record and read by nothing.
|
|
7
9
|
*
|
|
8
|
-
* Values match the column defaults in sqlite.ts; changing one is a schema migration,
|
|
9
|
-
*
|
|
10
|
+
* Values match the column defaults in sqlite.ts; changing one is a schema migration, not a
|
|
11
|
+
* rename.
|
|
10
12
|
*/
|
|
11
13
|
export declare const FACT_STATUS: {
|
|
12
|
-
/**
|
|
13
|
-
readonly
|
|
14
|
-
/**
|
|
15
|
-
readonly
|
|
16
|
-
/**
|
|
17
|
-
readonly
|
|
18
|
-
/**
|
|
19
|
-
readonly
|
|
20
|
-
/** Replaced by another fact; kept for the record, never injected. */
|
|
21
|
-
readonly superseded: 'superseded';
|
|
14
|
+
/** The exchange itself; not a distilled fact. */
|
|
15
|
+
readonly raw: 'raw';
|
|
16
|
+
/** Held, not injected: unverified inference or an unconfirmed rule. */
|
|
17
|
+
readonly candidate: 'candidate';
|
|
18
|
+
/** In use: retrieval, rules, profile, evidence. */
|
|
19
|
+
readonly active: 'active';
|
|
20
|
+
/** Replaced or withdrawn; kept for the audit trail, injected nowhere. */
|
|
21
|
+
readonly retracted: 'retracted';
|
|
22
22
|
};
|
|
23
23
|
export type FactStatus = (typeof FACT_STATUS)[keyof typeof FACT_STATUS];
|
|
24
24
|
/** Where a fact came from, in the trust sense (not a type). */
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fact lifecycle vocabulary — declared once, imported everywhere.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* These four replaced none/open/kept/broken/superseded. The older set mixed "what kind of
|
|
5
|
+
* fact" with "how far it has come", so every reader had to remember which values counted as
|
|
6
|
+
* live. The four below are the only ones written now: `raw` is the exchange itself,
|
|
7
|
+
* `candidate` is held but not injected, `active` is in use, `retracted` is kept for the
|
|
8
|
+
* record and read by nothing.
|
|
7
9
|
*
|
|
8
|
-
* Values match the column defaults in sqlite.ts; changing one is a schema migration,
|
|
9
|
-
*
|
|
10
|
+
* Values match the column defaults in sqlite.ts; changing one is a schema migration, not a
|
|
11
|
+
* rename.
|
|
10
12
|
*/
|
|
11
13
|
export const FACT_STATUS = {
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
/** Replaced by another fact; kept for the record, never injected. */
|
|
21
|
-
superseded: 'superseded',
|
|
14
|
+
/** The exchange itself; not a distilled fact. */
|
|
15
|
+
raw: 'raw',
|
|
16
|
+
/** Held, not injected: unverified inference or an unconfirmed rule. */
|
|
17
|
+
candidate: 'candidate',
|
|
18
|
+
/** In use: retrieval, rules, profile, evidence. */
|
|
19
|
+
active: 'active',
|
|
20
|
+
/** Replaced or withdrawn; kept for the audit trail, injected nowhere. */
|
|
21
|
+
retracted: 'retracted',
|
|
22
22
|
};
|
|
23
23
|
/** Where a fact came from, in the trust sense (not a type). */
|
|
24
24
|
export const FACT_SOURCE = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderExtractionPrompt } from './prompts.js';
|
|
2
|
-
import { SLOT_ENTITY_MARKER } from './config/domains.js';
|
|
2
|
+
import { SLOT_ENTITY_MARKER, FACT_ROLES } from './config/domains.js';
|
|
3
3
|
/**
|
|
4
4
|
* What a fact's confidence is when the model did not give one.
|
|
5
5
|
*
|
|
@@ -176,6 +176,9 @@ export function extractByRules(text, at = Date.now()) {
|
|
|
176
176
|
continue;
|
|
177
177
|
facts.push({
|
|
178
178
|
statement: toStatement(sentence),
|
|
179
|
+
// The floor's own decision: a rule can recognise a sentence but cannot judge what it
|
|
180
|
+
// is for, so it stores the minimal claim. The model path may say otherwise.
|
|
181
|
+
role: 'observation',
|
|
179
182
|
typeName: verb?.word,
|
|
180
183
|
at: time.at,
|
|
181
184
|
atText: time.atText,
|
|
@@ -320,6 +323,7 @@ function extractJson(raw) {
|
|
|
320
323
|
}
|
|
321
324
|
const ENTITY_TYPES = new Set(['person', 'thing', 'place', 'self']);
|
|
322
325
|
const PRECISIONS = new Set(['day', 'range', 'relative', 'unknown']);
|
|
326
|
+
const ROLES = new Set(FACT_ROLES);
|
|
323
327
|
const SOURCES = new Set(['experienced', 'told', 'inferred']);
|
|
324
328
|
const OPS = new Set(['add', 'supersede', 'update', 'noop']);
|
|
325
329
|
/** One fact payload, shared by the legacy `facts` array and the operations. */
|
|
@@ -355,8 +359,12 @@ function parseFactDraft(obj) {
|
|
|
355
359
|
: atText
|
|
356
360
|
? 'relative'
|
|
357
361
|
: 'unknown';
|
|
362
|
+
const roleRaw = typeof obj.role === 'string' ? obj.role.trim() : '';
|
|
358
363
|
return {
|
|
359
364
|
statement,
|
|
365
|
+
// The model's structural answer. An unrecognised value is dropped rather than guessed:
|
|
366
|
+
// the writer's neutral default is observation, never a guess from the type label.
|
|
367
|
+
...(roleRaw && ROLES.has(roleRaw) ? { role: roleRaw } : {}),
|
|
360
368
|
// `world` unless the model named a mind; the commit gate resolves the name.
|
|
361
369
|
holder: holderRaw || 'world',
|
|
362
370
|
typeName: typeof obj.type_name === 'string' ? obj.type_name : undefined,
|
|
@@ -525,12 +533,17 @@ export function parseLineDraft(raw) {
|
|
|
525
533
|
let supersedes = '';
|
|
526
534
|
let wantsSupersede = false;
|
|
527
535
|
for (const part of rest) {
|
|
528
|
-
const keyed = /^(
|
|
536
|
+
const keyed = /^(持有|精度|原则候选|槽位|操作|角色)\s*[==]\s*(.*)$/.exec(part);
|
|
529
537
|
if (keyed) {
|
|
530
538
|
if (keyed[1] === '持有')
|
|
531
539
|
fact.holder = keyed[2].trim();
|
|
532
540
|
else if (keyed[1] === '精度')
|
|
533
541
|
fact.at_precision = keyed[2].trim();
|
|
542
|
+
else if (keyed[1] === '角色') {
|
|
543
|
+
const role = keyed[2].trim();
|
|
544
|
+
if (ROLES.has(role))
|
|
545
|
+
fact.role = role;
|
|
546
|
+
}
|
|
534
547
|
else if (keyed[1] === '原则候选') {
|
|
535
548
|
// Only 是 promotes. "否" and anything else is the safe default.
|
|
536
549
|
if (/^(是|yes|true)$/i.test(keyed[2].trim()))
|
|
@@ -277,7 +277,7 @@ export declare class Soul {
|
|
|
277
277
|
*
|
|
278
278
|
* Neither bucket is safe for it: as a rule it would fire every turn, and as a
|
|
279
279
|
* profile line it would put an imperative into the description of a person. It
|
|
280
|
-
*
|
|
280
|
+
* becomes a candidate — held in the store, injected nowhere — and shows up in a review count.
|
|
281
281
|
*/
|
|
282
282
|
markUnclassified(factId: string): void;
|
|
283
283
|
/** The judgments about the person that are waiting to be merged. */
|
|
@@ -510,6 +510,7 @@ export class Soul {
|
|
|
510
510
|
userId: this.userId,
|
|
511
511
|
statement,
|
|
512
512
|
holder,
|
|
513
|
+
role: factDraft.role,
|
|
513
514
|
typeName: factDraft.typeName,
|
|
514
515
|
at: factDraft.at,
|
|
515
516
|
atText: factDraft.atText,
|
|
@@ -557,7 +558,7 @@ export class Soul {
|
|
|
557
558
|
// lost, and "the fact changed" is never claimed without a real predecessor.
|
|
558
559
|
const target = operation.targetId ? this.store.fact(operation.targetId) : null;
|
|
559
560
|
const sameScope = target && target.soulId === this.soulId && target.userId === this.userId;
|
|
560
|
-
if (target && sameScope && target
|
|
561
|
+
if (target && sameScope && isActive(target)) {
|
|
561
562
|
this.store.supersedeFact(target.id, written.id);
|
|
562
563
|
}
|
|
563
564
|
else {
|
|
@@ -638,6 +639,7 @@ export class Soul {
|
|
|
638
639
|
soulId: this.soulId,
|
|
639
640
|
userId: this.userId,
|
|
640
641
|
statement: gate.statement,
|
|
642
|
+
role: 'judgment',
|
|
641
643
|
typeName: '结论',
|
|
642
644
|
holder: 'self',
|
|
643
645
|
confidence: gate.confidence,
|
|
@@ -683,6 +685,7 @@ export class Soul {
|
|
|
683
685
|
* have to be always-on, which is exactly what this split exists to avoid.
|
|
684
686
|
*/
|
|
685
687
|
markRule(factId, classification) {
|
|
688
|
+
this.store.setFactRole(factId, 'rule');
|
|
686
689
|
this.store.setFactTypeName(factId, '规则');
|
|
687
690
|
const slots = [
|
|
688
691
|
{ slot: '触发', value: { kind: 'text', value: classification.trigger } },
|
|
@@ -693,6 +696,7 @@ export class Soul {
|
|
|
693
696
|
}
|
|
694
697
|
/** Apply a profile classification: it is about the person, not a triggered rule. */
|
|
695
698
|
markProfile(factId, options = {}) {
|
|
699
|
+
this.store.setFactRole(factId, 'profile');
|
|
696
700
|
this.store.setFactTypeName(factId, '画像');
|
|
697
701
|
// The dimension is written here because this is the moment the model decided
|
|
698
702
|
// which lens the judgment belongs to. The merge step cuts its batches along
|
|
@@ -708,10 +712,10 @@ export class Soul {
|
|
|
708
712
|
*
|
|
709
713
|
* Neither bucket is safe for it: as a rule it would fire every turn, and as a
|
|
710
714
|
* profile line it would put an imperative into the description of a person. It
|
|
711
|
-
*
|
|
715
|
+
* becomes a candidate — held in the store, injected nowhere — and shows up in a review count.
|
|
712
716
|
*/
|
|
713
717
|
markUnclassified(factId) {
|
|
714
|
-
this.store.
|
|
718
|
+
this.store.setFactStatus(factId, 'candidate');
|
|
715
719
|
}
|
|
716
720
|
/** The judgments about the person that are waiting to be merged. */
|
|
717
721
|
profileItems(limit = 60) {
|
|
@@ -737,6 +741,7 @@ export class Soul {
|
|
|
737
741
|
soulId: this.soulId,
|
|
738
742
|
userId: this.userId,
|
|
739
743
|
statement: line.statement,
|
|
744
|
+
role: 'profile',
|
|
740
745
|
typeName: '画像',
|
|
741
746
|
holder: 'self',
|
|
742
747
|
// More sources means more observations behind the line, so it ranks first.
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
export declare const extractionExamples = "<examples>\n<example>\n<source>\u6211\u540C\u4E8B\u5C0F\u6797\u4E0A\u5468\u88AB\u88C1\u5458\u4E86\uFF0C\u633A\u96BE\u53D7\u7684</source>\n<output>\u5B9E\u4F53: \u5C0F\u6797 | person | \u540C\u4E8B\n\u4E8B\u5B9E: \u5C0F\u6797\u5728 2026-10-24 \u90A3\u5468\u88AB\u88C1\u5458 | \u88C1\u5458 | 2026-10-24 | \u4E0A\u5468 | told | 0.8 | \u6301\u6709=world | \u7CBE\u5EA6=day | \u88AB\u88C1\u5458\u8005=@\u5C0F\u6797</output>\n</example>\n<example>\n<source>\u8FD9\u4E2A bug \u6211\u4E0A\u5468\u5C31\u4FEE\u597D\u4E86</source>\n<output>\u4E8B\u5B9E: \u7528\u6237\u8BF4\u4ED6\u5728 2026-10-24 \u90A3\u5468\u4FEE\u597D\u4E86\u8FD9\u4E2A bug | \u4FEE\u590D | 2026-10-24 | \u4E0A\u5468 | experienced | 0.7 | \u6301\u6709=world | \u7CBE\u5EA6=day | \u4FEE\u590D\u8005=\u7528\u6237</output>\n</example>\n<example>\n<source>\u597D\u7684\uFF0C\u8C22\u8C22</source>\n<output>\u65E0</output>\n</example>\n<example>\n<source>\u4F60\u5E2E\u6211\u628A\u90A3\u4E2A\u8868\u91CD\u65B0\u8DD1\u4E00\u904D</source>\n<output>\u65E0</output>\n</example>\n<example>\n<source>\u6211\u8BA8\u538C\u540C\u4E00\u4EF6\u4E8B\u53CD\u590D\u8BB2\uFF0C\u6362\u4E2A\u5DE5\u5177\u53C8\u8981\u91CD\u65B0\u8BF4\u4E00\u904D</source>\n<output>\u4E8B\u5B9E: \u7528\u6237\u8BA8\u538C\u540C\u4E00\u4EF6\u4E8B\u53CD\u590D\u8BB2\uFF0C\u6362\u5DE5\u5177\u4E5F\u4E0D\u60F3\u91CD\u590D\u4EA4\u4EE3 | \u504F\u597D | | | experienced | 0.9 | \u6301\u6709=\u7528\u6237 | \u7CBE\u5EA6=unknown | \u504F\u597D\u4E3B\u4F53=\u7528\u6237</output>\n</example>\n</examples>";
|
|
6
6
|
export declare const extractionRole = "<role>\n\u4F60\u662F\u8BB0\u5FC6\u62BD\u53D6\u5668\u3002\u4F60\u53EA\u4ECE\u5BF9\u8BDD\u91CC\u62BD\u53D6\u53EF\u9A8C\u8BC1\u7684\u4E8B\u5B9E\uFF0C\u4E0D\u505A\u5224\u65AD\u3001\u4E0D\u7ED9\u5EFA\u8BAE\u3001\u4E0D\u5199\u603B\u7ED3\u3002\n\u4F60\u7684\u8F93\u51FA\u7531\u4E00\u4E2A\u786E\u5B9A\u6027\u7684\u7A0B\u5E8F\u6D88\u8D39\uFF0C\u5B83\u4E0D\u4F1A\u8BFB\u4F60\u7684\u89E3\u91CA\uFF1B\u683C\u5F0F\u9519\u4E00\u4E2A\u5B57\u6BB5\uFF0C\u8FD9\u6761\u8BB0\u5FC6\u5C31\u4E22\u4E86\u3002\n</role>";
|
|
7
7
|
export declare const extractionRules = "<rules>\n\u672F\u8BED\u7684\u542B\u4E49\u89C1 <terms>\uFF1B\u8FD9\u91CC\u53EA\u8BF4\u64CD\u4F5C\u548C\u53D6\u820D\u3002\n- \u5408\u5E76\uFF1A\u4E0E <known-facts> \u8BED\u4E49\u76F8\u540C \u2192 noop\uFF1B\u4E92\u76F8\u77DB\u76FE \u2192 supersede \u5E76\u7ED9 target\uFF1B\u662F\u8865\u5145 \u2192 update\u3002\n \u76F8\u5173\u4F46\u72EC\u7ACB \u2192 add\u3002\u4E0D\u8981\u7528 <known-facts> \u91CC\u6CA1\u6709\u7684\u65B0\u4E8B\u5B9E\u53BB supersede\u3002\n- \u8981\u53D6\u4EE3\u65E7\u4E8B\u5B9E\u65F6\uFF0C\u5728\u4E8B\u5B9E\u884C\u5C3E\u90E8\u5199\u300C\u64CD\u4F5C=\u53D6\u4EE3 <id>\u300D\uFF08id \u53EA\u80FD\u6765\u81EA <known-facts>\uFF09\u3002\n \u4E0D\u5199\u5C31\u662F\u65B0\u589E\u3002\u4EE3\u7801\u4F1A\u5148\u786E\u8BA4\u90A3\u4E2A id \u771F\u5B9E\u5B58\u5728\uFF1B\u7F16\u9020\u7684 id \u4F1A\u9000\u5316\u6210\u65B0\u589E\u5E76\u8BB0\u539F\u56E0\u3002\n- \u69FD\u4F4D\u53EA\u653E\u53C2\u4E0E\u8005\uFF0C\u4E0D\u653E\u8BDD\u9898\uFF1A\u5B81\u53EF\u5C11\u5199\u4E00\u4E2A\u69FD\u4F4D\uFF0C\u4E5F\u4E0D\u8981\u628A\u4E00\u53E5\u8BDD\u585E\u8FDB\u69FD\u4F4D\u3002\n- \u4E0D\u786E\u5B9A\u5C31\u7701\u7565\u5B57\u6BB5\uFF0C\u7EDD\u4E0D\u7F16\u9020\u6570\u5B57\u3001\u65E5\u671F\u3001\u5173\u7CFB\u3002\n- \u4E0D\u8981\u91CD\u590D\u7528\u6237\u521A\u843D\u7684\u8BDD\uFF08\u56DE\u58F0\u4E0D\u7B97\u65B0\u4E8B\u5B9E\uFF09\u3002\n- \u6765\u6E90\u6309\u300C\u8C01\u8BF4\u7684\u300D\u5B9A\uFF08<source> \u91CC\u6807\u4E86\u53CC\u65B9\uFF09\uFF1A\u7528\u6237\u90A3\u4E00\u4FA7\u5199 told \u6216 experienced\uFF1B\n **\u6211\u90A3\u4E00\u4FA7\u5199 inferred**\u3002\u6211\u7684\u627F\u8BFA/\u51B3\u5B9A\u5199 \u7C7B\u578B\u540D=\u627F\u8BFA/\u51B3\u5B9A\uFF1B\n \u6211\u7684\u5B8C\u6210\u58F0\u660E\uFF08\"\u6211\u6539\u597D\u4E86\"\"\u5DF2\u7ECF\u4FEE\u4E86\"\uFF09\u5982\u679C\u6CA1\u6709\u5DE5\u5177\u6216\u6D4B\u8BD5\u7ED3\u679C\u4F5C\u4E3A\u4F9D\u636E\uFF0C**\u4E0D\u8981\u5199**\u3002\n \u81EA\u8FF0\uFF08\"\u6211\u662F\u5C0F\u7075\uFF0C\u4E00\u4E2A\u6301\u7EED\u7684\u4F19\u4F34\"\uFF09\u6C38\u8FDC\u4E0D\u5199\u3002\n- \u7EBF\u7D22\uFF1A\u5019\u9009\u89C1 <open-threads>\uFF0C\u6BCF\u6761\u5E26\u5B83\u6700\u8FD1\u51E0\u8F6E\u7684\u539F\u6587\u3002**\u9ED8\u8BA4\u7EE7\u7EED**\uFF1A\u8FD9\u4E00\u8F6E\u5728\u804A\u5019\u9009\u91CC\u7684\u67D0\u4E00\u6761\uFF0C\n \u5C31\u5199\u300C\u7EE7\u7EED <id>\u300D\u3002\u53EA\u6709\u51FA\u73B0\u660E\u786E\u8F6C\u6298\uFF08\"\u53E6\u5916 / \u6362\u4E2A\u8BDD\u9898 / \u8BF4\u56DE / \u4E0D\u804A\u8FD9\u4E2A\u4E86\"\uFF09\uFF0C\n \u6216\u8FD9\u4E00\u8F6E\u4E0E\u6240\u6709\u5019\u9009\u7684\u4E3B\u9898\u3001\u5B9E\u4F53\u90FD\u4E0D\u76F8\u5E72\u65F6\uFF0C\u624D\u5199\u300C\u7EBF\u7D22: \u65B0 | \u4E00\u53E5\u8BDD\u4E3B\u9898\u300D\u3002\n \u62FF\u4E0D\u51C6\u5C31\u7EE7\u7EED\u6700\u63A5\u8FD1\u7684\u90A3\u4E00\u6761\u2014\u2014\u7EBF\u7D22\u788E\u6389\u4F1A\u8BA9\u540E\u9762\u7684\u5F52\u7EB3\u65E0\u6CD5\u8FDB\u884C\uFF0C\u63A5\u9519\u53EF\u4EE5\u4E8B\u540E\u62C6\u5F00\u3002\n \u7EE7\u7EED\u65F6\uFF0C\u5982\u679C\u8FD9\u4E00\u8F6E\u5DF2\u7ECF\u4E0D\u662F\u5728\u804A\u5019\u9009\u539F\u6765\u7684\u4E3B\u9898\uFF08\u8BDD\u9898\u5728\u540C\u4E00\u7EBF\u7D22\u5185\u5F80\u524D\u8D70\u4E86\uFF09\uFF0C\n \u8865\u4E00\u53E5\u300C| \u4E3B\u9898=\u73B0\u5728\u804A\u7684\u662F\u4EC0\u4E48\u300D\u2014\u2014\u7EBF\u7D22\u7684\u4E3B\u9898\u662F\u7ED9\u540E\u7EED\u8BFB\u7684\u4EBA\u770B\u7684\uFF0C\u4E0D\u66F4\u65B0\u5C31\u4F1A\u4E00\u76F4\u505C\u5728\u7B2C\u4E00\u8F6E\u7684\u8BF4\u6CD5\u3002\n \u6CA1\u6709\u4EFB\u4F55\u5019\u9009\u65F6\u8FD9\u4E00\u884C\u53EA\u53EF\u80FD\u662F\"\u65B0\"\u3002\n</rules>\n\n<when-to-write>\n\u53EA\u5728\u8FD9\u56DB\u79CD\u60C5\u51B5\u4E0B\u5199\u4E8B\u5B9E\uFF1A\n1. \u67D0\u4E2A\u4EBA\u6216\u67D0\u4EF6\u4E8B\u7684\u72B6\u6001\u3001\u7ECF\u5386\uFF08\u201C\u5C0F\u6797\u6362\u4E86\u5DE5\u4F5C\u201D\u201C\u90A3\u4E2A\u9879\u76EE\u9EC4\u4E86\u201D\uFF09\n2. \u4E00\u4E2A\u51B3\u5B9A\uFF0C\u4EE5\u53CA\u505A\u8FD9\u4E2A\u51B3\u5B9A\u7684\u7406\u7531\n3. \u7528\u6237\u8868\u8FBE\u7684\u504F\u597D\u3001\u539F\u5219\u6216\u4E60\u60EF\uFF08\u201C\u6211\u8BA8\u538C\u53CD\u590D\u8BB2\u540C\u4E00\u4EF6\u4E8B\u201D\uFF09\n4. \u6211\u7ED9\u51FA\u7684\u627F\u8BFA\uFF08\u201C\u6211\u660E\u5929\u628A\u8868\u8865\u4E0A\u201D\uFF09\n\n**\u4E0D\u8981\u5199**\uFF08\u8FD9\u662F\u6700\u5E38\u89C1\u7684\u566A\u58F0\uFF09\uFF1A\n- \u7528\u6237\u5BF9\u6211\u7684\u8981\u6C42\u3001\u63D0\u95EE\u3001\u50AC\u4FC3\u3001\u540C\u610F\u3001\u5426\u5B9A\u2014\u2014\u8FD9\u4E9B\u662F\u5BF9\u8BDD\u52A8\u4F5C\uFF0C\u5BF9\u8BDD\u539F\u6587\u5DF2\u7ECF\u4FDD\u5B58\uFF0C\u518D\u5199\u4E00\u4EFD\u53EA\u4F1A\u53D8\u6210\u566A\u58F0\n- \u6211\u95EE\u7528\u6237\u7684\u6F84\u6E05\u95EE\u9898\n- \u5BD2\u6684\u3001\u6211\u7684\u81EA\u6211\u4ECB\u7ECD\u3001\u6211\u5BF9\u80FD\u529B\u7684\u8BF4\u660E\n- \u7528\u6237\u6B63\u5728\u95EE\u6211\u7684\u4E8B\uFF0C\u4E0D\u662F\u201C\u5173\u4E8E\u7528\u6237\u7684\u4E8B\u5B9E\u201D\n</when-to-write>\n\n<gaps>\n\u53EA\u6709\u4E24\u79CD\u60C5\u51B5\u5EFA\u7F3A\u53E3\uFF1A\n1. \u7528\u6237\u8BF4\u81EA\u5DF1\u4E0D\u77E5\u9053\u3001\u6CA1\u6D88\u606F\u3001\u60F3\u77E5\u9053\u67D0\u4EF6\u4E8B\uFF08\u201C\u5979\u540E\u6765\u600E\u4E48\u6837\u4E86\u201D\u201C\u4E0D\u77E5\u9053\u8FD8\u5728\u4E0D\u5728\u201D\uFF09\n2. \u6211\u8BF4\u8FC7\u8981\u505A\u3001\u4F46\u72B6\u6001\u4ECD\u672A\u5B8C\u6210\u7684\u4E8B\n\n\u7528\u6237\u53EA\u662F\u5728\u95EE\u6211\u4E00\u4E2A\u95EE\u9898\u65F6**\u4E0D\u8981**\u5EFA\u7F3A\u53E3\u2014\u2014\u90A3\u662F\u4ED6\u5728\u63D0\u95EE\uFF0C\u4E0D\u662F\u4ED6\u7F3A\u4FE1\u606F\u3002\n</gaps>";
|
|
8
|
-
export declare const extractionSchema = "<output-format>\n\u4E0D\u8981\u8F93\u51FA JSON\u3002\u6309\u4E0B\u9762\u7684\u8868\u683C\u4E00\u884C\u4E00\u6761\u586B\u5199\uFF0C\u5B57\u6BB5\u4E4B\u95F4\u7528 | \u5206\u9694\uFF0C\u6CA1\u6709\u5185\u5BB9\u7684\u5B57\u6BB5\u7559\u7A7A\u3002\n\u53EF\u4EE5\u53EA\u5199\u5176\u4E2D\u51E0\u884C\uFF1B\u4EC0\u4E48\u90FD\u8BB0\u4E0D\u4F4F\u5C31\u53EA\u5199\uFF1A\u65E0\n\n\u5B9E\u4F53: \u540D\u5B57 | \u7C7B\u578B | \u5173\u7CFB\n\u4E8B\u5B9E: \u9648\u8FF0 | \u7C7B\u578B\u540D | \u65E5\u671F(YYYY-MM-DD) | \u65F6\u95F4\u539F\u6587 | \u6765\u6E90 | \u7F6E\u4FE1(0-1) | \u6301\u6709=world\u6216\u4EBA\u540D | \u7CBE\u5EA6=day/range/relative/unknown | \u539F\u5219\u5019\u9009=\u662F | \u69FD\u4F4D | \u64CD\u4F5C=\u53D6\u4EE3 <id>\n\u7F3A\u53E3: \u4E3B\u9898 | \u5F20\u529B(0-1) | \u76F8\u5173\u5B9E\u4F53\n\u7EBF\u7D22: \u7EE7\u7EED <id> \u6216 \u7EBF\u7D22: \u65B0 | \u4E00\u53E5\u8BDD\u4E3B\u9898\n \u8BDD\u9898\u5728\u540C\u4E00\u7EBF\u7D22\u5185\u53D8\u4E86\uFF0C\u5C31\u5199\uFF1A\u7EBF\u7D22: \u7EE7\u7EED <id> | \u4E3B\u9898=\u65B0\u7684\u4E00\u53E5\u8BDD\u4E3B\u9898\n\n\u6BCF\u4E2A\u5B57\u6BB5\u7684\u8FB9\u754C\u89C1 <terms>\uFF0C\u8FD9\u91CC\u53EA\u8BF4\u683C\u5F0F\uFF1A\u65E5\u671F\u5FC5\u987B\u662F YYYY-MM-DD\uFF1B\u69FD\u4F4D\u5199\u6210 \u540D\u79F0=\u503C\uFF0C\u591A\u4E2A\u7528 ; \u9694\u5F00\uFF1B\n\"\u6301\u6709/\u7CBE\u5EA6/\u539F\u5219\u5019\u9009\"\u662F\u952E\u503C\u5BF9\uFF0C\u5199\u5728\u4E8B\u5B9E\u884C\u7684\u5C3E\u90E8\uFF1B\u7EBF\u7D22\u884C\u4E8C\u9009\u4E00\uFF08\u7EE7\u7EED\u65F6\u53EF\u9644 \u4E3B\u9898=\uFF09\u3002\n\u793A\u4F8B\uFF1A\n\u5B9E\u4F53: \u5C0F\u6797 | person | \u540C\u4E8B\n\u4E8B\u5B9E: \u5C0F\u6797\u5728 2026-10-24 \u90A3\u5468\u88AB\u88C1\u5458 | \u88C1\u5458 | 2026-10-24 | \u4E0A\u5468 | told | 0.8 | \u6301\u6709=world | \u7CBE\u5EA6=day | \u88AB\u88C1\u5458\u8005=@\u5C0F\u6797\n\u4E8B\u5B9E: \u7528\u6237\u8981\u6C42\u6539\u52A8\u524D\u5148\u8BF4\u660E\u4E3A\u4EC0\u4E48\u3001\u600E\u4E48\u505A\u3001\u6709\u4EC0\u4E48\u5F71\u54CD | \u8981\u6C42 | | | experienced | 0.9 | \u6301\u6709=\u7528\u6237 | \u7CBE\u5EA6=unknown | \u539F\u5219\u5019\u9009=\u662F | \u5BF9\u8C61=@\u7528\u6237\n\u7F3A\u53E3: \u5C0F\u6797\u540E\u6765\u6709\u7740\u843D\u5417 | 0.6 | \u5C0F\u6797\n\u7EBF\u7D22: \u7EE7\u7EED th_ab12\n</output-format>";
|
|
8
|
+
export declare const extractionSchema = "<output-format>\n\u4E0D\u8981\u8F93\u51FA JSON\u3002\u6309\u4E0B\u9762\u7684\u8868\u683C\u4E00\u884C\u4E00\u6761\u586B\u5199\uFF0C\u5B57\u6BB5\u4E4B\u95F4\u7528 | \u5206\u9694\uFF0C\u6CA1\u6709\u5185\u5BB9\u7684\u5B57\u6BB5\u7559\u7A7A\u3002\n\u53EF\u4EE5\u53EA\u5199\u5176\u4E2D\u51E0\u884C\uFF1B\u4EC0\u4E48\u90FD\u8BB0\u4E0D\u4F4F\u5C31\u53EA\u5199\uFF1A\u65E0\n\n\u5B9E\u4F53: \u540D\u5B57 | \u7C7B\u578B | \u5173\u7CFB\n\u4E8B\u5B9E: \u9648\u8FF0 | \u7C7B\u578B\u540D | \u65E5\u671F(YYYY-MM-DD) | \u65F6\u95F4\u539F\u6587 | \u6765\u6E90 | \u7F6E\u4FE1(0-1) | \u6301\u6709=world\u6216\u4EBA\u540D | \u7CBE\u5EA6=day/range/relative/unknown | \u539F\u5219\u5019\u9009=\u662F | \u89D2\u8272=observation/judgment/rule/profile/promise/lesson | \u69FD\u4F4D | \u64CD\u4F5C=\u53D6\u4EE3 <id>\n\u7F3A\u53E3: \u4E3B\u9898 | \u5F20\u529B(0-1) | \u76F8\u5173\u5B9E\u4F53\n\u7EBF\u7D22: \u7EE7\u7EED <id> \u6216 \u7EBF\u7D22: \u65B0 | \u4E00\u53E5\u8BDD\u4E3B\u9898\n \u8BDD\u9898\u5728\u540C\u4E00\u7EBF\u7D22\u5185\u53D8\u4E86\uFF0C\u5C31\u5199\uFF1A\u7EBF\u7D22: \u7EE7\u7EED <id> | \u4E3B\u9898=\u65B0\u7684\u4E00\u53E5\u8BDD\u4E3B\u9898\n\n\u6BCF\u4E2A\u5B57\u6BB5\u7684\u8FB9\u754C\u89C1 <terms>\uFF0C\u8FD9\u91CC\u53EA\u8BF4\u683C\u5F0F\uFF1A\u65E5\u671F\u5FC5\u987B\u662F YYYY-MM-DD\uFF1B\u69FD\u4F4D\u5199\u6210 \u540D\u79F0=\u503C\uFF0C\u591A\u4E2A\u7528 ; \u9694\u5F00\uFF1B\n\"\u6301\u6709/\u7CBE\u5EA6/\u539F\u5219\u5019\u9009/\u89D2\u8272\"\u662F\u952E\u503C\u5BF9\uFF0C\u5199\u5728\u4E8B\u5B9E\u884C\u7684\u5C3E\u90E8\uFF1B\u7EBF\u7D22\u884C\u4E8C\u9009\u4E00\uFF08\u7EE7\u7EED\u65F6\u53EF\u9644 \u4E3B\u9898=\uFF09\u3002\n\u89D2\u8272\u662F\u8FD9\u4E2A\u4E8B\u5B9E\u4EE5\u540E**\u88AB\u8C01\u7528**\uFF1A\u89C2\u5BDF\u7528 observation\uFF0C\u5224\u65AD/\u51B3\u5B9A\u7528 judgment\uFF0C\u89C4\u5219\u7528 rule\uFF0C\n\u753B\u50CF\u7528 profile\uFF0C\u6211\u7B54\u5E94\u7684\u4E8B\u7528 promise\uFF0C\u6559\u8BAD\u7528 lesson\u3002\u62FF\u4E0D\u51C6\u5C31\u628A\u89D2\u8272\u7559\u7A7A\u2014\u2014\u7A7A\u8868\u793A\u89C2\u5BDF\u3002\n\u793A\u4F8B\uFF1A\n\u5B9E\u4F53: \u5C0F\u6797 | person | \u540C\u4E8B\n\u4E8B\u5B9E: \u5C0F\u6797\u5728 2026-10-24 \u90A3\u5468\u88AB\u88C1\u5458 | \u88C1\u5458 | 2026-10-24 | \u4E0A\u5468 | told | 0.8 | \u6301\u6709=world | \u7CBE\u5EA6=day | \u88AB\u88C1\u5458\u8005=@\u5C0F\u6797\n\u4E8B\u5B9E: \u7528\u6237\u8981\u6C42\u6539\u52A8\u524D\u5148\u8BF4\u660E\u4E3A\u4EC0\u4E48\u3001\u600E\u4E48\u505A\u3001\u6709\u4EC0\u4E48\u5F71\u54CD | \u8981\u6C42 | | | experienced | 0.9 | \u6301\u6709=\u7528\u6237 | \u7CBE\u5EA6=unknown | \u539F\u5219\u5019\u9009=\u662F | \u89D2\u8272=rule | \u5BF9\u8C61=@\u7528\u6237\n\u7F3A\u53E3: \u5C0F\u6797\u540E\u6765\u6709\u7740\u843D\u5417 | 0.6 | \u5C0F\u6797\n\u7EBF\u7D22: \u7EE7\u7EED th_ab12\n</output-format>";
|
|
9
9
|
export declare const extractionTerms = "<terms>\n\u6BCF\u4E2A\u672F\u8BED\u7684\u542B\u4E49\u548C\u8FB9\u754C\u3002\u5199\u4E4B\u524D\u5148\u5224\u65AD\u5B83\u5C5E\u4E8E\u54EA\u4E2A\u672F\u8BED\u2014\u2014\u653E\u9519\u4F4D\u7F6E\u6BD4\u6F0F\u5199\u66F4\u7CDF\u3002\n\n**\u5B9E\u4F53**\uFF1A\u4E00\u4E2A\u5177\u4F53\u7684\u4EBA\u3001\u7269\u3001\u5730\u70B9\u6216\u5E73\u53F0\u3002\u5B83\u662F\"\u8C01/\u4EC0\u4E48\"\uFF0C\u4E0D\u662F\"\u505A\u4E86\u4EC0\u4E48\"\u3002\n \u7B97\uFF1A\u5C0F\u6797\u3001\u7528\u6237\u3001TypeScript\u3001\u6296\u97F3\u3001PowerShell\u3001config \u6587\u4EF6\u5939\n \u4E0D\u7B97\uFF1A\u4E0D\u5220\u9664\u539F\u59CB\u5BF9\u8BDD\u3001\u4ECE\u6839\u4E0A\u89E3\u51B3\u3001\u5FC5\u8981\u3001\u566A\u58F0\u8BB0\u5FC6\u3001\u60C5\u7EEA\u6CE8\u5165\u3001\u9879\u76EE\u8FDB\u5C55\n \u8FB9\u754C\uFF1A\u80FD\u7528\"\u8FD9\u8BF4\u7684\u662F\u8C01\"\u95EE\u51FA\u6765\u7684\u624D\u7B97\u3002\u4E00\u53E5\u8BDD\u3001\u4E00\u4E2A\u89C2\u70B9\u3001\u4E00\u4E2A\u505A\u6CD5\u90FD\u4E0D\u662F\u5B9E\u4F53\u3002\n \u5224\u636E\uFF1A\u8D85\u8FC7 12 \u4E2A\u5B57\uFF0C\u6216\u542B\"\u7684/\u548C/\u4E0E\"\uFF0C\u6216\u80FD\u52A0\u4E0A\"\u8FD9\u4EF6\u4E8B\"\u7684\uFF0C\u57FA\u672C\u90FD\u4E0D\u662F\u5B9E\u4F53\u3002\n \u540C\u4E00\u4E2A\u4EBA\u6216\u7269\u53EA\u51FA\u73B0\u4E00\u6B21\uFF1Btype \u53EA\u80FD\u662F person / thing / place / self\u3002\n\n**\u4E8B\u5B9E**\uFF1A\u4E00\u53E5\u81EA\u5305\u542B\u7684\u9648\u8FF0\uFF0C\u56DE\u7B54\"\u53D1\u751F\u4E86\u4EC0\u4E48\"\u6216\"\u67D0\u4EBA\u662F\u4EC0\u4E48\u6837\"\u3002\n \u7B97\uFF1A\u5C0F\u6797\u5728 2026-10-24 \u90A3\u5468\u88AB\u88C1\u5458\uFF1B\u7528\u6237\u8981\u6C42\u6539\u52A8\u524D\u5148\u8BF4\u660E\u5F71\u54CD\n \u4E0D\u7B97\uFF1A\u7528\u6237\u7684\u63D0\u95EE\u3001\u50AC\u4FC3\u3001\u540C\u610F\u3001\u5426\u5B9A\uFF1B\u5BD2\u6684\uFF1B\u6211\u5BF9\u81EA\u5DF1\u80FD\u529B\u7684\u8BF4\u660E\n \u8FB9\u754C\uFF1A\u628A\u4EE3\u8BCD\u6362\u6210\u540D\u5B57\u540E\u4ECD\u7136\u6210\u7ACB\uFF0C\u4E14\u6362\u4E2A\u4EBA\u590D\u8FF0\u4E0D\u9700\u8981\u52A0\u95EE\u53F7\u300215\u201380 \u5B57\uFF0C\u4FDD\u7559\u6570\u5B57\u548C\u4E13\u6709\u540D\u8BCD\u3002\n\n**\u6301\u6709**\uFF1A\u8FD9\u6761\u4E8B\u5B9E\u5728\u8C01\u7684\u89C6\u89D2\u91CC\u6210\u7ACB\u3002\u53EA\u6709\u4E24\u4E2A\u5408\u6CD5\u503C\u3002\n world\uFF1A\u4E16\u754C\u4E0A\u53D1\u751F\u7684\u4E8B\uFF0C\u7B2C\u4E09\u4E2A\u4EBA\u4E5F\u80FD\u6838\u5B9E\uFF08\u6362\u5DE5\u4F5C\u3001\u5403\u996D\u3001\u4E0B\u96E8\u3001\u7EA6\u5B9A\u6697\u53F7\uFF09\n \u4EBA\u540D\uFF1A\u67D0\u4EBA\u7684\u770B\u6CD5\u3001\u504F\u597D\u3001\u8981\u6C42\u3001\u62C5\u5FC3\uFF08\"\u7528\u6237\u8BA4\u4E3A\u2026\"\uFF09\uFF0C\u53EA\u6709\u90A3\u4E2A\u4EBA\u80FD\u786E\u8BA4\n \u8FB9\u754C\uFF1A\u80FD\u5199\u6210\u65B0\u95FB\u62A5\u9053\u7684 \u2192 world\uFF1B\u53EA\u80FD\u5199\u6210\"X \u8BA4\u4E3A\"\u7684 \u2192 \u4EBA\u540D\u3002\u4EBA\u7269\u5C42\u4E0D\u5199\u65E5\u671F\u3002\n\n**\u69FD\u4F4D**\uFF1A\u8FD9\u6761\u4E8B\u5B9E\u7684\u53C2\u4E0E\u8005\u4E0E\u53C2\u6570\uFF0C\u5199\u6210 \u540D\u79F0=\u503C\uFF0C\u591A\u4E2A\u7528 ; \u9694\u5F00\u3002\n \u53EA\u653E\"\u8C01\u3001\u5BF9\u8C01\u3001\u5728\u54EA\u91CC\u3001\u591A\u5C11\"\u8FD9\u7C7B\u7ED3\u6784\u4F4D\u7F6E\u3002\n \u7B97\uFF1A\u88AB\u88C1\u5458\u8005=@\u5C0F\u6797\uFF1B\u4EBA\u6570=300\uFF1B\u5730\u70B9=\u676D\u5DDE\n \u4E0D\u7B97\uFF1A\u4E8B\u9879=\u4E0D\u5220\u9664\u539F\u59CB\u5BF9\u8BDD\uFF1B\u505A\u6CD5=\u4ECE\u6839\u4E0A\u89E3\u51B3\uFF1B\u7406\u7531=\u5FC5\u8981\n \u8FB9\u754C\uFF1A\u503C\u8981\u4EE5\u5B9E\u4F53\u8EAB\u4EFD\u53C2\u4E0E\u65F6\uFF0C\u5199\u6210 **@\u540D\u5B57**\uFF1B\u4E0D\u5199 @ \u7684\u4E00\u5F8B\u5F53\u6587\u5B57\u3002\n @\u540D\u5B57 \u5FC5\u987B\u5728\u672C\u8F6E\u5B9E\u4F53\u884C\u58F0\u660E\uFF08\u6216\u5DF2\u5728 <known-entities> \u91CC\uFF09\uFF0C\u5426\u5219\u4ECD\u6309\u6587\u5B57\u4FDD\u5B58\u3001\u4E0D\u5EFA\u5B9E\u4F53\u3002\n\n**\u65E5\u671F\u4E0E\u7CBE\u5EA6**\uFF1A\u5148\u6309 <observation-date> \u628A\"\u6628\u5929/\u4E0A\u5468/\u4E0A\u4E2A\u6708\"\u6362\u7B97\u6210 YYYY-MM-DD \u586B\u5165\u65E5\u671F\u3002\n \u7CBE\u5EA6\u56DB\u503C\uFF1Aday \u786E\u5207\u65E5\u671F / range \u53EA\u77E5\u9053\u4E00\u5468\u4E00\u6708 / relative \u53EA\u6709\u76F8\u5BF9\u8BF4\u6CD5\uFF08\u4E0A\u6B21\u3001\u6700\u8FD1\u3001\u521D\u6B21\u89C1\u9762\uFF09/ unknown \u4E0D\u77E5\u9053\u3002\n \u6362\u7B97\u4E0D\u51FA\u6765\u5C31\u7559\u7A7A\u65E5\u671F\uFF0C\u628A\u539F\u8BDD\u5199\u8FDB\"\u65F6\u95F4\u539F\u6587\"\u3002**\u6CA1\u6709\u65F6\u95F4\u5C31\u5199 unknown\uFF0C\u7EDD\u4E0D\u7F16\u65E5\u671F\u3002**\n\n**\u6765\u6E90**\uFF1Aexperienced \u4F60\u4EB2\u5386 / told \u542C\u522B\u4EBA\u8BF4 / inferred \u4F60\u63A8\u65AD\u3002\n**\u7C7B\u578B\u540D**\uFF1A\u4E00\u53E5\u81EA\u7531\u63CF\u8FF0\uFF08\u504F\u597D\u3001\u8981\u6C42\u3001\u6280\u672F\u7EA6\u675F\u3001\u4E8B\u4EF6\uFF09\u3002\u5B83\u4E0D\u662F\u5206\u7C7B\u4F53\u7CFB\uFF0C\u5199\u6700\u8D34\u5207\u7684\u8BCD\u5373\u53EF\uFF0C\u4E0D\u7528\u9020\u5206\u7C7B\u3002\n**\u7F6E\u4FE1**\uFF1A0\u20131\u3002\u62FF\u4E0D\u51C6\u5199\u4F4E\uFF0C\u4E0D\u8981\u7A7A\u7740\u2014\u2014\u7A7A\u7740\u4F1A\u88AB\u5F53\u6210\"\u6CA1\u628A\u63E1\"\u3002\n\n**\u539F\u5219\u5019\u9009**\uFF1A\u8FD9\u6761\u4E8B\u5B9E\u662F\u4E0D\u662F\"\u4EE5\u540E\u8BE5\u600E\u4E48\u505A\u4E8B\u7684\u7ECF\u9A8C\"\uFF1F\u5224\u636E\u662F\u4F1A\u4E0D\u4F1A\u5F71\u54CD\u4EE5\u540E\u7684\u505A\u6CD5\u3002\n \u7B97\uFF1A\u6539\u52A8\u524D\u5148\u8BF4\u660E\u5F71\u54CD\uFF1B\u7528\u6237\u8BA8\u538C\u53CD\u590D\u4EA4\u4EE3\n \u4E0D\u7B97\uFF1A\u4ECA\u5929\u6539\u4E86 A \u6587\u4EF6\uFF1B\u5C0F\u6797\u88AB\u88C1\u5458\n \u62FF\u4E0D\u51C6\u5C31\u7701\u7565\u2014\u2014\u4E00\u6761\u4E0D\u8BE5\u5347\u683C\u7684\u7ECF\u9A8C\u6BD4\u6F0F\u6389\u66F4\u7CDF\u3002\n\n**\u7F3A\u53E3**\uFF1A\u63D0\u8FC7\u4F46\u6CA1\u6709\u7B54\u6848\u7684\u4E8B\uFF08\"\u5979\u540E\u6765\u600E\u4E48\u6837\u4E86\"\uFF09\u3002\n**\u7EBF\u7D22**\uFF1A\u8FD9\u6BB5\u5BF9\u8BDD\u5728\u63A5\u7740\u804A\u54EA\u4E2A\u8BDD\u9898\uFF0C\u89C1 <open-threads>\u3002\n</terms>";
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
export const extractionExamples = "<examples>\n<example>\n<source>我同事小林上周被裁员了,挺难受的</source>\n<output>实体: 小林 | person | 同事\n事实: 小林在 2026-10-24 那周被裁员 | 裁员 | 2026-10-24 | 上周 | told | 0.8 | 持有=world | 精度=day | 被裁员者=@小林</output>\n</example>\n<example>\n<source>这个 bug 我上周就修好了</source>\n<output>事实: 用户说他在 2026-10-24 那周修好了这个 bug | 修复 | 2026-10-24 | 上周 | experienced | 0.7 | 持有=world | 精度=day | 修复者=用户</output>\n</example>\n<example>\n<source>好的,谢谢</source>\n<output>无</output>\n</example>\n<example>\n<source>你帮我把那个表重新跑一遍</source>\n<output>无</output>\n</example>\n<example>\n<source>我讨厌同一件事反复讲,换个工具又要重新说一遍</source>\n<output>事实: 用户讨厌同一件事反复讲,换工具也不想重复交代 | 偏好 | | | experienced | 0.9 | 持有=用户 | 精度=unknown | 偏好主体=用户</output>\n</example>\n</examples>";
|
|
6
6
|
export const extractionRole = "<role>\n你是记忆抽取器。你只从对话里抽取可验证的事实,不做判断、不给建议、不写总结。\n你的输出由一个确定性的程序消费,它不会读你的解释;格式错一个字段,这条记忆就丢了。\n</role>";
|
|
7
7
|
export const extractionRules = "<rules>\n术语的含义见 <terms>;这里只说操作和取舍。\n- 合并:与 <known-facts> 语义相同 → noop;互相矛盾 → supersede 并给 target;是补充 → update。\n 相关但独立 → add。不要用 <known-facts> 里没有的新事实去 supersede。\n- 要取代旧事实时,在事实行尾部写「操作=取代 <id>」(id 只能来自 <known-facts>)。\n 不写就是新增。代码会先确认那个 id 真实存在;编造的 id 会退化成新增并记原因。\n- 槽位只放参与者,不放话题:宁可少写一个槽位,也不要把一句话塞进槽位。\n- 不确定就省略字段,绝不编造数字、日期、关系。\n- 不要重复用户刚落的话(回声不算新事实)。\n- 来源按「谁说的」定(<source> 里标了双方):用户那一侧写 told 或 experienced;\n **我那一侧写 inferred**。我的承诺/决定写 类型名=承诺/决定;\n 我的完成声明(\"我改好了\"\"已经修了\")如果没有工具或测试结果作为依据,**不要写**。\n 自述(\"我是小灵,一个持续的伙伴\")永远不写。\n- 线索:候选见 <open-threads>,每条带它最近几轮的原文。**默认继续**:这一轮在聊候选里的某一条,\n 就写「继续 <id>」。只有出现明确转折(\"另外 / 换个话题 / 说回 / 不聊这个了\"),\n 或这一轮与所有候选的主题、实体都不相干时,才写「线索: 新 | 一句话主题」。\n 拿不准就继续最接近的那一条——线索碎掉会让后面的归纳无法进行,接错可以事后拆开。\n 继续时,如果这一轮已经不是在聊候选原来的主题(话题在同一线索内往前走了),\n 补一句「| 主题=现在聊的是什么」——线索的主题是给后续读的人看的,不更新就会一直停在第一轮的说法。\n 没有任何候选时这一行只可能是\"新\"。\n</rules>\n\n<when-to-write>\n只在这四种情况下写事实:\n1. 某个人或某件事的状态、经历(“小林换了工作”“那个项目黄了”)\n2. 一个决定,以及做这个决定的理由\n3. 用户表达的偏好、原则或习惯(“我讨厌反复讲同一件事”)\n4. 我给出的承诺(“我明天把表补上”)\n\n**不要写**(这是最常见的噪声):\n- 用户对我的要求、提问、催促、同意、否定——这些是对话动作,对话原文已经保存,再写一份只会变成噪声\n- 我问用户的澄清问题\n- 寒暄、我的自我介绍、我对能力的说明\n- 用户正在问我的事,不是“关于用户的事实”\n</when-to-write>\n\n<gaps>\n只有两种情况建缺口:\n1. 用户说自己不知道、没消息、想知道某件事(“她后来怎么样了”“不知道还在不在”)\n2. 我说过要做、但状态仍未完成的事\n\n用户只是在问我一个问题时**不要**建缺口——那是他在提问,不是他缺信息。\n</gaps>";
|
|
8
|
-
export const extractionSchema = "<output-format>\n不要输出 JSON。按下面的表格一行一条填写,字段之间用 | 分隔,没有内容的字段留空。\n可以只写其中几行;什么都记不住就只写:无\n\n实体: 名字 | 类型 | 关系\n事实: 陈述 | 类型名 | 日期(YYYY-MM-DD) | 时间原文 | 来源 | 置信(0-1) | 持有=world或人名 | 精度=day/range/relative/unknown | 原则候选=是 | 槽位 | 操作=取代 <id>\n缺口: 主题 | 张力(0-1) | 相关实体\n线索: 继续 <id> 或 线索: 新 | 一句话主题\n 话题在同一线索内变了,就写:线索: 继续 <id> | 主题=新的一句话主题\n\n每个字段的边界见 <terms>,这里只说格式:日期必须是 YYYY-MM-DD;槽位写成 名称=值,多个用 ; 隔开;\n\"
|
|
8
|
+
export const extractionSchema = "<output-format>\n不要输出 JSON。按下面的表格一行一条填写,字段之间用 | 分隔,没有内容的字段留空。\n可以只写其中几行;什么都记不住就只写:无\n\n实体: 名字 | 类型 | 关系\n事实: 陈述 | 类型名 | 日期(YYYY-MM-DD) | 时间原文 | 来源 | 置信(0-1) | 持有=world或人名 | 精度=day/range/relative/unknown | 原则候选=是 | 角色=observation/judgment/rule/profile/promise/lesson | 槽位 | 操作=取代 <id>\n缺口: 主题 | 张力(0-1) | 相关实体\n线索: 继续 <id> 或 线索: 新 | 一句话主题\n 话题在同一线索内变了,就写:线索: 继续 <id> | 主题=新的一句话主题\n\n每个字段的边界见 <terms>,这里只说格式:日期必须是 YYYY-MM-DD;槽位写成 名称=值,多个用 ; 隔开;\n\"持有/精度/原则候选/角色\"是键值对,写在事实行的尾部;线索行二选一(继续时可附 主题=)。\n角色是这个事实以后**被谁用**:观察用 observation,判断/决定用 judgment,规则用 rule,\n画像用 profile,我答应的事用 promise,教训用 lesson。拿不准就把角色留空——空表示观察。\n示例:\n实体: 小林 | person | 同事\n事实: 小林在 2026-10-24 那周被裁员 | 裁员 | 2026-10-24 | 上周 | told | 0.8 | 持有=world | 精度=day | 被裁员者=@小林\n事实: 用户要求改动前先说明为什么、怎么做、有什么影响 | 要求 | | | experienced | 0.9 | 持有=用户 | 精度=unknown | 原则候选=是 | 角色=rule | 对象=@用户\n缺口: 小林后来有着落吗 | 0.6 | 小林\n线索: 继续 th_ab12\n</output-format>";
|
|
9
9
|
export const extractionTerms = "<terms>\n每个术语的含义和边界。写之前先判断它属于哪个术语——放错位置比漏写更糟。\n\n**实体**:一个具体的人、物、地点或平台。它是\"谁/什么\",不是\"做了什么\"。\n 算:小林、用户、TypeScript、抖音、PowerShell、config 文件夹\n 不算:不删除原始对话、从根上解决、必要、噪声记忆、情绪注入、项目进展\n 边界:能用\"这说的是谁\"问出来的才算。一句话、一个观点、一个做法都不是实体。\n 判据:超过 12 个字,或含\"的/和/与\",或能加上\"这件事\"的,基本都不是实体。\n 同一个人或物只出现一次;type 只能是 person / thing / place / self。\n\n**事实**:一句自包含的陈述,回答\"发生了什么\"或\"某人是什么样\"。\n 算:小林在 2026-10-24 那周被裁员;用户要求改动前先说明影响\n 不算:用户的提问、催促、同意、否定;寒暄;我对自己能力的说明\n 边界:把代词换成名字后仍然成立,且换个人复述不需要加问号。15–80 字,保留数字和专有名词。\n\n**持有**:这条事实在谁的视角里成立。只有两个合法值。\n world:世界上发生的事,第三个人也能核实(换工作、吃饭、下雨、约定暗号)\n 人名:某人的看法、偏好、要求、担心(\"用户认为…\"),只有那个人能确认\n 边界:能写成新闻报道的 → world;只能写成\"X 认为\"的 → 人名。人物层不写日期。\n\n**槽位**:这条事实的参与者与参数,写成 名称=值,多个用 ; 隔开。\n 只放\"谁、对谁、在哪里、多少\"这类结构位置。\n 算:被裁员者=@小林;人数=300;地点=杭州\n 不算:事项=不删除原始对话;做法=从根上解决;理由=必要\n 边界:值要以实体身份参与时,写成 **@名字**;不写 @ 的一律当文字。\n @名字 必须在本轮实体行声明(或已在 <known-entities> 里),否则仍按文字保存、不建实体。\n\n**日期与精度**:先按 <observation-date> 把\"昨天/上周/上个月\"换算成 YYYY-MM-DD 填入日期。\n 精度四值:day 确切日期 / range 只知道一周一月 / relative 只有相对说法(上次、最近、初次见面)/ unknown 不知道。\n 换算不出来就留空日期,把原话写进\"时间原文\"。**没有时间就写 unknown,绝不编日期。**\n\n**来源**:experienced 你亲历 / told 听别人说 / inferred 你推断。\n**类型名**:一句自由描述(偏好、要求、技术约束、事件)。它不是分类体系,写最贴切的词即可,不用造分类。\n**置信**:0–1。拿不准写低,不要空着——空着会被当成\"没把握\"。\n\n**原则候选**:这条事实是不是\"以后该怎么做事的经验\"?判据是会不会影响以后的做法。\n 算:改动前先说明影响;用户讨厌反复交代\n 不算:今天改了 A 文件;小林被裁员\n 拿不准就省略——一条不该升格的经验比漏掉更糟。\n\n**缺口**:提过但没有答案的事(\"她后来怎么样了\")。\n**线索**:这段对话在接着聊哪个话题,见 <open-threads>。\n</terms>";
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The two decisions every reader used to make for itself, in one place.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - `status` moves from none/superseded to raw/candidate/active/retracted, but old rows keep
|
|
8
|
-
* their old values until the migration runs.
|
|
4
|
+
* `role` says what a fact is for; `status` says how far it has come. Both used to be read by
|
|
5
|
+
* matching Chinese type labels and status strings at each call site, which is how one filter
|
|
6
|
+
* ended up admitting retired facts after a rename.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* This is the contract step: the dual-vocabulary fallback is gone. The migration normalises
|
|
9
|
+
* old rows (none/open/kept → active, broken/superseded → retracted) and the writer always
|
|
10
|
+
* stores a role, so a reader sees exactly one vocabulary.
|
|
13
11
|
*/
|
|
14
12
|
import type { Fact } from './types.ts';
|
|
15
13
|
import type { FactRole } from './types.ts';
|
|
16
|
-
|
|
14
|
+
/** Rows written before the role column existed cannot appear after the migration. */
|
|
15
|
+
export declare function roleOf(fact: Pick<Fact, 'role'>): FactRole;
|
|
17
16
|
export declare function isActive(fact: Pick<Fact, 'status'>): boolean;
|
|
18
17
|
export declare function isRetracted(fact: Pick<Fact, 'status'>): boolean;
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { FACT_STATE_MIGRATION, LEGACY_ROLE_BY_TYPE } from './config/domains.js';
|
|
2
1
|
import { FACT_STATUS } from './config/status.js';
|
|
3
|
-
|
|
4
|
-
/** Old and new status values that mean "this is in use". */
|
|
5
|
-
const ACTIVE_STATES = new Set(['active', 'none', 'kept', 'open']);
|
|
2
|
+
/** Rows written before the role column existed cannot appear after the migration. */
|
|
6
3
|
export function roleOf(fact) {
|
|
7
|
-
|
|
8
|
-
return fact.role;
|
|
9
|
-
return LEGACY_ROLES[fact.typeName ?? ''] ?? 'observation';
|
|
4
|
+
return fact.role ?? 'observation';
|
|
10
5
|
}
|
|
11
6
|
export function isActive(fact) {
|
|
12
|
-
return
|
|
7
|
+
return String(fact.status) === FACT_STATUS.active;
|
|
13
8
|
}
|
|
14
9
|
export function isRetracted(fact) {
|
|
15
|
-
|
|
16
|
-
return mapped === 'retracted' || String(fact.status) === FACT_STATUS.superseded;
|
|
10
|
+
return String(fact.status) === FACT_STATUS.retracted;
|
|
17
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import type { SoulStore } from './store.ts';
|
|
3
|
-
export declare const SCHEMA_VERSION =
|
|
3
|
+
export declare const SCHEMA_VERSION = 10;
|
|
4
4
|
export declare const AFFECT_RETENTION_MS: number;
|
|
5
5
|
export declare class DbNewerThanBinaryError extends Error {
|
|
6
6
|
readonly code = "DB_NEWER_THAN_BINARY";
|
|
@@ -13,6 +13,7 @@ export declare class DbNewerThanBinaryError extends Error {
|
|
|
13
13
|
* old table stays as a read-only legacy copy.
|
|
14
14
|
*/
|
|
15
15
|
/** v6 → v7: turn threads (a topic's turns need not be adjacent) plus turns.thread_id. */
|
|
16
|
+
export declare function upgradeToV10(db: DatabaseSync): void;
|
|
16
17
|
export declare function upgradeToV9(db: DatabaseSync): void;
|
|
17
18
|
export declare function upgradeToV8(db: DatabaseSync): void;
|
|
18
19
|
export declare function upgradeToV7(db: DatabaseSync): void;
|
|
@@ -10,9 +10,9 @@ import { copyFileSync, existsSync, renameSync } from 'node:fs';
|
|
|
10
10
|
import { DatabaseSync } from 'node:sqlite';
|
|
11
11
|
import { DEFAULT_EMOTION } from './types.js';
|
|
12
12
|
import { FACT_STATUS, MEMORY_STATUS } from './config/status.js';
|
|
13
|
-
import {
|
|
13
|
+
import { FACT_ROLES } from './config/domains.js';
|
|
14
14
|
import { bm25, buildCorpusStats, tokenize } from './scoring.js';
|
|
15
|
-
export const SCHEMA_VERSION =
|
|
15
|
+
export const SCHEMA_VERSION = 10;
|
|
16
16
|
/** How many recent pairs are considered before scoring. Bounded so recall stays O(candidates). */
|
|
17
17
|
const RECALL_CANDIDATES = 500;
|
|
18
18
|
export const AFFECT_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
@@ -175,7 +175,7 @@ CREATE TABLE IF NOT EXISTS facts (
|
|
|
175
175
|
at_end INTEGER,
|
|
176
176
|
at_precision TEXT NOT NULL DEFAULT 'unknown',
|
|
177
177
|
place_id TEXT,
|
|
178
|
-
status TEXT NOT NULL DEFAULT '
|
|
178
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
179
179
|
confidence REAL NOT NULL DEFAULT 1,
|
|
180
180
|
source TEXT NOT NULL DEFAULT 'experienced',
|
|
181
181
|
source_turn_id TEXT,
|
|
@@ -299,6 +299,12 @@ function upgradeToV3(db) {
|
|
|
299
299
|
* old table stays as a read-only legacy copy.
|
|
300
300
|
*/
|
|
301
301
|
/** v6 → v7: turn threads (a topic's turns need not be adjacent) plus turns.thread_id. */
|
|
302
|
+
export function upgradeToV10(db) {
|
|
303
|
+
// The four-state vocabulary (config/status.ts). Everything written under the old names
|
|
304
|
+
// becomes one of the two states the code now reads; after this the fallbacks are gone.
|
|
305
|
+
db.exec("UPDATE facts SET status = 'active' WHERE status IN ('none','open','kept')");
|
|
306
|
+
db.exec("UPDATE facts SET status = 'retracted' WHERE status IN ('broken','superseded')");
|
|
307
|
+
}
|
|
302
308
|
export function upgradeToV9(db) {
|
|
303
309
|
// Time became locatability (ADR 0032): a fact may be known to exist without a
|
|
304
310
|
// date, and that is a value — unknown — not a reason to discard it.
|
|
@@ -442,8 +448,8 @@ export function openSqlite(file) {
|
|
|
442
448
|
upgradeToV7(db);
|
|
443
449
|
if (version < 8)
|
|
444
450
|
upgradeToV8(db);
|
|
445
|
-
if (version <
|
|
446
|
-
|
|
451
|
+
if (version < 10)
|
|
452
|
+
upgradeToV10(db);
|
|
447
453
|
// v6 adds tables only; SCHEMA creates them on every open, nothing to copy.
|
|
448
454
|
setVersion(db, SCHEMA_VERSION);
|
|
449
455
|
db.exec('COMMIT');
|
|
@@ -763,10 +769,13 @@ export function openSqlite(file) {
|
|
|
763
769
|
putFact(spec) {
|
|
764
770
|
const id = spec.id ?? `fc_${Date.now().toString(36)}_${(factSeq += 1).toString(36)}`;
|
|
765
771
|
const createdAt = Date.now();
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
//
|
|
769
|
-
|
|
772
|
+
// Only the caller decides the role. The writer's neutral default is observation — it
|
|
773
|
+
// is the minimal claim — and it never guesses from `type_name`: that guess was the debt
|
|
774
|
+
// the vocabulary rebuild removed. An unknown value is a caller bug, not a guess.
|
|
775
|
+
if (spec.role !== undefined && !FACT_ROLES.includes(spec.role)) {
|
|
776
|
+
throw new Error(`invalid fact role: ${spec.role}`);
|
|
777
|
+
}
|
|
778
|
+
const role = spec.role ?? 'observation';
|
|
770
779
|
withRetry(() => {
|
|
771
780
|
const inserted = db
|
|
772
781
|
.prepare(`INSERT OR IGNORE INTO facts
|
|
@@ -827,7 +836,7 @@ export function openSqlite(file) {
|
|
|
827
836
|
},
|
|
828
837
|
supersedeFact(oldId, newId) {
|
|
829
838
|
withRetry(() => db
|
|
830
|
-
.prepare(`UPDATE facts SET status = '${FACT_STATUS.
|
|
839
|
+
.prepare(`UPDATE facts SET status = '${FACT_STATUS.retracted}', superseded_by = ? WHERE id = ?`)
|
|
831
840
|
.run(newId, oldId));
|
|
832
841
|
},
|
|
833
842
|
/* ---------------- gaps ---------------- */
|
|
@@ -1033,10 +1042,15 @@ export function openSqlite(file) {
|
|
|
1033
1042
|
},
|
|
1034
1043
|
/** Retag a fact, e.g. a lesson that turned out to be a rule (ADR 0033). */
|
|
1035
1044
|
setFactTypeName(factId, typeName) {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1045
|
+
withRetry(() => db.prepare('UPDATE facts SET type_name = ? WHERE id = ?').run(typeName, factId));
|
|
1046
|
+
},
|
|
1047
|
+
/** What the fact is for; the caller decides (config/domains.ts FACT_ROLES). */
|
|
1048
|
+
setFactRole(factId, role) {
|
|
1049
|
+
withRetry(() => db.prepare('UPDATE facts SET role = ? WHERE id = ?').run(role, factId));
|
|
1050
|
+
},
|
|
1051
|
+
/** Lifecycle state; code writes it, never the model (config/status.ts). */
|
|
1052
|
+
setFactStatus(factId, status) {
|
|
1053
|
+
withRetry(() => db.prepare('UPDATE facts SET status = ? WHERE id = ?').run(status, factId));
|
|
1040
1054
|
},
|
|
1041
1055
|
setFactConfidence(factId, confidence) {
|
|
1042
1056
|
withRetry(() => db.prepare('UPDATE facts SET confidence = ? WHERE id = ?').run(confidence, factId));
|
|
@@ -1083,7 +1097,7 @@ export function openSqlite(file) {
|
|
|
1083
1097
|
.prepare(`SELECT DISTINCT f.* FROM facts f
|
|
1084
1098
|
JOIN fact_slots s ON s.fact_id = f.id AND s.value_type = 'entity'
|
|
1085
1099
|
JOIN entities e ON e.id = s.entity_id
|
|
1086
|
-
WHERE f.soul_id = ? AND f.user_id = ? AND f.status
|
|
1100
|
+
WHERE f.soul_id = ? AND f.user_id = ? AND f.status = '${FACT_STATUS.active}'
|
|
1087
1101
|
AND (e.name = ? OR e.aliases LIKE ?)
|
|
1088
1102
|
AND (? IS NULL OR f.statement LIKE ? OR COALESCE(f.type_name, '') LIKE ?)
|
|
1089
1103
|
ORDER BY COALESCE(f.at, f.created_at) DESC
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The only port. Every added method is a storage engine's opinion leaking into
|
|
3
3
|
* the domain, so the surface stays deliberately small.
|
|
4
4
|
*/
|
|
5
|
-
import type { AffectSignal, Correction, Emotion, Entity, EntityQuery, EntitySpec, Fact, FactQuery, FactSpec, Gap, GapSpec, Identity, Memory, QuerySpec, SessionSummary, SlotValue, TurnPair, TurnPairSpec, TurnFindResult, TurnFindSpec, TurnSpec, TurnStep, TurnThread, ThreadSpec } from './types.ts';
|
|
5
|
+
import type { AffectSignal, Correction, Emotion, Entity, EntityQuery, EntitySpec, Fact, FactQuery, FactRole, FactSpec, FactStatus, Gap, GapSpec, Identity, Memory, QuerySpec, SessionSummary, SlotValue, TurnPair, TurnPairSpec, TurnFindResult, TurnFindSpec, TurnSpec, TurnStep, TurnThread, ThreadSpec } from './types.ts';
|
|
6
6
|
export interface SoulStore {
|
|
7
7
|
ensureSoul(identity: Identity): void;
|
|
8
8
|
identity(soulId: string): Identity | null;
|
|
@@ -66,6 +66,10 @@ export interface SoulStore {
|
|
|
66
66
|
value: SlotValue;
|
|
67
67
|
}[]): number;
|
|
68
68
|
setFactTypeName(factId: string, typeName: string): void;
|
|
69
|
+
/** What the fact is for (config/domains.ts FACT_ROLES); the caller decides. */
|
|
70
|
+
setFactRole(factId: string, role: FactRole): void;
|
|
71
|
+
/** Lifecycle state (config/status.ts); code writes it, never the model. */
|
|
72
|
+
setFactStatus(factId: string, status: FactStatus): void;
|
|
69
73
|
setFactConfidence(factId: string, confidence: number): void;
|
|
70
74
|
/** Count that retrieval surfaced these facts (0019: use slows decay). */
|
|
71
75
|
recordRecalls(factIds: string[], at: number): void;
|
|
@@ -218,6 +218,8 @@ export type DraftSlotValue = {
|
|
|
218
218
|
};
|
|
219
219
|
export interface FactDraft {
|
|
220
220
|
statement: string;
|
|
221
|
+
/** Structured role, chosen by the model (config/domains.ts FACT_ROLES). */
|
|
222
|
+
role?: FactRole;
|
|
221
223
|
/** world or a person's name — where the fact holds (ADR 0032). */
|
|
222
224
|
holder?: string;
|
|
223
225
|
typeName?: string;
|