TutorialsAugust 202611 min read

From idea to evidence: Build a business validator with eve and Blocks.ai.

By Dawid Urbas · Blocks.ai

Use a Vercel eve-based agent to turn a founder's rough pitch into a structured analysis, send it to the Blocks.ai Council of Agents, and return a clear pursue, pivot, or kill recommendation with practical experiments.

Build-time vs. runtime: Cursor uses Grok 4.5 as the coding harness that builds and tests this project. The completed eve agent itself uses anthropic/claude-sonnet-5 when it handles user requests.

The fastest way to get a bad answer from AI is to ask it whether or not your idea is a good one. A single model can produce a persuasive argument in either direction, and even with edits to the skills files, it doesn't expose (well) which assumptions actually determine the outcome.

In this tutorial, we explore a completely different approach. We use eve, Vercel's filesystem-first framework for durable agents, to build a venture analyst that prepares a consistent evidence pack. The agent then calls council_of_agents on the Blocks Network, where specialist judges examine feasibility, risk, and value before the eve agent makes its final recommendation.

The result at the end of this video will be a repeatable decision process that tells a founder what is known, what is merely assumed, and which cheap experiment should come next.

What you'll build using eve and Blocks.ai

By the end of this tutorial, you will have an eve agent that:

  • collects only the missing parts of a business idea
  • creates a concise analysis pack covering the problem, customer, solution, monetization, competition, risks, and value hypothesis
  • calls the Blocks.ai council_of_agents through a typed eve tool
  • collects the council's returned artifacts
  • produces a scored pursue, pivot, or kill verdict
  • recommends exactly three inexpensive experiments that could change the decision
  • can be verified end to end through eve's durable session stream.

This post turns the live demo into a repeatable build and explains how each part of the workflow fits together.

The resulting workflow

The agent follows one controlled path:

  • A founder describes a problem and proposed business.
  • The eve agent asks only for critical information that is still missing.
  • Its validate_business_idea skill turns the conversation into an analysis pack.
  • The typed call_council_of_agents tool sends the problem and proposed solution to Blocks.ai.
  • The Blocks Council evaluates feasibility, risk, and value, then returns its findings as artifacts.
  • The eve agent combines those findings with its own analysis.
  • The founder receives a verdict, scorecard, risks, and three next experiments.

The opinionated parts live in Markdown. The network boundary lives in TypeScript. That separation is one of the nicest properties of this build: changing the analysis method does not require rewriting the SDK integration, and changing the external agent does not require burying business policy in application code.

Before you begin

You need:

Step 1: Scaffold the eve project

Create the project with the standard command from the eve Getting Started guide:

shell
npx eve@latest init business-idea-validator
cd business-idea-validator
npm install @blocks-network/sdk@^1.0.11

The scaffold installs eve, ai, and Zod, creates the agent/ directory, and adds the dev, build, and start scripts. If the initializer starts the development interface, stop it with Ctrl+C before continuing.

Create the additional directories used by this agent:

shell
mkdir -p agent/lib agent/tools agent/skills/validate_business_idea

After the next steps, the authored part of the project will look like this:

business-idea-validator/
└── agent/
    ├── agent.ts
    ├── instructions.md
    ├── lib/
    │   └── blocks.ts
    ├── skills/
    │   └── validate_business_idea/
    │       └── SKILL.md
    └── tools/
        └── call_council_of_agents.ts

Step 2: Configure the eve runtime

Replace agent/agent.ts with:

typescript
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-sonnet-5",
});

This is the runtime model used by the finished agent. Grok 4.5 appears in the video as the model inside Cursor that authors the project; it is not the model serving user requests.

Sonnet 5 is not required for the pattern. You can replace the model ID with another eve-compatible model for which you have credentials. The rest of the agent and its Council integration stay the same.

Step 3: Give the agent a strict decision policy

Replace agent/instructions.md with the complete always-on policy:

markdown
# Identity

You are a tough but constructive venture analyst. You validate business ideas
with clear evidence, labeled assumptions, and actionable next steps. You do not
cheerlead weak ideas, and you do not dismiss strong ones without reason.

# Standing rules

1. Clarify the idea before judging. Ask only for missing critical fields:
   problem, ICP (ideal customer), solution, monetization, and stage. Do not dump
   a long form.
