I had a lot of fun working through my “Exploring LLMs as Agent” series. I’m starting to dive a little deeper into some specific Agent use cases, so I’ll move away from “exploring” and start “building” agents.
At work, we’ve started configuring rules outside of code, especially for situations where a non-developer wants to create some rule-based logic. We’ve looked at JSONLogic for this use case, but there are other approaches too.
Rule logic can get complex, which makes it hard for those non-developers (and even developers) to create valid rules. Maybe this is a use case for an Agent?
TLDR: Here’s the code repo for the JSONLogic Agent. It’s part of my ADK code repo. Keeping the ADK Agents in the same repo seemed like a good idea.
JSONLogic Agent
I decided to build an Agent, based on Google’s Agent Development Kit (ADK) since I had some experience with the framework. I had a rough idea of what I thought I wanted to do:
- Create a schema for JSONLogic
- Create a schema for the dataset (JSON) being evaluated
- Expose the schemas to the Agent, with a sprinkle of ReAct planning
- Agent create JSONLogic on its own
Side Track
Something I’ve started doing recently is asking Claude or Gemini what they think of the approach, and if they have any other options or suggestions. In this case, Claude suggested having the Agent create an intermediate format, not actual JSONLogic, and then using a separate tool to convert from the intermediate format to JSONLogic.
I didn’t much care for that idea, but I ended up doing just that. Initial experiments showed that the Agent didn’t always create valid or even functional JSONLogic — which doesn’t really have a well defined schema. Now, those problems could have been my own failings, but I began to see opportunities having the Agent parse my intent of the rule, using a higher-level, simpler structure. So the plan became:
- Create a schema for the Parsed Intent format
- Create a schema for the dataset (JSON) being evaluated
- Expose the schemas to the Agent, with a sprinkle of ReAct planning
- Expose a tool to convert Parsed Intent to JSONLogic
Parsing Intent
Instead of trying to make sure the Agent knew all the syntax and nuance around JSONLogic, I created a simple format of operations
and conditions
(which can be nested) to capture the user’s intent of the rule. It looks like this (from the system prompt):
The intent should follow this format:
{
"operation": "logical operation (AND, OR, NOT)",
"conditions": [
{
"field": "data field",
"operator": "One of the operators above",
"value": "The value to compare against (for most operators)"
},
// Additional conditions...
]
}
For nested conditions, use this structure:
{
"operation": "logical operation (AND, OR, NOT)",
"conditions": [
{
"field": "data field",
"operator": "operators from above",
"value": "the value"
},
{
"operation": "logical operation (AND, OR, NOT)",
"conditions": [
{
"field": "data field",
"operator": "operators from above",
"value": "the value"
},
// Additional nested conditions...
]
},
// Additional conditions...
]
}
One of the interesting aspects of using this approach is that I could create high-level operations that are not natively supported in JSONLogic directly, but could be implemented using JSONLogic — like macros. Here are some, again pulling from the system prompt:
When parsing conditions for the intent, use ONLY these operator keywords:
- **Comparison Operators**:
- Basic: "equals", "notEquals", "greaterThan", "lessThan", ...
- Range: "between", "betweenOrEqual"
- **Logical Operators**:
- "AND", "OR", "NOT"
- **String Operators**:
- "startsWith", "endsWith", "contains" (contains substring)
- **Array Operators**:
- Empty checks: "empty", "notEmpty"
- Item checks: "contains" (is value or values in array)
- Index access: use dot notation to access array elements, e.g., "object.array.0"
- Length checks: "lengthEquals", "lengthGreaterThan", "lengthLessThan"
Some of these operations are directly supported by JSONLogic. Others are supported if you understand the “Truthy & Falsy” nuance JSONLogic applies. The Range checks are a special case that JSONLogic supports, but using “between” is much more explicit. Item checks for a single item in an array are directly supported, but not checks with multiple items in an array — but that can be implemented using some
. Length checks are not directly supported, but can be implemented using reduce
, an idea I stole from a co-worker.
Checkout create_jsonlogic_from_intent
in jsonlogic.py
to see how I hacked some of these “macro” operations together.
Results
The JSONLogic Agent feels like a success. I include two different test datasets and a series of predefined “questions” for the Agent to act on and output JSONLogic. The command line tool will even execute the JSONLogic against the same datasets and compare to an expected answer. Here’s an example:
Query: Find customers who completed the survey in less than 5 minutes (300 seconds) and reported at least one pain point
Planning:
1. Get the available fields from the schema to identify the correct field names for "time spent" and "pain points".
2. Parse the user's intent into a structured representation. This will involve identifying the conditions for "time spent" being less than 300 seconds and "pain points" array not being empty.
3. Convert the structured intent into a JSONLogic rule using the `create_jsonlogic_from_intent` tool.
4. Validate the fields in the JSONLogic rule against the available fields from the schema using the `validate_fields_from_jsonlogic` tool.
5. Return the complete JSONLogic rule.
The relevant fields are:
- metadata.timeSpent (time spent)
- questions.satisfaction.painPoints.response (pain points)
Parsed Intent:
{
"operation": "AND",
"conditions": [
{
"operator": "lessThan",
"field": "metadata.timeSpent",
"value": 300
},
{
"field": "questions.satisfaction.painPoints.response",
"operator": "notEmpty"
}
]
}
Generated JSONLogic rule:
{
"and": [
{
"<": [
{
"var": "metadata.timeSpent"
},
300
]
},
{
"!=": [
{
"var": "questions.satisfaction.painPoints.response"
},
[]
]
}
]
}
Test result against sample data: False
? PASS - Result matches expected outcome: False
I added some utilities to make it a little easier to work with ADK Agents from mechanisms other than the adk
command line tools.
What’s Next
I’ll keep building some Agents using ADK. I want to explore vector search and embeddings, and how those might be used together with an Agent.