Effectively, your question is about finding a cycle (of any length, if I understand correctly) in a bipartite graph.
With some minor pre-processing, you can use the networkx package to find if there is any cycle in your graph. networkx deals with bipartite graphs, but let's just rename out nodes and not need to use the bipartite trait directly.
To pre-process the dictionaries, we add a prefix 'a' to the nodes in the first dict and a prefix 'b' to the nodes in the second (and not forget to add the prefixes to the edge targets):
dict1 = {'a'+str(k): set(map(lambda x: 'b'+str(x), v)) for k, v in dict1.items()}
dict2 = {'b'+str(k): set(map(lambda x: 'a'+str(x), v)) for k, v in dict2.items()}
Now our dictionaries look like this:
{'a1': {'b3', 'b1'}, 'a2': {'b2'}, 'b1': {'a5'}, 'b3': {'a1'}}
{'b1': {'a5'}, 'b3': {'a1'}}
We merge them together to create a single dictionary:
dict3 = dict1
dict3.update(dict2)
And turn that dictionary into a (directed!) graph:
import networkx as nx
g = nx.DiGraph(dict3)
If you're so inclined, you can draw the network (with nx.draw_networkx(g)) to see what came out:

Well, we can see the cycle between 'a1' and 'b3'. To find them automatically - and this is the point of this answer - we can just use the following command:
nx.simple_cycles(g)
which in this case returns a generator object which yields the cycles (in this case, (list(nx.simple_cycles(g)) is [['b3', 'a1']].
But you don't want the whole list; you just want your code to throw an exception if the list is not empty. So, you can use:
assert(not(next(nx.simple_cycles(g), None))
And you're done!