2. Produce evidence-based analysis. Label assumptions explicitly. Prefer
   specific risks and disconfirming tests over vague advice.
3. When validating a business idea, load the `validate_business_idea` skill and
   follow it.
4. Always call the `call_council_of_agents` tool before giving a final
   recommendation. Never invent a council verdict.
5. Return a structured scorecard covering: market, problem urgency,
   differentiation, go-to-market, risks, experiments, and a final verdict
   (`pursue` / `pivot` / `kill`).
6. Include an explicit **Council findings** section that quotes or faithfully
   summarizes the Blocks council result.

The critical rule is number four: external review is mandatory rather than an optional enhancement the runtime model may skip.

Step 4: Put the analysis method in an eve skill

Create agent/skills/validate_business_idea/SKILL.md:

markdown
---
description: Use when the user wants to validate, stress-test, score, or get a go/no-go on a business idea.
---

# Validate a business idea

Follow this procedure whenever the user asks you to validate a business idea.

## 1. Intake

Collect only what is missing from:

- **Problem** — who hurts, how often, how badly
- **ICP** — ideal customer profile
- **Solution** — what you would sell or build
- **Monetization** — how money is made
- **Stage** — idea, prototype, early revenue, or scaling

If enough of these are already in the conversation, do not re-ask.

## 2. Draft an analysis pack

Write a concise internal pack for you and for the council covering:

1. Problem and solution fit
2. Market and competition (named assumptions are acceptable)
3. Differentiation
4. Go-to-market sketch
5. Risks: feasibility, demand, distribution, and unit economics
6. Value hypothesis — why this could win

## 3. Call the council

Call `call_council_of_agents` with two fields:

- `problem` — the customer pain or decision being evaluated
- `proposedSolution` — the analysis pack, including the solution, ICP,
  monetization, competition, risks, and value hypothesis

Keep each field under approximately 8,000 characters. The council runs
specialist feasibility, risk, and value review.

Do not produce a final verdict until the tool returns.

## 4. Final scorecard

Merge your analysis with the council output into this structure:

### Verdict

One of: **pursue** / **pivot** / **kill**, with one sentence explaining why.

### Scorecard

| Dimension | Score (1–5) | Notes |
| --- | --- | --- |
| Market | | |
| Problem urgency | | |
| Differentiation | | |
| Go-to-market | | |
| Overall risk | | |

### Top risks

List 3–5 concrete risks, each with a disconfirming test.

### Next experiments

Give exactly three cheap experiments that would most change the decision.

### Council findings

Faithfully summarize, or briefly quote, what `council_of_agents` returned. Call
out agreement and disagreement with your own analysis.

eve discovers packaged skills from their directory and exposes the description as a routing hint. The model loads the full procedure only when the user's request matches it.

Step 5: Add the Blocks Network client

Create agent/lib/blocks.ts. This helper owns authentication, task submission, artifact collection, timeout handling, billing-mode fallback, and cleanup:

typescript
import {
  BillingModeMismatchError,
  TaskClient,
  decodeInlineArtifact,
  textPart,
} from "@blocks-network/sdk";

const COUNCIL_AGENT = "council_of_agents";
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
const PART_ID = "request";
const MAX_FIELD_CHARS = 8000;

export type CouncilCallResult = {
  agentName: string;
  taskId: string | null;
  terminalState: string;
  billingMode: "free" | "paid";
  partId: string;
  text: string;
  artifactCount: number;
};

function requireApiKey(): string {
  const key = process.env.BLOCKS_API_KEY?.trim();
  if (!key) {
    throw new Error(
      "BLOCKS_API_KEY is missing. Run `blocks login --write-env` from the project directory.",
    );
  }
  return key;
}

function truncate(value: string, label: string): string {
  const trimmed = value.trim();
  if (trimmed.length <= MAX_FIELD_CHARS) return trimmed;
  return `${trimmed.slice(0, MAX_FIELD_CHARS - 20)}\n…[truncated ${label}]`;
}

