There Is No Application · Chapter 6

From Idea to Agent: A Step-by-Step Tutorial

We’ve covered the philosophy, the architecture, and the tools. Now, it's time to build. In this chapter, we will walk through the entire process of creating a simple but complete AI agent from scratch.

Our agent will have a single competence: Given a topic, generate a brief summary and a list of key concepts.

This hands-on tutorial will bring together everything we've learned: the workspace setup from Chapter 3, and the core tools—Hono, Zod, the AI Gateway, and LangChain.js—from Chapter 4. By the end, you will have a working AI agent with a clear API, robust validation, and a defined, intelligent skill.

Step 1: Set Up the Project

Let's start with the foundation we created in Chapter 3. Create a new project directory called research-agent and populate it with the same package.json, tsconfig.json, and wrangler.toml files.

For this project, we need to add a few more dependencies to our package.json:

```json

{

"name": "research-agent",

"version": "0.1.0",

"private": true,

"type": "module",

"scripts": {

"dev": "wrangler dev src/index.ts",

"deploy": "wrangler deploy --minify src/index.ts",

"typecheck": "tsc --noEmit"

},

"devDependencies": {

"@cloudflare/workers-types": "^4.20260725.0",

"typescript": "^5.5.3",

"wrangler": "^3.64.0"

},

"dependencies": {

"hono": "^4.5.1",

"zod": "^3.23.8",

"@langchain/core": "^0.2.14",

"@langchain/cloudflare": "^0.2.1"

}

}

```

The new additions are zod for validation and the LangChain libraries for AI orchestration.

Run npm install to get everything set up.

Step 2: Define the API and Schemas

Next, let's define the agent's public interface. Our agent will expose a single POST endpoint at /research. It will accept a JSON object with a topic and return a JSON object with a summary and a list of concepts.

Using Zod, we can formally define these data structures. Create a new file at src/index.ts.

```typescript

// src/index.ts

import { Hono } from 'hono';

import { z } from 'zod';

import { zValidator } from '@hono/zod-validator';

// More imports will be added here later

// --- 1. Define Input and Output Schemas with Zod ---

// Input: What the user sends to us

const ResearchRequestSchema = z.object({

topic: z.string().min(5).max(100),

});

// Output: What the agent produces

const ResearchOutputSchema = z.object({

summary: z.string(),

concepts: z.array(z.string()),

});

// --- 2. Setup Hono Router ---

export type Bindings = {

AI: any;

};

const app = new Hono<{ Bindings: Bindings }>();

// --- 3. Create the Endpoint ---

app.post(

'/research',

// Use Hono's zod-validator middleware for clean input validation

zValidator('json', ResearchRequestSchema),

async (c) => {

// The middleware gives us validated data directly

const { topic } = c.req.valid('json');

// We'll add the agent's "brain" here in the next step

// For now, let's return a dummy response that matches our output schema

const dummyResponse = {

summary: This is a dummy summary about ${topic}.,

concepts: ["dummy concept", "example"],

};

return c.json(dummyResponse);

}

);

export default app;

```

Here, we've defined both the input and the output schemas. This is a crucial practice. We not only validate what comes in to our agent, but we also ensure what goes out conforms to the contract we've promised.

We also use a handy middleware from Hono, zValidator, which simplifies our validation logic. If the incoming JSON doesn't match ResearchRequestSchema, the middleware will automatically send back a 400 Bad Request error.

Run npm run dev and test this endpoint with a curl command:

```bash

curl -X POST http://localhost:8787/research \

-H "Content-Type: application/json" \

-d '{"topic": "The History of Cloudflare"}'

```

You should get the dummy response back. If you send a topic that's too short, you'll get a validation error. Our API boundary is secure.

Step 3: Build the Competence with LangChain.js

Now for the exciting part: building the agent's brain. We'll replace the dummy response with a real AI call orchestrated by LangChain.

We need a prompt that clearly instructs the LLM what to do and, critically, how to format its response.

Update your src/index.ts:

