Read the program the model wrote
By the end of this lesson, you can inspect generated JavaScript and diagnose common program errors.
Inspect generated calculations
Section titled “Inspect generated calculations”Lesson 2 gave a correct answer through code that you did not write and did not see. One day that answer is wrong. The prompt looks fine, the tools look fine, and there is no error anywhere.
Read the generated JavaScript to inspect how the program calculated the number.
Lesson 4 uses this inspection method to diagnose incorrect field names.
The program is an ordinary tool call
Section titled “The program is an ordinary tool call”generate_text returns a messages array. That array is the whole conversation,
and each message holds a content array of items. A tool call is one of those
items. This is the shape:
messages[] the conversation, in order └─ content[] the parts of one message └─ { "type": "tool_call" , "toolName": "uc_ai__run_code" , "args": "{\"code\": \"...the JavaScript...\"}" }A code-mode call is a tool call like any other. So to read the program, find the
content item whose type is tool_call and whose toolName is
uc_ai__run_code, then read the code key of its args.
cc_analyst_pkg.program_source reads the array that way. Condensed from the two
functions in the package:
if l_item.get_string('type') = 'tool_call' and l_item.get_string('toolName') = uc_ai_tools_api.c_code_mode_tool_codethen -- get_clob, not get_string: a generated program passes 32k easily. return json_object_t.parse(l_item.get_clob('args')).get_clob('code');end if;What the model wrote
Section titled “What the model wrote”This is the program behind the lesson 2 answer, with the middle removed:
// Step 1: Fetch all shipments and limits in parallelconst [shipments, limits] = await Promise.all([ callTool("CC_LIST_SHIPMENTS", {}), callTool("CC_GET_LIMITS", {})]);
// Step 2: Fetch readings for all shipments in parallelconst readingsArr = await Promise.all( shipments.map(s => callTool("CC_GET_READINGS", { shipment_id: s.shipment_id })));
// ... the run-length rule, then:result = { brokenShipments, totalClaimEur };Read three things off this program.
await callTool(code, args)is the only channel. The program has no SQL, no network and no modules. Lesson 5 proves it.- The result is a top-level variable named
result. Only its value goes back to the model. - The program can hold 24 calls in one tool call. That is where the 26-to-1 ratio comes from.
A program that was wrong and reported no error
Section titled “A program that was wrong and reported no error”An earlier recorded run of the same question produced this instead:
const { temp_limit_celsius, max_minutes_above, sample_minutes, claim_per_hour_eur } = limit;
for (const reading of readings) { if (reading.temperature_celsius > temp_limit_celsius) {temp_limit_celsius and temperature_celsius do not exist. The real names are
max_temp_c and temp_c. Every comparison was undefined > undefined, which is
false, so the program found no broken shipment and reported a total of zero.
Nothing raised. The tool call succeeded, the program ran, and it returned a well-formed wrong answer.
The model guessed those names because nothing told it the real ones. That is the subject of lesson 4.
Run seven sandbox examples
Section titled “Run seven sandbox examples”Part 1 of 03_inspect_program.sql makes one model call, so it costs tokens. Part 2
makes none, and you can run it as often as you like.
execute_agent_tool is the same entry point a provider uses when the model asks for
uc_ai__run_code. You can call it yourself:
l_settings := uc_ai_settings.build_from_globals;l_args.put('code', p_code);l_out := uc_ai_tools_api.execute_agent_tool( uc_ai_tools_api.c_code_mode_tool_code, l_args, l_settings);Part 2 of 03_inspect_program.sql runs seven programs directly in the sandbox.
These runs make no provider calls and use no tokens.
1 correct -> {"classes":2,"frozen":-15}
2 forgotten await -> {"classes":2}
3 aliased call -> {"error":"code execution failed: callTool() is asynchronous - write: await callTool(...)","hint":"Fix the program and call the code tool again. ..."}
4 no result -> {"error":"the program finished without assigning a value to `result`","hint":"..."}
5 log then throw -> {"error":"code execution failed: l.product_class.toUpperCase(...).nope is not a function","hint":"...","console":"fetched the limits"}
6 unknown tool -> {"error":"code execution failed: ORA-20504: Tool not found: CC_DELETE_EVERYTHING","hint":"..."}
7 syntax error -> {"error":"code execution failed: syntax error: <function>:2:0 Expected comma but found ; ...","hint":"..."}Your wording will differ. What must match is the data, and the checks below.
Five of the seven come back as errors. None of them raised. Program 1 is correct and program 2 is repaired, so both return a result.
A program that fails does not stop the run
Section titled “A program that fails does not stop the run”A code-mode failure is data. UC AI returns it to the model as a JSON object
with an error and a hint, and the run continues. The model reads the error and
writes a better program on its next turn.
What each mistake does
Section titled “What each mistake does”Program 2 — the missing await. It worked. UC AI inserts the missing await
before the program runs.
Program 3 — the same mistake behind an alias. const fetchIt = callTool;
cannot be repaired by a text rewrite. So UC AI raises an error instead of letting
the program use an unfinished call as if it were data.
Program 4 — no result. The program ran and assigned nothing. UC AI has
nothing to send back and says so in those words.
Program 5 — the console. console.log is the only debugging channel a
program has. UC AI captures up to 50 lines and gives them back when the program
fails, in a console field beside the error. A program that assigns result
drops its log, so you cannot use console.log to carry data out.
Program 6 — a tool that does not exist. The allow-list answers, not the database. The same error comes back for a tool that exists but is not part of this run.
Program 7 — a syntax error. The program never runs. The message names the line and the column, and the model corrects it.
Verification
Section titled “Verification”Two checks after this lesson.
First, the seven programs raised nothing. Prove that the session is healthy and that no program wrote anything:
select count(*) as claims_filed from cc_claims;CLAIMS_FILED 0Second, print the program of your own lesson 2 run and read it. The one question to answer: does it read the fields that your tools return? Compare its field names against the JSON your handler builds.
Key takeaways
Section titled “Key takeaways”- The program is in
messages[].content[], in thetool_callitem whosetoolNameisuc_ai__run_code. Readargsandcodeas CLOBs. - A code-mode failure comes back to the model as data, not as an exception. Your PL/SQL call succeeds either way.
execute_agent_toolruns a program with no model and no cost. Use it to learn the sandbox and to test a tool that a program will call.- A forgotten
awaitis repaired. An aliasedcallToolis not, and it raises rather than returning a promise as data. console.logcomes back only when the program fails. A program that assignsresultdrops its log.
Full reference: How the program calls your tools.