async function collectArtifactText(
  session: Awaited<ReturnType<TaskClient["sendMessage"]>>,
): Promise<{ text: string; artifactCount: number }> {
  const refs = session.listArtifacts();
  const chunks: string[] = [];

  for (const ref of refs) {
    try {
      const bytes =
        ref.kind === "inline" && ref.data
          ? decodeInlineArtifact(ref)
          : (await session.downloadArtifact(ref)).data;
      const decoded = new TextDecoder().decode(bytes).trim();
      if (decoded) chunks.push(decoded);
    } catch {
      // A binary or unavailable artifact should not hide readable findings.
    }
  }

  return {
    text: chunks.join("\n\n---\n\n"),
    artifactCount: refs.length,
  };
}

async function submitWithMode(
  apiKey: string,
  billingMode: "free" | "paid",
  payload: string,
  timeoutMs: number,
): Promise<CouncilCallResult> {
  const client = await TaskClient.create({ billingMode, apiKey });
  let session: Awaited<ReturnType<TaskClient["sendMessage"]>> | undefined;

  try {
    session = await client.sendMessage({
      agentName: COUNCIL_AGENT,
      requestParts: [textPart(payload, PART_ID)],
    });

    const terminal = await session.waitForTerminal(timeoutMs);
    const { text, artifactCount } = await collectArtifactText(session);

    return {
      agentName: COUNCIL_AGENT,
      taskId: session.taskId ?? null,
      terminalState: terminal.state,
      billingMode,
      partId: PART_ID,
      text:
        text ||
        `(Council task ${terminal.state} with no text artifacts. Task id: ${session.taskId ?? "unknown"})`,
      artifactCount,
    };
  } finally {
    session?.close();
    client.destroy();
  }
}

export async function callCouncilOfAgents(
  input: { problem: string; proposedSolution: string },
  options?: { timeoutMs?: number },
): Promise<CouncilCallResult> {
  const apiKey = requireApiKey();
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
  const payload = JSON.stringify({
    problem: truncate(input.problem, "problem"),
    proposed_solution: truncate(
      input.proposedSolution,
      "proposed_solution",
    ),
  });

  try {
    return await submitWithMode(apiKey, "free", payload, timeoutMs);
  } catch (error) {
    if (error instanceof BillingModeMismatchError) {
      return await submitWithMode(apiKey, "paid", payload, timeoutMs);
    }
    throw error;
  }
}

The request part ID is request, matching the Council agent's input contract. The client must also use the same billing mode as the registered agent, so the helper retries only the SDK's typed mismatch error.

Step 6: Expose the Council as a typed eve tool

Create agent/tools/call_council_of_agents.ts:

typescript
import { defineTool } from "eve/tools";
import { z } from "zod";
import { callCouncilOfAgents } from "../lib/blocks.js";

export default defineTool({
  description:
    "Submit a problem and proposed solution to Blocks Network council_of_agents for multi-judge review (feasibility, risk, value). Always call before a final go/no-go recommendation.",
  inputSchema: z.object({
    problem: z
      .string()
      .min(1)
      .describe(
        "The problem or decision to evaluate (business idea context, customer pain, constraints).",
      ),
    proposedSolution: z
      .string()
      .min(1)
      .describe(
        "The proposed solution, including ICP, monetization, differentiation, and key assumptions.",
      ),
  }),
  outputSchema: z.object({
    agentName: z.string(),
    taskId: z.string().nullable(),
    terminalState: z.string(),
    billingMode: z.enum(["free", "paid"]),
    partId: z.string(),
    text: z.string(),
    artifactCount: z.number(),
  }),
  async execute({ problem, proposedSolution }) {
    return callCouncilOfAgents({ problem, proposedSolution });
  },
  toModelOutput(output) {
    return {
      type: "text",
      value: [
        `Council (${output.agentName}) finished: ${output.terminalState}`,
        `Task: ${output.taskId ?? "unknown"}`,
        "",
        output.text,
      ].join("\n"),
    };
  },
});

The filename gives the tool its model-facing name. Zod validates both sides of the application boundary, while toModelOutput keeps the model context focused on the terminal state, task ID, and findings.

Step 7: Add credentials and compile the project

Install the Blocks CLI if it is not already available, authenticate, and let it write BLOCKS_API_KEY into the project environment file:

shell
npm install -g @blocks-network/cli
blocks login --write-env

Add the Vercel AI Gateway credential to the same .env file:

AI_GATEWAY_API_KEY=

blocks login --write-env supplies BLOCKS_API_KEY; do not replace or remove that entry when adding the gateway key. Keep both values out of prompts, screenshots, source files, and version control.

Add an explicit type-check script, then compile the project:

