Constraints are traditionally implemented by overloading those operators (it’s why it’s image=='string' for example), so that’s why, but yeah, that is more informative.
a = net.Node("alexa", tags==contain_all(...)) could work. a = net.Node("alexa", tags_contains_all(...)) could work too.
I didn’t go into details about images or multiple node use/allocations because they’re a little bit of a different topic and still a little bit unsolved.
For images, due to protobufs, the simplest thing would be for every resource to also have a list of images that it can run. This should be based off the tags (maybe you’ll get a warning if not the case).
As for multiple node use/allocation, like resource pools, I don’t think you have to do anything fancier than adding a list of resource pools, in which if you allocate anything in the pool, you allocate everything else in the pool.