Recipes← all recipesView on GitHub

Keep per-call state server-side, keyed by call id

AI Agentsserver-side session state for voice AI

Store full per-call state on your server, keyed by call ID, and expose only the fields the voice AI agent needs.

governance

The claim

global_data travels with every tool call and the model can see it. That makes it the right place for a short, AI-facing summary and the wrong place for a growing record. You keep the record in a store keyed by call_id, which the platform posts with every tool call. You write only a count and the distinct areas to global_data. A second tool reads the whole record back from the store when the mechanic asks for it.

How it works

def record_finding(self, args, raw_data):
    call_id = raw_data.get("call_id")
    findings = STORE.setdefault(call_id, [])
    findings.append({"area": area, "detail": detail})          # the full text
    seen = list(dict.fromkeys(f["area"] for f in findings))    # distinct areas
    r = FunctionResult(f"Recorded {area}. {len(findings)} findings so far.")
    r.add_action("set_global_data", {                          # the summary
        "findings": len(findings), "areas": ", ".join(seen)})
    return r

After the third finding, what the platform receives:

{"response": "Recorded wheels. 3 findings so far.",
 "action": [{"set_global_data": {"findings": 3, "areas": "brakes, gears, wheels"}}]}

The three findings in the store run to several hundred bytes; the whole action stays under 120. Because areas holds distinct values, twenty more findings on one area leave it the same size. read_back_report reads STORE[call_id], not global_data. global_data never carries the full text between turns; the model receives it in the read-back response.

call_id is the key because it is the one identifier the platform attaches to every tool call for the life of the call. Two distinct call ids get two records.

Limitations

The store is a dictionary in the process. A restart loses it, and two replicas would not share it; your version is a table keyed by call_id.

Nothing here removes a call’s record when the call ends. Where you do that is your design choice; the store only grows in this recipe.

What to change first

Put detail into the set_global_data action and run the verifier. It fails on the first finding, at the size check. That is the point: the record was about to ride along with every tool call for the rest of the conversation.