To solve the given optimization problem, we first need to convert the natural language description into a symbolic representation. Let's denote:

- $x_1$ as the number of easy questions solved,
- $x_2$ as the number of hard questions solved.

The objective function is to maximize the total points earned from solving these questions. Given that each easy question is worth 4 points and each hard question is worth 10 points, the objective function can be written as:

$$\text{Maximize: } 4x_1 + 10x_2$$

The constraints based on the problem description are:

1. Solve at least 4 easy questions: $x_1 \geq 4$
2. Solve at least 2 hard questions: $x_2 \geq 2$
3. Solve at most 12 easy questions: $x_1 \leq 12$
4. Solve at most 4 hard questions: $x_2 \leq 4$
5. Solve a maximum of 9 questions in total: $x_1 + x_2 \leq 9$

Additionally, since we cannot solve a negative number of questions, we also have:

6. $x_1 \geq 0$
7. $x_2 \geq 0$

And because the number of questions must be an integer:

8. $x_1$ is an integer
9. $x_2$ is an integer

Now, let's represent this problem in the requested format:

```json
{
    'sym_variables': [('x1', 'number of easy questions'), ('x2', 'number of hard questions')],
    'objective_function': '4*x1 + 10*x2',
    'constraints': [
        'x1 >= 4', 
        'x2 >= 2', 
        'x1 <= 12', 
        'x2 <= 4', 
        'x1 + x2 <= 9', 
        'x1 >= 0', 
        'x2 >= 0'
    ]
}
```

To solve this problem using Gurobi in Python, we will use the following code:

```python
from gurobipy import *

# Create a new model
m = Model("Exam_Questions")

# Add variables
x1 = m.addVar(vtype=GRB.INTEGER, name="easy_questions")
x2 = m.addVar(vtype=GRB.INTEGER, name="hard_questions")

# Set the objective function
m.setObjective(4*x1 + 10*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x1 >= 4, "at_least_4_easy")
m.addConstr(x2 >= 2, "at_least_2_hard")
m.addConstr(x1 <= 12, "at_most_12_easy")
m.addConstr(x2 <= 4, "at_most_4_hard")
m.addConstr(x1 + x2 <= 9, "total_questions")

# Optimize the model
m.optimize()

# Print the solution
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Easy questions: {x1.x}")
    print(f"Hard questions: {x2.x}")
else:
    print("No optimal solution found")
```