wow, this is indeed a very nice exchange.
I implemented a tou example using term_expansion/2 and goal_expansion/2 so that the KB can call to the corresponding “caller” module.
What I realized doing that is what both of you just pointed out:
The complete rewrite of the  kb  module to pass the additional argument is of course painful.
What makes it more complicated is that I don’t have the kb reasoning toolkit in just one module, but in many modules (actions, charge, items, etc…). So, a predicate X in the actions module may call one Y in charge module, which itself will need to post a goal Z against the original agent module (the one who called X in actions)! This means that the caller module should be passed across all predicates.
This makes me wonder that it is not so easy to design a mechanism, because in a sequence of goal calls, there will be many different contexts. which one we would want? the very first one? Why? not easy.
I am studying @jan last solution, it’s pretty smart, although using exceptions for this sounds a bit non-natural (of course, I am expecting everything can be done with any of this systems, as they are all turing-complete  )
 )
Thanks nonetheless to both for the rich exchanges.
Sebastian
PS: @Jan, maybe would be good to add something like this as example for term_expansion/2?
:- module(test_kb, [far_away/2]).
% operator to signal when the predicate should be expanded with context module call
:- op(1, fx, ^).
% List all predicates that need to be added 1 more argument to track the calling module
transform_kb_clause(far_away(G):-Body, far_away(G, Agent):-Body2) :-
	add_agent_module(Agent, Body, Body2).
% domain-independent
term_expansion(ClauseIn, ClauseOut) :-
    transform_kb_clause(ClauseIn, ClauseOut).
% Add Agent as calling module in SOME predicates; here, only on percepts/1	
add_agent_module(Agent, ^G, Agent:G2) :- !,
	G =.. [F|Args],
	append(Args, [Agent], Args2),
	G2 =.. [F|Args2].
%add_agent_module(Agent, percepts(X), Agent:percepts(X)) :- !.
add_agent_module(Agent, (G1, G2), (G1_New, G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(Agent, (G1; G2), (G1_New; G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(Agent, (G1 -> G2), (G1_New -> G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(_, G, G).
	
% Is destination far from the current agent location?
% 	Agent location LocAgent is obtained via the percept of the agent, which is not in this module
% 	but in the agent "calling" module
far_away(Destination) :-
	^percepts(P),
	member(loc(LocAgent), P), !,
	distance(LocAgent, Destination, D),
	D > 10.
	
distance(L1, L2, D) :- D is abs(L1-L2).
will produce this clause when consulting:
far_away(D, A) :-
	call(A:percepts(B, A)),
	member(loc(C), B), !,
	distance(C, D, E),
	E>10.
What I do not like here are the add_agent_module/3 clauses to process the Body. I am sure it is not even complete!