Am I missing something for ambient occlusion? I'm trying to implement ambient occlusion in Python 3 and I'm seeing shadows beneath my reflective spheres but they seem very faint. I'm not sure if that means I've missed something, or if I just have a false impression of how much shadow results from ambient occlusion.

The result looks like this:



This is modeled as spheres (rather than triangle meshes). The 4 yellowish mirror spheres are hovering just above a very large sphere to approximate a plane, and the whole scene is surrounded by a very large white emissive sphere that provides the ambient sky light.

For each pixel sample rays are chosen with a Gaussian distribution around the pixel centre and more samples are chosen until the variance is sufficiently low. Rays reflect in the single specular reflection direction from the mirror spheres, and in any direction from the hemisphere of possible directions when hitting the floor. All rays eventually hit the white sky sphere and the colour is determined based on the losses along the way.

Is there some vital step I'm overlooking?
It's a bit hard to tell from your image, but it does look a bit faint. When debugging these kinds of things, it's always useful to strip down your scene as much as possible to remove any unnecessary complexity from the picture. In your case, try only creating a single diffuse sphere that touches the ground in one point. Give the ground and the sphere an albedo of 1. If ambient occlusion is implemented correctly, the point where the sphere touches the ground should have a pixel value of 0; the further away from the sphere you get, the closer the pixel values should go to 1 (not above - make sure nothing gets clamped when you output the image). Make sure to gamma correct your images.

The one thing that sticks out from your description is 


  Rays reflect [...] in any direction from the hemisphere of possible directions when hitting the floor


That by itself is fine, but you need to make sure to multiply the rays by the Lambertian BRDF (i.e. dot(normal, ray)/Pi). Even better is to directly sample from a cosine hemisphere, in which case all factors cancel out. This blog post has all the important info in one place.