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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/MEMPAL_GUIDE.md +1074 -0
- data/README.en.md +173 -173
- data/README.md +467 -173
- data/config/brain.yml +69 -1
- data/conversation_demo.rb +438 -438
- data/db/migrate/002_turn_events_payload.sql +9 -0
- data/db/migrate/003_tiers_and_lifecycle.sql +28 -0
- data/db/migrate/004_kg_edges.sql +30 -0
- data/db/migrate/005_domains_and_memory_scopes.sql +163 -0
- data/docs/coding_todo.md +139 -0
- data/docs/context_package.md +220 -0
- data/docs/evidence_pack.md +190 -0
- data/docs/gap_vs_mempal.md +161 -0
- data/docs/mcp.md +93 -0
- data/docs/memory_types.md +278 -0
- data/docs/multi_scope_memory_refactor_plan.md +483 -0
- data/docs/multi_scope_migration.md +65 -0
- data/docs/policies.md +308 -0
- data/docs/retrieval_plan.md +231 -0
- data/docs/smartbrain_design.md +299 -0
- data/docs/user_guide.md +546 -0
- data/example.rb +91 -91
- data/examples/01_memory_basic.rb +57 -0
- data/examples/02_governance.rb +63 -0
- data/examples/03_postgres_persistence.rb +63 -0
- data/examples/04_ollama_llm.rb +69 -0
- data/examples/05_smart_rag_integration.rb +79 -0
- data/examples/06_multi_scope_memory.rb +50 -0
- data/examples/README.md +49 -0
- data/exe/smart_brain +168 -0
- data/lib/smart_brain/adapters/smart_rag/direct_client.rb +16 -5
- data/lib/smart_brain/adapters/smart_rag/http_client.rb +16 -5
- data/lib/smart_brain/adapters/smart_rag/null_client.rb +7 -2
- data/lib/smart_brain/adapters/smart_rag/scope_filter.rb +60 -0
- data/lib/smart_brain/configuration.rb +57 -0
- data/lib/smart_brain/consolidator/working_summary.rb +80 -12
- data/lib/smart_brain/context_composer/composer.rb +40 -3
- data/lib/smart_brain/contracts/retrieval_plan.rb +10 -0
- data/lib/smart_brain/contracts/scope_context.rb +46 -0
- data/lib/smart_brain/contracts/scope_ref.rb +25 -0
- data/lib/smart_brain/db.rb +109 -0
- data/lib/smart_brain/event_store/in_memory.rb +6 -2
- data/lib/smart_brain/event_store/postgres.rb +199 -0
- data/lib/smart_brain/fusion/merger.rb +31 -2
- data/lib/smart_brain/governance/briefing.rb +146 -0
- data/lib/smart_brain/governance/fact_check.rb +110 -0
- data/lib/smart_brain/governance/knowledge_graph.rb +60 -0
- data/lib/smart_brain/governance/lifecycle.rb +225 -0
- data/lib/smart_brain/governance/tiers.rb +60 -0
- data/lib/smart_brain/memory_extractor/extractor.rb +25 -7
- data/lib/smart_brain/memory_store/in_memory.rb +202 -17
- data/lib/smart_brain/memory_store/postgres.rb +500 -0
- data/lib/smart_brain/model_provider/base.rb +87 -0
- data/lib/smart_brain/model_provider/factory.rb +49 -0
- data/lib/smart_brain/model_provider/ollama.rb +60 -0
- data/lib/smart_brain/model_provider/openai.rb +60 -0
- data/lib/smart_brain/model_provider/stub.rb +26 -0
- data/lib/smart_brain/model_provider.rb +7 -0
- data/lib/smart_brain/observability/tracker.rb +39 -1
- data/lib/smart_brain/retrievers/exact_retriever.rb +6 -0
- data/lib/smart_brain/retrievers/memory_retriever.rb +59 -5
- data/lib/smart_brain/runtime.rb +288 -16
- data/lib/smart_brain/scopes/conflict_resolver.rb +67 -0
- data/lib/smart_brain/scopes/registry.rb +133 -0
- data/lib/smart_brain/scopes/resolver.rb +32 -0
- data/lib/smart_brain/server/http_app.rb +143 -0
- data/lib/smart_brain/server/mcp_server.rb +385 -0
- data/lib/smart_brain/server/service.rb +129 -0
- data/lib/smart_brain/support/levenshtein.rb +35 -0
- data/lib/smart_brain/version.rb +5 -5
- data/lib/smart_brain.rb +80 -35
- metadata +88 -36
data/example.rb
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'logger'
|
|
4
|
-
require 'json'
|
|
5
|
-
|
|
6
|
-
# Follow smart_agent/test.rb style: load SmartPrompt first, then SmartAgent.
|
|
7
|
-
require 'smart_prompt'
|
|
8
|
-
require 'smart_agent'
|
|
9
|
-
require_relative 'lib/smart_brain'
|
|
10
|
-
require_relative 'lib/smart_brain/adapters/smart_rag/direct_client'
|
|
11
|
-
|
|
12
|
-
begin
|
|
13
|
-
require 'smart_rag'
|
|
14
|
-
rescue LoadError => e
|
|
15
|
-
warn "SmartRAG load failed: #{e.message}"
|
|
16
|
-
warn 'Please install SmartRAG dependencies (especially sequel/pg) before running this example.'
|
|
17
|
-
exit 1
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
rag_config = SmartRAG::Config.load("./config/smart_rag.yml")
|
|
21
|
-
# SmartRAG config may include database.extensions with pgvector.
|
|
22
|
-
# For Sequel, pgvector should be loaded globally via Sequel.extension.
|
|
23
|
-
db_cfg = (rag_config[:database] || {}).dup
|
|
24
|
-
db_exts = Array(db_cfg.delete(:extensions)).map(&:to_s)
|
|
25
|
-
if db_exts.include?('pgvector')
|
|
26
|
-
require 'sequel'
|
|
27
|
-
Sequel.extension 'pgvector'
|
|
28
|
-
end
|
|
29
|
-
# SmartRAG currently passes database config into EmbeddingService.
|
|
30
|
-
# Inject SmartPrompt config path here so EmbeddingService can boot correctly.
|
|
31
|
-
db_cfg[:config_path] = File.expand_path('./config/example_llm.yml', __dir__)
|
|
32
|
-
rag_config = rag_config.merge(database: db_cfg)
|
|
33
|
-
|
|
34
|
-
rag = SmartRAG::SmartRAG.new(rag_config)
|
|
35
|
-
rag_client = SmartBrain::Adapters::SmartRag::DirectClient.new(rag: rag)
|
|
36
|
-
|
|
37
|
-
SmartBrain.configure(smart_rag_client: rag_client)
|
|
38
|
-
engine = SmartAgent::Engine.new('./config/example_agent.yml')
|
|
39
|
-
agent = engine.build_agent(:brain_assistant)
|
|
40
|
-
|
|
41
|
-
session_id = 'smartagent-smartbrain-demo'
|
|
42
|
-
user_messages = [
|
|
43
|
-
'请记住:SmartRAG 作为 SmartBrain 的底层基础库,提供存储与检索服务',
|
|
44
|
-
'继续这个话题,SmartBrain 默认使用哪种数据库?'
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
def build_worker_input(context, user_message)
|
|
48
|
-
{
|
|
49
|
-
context_id: context[:context_id],
|
|
50
|
-
working_summary: context[:working_summary],
|
|
51
|
-
recent_turns: context[:recent_turns],
|
|
52
|
-
evidence: context[:evidence],
|
|
53
|
-
latest_user_message: user_message,
|
|
54
|
-
constraints: context[:constraints]
|
|
55
|
-
}.to_json
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
user_messages.each_with_index do |user_message, idx|
|
|
59
|
-
# 1) SmartBrain composes context (will call SmartRAG when planner enables resource retrieval).
|
|
60
|
-
context = SmartBrain.compose_context(
|
|
61
|
-
session_id: session_id,
|
|
62
|
-
user_message: user_message,
|
|
63
|
-
agent_state: { agent: 'SmartAgent', turn: idx + 1 }
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
# 2) SmartAgent executes the real call_worker flow.
|
|
67
|
-
assistant_message = agent.please(build_worker_input(context, user_message))
|
|
68
|
-
|
|
69
|
-
# 3) SmartBrain commits this turn.
|
|
70
|
-
commit = SmartBrain.commit_turn(
|
|
71
|
-
session_id: session_id,
|
|
72
|
-
turn_events: {
|
|
73
|
-
messages: [
|
|
74
|
-
{ role: 'user', content: user_message },
|
|
75
|
-
{ role: 'assistant', content: assistant_message.to_s }
|
|
76
|
-
],
|
|
77
|
-
decisions: (idx.zero? ? [{ key: 'decision:smartbrain:storage', decision: 'Use Postgres by default' }] : [])
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
resource_hits = Array(context[:evidence]).count { |e| e[:source] == 'resource' }
|
|
82
|
-
memory_hits = Array(context[:evidence]).count { |e| e[:source] == 'memory' }
|
|
83
|
-
|
|
84
|
-
puts "\n=== Turn #{idx + 1} ==="
|
|
85
|
-
puts "context_id: #{context[:context_id]}"
|
|
86
|
-
puts "request_id: #{context.dig(:debug, :trace, :request_id)}"
|
|
87
|
-
puts "plan_id: #{context.dig(:debug, :trace, :plan_id)}"
|
|
88
|
-
puts "commit_id: #{commit[:commit_id]}"
|
|
89
|
-
puts "evidence(memory/resource): #{memory_hits}/#{resource_hits}"
|
|
90
|
-
puts "assistant: #{assistant_message}"
|
|
91
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
# Follow smart_agent/test.rb style: load SmartPrompt first, then SmartAgent.
|
|
7
|
+
require 'smart_prompt'
|
|
8
|
+
require 'smart_agent'
|
|
9
|
+
require_relative 'lib/smart_brain'
|
|
10
|
+
require_relative 'lib/smart_brain/adapters/smart_rag/direct_client'
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
require 'smart_rag'
|
|
14
|
+
rescue LoadError => e
|
|
15
|
+
warn "SmartRAG load failed: #{e.message}"
|
|
16
|
+
warn 'Please install SmartRAG dependencies (especially sequel/pg) before running this example.'
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rag_config = SmartRAG::Config.load("./config/smart_rag.yml")
|
|
21
|
+
# SmartRAG config may include database.extensions with pgvector.
|
|
22
|
+
# For Sequel, pgvector should be loaded globally via Sequel.extension.
|
|
23
|
+
db_cfg = (rag_config[:database] || {}).dup
|
|
24
|
+
db_exts = Array(db_cfg.delete(:extensions)).map(&:to_s)
|
|
25
|
+
if db_exts.include?('pgvector')
|
|
26
|
+
require 'sequel'
|
|
27
|
+
Sequel.extension 'pgvector'
|
|
28
|
+
end
|
|
29
|
+
# SmartRAG currently passes database config into EmbeddingService.
|
|
30
|
+
# Inject SmartPrompt config path here so EmbeddingService can boot correctly.
|
|
31
|
+
db_cfg[:config_path] = File.expand_path('./config/example_llm.yml', __dir__)
|
|
32
|
+
rag_config = rag_config.merge(database: db_cfg)
|
|
33
|
+
|
|
34
|
+
rag = SmartRAG::SmartRAG.new(rag_config)
|
|
35
|
+
rag_client = SmartBrain::Adapters::SmartRag::DirectClient.new(rag: rag)
|
|
36
|
+
|
|
37
|
+
SmartBrain.configure(smart_rag_client: rag_client)
|
|
38
|
+
engine = SmartAgent::Engine.new('./config/example_agent.yml')
|
|
39
|
+
agent = engine.build_agent(:brain_assistant)
|
|
40
|
+
|
|
41
|
+
session_id = 'smartagent-smartbrain-demo'
|
|
42
|
+
user_messages = [
|
|
43
|
+
'请记住:SmartRAG 作为 SmartBrain 的底层基础库,提供存储与检索服务',
|
|
44
|
+
'继续这个话题,SmartBrain 默认使用哪种数据库?'
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
def build_worker_input(context, user_message)
|
|
48
|
+
{
|
|
49
|
+
context_id: context[:context_id],
|
|
50
|
+
working_summary: context[:working_summary],
|
|
51
|
+
recent_turns: context[:recent_turns],
|
|
52
|
+
evidence: context[:evidence],
|
|
53
|
+
latest_user_message: user_message,
|
|
54
|
+
constraints: context[:constraints]
|
|
55
|
+
}.to_json
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
user_messages.each_with_index do |user_message, idx|
|
|
59
|
+
# 1) SmartBrain composes context (will call SmartRAG when planner enables resource retrieval).
|
|
60
|
+
context = SmartBrain.compose_context(
|
|
61
|
+
session_id: session_id,
|
|
62
|
+
user_message: user_message,
|
|
63
|
+
agent_state: { agent: 'SmartAgent', turn: idx + 1 }
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# 2) SmartAgent executes the real call_worker flow.
|
|
67
|
+
assistant_message = agent.please(build_worker_input(context, user_message))
|
|
68
|
+
|
|
69
|
+
# 3) SmartBrain commits this turn.
|
|
70
|
+
commit = SmartBrain.commit_turn(
|
|
71
|
+
session_id: session_id,
|
|
72
|
+
turn_events: {
|
|
73
|
+
messages: [
|
|
74
|
+
{ role: 'user', content: user_message },
|
|
75
|
+
{ role: 'assistant', content: assistant_message.to_s }
|
|
76
|
+
],
|
|
77
|
+
decisions: (idx.zero? ? [{ key: 'decision:smartbrain:storage', decision: 'Use Postgres by default' }] : [])
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
resource_hits = Array(context[:evidence]).count { |e| e[:source] == 'resource' }
|
|
82
|
+
memory_hits = Array(context[:evidence]).count { |e| e[:source] == 'memory' }
|
|
83
|
+
|
|
84
|
+
puts "\n=== Turn #{idx + 1} ==="
|
|
85
|
+
puts "context_id: #{context[:context_id]}"
|
|
86
|
+
puts "request_id: #{context.dig(:debug, :trace, :request_id)}"
|
|
87
|
+
puts "plan_id: #{context.dig(:debug, :trace, :plan_id)}"
|
|
88
|
+
puts "commit_id: #{commit[:commit_id]}"
|
|
89
|
+
puts "evidence(memory/resource): #{memory_hits}/#{resource_hits}"
|
|
90
|
+
puts "assistant: #{assistant_message}"
|
|
91
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# 场景一:纯 memory 后端,零外部依赖。
|
|
5
|
+
# 演示 SmartBrain 最小闭环:commit_turn → compose_context → search_memory → brief → diagnostics。
|
|
6
|
+
# 进程内存储(重启即丢);持久化见 03_postgres_persistence.rb。
|
|
7
|
+
#
|
|
8
|
+
# 运行: ruby examples/01_memory_basic.rb
|
|
9
|
+
|
|
10
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
11
|
+
require 'smart_brain'
|
|
12
|
+
|
|
13
|
+
def hr(title) = puts("\n=== #{title} ===")
|
|
14
|
+
|
|
15
|
+
SmartBrain.configure
|
|
16
|
+
sid = "mem-#{Process.pid}"
|
|
17
|
+
|
|
18
|
+
hr '1) commit_turn:写一轮对话 + 结构化记忆 + KG 边'
|
|
19
|
+
SmartBrain.commit_turn(
|
|
20
|
+
session_id: sid,
|
|
21
|
+
turn_events: {
|
|
22
|
+
messages: [{ role: 'user', content: '我们用 Postgres 做持久化,配 memory_chunks FTS' }],
|
|
23
|
+
decisions: [{ key: 'decision:db:storage', decision: '默认用 Postgres 持久化' }],
|
|
24
|
+
goals: [{ key: 'goal:ship:p2', goal: '完成 P2 知识图谱' }],
|
|
25
|
+
tasks: [{ key: 'task:kg', title: '实现 KG', status: 'doing' }],
|
|
26
|
+
entities: [{ key: 'entity:db:pg', name: 'Postgres', canonical: 'postgres', kind: 'db', remember: true }],
|
|
27
|
+
edges: [{ subject: 'postgres', predicate: 'uses', object: 'jsonb' }]
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
puts '✓ 已写入(decisions / goals / tasks / entities / kg_edge)'
|
|
31
|
+
|
|
32
|
+
hr '2) compose_context:装配上下文(召回证据 + 摘要 + trace)'
|
|
33
|
+
# 注:memory 后端用子串召回;FTS(pg 后端)对中文按字 unigram 命中更准。
|
|
34
|
+
ctx = SmartBrain.compose_context(session_id: sid, user_message: 'Postgres 持久化方案')
|
|
35
|
+
puts "working_summary:\n#{ctx[:working_summary]}"
|
|
36
|
+
puts "evidence 命中 #{ctx[:evidence].size} 条:"
|
|
37
|
+
ctx[:evidence].first(5).each do |e|
|
|
38
|
+
puts " - [#{e[:mode]}] #{e[:title]} score=#{e[:score].round(2)} tier=#{e[:tier]}"
|
|
39
|
+
end
|
|
40
|
+
puts "trace: #{ctx.dig(:debug, :trace).inspect}"
|
|
41
|
+
|
|
42
|
+
hr '3) search_memory:直接搜记忆'
|
|
43
|
+
puts "搜 'Postgres' → #{SmartBrain.search_memory(session_id: sid, query: 'Postgres').size} 条"
|
|
44
|
+
|
|
45
|
+
hr '4) brief:citation-first 认知快照'
|
|
46
|
+
b = SmartBrain.brief(session_id: sid)
|
|
47
|
+
puts "key_facts #{b[:key_facts].size} 条,首条带引用:"
|
|
48
|
+
kf = b[:key_facts].first
|
|
49
|
+
puts " #{kf[:key]} (tier=#{kf[:tier]}, source_turn=#{kf[:source_turn_id][0,8]}…, memory_item=#{kf[:memory_item_id][0,8]}…)"
|
|
50
|
+
puts "unresolved=#{b[:unresolved].size} next_actions=#{b[:next_actions].size}"
|
|
51
|
+
|
|
52
|
+
hr '5) diagnostics:可观测性'
|
|
53
|
+
d = SmartBrain.diagnostics
|
|
54
|
+
puts "backend=#{d[:backend]} compose_p95=#{d[:metrics][:compose_p95_ms]}ms " \
|
|
55
|
+
"mem/res=#{d[:metrics][:memory_resource_ratio]} over_budget=#{d[:metrics][:token_over_budget_rate]}"
|
|
56
|
+
|
|
57
|
+
puts "\n✅ 场景一完成。memory 后端进程内存储,重启即丢 —— 想要持久化请看 03_postgres_persistence.rb。"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# 场景二:记忆治理 —— 思维分层 + 知识生命周期 + KG + Fact-check + Brief。
|
|
5
|
+
# memory 后端,零外部依赖。演示「记忆宫殿」的治理能力。
|
|
6
|
+
#
|
|
7
|
+
# 运行: ruby examples/02_governance.rb
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
10
|
+
require 'smart_brain'
|
|
11
|
+
|
|
12
|
+
def hr(title) = puts("\n=== #{title} ===")
|
|
13
|
+
|
|
14
|
+
SmartBrain.configure
|
|
15
|
+
sid = "gov-#{Process.pid}"
|
|
16
|
+
|
|
17
|
+
hr '0) 先写几条 evidence(用作 distill 的支撑)'
|
|
18
|
+
3.times do |i|
|
|
19
|
+
SmartBrain.commit_turn(session_id: sid, turn_events: {
|
|
20
|
+
decisions: [{ key: "decision:ev:#{i}", decision: "观察 #{i}:持久化用 Postgres 更稳" }]
|
|
21
|
+
})
|
|
22
|
+
end
|
|
23
|
+
refs = SmartBrain.search_memory(session_id: sid, query: '持久化 Postgres').map { |h| h[:id] }
|
|
24
|
+
puts "✓ 写入 3 条 evidence,召回 #{refs.size} 条作为支撑"
|
|
25
|
+
|
|
26
|
+
hr '1) distill:把 evidence 蒸馏成一条候选知识(tier=dao_ren)'
|
|
27
|
+
d = SmartBrain.distill(session_id: sid, statement: '新存储默认走 Postgres',
|
|
28
|
+
content: '所有新 store 实现都以 PG 为目标', tier: 'dao_ren',
|
|
29
|
+
supporting_refs: refs)
|
|
30
|
+
mid = d[:memory_item][:id]
|
|
31
|
+
puts "✓ candidate id=#{mid[0,8]}… tier=#{d[:memory_item][:tier]} lifecycle=#{d[:memory_item][:lifecycle_status]}"
|
|
32
|
+
|
|
33
|
+
hr '2) gate → promote:检查门槛并提升'
|
|
34
|
+
gate = SmartBrain.gate(memory_item_id: mid)
|
|
35
|
+
puts "gate ready=#{gate[:ready]} (refs=#{gate[:checks][:supporting_refs]}, min=#{gate[:checks][:min_required]})"
|
|
36
|
+
promoted = SmartBrain.promote(memory_item_id: mid, verification_refs: refs, reason: '多次验证一致')[:memory_item]
|
|
37
|
+
puts "✓ promote → lifecycle=#{promoted[:lifecycle_status]}"
|
|
38
|
+
|
|
39
|
+
hr '3) KG:加三元组、查询、失效'
|
|
40
|
+
SmartBrain.kg_add(session_id: sid, subject: 'smart_brain', predicate: 'persists_via', object: 'postgres')
|
|
41
|
+
puts "kg_query(persists_via)=#{SmartBrain.kg_query(session_id: sid, predicate: 'persists_via').map { |e| "#{e[:subject]}→#{e[:object]}" }.inspect}"
|
|
42
|
+
edge = SmartBrain.kg_query(session_id: sid, subject: 'smart_brain').first
|
|
43
|
+
SmartBrain.kg_invalidate(edge_id: edge[:id], reason: '换方案')
|
|
44
|
+
puts "✓ invalidate 一条 → stats=#{SmartBrain.kg_stats(session_id: sid).inspect}"
|
|
45
|
+
|
|
46
|
+
hr '4) fact_check:离线矛盾检测'
|
|
47
|
+
SmartBrain.commit_turn(session_id: sid, turn_events: {
|
|
48
|
+
entities: [{ key: 'entity:p', name: 'Postgres', canonical: 'postgres', remember: true }],
|
|
49
|
+
edges: [{ subject: 'postgres', predicate: 'uses', object: 'jsonb' }]
|
|
50
|
+
})
|
|
51
|
+
r = SmartBrain.fact_check(session_id: sid, text: '我们停用了 postgres 的 jsonb')
|
|
52
|
+
puts "findings=#{r[:counts].inspect}"
|
|
53
|
+
r[:findings].each { |f| puts " - [#{f[:type]}] #{f[:detail]}" }
|
|
54
|
+
|
|
55
|
+
hr '5) knowledge_events:审计时间线'
|
|
56
|
+
events = SmartBrain.knowledge_events(memory_item_id: mid)
|
|
57
|
+
puts "事件链:#{events.map { |e| e[:event_type] }.inspect}"
|
|
58
|
+
|
|
59
|
+
hr '6) wake_up:L0/L1 恢复负载'
|
|
60
|
+
w = SmartBrain.wake_up(session_id: sid)
|
|
61
|
+
puts "L0 identity=#{w[:l0][:identity].size} L1 goals_decisions=#{w[:l1][:goals_decisions].size}"
|
|
62
|
+
|
|
63
|
+
puts "\n✅ 场景二完成。distill→promote→demote 全流程 + KG 时态 + Fact-check + Brief 均可用。"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# 场景三:Postgres 持久化 —— 跨「重启」召回。
|
|
5
|
+
# 演示:进程 A 写记忆 → 进程 B(新 Runtime,同一 DB)经 FTS 召回。
|
|
6
|
+
#
|
|
7
|
+
# 前置:本地 PG + smart_brain 角色/库。若没建,脚本会打印步骤并退出。
|
|
8
|
+
# sudo -u postgres createuser -d smart_brain
|
|
9
|
+
# sudo -u postgres psql -c "ALTER USER smart_brain PASSWORD 'smart_brain';"
|
|
10
|
+
# sudo -u postgres createdb -O smart_brain smart_brain_test
|
|
11
|
+
#
|
|
12
|
+
# 运行: SMARTBRAIN_DB_NAME=smart_brain_test ruby examples/03_postgres_persistence.rb
|
|
13
|
+
|
|
14
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
15
|
+
require 'smart_brain'
|
|
16
|
+
require 'smart_brain/db'
|
|
17
|
+
|
|
18
|
+
def hr(title) = puts("\n=== #{title} ===")
|
|
19
|
+
|
|
20
|
+
ENV['SMARTBRAIN_BACKEND'] = 'postgres'
|
|
21
|
+
ENV['SMARTBRAIN_DB_NAME'] ||= 'smart_brain_test'
|
|
22
|
+
|
|
23
|
+
config = SmartBrain::Configuration.load
|
|
24
|
+
unless SmartBrain::DB.reachable?(config)
|
|
25
|
+
warn "✗ 连不上 Postgres(#{config.database_config[:host]}:#{config.database_config[:port]}/#{config.database_config[:database]})。"
|
|
26
|
+
warn " 先建库:见脚本顶部注释或 docs/user_guide.md §10。"
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# 清场(保证可重复运行)
|
|
31
|
+
db = SmartBrain::DB.connect(config)
|
|
32
|
+
SmartBrain::DB.migrate(db)
|
|
33
|
+
db.run('TRUNCATE kg_edges, knowledge_events, memory_chunks, memory_items, summaries, messages, ' \
|
|
34
|
+
'refs, tool_calls, turns, sessions, entities, entity_mentions RESTART IDENTITY CASCADE')
|
|
35
|
+
SmartBrain::DB.disconnect!
|
|
36
|
+
|
|
37
|
+
sid = 'pg-demo'
|
|
38
|
+
|
|
39
|
+
hr '1) 进程 A:commit_turn 写入(含中文决策 + KG 边)'
|
|
40
|
+
SmartBrain.configure
|
|
41
|
+
SmartBrain.commit_turn(
|
|
42
|
+
session_id: sid,
|
|
43
|
+
turn_events: {
|
|
44
|
+
messages: [{ role: 'user', content: '记一下:SmartBrain 用 Postgres 做持久化' }],
|
|
45
|
+
decisions: [{ key: 'decision:pg:persist', decision: 'SmartBrain 用 Postgres 持久化记忆' }],
|
|
46
|
+
entities: [{ key: 'entity:db:pg', name: 'Postgres', canonical: 'postgres', remember: true }],
|
|
47
|
+
edges: [{ subject: 'smart_brain', predicate: 'persists_via', object: 'postgres' }]
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
puts '✓ 进程 A 写完,退出(模拟进程结束)'
|
|
51
|
+
|
|
52
|
+
hr '2) 进程 B:全新 Runtime(同一 DB)—— 验证持久化'
|
|
53
|
+
SmartBrain.configure # 模拟重启:构造全新的 Runtime,共享同一个 Postgres
|
|
54
|
+
puts "backend=#{SmartBrain.storage_backend}"
|
|
55
|
+
hits = SmartBrain.search_memory(session_id: sid, query: '持久化')
|
|
56
|
+
puts "FTS 搜 '持久化' → #{hits.size} 条(mode=#{hits.first&.dig(:mode)})"
|
|
57
|
+
puts "kg_query → #{SmartBrain.kg_query(session_id: sid, subject: 'smart_brain').map { |e| "#{e[:predicate]} #{e[:object]}" }.inspect}"
|
|
58
|
+
|
|
59
|
+
ctx = SmartBrain.compose_context(session_id: sid, user_message: '持久化方案是什么?')
|
|
60
|
+
mem = ctx[:evidence].select { |e| e[:source] == 'memory' }
|
|
61
|
+
puts "compose_context 召回 memory 证据 #{mem.size} 条(跨重启仍命中)"
|
|
62
|
+
|
|
63
|
+
puts "\n✅ 场景三完成。记忆、KG 边、摘要均跨进程持久化。"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# 场景四:Ollama LLM —— 真摘要 + rerank。
|
|
5
|
+
# 演示 ModelProvider 接真实本地模型后:working_summary 走真摘要、Fusion 走 LLM rerank。
|
|
6
|
+
#
|
|
7
|
+
# 前置:本机 ollama 已起、装了某模型(默认用 llama2 —— 快、非思维链;qwen3 更强但慢)。
|
|
8
|
+
# ollama serve & ollama pull llama2
|
|
9
|
+
#
|
|
10
|
+
# 运行: ruby examples/04_ollama_llm.rb
|
|
11
|
+
# 换模型: SMARTBRAIN_LLM_MODEL=qwen3 ruby examples/04_ollama_llm.rb
|
|
12
|
+
|
|
13
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
14
|
+
require 'smart_brain'
|
|
15
|
+
require 'smart_brain/model_provider'
|
|
16
|
+
require 'smart_brain/memory_store/in_memory'
|
|
17
|
+
require 'smart_brain/consolidator/working_summary'
|
|
18
|
+
|
|
19
|
+
def hr(title) = puts("\n=== #{title} ===")
|
|
20
|
+
|
|
21
|
+
base_url = ENV.fetch('SMARTBRAIN_LLM_BASE_URL', 'http://localhost:11434')
|
|
22
|
+
model = ENV.fetch('SMARTBRAIN_LLM_MODEL', 'llama2')
|
|
23
|
+
|
|
24
|
+
unless system("curl -s -m 2 #{base_url}/api/tags >/dev/null 2>&1")
|
|
25
|
+
warn "✗ 连不上 ollama(#{base_url})。先 ollama serve 并 ollama pull #{model}。"
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
provider = SmartBrain::ModelProvider::Ollama.new(
|
|
30
|
+
model: model, base_url: base_url, temperature: 0.2, timeout_seconds: 180, think: false
|
|
31
|
+
)
|
|
32
|
+
puts "provider=#{provider.class} model=#{model} llm?=#{provider.llm?}"
|
|
33
|
+
|
|
34
|
+
hr '1) complete:让模型回答'
|
|
35
|
+
r = provider.complete(prompt: '用一句话说明 PostgreSQL 的 FTS。', max_tokens: 120)
|
|
36
|
+
puts "回答:#{r[:text].strip[0, 200]}"
|
|
37
|
+
|
|
38
|
+
hr '2) rerank:LLM-as-judge 重排候选'
|
|
39
|
+
docs = [
|
|
40
|
+
'埃菲尔铁塔在巴黎。',
|
|
41
|
+
'PostgreSQL 支持 JSONB 与全文检索(FTS)。',
|
|
42
|
+
'Ruby 是一种动态语言。'
|
|
43
|
+
]
|
|
44
|
+
ranked = provider.rerank(query: '关系型数据库的全文检索', documents: docs)
|
|
45
|
+
puts '按相关性重排:'
|
|
46
|
+
ranked.each { |x| puts " [#{x[:index]}] score=#{x[:score].round(2)} :: #{docs[x[:index]][0, 40]}" }
|
|
47
|
+
|
|
48
|
+
hr '3) working_summary:真摘要(带模板兜底)'
|
|
49
|
+
store = SmartBrain::MemoryStore::InMemory.new
|
|
50
|
+
store.upsert(session_id: 's', items: [
|
|
51
|
+
{ type: 'decisions', key: 'decision:db', value_json: { decision: '用 Postgres 持久化' },
|
|
52
|
+
confidence: 0.8, status: 'active', tier: 'dao_ren', lifecycle_status: 'raw', updated_at: Time.now.utc.iso8601 },
|
|
53
|
+
{ type: 'tasks', key: 'task:mcp', value_json: { title: '加 MCP server' },
|
|
54
|
+
confidence: 0.9, status: 'active', tier: 'shu', lifecycle_status: 'raw', updated_at: Time.now.utc.iso8601 }
|
|
55
|
+
])
|
|
56
|
+
ws = SmartBrain::Consolidator::WorkingSummary.new(
|
|
57
|
+
config: SmartBrain::Configuration.load, clock: -> { Time.now.utc },
|
|
58
|
+
memory_store: store, model_provider: provider
|
|
59
|
+
)
|
|
60
|
+
summary = ws.update(
|
|
61
|
+
session_id: 's', turn_count: 1, stage_event: true,
|
|
62
|
+
recent_turns: [{ role: 'user', content: '用 Postgres' }, { role: 'assistant', content: '好,持久化到 memory_chunks' }],
|
|
63
|
+
memory_items: store.active_items(session_id: 's')
|
|
64
|
+
)
|
|
65
|
+
puts "summary_method=#{summary[:summary_method]}"
|
|
66
|
+
puts "摘要:\n#{summary[:text]}"
|
|
67
|
+
|
|
68
|
+
puts "\n✅ 场景四完成。stub→ollama 后 summary/rerank 走真实模型(超时/截断自动降级到模板/词法)。"
|
|
69
|
+
puts " 注:思维链模型(qwen3/deepseek-r1)同步较慢,本示例默认 llama2;换 qwen3 用 SMARTBRAIN_LLM_MODEL=qwen3。"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# 场景五:SmartBrain + SmartRAG —— 对话记忆 + 资源检索融合。
|
|
5
|
+
# SmartBrain 管对话记忆/治理,SmartRAG 管文档/网页 RAG;compose_context 命中「查资料/引用」
|
|
6
|
+
# 意图时自动调用 SmartRAG,再由 Fusion 统一去重 + rerank + diversity。
|
|
7
|
+
#
|
|
8
|
+
# 前置(缺任一则脚本会打印步骤并优雅退出):
|
|
9
|
+
# 1) SmartRAG gem 已装(bundle 里已有 smart_rag)。
|
|
10
|
+
# 2) SmartRAG 的数据库可连(默认读下方 env)。
|
|
11
|
+
# 3) (可选)embedding/llm 配置。
|
|
12
|
+
# 完整可运行版见仓库根目录 conversation_demo.rb。
|
|
13
|
+
#
|
|
14
|
+
# 运行:
|
|
15
|
+
# SMARTBRAIN_RAG_DB_HOST=127.0.0.1 \
|
|
16
|
+
# SMARTBRAIN_RAG_DB_NAME=smart_rag_development \
|
|
17
|
+
# SMARTBRAIN_RAG_DB_USER=rag_user SMARTBRAIN_RAG_DB_PASSWORD=rag_pwd \
|
|
18
|
+
# ruby examples/05_smart_rag_integration.rb
|
|
19
|
+
|
|
20
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
21
|
+
require 'smart_brain'
|
|
22
|
+
require 'smart_brain/adapters/smart_rag/direct_client'
|
|
23
|
+
|
|
24
|
+
def hr(title) = puts("\n=== #{title} ===")
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
require 'smart_rag'
|
|
28
|
+
rescue LoadError => e
|
|
29
|
+
warn "✗ 加载 smart_rag 失败:#{e.message}"
|
|
30
|
+
warn " 请先确保 smart_rag gem 可用(bundle 里已声明)。"
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
rag_config = {
|
|
35
|
+
database: {
|
|
36
|
+
adapter: 'postgresql',
|
|
37
|
+
host: ENV.fetch('SMARTBRAIN_RAG_DB_HOST', '127.0.0.1'),
|
|
38
|
+
port: ENV.fetch('SMARTBRAIN_RAG_DB_PORT', '5432').to_i,
|
|
39
|
+
database: ENV.fetch('SMARTBRAIN_RAG_DB_NAME', 'smart_rag_development'),
|
|
40
|
+
user: ENV.fetch('SMARTBRAIN_RAG_DB_USER', 'rag_user'),
|
|
41
|
+
password: ENV.fetch('SMARTBRAIN_RAG_DB_PASSWORD', 'rag_pwd')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
rag =
|
|
46
|
+
begin
|
|
47
|
+
SmartRAG::SmartRAG.new(rag_config)
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
warn "✗ SmartRAG 初始化失败:#{e.class}: #{e.message}"
|
|
50
|
+
warn " 检查上方数据库 env,或参考仓库根目录 conversation_demo.rb 的完整配置(含 llm/embedding)。"
|
|
51
|
+
exit 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# 挂载资源后端
|
|
55
|
+
SmartBrain.configure(smart_rag_client: SmartBrain::Adapters::SmartRag::DirectClient.new(rag: rag))
|
|
56
|
+
sid = "rag-#{Process.pid}"
|
|
57
|
+
|
|
58
|
+
hr '1) commit_turn:写一轮对话记忆'
|
|
59
|
+
SmartBrain.commit_turn(
|
|
60
|
+
session_id: sid,
|
|
61
|
+
turn_events: {
|
|
62
|
+
messages: [{ role: 'user', content: '帮我查一下 Ruby 命名规范的资料' }],
|
|
63
|
+
goals: [{ key: 'goal:learn:ruby', goal: '学习 Ruby 规范' }]
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
puts '✓ 对话记忆已写'
|
|
67
|
+
|
|
68
|
+
hr '2) compose_context:命中「查资料」意图 → 调 SmartRAG + 融合'
|
|
69
|
+
ctx = SmartBrain.compose_context(session_id: sid, user_message: '查一下 Ruby 命名规范并给出引用')
|
|
70
|
+
mem = ctx[:evidence].count { |e| e[:source] == 'memory' }
|
|
71
|
+
res = ctx[:evidence].count { |e| e[:source] == 'resource' }
|
|
72
|
+
puts "evidence:memory=#{mem} resource=#{res} (memory/resource 比 = #{SmartBrain.diagnostics[:metrics][:memory_resource_ratio]})"
|
|
73
|
+
puts "资源检索是否触发:#{ctx.dig(:debug, :planner, :purpose) == 'research' ? '是(purpose=research)' : '否'}"
|
|
74
|
+
ctx[:evidence].first(3).each do |e|
|
|
75
|
+
puts " - [#{e[:source]}] #{e[:title]} score=#{e[:score].round(2)}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
puts "\n✅ 场景五完成。SmartBrain 记忆 + SmartRAG 资源在 Fusion 层统一融合。"
|
|
79
|
+
puts " 若上方 resource=0:往 SmartRAG 灌点文档(rag.add_document ...),或换一个明显命中资源检索的 query。"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'smart_brain'
|
|
4
|
+
|
|
5
|
+
SmartBrain.configure
|
|
6
|
+
|
|
7
|
+
domain_id = 'demo-domain'
|
|
8
|
+
project_ref = { type: 'project', id: 'project-001' }
|
|
9
|
+
task_ref = { type: 'task', id: 'task-030' }
|
|
10
|
+
scope_context = {
|
|
11
|
+
read: [{ type: 'global', id: 'default' }, project_ref, task_ref],
|
|
12
|
+
write: [project_ref, task_ref],
|
|
13
|
+
default_write: task_ref
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
source = SmartBrain.commit_turn(
|
|
17
|
+
domain_id: domain_id,
|
|
18
|
+
session_id: 'writer-session',
|
|
19
|
+
scope_context: scope_context,
|
|
20
|
+
turn_events: {
|
|
21
|
+
decisions: [{ key: 'decision:database', decision: 'Use PostgreSQL for this task' }]
|
|
22
|
+
}
|
|
23
|
+
).dig(:memory_written, :items).first
|
|
24
|
+
|
|
25
|
+
promotion = SmartBrain.promote_to_scope(
|
|
26
|
+
memory_item_id: source[:id],
|
|
27
|
+
target_scope: project_ref,
|
|
28
|
+
domain_id: domain_id,
|
|
29
|
+
session_id: 'writer-session',
|
|
30
|
+
scope_context: scope_context,
|
|
31
|
+
verification_refs: ['architecture-review-001'],
|
|
32
|
+
reason: 'Confirmed as a project-wide decision',
|
|
33
|
+
reviewer: 'human'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
reader_context = {
|
|
37
|
+
read: [{ type: 'global', id: 'default' }, project_ref, { type: 'task', id: 'task-031' }],
|
|
38
|
+
write: [{ type: 'task', id: 'task-031' }],
|
|
39
|
+
default_write: { type: 'task', id: 'task-031' }
|
|
40
|
+
}
|
|
41
|
+
context = SmartBrain.compose_context(
|
|
42
|
+
domain_id: domain_id,
|
|
43
|
+
session_id: 'reader-session',
|
|
44
|
+
scope_context: reader_context,
|
|
45
|
+
user_message: 'Which database did the project choose?'
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
puts "Promoted item: #{promotion.dig(:memory_item, :id)}"
|
|
49
|
+
puts "Recalled scopes: #{context[:evidence].map { |item| item[:scope] }.compact.inspect}"
|
|
50
|
+
puts "Lineage: #{SmartBrain.promotion_lineage(memory_item_id: promotion.dig(:memory_item, :id)).map { |item| item[:id] }.inspect}"
|
data/examples/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SmartBrain 示例集
|
|
2
|
+
|
|
3
|
+
每个脚本都是**自包含、可独立运行**的,从易到难、按需引入外部依赖。
|
|
4
|
+
全部默认零外部依赖即可起步;PG / Ollama / SmartRAG 按场景按需开启。
|
|
5
|
+
|
|
6
|
+
## 场景速查
|
|
7
|
+
|
|
8
|
+
| 脚本 | 场景 | 依赖 | 后端 |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| [`01_memory_basic.rb`](01_memory_basic.rb) | 最小闭环:commit→compose→search→brief→diagnostics | 无 | memory |
|
|
11
|
+
| [`02_governance.rb`](02_governance.rb) | 记忆治理:lifecycle + KG + fact_check + brief | 无 | memory |
|
|
12
|
+
| [`03_postgres_persistence.rb`](03_postgres_persistence.rb) | 跨「重启」持久化召回 | PostgreSQL | postgres |
|
|
13
|
+
| [`04_ollama_llm.rb`](04_ollama_llm.rb) | LLM 真摘要 + rerank | Ollama | memory |
|
|
14
|
+
| [`05_smart_rag_integration.rb`](05_smart_rag_integration.rb) | 对话记忆 + 资源 RAG 融合 | SmartRAG | memory |
|
|
15
|
+
| [`06_multi_scope_memory.rb`](06_multi_scope_memory.rb) | project/task 跨会话共享、冲突与 promotion | 无 | memory |
|
|
16
|
+
|
|
17
|
+
## 运行
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd /root/smart_brain
|
|
21
|
+
|
|
22
|
+
ruby examples/01_memory_basic.rb # 直接跑,无需任何配置
|
|
23
|
+
ruby examples/02_governance.rb # 直接跑
|
|
24
|
+
|
|
25
|
+
SMARTBRAIN_DB_NAME=smart_brain_test \
|
|
26
|
+
ruby examples/03_postgres_persistence.rb # 需先建库(见脚本顶部注释)
|
|
27
|
+
|
|
28
|
+
ruby examples/04_ollama_llm.rb # 需 ollama serve + ollama pull llama2
|
|
29
|
+
SMARTBRAIN_LLM_MODEL=qwen3 ruby examples/04_ollama_llm.rb # 换更强的模型(更慢)
|
|
30
|
+
|
|
31
|
+
SMARTBRAIN_RAG_DB_HOST=... SMARTBRAIN_RAG_DB_NAME=smart_rag_development \
|
|
32
|
+
ruby examples/05_smart_rag_integration.rb # 需 SmartRAG 实例
|
|
33
|
+
ruby examples/06_multi_scope_memory.rb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> 没装对应依赖时,03/04/05 会**打印设置步骤并优雅退出**(不会崩)。
|
|
37
|
+
|
|
38
|
+
## 阅读顺序建议
|
|
39
|
+
|
|
40
|
+
1. `01_memory_basic.rb` —— 先建立 commit/compose 心智模型。
|
|
41
|
+
2. `02_governance.rb` —— 理解「记忆宫殿」的分层与生命周期。
|
|
42
|
+
3. 按需挑 03/04/05 之一看持久化、LLM、资源检索如何接入。
|
|
43
|
+
|
|
44
|
+
## 参考文档
|
|
45
|
+
|
|
46
|
+
- 完整用法:[`../docs/user_guide.md`](../docs/user_guide.md)
|
|
47
|
+
- MCP 接入:[`../docs/mcp.md`](../docs/mcp.md)
|
|
48
|
+
- 设计与契约:`../docs/smartbrain_design.md`、`../docs/memory_types.md`、`../docs/policies.md`
|
|
49
|
+
- 完整 SmartRAG 集成 demo:`../conversation_demo.rb`、`../example.rb`
|