superinstance-equipment-consensus-engine 1.0.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +448 -0
- data/lib/equipment/consensus_engine/conflict_resolution.rb +507 -0
- data/lib/equipment/consensus_engine/consensus_engine.rb +451 -0
- data/lib/equipment/consensus_engine/tripartite_deliberation.rb +645 -0
- data/lib/equipment/consensus_engine/types.rb +229 -0
- data/lib/equipment/consensus_engine/version.rb +9 -0
- data/lib/equipment/consensus_engine/weight_calculator.rb +438 -0
- data/lib/equipment/consensus_engine.rb +17 -0
- metadata +65 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'types'
|
|
4
|
+
|
|
5
|
+
module SuperInstance
|
|
6
|
+
module Equipment
|
|
7
|
+
module ConsensusEngine
|
|
8
|
+
# ConflictResolution - Resolve disagreements between perspectives
|
|
9
|
+
#
|
|
10
|
+
# Implements strategies for detecting and resolving conflicts when
|
|
11
|
+
# Pathos, Logos, and Ethos perspectives disagree on a proposition.
|
|
12
|
+
class ConflictResolution
|
|
13
|
+
# Default resolution configuration
|
|
14
|
+
DEFAULT_CONFIG = {
|
|
15
|
+
max_attempts: 3,
|
|
16
|
+
min_confidence: 0.6,
|
|
17
|
+
allow_escalation: true
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# Creates a new ConflictResolution instance
|
|
21
|
+
# @param config [Hash] Optional configuration overrides
|
|
22
|
+
def initialize(config = {})
|
|
23
|
+
@config = DEFAULT_CONFIG.merge(config)
|
|
24
|
+
@resolution_history = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Resolves a conflict between perspectives
|
|
28
|
+
# @param conflict [Hash] The conflict to resolve
|
|
29
|
+
# @param opinions [Array<Hash>] Current perspective opinions
|
|
30
|
+
# @return [Hash] The resolution result
|
|
31
|
+
def resolve(conflict, opinions)
|
|
32
|
+
# Select appropriate resolution strategy based on conflict type
|
|
33
|
+
strategy = select_strategy(conflict, opinions)
|
|
34
|
+
|
|
35
|
+
# Apply the selected strategy
|
|
36
|
+
result = apply_strategy(strategy, conflict, opinions)
|
|
37
|
+
|
|
38
|
+
# If resolution failed, try alternative strategies
|
|
39
|
+
attempts = 1
|
|
40
|
+
while !result[:resolved] && attempts < @config[:max_attempts]
|
|
41
|
+
alternative_strategy = select_alternative_strategy(strategy, conflict, result)
|
|
42
|
+
result = apply_strategy(alternative_strategy, conflict, opinions)
|
|
43
|
+
attempts += 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Record the resolution attempt
|
|
47
|
+
@resolution_history << {
|
|
48
|
+
conflict: conflict,
|
|
49
|
+
result: result,
|
|
50
|
+
timestamp: Time.now
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Gets the resolution history
|
|
57
|
+
# @return [Array<Hash>] Resolution history
|
|
58
|
+
def get_history
|
|
59
|
+
@resolution_history.dup
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Clears the resolution history
|
|
63
|
+
def clear_history
|
|
64
|
+
@resolution_history.clear
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Gets statistics about resolutions
|
|
68
|
+
# @return [Hash] Resolution statistics
|
|
69
|
+
def get_stats
|
|
70
|
+
total = @resolution_history.length
|
|
71
|
+
successful = @resolution_history.count { |r| r[:result][:resolved] }
|
|
72
|
+
|
|
73
|
+
strategy_usage = Hash.new(0)
|
|
74
|
+
total_confidence = 0.0
|
|
75
|
+
|
|
76
|
+
@resolution_history.each do |entry|
|
|
77
|
+
strategy_usage[entry[:result][:strategy]] += 1
|
|
78
|
+
total_confidence += entry[:result][:confidence]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
total_resolutions: total,
|
|
83
|
+
successful_resolutions: successful,
|
|
84
|
+
strategy_usage: strategy_usage,
|
|
85
|
+
average_confidence: total > 0 ? total_confidence / total : 0
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Selects the best resolution strategy for a conflict
|
|
92
|
+
def select_strategy(conflict, opinions)
|
|
93
|
+
# Strategy selection based on conflict type
|
|
94
|
+
strategy_map = {
|
|
95
|
+
fundamental_disagreement: %i[compromise reframing escalation],
|
|
96
|
+
uncertainty: %i[deliberation_extension suspension],
|
|
97
|
+
partial_disagreement: %i[weighted_voting conditional_approval],
|
|
98
|
+
weight_imbalance: %i[perspective_dominance reframing],
|
|
99
|
+
cross_perspective_tension: %i[compromise deliberation_extension],
|
|
100
|
+
context_inconsistency: %i[reframing deliberation_extension]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
strategies = strategy_map[conflict[:type]] || %i[weighted_voting]
|
|
104
|
+
|
|
105
|
+
# Consider severity
|
|
106
|
+
if conflict[:severity] == :critical && @config[:allow_escalation]
|
|
107
|
+
return :escalation
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if conflict[:severity] == :high
|
|
111
|
+
# Use more deliberative strategies for high severity
|
|
112
|
+
deliberative_strategy = strategies.find do |s|
|
|
113
|
+
s == :compromise || s == :deliberation_extension
|
|
114
|
+
end
|
|
115
|
+
return deliberative_strategy if deliberative_strategy
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Use first available strategy
|
|
119
|
+
strategies[0] || :weighted_voting
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Selects an alternative strategy when primary fails
|
|
123
|
+
def select_alternative_strategy(failed_strategy, conflict, previous_result)
|
|
124
|
+
preferred_strategies = %i[
|
|
125
|
+
weighted_voting compromise conditional_approval
|
|
126
|
+
deliberation_extension reframing escalation
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
current_index = preferred_strategies.index(failed_strategy)
|
|
130
|
+
next_index = current_index + 1
|
|
131
|
+
|
|
132
|
+
if next_index < preferred_strategies.length
|
|
133
|
+
return preferred_strategies[next_index]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# If we've tried all preferred strategies, use escalation as last resort
|
|
137
|
+
return :escalation if @config[:allow_escalation]
|
|
138
|
+
|
|
139
|
+
:suspension
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Applies a specific resolution strategy
|
|
143
|
+
def apply_strategy(strategy, conflict, opinions)
|
|
144
|
+
case strategy
|
|
145
|
+
when :weighted_voting
|
|
146
|
+
apply_weighted_voting(conflict, opinions)
|
|
147
|
+
when :deliberation_extension
|
|
148
|
+
apply_deliberation_extension(conflict, opinions)
|
|
149
|
+
when :reframing
|
|
150
|
+
apply_reframing(conflict, opinions)
|
|
151
|
+
when :escalation
|
|
152
|
+
apply_escalation(conflict, opinions)
|
|
153
|
+
when :compromise
|
|
154
|
+
apply_compromise(conflict, opinions)
|
|
155
|
+
when :conditional_approval
|
|
156
|
+
apply_conditional_approval(conflict, opinions)
|
|
157
|
+
when :suspension
|
|
158
|
+
apply_suspension(conflict, opinions)
|
|
159
|
+
when :perspective_dominance
|
|
160
|
+
apply_perspective_dominance(conflict, opinions)
|
|
161
|
+
else
|
|
162
|
+
create_unresolved_result(strategy, conflict)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Applies weighted voting strategy
|
|
167
|
+
def apply_weighted_voting(conflict, opinions)
|
|
168
|
+
# Calculate weighted scores
|
|
169
|
+
positive_score = 0.0
|
|
170
|
+
negative_score = 0.0
|
|
171
|
+
|
|
172
|
+
opinions.each do |opinion|
|
|
173
|
+
verdict = opinion[:verdict].downcase
|
|
174
|
+
is_positive = VerdictIndicators::POSITIVE.any? { |p| verdict.include?(p) }
|
|
175
|
+
is_negative = VerdictIndicators::NEGATIVE.any? { |n| verdict.include?(n) }
|
|
176
|
+
|
|
177
|
+
if is_positive
|
|
178
|
+
positive_score += opinion[:weight] * opinion[:confidence]
|
|
179
|
+
elsif is_negative
|
|
180
|
+
negative_score += opinion[:weight] * opinion[:confidence]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
total_score = positive_score + negative_score
|
|
185
|
+
verdict = positive_score > negative_score ? 'Proceed with the proposition' : 'Decline the proposition'
|
|
186
|
+
confidence = total_score > 0 ? (positive_score - negative_score).abs / total_score : 0.5
|
|
187
|
+
|
|
188
|
+
accepted = opinions.select do |op|
|
|
189
|
+
verdict_lower = op[:verdict].downcase
|
|
190
|
+
is_positive = VerdictIndicators::POSITIVE.any? { |p| verdict_lower.include?(p) }
|
|
191
|
+
(positive_score > negative_score) == is_positive
|
|
192
|
+
end.map { |op| op[:perspective] }
|
|
193
|
+
|
|
194
|
+
rejected = opinions.select do |op|
|
|
195
|
+
verdict_lower = op[:verdict].downcase
|
|
196
|
+
is_positive = VerdictIndicators::POSITIVE.any? { |p| verdict_lower.include?(p) }
|
|
197
|
+
(positive_score > negative_score) != is_positive
|
|
198
|
+
end.map { |op| op[:perspective] }
|
|
199
|
+
|
|
200
|
+
{
|
|
201
|
+
resolved: confidence >= @config[:min_confidence],
|
|
202
|
+
strategy: :weighted_voting,
|
|
203
|
+
outcome: "Weighted voting resulted in #{positive_score > negative_score ? 'approval' : 'rejection'} with #{(confidence * 100).round(1)}% confidence",
|
|
204
|
+
verdict: verdict,
|
|
205
|
+
confidence: confidence,
|
|
206
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
207
|
+
accepted_by: accepted,
|
|
208
|
+
rejected_by: rejected
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Applies deliberation extension strategy
|
|
213
|
+
def apply_deliberation_extension(conflict, opinions)
|
|
214
|
+
focus_areas = identify_focus_areas(conflict, opinions)
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
resolved: false,
|
|
218
|
+
strategy: :deliberation_extension,
|
|
219
|
+
outcome: 'Additional deliberation rounds required',
|
|
220
|
+
confidence: 0,
|
|
221
|
+
conditions: [
|
|
222
|
+
"Conduct additional deliberation focusing on: #{focus_areas.join(', ')}",
|
|
223
|
+
'Re-evaluate after gathering more information'
|
|
224
|
+
],
|
|
225
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
226
|
+
accepted_by: [],
|
|
227
|
+
rejected_by: []
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Applies reframing strategy
|
|
232
|
+
def apply_reframing(conflict, opinions)
|
|
233
|
+
# Generate reframed propositions
|
|
234
|
+
reframed_options = generate_reframed_options(opinions)
|
|
235
|
+
|
|
236
|
+
{
|
|
237
|
+
resolved: false,
|
|
238
|
+
strategy: :reframing,
|
|
239
|
+
outcome: 'Proposition should be reframed to address conflicting perspectives',
|
|
240
|
+
confidence: 0,
|
|
241
|
+
conditions: reframed_options.each_with_index.map { |option, i| "Alternative #{i + 1}: #{option}" },
|
|
242
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
243
|
+
accepted_by: [],
|
|
244
|
+
rejected_by: []
|
|
245
|
+
}
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Applies escalation strategy
|
|
249
|
+
def apply_escalation(conflict, opinions)
|
|
250
|
+
summary = summarize_conflict(conflict, opinions)
|
|
251
|
+
|
|
252
|
+
{
|
|
253
|
+
resolved: false,
|
|
254
|
+
strategy: :escalation,
|
|
255
|
+
outcome: 'Decision requires escalation to higher authority',
|
|
256
|
+
confidence: 0,
|
|
257
|
+
conditions: [
|
|
258
|
+
'Present all perspectives to decision-making authority',
|
|
259
|
+
"Conflict summary: #{summary}",
|
|
260
|
+
'Await authoritative decision'
|
|
261
|
+
],
|
|
262
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
263
|
+
accepted_by: [],
|
|
264
|
+
rejected_by: opinions.map { |op| op[:perspective] }
|
|
265
|
+
}
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Applies compromise strategy
|
|
269
|
+
def apply_compromise(conflict, opinions)
|
|
270
|
+
mediation = mediate_between_perspectives(opinions)
|
|
271
|
+
|
|
272
|
+
{
|
|
273
|
+
resolved: mediation[:success],
|
|
274
|
+
strategy: :compromise,
|
|
275
|
+
outcome: mediation[:success] ? 'Compromise reached between perspectives' : 'Unable to reach compromise',
|
|
276
|
+
verdict: mediation[:position],
|
|
277
|
+
confidence: mediation[:confidence],
|
|
278
|
+
conditions: mediation[:compromises].map { |c| "#{c[:perspective]}: #{c[:concession]}" },
|
|
279
|
+
remaining_concerns: mediation[:needs_further_deliberation] ? ['Further deliberation may refine the compromise'] : [],
|
|
280
|
+
accepted_by: mediation[:compromises].map { |c| c[:perspective] },
|
|
281
|
+
rejected_by: []
|
|
282
|
+
}
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Applies conditional approval strategy
|
|
286
|
+
def apply_conditional_approval(conflict, opinions)
|
|
287
|
+
conditions = generate_conditions(opinions)
|
|
288
|
+
accepting_perspectives = opinions.filter { |op| op[:confidence] >= 0.5 }
|
|
289
|
+
|
|
290
|
+
{
|
|
291
|
+
resolved: conditions.any?,
|
|
292
|
+
strategy: :conditional_approval,
|
|
293
|
+
outcome: 'Conditional approval granted subject to requirements',
|
|
294
|
+
verdict: 'Conditionally approved',
|
|
295
|
+
confidence: 0.65,
|
|
296
|
+
conditions: conditions,
|
|
297
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
298
|
+
accepted_by: accepting_perspectives.map { |op| op[:perspective] },
|
|
299
|
+
rejected_by: opinions.filter { |op| op[:confidence] < 0.5 }.map { |op| op[:perspective] }
|
|
300
|
+
}
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Applies suspension strategy
|
|
304
|
+
def apply_suspension(conflict, opinions)
|
|
305
|
+
requirements = identify_information_requirements(conflict, opinions)
|
|
306
|
+
|
|
307
|
+
{
|
|
308
|
+
resolved: false,
|
|
309
|
+
strategy: :suspension,
|
|
310
|
+
outcome: 'Decision suspended pending additional information',
|
|
311
|
+
confidence: 0,
|
|
312
|
+
conditions: requirements,
|
|
313
|
+
remaining_concerns: extract_remaining_concerns(opinions),
|
|
314
|
+
accepted_by: [],
|
|
315
|
+
rejected_by: []
|
|
316
|
+
}
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Applies perspective dominance strategy
|
|
320
|
+
def apply_perspective_dominance(conflict, opinions)
|
|
321
|
+
# Find the perspective with highest weighted confidence
|
|
322
|
+
weighted_opinions = opinions.map do |op|
|
|
323
|
+
op.merge(weighted_confidence: op[:confidence] * op[:weight])
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
weighted_opinions.sort! { |a, b| b[:weighted_confidence] <=> a[:weighted_confidence] }
|
|
327
|
+
dominant_opinion = weighted_opinions[0]
|
|
328
|
+
|
|
329
|
+
{
|
|
330
|
+
resolved: true,
|
|
331
|
+
strategy: :perspective_dominance,
|
|
332
|
+
outcome: "Decision based on dominant #{dominant_opinion[:perspective]} perspective",
|
|
333
|
+
verdict: dominant_opinion[:verdict],
|
|
334
|
+
confidence: dominant_opinion[:confidence],
|
|
335
|
+
conditions: [
|
|
336
|
+
"Note: Decision primarily driven by #{dominant_opinion[:perspective]} considerations",
|
|
337
|
+
'Other perspectives had lower weighted confidence'
|
|
338
|
+
],
|
|
339
|
+
remaining_concerns: opinions
|
|
340
|
+
.reject { |op| op[:perspective] == dominant_opinion[:perspective] }
|
|
341
|
+
.flat_map { |op| op[:concerns] },
|
|
342
|
+
accepted_by: [dominant_opinion[:perspective]],
|
|
343
|
+
rejected_by: opinions
|
|
344
|
+
.reject { |op| op[:perspective] == dominant_opinion[:perspective] }
|
|
345
|
+
.map { |op| op[:perspective] }
|
|
346
|
+
}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Creates an unresolved result
|
|
350
|
+
def create_unresolved_result(strategy, conflict)
|
|
351
|
+
{
|
|
352
|
+
resolved: false,
|
|
353
|
+
strategy: strategy,
|
|
354
|
+
outcome: 'Unable to resolve conflict with available strategies',
|
|
355
|
+
confidence: 0,
|
|
356
|
+
remaining_concerns: [conflict[:description]],
|
|
357
|
+
accepted_by: [],
|
|
358
|
+
rejected_by: conflict[:perspectives]
|
|
359
|
+
}
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Identifies focus areas for extended deliberation
|
|
363
|
+
def identify_focus_areas(conflict, opinions)
|
|
364
|
+
areas = []
|
|
365
|
+
|
|
366
|
+
opinions.each do |opinion|
|
|
367
|
+
if opinion[:concerns]&.any?
|
|
368
|
+
areas.concat(opinion[:concerns].slice(0, 2))
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
areas.uniq.slice(0, 5)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Generates reframed proposition options
|
|
376
|
+
def generate_reframed_options(opinions)
|
|
377
|
+
options = []
|
|
378
|
+
|
|
379
|
+
logos_op = opinions.find { |op| op[:perspective] == :logos }
|
|
380
|
+
pathos_op = opinions.find { |op| op[:perspective] == :pathos }
|
|
381
|
+
ethos_op = opinions.find { |op| op[:perspective] == :ethos }
|
|
382
|
+
|
|
383
|
+
if logos_op && pathos_op
|
|
384
|
+
options << "Consider a modified approach that addresses #{logos_op[:arguments][0] || 'logical concerns'} while being mindful of #{pathos_op[:arguments][0] || 'emotional impact'}"
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
if ethos_op
|
|
388
|
+
options << "Ensure the proposition aligns with #{ethos_op[:arguments][0] || 'ethical principles'} before proceeding"
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
options
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Summarizes a conflict for escalation
|
|
395
|
+
def summarize_conflict(conflict, opinions)
|
|
396
|
+
perspective_views = opinions.map do |op|
|
|
397
|
+
"#{op[:perspective]}: #{op[:verdict][0..50]}..."
|
|
398
|
+
end.join('; ')
|
|
399
|
+
|
|
400
|
+
"#{conflict[:type]} (#{conflict[:severity]}): #{conflict[:description]}. Perspectives: #{perspective_views}"
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Mediates between perspectives to find compromise
|
|
404
|
+
def mediate_between_perspectives(opinions)
|
|
405
|
+
compromises = []
|
|
406
|
+
|
|
407
|
+
# Find common ground
|
|
408
|
+
common_arguments = find_common_arguments(opinions)
|
|
409
|
+
|
|
410
|
+
# Generate compromise positions for each perspective
|
|
411
|
+
opinions.each do |opinion|
|
|
412
|
+
concession = generate_concession(opinion, common_arguments)
|
|
413
|
+
compromises << {
|
|
414
|
+
perspective: opinion[:perspective],
|
|
415
|
+
concession: concession
|
|
416
|
+
}
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Calculate confidence based on compromise alignment
|
|
420
|
+
avg_confidence = opinions.sum { |op| op[:confidence] }.to_f / opinions.length
|
|
421
|
+
|
|
422
|
+
{
|
|
423
|
+
success: compromises.length == opinions.length,
|
|
424
|
+
position: "Compromise position: #{common_arguments.join(' and ')}",
|
|
425
|
+
confidence: avg_confidence * 0.8,
|
|
426
|
+
compromises: compromises,
|
|
427
|
+
needs_further_deliberation: avg_confidence < @config[:min_confidence]
|
|
428
|
+
}
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# Finds common arguments across perspectives
|
|
432
|
+
def find_common_arguments(opinions)
|
|
433
|
+
all_arguments = opinions.flat_map { |op| op[:arguments] || [] }
|
|
434
|
+
|
|
435
|
+
unique_arguments = []
|
|
436
|
+
all_arguments.each do |arg|
|
|
437
|
+
is_similar = unique_arguments.any? { |existing| are_arguments_similar(existing, arg) }
|
|
438
|
+
unless is_similar
|
|
439
|
+
unique_arguments << arg
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
unique_arguments.slice(0, 3)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Checks if two arguments are semantically similar
|
|
447
|
+
def are_arguments_similar(arg1, arg2)
|
|
448
|
+
words1 = arg1.downcase.split(/\s+/)
|
|
449
|
+
words2 = arg2.downcase.split(/\s+/)
|
|
450
|
+
common_words = words1 & words2
|
|
451
|
+
common_words.length >= 3
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Generates a concession for a perspective
|
|
455
|
+
def generate_concession(opinion, common_arguments)
|
|
456
|
+
if opinion[:concerns]&.any?
|
|
457
|
+
"Willing to proceed if #{opinion[:concerns][0].downcase} is addressed"
|
|
458
|
+
else
|
|
459
|
+
"Accepting common ground on #{common_arguments[0] || 'shared interests'}"
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Generates conditions for conditional approval
|
|
464
|
+
def generate_conditions(opinions)
|
|
465
|
+
conditions = []
|
|
466
|
+
|
|
467
|
+
opinions.each do |opinion|
|
|
468
|
+
if opinion[:concerns]&.any?
|
|
469
|
+
conditions << "#{opinion[:perspective]}: Address #{opinion[:concerns][0].downcase}"
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
conditions.slice(0, 5)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Identifies information requirements for suspension
|
|
477
|
+
def identify_information_requirements(conflict, opinions)
|
|
478
|
+
requirements = []
|
|
479
|
+
|
|
480
|
+
case conflict[:type]
|
|
481
|
+
when :fundamental_disagreement
|
|
482
|
+
requirements << 'Gather additional evidence to resolve fundamental disagreements'
|
|
483
|
+
when :uncertainty
|
|
484
|
+
requirements << 'Obtain more information to reduce uncertainty'
|
|
485
|
+
when :context_inconsistency
|
|
486
|
+
requirements << 'Clarify contextual factors causing interpretation differences'
|
|
487
|
+
else
|
|
488
|
+
requirements << 'Conduct further analysis before proceeding'
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
opinions.each do |opinion|
|
|
492
|
+
if opinion[:confidence] < 0.5
|
|
493
|
+
requirements << "Address #{opinion[:perspective]} perspective's low confidence"
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
requirements
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Extracts remaining concerns from opinions
|
|
501
|
+
def extract_remaining_concerns(opinions)
|
|
502
|
+
opinions.flat_map { |op| op[:concerns] || [] }.slice(0, 5)
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
end
|