agent99 0.0.3 → 0.0.5
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/A2A_SPEC-dev.md +1829 -0
- data/CHANGELOG.md +38 -0
- data/COMMITS.md +196 -0
- data/DOCS.md +96 -0
- data/README.md +212 -84
- data/Rakefile +62 -0
- data/docs/AI/htm.md +215 -0
- data/docs/AI/htm.rb +141 -0
- data/docs/AI/htm_demo.db +0 -0
- data/docs/AI/notes_on_htm_implementation.md +1319 -0
- data/docs/AI/some_code.rb +692 -0
- data/docs/advanced-topics/a2a-protocol.md +13 -0
- data/docs/{advanced_features.md → advanced-topics/advanced-features.md} +9 -4
- data/docs/{control_actions.md → advanced-topics/control-actions.md} +2 -0
- data/docs/advanced-topics/model-context-protocol.md +4 -0
- data/docs/advanced-topics/multi-agent-processing.md +674 -0
- data/docs/agent-development/request-response-handling.md +512 -0
- data/docs/agent99_framework/central_registry.md +94 -0
- data/docs/agent99_framework/message_client.md +120 -0
- data/docs/agent99_framework/registry_client.md +119 -0
- data/docs/api-reference/agent99-base.md +463 -0
- data/docs/api-reference/message-clients.md +495 -0
- data/docs/{api_reference.md → api-reference/overview.md} +14 -4
- data/docs/api-reference/registry-client.md +470 -0
- data/docs/api-reference/schemas.md +518 -0
- data/docs/assets/css/custom.css +27 -0
- data/docs/assets/images/agent-lifecycle.svg +73 -0
- data/docs/assets/images/agent-registry-process.svg +86 -0
- data/docs/assets/images/agent-registry-processes.svg +114 -0
- data/docs/assets/images/agent-types-overview.svg +51 -0
- data/docs/assets/images/agent99-architecture.svg +85 -0
- data/docs/assets/images/agent99_logo.png +0 -0
- data/docs/assets/images/control-actions-state.svg +83 -0
- data/docs/assets/images/knowledge-graph.svg +77 -0
- data/docs/assets/images/message-processing-flow.svg +148 -0
- data/docs/assets/images/multi-agent-system.svg +66 -0
- data/docs/assets/images/proxy-pattern-sequence.svg +48 -0
- data/docs/assets/images/request-flow.svg +97 -0
- data/docs/assets/images/request-processing-lifecycle.svg +50 -0
- data/docs/assets/images/request-response-sequence.svg +39 -0
- data/docs/{agent_lifecycle.md → core-concepts/agent-lifecycle.md} +2 -0
- data/docs/core-concepts/agent-types.md +255 -0
- data/docs/{architecture.md → core-concepts/architecture.md} +5 -5
- data/docs/core-concepts/what-is-an-agent.md +293 -0
- data/docs/diagrams/message-flow-sequence.svg +198 -0
- data/docs/diagrams/p2p-network-topology.svg +181 -0
- data/docs/diagrams/smart-transport-routing.svg +165 -0
- data/docs/diagrams/three-layer-architecture.svg +77 -0
- data/docs/diagrams/transport-extension-api.svg +309 -0
- data/docs/diagrams/transport-extension-architecture.svg +234 -0
- data/docs/diagrams/transport-selection-flowchart.svg +264 -0
- data/docs/examples/advanced-examples.md +951 -0
- data/docs/examples/basic-examples.md +268 -0
- data/docs/{agent_discovery.md → framework-components/agent-discovery.md} +9 -5
- data/docs/{agent_registry_processes.md → framework-components/agent-registry.md} +9 -3
- data/docs/{message_processing.md → framework-components/message-processing.md} +3 -1
- data/docs/getting-started/basic-example.md +306 -0
- data/docs/getting-started/installation.md +160 -0
- data/docs/getting-started/overview.md +64 -0
- data/docs/getting-started/quick-start.md +179 -0
- data/docs/index.md +97 -0
- data/docs/operations/breaking-changes.md +26 -0
- data/examples/DEMO.md +148 -0
- data/examples/README.md +50 -0
- data/examples/agent_watcher.rb +5 -1
- data/examples/bad_agent.rb +32 -0
- data/examples/chief_agent.rb +17 -6
- data/examples/control.rb +16 -7
- data/examples/example_agent.rb +16 -3
- data/examples/maxwell_agent86.rb +15 -26
- data/examples/registry.rb +10 -9
- data/examples/run_demo.rb +433 -0
- data/lib/agent99/agent_discovery.rb +4 -0
- data/lib/agent99/agent_lifecycle.rb +34 -10
- data/lib/agent99/amqp_message_client.rb +2 -2
- data/lib/agent99/base.rb +6 -2
- data/lib/agent99/message_processing.rb +6 -10
- data/lib/agent99/registry_client.rb +15 -11
- data/lib/agent99/tcp_message_client.rb +183 -0
- data/lib/agent99/version.rb +1 -1
- data/lib/agent99.rb +1 -1
- data/mkdocs.yml +195 -0
- data/p2p_plan.md +533 -0
- data/p2p_roadmap.md +299 -0
- data/registry_plan.md +1818 -0
- metadata +93 -30
- data/docs/README.md +0 -57
- data/docs/diagrams/agent_registry_processes.dot +0 -42
- data/docs/diagrams/agent_registry_processes.png +0 -0
- data/docs/diagrams/high_level_architecture.dot +0 -26
- data/docs/diagrams/high_level_architecture.png +0 -0
- data/docs/diagrams/request_flow.dot +0 -42
- data/docs/diagrams/request_flow.png +0 -0
- /data/docs/{extending_the_framework.md → advanced-topics/extending-the-framework.md} +0 -0
- /data/docs/{custom_agent_implementation.md → agent-development/custom-agent-implementation.md} +0 -0
- /data/docs/{error_handling_and_logging.md → agent-development/error-handling-and-logging.md} +0 -0
- /data/docs/{schema_definition.md → agent-development/schema-definition.md} +0 -0
- /data/docs/{messaging_system.md → framework-components/messaging-system.md} +0 -0
- /data/docs/{configuration.md → operations/configuration.md} +0 -0
- /data/docs/{preformance_considerations.md → operations/performance-considerations.md} +0 -0
- /data/docs/{security.md → operations/security.md} +0 -0
- /data/docs/{troubleshooting.md → operations/troubleshooting.md} +0 -0
@@ -0,0 +1,1319 @@
|
|
1
|
+
|
2
|
+
ruby-12-15 18:49:23] INFO: #<AIA::Config arguments=#<Hashie::Array ["ruby_expert"]> backend="mods" chat?=true completion=nil config_file=nil debug?=false directives=#<Hashie::Array []> dump_file=nil edit?=false erb?=false extra="" fuzzy?=false help?=false image_quality="" image_size="" log_file=#<Pathname:/Users/dewayne/.prompts/_prompts.log> markdown?=true model="gpt-4o-mini" next="" out_file=#<IO:<STDOUT>> pipeline=#<Hashie::Array []> prompts_dir=#<Pathname:/Users/dewayne/.prompts> render?=false role="" roles_dir=#<Pathname:/Users/dewayne/.prompts/roles> shell?=false speak?=false speech_model="tts-1" terse?=false transcription_model="wisper-1" verbose?=true version?=false voice="alloy">
|
3
|
+
|
4
|
+
|
5
|
+
[2024-12-15 18:51:14] INFO: PROMPT ID ruby_expert
|
6
|
+
PATH: /Users/dewayne/.prompts/ruby_expert.txt
|
7
|
+
KEYWORDS: [DO_WHAT]
|
8
|
+
|
9
|
+
As an experienced Ruby software engineer when giving tasks involving the Ruby programming language, use modern Ruby 3.3 syntax, especially for hashes and method signatures with named parameters when generating Ruby source code. Whe you do generate Ruby source code I want the following formatting to be used. Consecutive assignment statements will have theirh "=" character in the same column. This also applies for the the insert operator "<<" as well. When formatting a Hash statement all values will start in the same column.
|
10
|
+
|
11
|
+
I would like to talk through an idean that I have for implemenmtion something like heirarchical temporal memory at the database level. do you know what HTM is
|
12
|
+
|
13
|
+
RESULT:
|
14
|
+
Yes, I'm familiar with Hierarchical Temporal Memory (HTM). HTM is a machine learning
|
15
|
+
technology that aims to capture the structural and algorithmic properties of the neocortex in
|
16
|
+
the human brain. It's designed to model temporal sequences of patterns and make predictions
|
17
|
+
based on learned sequences.
|
18
|
+
|
19
|
+
The key features of HTM include:
|
20
|
+
|
21
|
+
1. Sparse Distributed Representations
|
22
|
+
2. Temporal learning
|
23
|
+
3. Hierarchical structure
|
24
|
+
4. Online learning
|
25
|
+
5. Anomaly detection
|
26
|
+
|
27
|
+
Implementing HTM at the database level is an interesting concept. It could potentially allow
|
28
|
+
for more intelligent data storage, retrieval, and analysis directly within the database
|
29
|
+
system. This could lead to more efficient processing of time-series data, better pattern
|
30
|
+
recognition, and improved predictive capabilities built into the database itself.
|
31
|
+
|
32
|
+
Would you like to discuss specific aspects of how you're thinking of implementing HTM at the
|
33
|
+
database level? For example:
|
34
|
+
|
35
|
+
1. How would you represent the hierarchical structure in the database schema?
|
36
|
+
2. Are you considering implementing the learning algorithms within the database, or just the
|
37
|
+
data structure?
|
38
|
+
3. What kind of queries or operations do you envision for interacting with the HTM-like
|
39
|
+
structure?
|
40
|
+
4. Are there specific use cases or types of data you're targeting with this implementation?
|
41
|
+
|
42
|
+
Let me know which aspects you'd like to explore further, and I'd be happy to discuss them in
|
43
|
+
more detail.
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
[2024-12-15 18:56:41] INFO: Follow Up:
|
50
|
+
I was thinking about using knowledge graphs to represent a topic of discourse based upon the idea that I can construct a KG that spans the range of detail about the topic from most general to most detailed. what do you thing about that?
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
[2024-12-15 18:56:41] INFO: Response:
|
55
|
+
That's an intriguing approach! Using knowledge graphs to represent a topic of discourse with
|
56
|
+
varying levels of detail is a powerful idea. It aligns well with the hierarchical nature of
|
57
|
+
HTM and could provide a flexible, scalable way to represent and navigate complex information.
|
58
|
+
Let's explore this concept further:
|
59
|
+
|
60
|
+
Advantages of this approach:
|
61
|
+
|
62
|
+
1. Hierarchical Representation: Knowledge graphs naturally support hierarchical structures,
|
63
|
+
which aligns well with the HTM concept.
|
64
|
+
|
65
|
+
2. Flexibility: You can easily add new nodes and relationships at any level of detail.
|
66
|
+
|
67
|
+
3. Contextual Understanding: The relationships between concepts at different levels can
|
68
|
+
provide rich context.
|
69
|
+
|
70
|
+
4. Scalability: Knowledge graphs can handle large amounts of interconnected data efficiently.
|
71
|
+
|
72
|
+
5. Query Flexibility: Graph databases allow for complex queries that can traverse the
|
73
|
+
hierarchy efficiently.
|
74
|
+
|
75
|
+
Implementation considerations:
|
76
|
+
|
77
|
+
1. Graph Database: You might want to use a graph database like Neo4j or Amazon Neptune to
|
78
|
+
store and query your knowledge graph efficiently.
|
79
|
+
|
80
|
+
2. Node Types: You could have different node types representing different levels of
|
81
|
+
abstraction or detail.
|
82
|
+
|
83
|
+
3. Edge Types: Define relationships that connect nodes both horizontally (related concepts)
|
84
|
+
and vertically (more general/more specific).
|
85
|
+
|
86
|
+
4. Metadata: Include metadata on nodes to indicate their level of detail or abstraction.
|
87
|
+
|
88
|
+
Here's a simple Ruby example of how you might start to model this:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class KnowledgeNode
|
92
|
+
attr_reader :id, :content, :detail_level
|
93
|
+
|
94
|
+
def initialize(id:, content:, detail_level:)
|
95
|
+
@id = id
|
96
|
+
@content = content
|
97
|
+
@detail_level = detail_level
|
98
|
+
@connections = {
|
99
|
+
more_general: [],
|
100
|
+
more_specific: [],
|
101
|
+
related: []
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_connection(node:, relationship:)
|
106
|
+
@connections[relationship] << node
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class KnowledgeGraph
|
111
|
+
def initialize
|
112
|
+
@nodes = {}
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_node(node:)
|
116
|
+
@nodes[node.id] = node
|
117
|
+
end
|
118
|
+
|
119
|
+
def connect_nodes(from_id:, to_id:, relationship:)
|
120
|
+
from_node = @nodes[from_id]
|
121
|
+
to_node = @nodes[to_id]
|
122
|
+
from_node.add_connection(node: to_node, relationship: relationship)
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_more_specific(node_id:)
|
126
|
+
@nodes[node_id].connections[:more_specific]
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_more_general(node_id:)
|
130
|
+
@nodes[node_id].connections[:more_general]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
This basic structure allows you to create nodes with different levels of detail and connect
|
136
|
+
them in a hierarchical manner. You could expand on this to include methods for traversing the
|
137
|
+
graph, finding paths between concepts, or identifying clusters of related ideas.
|
138
|
+
|
139
|
+
Some challenges to consider:
|
140
|
+
|
141
|
+
1. Maintaining Consistency: Ensuring that relationships between nodes at different levels
|
142
|
+
remain consistent as the graph grows.
|
143
|
+
|
144
|
+
2. Scalability: As the graph grows, efficient querying and traversal become crucial.
|
145
|
+
|
146
|
+
3. Ontology Design: Defining a clear ontology for your knowledge domain to guide the creation
|
147
|
+
and connection of nodes.
|
148
|
+
|
149
|
+
4. Integration with HTM: Considering how to integrate this knowledge graph structure with
|
150
|
+
HTM's learning and prediction capabilities.
|
151
|
+
|
152
|
+
What are your thoughts on this approach? Do you see any specific challenges or opportunities
|
153
|
+
in implementing this for your use case?
|
154
|
+
|
155
|
+
|
156
|
+
[2024-12-15 19:03:35] INFO: Follow Up:
|
157
|
+
Are there any ruby gems that implement knowledge graphs
|
158
|
+
|
159
|
+
|
160
|
+
[2024-12-15 19:03:35] INFO: Response:
|
161
|
+
Yes, there are several Ruby gems that can help you work with knowledge graphs or implement
|
162
|
+
graph-like structures. While there isn't a gem specifically named "knowledge graph," there are
|
163
|
+
gems that provide graph database interfaces or graph data structures that could be used to
|
164
|
+
implement a knowledge graph. Here are a few options:
|
165
|
+
|
166
|
+
1. Neo4j.rb
|
167
|
+
This is a Ruby wrapper for the Neo4j graph database. It's one of the most popular options for
|
168
|
+
working with graph structures in Ruby.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
gem 'neo4j'
|
172
|
+
```
|
173
|
+
|
174
|
+
Example usage:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
require 'neo4j'
|
178
|
+
|
179
|
+
class Concept
|
180
|
+
include Neo4j::ActiveNode
|
181
|
+
property :name, type: String
|
182
|
+
property :detail_level, type: Integer
|
183
|
+
|
184
|
+
has_many :out, :more_specific, type: :SPECIALIZES, model_class: 'Concept'
|
185
|
+
has_many :in, :more_general, type: :SPECIALIZES, model_class: 'Concept'
|
186
|
+
end
|
187
|
+
|
188
|
+
# Create nodes
|
189
|
+
general = Concept.create(name: "Science", detail_level: 1)
|
190
|
+
specific = Concept.create(name: "Physics", detail_level: 2)
|
191
|
+
|
192
|
+
# Create relationship
|
193
|
+
general.more_specific << specific
|
194
|
+
```
|
195
|
+
|
196
|
+
2. Ruby-GraphViz
|
197
|
+
While primarily for visualization, it can be used to create and manipulate graph structures.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
gem 'ruby-graphviz'
|
201
|
+
```
|
202
|
+
|
203
|
+
Example usage:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
require 'graphviz'
|
207
|
+
|
208
|
+
graph = GraphViz.new(:G, type: :digraph)
|
209
|
+
|
210
|
+
node1 = graph.add_nodes("Science")
|
211
|
+
node2 = graph.add_nodes("Physics")
|
212
|
+
|
213
|
+
graph.add_edges(node1, node2)
|
214
|
+
|
215
|
+
# See knowledge graph visualization at:
|
216
|
+
# docs/assets/images/knowledge-graph.svg
|
217
|
+
# graph.output(png: "knowledge_graph.png")
|
218
|
+
```
|
219
|
+
|
220
|
+

|
221
|
+
|
222
|
+
3. RGL (Ruby Graph Library)
|
223
|
+
A pure Ruby graph library that provides a set of graph data structures and algorithms.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
gem 'rgl'
|
227
|
+
```
|
228
|
+
|
229
|
+
Example usage:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
require 'rgl/adjacency'
|
233
|
+
require 'rgl/dot'
|
234
|
+
|
235
|
+
graph = RGL::DirectedAdjacencyGraph.new
|
236
|
+
|
237
|
+
graph.add_edge('Science', 'Physics')
|
238
|
+
graph.add_edge('Physics', 'Quantum Mechanics')
|
239
|
+
|
240
|
+
graph.write_to_graphic_file('jpg')
|
241
|
+
```
|
242
|
+
|
243
|
+
4. OrientDB-JRuby
|
244
|
+
If you're using JRuby, this gem provides access to OrientDB, a multi-model database that
|
245
|
+
supports graph structures.
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
gem 'orientdb'
|
249
|
+
```
|
250
|
+
|
251
|
+
Example usage:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
require 'orientdb'
|
255
|
+
|
256
|
+
database = OrientDB::Database.new(url: "remote:localhost/knowledge_graph")
|
257
|
+
|
258
|
+
database.create_vertex_type('Concept')
|
259
|
+
|
260
|
+
science = database.create_vertex('Concept', name: 'Science', detail_level: 1)
|
261
|
+
physics = database.create_vertex('Concept', name: 'Physics', detail_level: 2)
|
262
|
+
|
263
|
+
database.create_edge('Specializes', science, physics)
|
264
|
+
```
|
265
|
+
|
266
|
+
While these gems don't implement knowledge graphs directly, they provide the building blocks
|
267
|
+
you'd need to create one. You'd likely need to build additional abstraction layers on top of
|
268
|
+
these to implement the specific semantics and operations of a knowledge graph for your use
|
269
|
+
case.
|
270
|
+
|
271
|
+
Remember to choose the gem that best fits your specific requirements, considering factors like
|
272
|
+
performance, ease of use, and whether you need a full database solution or just an in-memory
|
273
|
+
graph structure.
|
274
|
+
|
275
|
+
|
276
|
+
[2024-12-15 19:06:02] INFO: Follow Up:
|
277
|
+
is it fair to posit that within the temporal component the most detailed knowledge nodes are forgotten first before the more general nodes.
|
278
|
+
|
279
|
+
|
280
|
+
[2024-12-15 19:06:02] INFO: Response:
|
281
|
+
That's an insightful observation, and it aligns well with both cognitive science research and
|
282
|
+
practical considerations in knowledge management. Let's explore this idea:
|
283
|
+
|
284
|
+
1. Cognitive Science Perspective:
|
285
|
+
Research in human memory and learning suggests that general concepts tend to be more resilient
|
286
|
+
than specific details. This phenomenon is often referred to as the "gist" or "fuzzy-trace"
|
287
|
+
theory of memory.
|
288
|
+
|
289
|
+
2. Information Theory:
|
290
|
+
More general nodes typically contain less specific information, making them more compressible
|
291
|
+
and potentially easier to retain.
|
292
|
+
|
293
|
+
3. Practical Knowledge Management:
|
294
|
+
In many systems, retaining high-level concepts while allowing low-level details to expire can
|
295
|
+
be an efficient strategy for managing information over time.
|
296
|
+
|
297
|
+
Implementing this concept in a Ruby-based knowledge graph system could look something like
|
298
|
+
this:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
class KnowledgeNode
|
302
|
+
attr_reader :id, :content, :detail_level, :last_accessed
|
303
|
+
def initialize(id:, content:, detail_level:)
|
304
|
+
@id = id
|
305
|
+
@content = content
|
306
|
+
@detail_level = detail_level
|
307
|
+
@last_accessed = Time.now
|
308
|
+
@connections = {
|
309
|
+
more_general: [],
|
310
|
+
more_specific: [],
|
311
|
+
related: []
|
312
|
+
}
|
313
|
+
end
|
314
|
+
|
315
|
+
def access
|
316
|
+
@last_accessed = Time.now
|
317
|
+
end
|
318
|
+
|
319
|
+
def forgettable?(threshold)
|
320
|
+
Time.now - @last_accessed > threshold * (1.0 / @detail_level)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
class TemporalKnowledgeGraph
|
325
|
+
def initialize(forget_threshold: 30 * 24 * 60 * 60) # 30 days in seconds
|
326
|
+
@nodes = {}
|
327
|
+
@forget_threshold = forget_threshold
|
328
|
+
end
|
329
|
+
|
330
|
+
def add_node(node:)
|
331
|
+
@nodes[node.id] = node
|
332
|
+
end
|
333
|
+
|
334
|
+
def access_node(id:)
|
335
|
+
node = @nodes[id]
|
336
|
+
node.access if node
|
337
|
+
node
|
338
|
+
end
|
339
|
+
|
340
|
+
def forget_old_nodes
|
341
|
+
@nodes.delete_if do |id, node|
|
342
|
+
node.forgettable?(@forget_threshold)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def connect_nodes(from_id:, to_id:, relationship:)
|
347
|
+
from_node = @nodes[from_id]
|
348
|
+
to_node = @nodes[to_id]
|
349
|
+
from_node.connections[relationship] << to_node if from_node && to_node
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# Usage
|
354
|
+
graph = TemporalKnowledgeGraph.new(forget_threshold: 60 * 60 * 24) # 1 day
|
355
|
+
|
356
|
+
node1 = KnowledgeNode.new(id: 1, content: "Science", detail_level: 1)
|
357
|
+
node2 = KnowledgeNode.new(id: 2, content: "Physics", detail_level: 2)
|
358
|
+
node3 = KnowledgeNode.new(id: 3, content: "Quantum Entanglement", detail_level: 3)
|
359
|
+
|
360
|
+
graph.add_node(node: node1)
|
361
|
+
graph.add_node(node: node2)
|
362
|
+
graph.add_node(node: node3)
|
363
|
+
|
364
|
+
graph.connect_nodes(from_id: 1, to_id: 2, relationship: :more_specific)
|
365
|
+
graph.connect_nodes(from_id: 2, to_id: 3, relationship: :more_specific)
|
366
|
+
|
367
|
+
# Simulate passage of time and node access
|
368
|
+
sleep(2)
|
369
|
+
graph.access_node(id: 1)
|
370
|
+
graph.access_node(id: 2)
|
371
|
+
|
372
|
+
sleep(2)
|
373
|
+
graph.forget_old_nodes
|
374
|
+
# At this point, node3 might be forgotten, while node1 and node2 are retained
|
375
|
+
```
|
376
|
+
|
377
|
+
This implementation introduces a `forgettable?` method that takes into account both the time
|
378
|
+
since last access and the detail level of the node. More detailed nodes (higher
|
379
|
+
`detail_level`) will be considered forgettable sooner than more general nodes.
|
380
|
+
|
381
|
+
Some considerations for this approach:
|
382
|
+
|
383
|
+
1. Balancing Act: You'll need to fine-tune the forgetting threshold and the relationship
|
384
|
+
between detail level and forgetting speed.
|
385
|
+
|
386
|
+
2. Contextual Importance: Some detailed nodes might be more important than others, regardless
|
387
|
+
of their detail level. You might want to introduce an "importance" factor.
|
388
|
+
|
389
|
+
3. Cascading Forgetfulness: When a node is forgotten, you might want to update or remove
|
390
|
+
references to it in connected nodes.
|
391
|
+
|
392
|
+
4. Relearning: Consider implementing a mechanism to "relearn" forgotten nodes if they become
|
393
|
+
relevant again.
|
394
|
+
|
395
|
+
5. Persistence: If you're storing this graph in a database, you'll need to implement periodic
|
396
|
+
cleanup operations to remove forgotten nodes.
|
397
|
+
|
398
|
+
This approach provides a starting point for implementing a temporal aspect to your knowledge
|
399
|
+
graph, where more detailed information is more susceptible to being forgotten over time. It's
|
400
|
+
a complex topic with many nuances, and the exact implementation would depend on your specific
|
401
|
+
use case and requirements.
|
402
|
+
|
403
|
+
|
404
|
+
[2024-12-15 19:11:27] INFO: Follow Up:
|
405
|
+
The reason that I am looking at HTM is for application in retrieval augmented generation RAG in which information consistent with an LLM prompt is retrieved semantically from a presistant data store for inclusion as context to the prompt. The size of the context windows is often limited with regard to the amount of information by token count that can be added. I am thinking that HTM with its ability to forget information over time might lead to better results better in this case may mean quick responses
|
406
|
+
|
407
|
+
|
408
|
+
[2024-12-15 19:11:27] INFO: Response:
|
409
|
+
Your insight about using HTM principles for Retrieval Augmented Generation (RAG) is quite
|
410
|
+
innovative and could indeed lead to more efficient and effective context retrieval. Let's
|
411
|
+
explore how this could work and how it might be implemented in Ruby.
|
412
|
+
|
413
|
+
The key advantages of this approach for RAG would be:
|
414
|
+
|
415
|
+
1. Temporal Relevance: More recent or frequently accessed information is prioritized.
|
416
|
+
2. Hierarchical Context: Ability to provide a mix of general and specific information within
|
417
|
+
token limits.
|
418
|
+
3. Adaptive Memory: The system can "forget" less relevant details, making room for new
|
419
|
+
information.
|
420
|
+
|
421
|
+
Here's a conceptual implementation in Ruby that combines HTM principles with RAG:
|
422
|
+
|
423
|
+
```ruby
|
424
|
+
require 'set'
|
425
|
+
|
426
|
+
class HTMNode
|
427
|
+
attr_reader :id, :content, :detail_level, :last_accessed, :access_count
|
428
|
+
|
429
|
+
def initialize(id:, content:, detail_level:)
|
430
|
+
@id = id
|
431
|
+
@content = content
|
432
|
+
@detail_level = detail_level
|
433
|
+
@last_accessed = Time.now
|
434
|
+
@access_count = 0
|
435
|
+
@connections = Set.new
|
436
|
+
end
|
437
|
+
|
438
|
+
def access
|
439
|
+
@last_accessed = Time.now
|
440
|
+
@access_count += 1
|
441
|
+
end
|
442
|
+
|
443
|
+
def add_connection(node)
|
444
|
+
@connections.add(node)
|
445
|
+
end
|
446
|
+
|
447
|
+
def relevance_score(current_time)
|
448
|
+
time_factor = 1.0 / (current_time - @last_accessed)
|
449
|
+
detail_factor = 1.0 / @detail_level
|
450
|
+
access_factor = Math.log(@access_count + 1)
|
451
|
+
time_factor * detail_factor * access_factor
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
class HTMRAG
|
456
|
+
def initialize(forget_threshold: 30 * 24 * 60 * 60) # 30 days in seconds
|
457
|
+
@nodes = {}
|
458
|
+
@forget_threshold = forget_threshold
|
459
|
+
end
|
460
|
+
|
461
|
+
def add_node(node)
|
462
|
+
@nodes[node.id] = node
|
463
|
+
end
|
464
|
+
|
465
|
+
def connect_nodes(from_id, to_id)
|
466
|
+
@nodes[from_id].add_connection(@nodes[to_id])
|
467
|
+
end
|
468
|
+
|
469
|
+
def forget_old_nodes
|
470
|
+
current_time = Time.now
|
471
|
+
@nodes.delete_if do |_, node|
|
472
|
+
current_time - node.last_accessed > @forget_threshold
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
def retrieve_context(query, max_tokens: 1000)
|
477
|
+
relevant_nodes = find_relevant_nodes(query)
|
478
|
+
construct_context(relevant_nodes, max_tokens)
|
479
|
+
end
|
480
|
+
|
481
|
+
private
|
482
|
+
|
483
|
+
def find_relevant_nodes(query)
|
484
|
+
# This is a placeholder for semantic search
|
485
|
+
# In a real implementation, you'd use embeddings or other NLP techniques
|
486
|
+
@nodes.values.sort_by { |node| -node.relevance_score(Time.now) }
|
487
|
+
end
|
488
|
+
|
489
|
+
def construct_context(nodes, max_tokens)
|
490
|
+
context = ""
|
491
|
+
token_count = 0
|
492
|
+
|
493
|
+
nodes.each do |node|
|
494
|
+
break if token_count >= max_tokens
|
495
|
+
|
496
|
+
node_content = node.content
|
497
|
+
node_tokens = node_content.split.size # Simple tokenization
|
498
|
+
|
499
|
+
if token_count + node_tokens <= max_tokens
|
500
|
+
context += node_content + " "
|
501
|
+
token_count += node_tokens
|
502
|
+
node.access
|
503
|
+
else
|
504
|
+
remaining_tokens = max_tokens - token_count
|
505
|
+
truncated_content = node_content.split[0...remaining_tokens].join(' ')
|
506
|
+
context += truncated_content + " "
|
507
|
+
break
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
context.strip
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# Usage example
|
516
|
+
rag = HTMRAG.new
|
517
|
+
|
518
|
+
node1 = HTMNode.new(id: 1, content: "AI is a broad field of computer science.", detail_level:
|
519
|
+
1)
|
520
|
+
node2 = HTMNode.new(id: 2, content: "Machine Learning is a subset of AI focused on data-driven
|
521
|
+
algorithms.", detail_level: 2)
|
522
|
+
node3 = HTMNode.new(id: 3, content: "Neural networks are a type of machine learning model
|
523
|
+
inspired by biological neurons.", detail_level: 3)
|
524
|
+
|
525
|
+
rag.add_node(node1)
|
526
|
+
rag.add_node(node2)
|
527
|
+
rag.add_node(node3)
|
528
|
+
|
529
|
+
rag.connect_nodes(1, 2)
|
530
|
+
rag.connect_nodes(2, 3)
|
531
|
+
|
532
|
+
context = rag.retrieve_context("What is AI?", max_tokens: 50)
|
533
|
+
puts context
|
534
|
+
```
|
535
|
+
|
536
|
+
This implementation incorporates several key HTM and RAG concepts:
|
537
|
+
|
538
|
+
1. Hierarchical Structure: Nodes have a `detail_level` attribute.
|
539
|
+
2. Temporal Aspect: Nodes track `last_accessed` time and `access_count`.
|
540
|
+
3. Relevance Scoring: Combines recency, detail level, and access frequency.
|
541
|
+
4. Forgetting Mechanism: Old nodes are removed based on a time threshold.
|
542
|
+
5. Context Construction: Retrieves and combines relevant nodes within token limits.
|
543
|
+
|
544
|
+
To make this more effective for RAG:
|
545
|
+
|
546
|
+
1. Semantic Search: Implement a proper semantic search for `find_relevant_nodes`, possibly
|
547
|
+
using embeddings.
|
548
|
+
2. Smarter Tokenization: Use a proper tokenizer that aligns with your LLM.
|
549
|
+
3. Adaptive Forgetting: Adjust the forgetting threshold based on system performance and data
|
550
|
+
volume.
|
551
|
+
4. Contextual Connections: Enhance the connection mechanism to capture semantic relationships
|
552
|
+
between nodes.
|
553
|
+
|
554
|
+
This approach could indeed lead to quicker responses in RAG systems by:
|
555
|
+
|
556
|
+
1. Prioritizing recent and frequently accessed information.
|
557
|
+
2. Maintaining a balance between general and specific information.
|
558
|
+
3. Automatically curating the knowledge base over time.
|
559
|
+
4. Allowing for faster retrieval by maintaining a more focused set of nodes.
|
560
|
+
|
561
|
+
Remember, the effectiveness of this system would heavily depend on the quality of your
|
562
|
+
semantic search and the relevance scoring mechanism. You might need to experiment with
|
563
|
+
different scoring formulas and parameters to optimize for your specific use case.
|
564
|
+
|
565
|
+
|
566
|
+
[2024-12-15 19:17:29] INFO: Follow Up:
|
567
|
+
Instead of the common vectorization of the prompt and a semantic search of the database for the nearest neighbor based upon the vectorization, I was thinking along the lines of breaking the prompt up into a knowledge graph of propositions. I could then chose from the database those existing graphcs in which their sequences mose closely resembles the sequences in the prompts graph. does that make sense?
|
568
|
+
|
569
|
+
|
570
|
+
[2024-12-15 19:17:29] INFO: Response:
|
571
|
+
That's a fascinating and innovative approach! It makes a lot of sense and could potentially
|
572
|
+
offer some significant advantages over traditional vector-based semantic search. Let's break
|
573
|
+
down this concept and explore how it might be implemented in Ruby.
|
574
|
+
|
575
|
+
The key ideas in your approach are:
|
576
|
+
|
577
|
+
1. Representing the prompt as a knowledge graph of propositions.
|
578
|
+
2. Comparing the structure and sequence of this graph to existing graphs in the database.
|
579
|
+
3. Selecting the most similar graphs for context retrieval.
|
580
|
+
|
581
|
+
This approach could indeed provide more nuanced and contextually relevant results, as it takes
|
582
|
+
into account the relationships and sequence of ideas, not just the overall semantic
|
583
|
+
similarity.
|
584
|
+
|
585
|
+
Here's a conceptual implementation in Ruby:
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
class Proposition
|
589
|
+
attr_reader :id, :content
|
590
|
+
attr_accessor :next, :related
|
591
|
+
|
592
|
+
def initialize(id:, content:)
|
593
|
+
@id = id
|
594
|
+
@content = content
|
595
|
+
@next = nil
|
596
|
+
@related = []
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
class PropositionGraph
|
601
|
+
attr_reader :propositions
|
602
|
+
|
603
|
+
def initialize
|
604
|
+
@propositions = {}
|
605
|
+
end
|
606
|
+
|
607
|
+
def add_proposition(prop)
|
608
|
+
@propositions[prop.id] = prop
|
609
|
+
end
|
610
|
+
|
611
|
+
def connect(from_id:, to_id:, relationship: :next)
|
612
|
+
from_prop = @propositions[from_id]
|
613
|
+
to_prop = @propositions[to_id]
|
614
|
+
if relationship == :next
|
615
|
+
from_prop.next = to_prop
|
616
|
+
else
|
617
|
+
from_prop.related << to_prop
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
def similarity_score(other_graph)
|
622
|
+
sequence_similarity = calculate_sequence_similarity(other_graph)
|
623
|
+
structure_similarity = calculate_structure_similarity(other_graph)
|
624
|
+
(sequence_similarity + structure_similarity) / 2.0
|
625
|
+
end
|
626
|
+
|
627
|
+
private
|
628
|
+
|
629
|
+
def calculate_sequence_similarity(other_graph)
|
630
|
+
# Implement sequence comparison logic here
|
631
|
+
# This could involve comparing the 'next' chains of both graphs
|
632
|
+
# Return a score between 0 and 1
|
633
|
+
end
|
634
|
+
|
635
|
+
def calculate_structure_similarity(other_graph)
|
636
|
+
# Implement structure comparison logic here
|
637
|
+
# This could involve comparing the 'related' connections of both graphs
|
638
|
+
# Return a score between 0 and 1
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
class GraphBasedRAG
|
643
|
+
def initialize
|
644
|
+
@stored_graphs = []
|
645
|
+
end
|
646
|
+
|
647
|
+
def add_graph(graph)
|
648
|
+
@stored_graphs << graph
|
649
|
+
end
|
650
|
+
|
651
|
+
def retrieve_context(prompt_graph, top_n: 3)
|
652
|
+
scored_graphs = @stored_graphs.map do |graph|
|
653
|
+
{
|
654
|
+
graph: graph,
|
655
|
+
score: graph.similarity_score(prompt_graph)
|
656
|
+
}
|
657
|
+
end
|
658
|
+
|
659
|
+
top_graphs = scored_graphs.sort_by { |g| -g[:score] }.take(top_n)
|
660
|
+
construct_context(top_graphs)
|
661
|
+
end
|
662
|
+
|
663
|
+
private
|
664
|
+
|
665
|
+
def construct_context(top_graphs)
|
666
|
+
# Implement logic to construct context from top graphs
|
667
|
+
# This could involve extracting key propositions or summarizing the graphs
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# Usage example
|
672
|
+
rag = GraphBasedRAG.new
|
673
|
+
|
674
|
+
# Create and add some stored graphs
|
675
|
+
graph1 = PropositionGraph.new
|
676
|
+
prop1 = Proposition.new(id: 1, content: "AI is a field of computer science")
|
677
|
+
prop2 = Proposition.new(id: 2, content: "Machine Learning is a subset of AI")
|
678
|
+
graph1.add_proposition(prop1)
|
679
|
+
graph1.add_proposition(prop2)
|
680
|
+
graph1.connect(from_id: 1, to_id: 2)
|
681
|
+
rag.add_graph(graph1)
|
682
|
+
|
683
|
+
# ... Add more graphs ...
|
684
|
+
|
685
|
+
# Create a graph from the prompt
|
686
|
+
prompt_graph = PropositionGraph.new
|
687
|
+
prompt_prop1 = Proposition.new(id: 1, content: "What is AI")
|
688
|
+
prompt_prop2 = Proposition.new(id: 2, content: "How does it relate to Machine Learning")
|
689
|
+
prompt_graph.add_proposition(prompt_prop1)
|
690
|
+
prompt_graph.add_proposition(prompt_prop2)
|
691
|
+
prompt_graph.connect(from_id: 1, to_id: 2)
|
692
|
+
|
693
|
+
# Retrieve context
|
694
|
+
context = rag.retrieve_context(prompt_graph)
|
695
|
+
puts context
|
696
|
+
```
|
697
|
+
|
698
|
+
This implementation provides a framework for your approach. Some key points:
|
699
|
+
|
700
|
+
1. `Proposition` represents individual ideas or statements.
|
701
|
+
2. `PropositionGraph` represents a network of related propositions.
|
702
|
+
3. The `similarity_score` method in `PropositionGraph` compares both sequence and structure.
|
703
|
+
4. `GraphBasedRAG` stores graphs and retrieves the most similar ones for a given prompt graph.
|
704
|
+
|
705
|
+
To make this system more effective:
|
706
|
+
|
707
|
+
1. Implement sophisticated graph comparison algorithms in `calculate_sequence_similarity` and
|
708
|
+
`calculate_structure_similarity`.
|
709
|
+
2. Develop a method to automatically convert natural language prompts into proposition graphs.
|
710
|
+
3. Optimize the storage and retrieval of graphs for large-scale applications.
|
711
|
+
4. Implement a method to extract or summarize relevant information from the retrieved graphs.
|
712
|
+
|
713
|
+
Advantages of this approach:
|
714
|
+
|
715
|
+
1. Captures relationships between ideas, not just individual concepts.
|
716
|
+
2. Can potentially handle complex queries more effectively.
|
717
|
+
3. Might be better at maintaining context and coherence in retrieved information.
|
718
|
+
|
719
|
+
Challenges to consider:
|
720
|
+
|
721
|
+
1. Complexity in converting natural language to proposition graphs.
|
722
|
+
2. Potentially higher computational cost for graph comparisons.
|
723
|
+
3. Need for sophisticated algorithms to handle graph similarity at scale.
|
724
|
+
|
725
|
+
This approach is innovative and has the potential to provide more contextually relevant
|
726
|
+
results for RAG systems, especially for complex queries where the relationship between
|
727
|
+
concepts is crucial. It would be particularly effective in domains where the sequence and
|
728
|
+
structure of information are as important as the content itself.
|
729
|
+
|
730
|
+
|
731
|
+
[2024-12-15 19:19:56] INFO: Follow Up:
|
732
|
+
are their graph-based extensions to PostgreSQL that we could use for this project?
|
733
|
+
|
734
|
+
|
735
|
+
[2024-12-15 19:19:56] INFO: Response:
|
736
|
+
Yes, there are indeed graph-based extensions for PostgreSQL that could be very useful for this
|
737
|
+
project. The most notable one is pgvector, which while not strictly graph-based, provides
|
738
|
+
vector operations that can be helpful in graph-like queries. However, for true graph
|
739
|
+
operations, we can look at a few options:
|
740
|
+
|
741
|
+
1. AGE (Apache AGE)
|
742
|
+
AGE (A Graph Extension) is an extension that adds graph database functionality to PostgreSQL.
|
743
|
+
It's based on the Cypher query language and supports property graphs.
|
744
|
+
|
745
|
+
To use AGE with Ruby, you'd typically use the pg gem and write Cypher queries as strings.
|
746
|
+
Here's a basic example:
|
747
|
+
|
748
|
+
```ruby
|
749
|
+
require 'pg'
|
750
|
+
|
751
|
+
class GraphDatabase
|
752
|
+
def initialize(dbname:, user:, password:)
|
753
|
+
@conn = PG.connect(dbname: dbname, user: user, password: password)
|
754
|
+
@conn.exec("CREATE EXTENSION IF NOT EXISTS age")
|
755
|
+
@conn.exec("LOAD 'age'")
|
756
|
+
@conn.exec("SET search_path = ag_catalog, \"$user\", public")
|
757
|
+
end
|
758
|
+
|
759
|
+
def create_graph(name)
|
760
|
+
@conn.exec("SELECT create_graph('#{name}')")
|
761
|
+
end
|
762
|
+
|
763
|
+
def add_vertex(graph, properties)
|
764
|
+
props = properties.map { |k, v| "#{k}: '#{v}'" }.join(", ")
|
765
|
+
@conn.exec("SELECT * FROM cypher('#{graph}', $$ CREATE (:Proposition {#{props}}) $$) as (v
|
766
|
+
agtype)")
|
767
|
+
end
|
768
|
+
|
769
|
+
def add_edge(graph, from_id, to_id, relationship)
|
770
|
+
@conn.exec("SELECT * FROM cypher('#{graph}', $$ MATCH (a), (b) WHERE id(a) = #{from_id} AND
|
771
|
+
id(b) = #{to_id} CREATE (a)-[:#{relationship}]->(b) $$) as (e agtype)")
|
772
|
+
end
|
773
|
+
|
774
|
+
def query(graph, cypher)
|
775
|
+
result = @conn.exec("SELECT * FROM cypher('#{graph}', $$ #{cypher} $$) as (result agtype)")
|
776
|
+
result.map { |row| row['result'] }
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
# Usage
|
781
|
+
db = GraphDatabase.new(dbname: 'your_db', user: 'your_user', password: 'your_password')
|
782
|
+
db.create_graph('knowledge_graph')
|
783
|
+
db.add_vertex('knowledge_graph', {content: "AI is a field of computer science", detail_level:
|
784
|
+
1})
|
785
|
+
db.add_vertex('knowledge_graph', {content: "Machine Learning is a subset of AI", detail_level:
|
786
|
+
2})
|
787
|
+
db.add_edge('knowledge_graph', 0, 1, 'SPECIALIZES')
|
788
|
+
|
789
|
+
result = db.query('knowledge_graph', "MATCH (p:Proposition) RETURN p")
|
790
|
+
puts result
|
791
|
+
```
|
792
|
+
|
793
|
+
2. pggraph
|
794
|
+
pggraph is another extension that adds graph functionality to PostgreSQL. It's less
|
795
|
+
feature-rich than AGE but might be simpler for some use cases.
|
796
|
+
|
797
|
+
3. pgRouting
|
798
|
+
If your graph operations involve pathfinding or network analysis, pgRouting could be useful.
|
799
|
+
It's primarily designed for geospatial routing but can be adapted for other types of graphs.
|
800
|
+
|
801
|
+
For our specific use case of proposition graphs and RAG, AGE would likely be the most suitable
|
802
|
+
option. It allows for complex graph structures and queries, which aligns well with the idea of
|
803
|
+
representing and querying proposition graphs.
|
804
|
+
|
805
|
+
Here's how you might implement some of the core functionality using AGE:
|
806
|
+
|
807
|
+
```ruby
|
808
|
+
class PropositionGraphDatabase
|
809
|
+
def initialize(dbname:, user:, password:)
|
810
|
+
@db = GraphDatabase.new(dbname: dbname, user: user, password: password)
|
811
|
+
@db.create_graph('proposition_graph')
|
812
|
+
end
|
813
|
+
|
814
|
+
def add_proposition(content:, detail_level:)
|
815
|
+
@db.add_vertex('proposition_graph', {content: content, detail_level: detail_level})
|
816
|
+
end
|
817
|
+
|
818
|
+
def connect_propositions(from_id:, to_id:, relationship: 'NEXT')
|
819
|
+
@db.add_edge('proposition_graph', from_id, to_id, relationship)
|
820
|
+
end
|
821
|
+
|
822
|
+
def find_similar_graphs(prompt_graph)
|
823
|
+
# This is a simplified example. In practice, you'd need a more sophisticated
|
824
|
+
# algorithm to compare graph structures.
|
825
|
+
cypher = """
|
826
|
+
MATCH path = (start:Proposition)-[:NEXT*]->(end:Proposition)
|
827
|
+
WHERE NOT (end)-[:NEXT]->()
|
828
|
+
WITH path, nodes(path) AS props
|
829
|
+
RETURN path,
|
830
|
+
reduce(similarity = 0, p IN props |
|
831
|
+
similarity + CASE WHEN p.content CONTAINS '#{prompt_graph.first.content}' THEN 1 ELSE 0 END
|
832
|
+
) AS similarity_score
|
833
|
+
ORDER BY similarity_score DESC
|
834
|
+
LIMIT 5
|
835
|
+
"""
|
836
|
+
@db.query('proposition_graph', cypher)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
# Usage
|
841
|
+
graph_db = PropositionGraphDatabase.new(dbname: 'your_db', user: 'your_user', password:
|
842
|
+
'your_password')
|
843
|
+
graph_db.add_proposition(content: "AI is a field of computer science", detail_level: 1)
|
844
|
+
graph_db.add_proposition(content: "Machine Learning is a subset of AI", detail_level: 2)
|
845
|
+
graph_db.connect_propositions(from_id: 0, to_id: 1)
|
846
|
+
|
847
|
+
prompt_graph = [OpenStruct.new(content: "What is AI")]
|
848
|
+
similar_graphs = graph_db.find_similar_graphs(prompt_graph)
|
849
|
+
puts similar_graphs
|
850
|
+
```
|
851
|
+
|
852
|
+
This approach allows you to leverage PostgreSQL's robustness and scalability while gaining
|
853
|
+
graph database capabilities. It's particularly well-suited for your proposition graph idea, as
|
854
|
+
it can represent complex relationships between propositions and allow for sophisticated
|
855
|
+
querying based on graph structure.
|
856
|
+
|
857
|
+
Remember that you'll need to install and configure the AGE extension on your PostgreSQL server
|
858
|
+
before using it. Also, for production use, you'd want to implement more sophisticated graph
|
859
|
+
comparison algorithms and possibly combine this with vector embeddings for even more accurate
|
860
|
+
similarity matching.
|
861
|
+
|
862
|
+
|
863
|
+
[2024-12-15 19:27:00] INFO: Follow Up:
|
864
|
+
do you know of a goo LLM that would take a block of text for example a paragraph and great a knowledge graph from that text.
|
865
|
+
|
866
|
+
|
867
|
+
[2024-12-15 19:27:00] INFO: Response:
|
868
|
+
Yes, there are several large language models (LLMs) that can be used to generate knowledge
|
869
|
+
graphs from text. One of the most capable and widely used models for this task is GPT-4 from
|
870
|
+
OpenAI. While it doesn't directly output a structured knowledge graph, it can be prompted to
|
871
|
+
generate the components of a knowledge graph, which you can then parse and structure.
|
872
|
+
|
873
|
+
Here's an example of how you might use GPT-4 (or a similar model) to generate a knowledge
|
874
|
+
graph from a paragraph of text, and then process the output in Ruby:
|
875
|
+
|
876
|
+
```ruby
|
877
|
+
require 'openai'
|
878
|
+
require 'json'
|
879
|
+
|
880
|
+
class KnowledgeGraphGenerator
|
881
|
+
def initialize(api_key)
|
882
|
+
@client = OpenAI::Client.new(access_token: api_key)
|
883
|
+
end
|
884
|
+
|
885
|
+
def generate_graph(text)
|
886
|
+
prompt = <<~PROMPT
|
887
|
+
Given the following paragraph, create a knowledge graph.
|
888
|
+
Output the result as a JSON object with the following structure:
|
889
|
+
{
|
890
|
+
"nodes": [
|
891
|
+
{"id": "unique_id", "label": "concept or entity", "type": "entity_type"}
|
892
|
+
],
|
893
|
+
"edges": [
|
894
|
+
{"source": "source_id", "target": "target_id", "label": "relationship"}
|
895
|
+
]
|
896
|
+
}
|
897
|
+
|
898
|
+
Paragraph:
|
899
|
+
#{text}
|
900
|
+
|
901
|
+
JSON Knowledge Graph:
|
902
|
+
PROMPT
|
903
|
+
|
904
|
+
response = @client.chat(
|
905
|
+
parameters: {
|
906
|
+
model: "gpt-4", # or "gpt-3.5-turbo" if you don't have GPT-4 access
|
907
|
+
messages: [{ role: "user", content: prompt }],
|
908
|
+
temperature: 0.7,
|
909
|
+
}
|
910
|
+
)
|
911
|
+
|
912
|
+
JSON.parse(response.dig("choices", 0, "message", "content"))
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
class KnowledgeGraph
|
917
|
+
attr_reader :nodes, :edges
|
918
|
+
|
919
|
+
def initialize(data)
|
920
|
+
@nodes = data["nodes"]
|
921
|
+
@edges = data["edges"]
|
922
|
+
end
|
923
|
+
|
924
|
+
def to_s
|
925
|
+
"Nodes:\n" + @nodes.map { |n| " #{n['id']}: #{n['label']} (#{n['type']})" }.join("\n") +
|
926
|
+
"\nEdges:\n" + @edges.map { |e| " #{e['source']} -> #{e['target']}: #{e['label']}"
|
927
|
+
}.join("\n")
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
931
|
+
# Usage
|
932
|
+
generator = KnowledgeGraphGenerator.new('your-openai-api-key')
|
933
|
+
|
934
|
+
text = "Artificial Intelligence (AI) is a broad field of computer science focused on creating
|
935
|
+
intelligent machines that can perform tasks that typically require human intelligence. Machine
|
936
|
+
Learning, a subset of AI, uses statistical techniques to give computer systems the ability to
|
937
|
+
'learn' from data, without being explicitly programmed. Deep Learning, a further
|
938
|
+
specialization of Machine Learning, uses neural networks with many layers (hence 'deep') to
|
939
|
+
analyze various factors of data."
|
940
|
+
|
941
|
+
graph_data = generator.generate_graph(text)
|
942
|
+
graph = KnowledgeGraph.new(graph_data)
|
943
|
+
|
944
|
+
puts graph
|
945
|
+
```
|
946
|
+
|
947
|
+
This script does the following:
|
948
|
+
|
949
|
+
1. It uses the OpenAI API to send a prompt to GPT-4, asking it to generate a knowledge graph
|
950
|
+
from the given text.
|
951
|
+
2. The prompt specifies the desired output format (a JSON object with nodes and edges).
|
952
|
+
3. The response from GPT-4 is parsed as JSON.
|
953
|
+
4. A `KnowledgeGraph` class is used to represent the graph structure and provide a simple
|
954
|
+
string representation.
|
955
|
+
|
956
|
+
When you run this script, it will output something like:
|
957
|
+
|
958
|
+
```
|
959
|
+
Nodes:
|
960
|
+
1: Artificial Intelligence (AI) (field)
|
961
|
+
2: Computer Science (field)
|
962
|
+
3: Intelligent Machines (concept)
|
963
|
+
4: Human Intelligence (concept)
|
964
|
+
5: Machine Learning (subfield)
|
965
|
+
6: Statistical Techniques (method)
|
966
|
+
7: Computer Systems (technology)
|
967
|
+
8: Data (concept)
|
968
|
+
9: Deep Learning (subfield)
|
969
|
+
10: Neural Networks (technology)
|
970
|
+
Edges:
|
971
|
+
1 -> 2: is part of
|
972
|
+
1 -> 3: focuses on creating
|
973
|
+
3 -> 4: perform tasks requiring
|
974
|
+
5 -> 1: is subset of
|
975
|
+
5 -> 6: uses
|
976
|
+
6 -> 7: give ability to
|
977
|
+
7 -> 8: learn from
|
978
|
+
9 -> 5: is specialization of
|
979
|
+
9 -> 10: uses
|
980
|
+
10 -> 8: analyze
|
981
|
+
```
|
982
|
+
|
983
|
+
This approach allows you to generate knowledge graphs from text in a flexible way. However,
|
984
|
+
keep in mind:
|
985
|
+
|
986
|
+
1. The quality and structure of the generated graph will depend on the capabilities of the LLM
|
987
|
+
and how well you craft the prompt.
|
988
|
+
2. You may need to post-process the LLM output to ensure consistency or to adapt it to your
|
989
|
+
specific needs.
|
990
|
+
3. This method may not be suitable for processing large volumes of text due to API rate limits
|
991
|
+
and costs.
|
992
|
+
4. For production use, you'd want to add error handling and possibly implement retries for API
|
993
|
+
calls.
|
994
|
+
|
995
|
+
For more specialized or consistent results, you might consider fine-tuning a model on
|
996
|
+
knowledge graph generation tasks, or using a dedicated NLP pipeline with named entity
|
997
|
+
recognition and relationship extraction components.
|
998
|
+
|
999
|
+
|
1000
|
+
[2024-12-15 19:31:08] INFO: Follow Up:
|
1001
|
+
Lets do an exercise. I'm going to give you some text and I would like you to give back to me a represention of that text as a knowledge graph. The text is "As a resident of Bossier City, LA I am not allowed to drive my 2021 Tahoe LS because of my poor eyesight."
|
1002
|
+
|
1003
|
+
|
1004
|
+
[2024-12-15 19:31:08] INFO: Response:
|
1005
|
+
Certainly! I'll represent this text as a knowledge graph for you. I'll provide the graph
|
1006
|
+
structure in a JSON format, which can be easily parsed and used in Ruby or other programming
|
1007
|
+
languages.
|
1008
|
+
|
1009
|
+
```json
|
1010
|
+
{
|
1011
|
+
"nodes": [
|
1012
|
+
{
|
1013
|
+
"id": "1",
|
1014
|
+
"label": "Resident",
|
1015
|
+
"type": "Person"
|
1016
|
+
},
|
1017
|
+
{
|
1018
|
+
"id": "2",
|
1019
|
+
"label": "Bossier City",
|
1020
|
+
"type": "Location"
|
1021
|
+
},
|
1022
|
+
{
|
1023
|
+
"id": "3",
|
1024
|
+
"label": "Louisiana",
|
1025
|
+
"type": "Location"
|
1026
|
+
},
|
1027
|
+
{
|
1028
|
+
"id": "4",
|
1029
|
+
"label": "2021 Tahoe LS",
|
1030
|
+
"type": "Vehicle"
|
1031
|
+
},
|
1032
|
+
{
|
1033
|
+
"id": "5",
|
1034
|
+
"label": "Poor eyesight",
|
1035
|
+
"type": "Condition"
|
1036
|
+
},
|
1037
|
+
{
|
1038
|
+
"id": "6",
|
1039
|
+
"label": "Driving restriction",
|
1040
|
+
"type": "Regulation"
|
1041
|
+
}
|
1042
|
+
],
|
1043
|
+
"edges": [
|
1044
|
+
{
|
1045
|
+
"source": "1",
|
1046
|
+
"target": "2",
|
1047
|
+
"label": "resides in"
|
1048
|
+
},
|
1049
|
+
{
|
1050
|
+
"source": "2",
|
1051
|
+
"target": "3",
|
1052
|
+
"label": "located in"
|
1053
|
+
},
|
1054
|
+
{
|
1055
|
+
"source": "1",
|
1056
|
+
"target": "4",
|
1057
|
+
"label": "owns"
|
1058
|
+
},
|
1059
|
+
{
|
1060
|
+
"source": "1",
|
1061
|
+
"target": "5",
|
1062
|
+
"label": "has condition"
|
1063
|
+
},
|
1064
|
+
{
|
1065
|
+
"source": "1",
|
1066
|
+
"target": "6",
|
1067
|
+
"label": "subject to"
|
1068
|
+
},
|
1069
|
+
{
|
1070
|
+
"source": "6",
|
1071
|
+
"target": "4",
|
1072
|
+
"label": "applies to"
|
1073
|
+
},
|
1074
|
+
{
|
1075
|
+
"source": "5",
|
1076
|
+
"target": "6",
|
1077
|
+
"label": "causes"
|
1078
|
+
}
|
1079
|
+
]
|
1080
|
+
}
|
1081
|
+
```
|
1082
|
+
|
1083
|
+
This knowledge graph representation captures the key entities and relationships in the given
|
1084
|
+
text. Here's a breakdown of the graph:
|
1085
|
+
|
1086
|
+
1. Nodes represent the main entities and concepts:
|
1087
|
+
- The resident (Person)
|
1088
|
+
- Bossier City and Louisiana (Locations)
|
1089
|
+
- 2021 Tahoe LS (Vehicle)
|
1090
|
+
- Poor eyesight (Condition)
|
1091
|
+
- Driving restriction (Regulation)
|
1092
|
+
|
1093
|
+
2. Edges represent the relationships between these entities:
|
1094
|
+
- The resident resides in Bossier City, which is located in Louisiana
|
1095
|
+
- The resident owns the 2021 Tahoe LS
|
1096
|
+
- The resident has the condition of poor eyesight
|
1097
|
+
- The resident is subject to a driving restriction
|
1098
|
+
- The driving restriction applies to the 2021 Tahoe LS
|
1099
|
+
- The poor eyesight causes the driving restriction
|
1100
|
+
|
1101
|
+
This structure allows for querying and reasoning about the relationships between the entities.
|
1102
|
+
For example, you can easily trace why the resident can't drive their vehicle (poor eyesight
|
1103
|
+
leading to a driving restriction).
|
1104
|
+
|
1105
|
+
To use this in Ruby, you could parse the JSON and create a graph structure similar to the one
|
1106
|
+
we discussed earlier:
|
1107
|
+
|
1108
|
+
```ruby
|
1109
|
+
require 'json'
|
1110
|
+
|
1111
|
+
class KnowledgeGraph
|
1112
|
+
attr_reader :nodes, :edges
|
1113
|
+
|
1114
|
+
def initialize(data)
|
1115
|
+
@nodes = data["nodes"]
|
1116
|
+
@edges = data["edges"]
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def to_s
|
1120
|
+
"Nodes:\n" + @nodes.map { |n| " #{n['id']}: #{n['label']} (#{n['type']})" }.join("\n") +
|
1121
|
+
"\nEdges:\n" + @edges.map { |e| " #{e['source']} -> #{e['target']}: #{e['label']}"
|
1122
|
+
}.join("\n")
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
# Assuming the JSON is stored in a variable called 'json_data'
|
1127
|
+
graph_data = JSON.parse(json_data)
|
1128
|
+
graph = KnowledgeGraph.new(graph_data)
|
1129
|
+
|
1130
|
+
puts graph
|
1131
|
+
```
|
1132
|
+
|
1133
|
+
This would give you a readable representation of the knowledge graph derived from the text.
|
1134
|
+
|
1135
|
+
|
1136
|
+
[2024-12-15 19:47:47] INFO: Follow Up:
|
1137
|
+
Is there a way that I can display the knowledge graph graphically within a rails application
|
1138
|
+
|
1139
|
+
|
1140
|
+
[2024-12-15 19:47:47] INFO: Response:
|
1141
|
+
Absolutely! There are several ways to display a knowledge graph graphically within a Rails
|
1142
|
+
application. One popular and effective approach is to use a JavaScript library for graph
|
1143
|
+
visualization. D3.js is a powerful option, but for simplicity and ease of use, we'll go with
|
1144
|
+
vis.js, which is particularly well-suited for network graphs.
|
1145
|
+
|
1146
|
+
Here's a step-by-step guide to implement this in your Rails application:
|
1147
|
+
|
1148
|
+
1. Add the vis-network library to your Rails application:
|
1149
|
+
|
1150
|
+
Add this line to your `app/javascript/application.js` file:
|
1151
|
+
|
1152
|
+
```javascript
|
1153
|
+
import "vis-network/styles/vis-network.css";
|
1154
|
+
import { Network } from "vis-network/standalone";
|
1155
|
+
```
|
1156
|
+
|
1157
|
+
And add this to your `config/importmap.rb`:
|
1158
|
+
|
1159
|
+
```ruby
|
1160
|
+
pin "vis-network", to:
|
1161
|
+
"https://ga.jspm.io/npm:vis-network@9.1.6/standalone/esm/vis-network.js"
|
1162
|
+
```
|
1163
|
+
|
1164
|
+
2. Create a controller action to serve the graph data:
|
1165
|
+
|
1166
|
+
```ruby
|
1167
|
+
# app/controllers/knowledge_graphs_controller.rb
|
1168
|
+
class KnowledgeGraphsController < ApplicationController
|
1169
|
+
def show
|
1170
|
+
@graph_data = {
|
1171
|
+
nodes: [
|
1172
|
+
{ id: 1, label: "Resident", group: "Person" },
|
1173
|
+
{ id: 2, label: "Bossier City", group: "Location" },
|
1174
|
+
{ id: 3, label: "Louisiana", group: "Location" },
|
1175
|
+
{ id: 4, label: "2021 Tahoe LS", group: "Vehicle" },
|
1176
|
+
{ id: 5, label: "Poor eyesight", group: "Condition" },
|
1177
|
+
{ id: 6, label: "Driving restriction", group: "Regulation" }
|
1178
|
+
],
|
1179
|
+
edges: [
|
1180
|
+
{ from: 1, to: 2, label: "resides in" },
|
1181
|
+
{ from: 2, to: 3, label: "located in" },
|
1182
|
+
{ from: 1, to: 4, label: "owns" },
|
1183
|
+
{ from: 1, to: 5, label: "has condition" },
|
1184
|
+
{ from: 1, to: 6, label: "subject to" },
|
1185
|
+
{ from: 6, to: 4, label: "applies to" },
|
1186
|
+
{ from: 5, to: 6, label: "causes" }
|
1187
|
+
]
|
1188
|
+
}
|
1189
|
+
end
|
1190
|
+
end
|
1191
|
+
```
|
1192
|
+
|
1193
|
+
3. Create a view to display the graph:
|
1194
|
+
|
1195
|
+
```erb
|
1196
|
+
<!-- app/views/knowledge_graphs/show.html.erb -->
|
1197
|
+
<div id="knowledge-graph" style="width: 100%; height: 600px;"></div>
|
1198
|
+
|
1199
|
+
<%= javascript_tag do %>
|
1200
|
+
document.addEventListener('DOMContentLoaded', function() {
|
1201
|
+
var container = document.getElementById('knowledge-graph');
|
1202
|
+
var data = {
|
1203
|
+
nodes: new vis.DataSet(<%= raw @graph_data[:nodes].to_json %>),
|
1204
|
+
edges: new vis.DataSet(<%= raw @graph_data[:edges].to_json %>)
|
1205
|
+
};
|
1206
|
+
var options = {
|
1207
|
+
nodes: {
|
1208
|
+
shape: 'dot',
|
1209
|
+
size: 16
|
1210
|
+
},
|
1211
|
+
physics: {
|
1212
|
+
forceAtlas2Based: {
|
1213
|
+
gravitationalConstant: -26,
|
1214
|
+
centralGravity: 0.005,
|
1215
|
+
springLength: 230,
|
1216
|
+
springConstant: 0.18
|
1217
|
+
},
|
1218
|
+
maxVelocity: 146,
|
1219
|
+
solver: 'forceAtlas2Based',
|
1220
|
+
timestep: 0.35,
|
1221
|
+
stabilization: { iterations: 150 }
|
1222
|
+
}
|
1223
|
+
};
|
1224
|
+
var network = new vis.Network(container, data, options);
|
1225
|
+
});
|
1226
|
+
<% end %>
|
1227
|
+
```
|
1228
|
+
|
1229
|
+
4. Add a route for the knowledge graph:
|
1230
|
+
|
1231
|
+
```ruby
|
1232
|
+
# config/routes.rb
|
1233
|
+
Rails.application.routes.draw do
|
1234
|
+
get 'knowledge_graph', to: 'knowledge_graphs#show'
|
1235
|
+
# ... other routes ...
|
1236
|
+
end
|
1237
|
+
```
|
1238
|
+
|
1239
|
+
5. Style the graph (optional):
|
1240
|
+
|
1241
|
+
You can add custom styles to make the graph more visually appealing. Add this to your CSS
|
1242
|
+
(e.g., in `app/assets/stylesheets/application.css`):
|
1243
|
+
|
1244
|
+
```css
|
1245
|
+
#knowledge-graph {
|
1246
|
+
border: 1px solid #ddd;
|
1247
|
+
border-radius: 4px;
|
1248
|
+
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
1249
|
+
}
|
1250
|
+
```
|
1251
|
+
|
1252
|
+
Now, when you visit `/knowledge_graph` in your Rails application, you should see a graphical
|
1253
|
+
representation of the knowledge graph.
|
1254
|
+
|
1255
|
+
To make this more dynamic:
|
1256
|
+
|
1257
|
+
1. You could store your graph data in the database and retrieve it in the controller.
|
1258
|
+
2. You could create a form that allows users to input text, which is then processed (possibly
|
1259
|
+
using an LLM as discussed earlier) to generate the graph data.
|
1260
|
+
|
1261
|
+
Here's a quick example of how you might do that:
|
1262
|
+
|
1263
|
+
```ruby
|
1264
|
+
# app/controllers/knowledge_graphs_controller.rb
|
1265
|
+
class KnowledgeGraphsController < ApplicationController
|
1266
|
+
def new
|
1267
|
+
@knowledge_graph = KnowledgeGraph.new
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
def create
|
1271
|
+
@knowledge_graph = KnowledgeGraph.new(knowledge_graph_params)
|
1272
|
+
if @knowledge_graph.save
|
1273
|
+
redirect_to @knowledge_graph
|
1274
|
+
else
|
1275
|
+
render :new
|
1276
|
+
end
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
def show
|
1280
|
+
@knowledge_graph = KnowledgeGraph.find(params[:id])
|
1281
|
+
@graph_data = JSON.parse(@knowledge_graph.graph_data)
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
private
|
1285
|
+
|
1286
|
+
def knowledge_graph_params
|
1287
|
+
params.require(:knowledge_graph).permit(:input_text)
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
```
|
1291
|
+
|
1292
|
+
You would need to create a `KnowledgeGraph` model that stores the input text and the generated
|
1293
|
+
graph data. The model could include a method to generate the graph data from the input text
|
1294
|
+
(possibly using an LLM API).
|
1295
|
+
|
1296
|
+
This setup allows for a more interactive and dynamic knowledge graph visualization in your
|
1297
|
+
Rails application.
|
1298
|
+
|
1299
|
+
|
1300
|
+
[2024-12-15 19:49:12] INFO: Follow Up:
|
1301
|
+
thank you I believe we have covered the wubject well enough.
|
1302
|
+
|
1303
|
+
|
1304
|
+
[2024-12-15 19:49:12] INFO: Response:
|
1305
|
+
You're welcome! I'm glad we were able to cover the subject of knowledge graphs, their
|
1306
|
+
implementation in Ruby and Rails, and their visualization to your satisfaction. This
|
1307
|
+
exploration touched on several important aspects:
|
1308
|
+
|
1309
|
+
1. The concept of knowledge graphs and their representation
|
1310
|
+
2. Using LLMs to generate knowledge graphs from text
|
1311
|
+
3. Implementing knowledge graph structures in Ruby
|
1312
|
+
4. Visualizing knowledge graphs in a Rails application using JavaScript libraries
|
1313
|
+
|
1314
|
+
These concepts provide a solid foundation for working with knowledge graphs in a Ruby on Rails
|
1315
|
+
environment. If you decide to implement this in a project, you'll have a good starting point
|
1316
|
+
for representing, manipulating, and visualizing complex information structures.
|
1317
|
+
|
1318
|
+
If you have any more questions in the future about this topic or any related areas, feel free
|
1319
|
+
to ask. Good luck with your work involving knowledge graphs!
|