DFS-based exhaustive word grid search
This is fairly tight; I don't think there's much more performance to pull out without dropping into C. That said:
possible_directions
First, I would rename this to neighbors or successors; it's not really about direction but about the nodes in the graph that come next or are adjacent to your current node.
Second, you can reduce the number of branches and arithmetic operations a little:
for xx in range(max(0, x - 1), min(x + 1, len(grid)):
    for yy in range(max(0, y - 1), min(y + 1, len(grid[x])):
        if xx == x and yy = y: continue
        yield xx, yy

Third, you can prevent many no-op calls of dfs by doing your seen check in this function rather than in the dfs base case. That is, pass seen to this function and modify your yield line to something like
if (xx, yy) not in seen: yield xx, yy

dfs
You compute word + grid[x][y] many times (once + once per neighbor). You should memoize this before yielding it (Python may already perform this optimization for you but I doubt it).
I don't think a set of tuples is the best implementation for seen.  You can't really avoid the copy of seen that I can see, and the lookups are not as fast as they could be. You already implicitly require that every row is the same length (0 &lt;= y + yy &lt; len(grid[x]), not 0 &lt;= y + yy &lt; len(grid[x + xx])) and that seems like a sensible restriction in any case. I suggest a bit array of some sort (e.g. bitarray, which I've never used but seems to fit the bill), which will allow fast setting, checking, and copying of seen (instead of seen[x][y], check and set seen[y * len(grid) + x]).
Best practice here would probably be to have a wrapper function that does not take a seen or word argument, but initializes them for a call to your recursive helper, something like
def dfs(x, y):
    return dfs_recursive(x, y, set(), '')