Skip to content

Chapter 3 · 'use agent': The Functional Agent ​

Chapter goals: Fully understand the semantics of the 'use agent' directive, build a "Agent as a function" mental model, and grasp the design intent behind re-rendering instructions on every call.

3.1 Directive Semantics: A String Literal That Marks an Entire Module ​

'use agent' follows the same lineage as JavaScript's 'use strict' and React Server Components' 'use client' — it's just a string literal at the top of a file with no runtime behavior of its own; the build tool gives it meaning during scanning:

typescript
// src/agents/triage-agent.ts
'use agent'; // ① Must be the first statement in the file (comments exempt)
import { useModel } from '@flue/runtime';

// ② Build time: Flue scans all files with this directive,
//    registering each exported PascalCase function as an addressable agent
export function TriageAgent() {
  useModel('anthropic/claude-sonnet-4-6');
  return 'Investigate the reported issue and recommend the next action.';
}

// ③ A single file can export multiple agents
export function TriageSupervisor() {
  useModel('anthropic/claude-opus-4-6');
  return 'Review the triage results and escalate critical issues.';
}

Registration makes agents addressable by name from other parts of the application: flue run and createAgentRouter(TriageAgent) both rely on it.

3.2 Agent as Function: Three Key Properties ​

Property 1: The Return Value Is the System Prompt ​

The string returned by the function is rendered as the agent's system prompt, combined with user messages and assistant messages to form the complete context sent to the LLM:

typescript
'use agent';
import { useModel } from '@flue/runtime';

export function SupportAgent() {
  useModel('anthropic/claude-haiku-4-5');
  // This text is the agent's "personality + behavioral rules"
  return `
You are a customer support assistant. Rules:
1. Keep replies concise and professional;
2. Check order status before discussing refunds;
3. Proactively escalate issues you cannot resolve.`;
}

Property 2: Re-rendered Before Every Model Call ​

This is the biggest difference from ordinary functions. Before each round of model invocation, Flue re-executes your function from scratch, rebuilding the instructions. The returned string always reflects the agent's current state:

typescript
'use agent';
import { useModel, usePersistentState } from '@flue/runtime';

export function AdaptiveAgent() {
  const [resolved] = usePersistentState('resolved', false);

  useModel('anthropic/claude-haiku-4-5');

  // Instructions change dynamically with state — flipping resolved changes the prompt immediately
  return resolved
    ? 'Issue resolved. Please confirm satisfaction with the user and close the session.'
    : 'Actively investigate the issue reported by the user; provide troubleshooting steps incrementally.';
}

Cache warning

Inserting dynamic data (like timestamps) into instructions will bust the LLM's prompt cache, making inference tokens more expensive. Production agents should keep the static prefix of instructions stable.

Property 3: Can Receive ID as a Parameter ​

Agent functions can receive initialization parameters (such as a session ID), weaving business identifiers directly into instructions:

typescript
'use agent';
import { useModel } from '@flue/runtime';

// id comes from the --id flag or POST /:id route — semantics defined by you:
// can be a user ID, ticket number, GitHub issue number...
export function IssueAgent({ id }: { id: string }) {
  useModel('anthropic/claude-sonnet-4-6');
  return `Investigate GitHub issue #${id}, reproduce the problem, and suggest a fix.`;
}

3.3 Analogy with React Components ​

This design is not coincidental — Flue explicitly models agent functions after React render functions:

ReactFlue
Component function re-executes on each renderAgent function re-executes before every model call
useXxx() Hooks declare state and side effectsuseModel() etc. Hooks declare capabilities
JSX describes UIReturned string describes instructions
Props pass data{ id } and initial data pass business data
'use client' marks client components'use agent' marks agent modules

Once you understand this, all subsequent Hook behavior rules (call order, conditional mounting, re-run on each render) become intuitive.

3.4 Minimal Complete Example Recap ​

typescript
// Minimal runnable agent: three core lines of code
'use agent';
import { useModel } from '@flue/runtime';

export function Assistant() {
  useModel('anthropic/claude-haiku-4-5'); // ① Declare model (required)
  return 'You are a helpful assistant.';  // ② Return instructions
}
// ③ 'use agent' at the top of the file completes registration

Three iron rules:

  1. useModel() must be called exactly once per render — an agent without a model cannot start;
  2. Only exported PascalCase functions are registered as top-level agents; non-exported functions are ordinary functions;
  3. Return a string — returning other types is meaningless; instructions must be text.

3.5 Chapter Summary ​

  • 'use agent' is a file-level directive; the build process scans and registers all exported PascalCase functions;
  • Three agent function properties: return value = system prompt, re-rendered every round, can receive ID params;
  • Mental model = React component: Hooks declare capabilities, rendering produces "UI" (here, instructions);
  • Keep the static prefix of instructions stable to avoid busting the prompt cache.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What is the nature of the "use agent" directive?

2. When does the agent function get re-executed?

3. Which function gets registered as a top-level agent by Flue?

4. Why should production agents avoid inserting dynamic values like timestamps into instructions?

🛠️ Hands-On Exercises ​

  1. Export two agents (different models, different instructions) in one file; converse with each using flue run.
  2. Write an agent with usePersistentState that flips its instructions; observe the behavior change before and after the state flip.
  3. Intentionally add Date.now() to instructions and converse continuously; compare cache hit rates using the Provider's backend token stats.

The programming model is established. Next, learn how to choose models and design high-quality instructions.