smart_brain 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/MEMPAL_GUIDE.md +1074 -0
  4. data/README.en.md +173 -173
  5. data/README.md +467 -173
  6. data/config/brain.yml +69 -1
  7. data/conversation_demo.rb +438 -438
  8. data/db/migrate/002_turn_events_payload.sql +9 -0
  9. data/db/migrate/003_tiers_and_lifecycle.sql +28 -0
  10. data/db/migrate/004_kg_edges.sql +30 -0
  11. data/db/migrate/005_domains_and_memory_scopes.sql +163 -0
  12. data/docs/coding_todo.md +139 -0
  13. data/docs/context_package.md +220 -0
  14. data/docs/evidence_pack.md +190 -0
  15. data/docs/gap_vs_mempal.md +161 -0
  16. data/docs/mcp.md +93 -0
  17. data/docs/memory_types.md +278 -0
  18. data/docs/multi_scope_memory_refactor_plan.md +483 -0
  19. data/docs/multi_scope_migration.md +65 -0
  20. data/docs/policies.md +308 -0
  21. data/docs/retrieval_plan.md +231 -0
  22. data/docs/smartbrain_design.md +299 -0
  23. data/docs/user_guide.md +546 -0
  24. data/example.rb +91 -91
  25. data/examples/01_memory_basic.rb +57 -0
  26. data/examples/02_governance.rb +63 -0
  27. data/examples/03_postgres_persistence.rb +63 -0
  28. data/examples/04_ollama_llm.rb +69 -0
  29. data/examples/05_smart_rag_integration.rb +79 -0
  30. data/examples/06_multi_scope_memory.rb +50 -0
  31. data/examples/README.md +49 -0
  32. data/exe/smart_brain +168 -0
  33. data/lib/smart_brain/adapters/smart_rag/direct_client.rb +16 -5
  34. data/lib/smart_brain/adapters/smart_rag/http_client.rb +16 -5
  35. data/lib/smart_brain/adapters/smart_rag/null_client.rb +7 -2
  36. data/lib/smart_brain/adapters/smart_rag/scope_filter.rb +60 -0
  37. data/lib/smart_brain/configuration.rb +57 -0
  38. data/lib/smart_brain/consolidator/working_summary.rb +80 -12
  39. data/lib/smart_brain/context_composer/composer.rb +40 -3
  40. data/lib/smart_brain/contracts/retrieval_plan.rb +10 -0
  41. data/lib/smart_brain/contracts/scope_context.rb +46 -0
  42. data/lib/smart_brain/contracts/scope_ref.rb +25 -0
  43. data/lib/smart_brain/db.rb +109 -0
  44. data/lib/smart_brain/event_store/in_memory.rb +6 -2
  45. data/lib/smart_brain/event_store/postgres.rb +199 -0
  46. data/lib/smart_brain/fusion/merger.rb +31 -2
  47. data/lib/smart_brain/governance/briefing.rb +146 -0
  48. data/lib/smart_brain/governance/fact_check.rb +110 -0
  49. data/lib/smart_brain/governance/knowledge_graph.rb +60 -0
  50. data/lib/smart_brain/governance/lifecycle.rb +225 -0
  51. data/lib/smart_brain/governance/tiers.rb +60 -0
  52. data/lib/smart_brain/memory_extractor/extractor.rb +25 -7
  53. data/lib/smart_brain/memory_store/in_memory.rb +202 -17
  54. data/lib/smart_brain/memory_store/postgres.rb +500 -0
  55. data/lib/smart_brain/model_provider/base.rb +87 -0
  56. data/lib/smart_brain/model_provider/factory.rb +49 -0
  57. data/lib/smart_brain/model_provider/ollama.rb +60 -0
  58. data/lib/smart_brain/model_provider/openai.rb +60 -0
  59. data/lib/smart_brain/model_provider/stub.rb +26 -0
  60. data/lib/smart_brain/model_provider.rb +7 -0
  61. data/lib/smart_brain/observability/tracker.rb +39 -1
  62. data/lib/smart_brain/retrievers/exact_retriever.rb +6 -0
  63. data/lib/smart_brain/retrievers/memory_retriever.rb +59 -5
  64. data/lib/smart_brain/runtime.rb +288 -16
  65. data/lib/smart_brain/scopes/conflict_resolver.rb +67 -0
  66. data/lib/smart_brain/scopes/registry.rb +133 -0
  67. data/lib/smart_brain/scopes/resolver.rb +32 -0
  68. data/lib/smart_brain/server/http_app.rb +143 -0
  69. data/lib/smart_brain/server/mcp_server.rb +385 -0
  70. data/lib/smart_brain/server/service.rb +129 -0
  71. data/lib/smart_brain/support/levenshtein.rb +35 -0
  72. data/lib/smart_brain/version.rb +5 -5
  73. data/lib/smart_brain.rb +80 -35
  74. metadata +88 -36
