<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>OpenAI | Jacob Aloysious</title><link>https://jacobaloysious.in/tag/openai/</link><atom:link href="https://jacobaloysious.in/tag/openai/index.xml" rel="self" type="application/rss+xml"/><description>OpenAI</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Sun, 11 Oct 2026 00:00:00 +0000</lastBuildDate><image><url>https://jacobaloysious.in/img/jacob-leadership-social.png</url><title>OpenAI</title><link>https://jacobaloysious.in/tag/openai/</link></image><item><title>Building and Evaluating an AI Agent: A Hands-On Case Study</title><link>https://jacobaloysious.in/post/learning-agent-evals-with-codex/</link><pubDate>Sun, 11 Oct 2026 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/learning-agent-evals-with-codex/</guid><description>&lt;p>I wanted to learn agent evaluations end to end, not just read about them. With Codex as a coding and teaching partner, I built a fictional customer-support agent, deployed it, and worked through a course project one step at a time: define behavior, test deterministic rules, measure a baseline, make a targeted change, and check what the evidence supports.&lt;/p>
&lt;p>&lt;strong>Part 1 of a two-part series:&lt;/strong> this post covers setup and evals.
&lt;a href="https://jacobaloysious.in/post/tracing-agent-across-vercel-openai/" target="_blank" rel="noopener">Part 2&lt;/a> is an engineer-focused observability deep dive.&lt;/p>
&lt;p>The result is a learning project, not a production support platform. The useful outcome was learning how to make agent behavior testable and improve it without treating a small demo score as a reliability guarantee.&lt;/p>
&lt;p>&lt;strong>Try it:&lt;/strong>
&lt;a href="https://ai-evals-support-agent.vercel.app/" target="_blank" rel="noopener">Live demo&lt;/a> ·
&lt;a href="https://github.com/jacobaloysious/ai-evals-support-agent" target="_blank" rel="noopener">Source repository&lt;/a> ·
&lt;a href="https://docs.google.com/document/d/1KSUlzN-C8z1q_gHduCi6eKgCzlHVV6u3dT0aypqi_YI/edit?tab=t.0" target="_blank" rel="noopener">Course syllabus&lt;/a>&lt;/p>
&lt;h2 id="start-with-policy-not-prompting">Start with policy, not prompting&lt;/h2>
&lt;p>The example is an online shop with fictional customers, orders, delivery promises, and refund requests. Before tuning a prompt, I wrote down what the system should allow:&lt;/p>
&lt;ul>
&lt;li>Order access comes from the server&amp;rsquo;s authenticated session, never a customer ID asserted in chat.&lt;/li>
&lt;li>Address changes must check ownership, shipment status, serviceability, and delivery timing.&lt;/li>
&lt;li>A mismatch between the requested state and postal PIN needs separate, explicit confirmation.&lt;/li>
&lt;li>Refund requests outside policy are declined automatically; eligible requests go to a human for review. The agent cannot approve or issue money.&lt;/li>
&lt;/ul>
&lt;p>That policy shaped the trust boundary. The model can select a tool and formulate arguments. Server-side tools apply ownership and policy checks against trusted session and fixture data. Tool descriptions help the model choose an action; they are not authorization controls. The human review capability is intentionally not exposed to the agent.&lt;/p>
&lt;h2 id="how-the-project-fits-together">How the project fits together&lt;/h2>
&lt;p>The browser sends a chat request to &lt;code>/api/demo&lt;/code>. A Vercel Node.js function runs the OpenAI Agents SDK. The SDK sends model requests to OpenAI, executes registered JavaScript tools on the server when requested, and feeds tool results back into the model. It then returns a response and correlation IDs to the browser.&lt;/p>
&lt;pre>&lt;code class="language-text">Browser → Vercel function → Agents SDK → OpenAI model
│ │
└─ local policy tools
→ tool result → model response
&lt;/code>&lt;/pre>
&lt;p>Vercel hosts and runs the application; it does not decide which tool the agent needs. The model proposes a tool call; the server executes that function and enforces policy. One browser submission can contain multiple model turns while remaining a single inbound HTTP request.&lt;/p>
&lt;p>The project is in GitHub so each lesson leaves a reviewable change. GitHub Actions runs unit/API tests and deterministic policy scenarios on pushes and pull requests. It does not call the live model: those evals require an API key, incur usage, and can vary. Live chat and model evals use &lt;code>OPENAI_API_KEY&lt;/code> on the server. I set it in Vercel&amp;rsquo;s project environment settings, alongside &lt;code>DEMO_SESSION_SECRET&lt;/code>; neither secret belongs in the browser bundle. Saving an environment variable requires a new deployment before the running function sees it. ChatGPT subscriptions and API Platform billing are separate (
&lt;a href="https://help.openai.com/en/articles/9039756-billing-settings-in-chatgpt-vs-platform" target="_blank" rel="noopener">OpenAI billing details&lt;/a>,
&lt;a href="https://vercel.com/docs/environment-variables/managing-environment-variables" target="_blank" rel="noopener">Vercel environment variables&lt;/a>).&lt;/p>
&lt;h2 id="build-an-evaluation-ladder">Build an evaluation ladder&lt;/h2>
&lt;p>I used different checks for different failure surfaces:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Unit and API tests&lt;/strong> check deterministic rules such as ownership denials, refund eligibility, and side effects. They run without a model.&lt;/li>
&lt;li>&lt;strong>Deterministic policy scenarios&lt;/strong> run proposals through server tools with fixed data and a controlled clock.&lt;/li>
&lt;li>&lt;strong>Live model evals&lt;/strong> send customer language to the agent and inspect tool results, state changes, and answer requirements.&lt;/li>
&lt;li>&lt;strong>Conversation evals&lt;/strong> preserve a transcript and shared fixture state. One scenario asks for a refund before the promised delivery date, advances the fixture clock, and asks again. The first request is declined; a later eligible one can enter the human review queue.&lt;/li>
&lt;li>&lt;strong>Grader tests and adversarial cases&lt;/strong> check that the eval rejects unsafe outcomes while accepting valid alternative wording and tool paths.&lt;/li>
&lt;/ol>
&lt;p>The evaluator needed testing too. A correct explanation that an order was “within its promised delivery window” initially failed because the phrase check was too narrow. A cross-account case required one particular tool to report denial even though another server-checked path had the same safe outcome. We changed the rubric to consider policy outcome, side effects, and response meaning instead of one phrase or one valid route.&lt;/p>
&lt;p>This is a useful quality-engineering distinction: a failed eval can mean the agent is wrong, the application is wrong, or the grader is wrong. A pass rate without failure classification hides those differences.&lt;/p>
&lt;h2 id="measure-a-baseline-then-make-a-narrow-change">Measure a baseline, then make a narrow change&lt;/h2>
&lt;p>For the optimization exercise, I chose three ordinary tasks: check an order status, list the signed-in account&amp;rsquo;s orders, and change an eligible address while reporting its revised delivery promise. I ran the same live model eval before changing the prompt or tool description.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Run&lt;/th>
&lt;th align="right">Passes&lt;/th>
&lt;th align="right">Average tool calls&lt;/th>
&lt;th align="right">Average elapsed time&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Baseline&lt;/td>
&lt;td align="right">2/3 (67%)&lt;/td>
&lt;td align="right">1.00&lt;/td>
&lt;td align="right">3,766 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Prompt instruction added&lt;/td>
&lt;td align="right">3/3 (100%)&lt;/td>
&lt;td align="right">1.33&lt;/td>
&lt;td align="right">4,802 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Prompt + tool-description guidance&lt;/td>
&lt;td align="right">3/3 (100%)&lt;/td>
&lt;td align="right">1.00&lt;/td>
&lt;td align="right">3,322 ms&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The baseline failure was specific: the address-change tool succeeded and returned a revised promised date, but the final answer omitted it. I updated the rubric to require the new delivery promise, added an instruction to include the date returned by the tool, and added a grader regression test.&lt;/p>
&lt;p>That fixed the answer requirement but raised average tool calls to 1.33. Inspecting the runs showed that for a clear address-change request, the agent first looked up the order and then called the address-change tool, which already checks ownership and shipment status. I clarified the tool description so the agent could call it directly when intent was clear, while retaining lookup for questions or ambiguity. The next run stayed at 3/3 and averaged one call per scenario. We reduced redundant work without weakening server checks.&lt;/p>
&lt;h2 id="read-the-numbers-honestly">Read the numbers honestly&lt;/h2>
&lt;p>These measurements compare the exact runs; they do not establish production quality or a statistically significant improvement. Three scenarios are a tiny sample. “100%” means 3 of 3 cases passed, not that the agent is 100% accurate. Elapsed time includes variable network and service conditions, so timing differences are suggestive only.&lt;/p>
&lt;p>Tool-call count is an efficiency proxy. This eval runner does not expose token usage, so it cannot support a dollar-cost calculation. One fewer tool call may reduce work, but it does not tell us how many tokens or dollars were saved. Once I used the optimization cases to guide changes, they were no longer a pristine holdout. I created separate final-validation cases—PIN/state mismatch, shipped order, and an untrusted lost-package claim—and ran them after the change.&lt;/p>
&lt;p>For a technical leader, the lesson is the measurement loop: define the customer outcome, set a baseline, inspect a failure, make a narrow change, and evaluate again. Keep outcome quality, safety/side effects, efficiency, and operational health separate; one score cannot stand in for all four.&lt;/p>
&lt;h2 id="continue-with-the-observability-deep-dive">Continue with the observability deep dive&lt;/h2>
&lt;p>The next post follows one submitted message across Vercel and OpenAI: how the request ID differs from the agent trace ID, what the log and span views contain, and where an engineer should investigate each failure. Read
&lt;a href="https://jacobaloysious.in/post/tracing-agent-across-vercel-openai/" target="_blank" rel="noopener">Tracing an AI Agent Across Vercel and OpenAI&lt;/a>.&lt;/p>
&lt;p>The
&lt;a href="https://github.com/jacobaloysious/ai-evals-support-agent" target="_blank" rel="noopener">GitHub repository&lt;/a> contains the code and lesson notes. The
&lt;a href="https://ai-evals-support-agent.vercel.app/" target="_blank" rel="noopener">live demo&lt;/a> uses fictional data. The project taught me to treat agent behavior as something to specify and measure, then make claims no stronger than the evidence.&lt;/p></description></item><item><title>Tracing an AI Agent Across Vercel and OpenAI: An Observability Deep Dive</title><link>https://jacobaloysious.in/post/tracing-agent-across-vercel-openai/</link><pubDate>Sun, 11 Oct 2026 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tracing-agent-across-vercel-openai/</guid><description>&lt;p>One chat message can produce a serverless invocation, several model API calls, and local tool executions. When the answer is wrong—or missing—the challenge is finding which system owns the failure.&lt;/p>
&lt;p>This post follows one request through a small support-agent project and shows what to inspect in Vercel Runtime Logs, OpenAI Agents SDK Traces, and the API Platform&amp;rsquo;s request and usage views. It is the engineering companion to
&lt;a href="https://jacobaloysious.in/post/learning-agent-evals-with-codex/" target="_blank" rel="noopener">Building and Evaluating an AI Agent&lt;/a>, which covers setup, eval design, baselines, and improvement decisions.&lt;/p>
&lt;p>&lt;strong>Part 2 of a two-part series:&lt;/strong> this deep dive is for engineers following a request through the runtime and agent execution layers.&lt;/p>
&lt;h2 id="one-request-multiple-observability-views">One request, multiple observability views&lt;/h2>
&lt;p>The browser posts to &lt;code>/api/demo&lt;/code>. A Vercel Node.js function calls the Agents SDK &lt;code>Runner.run&lt;/code>, which sends generation requests to OpenAI. If the model requests a tool, the SDK executes the registered JavaScript function in the server process and sends its result back to the model. The model can then make another generation before the answer returns.&lt;/p>
&lt;pre>&lt;code class="language-text">Customer message
│
▼
Browser ── POST /api/demo ──► Vercel Node.js function
│
├── platform request metadata
├── app console events ─────► Vercel Runtime Logs
│
└── Agents SDK Runner.run
│ traceId = this run
│ groupId = conversation
▼
OpenAI model ────────────► API request activity
│ generation span Usage Dashboard
│
└─ function span
▼
Local server tool/policy
│ result code
└──► model continues or answers
GitHub Actions / local eval runner ──► tests, outcomes, tool counts, elapsed time
&lt;/code>&lt;/pre>
&lt;p>Vercel executes the HTTP request; the Agents SDK orchestrates the run; the model generates text and proposes tool calls; server functions execute tools and enforce policy. One inbound request can contain multiple model calls. A later message creates a new request and trace, linked to the earlier one by the conversation group ID.&lt;/p>
&lt;h2 id="keep-the-identifiers-straight">Keep the identifiers straight&lt;/h2>
&lt;p>In this app, &lt;code>api/demo.js&lt;/code> generates a custom &lt;code>traceId&lt;/code> for each submitted chat turn and uses the browser&amp;rsquo;s &lt;code>conversationId&lt;/code> as the Agents SDK &lt;code>groupId&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-js">const traceId = `trace_${randomUUID().replaceAll(&amp;quot;-&amp;quot;, &amp;quot;&amp;quot;)}`;
const { conversationId } = body;
const runner = new Runner({
workflowName: &amp;quot;Northstar Shop support chat&amp;quot;,
traceId,
groupId: conversationId,
traceIncludeSensitiveData: false,
});
&lt;/code>&lt;/pre>
&lt;p>The IDs answer different questions:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Identifier&lt;/th>
&lt;th>Created by&lt;/th>
&lt;th>Use it to find&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Vercel request ID&lt;/td>
&lt;td>Vercel&lt;/td>
&lt;td>The platform invocation: route, status, timing, deployment, and runtime details.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>App &lt;code>traceId&lt;/code>&lt;/td>
&lt;td>&lt;code>api/demo.js&lt;/code>&lt;/td>
&lt;td>One submitted message&amp;rsquo;s agent run, including its model and tool spans in OpenAI Traces.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>conversationId&lt;/code> / SDK &lt;code>groupId&lt;/code>&lt;/td>
&lt;td>Browser/app&lt;/td>
&lt;td>The set of separate agent runs from one chat conversation.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenAI API request ID&lt;/td>
&lt;td>OpenAI API&lt;/td>
&lt;td>One individual model API request, useful when investigating that request&amp;rsquo;s status or service behavior.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Do not assume similarly named trace fields are interchangeable. Vercel can show its own request, invocation, or tracing metadata. Search OpenAI Traces with the &lt;strong>app trace ID printed in the Vercel application log&lt;/strong>; use the Vercel request ID to locate the platform request. For a whole multi-message chat, use the conversation ID/group ID.&lt;/p>
&lt;h2 id="what-is-logged-in-each-system">What is logged in each system&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>System&lt;/th>
&lt;th>What it owns&lt;/th>
&lt;th>What to inspect&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Browser&lt;/td>
&lt;td>Sends messages and renders responses.&lt;/td>
&lt;td>Network result and returned IDs; not authoritative for policy or ownership.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Vercel Runtime Logs&lt;/td>
&lt;td>Runs the deployed function.&lt;/td>
&lt;td>Method/path, status, duration, deployment, platform metadata, app &lt;code>console&lt;/code> lines. The app omits the full chat body.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenAI Agents SDK Traces&lt;/td>
&lt;td>Captures the agent run.&lt;/td>
&lt;td>Span hierarchy, generations, functions, timings, and run outcome. Default spans include task, agent, turn, generation, and function.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenAI API Platform&lt;/td>
&lt;td>Serves model inference.&lt;/td>
&lt;td>HTTP Requests view for individual failed/slow calls; Usage Dashboard for aggregate usage/cost. Separate from SDK traces and Vercel logs.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Server tools and policy code&lt;/td>
&lt;td>Checks session, ownership, eligibility, and allowed changes.&lt;/td>
&lt;td>Deterministic result codes and state. Fix policy/data bugs here and add tests.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Eval runner / GitHub Actions&lt;/td>
&lt;td>Measures scenarios and gates changes.&lt;/td>
&lt;td>Outcomes, pass rate, tool count, elapsed time. CI is deterministic; live evals are metered.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A &lt;strong>log&lt;/strong> is an event, a &lt;strong>span&lt;/strong> is a timed operation, a &lt;strong>trace&lt;/strong> connects spans for one run, and a &lt;strong>metric&lt;/strong> aggregates runs. A trace explains one execution; evals show behavior across cases.&lt;/p>
&lt;p>The app sets &lt;code>traceIncludeSensitiveData: false&lt;/code>, excluding LLM and tool inputs/outputs from traces while retaining spans. It separately logs safe result codes with the app trace ID. Thus the trace will not contain the customer&amp;rsquo;s full prompt, tool arguments, or tool-result body. See the
&lt;a href="https://openai.github.io/openai-agents-js/guides/tracing/" target="_blank" rel="noopener">Agents SDK tracing guide&lt;/a> and
&lt;a href="https://vercel.com/docs/logs/runtime" target="_blank" rel="noopener">Vercel Runtime Logs&lt;/a>.&lt;/p>
&lt;h2 id="correlate-a-vercel-log-with-an-openai-trace">Correlate a Vercel log with an OpenAI trace&lt;/h2>
&lt;p>The following IDs are invented and the lines are shortened, but the event names and app fields match the project. Vercel&amp;rsquo;s platform request metadata is shown separately from the app&amp;rsquo;s own log messages:&lt;/p>
&lt;pre>&lt;code class="language-text">Vercel platform: POST /api/demo status=200 duration=4.1s requestId=req_91…
app stdout: {&amp;quot;event&amp;quot;:&amp;quot;agent_run_started&amp;quot;,&amp;quot;conversationId&amp;quot;:&amp;quot;conv_7f…&amp;quot;,&amp;quot;traceId&amp;quot;:&amp;quot;trace_4a…&amp;quot;}
app stdout: {&amp;quot;event&amp;quot;:&amp;quot;agent_tool_result&amp;quot;,&amp;quot;traceId&amp;quot;:&amp;quot;trace_4a…&amp;quot;,&amp;quot;tool&amp;quot;:&amp;quot;request_shipping_fee_refund_review&amp;quot;,&amp;quot;code&amp;quot;:&amp;quot;REFUND_NOT_ELIGIBLE&amp;quot;}
app stdout: {&amp;quot;event&amp;quot;:&amp;quot;agent_run_completed&amp;quot;,&amp;quot;conversationId&amp;quot;:&amp;quot;conv_7f…&amp;quot;,&amp;quot;traceId&amp;quot;:&amp;quot;trace_4a…&amp;quot;,&amp;quot;turns&amp;quot;:5}
&lt;/code>&lt;/pre>
&lt;p>The actual &lt;code>turns&lt;/code> value is &lt;code>result.newItems?.length ?? 0&lt;/code>; it is a count of SDK result items, not necessarily model generations. The tool-result line includes a trace ID but not a conversation ID. That lets you connect the tool outcome to a run, while the start/completion lines connect that run to the conversation.&lt;/p>
&lt;p>In OpenAI Traces, the same run is represented as a span tree. The exact display labels can vary by SDK version; conceptually it looks like this:&lt;/p>
&lt;pre>&lt;code class="language-text">Northstar Shop support chat traceId=trace_4a… groupId=conv_7f…
└─ task / agent / turn
├─ generation span model decides whether a tool is needed
├─ function span request_shipping_fee_refund_review
│ payload omitted by trace redaction
└─ generation span model forms its final answer
&lt;/code>&lt;/pre>
&lt;p>The function span shows that the tool ran and where it fits in the agent loop. The Vercel &lt;code>agent_tool_result&lt;/code> event gives the safe outcome code, &lt;code>REFUND_NOT_ELIGIBLE&lt;/code>. Payload capture is disabled, so the trace omits the message and full tool input/output. Use the API Requests view for individual model-call errors or latency, the SDK trace for agent flow, and the Usage Dashboard for aggregate usage and cost.&lt;/p>
&lt;p>The API handler calls &lt;code>forceFlush()&lt;/code> before responding so the trace exporter has a chance to send the spans. If flushing fails, it writes &lt;code>agent_trace_flush_failed&lt;/code> with the same conversation and trace IDs. That event is useful when the Vercel request completed but the expected OpenAI trace is absent.&lt;/p>
&lt;h2 id="triage-by-symptom">Triage by symptom&lt;/h2>
&lt;p>Follow the failure to the system that owns it:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Browser shows 5xx or times out.&lt;/strong> Start in Vercel Runtime Logs. Filter by deployment, route, time, and status; inspect duration and &lt;code>agent_run_failed&lt;/code>. Check for &lt;code>agent_run_started&lt;/code> and server config such as &lt;code>OPENAI_API_KEY&lt;/code>. Fix the runtime or API boundary if the request failed before completion.&lt;/li>
&lt;li>&lt;strong>Request succeeds, but the model picks a poor tool or stops early.&lt;/strong> Use &lt;code>traceId&lt;/code> from &lt;code>agent_run_started&lt;/code> to inspect generation → function → generation. Improve instructions, tool descriptions, or eval scenarios. Keep authorization and policy checks in server code.&lt;/li>
&lt;li>&lt;strong>Right tool ran, but returned the wrong result or changed state incorrectly.&lt;/strong> Match &lt;code>agent_tool_result&lt;/code> by &lt;code>traceId&lt;/code>; inspect its code, session, fixture, and tool implementation. Fix domain logic and add a deterministic test.&lt;/li>
&lt;li>&lt;strong>Tool result is right, but the answer is incomplete.&lt;/strong> Compare the final generation with the rubric. Improve instructions or response construction and add a grader regression case. For critical dates or policy disclosures, deterministic text can give stronger guarantees.&lt;/li>
&lt;li>&lt;strong>Traces look fine, but pass rate or latency declines.&lt;/strong> Segment eval failures by scenario; compare tool counts and elapsed time, and check grader false positives. Improve coverage or measurement before optimizing. Three cases cannot establish a production trend.&lt;/li>
&lt;li>&lt;strong>Vercel shows a request, but OpenAI Traces does not.&lt;/strong> Search the app &lt;code>traceId&lt;/code>; check &lt;code>agent_trace_flush_failed&lt;/code>, deployment/time range, and whether this was hosted chat. Local deterministic controls produce no OpenAI trace.&lt;/li>
&lt;/ol>
&lt;p>Correlate IDs, classify the failure, and fix the layer that owns it. A real system also needs retention, access control, alerting, and a policy for sensitive trace data.&lt;/p>
&lt;h2 id="continue-the-evals-story">Continue the evals story&lt;/h2>
&lt;p>For the project setup, eval design, baseline numbers, and the changes those measurements motivated, read
&lt;a href="https://jacobaloysious.in/post/learning-agent-evals-with-codex/" target="_blank" rel="noopener">Building and Evaluating an AI Agent: A Hands-On Case Study&lt;/a>. The
&lt;a href="https://github.com/jacobaloysious/ai-evals-support-agent" target="_blank" rel="noopener">source repository&lt;/a> and
&lt;a href="https://ai-evals-support-agent.vercel.app/" target="_blank" rel="noopener">live demo&lt;/a> are available for trying the example with fictional data.&lt;/p></description></item></channel></rss>