shell
npm pkg set scripts.typecheck="tsc"
npm run typecheck
npm run build

At this point, compilation must pass before you spend time or credits on a live model or Council call.

Step 8: Run the agent locally

Start eve's development interface:

shell
npm run dev

Now let's try it. Here, we start with a simple idea: selling lemonade from an existing stand on a parcel beside a crowded street, charging $2 per cup.

Try a prompt with enough context for the agent to begin, but leave a few genuine unknowns:

Validate my lemonade stand idea. I own a parcel beside a street that I believe has a lot of foot traffic, and I already have a stand. I want to sell lemonade for $2 per cup. Tell me whether I should pursue it and what I need to test first.

If working correctly, the agent should clarify important missing information, prepare its analysis pack, call the council, and wait for the real result before responding.

In the recorded run, the final verdict was pivot: run a small, structured pilot before treating the idea as a real business. The agent recognized the low cost of testing and the advantage of owning the land and stand, but it also surfaced the assumptions that had not been validated:

  • whether the street actually has useful pedestrian traffic
  • whether selling there is permitted
  • whether the founder's time is worth the likely return
  • what the stand can earn under real conditions

That is a good example of what pivot means in this system. It's not an acceptance or a rejection, it's a request to turn a cheap assumption into evidence.

What moved across the network

The workflow keeps a useful boundary between local execution and remote review:

  • The credentials are stored in the local environment. The runtime uses them for provider and Blocks.ai authentication, but never intentionally places them in the council task payload.
  • The eve agent sends a bounded analysis payload. It contains the problem and proposed solution, not the entire local workspace or raw conversation history.
  • Blocks.ai carries the task to the council. It routes the request to council_of_agents, and the council's findings return as task artifacts.
  • The final decision remains with the eve agent. It must report the council faithfully, but it merges those findings with its own scorecard, risks, and experiments.

The council is an advisory layer, not an oracle. Multiple model judgments can broaden a review, but they do not turn assumptions into customer evidence. The final output should always tell the founder what to test in the real world.

Why this pattern is useful beyond startup ideas

The same architecture works whenever one agent should prepare context and another should provide a specialized second opinion:

  • architecture reviews before implementation
  • security or compliance checks before deployment
  • pricing and positioning reviews before a launch
  • incident-response plans before execution
  • research synthesis reviewed by multiple domain specialists

The reusable pattern is simple: keep orchestration and durable conversation in eve, encode the process as a skill, put external access behind typed tools, and use Blocks.ai to reach the specialist agent.

Troubleshooting common challenges

The eve interface starts, but the first model turn fails

Check AI_GATEWAY_API_KEY in .env and confirm it has access to the runtime model. Restart the dev process after changing environment variables.

The first npm run build may also need network access while eve downloads the AI Gateway model catalog. If you deliberately use a custom or unlisted model, set modelContextWindowTokens in agent/agent.ts rather than guessing silently.

The council tool says BLOCKS_API_KEY is missing

Run blocks login --write-env from your project directory or add a valid key to .env, then restart eve. Do not paste the key into the agent conversation.

The Blocks task rejects the billing mode

The integration already retries a typed BillingModeMismatchError from free to paid. If the second call also fails, confirm that your account may call the target agent and that its current listing and billing configuration permit the request.

The council completes without readable findings

Use the returned task ID to inspect the task in Blocks.ai. The integration reports a no-artifact result explicitly rather than allowing the eve agent to invent council feedback.

Council runs can take several minutes, and an individual judge may time out while the overall task still returns useful findings. Treat warnings in the returned artifact as part of the evidence, not as text to hide.

Where to take it next

The demo proves the core loop, but a production version should add domain-specific evidence sources, evaluation cases, telemetry, and explicit approval policies for any tool that can create an external side effect. eve may replay a tool execution interrupted mid-step, so paid or otherwise side-effecting council calls also need an application-level idempotency or deduplication strategy. You could also store historical experiments and bring their measured results into the next council review.

For now, the most important outcome is already here: instead of asking one model for confidence, the founder gets a structured disagreement, a visible evidence gap, and a cheap next move.

Try the agent first, then explore the eve documentation, read the Blocks.ai docs, and replace the lemonade stand with an idea you care about.

Tell us what you think — give me your thoughts and feedback over on X at @getbold_.