@@ -0,0 +1,9 @@
1
+ -- SmartBrain v0.1.1: persist the full per-turn events payload (JSONB).
2
+ --
3
+ -- Why: the runtime computes entity-gate frequencies from the *candidate*
4
+ -- entities a caller passes in each turn_events (before any gating decision).
5
+ -- The normalized tables (messages / refs / entity_mentions) don't capture
6
+ -- that raw input, so we store the normalized turn_events blob here to let
7
+ -- EventStore::Postgres faithfully reproduce EventStore::InMemory semantics.
8
+
9
+ ALTER TABLE turns ADD COLUMN IF NOT EXISTS events_json JSONB NOT NULL DEFAULT '{}'::jsonb;
@@ -0,0 +1,28 @@
1
+ -- SmartBrain v0.1.3: mind-model tiers + knowledge lifecycle (Stage-1).
2
+ --
3
+ -- Tiers (mempal mind model): dao_tian / dao_ren / shu / qi / evidence.
4
+ -- Lifecycle: raw -> candidate -> promoted / demoted (gated by knowledge_events).
5
+ -- These are orthogonal to the conflict `status` (active/superseded/retracted):
6
+ -- an item has both — status resolves conflicts, lifecycle_status governs knowledge.
7
+
8
+ ALTER TABLE memory_items ADD COLUMN IF NOT EXISTS tier TEXT NOT NULL DEFAULT 'evidence';
9
+ ALTER TABLE memory_items ADD COLUMN IF NOT EXISTS lifecycle_status TEXT NOT NULL DEFAULT 'raw';
10
+
11
+ CREATE INDEX IF NOT EXISTS idx_memory_items_tier ON memory_items(tier);
12
+ CREATE INDEX IF NOT EXISTS idx_memory_items_lifecycle ON memory_items(lifecycle_status);
13
+
14
+ -- Append-only audit trail for the distill/gate/promote/demote lifecycle.
15
+ CREATE TABLE IF NOT EXISTS knowledge_events (
16
+ id TEXT PRIMARY KEY,
17
+ memory_item_id TEXT NOT NULL,
18
+ event_type TEXT NOT NULL, -- distill | promote | demote | gate
19
+ from_lifecycle TEXT,
20
+ to_lifecycle TEXT,
21
+ reason TEXT,
22
+ reason_type TEXT, -- contradicted | obsolete | superseded (demote)
23
+ reviewer TEXT,
24
+ evidence_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
25
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
26
+ );
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_knowledge_events_item ON knowledge_events(memory_item_id);
@@ -0,0 +1,30 @@
1
+ -- SmartBrain v0.1.4: knowledge graph (triples with temporal validity).
2
+ --
3
+ -- KG edges are orthogonal to memory_items: they record relations between
4
+ -- entities (subject/predicate/object) with valid_from/valid_to temporal
5
+ -- validity and an active|invalidated status. They do NOT participate in the
6
+ -- Stage-1 lifecycle (candidate/promoted/demoted) or the conflict `status` of
7
+ -- memory_items. Invalidation sets valid_to=now + status='invalidated'.
8
+
9
+ CREATE TABLE IF NOT EXISTS kg_edges (
10
+ id TEXT PRIMARY KEY,
11
+ session_id TEXT NOT NULL,
12
+ subject TEXT NOT NULL,
13
+ predicate TEXT NOT NULL,
14
+ object TEXT NOT NULL,
15
+ subject_entity_id TEXT,
16
+ object_entity_id TEXT,
17
+ valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
18
+ valid_to TIMESTAMPTZ,
19
+ source_turn_id TEXT,
20
+ source_memory_item_id TEXT,
21
+ confidence NUMERIC(3,2) NOT NULL DEFAULT 0.6,
22
+ status TEXT NOT NULL DEFAULT 'active',
23
+ meta_json JSONB NOT NULL DEFAULT '{}'::jsonb,
24
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_session ON kg_edges(session_id);
28
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_subject ON kg_edges(subject);
29
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_object ON kg_edges(object);
30
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_status ON kg_edges(status);
@@ -0,0 +1,163 @@
1
+ -- SmartBrain v0.2: domain isolation and multi-scope long-term memory.
2
+
3
+ CREATE TABLE IF NOT EXISTS domains (
4
+ id TEXT PRIMARY KEY,
5
+ metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
6
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
7
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
8
+ );
9
+
10
+ INSERT INTO domains (id) VALUES ('legacy') ON CONFLICT (id) DO NOTHING;
11
+
12
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS domain_id TEXT;
13
+
14
+ -- Older memory_items/kg_edges did not enforce a sessions FK. Preserve them by
15
+ -- materializing any missing legacy sessions before creating scope links.
16
+ INSERT INTO sessions (id, domain_id, metadata_json)
17
+ SELECT source.session_id, 'legacy', '{}'::jsonb
18
+ FROM (
19
+ SELECT DISTINCT session_id FROM memory_items
20
+ UNION
21
+ SELECT DISTINCT session_id FROM kg_edges
22
+ ) source
23
+ WHERE source.session_id IS NOT NULL
24
+ ON CONFLICT (id) DO NOTHING;
25
+
26
+ UPDATE sessions SET domain_id = 'legacy' WHERE domain_id IS NULL;
27
+ ALTER TABLE sessions ALTER COLUMN domain_id SET NOT NULL;
28
+
29
+ DO $$ BEGIN
30
+ ALTER TABLE sessions ADD CONSTRAINT fk_sessions_domain
31
+ FOREIGN KEY (domain_id) REFERENCES domains(id);
32
+ EXCEPTION WHEN duplicate_object THEN NULL;
33
+ END $$;
34
+
35
+ CREATE TABLE IF NOT EXISTS memory_scopes (
36
+ id TEXT PRIMARY KEY,
37
+ domain_id TEXT NOT NULL REFERENCES domains(id),
38
+ scope_type TEXT NOT NULL,
39
+ external_id TEXT NOT NULL,
40
+ parent_scope_id TEXT REFERENCES memory_scopes(id),
41
+ metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
42
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
43
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
44
+ UNIQUE(domain_id, scope_type, external_id)
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS session_scope_links (
48
+ session_id TEXT NOT NULL REFERENCES sessions(id),
49
+ scope_id TEXT NOT NULL REFERENCES memory_scopes(id),
50
+ access_mode TEXT NOT NULL,
51
+ priority INTEGER NOT NULL,
52
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
53
+ PRIMARY KEY(session_id, scope_id)
54
+ );
55
+
56
+ INSERT INTO memory_scopes (id, domain_id, scope_type, external_id)
57
+ SELECT 'legacy:session:' || id, 'legacy', 'session', id
58
+ FROM sessions
59
+ WHERE domain_id = 'legacy'
60
+ ON CONFLICT (domain_id, scope_type, external_id) DO NOTHING;
61
+
62
+ INSERT INTO session_scope_links (session_id, scope_id, access_mode, priority)
63
+ SELECT s.id, ms.id, 'read_write', 0
64
+ FROM sessions s
65
+ JOIN memory_scopes ms
66
+ ON ms.domain_id = s.domain_id AND ms.scope_type = 'session' AND ms.external_id = s.id
67
+ ON CONFLICT (session_id, scope_id) DO NOTHING;
68
+
69
+ ALTER TABLE memory_items ADD COLUMN IF NOT EXISTS scope_id TEXT;
70
+ ALTER TABLE memory_items ADD COLUMN IF NOT EXISTS source_session_id TEXT;
71
+ UPDATE memory_items m
72
+ SET scope_id = ms.id, source_session_id = COALESCE(m.source_session_id, m.session_id)
73
+ FROM memory_scopes ms
74
+ WHERE m.scope_id IS NULL
75
+ AND ms.domain_id = 'legacy' AND ms.scope_type = 'session' AND ms.external_id = m.session_id;
76
+ ALTER TABLE memory_items ALTER COLUMN scope_id SET NOT NULL;
77
+ ALTER TABLE memory_items ALTER COLUMN source_session_id SET NOT NULL;
78
+
79
+ ALTER TABLE entities ADD COLUMN IF NOT EXISTS scope_id TEXT;
80
+ ALTER TABLE entities ADD COLUMN IF NOT EXISTS source_session_id TEXT;
81
+
82
+ INSERT INTO sessions (id, domain_id, metadata_json)
83
+ VALUES ('legacy:entities', 'legacy', '{"synthetic":true}'::jsonb)
84
+ ON CONFLICT (id) DO NOTHING;
85
+ INSERT INTO memory_scopes (id, domain_id, scope_type, external_id, metadata_json)
86
+ VALUES ('legacy:session:legacy:entities', 'legacy', 'session', 'legacy:entities', '{"synthetic":true}'::jsonb)
87
+ ON CONFLICT (domain_id, scope_type, external_id) DO NOTHING;
88
+ INSERT INTO session_scope_links (session_id, scope_id, access_mode, priority)
89
+ VALUES ('legacy:entities', 'legacy:session:legacy:entities', 'read_write', 0)
90
+ ON CONFLICT (session_id, scope_id) DO NOTHING;
91
+
92
+ WITH entity_sources AS (
93
+ SELECT em.entity_id, MIN(t.session_id) AS session_id
94
+ FROM entity_mentions em
95
+ JOIN turns t ON t.id = em.turn_id
96
+ GROUP BY em.entity_id
97
+ )
98
+ UPDATE entities e
99
+ SET source_session_id = sources.session_id, scope_id = ms.id
100
+ FROM entity_sources sources
101
+ JOIN memory_scopes ms
102
+ ON ms.domain_id = 'legacy' AND ms.scope_type = 'session' AND ms.external_id = sources.session_id
103
+ WHERE e.id = sources.entity_id AND (e.scope_id IS NULL OR e.source_session_id IS NULL);
104
+
105
+ UPDATE entities
106
+ SET source_session_id = COALESCE(source_session_id, 'legacy:entities'),
107
+ scope_id = COALESCE(scope_id, 'legacy:session:legacy:entities');
108
+ ALTER TABLE entities ALTER COLUMN scope_id SET NOT NULL;
109
+ ALTER TABLE entities ALTER COLUMN source_session_id SET NOT NULL;
110
+
111
+ ALTER TABLE kg_edges ADD COLUMN IF NOT EXISTS scope_id TEXT;
112
+ ALTER TABLE kg_edges ADD COLUMN IF NOT EXISTS source_session_id TEXT;
113
+ UPDATE kg_edges e
114
+ SET scope_id = ms.id, source_session_id = COALESCE(e.source_session_id, e.session_id)
115
+ FROM memory_scopes ms
116
+ WHERE e.scope_id IS NULL
117
+ AND ms.domain_id = 'legacy' AND ms.scope_type = 'session' AND ms.external_id = e.session_id;
118
+ ALTER TABLE kg_edges ALTER COLUMN scope_id SET NOT NULL;
119
+ ALTER TABLE kg_edges ALTER COLUMN source_session_id SET NOT NULL;
120
+
121
+ DO $$ BEGIN
122
+ ALTER TABLE memory_items ADD CONSTRAINT fk_memory_items_scope
123
+ FOREIGN KEY (scope_id) REFERENCES memory_scopes(id);
124
+ EXCEPTION WHEN duplicate_object THEN NULL;
125
+ END $$;
126
+ DO $$ BEGIN
127
+ ALTER TABLE kg_edges ADD CONSTRAINT fk_kg_edges_scope
128
+ FOREIGN KEY (scope_id) REFERENCES memory_scopes(id);
129
+ EXCEPTION WHEN duplicate_object THEN NULL;
130
+ END $$;
131
+ DO $$ BEGIN
132
+ ALTER TABLE entities ADD CONSTRAINT fk_entities_scope
133
+ FOREIGN KEY (scope_id) REFERENCES memory_scopes(id);
134
+ EXCEPTION WHEN duplicate_object THEN NULL;
135
+ END $$;
136
+
137
+ CREATE INDEX IF NOT EXISTS idx_sessions_domain ON sessions(domain_id);
138
+ CREATE INDEX IF NOT EXISTS idx_memory_items_scope_status_lifecycle ON memory_items(scope_id, status, lifecycle_status);
139
+ CREATE INDEX IF NOT EXISTS idx_memory_items_scope_type_key ON memory_items(scope_id, type, key);
140
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_scope_status ON kg_edges(scope_id, status);
141
+ CREATE INDEX IF NOT EXISTS idx_entities_scope_canonical_kind ON entities(scope_id, canonical_id, kind);
142
+
143
+ DO $$
144
+ BEGIN
145
+ IF EXISTS (
146
+ SELECT 1 FROM memory_items m
147
+ JOIN memory_scopes ms ON ms.id = m.scope_id
148
+ JOIN sessions s ON s.id = m.source_session_id
149
+ WHERE ms.domain_id <> s.domain_id
150
+ ) OR EXISTS (
151
+ SELECT 1 FROM kg_edges e
152
+ JOIN memory_scopes ms ON ms.id = e.scope_id
153
+ JOIN sessions s ON s.id = e.source_session_id
154
+ WHERE ms.domain_id <> s.domain_id
155
+ ) OR EXISTS (
156
+ SELECT 1 FROM entities e
157
+ JOIN memory_scopes ms ON ms.id = e.scope_id
158
+ JOIN sessions s ON s.id = e.source_session_id
159
+ WHERE ms.domain_id <> s.domain_id
160
+ ) THEN
161
+ RAISE EXCEPTION 'multi-scope migration consistency check failed';
162
+ END IF;
163
+ END $$;
@@ -0,0 +1,139 @@
1
+ # SmartBrain Ruby 开发待办(v0.1)
2
+
3
+ ## 0. 目标与范围
4
+ - 目标:实现 `commit_turn` 与 `compose_context` 主链路,满足 `docs/policies.md` 的最低验收标准。
5
+ - 边界:SmartBrain 管理对话记忆与上下文装配;资源检索由 SmartRAG 提供。
6
+ - 默认部署:PostgreSQL(SQLite 仅开发模式)。
7
+
8
+ ## 1. 技术基线(Ruby)
9
+ - [x] 确定 Ruby 版本:`3.2+`
10
+ - [x] 选型并固定 ORM:`sequel + pg`(或 ActiveRecord,二选一)
11
+ - [x] 测试框架:`rspec`
12
+ - [x] 契约与校验:`dry-struct` / `dry-validation`
13
+ - [x] HTTP 客户端:`faraday`(SmartRAG Adapter)
14
+ - [x] JSON:`oj`
15
+ - [x] 新增配置文件:`config/brain.yml`(映射 `docs/policies.md` 默认策略)
16
+
17
+ ## 2. 目录与模块骨架
18
+ - [x] 建立目录:
19
+ - `lib/smart_brain/event_store/`
20
+ - `lib/smart_brain/memory_extractor/`
21
+ - `lib/smart_brain/consolidator/`
22
+ - `lib/smart_brain/retrieval_planner/`
23
+ - `lib/smart_brain/retrievers/`
24
+ - `lib/smart_brain/adapters/smart_rag/`
25
+ - `lib/smart_brain/fusion/`
26
+ - `lib/smart_brain/context_composer/`
27
+ - `lib/smart_brain/model_provider/`
28
+ - [x] 建立测试目录:
29
+ - `spec/commit_turn_spec.rb`
30
+ - `spec/compose_context_spec.rb`
31
+ - `spec/integration_smart_rag_adapter_spec.rb`
32
+
33
+ ## 3. 数据库与迁移(Postgres)
34
+ - [x] 创建表:`sessions`
35
+ - [x] 创建表:`turns`
36
+ - [x] 创建表:`messages`
37
+ - [x] 创建表:`tool_calls`
38
+ - [x] 创建表:`refs`
39
+ - [x] 创建表:`memory_items`
40
+ - [x] 创建表:`memory_chunks`
41
+ - [x] 创建表:`entities`
42
+ - [x] 创建表:`entity_mentions`
43
+ - [x] 创建表:`summaries`(存 working_summary 与元信息)
44
+ - [x] 索引:`session_id/turn_id/updated_at/type+key/status`
45
+ - [x] FTS 索引:`messages`、`memory_chunks`
46
+
47
+ ## 4. 里程碑 M1(第 1 周):主链路打通
48
+ ### 4.1 commit_turn
49
+ - [x] 实现 `SmartBrain.commit_turn(session_id:, turn_events:)`
50
+ - [x] EventStore 全量写入:turn/messages/tool_calls/refs
51
+ - [x] Retention Gate v0:
52
+ - 必写:`tasks`、`decisions`、refs(事件层)
53
+ - 条件写:`preferences`、`goals`、`events`
54
+ - 不写长期记忆:闲聊、未确认推测
55
+ - [x] 冲突处理 v0:
56
+ - 覆盖:`preferences/tasks` -> 旧值 `superseded`
57
+ - 撤回:`retracted`
58
+
59
+ ### 4.2 compose_context
60
+ - [x] 实现 `SmartBrain.compose_context(session_id:, user_message:, agent_state: {})`
61
+ - [x] 固定槽位装配 `ContextPackage`
62
+ - [x] 实现 `RetrievalPlanner` v0(exact/hybrid + 是否调用 SmartRAG)
63
+ - [x] 实现 SmartRAG Adapter:`retrieve(plan)` -> `EvidencePack`
64
+
65
+ ### 4.3 M1 验收
66
+ - [x] `spec/commit_turn_spec.rb` 通过
67
+ - [x] `spec/compose_context_spec.rb` 通过
68
+ - [x] `context_id/request_id/plan_id` 全链路可追踪
69
+
70
+ ## 5. 里程碑 M2(第 2 周):策略落地与检索增强
71
+ ### 5.1 policies 参数化
72
+ - [x] `retention.entity_gate.window_turns=20`
73
+ - [x] `retention.entity_gate.freq_threshold=2`
74
+ - [x] `confidence.user_asserted/tool_derived/inferred`
75
+ - [x] `retrieval.top_k=30`、`candidate_k=200`
76
+ - [x] `retrieval.query_expansion.enabled/max_queries`
77
+ - [x] `composition.token_limit/system_blocks_max_tokens/summary_max_tokens`
78
+ - [x] `composition.recent_turns_max/evidence_max_items/max_snippet_chars`
79
+ - [x] `composition.diversity.by_document/by_source_uri/memory_resource_ratio`
80
+
81
+ ### 5.2 检索与融合
82
+ - [x] 对话侧 `ExactRetriever`(FTS)
83
+ - [x] 对话侧 `RelationalRetriever`(entities + mentions)
84
+ - [x] 多源去重:
85
+ - resource: `document_id + section_id (+chunk_index)`
86
+ - memory: `memory_item_id` 或 `turn_id + message_id`
87
+ - [x] 跨源统一重排接口(可先规则分,后接 reranker)
88
+
89
+ ### 5.3 M2 验收
90
+ - [x] 资源检索触发条件符合 `docs/policies.md`
91
+ - [x] evidence 条数限制与截断生效
92
+ - [x] 多样性约束生效(同文档/同 source 限制)
93
+ - [x] `ignored_fields` 可记录后端不支持字段
94
+
95
+ ## 6. 里程碑 M3(第 3 周):摘要巩固与可观测
96
+ ### 6.1 Consolidation
97
+ - [x] working_summary 触发条件:
98
+ - `summarize_after_turns`
99
+ - token 压力
100
+ - 阶段事件(task 完成/decision 形成)
101
+ - [x] 摘要模板固定:
102
+ - Goals
103
+ - Decisions
104
+ - Tasks
105
+ - Key References
106
+ - Open Questions
107
+ - [x] 保存摘要元信息:`summary_version/source_turn_range/generated_at`
108
+
109
+ ### 6.2 Observability
110
+ - [x] `compose_context` 日志:
111
+ - plan
112
+ - evidence 入选与分数
113
+ - token 预算与 trimmed 原因
114
+ - ignored_fields
115
+ - [x] `commit_turn` 日志:
116
+ - 写入 memory_items(type/key/confidence/status)
117
+ - 冲突处理结果
118
+ - 摘要触发情况
119
+
120
+ ### 6.3 M3 验收
121
+ - [x] 满足 `docs/policies.md` 第 7 节最低验收标准
122
+ - [x] 单个 session 可回放并解释“为何写入/为何选中证据”
123
+
124
+ ## 7. 测试与回归清单
125
+ - [x] 单元测试:gate/conflict/planner/budget/diversity/dedupe
126
+ - [x] 集成测试:SmartRAG adapter(成功、超时、字段降级)
127
+ - [x] 回归测试:固定会话数据集,比较 ContextPackage 关键字段稳定性
128
+ - [x] 性能指标:
129
+ - `compose_context` P95
130
+ - evidence 命中来源比例(memory/resource)
131
+ - token 超预算率
132
+
133
+ ## 8. 执行顺序(严格)
134
+ 1. [x] 先做数据库迁移 + 契约对象(RetrievalPlan / EvidencePack / ContextPackage)
135
+ 2. [x] 打通 `commit_turn` 最小闭环
136
+ 3. [x] 打通 `compose_context` 最小闭环(含 SmartRAG adapter)
137
+ 4. [x] 落地 `policies.md` 参数化
138
+ 5. [x] 补齐 observability + 回归测试
139
+ 6. [x] 再做检索增强与性能优化
@@ -0,0 +1,220 @@
1
+ ## 1. 目的
2
+
3
+ ContextPackage 用于把“本轮给模型的上下文”表达为结构化对象,解决:
4
+ - 直接拼接全部 history 不可扩展(token 预算)
5
+ - 证据与对话混在一起不可控(难 debug)
6
+ - 工具结果与长期记忆无法一致治理(无统一入口)
7
+
8
+ ContextPackage 是 **SmartBrain 的输出**,SmartBrain 对其内容负责;SmartPrompt 只负责把它转换为模型调用格式并发送。
9
+
10
+ ---
11
+
12
+ ## 2. 顶层结构
13
+
14
+ ```json
15
+ {
16
+ "version": "0.1",
17
+ "context_id": "uuid",
18
+ "session_id": "string",
19
+ "created_at": "2026-02-20T12:00:00Z",
20
+
21
+ "system_blocks": [],
22
+ "developer_blocks": [],
23
+ "working_summary": "",
24
+ "recent_turns": [],
25
+ "evidence": [],
26
+ "user_message": { "role": "user", "content": "..." },
27
+
28
+ "constraints": {},
29
+ "debug": {}
30
+ }
31
+ ```
32
+
33
+ 字段说明:
34
+
35
+ * `version`:协议版本(必填)
36
+ * `context_id`:本次装配唯一 id(建议必填)
37
+ * `session_id`:会话 id(必填)
38
+ * `created_at`:生成时间(必填)
39
+ * `system_blocks`:可钉在 system 的核心记忆与规则
40
+ * `developer_blocks`:开发者侧指令(工具使用规则、引用规则等)
41
+ * `working_summary`:滚动摘要(可选)
42
+ * `recent_turns`:最近窗口对话(可选)
43
+ * `evidence`:检索证据(可选但关键)
44
+ * `user_message`:本轮用户输入(必填)
45
+ * `constraints`:token 预算/多样性等约束的“结果”或“输入”(可选)
46
+ * `debug`:解释信息(可选)
47
+
48
+ ---
49
+
50
+ ## 3. system_blocks
51
+
52
+ 用于承载稳定信息,通常来自 SmartBrain 的 MemoryExtractor/Consolidator。
53
+
54
+ ```json
55
+ {
56
+ "type": "core_profile|preferences|goals|policies|other",
57
+ "text": "string",
58
+ "updated_at": "2026-02-20T00:00:00Z",
59
+ "source": { "turn_id": "...", "memory_item_id": "..." }
60
+ }
61
+ ```
62
+
63
+ 建议约定:
64
+
65
+ * `core_profile`:用户身份/背景(稳定)
66
+ * `preferences`:偏好与约束(稳定但会更新)
67
+ * `goals`:长期目标/项目目标
68
+ * `policies`:系统规则(如引用要求、工具调用规范)
69
+
70
+ ---
71
+
72
+ ## 4. developer_blocks(可选)
73
+
74
+ 用于放置“执行层规则”,例如:
75
+
76
+ * 必须引用证据回答
77
+ * 工具调用失败时如何降级
78
+ * 输出格式要求
79
+
80
+ 结构同 system_blocks,type 可为:`tooling_rules|format_rules|safety_rules|other`
81
+
82
+ ---
83
+
84
+ ## 5. working_summary(可选)
85
+
86
+ * 目的:压缩较早对话
87
+ * 内容:一段文字(可带 bullet)
88
+ * 由 SmartBrain 维护,SmartPrompt 不应擅自修改
89
+
90
+ ---
91
+
92
+ ## 6. recent_turns(可选)
93
+
94
+ ```json
95
+ [
96
+ { "role": "user", "content": "..." },
97
+ { "role": "assistant", "content": "..." }
98
+ ]
99
+ ```
100
+
101
+ 约束建议:
102
+
103
+ * 只保留最近 N 轮(由 brain.yml 的 retention.window_turns 控制)
104
+ * 不应包含过长的 tool result(tool result 应进入 evidence 或另一个结构)
105
+
106
+ ---
107
+
108
+ ## 7. evidence(关键)
109
+
110
+ Evidence 是 SmartBrain 从多个来源收集并筛选后的“可引用证据”,通常来自:
111
+
112
+ * SmartBrain 自己的 memory store(对话记忆、任务状态、实体事件)
113
+ * SmartRAG 的资源检索(文档/URL 知识库)
114
+
115
+ ### 7.1 EvidenceItem 结构
116
+
117
+ ```json
118
+ {
119
+ "id": "string",
120
+ "source": "memory|resource",
121
+ "source_uri": "string",
122
+ "title": "string",
123
+ "snippet": "string",
124
+ "mode": "exact|semantic|hybrid|relational|associative",
125
+ "score": 0.0,
126
+ "signals": {
127
+ "rerank_score": 0.0,
128
+ "rrf_score": 0.0,
129
+ "vector_score": 0.0,
130
+ "fts_score": 0.0
131
+ },
132
+ "provenance": {
133
+ "request_id": "uuid",
134
+ "plan_version": "0.1",
135
+ "retrieved_at": "2026-02-20T12:00:00Z"
136
+ },
137
+ "ref": {
138
+ "document_id": "...",
139
+ "section_id": "...",
140
+ "turn_id": "...",
141
+ "memory_item_id": "..."
142
+ }
143
+ }
144
+ ```
145
+
146
+ 字段说明:
147
+
148
+ * `source_uri`:必须能定位回源(URL、file://、viking://、smartbrain://turn/...)
149
+ * `snippet`:默认短文本,可用于直接拼入 prompt
150
+ * `signals`:可选,用于 debug 与可解释
151
+ * `ref`:可选,内部定位
152
+
153
+ ### 7.2 Evidence 分组(可选)
154
+
155
+ SmartBrain 可以在 evidence 上附加 `group` 字段(例如 `exact_hits` / `semantic_hits` / `relational_bundle`),但 v0.1 不强制。
156
+
157
+ ---
158
+
159
+ ## 8. user_message(必填)
160
+
161
+ ```json
162
+ { "role": "user", "content": "..." }
163
+ ```
164
+
165
+ > SmartBot/SmartPrompt 使用该字段作为最终 user message,而不是从 recent_turns 推断。
166
+
167
+ ---
168
+
169
+ ## 9. constraints(可选)
170
+
171
+ 用于携带“预算输入/装配结果”,便于观测与回归。
172
+
173
+ ```json
174
+ {
175
+ "token_budget": { "limit": 8000, "used_estimate": 6120 },
176
+ "diversity": { "by_document": 3, "by_source": 10 },
177
+ "truncation": { "snippets_max_chars": 800, "recent_turns_max": 8 }
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ## 10. debug(可选但强烈建议开发期启用)
184
+
185
+ ```json
186
+ {
187
+ "planner": { "intent": "qa", "reason": "user asked for ...", "queries": ["..."] },
188
+ "why_selected": [
189
+ "evidence#5 rerank_score=0.91, covers key entity X",
190
+ "recent_turns kept because user said 'as we discussed earlier'"
191
+ ],
192
+ "ignored": [
193
+ "filter time_range not supported by backend, skipped",
194
+ "diversity.by_source not supported, skipped"
195
+ ]
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 11. SmartPrompt 消息转换建议(非规范,但推荐)
202
+
203
+ SmartBot/SmartPrompt 将 ContextPackage 转成 messages 的顺序建议:
204
+
205
+ 1. system:拼接所有 `system_blocks.text`(可加标题分隔)
206
+ 2. developer:拼接 `developer_blocks.text`(如果你的 runtime 支持 developer role,否则并入 system)
207
+ 3. system/assistant:插入 `working_summary`(建议以“Summary:”前缀)
208
+ 4. history:插入 `recent_turns`
209
+ 5. assistant/system:插入 `evidence`(建议以“Evidence:”结构化列出)
210
+ 6. user:`user_message`
211
+
212
+ > 重要:证据与摘要应当以稳定格式注入,便于模型引用与后续评估。
213
+
214
+ ---
215
+
216
+ ## 12. 变更策略
217
+
218
+ * v0.1:新增字段只能“可选”,不得破坏现有字段含义
219
+ * 执行方必须忽略未知字段(向后兼容)
220
+ * 如需破坏性变更,另起 major 版本并提供迁移说明