```typescript

// src/index.ts

import { Hono } from 'hono';

import { z } from 'zod';

import { zValidator } from '@hono/zod-validator';

// --- LangChain Imports ---

import { ChatCloudflareWorkersAI } from '@langchain/cloudflare';

import { PromptTemplate } from '@langchain/core/prompts';

import { LLMChain } from 'langchain/chains';

import { JsonOutputParser } from '@langchain/core/output_parsers';

// ... (Zod Schemas are the same)

export type Bindings = {

AI: any;

};

const app = new Hono<{ Bindings: Bindings }>();

app.post(

'/research',

zValidator('json', ResearchRequestSchema),

async (c) => {

const { topic } = c.req.valid('json');

// --- 1. Define the Prompt Template ---

// This prompt instructs the LLM on its task and the exact JSON format to return.

const template = `

You are a research assistant. Your goal is to provide a concise summary and a list of key concepts for a given topic.

Topic: {topic}

Provide your response as a JSON object with the following structure:

{{

"summary": "Your summary here.",

"concepts": ["Concept 1", "Concept 2", "Concept 3"]

}}

`;

const prompt = new PromptTemplate({

template,

inputVariables: ['topic'],

});

// --- 2. Instantiate the Model ---

// We use ChatCloudflareWorkersAI to connect to the AI Gateway.

const model = new ChatCloudflareWorkersAI({

model: '@cf/meta/llama-3-8b-instruct',

cloudflareApiToken: 'local', // Placeholder for local dev

// In production, the binding is used automatically.

// In local dev, you might need to configure credentials.

// For this simple case, the binding is often enough.

});

// --- 3. Create a Simple LLM Chain ---

// This chain combines the prompt, model, and an output parser.

const chain = new LLMChain({

llm: model,

prompt: prompt,

outputParser: new JsonOutputParser(), // Tells LangChain to expect and parse JSON output

});

// --- 4. Run the Chain ---

const result = await chain.invoke({ topic });

// --- 5. Validate the LLM's Output ---

// We don't blindly trust the LLM. We validate its output against our Zod schema.

const validation = ResearchOutputSchema.safeParse(result);

if (!validation.success) {

console.error("LLM output failed validation:", validation.error);

return c.json({ error: "The AI returned an unexpected response format." }, 500);

}

// --- 6. Return the Validated, Structured Response ---

return c.json(validation.data);

}

);

export default app;

```

Let's break down the competence we just built:

  1. PromptTemplate: We created a template that clearly defines the AI's role, its task, the input variable ({topic}), and most importantly, the exact JSON structure it must return.
  2. Model Instantiation: We create an instance of ChatCloudflareWorkersAI, which is LangChain's specific class for using the Cloudflare AI Gateway.
  3. LLMChain: We link the prompt and the model together in a simple chain. We also add a JsonOutputParser, which tells the chain to expect a JSON string from the model and parse it automatically.
  4. Invocation: We run the chain by calling .invoke() with the topic from our user's request.
  5. Output Validation: This is a critical step. We do not blindly trust the LLM's output. Even with a strong prompt, models can sometimes make mistakes or return malformed JSON. We parse the result of the chain through our ResearchOutputSchema. If it doesn't match, we return a server error instead of passing bad data to the user.
  6. Return: Only after the LLM's output has been validated do we return it to the user.

Now, run npm run dev and execute the curl command again. This time, you'll get a real, AI-generated summary and list of concepts, all neatly structured and validated.

The Blueprint for an Agent

What we've built here is the fundamental pattern for almost any agent competence. It contains all the core ideas of our new architectural paradigm:

  • A clearly defined API endpoint (Hono).
  • Robust input validation (Zod).
  • A core "thinking" process (LangChain prompt + model).
  • Secure and efficient model access (AI Gateway).
  • Strict output validation (Zod).

You now have a working AI agent. This simple but powerful blueprint is what you will expand upon as you build ever more complex and capable agents.

Want this thinking applied to your build?