eyeprolog

Data negotiation

Which data requests can both sides agree to?

Two agents each hold three datasets and each has a policy. Agent 1 wants three datasets from agent 2. A negotiation succeeds only when the asking side lacks the data, the other side has it, and both policies allow the exchange. Of three requests, exactly one survives.

Run it in the playground · Source


The question

Data sharing has two gatekeepers. The requester’s own policy decides what it may ask for, and the provider’s policy decides what it may hand over. A request that one side would refuse is not worth sending, so the reasoner checks both sides before anyone acts.


The agents

hasData(agent1, [data1, data2, data3]).
hasData(agent2, [data4, data5, data6]).

want_negotiate(agent1, [agent2, data4]).
want_negotiate(agent1, [agent2, data5]).
want_negotiate(agent1, [agent2, data7]).

policy(agent1, [request, Data]) :-
  member(Data, [data4, data6]).
policy(agent2, [accept, Data]) :-
  (Data \= data5).

Agent 1 may request only data4 and data6. Agent 2 will share anything except data5. Nobody holds data7.


The rules

request_data(Agenta, Agentb, Data) :-
  hasData(Agenta, Datalista),
  hasData(Agentb, Datalistb),
  member(Data, Datalistb),
  \+ member(Data, Datalista),
  policy(Agenta, [request, Data]).

negotiate(Agenta, [Agentb, Data]) :-
  want_negotiate(Agenta, [Agentb, Data]),
  request_data(Agenta, Agentb, Data),
  accept_request(Agentb, Agenta, Data).

A request is possible when the provider has the data, the requester does not, and the requester’s policy allows asking. A negotiation needs the wish, the request, and the provider’s acceptance.


The answer

eyeprolog examples/data-negotiation.pl
negotiate(agent1, [agent2, data4]).

data4 goes through. data5 fails twice: agent 1 may not ask for it, and agent 2 would refuse it anyway. data7 fails because agent 2 does not have it.


The reason

step(request_data(agent1, agent2, data4),
     rule(8),
     ['Agenta' = agent1,
      'Agentb' = agent2,
      'Data' = data4,
      'Datalista' = [data1, data2, data3],
      'Datalistb' = [data4, data5, data6]],
     [hasData(agent1, [data1, data2, data3]),
      hasData(agent2, [data4, data5, data6]),
      member(data4, [data4, data5, data6]),
      \+ member(data4, [data1, data2, data3]),
      policy(agent1, [request, data4])]).

The request is justified by both data inventories, by data4 being in agent 2’s list and not in agent 1’s, and by agent 1’s request policy. Every premise the decision rests on is named.


The check

condition('C1', resolution, ok, 8).
condition('C5', re_decision, ok, 4).
condition('C6', boundary_consistency, ok, 0).
steps(12).
trusted(0).
verdict(checked).

Eight steps are replayed and four recomputed: three member/2 calls and one negation, “agent 1 does not already have data4”, written \+ member(data4, [data1, data2, data3]). A failed search cannot be replayed as a derivation, but this one is about a list written out in the step, so the checker decides it again itself. Nothing is left as an assumption.


Try it

Learn more in The Art of EyeProlog, Chapter 7 (failure and negation) and Chapter 25 (an auditable decision service).