tf.custom_gradient silently fails when using all keyword arguments
Triage Notes:
I was able to reproduce the issue on tensorflow v2.15, v2.16 and tf-nightly. Kindly find the gist of it [here](https://colab.research.google.com/gist/tilakrayal/b43909cc45fce3162c36bd37c3af981d/untitled1863.ipynb).

The tf.custom_gradients are working as expected when z = bar(x, y) & z = bar(x=x, y=y) and it was failing with z = bar(x, y=y)  by throwing the error. @tilakrayal when running your gist, the gradient is NOT as expected when z = bar(x=x, y=y). the expected output should be the custom gradient (which is [[-2000], [-4000], [-6000]] for dz/dx), given the function is wrapped in tf.custom_gradient, but instead returns the gradient produced by autograd (which is [[-0.66], [-1.33], [-2.]]).

The issue is that the custom gradient is silently ignored when using keyword arguments for every parameter. The decorator `@tf.custom_gradient` uses the input to deduce which arguments are differentiable inputs, and which arguments are non-differentiable parameters to the function.  Positional arguments are considered differentiable, keyword arguments non-differentiable.

> When passing all keyword arguments, the custom gradient is silently ignored and defaults to the autograd.

It thinks your function has no differentiable inputs, so simply forwards the upstream gradient and continues the computation.

> When passing a mix of positional and keyword arguments, an exception is thrown.

Because you're returning multiple gradients when only the one non-keyword input is considered a differentiable input.

---

As for what to do: we will not change the behavior of the `custom_gradient` decorator.  You can update the documentation with a PR if you like to reflect this behavior.

 This issue is stale because it has been open for 7 days with no activity. It will be closed if no further activity occurs. Thank you. Are you satisfied with the resolution of your issue?
<a href="https://docs.google.com/forms/d/e/1FAIpQLSfaP12TRhd9xSxjXZjcZFNXPGk4kc1-qMdv3gc6bEP90vY1ew/viewform?entry.85265664=Yes&entry.2137816233=https://github.com/tensorflow/tensorflow/issues/65839">Yes</a>
<a href="https://docs.google.com/forms/d/e/1FAIpQLSfaP12TRhd9xSxjXZjcZFNXPGk4kc1-qMdv3gc6bEP90vY1ew/viewform?entry.85265664=No&entry.2137816233=https://github.com/tensorflow/tensorflow/issues/65839">No</a>

### Issue type

Bug

### Have you reproduced the bug with TensorFlow Nightly?

Yes

### Source

source

### TensorFlow version

tf 2.12

### Custom code

Yes

### OS platform and distribution

Amazon Linux 2

### Mobile device

_No response_

### Python version

3.8.11

### Bazel version

_No response_

### GCC/compiler version

_No response_

### CUDA/cuDNN version

_No response_

### GPU model and memory

_No response_

### Current behavior?

When decorating a function with tf.custom_gradient, calling that function results in unexpected behavior when passing all keyword arguments. When passing all keyword arguments, the custom gradient is silently ignored and defaults to the autograd. When passing a mix of positional and keyword arguments, an exception is thrown.

I believe that passing keyword arguments should throw an exception, since there is no warning that the autograd will be returned and not the custom grad.

### Standalone code to reproduce the issue

```shell
@tf.custom_gradient
def bar(x, y):
    def grad(upstream):
        return 1000 * 2 * (x - y), -2 * (x - y)
    return tf.reduce_mean(tf.math.square(x - y), axis=0), grad

x = tf.constant([[4], [5], [6]], dtype=tf.float32)
y = tf.constant([[5], [7], [9]], dtype=tf.float32)

# first call with positional arguments, this works as expected
with tf.GradientTape(persistent=True) as tape:
    tape.watch(x)
    tape.watch(y)
    z = bar(x, y)

print("without keywords...")
print(z)
print(tape.gradient(z, x))
print(tape.gradient(z, y))
print()

# next, call with all keyword arguments, this returns the autograd and not the custom grad
with tf.GradientTape(persistent=True) as tape:
    tape.watch(x)
    tape.watch(y)
    z = bar(x=x, y=y)

print("with keywords...")
print(z)
print(tape.gradient(z, x))
print(tape.gradient(z, y))

# last, call with a mix of positional and keyword args, exception is thrown
with tf.GradientTape(persistent=True) as tape:
    tape.watch(x)
    tape.watch(y)
    z = bar(x, y=y)

print("with keywords...")
print(z)
print(tape.gradient(z, x))
print(tape.gradient(z, y))
```


### Relevant log output

_No response_