diff --git a/.changeset/getting-started-onboarding-fixes.md b/.changeset/getting-started-onboarding-fixes.md new file mode 100644 index 0000000000..ae7a0fbf73 --- /dev/null +++ b/.changeset/getting-started-onboarding-fixes.md @@ -0,0 +1,5 @@ +--- +"@agent-native/core": patch +--- + +Rewrite getting started guide based on hands-on walkthrough diff --git a/packages/core/docs/content/getting-started.mdx b/packages/core/docs/content/getting-started.mdx index 72f148b717..9a2fb9b7b0 100644 --- a/packages/core/docs/content/getting-started.mdx +++ b/packages/core/docs/content/getting-started.mdx @@ -10,13 +10,18 @@ share the same [actions](/docs/actions), SQL data, and application state. Start with chat so users can talk to the agent immediately, then add the app surfaces your workflow earns. +The quickest way in is the **Chat template**: a minimal app that gives you a +working AI chat interface, durable threads, auth, and an `actions/` directory +ready to extend. It's the foundation most Agent-Native apps grow from. + The first useful path is: 1. Create a chat app. -2. Add one action. -3. Render the action result inline in chat. -4. Persist data in SQL. -5. Add a page the agent can open when visual inspection is better than another +2. Connect an AI engine so the agent can respond. +3. Add one action. +4. Render the action result inline in chat. +5. Persist data in SQL. +6. Add a page the agent can open when visual inspection is better than another paragraph in the transcript. Want a complete domain app instead? Clone a rich template such as @@ -29,7 +34,7 @@ Want a complete domain app instead? Clone a rich template such as You'll need [Node.js 22+](https://nodejs.org) and [pnpm](https://pnpm.io). -Create the minimal chat-first app: +Open a terminal, go to the directory where you want your Agent Native app, and run: ```bash npx @agent-native/core@latest create my-app --template chat @@ -42,103 +47,212 @@ This gives you durable chat threads, auth, live sync, an `actions/` directory, standard `view-screen` and `navigate` actions, and a small React app you can extend. -Run `create` with no flags if you want the CLI picker for domain templates and -advanced app shapes: +The dev server starts and opens `http://localhost:8080` in your browser. You may +notice a few startup log lines like `NitroViteError: Vite environment "nitro" is +unavailable`. These are a normal race condition during the initial boot and +resolve on their own. The app is ready once **VITE ready** displays in the +terminal output. + +When the app starts, you might find yourself on the login screen rather than in the app. In this case, just sign up with a made up login to satisfy the login screen. +For local development, no email verification is actually required. + +### If you want a different type of app + +This guide uses the Chat template because it is the shortest path +to an agentic application: the agent can act on day one, and you have a real app +surface to grow from. However, if you want to create a different kind of app, run `create` with no flags for the CLI picker for domain templates and advanced app shapes: ```bash npx @agent-native/core@latest create my-app ``` -The rest of this guide assumes the Chat template because it is the shortest path -to an agentic application: the agent can act on day one, and you have a real app -surface to grow from. +## 2. Connect an AI engine {#connect-ai} + +The agent chat can't respond until you connect an AI engine. After you're logged in, click the **Connect AI** button. + +You have two options: + +**Option A: Connect Builder.** Click **Connect Builder.io**, choose the Builder Space, and click the **Authorize** button. You don't have to manually provide any keys. + +**Option B: Add your own keys.** Enter your Anthropic or OpenAI API key. You can get an +Anthropic key at [console.anthropic.com](https://console.anthropic.com). + +Alternatively, create a `.env` file in the `my-app/` directory (the same +directory that contains `package.json`) before running `pnpm dev`: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +Restart the dev server. Once an AI engine is connected, the Setup panel +hides itself and the agent is ready to chat. + +> **Blank screen?** Create a `.env` file in your `my-app/` directory with +> `ANTHROPIC_API_KEY=sk-ant-...`, then restart `pnpm dev`. The in-app Setup +> panel only appears once the app has loaded, so a missing key that prevents +> the app from rendering needs to be fixed via the environment variable rather than +> the UI. + +## 3. Add an action {#add-an-action} -## 2. Add an action {#add-an-action} +An action is a typed operation that both your agent and your UI can call. It's +how the agent does things in your app. Actions live in the `actions/` directory +and can be triggered from chat, from React components, from the CLI, or on a +schedule. You define them once and call them from anywhere. -An action is one typed operation your agent and UI can both call. Replace the -starter `hello` action with the first real operation in your domain. This example -analyzes form responses and returns a validated shape for a custom chat chart: +### Try the starter action -```ts filename="actions/analyze-responses.ts" +The Chat template includes a `hello` action at `actions/hello.ts`: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +Run it from the terminal (inside your `my-app/` directory): + +```bash +pnpm action hello --name Alice +``` + +Or open your app at `http://localhost:8080` and ask the agent in the chat there: + +> Use the hello action with the name Alice. + +### Add your own action + +Replace the starter action with the first real operation in your domain. This example +counts words, sentences, and paragraphs in any text you pass it. It computes +everything locally, so there's nothing to configure and no external service to connect. + +Create a new file called `analyze-text.ts` in your `actions/` directory: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", + description: "Count words, sentences, and paragraphs in a block of text.", schema: z.object({ - formId: z.string().default("demo"), + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, + outputSchema: textStatsSchema, chatUI: { - renderer: "responses.response-chart", - title: "Response chart", + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, + run: async ({ text }) => ({ + title: "Text statistics", points: [ - { day: "Mon", responses: 12 }, - { day: "Tue", responses: 18 }, - { day: "Wed", responses: 24 }, - { day: "Thu", responses: 21 }, + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, ], }), }); ``` -Try it directly: +Try it from the terminal: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -Then ask the agent in the browser: +Or open your app at `http://localhost:8080` and ask the agent in the chat there: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### Define once, call from anywhere + +This action is now reachable from chat, React hooks, CLI, HTTP, MCP, A2A, +scheduled jobs, and webhooks. + +TIP: Any time you want the agent to call a specific action without ambiguity, phrasing it as "Run the `` action" is most reliable. Natural-language prompts work well once the agent has enough context about your app's domain. For a brand-new app with no data or context yet, explicit is safer. -> Analyze the recent responses for the demo form. +## 4. Render the result inline {#render-inline} -One action is now reachable from chat, React hooks, CLI, HTTP, MCP, A2A, -scheduled jobs, and webhooks. Define once, call from anywhere. +When the agent runs `analyze-text`, it returns structured data: a title and an +array of counts. By default the agent will describe that data in prose: "The +text has 9 words, 2 sentences..." and so on. That works, but you can +also render the result as a real UI component (a bar chart, a table, a card) +directly inside the chat transcript, right where the agent responded. -## 3. Render the result inline {#render-inline} +This is what `chatUI.renderer` in the action does. It's a label that says "when +this action's result appears in chat, hand it to this React component instead of +summarizing it in text." The component receives the validated action output as +props and renders whatever you want. -The action above declares `outputSchema` and a product-specific -`chatUI.renderer`, so the transcript does not have to flatten structured data -into prose. Register a React component for that exact renderer id, for example -by importing `app/chat-renderers.tsx` once from `app/root.tsx`: +In the next step, you'll create `app/chat-renderers.tsx`, but first, add one import line +to `app/root.tsx` so it runs on startup: -```tsx +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +Add it alongside your other imports at the top of the file. That's the only +change to `root.tsx`. The import just ensures the file runs and registers the +renderer. Now create the renderer file: + +```tsx filename="app/chat-renderers.tsx" import { registerActionChatRenderer, type ToolRendererProps, } from "@agent-native/core/client/chat"; -type ResponseChartResult = { +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => (
- {point.day} + {point.label}
))}
@@ -147,20 +261,21 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Chat now renders your app's own chart component inline, with the validated action -result as props through the renderer context. +Once the renderer is registered, the agent's response looks like this. Instead +of a paragraph of text, your React component renders directly inside the chat +transcript:
User

Analyze demo responses.

Agent

Rendered with responses.response-chart.

Response chart

Mon
Tue
Wed
Thu
" + "
User

Run the analyze-text action on \"Hello world. How are you today?\"

Agent

Rendered with text.stats-chart.

Text statistics

Characters
Words
Sentences
Paragraphs
" } /> @@ -179,69 +294,279 @@ summary/chart/table cards. See [Native Chat UI](/docs/native-chat-ui). For temporary controls the agent creates at runtime, see [Generative UI](/docs/generative-ui). -## 4. Persist data in SQL {#persist-data} +## 5. Persist data in SQL {#persist-data} + +Right now, every time the agent runs `analyze-text` the result appears in chat +and then disappears. There's nothing to look back at, nothing the agent can +reference later, and no way to build a page around the data. Persisting to SQL +fixes that: the agent writes results to a table, and both the agent and your UI +can read them back at any time. + +Agent-Native apps have a SQL database available by default: SQLite locally, +and your configured provider (Postgres, Turso/libSQL, Cloudflare D1) in +production. + +### Wire up the database plugin + +The Chat template doesn't include a database plugin by default. Create +`server/plugins/db.ts` to initialize it. This is what runs migrations and +makes the database available to your actions: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` + +Each entry in the array is an additive migration. When you add new columns or +tables later, append a new version object. Never edit existing ones. -Inline chat is great for the first result. A real app needs durable data the -agent can add, update, and revisit. Add a response-insights table in your schema -and keep reads/writes behind actions: +### Define the schema + +Create `server/db/schema.ts`. The `server/db/` directory may not exist yet, +so create it if needed. This file describes your tables using typed helpers so +your actions get full TypeScript autocomplete: ```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -Use the framework schema helpers rather than `sqliteTable`, `pgTable`, or -dialect-specific column imports. They choose the configured SQL backend, so the -same schema can run locally on SQLite and in production on Postgres, -Turso/libSQL, D1, or another supported SQL provider. +Use the framework schema helpers (`table`, `text`, `integer`, `now`) rather than +`sqliteTable`, `pgTable`, or dialect-specific imports. They pick the configured +SQL backend automatically, so the same schema runs locally on SQLite and in +production on any supported provider. + +After adding both files, restart the dev server so the migration runs: + +```bash +pnpm dev +``` + +Look for these two lines in the terminal output. They confirm the table was created: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` -Then split the work into focused actions: +The `NitroViteError` lines, `BETTER_AUTH_SECRET` warning, and +`SECRETS_ENCRYPTION_KEY` warning that also appear are normal for local dev and +can be ignored. -- `create-response-insight` writes a new insight row. -- `list-response-insights` reads the rows for the page. -- `update-response-insight` edits an existing row after the agent learns more. -- `analyze-responses` can call the same internal helper and return the native - chat widget for the latest insight. +### Add actions for the table -The important rule is that the agent and UI share these actions. Do not add a -separate REST route just for the browser if an action is the operation. +Now create the action files that read and write the table. These go in your +`actions/` directory, the same place as `hello.ts` and `analyze-text.ts`. You +create them yourself, one file per operation. The agent and your UI will call +them the same way they call any other action. -## 5. Add a page the agent can open {#add-a-page} +**`actions/save-text-analysis.ts`** writes a result row to the database. +Call this after running `analyze-text` to make the result durable: -Now add a durable page for the data behind the chat result: +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -```tsx filename="app/routes/response-insights.tsx" +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`** reads all saved results. The agent can +call this to summarize past analyses, and your UI can use it to populate a page: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** removes a row by id: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +Once these files are saved the dev server picks them up automatically. No +restart needed. Try listing analyses from the terminal: + +```bash +pnpm action list-text-analyses +``` + +You should see an empty array. The table exists and the action works; there's +just nothing saved yet: + +``` +[] +``` + +Data is saved to `data/app.db`, a SQLite file in your project directory that +gets created automatically on first run. In production you'd point +`DATABASE_URL` at a hosted database instead, but locally this file is all you +need. + +To save something, first run `analyze-text` to get the counts: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +You'll see output like: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +Then pass those values to `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +Now run `list-text-analyses` again and you'll see the saved row: + +```bash +pnpm action list-text-analyses +``` + +Or ask the agent in the chat at `http://localhost:8080` to do both steps at once: + +> Run analyze-text on "Hello world", then save the result. + +## 6. Add a page the agent can open {#add-a-page} + +Chat is great for conversational interaction, but some data is better inspected +in a dedicated UI: a table you can scan, sort, or delete rows from. This step +adds a React route that displays everything saved in `text_analyses`, using the +same `list-text-analyses` and `delete-text-analysis` actions you already wrote. +There's no second data layer. The page is just a view over the same SQL state +the agent reads and writes. + +Create the route file at `app/routes/text-analyses.tsx`. Route files in +`app/routes/` are automatically picked up by the framework. The filename +becomes the URL path, so this page will be available at +`http://localhost:8080/text-analyses`. + +```tsx filename="app/routes/text-analyses.tsx" import { useActionMutation, useActionQuery, } from "@agent-native/core/client/hooks"; -export default function ResponseInsightsRoute() { - const insights = useActionQuery("list-response-insights", {}); - const createInsight = useActionMutation("create-response-insight"); + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); return ( -
+
-

Response insights

-

Insights the agent created from form responses.

+

Text analyses

+

+ Results saved by the agent or triggered manually. +

- -
- {insights.data?.map((insight) => ( -
-

{insight.title}

-

{insight.summary}

+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+
))}
@@ -250,29 +575,66 @@ export default function ResponseInsightsRoute() { } ``` -The page is not a second implementation. It is a projection of SQL state written -through the same actions the agent uses. +`useActionQuery` calls `list-text-analyses` and keeps the result live. If the +agent saves a new row while the page is open, it appears automatically. +`useActionMutation` calls `delete-text-analysis` when the user clicks Delete, +then invalidates the query so the list refreshes. + +Open `http://localhost:8080/text-analyses` in your browser. If you saved an +analysis in the previous step you'll see it listed. Then ask the agent in chat: + +> Open the text analyses page. + +If you get a 404, try restarting your dev server. + +The agent calls the `navigate` action (already included in the Chat +template) to send the browser to `/text-analyses`. This is what it looks like +with a few saved rows:

Response insights

Insights the agent created from form responses.

Pricing theme

Users ask for a team plan.

Onboarding drop-off

Three steps need clearer copy.

Demo form42 responses analyzed
Next stepReview two draft insights
" + "

Text analyses

Results saved by the agent or triggered manually.

Hello world

2 words · 11 characters · 1 sentence

Delete

The quick brown fox jumps over the lazy dog.

9 words · 44 characters · 1 sentence

Delete

Pack my box with five dozen liquor jugs.

8 words · 40 characters · 1 sentence

Delete
" } /> -## 6. Let the agent navigate {#agent-navigation} +## 7. Extend the navigation {#extend-navigation} + +The sidebar is configured in `app/config/nav.ts`. Open it and add an entry for +the Text analyses page: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +Save the file. The dev server picks up the change automatically and the sidebar +updates without a restart. + +### Agent navigation + +The sidebar link lets users navigate manually. The agent can also open pages on +its own using two built-in actions that ship with the Chat template: -The Chat template includes `view-screen` and `navigate` actions. Extend them as -your app grows: +- **`view-screen`** reads the current route and returns a compact summary of + what the user is looking at. +- **`navigate`** writes a same-origin path to the browser's history. -- `view-screen` should read application state and return the current route, - selected insight, active filters, and any compact page context the agent needs. -- `navigate` should write a same-origin path such as `/response-insights` when - the agent decides the user should inspect the result visually. -- Action results can include links such as `href: "/response-insights"` so chat - can offer an explicit "Open response insights" button. +As you add more pages, keep `navigate` updated so the agent knows what +destinations exist. Document available paths in `AGENTS.md` so the model can +reason about them. When the app has both a full-page chat route and an app page, use the shared chat handoff helpers described in [Agent Surfaces](/docs/agent-surfaces#rich-chat): @@ -295,7 +657,7 @@ my-app/ ## Want a full analytics starting point? {#analytics-starting-point} -The response-insights example above is intentionally small so you can see the +The text-analyses example above is intentionally small so you can see the framework pieces. If you are building a real analytics product, start from [Analytics](/docs/template-analytics) instead. It is the robust starting point: connect your providers, use the existing dashboards and agent actions, then @@ -303,15 +665,15 @@ customize the app from there. ## What's next {#next} -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks, and transport. -- **[Native Chat UI](/docs/native-chat-ui)** — render action results as tables, +- **[Actions](/docs/actions)**: schemas, auth, approvals, hooks, and transport. +- **[Native Chat UI](/docs/native-chat-ui)**: render action results as tables, charts, and typed cards. -- **[Chat Template](/docs/template-chat)** — the minimal chat-first app you just +- **[Chat Template](/docs/template-chat)**: the minimal chat-first app you just created. -- **[Analytics Template](/docs/template-analytics)** — a robust analytics app +- **[Analytics Template](/docs/template-analytics)**: a robust analytics app starting point; connect providers and customize from there. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, +- **[Context Awareness](/docs/context-awareness)**: `view-screen`, `navigate`, route state, and selected objects. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, +- **[Agent Surfaces](/docs/agent-surfaces)**: chat, inline UI, app pages, embedded sidecars, automation, and external agents. -- **[Deployment](/docs/deployment)** — put your app on your own domain. +- **[Deployment](/docs/deployment)**: put your app on your own domain. diff --git a/packages/core/docs/content/locales/ar-SA/getting-started.mdx b/packages/core/docs/content/locales/ar-SA/getting-started.mdx index 41aa2e8d59..a6a99601f6 100644 --- a/packages/core/docs/content/locales/ar-SA/getting-started.mdx +++ b/packages/core/docs/content/locales/ar-SA/getting-started.mdx @@ -1,23 +1,40 @@ --- title: "الخطوات الأولى" -description: "أنشئ agentic app تبدأ بالدردشة، أضف action، اعرض النتائج المنظمة داخل الدردشة، ثم وسعها إلى صفحة دائمة يستطيع agent فتحها." +description: "أنشئ تطبيقاً وكيلياً يرتكز على المحادثة، وأضف إجراءً، واعرض النتائج المنظَّمة مضمَّنةً، ثم طوِّره ليصبح صفحةً دائمة يستطيع الوكيل فتحها." --- # الخطوات الأولى -Agent-Native مخصص لبناء agentic applications: يشترك AI agent وواجهة UI في نفس [actions](/docs/actions) وبيانات SQL و application state. المسار الأساسي يبدأ من Chat app كي يستطيع المستخدمون التحدث مع agent فورًا، ثم يضيف أسطح التطبيق التي يحتاجها سير العمل. +Agent-Native مخصَّص للتطبيقات الوكيلية: التطبيقات التي يتشارك فيها وكيل الذكاء الاصطناعي وواجهة المستخدم +نفس [الإجراءات](/docs/actions) وبيانات SQL وحالة التطبيق. ابدأ +بالمحادثة حتى يتمكن المستخدمون من التحدث مع الوكيل فوراً، ثم أضف أسطح التطبيق +التي يستحقها سير عملك. -المسار المفيد: +أسرع طريقة للبدء هي **قالب المحادثة**: تطبيق بسيط يمنحك +واجهة محادثة ذكاء اصطناعي جاهزة للعمل، وخيوط محادثة دائمة، ومصادقة، ودليل `actions/` +جاهز للتوسيع. إنه الأساس الذي تنمو منه معظم تطبيقات Agent-Native. -1. إنشاء Chat app. -2. إضافة action. -3. عرض نتيجة action داخل الدردشة كواجهة أصلية. -4. حفظ البيانات في SQL. -5. إضافة صفحة يستطيع agent فتحها عندما يكون الفحص البصري أنسب. +المسار الأول المفيد هو: -## 1. أنشئ Chat app {#create-your-app} +1. إنشاء تطبيق محادثة. +2. توصيل محرك ذكاء اصطناعي حتى يتمكن الوكيل من الرد. +3. إضافة إجراء واحد. +4. عرض نتيجة الإجراء مضمَّنةً في المحادثة. +5. حفظ البيانات في SQL. +6. إضافة صفحة يستطيع الوكيل فتحها عندما يكون الفحص المرئي أفضل من فقرة أخرى + في نص المحادثة. -تحتاج إلى [Node.js 22+](https://nodejs.org) و[pnpm](https://pnpm.io). +هل تريد تطبيق نطاق كامل عوضاً عن ذلك؟ استنسخ قالباً غنياً مثل +[Mail](/docs/template-mail)، أو [Calendar](/docs/template-calendar)، +أو [Forms](/docs/template-forms)، أو [Analytics](/docs/template-analytics)، أو +[Plan](/docs/template-plan). هل تريد عدم وجود واجهة مستخدم متصفح في الوقت الحالي؟ راجع +[تطبيقات الأتمتة أولاً](/docs/pure-agent-apps) بعد هذا الدليل التعليمي. + +## 1. إنشاء تطبيق محادثة {#create-your-app} + +ستحتاج إلى [Node.js 22+](https://nodejs.org) و[pnpm](https://pnpm.io). + +افتح طرفية، وانتقل إلى الدليل الذي تريد وضع تطبيق Agent Native فيه، ثم شغِّل: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +43,207 @@ pnpm install pnpm dev ``` -يتضمن القالب durable chat threads و auth و live sync ومجلد `actions/` و actions القياسية `view-screen` و `navigate` وتطبيق React يمكن توسيعه. +يمنحك هذا خيوط محادثة دائمة، ومصادقة، ومزامنة مباشرة، ودليل `actions/`، +وإجراءَي `view-screen` و`navigate` الافتراضيَّين، وتطبيق React صغيراً قابلاً للتوسيع. + +يبدأ خادم التطوير ويفتح `http://localhost:8080` في متصفحك. قد +تلاحظ بعض سطور سجل بدء التشغيل مثل `NitroViteError: Vite environment "nitro" is +unavailable`. هذه حالة تعارض عادية أثناء التشغيل الأولي وتُحلّ من تلقاء نفسها. يكون التطبيق جاهزاً بمجرد ظهور **VITE ready** في مخرجات الطرفية. + +عند بدء تشغيل التطبيق، قد تجد نفسك في شاشة تسجيل الدخول بدلاً من التطبيق. في هذه الحالة، ما عليك سوى التسجيل بمعلومات تسجيل دخول وهمية لتخطي شاشة تسجيل الدخول. +في بيئة التطوير المحلية، لا يلزم التحقق الفعلي من البريد الإلكتروني. + +### إذا كنت تريد نوعاً مختلفاً من التطبيقات + +يستخدم هذا الدليل قالب المحادثة لأنه أقصر مسار +لتطبيق وكيلي: يستطيع الوكيل التصرف من اليوم الأول، ولديك سطح تطبيق حقيقي +تنمو منه. ومع ذلك، إذا أردت إنشاء نوع مختلف من التطبيقات، شغِّل `create` بدون أعلام للحصول على منتقي CLI لقوالب النطاق وأشكال التطبيقات المتقدمة: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. توصيل محرك ذكاء اصطناعي {#connect-ai} + +لا تستطيع محادثة الوكيل الرد حتى تقوم بتوصيل محرك ذكاء اصطناعي. بعد تسجيل الدخول، انقر على زر **Connect AI**. + +لديك خياران: + +**الخيار أ: توصيل Builder.** انقر على **Connect Builder.io**، واختر Builder Space، ثم انقر على زر **Authorize**. لا داعي لتوفير أي مفاتيح يدوياً. -## 2. أضف action {#add-an-action} +**الخيار ب: إضافة مفاتيحك الخاصة.** أدخل مفتاح Anthropic أو OpenAI API. يمكنك الحصول على +مفتاح Anthropic من [console.anthropic.com](https://console.anthropic.com). + +بدلاً من ذلك، أنشئ ملف `.env` في دليل `my-app/` (نفس +الدليل الذي يحتوي على `package.json`) قبل تشغيل `pnpm dev`: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` -Action هي عملية typed operation مشتركة بين agent و UI. في هذا المسار أنشئ `actions/analyze-responses.ts`: تحلل ردود النماذج وتعيد شكلاً موثقًا لرسم مخصص داخل الدردشة. +أعد تشغيل خادم التطوير. بمجرد توصيل محرك الذكاء الاصطناعي، تختفي لوحة الإعداد +وسيكون الوكيل جاهزاً للمحادثة. - -```ts -// actions/analyze-responses.ts +> **شاشة فارغة؟** أنشئ ملف `.env` في دليل `my-app/` الخاص بك مع +> `ANTHROPIC_API_KEY=sk-ant-...`، ثم أعد تشغيل `pnpm dev`. تظهر لوحة الإعداد داخل التطبيق فقط بمجرد تحميل التطبيق، لذا يجب إصلاح المفتاح المفقود الذي يمنع +> التطبيق من العرض عبر متغير البيئة بدلاً من +> واجهة المستخدم. + +## 3. إضافة إجراء {#add-an-action} + +الإجراء هو عملية مكتوبة يمكن لكل من وكيلك وواجهة المستخدم الخاصة بك استدعاؤها. هكذا +يقوم الوكيل بالأشياء في تطبيقك. تعيش الإجراءات في دليل `actions/` +ويمكن تشغيلها من المحادثة، أو من مكونات React، أو من CLI، أو وفق جدول زمني. تُعرِّفها مرة واحدة وتستدعيها من أي مكان. + +### جرِّب الإجراء التمهيدي + +يتضمن قالب المحادثة إجراء `hello` في `actions/hello.ts`: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +شغِّله من الطرفية (داخل دليل `my-app/`): + +```bash +pnpm action hello --name Alice +``` + +أو افتح تطبيقك على `http://localhost:8080` واسأل الوكيل في المحادثة هناك: + +> Use the hello action with the name Alice. + +### أضف إجراءك الخاص + +استبدل الإجراء التمهيدي بأول عملية حقيقية في نطاقك. يعدّ هذا المثال +الكلمات والجمل والفقرات في أي نص تمرره إليه. يحسب +كل شيء محلياً، لذا لا يوجد شيء لتهيئته ولا خدمة خارجية للتوصيل: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "مخطط الردود" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -شغلها مباشرة: +جرِّبه من الطرفية: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -أو اسأل agent في المتصفح: +أو افتح تطبيقك على `http://localhost:8080` واسأل الوكيل في المحادثة هناك: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### عرِّفه مرة واحدة، واستدعه من أي مكان + +يمكن الآن الوصول إلى هذا الإجراء من المحادثة، وخطافات React، وCLI، وHTTP، وMCP، وA2A، +والمهام المجدولة، والـ webhooks. + +تلميح: في أي وقت تريد أن يستدعي الوكيل إجراءً محدداً دون غموض، فإن صياغته على هيئة "Run the `` action" هي الأكثر موثوقية. تعمل مطالبات اللغة الطبيعية بشكل جيد بمجرد أن يكون لدى الوكيل سياق كافٍ حول نطاق تطبيقك. بالنسبة لتطبيق جديد تماماً بدون بيانات أو سياق بعد، فالصياغة الصريحة أأمن. -> حلّل الردود الأخيرة لنموذج العرض. +## 4. عرض النتيجة مضمَّنةً {#render-inline} -## 3. اعرض النتيجة داخل الدردشة {#render-inline} +عندما يشغِّل الوكيل `analyze-text`، فإنه يُعيد بيانات منظَّمة: عنوان ومصفوفة +من الأعداد. افتراضياً سيصف الوكيل تلك البيانات بنثر: "النص يحتوي على 9 كلمات، و2 جملة..." وهكذا. يعمل هذا، لكن يمكنك أيضاً +عرض النتيجة كمكوِّن واجهة مستخدم حقيقي (رسم بياني شريطي، جدول، بطاقة) +مباشرةً داخل نص المحادثة، في المكان الذي استجاب فيه الوكيل تماماً. -لأن action تعلن `outputSchema` و `chatUI.renderer` خاصًا بالمنتج، لا يحول transcript البيانات المنظمة إلى نص عادي. سجّل مكون React لذلك المعرّف نفسه عند بدء التطبيق، مثل استيراد `app/chat-renderers.tsx` مرة واحدة من `app/root.tsx`: +هذا ما يفعله `chatUI.renderer` في الإجراء. إنه تسمية تقول "عندما +تظهر نتيجة هذا الإجراء في المحادثة، سلِّمها إلى مكوِّن React هذا بدلاً من +تلخيصها في نص." يتلقى المكوِّن مخرجات الإجراء المتحقق منها كخصائص +ويعرض ما تريده. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +في الخطوة التالية، ستنشئ `app/chat-renderers.tsx`، لكن أولاً، أضف سطر استيراد واحد +إلى `app/root.tsx` حتى يعمل عند بدء التشغيل: -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +أضفه إلى جانب استيراداتك الأخرى في أعلى الملف. هذا هو التغيير الوحيد +على `root.tsx`. الاستيراد فقط يضمن تشغيل الملف وتسجيل المُعرض. الآن أنشئ ملف المُعرض: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +252,386 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -للمخرجات العامة القابلة لإعادة الاستخدام، يوفّر framework أيضًا renderers مدمجة هي `data-chart` و `data-table`، إضافة إلى `data-insights` لبطاقات تجمع الملخص والرسم والجدول. راجع [Native Chat UI](/docs/native-chat-ui). - - -
User

حلّل ردود العرض.

Agent

تم العرض باستخدام responses.response-chart.

مخطط الردود

إث
ثل
أر
خم
" - } - /> - +بمجرد تسجيل المُعرض، يبدو رد الوكيل هكذا. بدلاً من +فقرة نصية، يُعرض مكوِّن React الخاص بك مباشرةً داخل نص المحادثة. + +استخدم هذه الخطوة عندما تنتمي النتيجة إلى المكان الذي يتحدث فيه الوكيل: + +- ملخصات الإعداد +- التقارير القصيرة +- الموافقات +- الجداول أو الرسوم البيانية الصغيرة بما يكفي للفحص المضمَّن +- الروابط إلى مشاهدات التطبيق الدائمة + +للمخرجات العامة القابلة لإعادة الاستخدام، يشحن الإطار أيضاً مُعرضات +`data-chart` و`data-table` المدمجة، بالإضافة إلى `data-insights` لبطاقات الملخص/الرسم البياني/الجدول المجمَّعة. راجع [واجهة المحادثة الأصيلة](/docs/native-chat-ui). لعناصر التحكم المؤقتة التي ينشئها الوكيل في وقت التشغيل، راجع +[واجهة المستخدم التوليدية](/docs/generative-ui). + +## 5. حفظ البيانات في SQL {#persist-data} + +في الوقت الحالي، في كل مرة يشغِّل فيها الوكيل `analyze-text` تظهر النتيجة في المحادثة +ثم تختفي. لا يوجد ما يمكن الرجوع إليه لاحقاً، ولا ما يمكن للوكيل الرجوع إليه، +ولا طريقة لبناء صفحة حول البيانات. يُصلح الحفظ في SQL ذلك: يكتب الوكيل النتائج في جدول، ويمكن لكل من الوكيل وواجهة المستخدم قراءتها في أي وقت. + +تمتلك تطبيقات Agent-Native قاعدة بيانات SQL متاحة افتراضياً: SQLite محلياً، +ومزوِّدك المُهيَّأ (Postgres، أو Turso/libSQL، أو Cloudflare D1) في +الإنتاج. + +### توصيل ملحق قاعدة البيانات + +لا يتضمن قالب المحادثة ملحق قاعدة بيانات افتراضياً. أنشئ +`server/plugins/db.ts` لتهيئته. هذا ما يشغِّل عمليات الترحيل +ويجعل قاعدة البيانات متاحة لإجراءاتك: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. احفظ البيانات في SQL {#persist-data} +كل إدخال في المصفوفة هو ترحيل إضافي. عندما تضيف أعمدة أو +جداول جديدة لاحقاً، أضف كائن إصدار جديد. لا تُعدِّل الإصدارات الموجودة أبداً. -عندما تحتاج النتيجة إلى المراجعة أو المقارنة أو التحديث، احفظها في SQL واجعل القراءة والكتابة خلف actions: +### تعريف المخطط -عرّف الجدول باستخدام helpers المحمولة من framework، وليس عبر imports من `drizzle-orm/sqlite-core` أو `drizzle-orm/pg-core`: +أنشئ `server/db/schema.ts`. قد لا يكون دليل `server/db/` موجوداً بعد، +لذا أنشئه إذا لزم الأمر. يصف هذا الملف جداولك باستخدام مساعدات مكتوبة حتى +تحصل إجراءاتك على إكمال تلقائي كامل لـ TypeScript: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -تختار هذه helpers backend SQL المضبوط، لذلك يعمل schema نفسه على SQLite محليًا وفي الإنتاج على Postgres أو Turso/libSQL أو D1 أو أي provider SQL مدعوم. +استخدم مساعدات مخطط الإطار (`table`، و`text`، و`integer`، و`now`) بدلاً من +`sqliteTable`، أو `pgTable`، أو الاستيرادات الخاصة بكل لهجة. تختار الخلفية SQL المُهيَّأة تلقائياً، لذا يعمل نفس المخطط محلياً على SQLite وفي +الإنتاج على أي مزوِّد مدعوم. + +بعد إضافة كلا الملفين، أعد تشغيل خادم التطوير حتى يعمل الترحيل: -- `create-response-insight` يكتب insight جديدًا. -- `list-response-insights` يقرأ بيانات الصفحة. -- `update-response-insight` يحدث السجل. -- `analyze-responses` يستمر في إعادة widget الدردشة الأصلي. +```bash +pnpm dev +``` -لا تنشئ REST route مكررة للمتصفح فقط؛ يجب أن تشترك UI و agent في actions. +ابحث عن هذين السطرين في مخرجات الطرفية. يؤكدان إنشاء الجدول: -## 5. أضف صفحة يستطيع agent فتحها {#add-a-page} +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` -أضف route باسم `/response-insights` واستخدم `useActionQuery("list-response-insights")` و `useActionMutation("create-response-insight")`. هذه الصفحة عرض لحالة SQL؛ وتظل البيانات مكتوبة عبر نفس actions. +سطور `NitroViteError` وتحذير `BETTER_AUTH_SECRET` و +تحذير `SECRETS_ENCRYPTION_KEY` التي تظهر أيضاً طبيعية في بيئة التطوير المحلية +ويمكن تجاهلها. - -

رؤى الردود

رؤى أنشأها الوكيل من ردود النموذج.

موضوع التسعير

المستخدمون يطلبون خطة للفرق.

تراجع في الإعداد

تحتاج ثلاث خطوات إلى نص أوضح.

نموذج العرضتم تحليل 42 ردًا
الخطوة التاليةراجع مسودتي رؤى
" - } - /> - +### إضافة إجراءات للجدول -## 6. دع agent يتنقل {#agent-navigation} +الآن أنشئ ملفات الإجراءات التي تقرأ الجدول وتكتب إليه. تنتقل هذه إلى +دليل `actions/`، في نفس المكان مثل `hello.ts` و`analyze-text.ts`. تنشئها +أنت بنفسك، ملف واحد لكل عملية. سيستدعيها الوكيل وواجهة المستخدم +بنفس الطريقة التي يستدعيان بها أي إجراء آخر. -يتضمن قالب Chat action من نوع `view-screen` و `navigate`. وسّعهما مع نمو تطبيقك: +**`actions/save-text-analysis.ts`** يكتب صف نتيجة إلى قاعدة البيانات. +استدعِه بعد تشغيل `analyze-text` لجعل النتيجة دائمة: -- يجب أن يقرأ `view-screen` حالة التطبيق ويُعيد المسار الحالي، والرؤية المحددة، والفلاتر النشطة، وأي سياق صفحة مضغوط يحتاجه agent. -- يجب أن يكتب `navigate` مسارًا من نفس الأصل مثل `/response-insights` عندما يقرر agent أن على المستخدم معاينة النتيجة بصريًا. -- يمكن لنتائج action تضمين روابط مثل `href: "/response-insights"` كي تعرض الدردشة زر "افتح رؤى الردود" بشكل صريح. +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -عندما تفتح الدردشة الكاملة صفحة تطبيق، استخدم `AgentChatSurface` و `AgentSidebar` و `useAgentChatHomeHandoff` و `useAgentChatHomeHandoffLinks` و `chatViewTransition` من [Agent Surfaces](/docs/agent-surfaces#rich-chat) كي تنتقل الدردشة إلى اللوحة الجانبية مع الحفاظ على نفس thread. +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` -## بنية المشروع {#project-structure} +**`actions/list-text-analyses.ts`** يقرأ جميع النتائج المحفوظة. يمكن للوكيل +استدعاء هذا لتلخيص التحليلات السابقة، ويمكن لواجهة المستخدم استخدامه لملء صفحة: -```text -my-app/ - actions/ # عمليات يمكن للوكيل وواجهة المستخدم استدعاؤها - app/ # مسارات React وصفحاته وواجهات الدردشة - server/ # خادم Nitro ومخطط SQL - AGENTS.md # تعليمات دائمة لوكيل التطبيق - .agents/ # المهارات التي يحمّلها الوكيل عند الحاجة - data/app.db # حالة SQLite المحلية عند عدم ضبط DATABASE_URL +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** يحذف صفاً بالمعرِّف: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); ``` -## هل تريد نقطة بداية كاملة للتحليلات؟ {#analytics-starting-point} +بمجرد حفظ هذه الملفات يلتقطها خادم التطوير تلقائياً. لا +حاجة لإعادة التشغيل. جرِّب سرد التحليلات من الطرفية: -مثال رؤى الردود أعلاه صغير عمدًا كي ترى أجزاء framework بوضوح. إذا كنت تبني منتج analytics حقيقيًا، فابدأ من [Analytics](/docs/template-analytics). إنه نقطة بداية قوية: صل providers الخاصة بك، استخدم dashboards و agent actions الموجودة، ثم خصص التطبيق من هناك. +```bash +pnpm action list-text-analyses +``` + +يجب أن ترى مصفوفة فارغة. الجدول موجود والإجراء يعمل؛ ولا يوجد شيء +محفوظ بعد: + +``` +[] +``` -## الخطوات التالية {#next} +تُحفظ البيانات في `data/app.db`، وهو ملف SQLite في دليل مشروعك يُنشأ +تلقائياً عند أول تشغيل. في الإنتاج ستوجِّه +`DATABASE_URL` إلى قاعدة بيانات مستضافة عوضاً عن ذلك، لكن محلياً هذا الملف كل ما تحتاجه. + +لحفظ شيء ما، شغِّل أولاً `analyze-text` للحصول على الأعداد: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +ستشاهد مخرجات مثل: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +ثم مرِّر تلك القيم إلى `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +الآن شغِّل `list-text-analyses` مجدداً وسترى الصف المحفوظ: + +```bash +pnpm action list-text-analyses +``` + +أو اسأل الوكيل في المحادثة على `http://localhost:8080` للقيام بكلتا الخطوتين معاً: + +> Run analyze-text on "Hello world", then save the result. + +## 6. إضافة صفحة يستطيع الوكيل فتحها {#add-a-page} + +المحادثة رائعة للتفاعل الحواري، لكن بعض البيانات يُفضَّل فحصها +في واجهة مستخدم مخصصة: جدول يمكنك مسحه بالعين وفرزه أو حذف الصفوف منه. تضيف هذه الخطوة +مسار React يعرض كل شيء محفوظ في `text_analyses`، باستخدام +نفس إجراءَي `list-text-analyses` و`delete-text-analysis` اللذين كتبتهما بالفعل. +لا توجد طبقة بيانات ثانية. الصفحة هي مجرد عرض لنفس حالة SQL +التي يقرأها الوكيل ويكتب إليها. + +أنشئ ملف المسار في `app/routes/text-analyses.tsx`. يُلتقط ملفات المسار في +`app/routes/` تلقائياً من قِبَل الإطار. يصبح اسم الملف +مسار URL، لذا ستكون هذه الصفحة متاحة على +`http://localhost:8080/text-analyses`. + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +يستدعي `useActionQuery` الإجراء `list-text-analyses` ويُبقي النتيجة حيَّة. إذا +حفظ الوكيل صفاً جديداً أثناء فتح الصفحة، فإنه يظهر تلقائياً. +يستدعي `useActionMutation` الإجراء `delete-text-analysis` عندما ينقر المستخدم على Delete، +ثم يُبطل الاستعلام حتى تتحدث القائمة. + +افتح `http://localhost:8080/text-analyses` في متصفحك. إذا حفظت +تحليلاً في الخطوة السابقة، فستراه مُدرَجاً. ثم اسأل الوكيل في المحادثة: + +> Open the text analyses page. + +إذا حصلت على خطأ 404، جرِّب إعادة تشغيل خادم التطوير. + +## 7. توسيع التنقل {#extend-navigation} + +يتم تهيئة الشريط الجانبي في `app/config/nav.ts`. افتحه وأضف إدخالاً +لصفحة تحليلات النصوص: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +احفظ الملف. يلتقط خادم التطوير التغيير تلقائياً ويُحدِّث الشريط الجانبي +دون الحاجة إلى إعادة التشغيل. + +### تنقل الوكيل + +يتيح رابط الشريط الجانبي للمستخدمين التنقل يدوياً. يمكن للوكيل أيضاً فتح الصفحات +من تلقاء نفسه باستخدام إجراءَين مدمجَين يُشحنان مع قالب المحادثة: + +- **`view-screen`** يقرأ المسار الحالي ويُعيد ملخصاً موجزاً لـ + ما يراه المستخدم. +- **`navigate`** يكتب مساراً من نفس الأصل إلى سجل المتصفح. + +مع إضافة المزيد من الصفحات، أبقِ `navigate` محدَّثاً حتى يظل الوكيل على علم +بالوجهات الموجودة. وثِّق المسارات المتاحة في `AGENTS.md` حتى يتمكن النموذج من +الاستدلال عليها. + +عندما يحتوي التطبيق على مسار محادثة كامل الصفحة وصفحة تطبيق، استخدم مساعدات تسليم المحادثة المشتركة الموصوفة في [أسطح الوكيل](/docs/agent-surfaces#rich-chat): +`AgentChatSurface`، و`AgentSidebar`، و`useAgentChatHomeHandoff`، +و`useAgentChatHomeHandoffLinks`، و`chatViewTransition`. هذا يسمح للمحادثة الكاملة بالانزلاق إلى اللوحة الجانبية مع فتح الصفحة، مع الحفاظ على نفس الخيط بينما +يفحص المستخدم البيانات الدائمة. + +## هيكل المشروع {#project-structure} + +```text +my-app/ + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset +``` -- **[Actions](/docs/actions)** — schemas و auth و approvals و hooks و transport. -- **[Native Chat UI](/docs/native-chat-ui)** — عرض نتائج actions كجداول ورسوم و typed cards. -- **[Chat Template](/docs/template-chat)** — أصغر chat-first app. -- **[Analytics Template](/docs/template-analytics)** — نقطة بداية قوية لتطبيق analytics؛ صل providers ثم خصص من هناك. -- **[Context Awareness](/docs/context-awareness)** — `view-screen` و `navigate` و route state والعناصر المحددة. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat و inline UI و app pages و embedded sidecars و automation و agents الخارجية. +## هل تريد نقطة انطلاق تحليلية كاملة؟ {#analytics-starting-point} + +مثال تحليلات النصوص أعلاه صغير عن قصد حتى تتمكن من رؤية +قطع الإطار. إذا كنت تبني منتج تحليلات حقيقياً، ابدأ من +[Analytics](/docs/template-analytics) عوضاً عن ذلك. إنه نقطة البداية القوية: +وصِّل مزوِّديك، واستخدم لوحات المعلومات وإجراءات الوكيل الموجودة، ثم +خصِّص التطبيق من هناك. + +## ما الخطوة التالية {#next} + +- **[الإجراءات](/docs/actions)**: المخططات، والمصادقة، والموافقات، والخطافات، والنقل. +- **[واجهة المحادثة الأصيلة](/docs/native-chat-ui)**: عرض نتائج الإجراءات كجداول، + ورسوم بيانية، وبطاقات مكتوبة. +- **[قالب المحادثة](/docs/template-chat)**: تطبيق المحادثة البسيط الذي أنشأته للتو. +- **[قالب Analytics](/docs/template-analytics)**: نقطة انطلاق تطبيق تحليلات قوية؛ وصِّل المزوِّدين وخصِّص من هناك. +- **[الوعي بالسياق](/docs/context-awareness)**: `view-screen`، و`navigate`، + وحالة المسار، والكائنات المحددة. +- **[أسطح الوكيل](/docs/agent-surfaces)**: المحادثة، وواجهة المستخدم المضمَّنة، وصفحات التطبيق، + والشريط الجانبي المضمَّن، والأتمتة، والوكلاء الخارجيون. +- **[النشر](/docs/deployment)**: ضع تطبيقك على نطاقك الخاص. diff --git a/packages/core/docs/content/locales/de-DE/getting-started.mdx b/packages/core/docs/content/locales/de-DE/getting-started.mdx index cd67ccfcdb..f975a516b8 100644 --- a/packages/core/docs/content/locales/de-DE/getting-started.mdx +++ b/packages/core/docs/content/locales/de-DE/getting-started.mdx @@ -1,23 +1,40 @@ --- title: "Erste Schritte" -description: "Erstellen Sie eine chat-first agentic app, fügen Sie eine action hinzu, rendern Sie strukturierte Ergebnisse inline und wachsen Sie zu einer persistenten Seite, die der Agent öffnen kann." +description: "Erstellen Sie eine Chat-first-Agenten-App, fügen Sie eine Aktion hinzu, rendern Sie strukturierte Ergebnisse inline und erweitern Sie die App zu einer dauerhaften Seite, die der Agent öffnen kann." --- # Erste Schritte -Agent-Native ist für agentic applications gedacht: AI agent und UI teilen dieselben [actions](/docs/actions), SQL-Daten und application state. Der Standardpfad beginnt mit Chat, damit Benutzer sofort mit dem Agent sprechen können, und wächst dann zu den Produktoberflächen, die der Workflow braucht. +Agent-Native ist für agentische Anwendungen gedacht: Apps, bei denen der KI-Agent und die UI +dieselben [Aktionen](/docs/actions), SQL-Daten und den Anwendungszustand teilen. Beginnen +Sie mit Chat, damit Benutzer sofort mit dem Agenten kommunizieren können, und fügen Sie dann die +App-Oberflächen hinzu, die Ihr Workflow erfordert. -Der nützliche Einstieg: +Der schnellste Einstieg ist das **Chat-Template**: eine minimale App, die Ihnen eine +funktionierende KI-Chat-Oberfläche, dauerhafte Threads, Authentifizierung und ein `actions/`-Verzeichnis +bietet, das Sie sofort erweitern können. Es ist die Grundlage, aus der die meisten Agent-Native-Apps wachsen. -1. Eine Chat app erstellen. -2. Eine action hinzufügen. -3. Das action-Ergebnis inline im Chat rendern. -4. Daten in SQL persistieren. -5. Eine Seite hinzufügen, die der Agent öffnen kann, wenn visuelle Prüfung besser ist. +Der erste sinnvolle Pfad ist: -## 1. Chat app erstellen {#create-your-app} +1. Eine Chat-App erstellen. +2. Eine KI-Engine verbinden, damit der Agent antworten kann. +3. Eine Aktion hinzufügen. +4. Das Aktionsergebnis inline im Chat rendern. +5. Daten in SQL speichern. +6. Eine Seite hinzufügen, die der Agent öffnen kann, wenn eine visuelle Überprüfung besser ist als ein weiterer + Absatz im Transkript. -Sie brauchen [Node.js 22+](https://nodejs.org) und [pnpm](https://pnpm.io). +Möchten Sie stattdessen eine vollständige Domain-App? Klonen Sie ein umfangreiches Template wie +[Mail](/docs/template-mail), [Calendar](/docs/template-calendar), +[Forms](/docs/template-forms), [Analytics](/docs/template-analytics) oder +[Plan](/docs/template-plan). Möchten Sie noch keine Browser-UI? Lesen Sie nach diesem Tutorial +[Automation-First Apps](/docs/pure-agent-apps). + +## 1. Eine Chat-App erstellen {#create-your-app} + +Sie benötigen [Node.js 22+](https://nodejs.org) und [pnpm](https://pnpm.io). + +Öffnen Sie ein Terminal, wechseln Sie in das Verzeichnis, in dem Ihre Agent-Native-App erstellt werden soll, und führen Sie aus: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +43,213 @@ pnpm install pnpm dev ``` -Das Template enthält durable chat threads, auth, live sync, ein `actions/`-Verzeichnis, die Standard-actions `view-screen` und `navigate` sowie eine React app, die Sie erweitern können. +Dies gibt Ihnen dauerhafte Chat-Threads, Authentifizierung, Live-Synchronisation, ein `actions/`-Verzeichnis, +Standard-Aktionen `view-screen` und `navigate` sowie eine kleine React-App, die Sie erweitern können. + +Der Entwicklungsserver startet und öffnet `http://localhost:8080` in Ihrem Browser. Sie werden +möglicherweise einige Startprotokollzeilen wie `NitroViteError: Vite environment "nitro" is +unavailable` bemerken. Diese sind eine normale Race Condition beim ersten Start und +lösen sich von selbst auf. Die App ist bereit, sobald **VITE ready** in der +Terminalausgabe angezeigt wird. + +Wenn die App startet, befinden Sie sich möglicherweise auf dem Anmeldebildschirm und nicht in der App. Registrieren Sie sich in diesem Fall einfach mit einem fiktiven Login, um den Anmeldebildschirm zu passieren. +Für die lokale Entwicklung ist keine E-Mail-Verifizierung erforderlich. + +### Wenn Sie einen anderen App-Typ möchten + +Dieser Leitfaden verwendet das Chat-Template, da es der kürzeste Weg +zu einer agentischen Anwendung ist: Der Agent kann ab Tag eins handeln, und Sie haben eine echte App- +Oberfläche, von der aus Sie wachsen können. Wenn Sie jedoch eine andere Art von App erstellen möchten, führen Sie `create` ohne Flags für die CLI-Auswahl für Domain-Templates und erweiterte App-Formen aus: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. Eine KI-Engine verbinden {#connect-ai} + +Der Agent-Chat kann erst antworten, wenn Sie eine KI-Engine verbinden. Nachdem Sie sich angemeldet haben, klicken Sie auf die Schaltfläche **Connect AI**. + +Sie haben zwei Optionen: + +**Option A: Builder verbinden.** Klicken Sie auf **Connect Builder.io**, wählen Sie den Builder Space und klicken Sie auf die Schaltfläche **Authorize**. Sie müssen keine Schlüssel manuell angeben. + +**Option B: Eigene Schlüssel hinzufügen.** Geben Sie Ihren Anthropic- oder OpenAI-API-Schlüssel ein. Sie erhalten einen +Anthropic-Schlüssel unter [console.anthropic.com](https://console.anthropic.com). + +Alternativ können Sie eine `.env`-Datei im Verzeichnis `my-app/` (demselben +Verzeichnis, das `package.json` enthält) erstellen, bevor Sie `pnpm dev` ausführen: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +Starten Sie den Entwicklungsserver neu. Sobald eine KI-Engine verbunden ist, blendet sich das +Setup-Panel aus und der Agent ist bereit zum Chatten. + +> **Leerer Bildschirm?** Erstellen Sie eine `.env`-Datei in Ihrem `my-app/`-Verzeichnis mit +> `ANTHROPIC_API_KEY=sk-ant-...` und starten Sie dann `pnpm dev` neu. Das In-App-Setup- +> Panel erscheint nur, wenn die App geladen wurde. Ein fehlender Schlüssel, der verhindert, +> dass die App gerendert wird, muss daher über die Umgebungsvariable und nicht über +> die UI behoben werden. + +## 3. Eine Aktion hinzufügen {#add-an-action} + +Eine Aktion ist eine typisierte Operation, die sowohl Ihr Agent als auch Ihre UI aufrufen können. So +führt der Agent Dinge in Ihrer App aus. Aktionen befinden sich im `actions/`-Verzeichnis +und können aus dem Chat, aus React-Komponenten, über die CLI oder nach einem +Zeitplan ausgelöst werden. Sie definieren sie einmal und rufen sie von überall auf. + +### Die Starter-Aktion ausprobieren + +Das Chat-Template enthält eine `hello`-Aktion unter `actions/hello.ts`: + +```ts filename="actions/hello.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +Führen Sie es vom Terminal aus (innerhalb Ihres `my-app/`-Verzeichnisses) aus: -## 2. Action hinzufügen {#add-an-action} +```bash +pnpm action hello --name Alice +``` + +Oder öffnen Sie Ihre App unter `http://localhost:8080` und fragen Sie den Agenten im Chat: + +> Use the hello action with the name Alice. + +### Eine eigene Aktion hinzufügen -Eine action ist eine typisierte Operation, die Agent und UI gemeinsam aufrufen. Für diesen Ablauf erstellen Sie `actions/analyze-responses.ts`: Sie analysiert Formularantworten und gibt eine validierte Form für ein eigenes Chat-Diagramm zurück. +Ersetzen Sie die Starter-Aktion durch die erste echte Operation in Ihrer Domain. Dieses Beispiel +zählt Wörter, Sätze und Absätze in beliebigem Text. Es berechnet alles +lokal, sodass nichts konfiguriert und kein externer Dienst verbunden werden muss: - -```ts -// actions/analyze-responses.ts +```ts filename="actions/analyze-text.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "Antwortdiagramm" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -Direkt ausführen: +Probieren Sie es im Terminal aus: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -Oder im Browser den Agent fragen: +Oder öffnen Sie Ihre App unter `http://localhost:8080` und fragen Sie den Agenten im Chat: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### Einmal definieren, überall aufrufen + +Diese Aktion ist jetzt über Chat, React-Hooks, CLI, HTTP, MCP, A2A, +geplante Jobs und Webhooks erreichbar. + +TIPP: Wenn Sie möchten, dass der Agent eine bestimmte Aktion eindeutig aufruft, ist die Formulierung "Run the `` action" am zuverlässigsten. Prompts in natürlicher Sprache funktionieren gut, sobald der Agent genügend Kontext über die Domain Ihrer App hat. Für eine brandneue App ohne Daten oder Kontext ist explizit sicherer. -> Analysiere die letzten Antworten für das Demo-Formular. +## 4. Das Ergebnis inline rendern {#render-inline} -## 3. Ergebnis im Chat rendern {#render-inline} +Wenn der Agent `analyze-text` ausführt, gibt er strukturierte Daten zurück: einen Titel und ein +Array von Zählwerten. Standardmäßig beschreibt der Agent diese Daten in Prosa: "Der +Text hat 9 Wörter, 2 Sätze..." und so weiter. Das funktioniert, aber Sie können +das Ergebnis auch als echte UI-Komponente (ein Balkendiagramm, eine Tabelle, eine Karte) +direkt im Chat-Transkript rendern, genau dort, wo der Agent geantwortet hat. -Weil die action `outputSchema` und eine produktspezifische `chatUI.renderer` deklariert, muss der Transcript strukturierte Daten nicht in Fließtext verwandeln. Registrieren Sie beim App-Start eine React-Komponente für genau diese Renderer-ID, zum Beispiel indem `app/chat-renderers.tsx` einmal aus `app/root.tsx` importiert wird: +Das ist, was `chatUI.renderer` in der Aktion bewirkt. Es ist ein Label, das besagt: "Wenn +das Ergebnis dieser Aktion im Chat erscheint, übergib es dieser React-Komponente anstatt +es in Text zusammenzufassen." Die Komponente empfängt die validierte Aktionsausgabe als +Props und rendert was immer Sie möchten. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +Im nächsten Schritt erstellen Sie `app/chat-renderers.tsx`, aber fügen Sie zuerst eine Import-Zeile +zu `app/root.tsx` hinzu, damit sie beim Start ausgeführt wird: -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +Fügen Sie es zusammen mit Ihren anderen Importen am Anfang der Datei hinzu. Das ist die einzige +Änderung an `root.tsx`. Der Import stellt lediglich sicher, dass die Datei ausgeführt wird und den +Renderer registriert. Erstellen Sie jetzt die Renderer-Datei: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +258,393 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Für wiederverwendbare generische Ausgaben liefert das Framework außerdem eingebaute `data-chart`- und `data-table`-Renderer sowie `data-insights` für kombinierte Zusammenfassung-/Diagramm-/Tabellenkarten. Siehe [Native Chat UI](/docs/native-chat-ui). - - -
User

Analysiere Demo-Antworten.

Agent

Gerendert mit responses.response-chart.

Antwortdiagramm

Mo
Di
Mi
Do
" - } - /> - +Sobald der Renderer registriert ist, sieht die Antwort des Agenten so aus. Anstelle eines +Textabsatzes rendert Ihre React-Komponente direkt im Chat-Transkript. + +Verwenden Sie diesen Schritt, wenn das Ergebnis dorthin gehört, wo der Agent spricht: + +- Setup-Zusammenfassungen +- Kurze Berichte +- Genehmigungen +- Tabellen oder Diagramme, die klein genug sind, um sie inline zu überprüfen +- Links zu dauerhaften App-Ansichten + +Für wiederverwendbare generische Ausgaben enthält das Framework auch eingebaute +`data-chart`- und `data-table`-Renderer sowie `data-insights` für kombinierte +Zusammenfassungs-/Diagramm-/Tabellenkarten. Siehe [Native Chat UI](/docs/native-chat-ui). Für +temporäre Steuerelemente, die der Agent zur Laufzeit erstellt, siehe +[Generative UI](/docs/generative-ui). + +## 5. Daten in SQL speichern {#persist-data} + +Derzeit verschwindet das Ergebnis jedes Mal, wenn der Agent `analyze-text` ausführt, nach dem Erscheinen im Chat wieder. Es gibt nichts, worauf man zurückblicken könnte, nichts, worauf der Agent später verweisen kann, und keine Möglichkeit, eine Seite um die Daten aufzubauen. Das Speichern in SQL behebt das: Der Agent schreibt Ergebnisse in eine Tabelle, und sowohl der Agent als auch Ihre UI können sie jederzeit abrufen. + +Agent-Native-Apps haben standardmäßig eine SQL-Datenbank: SQLite lokal +und Ihren konfigurierten Anbieter (Postgres, Turso/libSQL, Cloudflare D1) in der +Produktionsumgebung. + +### Das Datenbank-Plugin einrichten + +Das Chat-Template enthält standardmäßig kein Datenbank-Plugin. Erstellen Sie +`server/plugins/db.ts`, um es zu initialisieren. Dies führt Migrationen aus und +macht die Datenbank für Ihre Aktionen verfügbar: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. Daten in SQL persistieren {#persist-data} +Jeder Eintrag im Array ist eine additive Migration. Wenn Sie später neue Spalten oder +Tabellen hinzufügen, fügen Sie ein neues Versionsobjekt an. Bearbeiten Sie niemals vorhandene. -Wenn Ergebnisse erneut geprüft, verglichen oder aktualisiert werden müssen, speichern Sie sie in SQL und halten Lese-/Schreibzugriffe hinter actions: +### Das Schema definieren -Definieren Sie die Tabelle mit den portablen Framework-Helpern, nicht mit Imports aus `drizzle-orm/sqlite-core` oder `drizzle-orm/pg-core`: +Erstellen Sie `server/db/schema.ts`. Das Verzeichnis `server/db/` existiert möglicherweise noch nicht, +erstellen Sie es daher bei Bedarf. Diese Datei beschreibt Ihre Tabellen mithilfe typisierter Hilfsfunktionen, damit +Ihre Aktionen vollständige TypeScript-Autovervollständigung erhalten: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -Diese Helper wählen das konfigurierte SQL-Backend, sodass dasselbe Schema lokal mit SQLite und in Produktion mit Postgres, Turso/libSQL, D1 oder einem anderen unterstützten SQL-Provider laufen kann. +Verwenden Sie die Framework-Schema-Hilfsfunktionen (`table`, `text`, `integer`, `now`) anstelle von +`sqliteTable`, `pgTable` oder dialektspezifischen Importen. Sie wählen automatisch das konfigurierte +SQL-Backend aus, sodass dasselbe Schema lokal auf SQLite und in der Produktionsumgebung +auf jedem unterstützten Anbieter läuft. -- `create-response-insight` schreibt einen neuen insight. -- `list-response-insights` liest Daten für die Seite. -- `update-response-insight` aktualisiert einen Datensatz. -- `analyze-responses` gibt weiterhin das native Chat-widget zurück. +Nachdem Sie beide Dateien hinzugefügt haben, starten Sie den Entwicklungsserver neu, damit die Migration ausgeführt wird: -Erstellen Sie keine doppelte REST route nur für den Browser; UI und Agent sollten actions teilen. +```bash +pnpm dev +``` -## 5. Seite hinzufügen, die der Agent öffnen kann {#add-a-page} +Achten Sie auf diese zwei Zeilen in der Terminalausgabe. Sie bestätigen, dass die Tabelle erstellt wurde: -Fügen Sie eine Route `/response-insights` mit `useActionQuery("list-response-insights")` und `useActionMutation("create-response-insight")` hinzu. Die Seite ist eine Projektion des SQL state; geschrieben wird weiter über dieselben actions. +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` - -

Antwort-Insights

Insights, die der Agent aus Formularantworten erstellt hat.

Preisthema

Nutzer fragen nach einem Team-Tarif.

Onboarding-Abbruch

Drei Schritte brauchen klarere Texte.

Demo-Formular42 Antworten analysiert
Nächster SchrittZwei Insight-Entwürfe prüfen
" - } - /> -
+Die `NitroViteError`-Zeilen, die `BETTER_AUTH_SECRET`-Warnung und die +`SECRETS_ENCRYPTION_KEY`-Warnung, die ebenfalls erscheinen, sind für die lokale Entwicklung normal +und können ignoriert werden. -## 6. Agent navigieren lassen {#agent-navigation} +### Aktionen für die Tabelle hinzufügen -Die Chat-Vorlage enthält die actions `view-screen` und `navigate`. Erweitern Sie sie, wenn Ihre App wächst: +Erstellen Sie jetzt die Aktionsdateien, die die Tabelle lesen und schreiben. Diese befinden sich in Ihrem +`actions/`-Verzeichnis, am selben Ort wie `hello.ts` und `analyze-text.ts`. Sie +erstellen sie selbst, eine Datei pro Operation. Der Agent und Ihre UI rufen sie +auf dieselbe Weise auf wie jede andere Aktion. -- `view-screen` sollte den application state lesen und die aktuelle Route, das ausgewählte Insight, aktive Filter und jeden knappen Seitenkontext zurückgeben, den der Agent braucht. -- `navigate` sollte einen same-origin Pfad wie `/response-insights` schreiben, wenn der Agent entscheidet, dass der Nutzer das Ergebnis visuell prüfen soll. -- Action-Ergebnisse können Links wie `href: "/response-insights"` enthalten, damit der Chat einen expliziten Button „Antwort-Insights öffnen“ anbieten kann. +**`actions/save-text-analysis.ts`** schreibt eine Ergebniszeile in die Datenbank. +Rufen Sie dies nach dem Ausführen von `analyze-text` auf, um das Ergebnis dauerhaft zu machen: -Wenn der Vollseiten-Chat eine App-Seite öffnet, verwenden Sie `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks` und `chatViewTransition` aus [Agent Surfaces](/docs/agent-surfaces#rich-chat), damit der Chat in die Seitenleiste gleitet und derselbe Thread erhalten bleibt. +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -## Projektstruktur {#project-structure} +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` -```text -my-app/ - actions/ # Operationen, die Agent und UI aufrufen können - app/ # React-Routen, Seiten und Chat-Oberflächen - server/ # Nitro-Server und SQL-Schema - AGENTS.md # Immer aktive Anweisungen für den App-Agenten - .agents/ # Skills, die der Agent bei Bedarf lädt - data/app.db # Lokaler SQLite-Status, wenn DATABASE_URL nicht gesetzt ist +**`actions/list-text-analyses.ts`** liest alle gespeicherten Ergebnisse. Der Agent kann +dies aufrufen, um vergangene Analysen zusammenzufassen, und Ihre UI kann es verwenden, um eine Seite zu befüllen: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** entfernt eine Zeile anhand der ID: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +Sobald diese Dateien gespeichert sind, übernimmt der Entwicklungsserver sie automatisch. Kein +Neustart erforderlich. Versuchen Sie, Analysen vom Terminal aus aufzulisten: + +```bash +pnpm action list-text-analyses +``` + +Sie sollten ein leeres Array sehen. Die Tabelle existiert und die Aktion funktioniert; es ist +noch nichts gespeichert: + +``` +[] +``` + +Daten werden in `data/app.db` gespeichert, einer SQLite-Datei in Ihrem Projektverzeichnis, die +beim ersten Ausführen automatisch erstellt wird. In der Produktionsumgebung würden Sie +`DATABASE_URL` auf eine gehostete Datenbank verweisen, aber lokal genügt diese Datei. + +Um etwas zu speichern, führen Sie zunächst `analyze-text` aus, um die Zählwerte zu erhalten: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +Sie sehen eine Ausgabe wie: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +Übergeben Sie diese Werte dann an `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +Führen Sie nun `list-text-analyses` erneut aus und Sie sehen die gespeicherte Zeile: + +```bash +pnpm action list-text-analyses +``` + +Oder bitten Sie den Agenten im Chat unter `http://localhost:8080`, beide Schritte auf einmal auszuführen: + +> Run analyze-text on "Hello world", then save the result. + +## 6. Eine Seite hinzufügen, die der Agent öffnen kann {#add-a-page} + +Chat eignet sich hervorragend für die konversationelle Interaktion, aber manche Daten lassen sich +besser in einer dedizierten UI überprüfen: einer Tabelle, die man durchsuchen, sortieren oder aus der man Zeilen löschen kann. Dieser Schritt +fügt eine React-Route hinzu, die alles anzeigt, was in `text_analyses` gespeichert ist, und verwendet +die gleichen `list-text-analyses`- und `delete-text-analysis`-Aktionen, die Sie bereits geschrieben haben. +Es gibt keine zweite Datenschicht. Die Seite ist nur eine Ansicht des gleichen SQL-Zustands, +den der Agent liest und schreibt. + +Erstellen Sie die Route-Datei unter `app/routes/text-analyses.tsx`. Route-Dateien in +`app/routes/` werden automatisch vom Framework aufgenommen. Der Dateiname +wird zum URL-Pfad, sodass diese Seite unter +`http://localhost:8080/text-analyses` verfügbar sein wird. + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} ``` -## Sie möchten einen vollständigen Analytics-Startpunkt? {#analytics-starting-point} +`useActionQuery` ruft `list-text-analyses` auf und hält das Ergebnis aktuell. Wenn der +Agent eine neue Zeile speichert, während die Seite geöffnet ist, erscheint sie automatisch. +`useActionMutation` ruft `delete-text-analysis` auf, wenn der Benutzer auf "Delete" klickt, +und invalidiert dann die Query, sodass die Liste aktualisiert wird. + +Öffnen Sie `http://localhost:8080/text-analyses` in Ihrem Browser. Wenn Sie im +vorherigen Schritt eine Analyse gespeichert haben, sehen Sie sie aufgelistet. Fragen Sie dann den Agenten im Chat: + +> Open the text analyses page. -Das Response-insights-Beispiel oben ist absichtlich klein, damit die Framework-Bausteine sichtbar bleiben. Wenn Sie ein echtes Analytics-Produkt bauen, starten Sie stattdessen mit [Analytics](/docs/template-analytics). Das ist der robuste Startpunkt: Provider verbinden, vorhandene Dashboards und agent actions nutzen und von dort aus anpassen. +Wenn Sie einen 404-Fehler erhalten, starten Sie Ihren Entwicklungsserver neu. -## Nächste Schritte {#next} +## 7. Die Navigation erweitern {#extend-navigation} + +Die Seitenleiste wird in `app/config/nav.ts` konfiguriert. Öffnen Sie die Datei und fügen Sie +einen Eintrag für die Seite Textanalysen hinzu: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +Speichern Sie die Datei. Der Entwicklungsserver übernimmt die Änderung automatisch und die +Seitenleiste aktualisiert sich ohne Neustart. + +### Agenten-Navigation + +Der Seitenleisten-Link ermöglicht es Benutzern, manuell zu navigieren. Der Agent kann Seiten auch +eigenständig öffnen, mithilfe von zwei integrierten Aktionen, die mit dem Chat-Template geliefert +werden: + +- **`view-screen`** liest die aktuelle Route und gibt eine kompakte Zusammenfassung zurück, + was der Benutzer gerade sieht. +- **`navigate`** schreibt einen gleichrangigen Pfad in den Browserverlauf. + +Wenn Sie weitere Seiten hinzufügen, halten Sie `navigate` aktuell, damit der Agent weiß, welche +Ziele verfügbar sind. Dokumentieren Sie verfügbare Pfade in `AGENTS.md`, damit das Modell über +sie nachdenken kann. + +Wenn die App sowohl eine vollständige Chat-Route als auch eine App-Seite hat, verwenden Sie die +gemeinsamen Chat-Übergabe-Hilfsfunktionen, die in +[Agent Surfaces](/docs/agent-surfaces#rich-chat) beschrieben sind: +`AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, +`useAgentChatHomeHandoffLinks` und `chatViewTransition`. Dadurch gleitet der vollständige +Chat in das Seitenpanel, wenn die Seite geöffnet wird, und behält denselben Thread bei, +während der Benutzer dauerhafte Daten überprüft. + +## Projektstruktur {#project-structure} + +```text +my-app/ + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset +``` -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks und transport. -- **[Native Chat UI](/docs/native-chat-ui)** — Tabellen, Diagramme und typed cards für action-Ergebnisse. -- **[Chat Template](/docs/template-chat)** — die minimale chat-first app. -- **[Analytics Template](/docs/template-analytics)** — ein robuster Analytics-Startpunkt; Provider verbinden und dann anpassen. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state und ausgewählte Objekte. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation und externe Agents. +## Möchten Sie einen vollständigen Analytics-Ausgangspunkt? {#analytics-starting-point} + +Das obige Textanalysen-Beispiel ist bewusst klein gehalten, damit Sie die +Framework-Bestandteile sehen können. Wenn Sie ein echtes Analytics-Produkt entwickeln, starten Sie von +[Analytics](/docs/template-analytics). Es ist der solide Ausgangspunkt: +Verbinden Sie Ihre Anbieter, verwenden Sie die vorhandenen Dashboards und Agent-Aktionen und +passen Sie die App dann von dort aus an. + +## Was kommt als nächstes {#next} + +- **[Actions](/docs/actions)**: Schemas, Authentifizierung, Genehmigungen, Hooks und Transport. +- **[Native Chat UI](/docs/native-chat-ui)**: Aktionsergebnisse als Tabellen, + Diagramme und typisierte Karten rendern. +- **[Chat Template](/docs/template-chat)**: Die minimale Chat-first-App, die Sie gerade + erstellt haben. +- **[Analytics Template](/docs/template-analytics)**: Ein solider Analytics-App- + Ausgangspunkt; Anbieter verbinden und von dort aus anpassen. +- **[Context Awareness](/docs/context-awareness)**: `view-screen`, `navigate`, + Routenzustand und ausgewählte Objekte. +- **[Agent Surfaces](/docs/agent-surfaces)**: Chat, Inline-UI, App-Seiten, + eingebettete Sidecars, Automatisierung und externe Agenten. +- **[Deployment](/docs/deployment)**: Stellen Sie Ihre App auf Ihrer eigenen Domain bereit. diff --git a/packages/core/docs/content/locales/es-ES/getting-started.mdx b/packages/core/docs/content/locales/es-ES/getting-started.mdx index fc8cd71b5c..d8cb8d8f5b 100644 --- a/packages/core/docs/content/locales/es-ES/getting-started.mdx +++ b/packages/core/docs/content/locales/es-ES/getting-started.mdx @@ -1,23 +1,40 @@ --- title: "Primeros pasos" -description: "Crea una app agentic empezando por chat, añade una action, renderiza resultados estructurados en línea y crece hacia una página persistente que el agent puede abrir." +description: "Crea una aplicación agéntica centrada en el chat, añade una acción, muestra resultados estructurados en línea y, a continuación, amplíala a una página persistente que el agente puede abrir." --- # Primeros pasos -Agent-Native sirve para crear agentic applications: el AI agent y la UI comparten las mismas [actions](/docs/actions), datos SQL y application state. El camino principal empieza con Chat para que los usuarios puedan hablar con el agent de inmediato, y después añade las superficies de producto que el flujo necesita. +Agent-Native es para aplicaciones agénticas: aplicaciones en las que el agente de IA y la interfaz de usuario +comparten las mismas [acciones](/docs/actions), datos SQL y el estado de la aplicación. Empieza +con el chat para que los usuarios puedan hablar con el agente de inmediato y, después, añade las superficies de la aplicación +que tu flujo de trabajo necesite. -El recorrido útil es: +La forma más rápida de empezar es la **plantilla Chat**: una aplicación mínima que te ofrece una +interfaz de chat con IA funcional, hilos duraderos, autenticación y un directorio `actions/` +listo para ampliar. Es la base desde la que crece la mayoría de las aplicaciones Agent-Native. -1. Crear una Chat app. -2. Añadir una action. -3. Renderizar el resultado de la action dentro del chat. -4. Persistir datos en SQL. -5. Añadir una página que el agent pueda abrir cuando conviene inspeccionar el resultado visualmente. +El primer camino útil es: -## 1. Crea una Chat app {#create-your-app} +1. Crear una aplicación de chat. +2. Conectar un motor de IA para que el agente pueda responder. +3. Añadir una acción. +4. Mostrar el resultado de la acción en línea dentro del chat. +5. Persistir datos en SQL. +6. Añadir una página que el agente pueda abrir cuando la inspección visual sea mejor que otro + párrafo en la transcripción. -Necesitas [Node.js 22+](https://nodejs.org) y [pnpm](https://pnpm.io). +¿Prefieres una aplicación de dominio completa? Clona una plantilla enriquecida como +[Mail](/docs/template-mail), [Calendar](/docs/template-calendar), +[Forms](/docs/template-forms), [Analytics](/docs/template-analytics) o +[Plan](/docs/template-plan). ¿Aún no quieres interfaz de navegador? Consulta +[Aplicaciones basadas en automatización](/docs/pure-agent-apps) tras este tutorial. + +## 1. Crear una aplicación de chat {#create-your-app} + +Necesitarás [Node.js 22+](https://nodejs.org) y [pnpm](https://pnpm.io). + +Abre una terminal, ve al directorio donde quieres tu aplicación Agent Native y ejecuta: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +43,213 @@ pnpm install pnpm dev ``` -El template incluye durable chat threads, auth, live sync, un directorio `actions/`, las actions estándar `view-screen` y `navigate`, y una app React lista para extender. +Esto te proporciona hilos de chat duraderos, autenticación, sincronización en tiempo real, un directorio `actions/`, +las acciones estándar `view-screen` y `navigate`, y una pequeña aplicación React que puedes +ampliar. + +El servidor de desarrollo se inicia y abre `http://localhost:8080` en tu navegador. Es posible que +veas algunas líneas de registro al arrancar, como `NitroViteError: Vite environment "nitro" is +unavailable`. Se trata de una condición de carrera normal durante el inicio inicial y +se resuelven solas. La aplicación está lista cuando **VITE ready** aparece en la +salida del terminal. + +Cuando la aplicación se inicie, es posible que te encuentres en la pantalla de inicio de sesión en lugar de dentro de la aplicación. En ese caso, simplemente regístrate con un nombre de usuario inventado para satisfacer la pantalla de inicio de sesión. +En el entorno de desarrollo local no se requiere verificación de correo electrónico. + +### Si deseas un tipo de aplicación diferente + +Esta guía utiliza la plantilla Chat porque es el camino más corto +hacia una aplicación agéntica: el agente puede actuar desde el primer día y dispones de una superficie de aplicación real +desde la que crecer. Sin embargo, si deseas crear un tipo de aplicación diferente, ejecuta `create` sin indicadores para acceder al selector de CLI con plantillas de dominio y formas de aplicación avanzadas: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. Conectar un motor de IA {#connect-ai} + +El chat del agente no puede responder hasta que conectes un motor de IA. Una vez que hayas iniciado sesión, haz clic en el botón **Connect AI**. + +Tienes dos opciones: + +**Opción A: Conectar Builder.** Haz clic en **Connect Builder.io**, elige el espacio de Builder y haz clic en el botón **Authorize**. No es necesario proporcionar ninguna clave manualmente. + +**Opción B: Añadir tus propias claves.** Introduce tu clave de API de Anthropic u OpenAI. Puedes obtener una +clave de Anthropic en [console.anthropic.com](https://console.anthropic.com). + +Alternativamente, crea un archivo `.env` en el directorio `my-app/` (el mismo +directorio que contiene `package.json`) antes de ejecutar `pnpm dev`: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +Reinicia el servidor de desarrollo. Una vez que el motor de IA esté conectado, el panel de configuración +se ocultará y el agente estará listo para chatear. + +> **¿Pantalla en blanco?** Crea un archivo `.env` en tu directorio `my-app/` con +> `ANTHROPIC_API_KEY=sk-ant-...` y, a continuación, reinicia `pnpm dev`. El panel de configuración dentro de la aplicación +> solo aparece una vez que la aplicación ha cargado, por lo que una clave que falta y que impide +> que la aplicación se renderice debe corregirse mediante la variable de entorno y no a través de +> la interfaz de usuario. + +## 3. Añadir una acción {#add-an-action} + +Una acción es una operación tipada que tanto tu agente como tu interfaz de usuario pueden invocar. Es +la forma en que el agente realiza tareas en tu aplicación. Las acciones se encuentran en el directorio `actions/` +y pueden activarse desde el chat, desde componentes React, desde la CLI o de forma programada. Las defines una vez y las llamas desde cualquier lugar. + +### Probar la acción de ejemplo + +La plantilla Chat incluye una acción `hello` en `actions/hello.ts`: + +```ts filename="actions/hello.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +Ejecútala desde el terminal (dentro de tu directorio `my-app/`): -## 2. Añade una action {#add-an-action} +```bash +pnpm action hello --name Alice +``` + +O abre tu aplicación en `http://localhost:8080` y pídele al agente en el chat: + +> Use the hello action with the name Alice. + +### Añadir tu propia acción -Una action es una operación tipada que comparten el agent y la UI. Para este flujo, crea `actions/analyze-responses.ts`: analiza respuestas de formularios y devuelve una forma validada para un gráfico de chat propio. +Reemplaza la acción de ejemplo con la primera operación real de tu dominio. Este ejemplo +cuenta palabras, frases y párrafos en cualquier texto que le pases. Todo se calcula +de forma local, por lo que no hay nada que configurar ni ningún servicio externo al que conectarse: - -```ts -// actions/analyze-responses.ts +```ts filename="actions/analyze-text.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "Gráfico de respuestas" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -Pruébala directamente: +Pruébala desde el terminal: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -O pídeselo al agent en el navegador: +O abre tu aplicación en `http://localhost:8080` y pídele al agente en el chat: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### Definir una vez, llamar desde cualquier lugar + +Esta acción es ahora accesible desde el chat, hooks de React, CLI, HTTP, MCP, A2A, +trabajos programados y webhooks. + +CONSEJO: Siempre que quieras que el agente llame a una acción específica sin ambigüedad, formularlo como "Run the `` action" es lo más fiable. Las instrucciones en lenguaje natural funcionan bien una vez que el agente tiene suficiente contexto sobre el dominio de tu aplicación. Para una aplicación nueva sin datos ni contexto todavía, ser explícito es más seguro. -> Analiza las respuestas recientes del formulario de demo. +## 4. Mostrar el resultado en línea {#render-inline} -## 3. Renderiza el resultado en chat {#render-inline} +Cuando el agente ejecuta `analyze-text`, devuelve datos estructurados: un título y un +array de recuentos. De forma predeterminada, el agente describirá esos datos en prosa: "El +texto tiene 9 palabras, 2 frases..." y así sucesivamente. Eso funciona, pero también puedes +mostrar el resultado como un componente de interfaz de usuario real (un gráfico de barras, una tabla, una tarjeta) +directamente dentro de la transcripción del chat, justo donde respondió el agente. -Como la action declara `outputSchema` y una `chatUI.renderer` específica del producto, el transcript no convierte los datos en texto plano. Registra un componente React para ese id exacto desde el arranque de la app, por ejemplo importando `app/chat-renderers.tsx` una vez desde `app/root.tsx`: +Esto es lo que hace `chatUI.renderer` en la acción. Es una etiqueta que dice "cuando +el resultado de esta acción aparezca en el chat, entrégaselo a este componente React en lugar de +resumirlo en texto". El componente recibe el resultado validado de la acción como +props y renderiza lo que quieras. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +En el siguiente paso, crearás `app/chat-renderers.tsx`, pero primero añade una línea de importación +a `app/root.tsx` para que se ejecute al inicio: -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +Añádela junto a tus otras importaciones en la parte superior del archivo. Ese es el único +cambio en `root.tsx`. La importación simplemente garantiza que el archivo se ejecute y registre el +renderer. Ahora crea el archivo del renderer: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +258,397 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Para salidas genéricas reutilizables, el framework también incluye renderers integrados `data-chart` y `data-table`, además de `data-insights` para tarjetas combinadas de resumen/gráfico/tabla. Consulta [Native Chat UI](/docs/native-chat-ui). - - -
User

Analiza respuestas de demo.

Agent

Renderizado con responses.response-chart.

Gráfico de respuestas

Lun
Mar
Mié
Jue
" - } - /> - +Una vez registrado el renderer, la respuesta del agente tiene este aspecto. En lugar +de un párrafo de texto, tu componente React se renderiza directamente dentro de la transcripción del chat. + +Utiliza este paso cuando el resultado pertenezca al lugar donde el agente está hablando: + +- resúmenes de configuración +- informes breves +- aprobaciones +- tablas o gráficos lo suficientemente pequeños para inspeccionar en línea +- enlaces a vistas de aplicación duraderas + +Para salidas genéricas reutilizables, el framework también incluye renderers integrados de +`data-chart` y `data-table`, además de `data-insights` para tarjetas combinadas de +resumen/gráfico/tabla. Consulta [IU de chat nativa](/docs/native-chat-ui). Para +controles temporales que el agente crea en tiempo de ejecución, consulta +[IU generativa](/docs/generative-ui). + +## 5. Persistir datos en SQL {#persist-data} + +En este momento, cada vez que el agente ejecuta `analyze-text`, el resultado aparece en el chat +y luego desaparece. No hay nada a lo que volver, nada a lo que el agente pueda +hacer referencia más tarde y ninguna forma de construir una página en torno a los datos. Persistir en SQL +soluciona eso: el agente escribe los resultados en una tabla, y tanto el agente como tu interfaz de usuario +pueden leerlos en cualquier momento. + +Las aplicaciones Agent-Native disponen de una base de datos SQL por defecto: SQLite de forma local +y tu proveedor configurado (Postgres, Turso/libSQL, Cloudflare D1) en +producción. + +### Conectar el complemento de base de datos + +La plantilla Chat no incluye un complemento de base de datos por defecto. Crea +`server/plugins/db.ts` para inicializarlo. Esto es lo que ejecuta las migraciones y +pone la base de datos a disposición de tus acciones: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. Persiste datos en SQL {#persist-data} +Cada entrada del array es una migración aditiva. Cuando añadas nuevas columnas o +tablas más adelante, agrega un nuevo objeto de versión. Nunca edites los existentes. -Cuando el resultado debe revisarse, compararse o actualizarse, guárdalo en SQL y mantén las lecturas/escrituras detrás de actions: +### Definir el esquema -Define la tabla con los helpers portables del framework, no con imports de `drizzle-orm/sqlite-core` o `drizzle-orm/pg-core`: +Crea `server/db/schema.ts`. El directorio `server/db/` puede que aún no exista, +así que créalo si es necesario. Este archivo describe tus tablas usando helpers tipados para que +tus acciones tengan autocompletado completo de TypeScript: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -Estos helpers eligen el backend SQL configurado, así que el mismo schema puede correr en SQLite local y en producción con Postgres, Turso/libSQL, D1 u otro provider SQL soportado. +Utiliza los helpers de esquema del framework (`table`, `text`, `integer`, `now`) en lugar de +`sqliteTable`, `pgTable` o importaciones específicas del dialecto. Seleccionan el backend +SQL configurado automáticamente, por lo que el mismo esquema se ejecuta localmente en SQLite y en +producción con cualquier proveedor compatible. + +Tras añadir ambos archivos, reinicia el servidor de desarrollo para que se ejecute la migración: + +```bash +pnpm dev +``` + +Busca estas dos líneas en la salida del terminal. Confirman que la tabla se creó: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` + +Las líneas `NitroViteError`, la advertencia `BETTER_AUTH_SECRET` y la +advertencia `SECRETS_ENCRYPTION_KEY` que también aparecen son normales en el entorno de desarrollo local y +pueden ignorarse. + +### Añadir acciones para la tabla + +Ahora crea los archivos de acción que leen y escriben en la tabla. Estos van en tu +directorio `actions/`, en el mismo lugar que `hello.ts` y `analyze-text.ts`. Los +creas tú mismo, un archivo por operación. El agente y tu interfaz de usuario los llamarán +de la misma forma que llaman a cualquier otra acción. + +**`actions/save-text-analysis.ts`** escribe una fila de resultado en la base de datos. +Llama a esto después de ejecutar `analyze-text` para que el resultado sea duradero: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`** lee todos los resultados guardados. El agente puede +llamar a esto para resumir análisis anteriores, y tu interfaz de usuario puede usarlo para rellenar una página: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** elimina una fila por id: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +Una vez guardados estos archivos, el servidor de desarrollo los recoge automáticamente. No +es necesario reiniciar. Prueba a listar los análisis desde el terminal: + +```bash +pnpm action list-text-analyses +``` + +Deberías ver un array vacío. La tabla existe y la acción funciona; simplemente +no hay nada guardado todavía: + +``` +[] +``` + +Los datos se guardan en `data/app.db`, un archivo SQLite en el directorio de tu proyecto que +se crea automáticamente en la primera ejecución. En producción apuntarías +`DATABASE_URL` a una base de datos alojada, pero de forma local este archivo es todo lo que +necesitas. + +Para guardar algo, primero ejecuta `analyze-text` para obtener los recuentos: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +Verás una salida similar a esta: -- `create-response-insight` escribe un insight nuevo. -- `list-response-insights` lee los datos para la página. -- `update-response-insight` actualiza un registro. -- `analyze-responses` sigue devolviendo el widget nativo del chat. +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +Luego pasa esos valores a `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +Ahora ejecuta `list-text-analyses` de nuevo y verás la fila guardada: + +```bash +pnpm action list-text-analyses +``` + +O pídele al agente en el chat en `http://localhost:8080` que realice ambos pasos a la vez: -No crees una REST route duplicada solo para el navegador; la UI y el agent deben compartir actions. +> Run analyze-text on "Hello world", then save the result. -## 5. Añade una página que el agent pueda abrir {#add-a-page} +## 6. Añadir una página que el agente puede abrir {#add-a-page} -Añade una route `/response-insights` con `useActionQuery("list-response-insights")` y `useActionMutation("create-response-insight")`. La página es una proyección del SQL state; los datos siguen escribiéndose por las mismas actions. +El chat es ideal para la interacción conversacional, pero algunos datos se inspeccionan mejor +en una interfaz dedicada: una tabla que puedas explorar, ordenar o de la que puedas eliminar filas. Este paso +añade una ruta React que muestra todo lo guardado en `text_analyses`, usando las +mismas acciones `list-text-analyses` y `delete-text-analysis` que ya escribiste. +No hay una segunda capa de datos. La página es simplemente una vista del mismo estado SQL +que el agente lee y escribe. - -

Insights de respuestas

Insights que el agente creó a partir de respuestas del formulario.

Tema de precios

Los usuarios piden un plan de equipo.

Abandono en onboarding

Tres pasos necesitan textos más claros.

Formulario de demo42 respuestas analizadas
Siguiente pasoRevisar dos borradores de insights
" - } - /> -
+Crea el archivo de ruta en `app/routes/text-analyses.tsx`. Los archivos de ruta en +`app/routes/` son recogidos automáticamente por el framework. El nombre del archivo +se convierte en la ruta de la URL, por lo que esta página estará disponible en +`http://localhost:8080/text-analyses`. -## 6. Deja que el agent navegue {#agent-navigation} +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; -La Chat template incluye las actions `view-screen` y `navigate`. Amplíalas a medida que tu app crece: +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` -- `view-screen` debería leer el application state y devolver la route actual, el insight seleccionado, los filtros activos y cualquier contexto de página compacto que el agent necesite. -- `navigate` debería escribir una route same-origin como `/response-insights` cuando el agent decida que el usuario debe inspeccionar el resultado visualmente. -- Los resultados de una action pueden incluir enlaces como `href: "/response-insights"` para que el chat ofrezca un botón explícito "Abrir insights de respuestas". +`useActionQuery` llama a `list-text-analyses` y mantiene el resultado actualizado. Si el +agente guarda una nueva fila mientras la página está abierta, aparece automáticamente. +`useActionMutation` llama a `delete-text-analysis` cuando el usuario hace clic en Delete +y luego invalida la consulta para que la lista se actualice. -Cuando el chat de página completa abra una página de app, usa `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks` y `chatViewTransition` desde [Agent Surfaces](/docs/agent-surfaces#rich-chat) para deslizar el chat al panel lateral sin perder el thread. +Abre `http://localhost:8080/text-analyses` en tu navegador. Si guardaste un +análisis en el paso anterior, lo verás listado. Luego pídele al agente en el chat: + +> Open the text analyses page. + +Si obtienes un error 404, intenta reiniciar el servidor de desarrollo. + +## 7. Ampliar la navegación {#extend-navigation} + +La barra lateral se configura en `app/config/nav.ts`. Ábrelo y añade una entrada +para la página de análisis de texto: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +Guarda el archivo. El servidor de desarrollo detecta el cambio automáticamente y +la barra lateral se actualiza sin necesidad de reiniciarlo. + +### Navegación del agente + +El enlace de la barra lateral permite a los usuarios navegar manualmente. El agente también +puede abrir páginas por su cuenta usando dos acciones integradas que se incluyen con +la plantilla Chat: + +- **`view-screen`** lee la ruta actual y devuelve un resumen compacto de + lo que el usuario está viendo. +- **`navigate`** escribe una ruta del mismo origen en el historial del navegador. + +A medida que añadas más páginas, mantén `navigate` actualizado para que el agente +sepa qué destinos existen. Documenta las rutas disponibles en `AGENTS.md` para +que el modelo pueda razonar sobre ellas. + +Cuando la aplicación tiene tanto una ruta de chat a pantalla completa como una página de aplicación, +utiliza los helpers de transferencia de chat compartidos descritos en +[Superficies del agente](/docs/agent-surfaces#rich-chat): +`AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, +`useAgentChatHomeHandoffLinks` y `chatViewTransition`. Esto permite que el chat completo +se deslice al panel lateral a medida que se abre la página, manteniendo el mismo hilo mientras +el usuario inspecciona datos duraderos. ## Estructura del proyecto {#project-structure} ```text my-app/ - actions/ # Operaciones invocables por el agente y la interfaz - app/ # Rutas, páginas y superficies de chat de React + actions/ # Operaciones invocables por el agente y la interfaz de usuario + app/ # Rutas React, páginas y superficies de chat server/ # Servidor Nitro y esquema SQL - AGENTS.md # Instrucciones siempre activas para el agente de la app - .agents/ # Skills que el agente carga cuando corresponde + AGENTS.md # Instrucciones siempre activas para el agente de la aplicación + .agents/ # Habilidades que el agente carga cuando son relevantes data/app.db # Estado SQLite local cuando DATABASE_URL no está definido ``` -## ¿Quieres un punto de partida completo para analytics? {#analytics-starting-point} - -El ejemplo de Insights de respuestas anterior es pequeño a propósito para mostrar las piezas del framework. Si vas a crear un producto real de analytics, empieza con [Analytics](/docs/template-analytics). Es el punto de partida robusto: conecta tus providers, usa los dashboards y agent actions existentes, y personaliza desde ahí. - -## Siguientes pasos {#next} - -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks y transport. -- **[Native Chat UI](/docs/native-chat-ui)** — tablas, gráficos y typed cards para resultados de actions. -- **[Chat Template](/docs/template-chat)** — la app mínima chat-first. -- **[Analytics Template](/docs/template-analytics)** — una base robusta para analytics; conecta providers y personaliza desde ahí. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state y objetos seleccionados. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation y agents externos. +## ¿Quieres un punto de partida completo para analítica? {#analytics-starting-point} + +El ejemplo de text-analyses anterior es intencionadamente pequeño para que puedas ver las +piezas del framework. Si estás construyendo un producto de analítica real, comienza desde +[Analytics](/docs/template-analytics) en su lugar. Es el punto de partida sólido: +conecta tus proveedores, usa los paneles y las acciones del agente existentes y, a continuación, +personaliza la aplicación desde ahí. + +## Qué viene después {#next} + +- **[Acciones](/docs/actions)**: esquemas, autenticación, aprobaciones, hooks y transporte. +- **[IU de chat nativa](/docs/native-chat-ui)**: muestra los resultados de las acciones como tablas, + gráficos y tarjetas tipadas. +- **[Plantilla Chat](/docs/template-chat)**: la aplicación mínima centrada en el chat que acabas de + crear. +- **[Plantilla Analytics](/docs/template-analytics)**: un sólido punto de partida para aplicaciones de analítica; conecta proveedores y personaliza desde ahí. +- **[Conciencia del contexto](/docs/context-awareness)**: `view-screen`, `navigate`, + estado de la ruta y objetos seleccionados. +- **[Superficies del agente](/docs/agent-surfaces)**: chat, IU en línea, páginas de aplicación, + sidecars integrados, automatización y agentes externos. +- **[Despliegue](/docs/deployment)**: publica tu aplicación en tu propio dominio. diff --git a/packages/core/docs/content/locales/fr-FR/getting-started.mdx b/packages/core/docs/content/locales/fr-FR/getting-started.mdx index 1ff083edfc..dadc6c76a1 100644 --- a/packages/core/docs/content/locales/fr-FR/getting-started.mdx +++ b/packages/core/docs/content/locales/fr-FR/getting-started.mdx @@ -1,23 +1,41 @@ --- title: "Démarrage" -description: "Créez une app agentic centrée sur le chat, ajoutez une action, rendez des résultats structurés en ligne, puis ajoutez une page persistante que l'agent peut ouvrir." +description: "Créez une application agentique orientée chat, ajoutez une action, affichez les résultats structurés en ligne, puis développez-la en une page persistante que l'agent peut ouvrir." --- # Démarrage -Agent-Native sert à créer des agentic applications : l'AI agent et l'UI partagent les mêmes [actions](/docs/actions), les mêmes données SQL et le même application state. Le chemin principal commence par Chat, afin que les utilisateurs puissent parler à l'agent immédiatement, puis ajoute les surfaces produit nécessaires. +Agent-Native est conçu pour les applications agentiques : des applications où l'agent IA et +l'interface utilisateur partagent les mêmes [actions](/docs/actions), données SQL et état applicatif. +Commencez par le chat pour que les utilisateurs puissent parler à l'agent immédiatement, puis ajoutez +les surfaces applicatives que votre flux de travail mérite. -Le parcours utile : +La façon la plus rapide de démarrer est le **modèle Chat** : une application minimale qui vous fournit +une interface de chat IA fonctionnelle, des fils de discussion durables, une authentification et un +répertoire `actions/` prêt à être étendu. C'est la base à partir de laquelle la plupart des applications +Agent-Native se développent. -1. Créer une Chat app. -2. Ajouter une action. -3. Rendre le résultat de l'action directement dans le chat. -4. Persister les données dans SQL. -5. Ajouter une page que l'agent peut ouvrir lorsqu'une inspection visuelle est préférable. +Le premier chemin utile est le suivant : -## 1. Créer une Chat app {#create-your-app} +1. Créer une application de chat. +2. Connecter un moteur IA pour que l'agent puisse répondre. +3. Ajouter une action. +4. Afficher le résultat de l'action en ligne dans le chat. +5. Persister les données en SQL. +6. Ajouter une page que l'agent peut ouvrir lorsque l'inspection visuelle est préférable à un + autre paragraphe dans la transcription. -Vous avez besoin de [Node.js 22+](https://nodejs.org) et de [pnpm](https://pnpm.io). +Vous préférez une application de domaine complète ? Clonez un modèle riche tel que +[Mail](/docs/template-mail), [Calendar](/docs/template-calendar), +[Forms](/docs/template-forms), [Analytics](/docs/template-analytics) ou +[Plan](/docs/template-plan). Vous ne voulez pas encore d'interface navigateur ? Consultez +[Applications Automation-First](/docs/pure-agent-apps) après ce tutoriel. + +## 1. Créer une application de chat {#create-your-app} + +Vous aurez besoin de [Node.js 22+](https://nodejs.org) et de [pnpm](https://pnpm.io). + +Ouvrez un terminal, accédez au répertoire où vous souhaitez créer votre application Agent Native, et exécutez : ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +44,221 @@ pnpm install pnpm dev ``` -Le template inclut durable chat threads, auth, live sync, un dossier `actions/`, les actions standard `view-screen` et `navigate`, et une app React prête à évoluer. +Cela vous fournit des fils de discussion durables, une authentification, une synchronisation en direct, +un répertoire `actions/`, les actions standard `view-screen` et `navigate`, ainsi qu'une petite +application React que vous pouvez étendre. + +Le serveur de développement démarre et ouvre `http://localhost:8080` dans votre navigateur. Vous pourrez +remarquer quelques lignes de journal au démarrage comme `NitroViteError: Vite environment "nitro" is +unavailable`. Il s'agit d'une condition de concurrence normale lors du démarrage initial qui se résout +d'elle-même. L'application est prête une fois que **VITE ready** s'affiche dans la sortie du terminal. + +Lorsque l'application démarre, vous pourriez vous retrouver sur l'écran de connexion plutôt que dans +l'application. Dans ce cas, inscrivez-vous simplement avec un identifiant fictif pour satisfaire +l'écran de connexion. Pour le développement local, aucune vérification d'e-mail n'est réellement requise. + +### Si vous souhaitez un autre type d'application + +Ce guide utilise le modèle Chat car c'est le chemin le plus court vers une application agentique : +l'agent peut agir dès le premier jour, et vous disposez d'une vraie surface applicative à partir de +laquelle vous développer. Cependant, si vous souhaitez créer un autre type d'application, exécutez +`create` sans options pour accéder au sélecteur CLI proposant des modèles de domaine et des formes +d'application avancées : + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. Connecter un moteur IA {#connect-ai} + +Le chat de l'agent ne peut pas répondre tant que vous n'avez pas connecté un moteur IA. Une fois connecté, +cliquez sur le bouton **Connect AI**. + +Vous avez deux options : + +**Option A : Connecter Builder.** Cliquez sur **Connect Builder.io**, choisissez le Builder Space, puis +cliquez sur le bouton **Authorize**. Vous n'avez pas besoin de fournir de clés manuellement. + +**Option B : Ajouter vos propres clés.** Entrez votre clé API Anthropic ou OpenAI. Vous pouvez obtenir +une clé Anthropic sur [console.anthropic.com](https://console.anthropic.com). + +Vous pouvez également créer un fichier `.env` dans le répertoire `my-app/` (le même répertoire +qui contient `package.json`) avant d'exécuter `pnpm dev` : + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +Redémarrez le serveur de développement. Une fois qu'un moteur IA est connecté, le panneau de +configuration se masque et l'agent est prêt à converser. + +> **Écran blanc ?** Créez un fichier `.env` dans votre répertoire `my-app/` avec +> `ANTHROPIC_API_KEY=sk-ant-...`, puis redémarrez `pnpm dev`. Le panneau de configuration +> intégré à l'application n'apparaît qu'une fois l'application chargée, donc une clé manquante +> qui empêche le rendu de l'application doit être corrigée via la variable d'environnement plutôt +> que via l'interface utilisateur. + +## 3. Ajouter une action {#add-an-action} + +Une action est une opération typée que votre agent et votre interface utilisateur peuvent appeler. +C'est ainsi que l'agent effectue des opérations dans votre application. Les actions se trouvent dans +le répertoire `actions/` et peuvent être déclenchées depuis le chat, depuis des composants React, +depuis la CLI ou selon un calendrier. Vous les définissez une seule fois et les appelez de n'importe où. + +### Essayer l'action de démarrage + +Le modèle Chat inclut une action `hello` dans `actions/hello.ts` : + +```ts filename="actions/hello.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +Exécutez-la depuis le terminal (dans votre répertoire `my-app/`) : -## 2. Ajouter une action {#add-an-action} +```bash +pnpm action hello --name Alice +``` + +Ou ouvrez votre application sur `http://localhost:8080` et demandez à l'agent dans le chat : + +> Use the hello action with the name Alice. + +### Ajouter votre propre action -Une action est une opération typée appelée par l'agent et par l'UI. Pour ce guide, créez `actions/analyze-responses.ts` : elle analyse des réponses de formulaire et renvoie une forme validée pour un graphique de chat personnalisé. +Remplacez l'action de démarrage par la première opération réelle de votre domaine. Cet exemple +compte les mots, les phrases et les paragraphes dans n'importe quel texte que vous lui passez. +Il calcule tout localement, il n'y a donc rien à configurer et aucun service externe à connecter : - -```ts -// actions/analyze-responses.ts +```ts filename="actions/analyze-text.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "Graphique des réponses" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -Exécutez-la directement : +Essayez-la depuis le terminal : ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -Ou demandez à l'agent dans le navigateur : +Ou ouvrez votre application sur `http://localhost:8080` et demandez à l'agent dans le chat : + +> Run the analyze-text action on "Hello world. How are you today?" + +#### Définir une fois, appeler de partout + +Cette action est désormais accessible depuis le chat, les hooks React, la CLI, HTTP, MCP, A2A, +les tâches planifiées et les webhooks. + +CONSEIL : Chaque fois que vous souhaitez que l'agent appelle une action spécifique sans ambiguïté, +la formulation « Run the `` action » est la plus fiable. Les invites en langage naturel +fonctionnent bien une fois que l'agent dispose de suffisamment de contexte sur le domaine de votre +application. Pour une toute nouvelle application sans données ni contexte, être explicite est plus sûr. -> Analyse les réponses récentes du formulaire de démonstration. +## 4. Afficher le résultat en ligne {#render-inline} -## 3. Rendre le résultat dans le chat {#render-inline} +Lorsque l'agent exécute `analyze-text`, il retourne des données structurées : un titre et un +tableau de comptages. Par défaut, l'agent décrit ces données en prose : « Le texte contient +9 mots, 2 phrases... » et ainsi de suite. Cela fonctionne, mais vous pouvez également afficher +le résultat sous forme d'un vrai composant UI (un graphique à barres, un tableau, une carte) +directement dans la transcription du chat, là où l'agent a répondu. -Comme l'action déclare `outputSchema` et un `chatUI.renderer` propre au produit, le transcript ne transforme pas les données structurées en simple texte. Enregistrez un composant React pour cet id exact au démarrage de l'app, par exemple en important `app/chat-renderers.tsx` une fois depuis `app/root.tsx` : +C'est ce que fait `chatUI.renderer` dans l'action. C'est une étiquette qui indique « quand +le résultat de cette action apparaît dans le chat, transmettez-le à ce composant React plutôt +que de le résumer en texte. » Le composant reçoit la sortie validée de l'action en tant que +props et affiche ce que vous souhaitez. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +À l'étape suivante, vous créerez `app/chat-renderers.tsx`, mais d'abord, ajoutez une ligne +d'importation dans `app/root.tsx` pour qu'elle s'exécute au démarrage : -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +Ajoutez-la avec vos autres imports en haut du fichier. C'est le seul changement apporté à +`root.tsx`. L'import s'assure simplement que le fichier s'exécute et enregistre le rendu. +Maintenant, créez le fichier de rendu : + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +267,399 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Pour les sorties génériques réutilisables, le framework fournit aussi les renderers intégrés `data-chart` et `data-table`, ainsi que `data-insights` pour les cartes combinant résumé, graphique et tableau. Voir [Native Chat UI](/docs/native-chat-ui). - - -
User

Analyse les réponses de démo.

Agent

Rendu avec responses.response-chart.

Graphique des réponses

Lun
Mar
Mer
Jeu
" - } - /> - +Une fois le rendu enregistré, la réponse de l'agent ressemble à ceci. Au lieu d'un paragraphe +de texte, votre composant React s'affiche directement dans la transcription du chat. + +Utilisez cette étape lorsque le résultat appartient là où l'agent s'exprime : + +- résumés de configuration +- rapports courts +- approbations +- tableaux ou graphiques suffisamment petits pour être inspectés en ligne +- liens vers des vues d'application durables + +Pour les sorties génériques réutilisables, le framework inclut également des rendus intégrés +`data-chart` et `data-table`, ainsi que `data-insights` pour des cartes combinées +résumé/graphique/tableau. Voir [Interface de chat native](/docs/native-chat-ui). Pour les +contrôles temporaires que l'agent crée à l'exécution, voir +[Interface utilisateur générative](/docs/generative-ui). + +## 5. Persister les données en SQL {#persist-data} + +Pour l'instant, chaque fois que l'agent exécute `analyze-text`, le résultat apparaît dans le +chat puis disparaît. Il n'y a rien à consulter ultérieurement, rien que l'agent peut référencer +plus tard, et aucun moyen de construire une page autour des données. La persistance en SQL corrige +cela : l'agent écrit les résultats dans une table, et l'agent comme votre interface utilisateur +peuvent les relire à tout moment. + +Les applications Agent-Native disposent d'une base de données SQL disponible par défaut : SQLite +en local, et votre fournisseur configuré (Postgres, Turso/libSQL, Cloudflare D1) en production. + +### Connecter le plugin de base de données + +Le modèle Chat n'inclut pas de plugin de base de données par défaut. Créez +`server/plugins/db.ts` pour l'initialiser. C'est ce qui exécute les migrations et rend la base +de données disponible pour vos actions : + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. Persister les données dans SQL {#persist-data} +Chaque entrée dans le tableau est une migration additive. Lorsque vous ajoutez de nouvelles +colonnes ou tables ultérieurement, ajoutez un nouvel objet de version. Ne modifiez jamais +les existantes. -Quand le résultat doit être revu, comparé ou mis à jour, stockez-le dans SQL et gardez les lectures/écritures derrière des actions : +### Définir le schéma -Définissez la table avec les helpers portables du framework, pas avec des imports de `drizzle-orm/sqlite-core` ou `drizzle-orm/pg-core` : +Créez `server/db/schema.ts`. Le répertoire `server/db/` n'existe peut-être pas encore, +créez-le si nécessaire. Ce fichier décrit vos tables à l'aide de helpers typés pour que +vos actions bénéficient de la complétion automatique TypeScript complète : -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -Ces helpers choisissent le backend SQL configuré, donc le même schéma peut fonctionner avec SQLite en local et avec Postgres, Turso/libSQL, D1 ou un autre fournisseur SQL pris en charge en production. +Utilisez les helpers de schéma du framework (`table`, `text`, `integer`, `now`) plutôt que +`sqliteTable`, `pgTable` ou des imports spécifiques à un dialecte. Ils choisissent +automatiquement le backend SQL configuré, de sorte que le même schéma s'exécute localement +sur SQLite et en production sur n'importe quel fournisseur pris en charge. -- `create-response-insight` écrit un nouvel insight. -- `list-response-insights` lit les données de la page. -- `update-response-insight` met à jour un enregistrement. -- `analyze-responses` continue de renvoyer le widget natif du chat. +Après avoir ajouté les deux fichiers, redémarrez le serveur de développement pour que la +migration s'exécute : -N'ajoutez pas de REST route en double pour le navigateur ; l'UI et l'agent doivent partager les actions. +```bash +pnpm dev +``` -## 5. Ajouter une page que l'agent peut ouvrir {#add-a-page} +Recherchez ces deux lignes dans la sortie du terminal. Elles confirment que la table a été créée : -Ajoutez une route `/response-insights` avec `useActionQuery("list-response-insights")` et `useActionMutation("create-response-insight")`. La page est une projection du SQL state ; les données restent écrites par les mêmes actions. +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` - -

Insights de réponses

Insights créés par l'agent à partir des réponses du formulaire.

Thème de tarification

Les utilisateurs demandent un forfait équipe.

Abandon d'onboarding

Trois étapes demandent un texte plus clair.

Formulaire de démonstration42 réponses analysées
Étape suivanteRelire deux brouillons d'insights
" - } - /> -
+Les lignes `NitroViteError`, l'avertissement `BETTER_AUTH_SECRET` et l'avertissement +`SECRETS_ENCRYPTION_KEY` qui apparaissent également sont normaux pour le développement local +et peuvent être ignorés. -## 6. Laisser l'agent naviguer {#agent-navigation} +### Ajouter des actions pour la table -Le modèle Chat inclut les actions `view-screen` et `navigate`. Étendez-les à mesure que votre app grandit : +Créez maintenant les fichiers d'action qui lisent et écrivent dans la table. Ils se trouvent +dans votre répertoire `actions/`, au même endroit que `hello.ts` et `analyze-text.ts`. Vous +les créez vous-même, un fichier par opération. L'agent et votre interface utilisateur les +appelleront de la même manière qu'ils appellent n'importe quelle autre action. -- `view-screen` doit lire le application state et renvoyer la route active, l'insight sélectionné, les filtres actifs et tout contexte de page compact dont l'agent a besoin. -- `navigate` doit écrire un chemin same-origin comme `/response-insights` quand l'agent décide que l'utilisateur doit inspecter le résultat visuellement. -- Les résultats d'une action peuvent inclure des liens comme `href: "/response-insights"` pour que le chat propose un bouton explicite « Ouvrir les insights de réponses ». +**`actions/save-text-analysis.ts`** écrit une ligne de résultat dans la base de données. +Appelez cela après avoir exécuté `analyze-text` pour rendre le résultat durable : -Quand le chat pleine page ouvre une page d'app, utilisez `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks` et `chatViewTransition` depuis [Agent Surfaces](/docs/agent-surfaces#rich-chat) pour glisser le chat dans le panneau latéral sans perdre le thread. +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -## Structure du projet {#project-structure} +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` -```text -my-app/ - actions/ # Opérations appelables par l'agent et l'interface - app/ # Routes, pages et surfaces de chat React - server/ # Serveur Nitro et schéma SQL - AGENTS.md # Instructions permanentes pour l'agent de l'app - .agents/ # Skills que l'agent charge quand c'est pertinent - data/app.db # État SQLite local quand DATABASE_URL n'est pas défini +**`actions/list-text-analyses.ts`** lit tous les résultats sauvegardés. L'agent peut +l'appeler pour résumer les analyses passées, et votre interface utilisateur peut l'utiliser +pour alimenter une page : + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** supprime une ligne par identifiant : + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +Une fois ces fichiers sauvegardés, le serveur de développement les détecte automatiquement. +Aucun redémarrage n'est nécessaire. Essayez de lister les analyses depuis le terminal : + +```bash +pnpm action list-text-analyses +``` + +Vous devriez voir un tableau vide. La table existe et l'action fonctionne ; il n'y a simplement +rien de sauvegardé pour l'instant : + +``` +[] +``` + +Les données sont sauvegardées dans `data/app.db`, un fichier SQLite dans le répertoire de votre +projet qui est créé automatiquement lors de la première exécution. En production, vous +dirigeriez `DATABASE_URL` vers une base de données hébergée, mais localement ce fichier est +tout ce dont vous avez besoin. + +Pour sauvegarder quelque chose, exécutez d'abord `analyze-text` pour obtenir les comptages : + +```bash +pnpm action analyze-text --text "Hello world" +``` + +Vous verrez une sortie comme : + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +Ensuite, transmettez ces valeurs à `save-text-analysis` : + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +Maintenant, exécutez à nouveau `list-text-analyses` et vous verrez la ligne sauvegardée : + +```bash +pnpm action list-text-analyses +``` + +Ou demandez à l'agent dans le chat sur `http://localhost:8080` de faire les deux étapes à la fois : + +> Run analyze-text on "Hello world", then save the result. + +## 6. Ajouter une page que l'agent peut ouvrir {#add-a-page} + +Le chat est idéal pour les interactions conversationnelles, mais certaines données sont mieux +inspectées dans une interface dédiée : un tableau que vous pouvez parcourir, trier ou dont vous +pouvez supprimer des lignes. Cette étape ajoute une route React qui affiche tout ce qui est +sauvegardé dans `text_analyses`, en utilisant les mêmes actions `list-text-analyses` et +`delete-text-analysis` que vous avez déjà écrites. Il n'y a pas de deuxième couche de données. +La page est simplement une vue sur le même état SQL que l'agent lit et écrit. + +Créez le fichier de route dans `app/routes/text-analyses.tsx`. Les fichiers de route dans +`app/routes/` sont automatiquement détectés par le framework. Le nom de fichier devient le +chemin d'URL, donc cette page sera disponible sur `http://localhost:8080/text-analyses`. + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} ``` -## Vous voulez un vrai point de départ analytics ? {#analytics-starting-point} +`useActionQuery` appelle `list-text-analyses` et maintient le résultat en temps réel. Si l'agent +sauvegarde une nouvelle ligne pendant que la page est ouverte, elle apparaît automatiquement. +`useActionMutation` appelle `delete-text-analysis` lorsque l'utilisateur clique sur Delete, +puis invalide la requête pour que la liste se rafraîchisse. + +Ouvrez `http://localhost:8080/text-analyses` dans votre navigateur. Si vous avez sauvegardé une +analyse à l'étape précédente, vous la verrez listée. Puis demandez à l'agent dans le chat : + +> Open the text analyses page. -L'exemple Insights de réponses ci-dessus est volontairement petit pour montrer les pièces du framework. Si vous construisez un vrai produit analytics, commencez plutôt avec [Analytics](/docs/template-analytics). C'est le point de départ robuste : connectez vos providers, utilisez les dashboards et agent actions existants, puis personnalisez à partir de là. +Si vous obtenez une erreur 404, essayez de redémarrer votre serveur de développement. -## Suite {#next} +## 7. Étendre la navigation {#extend-navigation} + +La barre latérale est configurée dans `app/config/nav.ts`. Ouvrez-la et ajoutez une entrée pour +la page Analyses de texte : + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +Sauvegardez le fichier. Le serveur de développement détecte automatiquement la modification et +la barre latérale se met à jour sans redémarrage. + +### Navigation par l'agent + +Le lien dans la barre latérale permet aux utilisateurs de naviguer manuellement. L'agent peut +également ouvrir des pages de lui-même en utilisant deux actions intégrées fournies avec le +modèle Chat : + +- **`view-screen`** lit la route actuelle et retourne un résumé compact de ce que l'utilisateur + regarde. +- **`navigate`** écrit un chemin de même origine dans l'historique du navigateur. + +Au fur et à mesure que vous ajoutez des pages, maintenez `navigate` à jour pour que l'agent +connaisse les destinations disponibles. Documentez les chemins disponibles dans `AGENTS.md` pour +que le modèle puisse raisonner à leur sujet. + +Lorsque l'application possède à la fois une route de chat pleine page et une page d'application, +utilisez les helpers de transfert de chat partagés décrits dans +[Surfaces d'agent](/docs/agent-surfaces#rich-chat) : +`AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, +`useAgentChatHomeHandoffLinks` et `chatViewTransition`. Cela permet au chat pleine page de +glisser dans le panneau latéral à mesure que la page s'ouvre, en maintenant le même fil pendant +que l'utilisateur inspecte des données durables. + +## Structure du projet {#project-structure} + +```text +my-app/ + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset +``` -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks et transport. -- **[Native Chat UI](/docs/native-chat-ui)** — tableaux, graphiques et typed cards pour les résultats d'actions. -- **[Chat Template](/docs/template-chat)** — l'app minimale chat-first. -- **[Analytics Template](/docs/template-analytics)** — une base analytics robuste ; connectez vos providers puis personnalisez. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state et objets sélectionnés. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation et agents externes. +## Vous souhaitez un point de départ analytique complet ? {#analytics-starting-point} + +L'exemple text-analyses ci-dessus est intentionnellement petit pour vous permettre de voir +les éléments du framework. Si vous construisez un vrai produit d'analyse, commencez plutôt +par [Analytics](/docs/template-analytics). C'est le point de départ robuste : connectez vos +fournisseurs, utilisez les tableaux de bord et actions d'agent existants, puis personnalisez +l'application à partir de là. + +## Prochaines étapes {#next} + +- **[Actions](/docs/actions)** : schémas, authentification, approbations, hooks et transport. +- **[Interface de chat native](/docs/native-chat-ui)** : affichez les résultats d'action sous + forme de tableaux, graphiques et cartes typées. +- **[Modèle Chat](/docs/template-chat)** : l'application minimale orientée chat que vous venez + de créer. +- **[Modèle Analytics](/docs/template-analytics)** : un point de départ robuste pour une + application d'analyse ; connectez les fournisseurs et personnalisez à partir de là. +- **[Conscience du contexte](/docs/context-awareness)** : `view-screen`, `navigate`, + état des routes et objets sélectionnés. +- **[Surfaces d'agent](/docs/agent-surfaces)** : chat, interface en ligne, pages d'application, + barres latérales intégrées, automatisation et agents externes. +- **[Déploiement](/docs/deployment)** : publiez votre application sur votre propre domaine. diff --git a/packages/core/docs/content/locales/hi-IN/getting-started.mdx b/packages/core/docs/content/locales/hi-IN/getting-started.mdx index dc92964856..3f1e9672e9 100644 --- a/packages/core/docs/content/locales/hi-IN/getting-started.mdx +++ b/packages/core/docs/content/locales/hi-IN/getting-started.mdx @@ -1,23 +1,30 @@ --- -title: "शुरुआत" -description: "Chat से शुरू होने वाली agentic app बनाएं, action जोड़ें, structured results inline render करें, और फिर agent के खोलने योग्य persistent page तक बढ़ाएं." +title: "शुरुआत करें" +description: "एक chat-first agentic ऐप बनाएं, एक action जोड़ें, structured results को inline render करें, फिर एक persistent page में विस्तार करें जिसे agent खोल सके।" --- -# शुरुआत +# शुरुआत करें -Agent-Native agentic applications बनाने के लिए है: AI agent और UI वही [actions](/docs/actions), SQL data और application state साझा करते हैं. मुख्य रास्ता headless agent से नहीं, बल्कि Chat app से शुरू होता है ताकि उपयोगकर्ता agent से तुरंत बात कर सकें. +Agent-Native agentic applications के लिए है: ऐसे ऐप जहाँ AI agent और UI एक ही [actions](/docs/actions), SQL data, और application state साझा करते हैं। Chat से शुरू करें ताकि users तुरंत agent से बात कर सकें, फिर वे app surfaces जोड़ें जो आपका workflow अर्जित करे। -काम का क्रम: +सबसे तेज़ रास्ता है **Chat template**: एक minimal ऐप जो आपको एक काम करने वाला AI chat interface, durable threads, auth, और एक `actions/` directory देता है जो extend करने के लिए तैयार है। यह वह नींव है जिससे अधिकांश Agent-Native ऐप बढ़ते हैं। -1. Chat app बनाएं. -2. एक action जोड़ें. -3. action result को chat में native inline UI के रूप में render करें. -4. data को SQL में persist करें. -5. ऐसा page जोड़ें जिसे visual inspection ज़रूरी होने पर agent खोल सके. +पहला उपयोगी रास्ता है: -## 1. Chat app बनाएं {#create-your-app} +1. एक chat ऐप बनाएं। +2. एक AI engine कनेक्ट करें ताकि agent जवाब दे सके। +3. एक action जोड़ें। +4. Action का result chat में inline render करें। +5. SQL में data persist करें। +6. एक ऐसा page जोड़ें जिसे agent तब खोल सके जब visual inspection transcript में एक और paragraph से बेहतर हो। -[Node.js 22+](https://nodejs.org) और [pnpm](https://pnpm.io) चाहिए. +इसके बजाय एक complete domain ऐप चाहते हैं? [Mail](/docs/template-mail), [Calendar](/docs/template-calendar), [Forms](/docs/template-forms), [Analytics](/docs/template-analytics), या [Plan](/docs/template-plan) जैसा कोई rich template clone करें। अभी browser UI नहीं चाहते? इस tutorial के बाद [Automation-First Apps](/docs/pure-agent-apps) देखें। + +## 1. एक chat ऐप बनाएं {#create-your-app} + +आपको [Node.js 22+](https://nodejs.org) और [pnpm](https://pnpm.io) की ज़रूरत होगी। + +एक terminal खोलें, उस directory में जाएं जहाँ आप अपना Agent Native ऐप चाहते हैं, और चलाएं: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +33,182 @@ pnpm install pnpm dev ``` -इस template में durable chat threads, auth, live sync, `actions/` directory, standard `view-screen` और `navigate` actions, और extend करने योग्य React app शामिल है. +यह आपको durable chat threads, auth, live sync, एक `actions/` directory, standard `view-screen` और `navigate` actions, और एक छोटा React ऐप देता है जिसे आप extend कर सकते हैं। + +Dev server शुरू होता है और आपके browser में `http://localhost:8080` खोलता है। आपको कुछ startup log lines दिख सकती हैं जैसे `NitroViteError: Vite environment "nitro" is unavailable`। ये initial boot के दौरान एक सामान्य race condition हैं और अपने आप resolve हो जाती हैं। ऐप तब तैयार होता है जब terminal output में **VITE ready** दिखे। + +जब ऐप शुरू हो, तो आप ऐप में जाने के बजाय login screen पर हो सकते हैं। इस स्थिति में, बस login screen को satisfy करने के लिए एक बनावटी login से sign up करें। Local development के लिए, वास्तव में कोई email verification आवश्यक नहीं है। + +### यदि आप किसी अलग प्रकार का ऐप चाहते हैं + +यह guide Chat template का उपयोग करती है क्योंकि यह एक agentic application का सबसे छोटा रास्ता है: agent पहले दिन से काम कर सकता है, और आपके पास बढ़ने के लिए एक real app surface है। हालाँकि, यदि आप किसी अलग प्रकार का ऐप बनाना चाहते हैं, तो domain templates और advanced app shapes के लिए CLI picker के साथ बिना flags के `create` चलाएं: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. एक AI engine कनेक्ट करें {#connect-ai} + +AI engine कनेक्ट करने तक agent chat जवाब नहीं दे सकता। Login होने के बाद, **Connect AI** बटन पर क्लिक करें। + +आपके पास दो विकल्प हैं: + +**विकल्प A: Builder कनेक्ट करें।** **Connect Builder.io** पर क्लिक करें, Builder Space चुनें, और **Authorize** बटन पर क्लिक करें। आपको manually कोई keys प्रदान नहीं करनी होंगी। + +**विकल्प B: अपनी खुद की keys जोड़ें।** अपनी Anthropic या OpenAI API key दर्ज करें। आप [console.anthropic.com](https://console.anthropic.com) पर Anthropic key प्राप्त कर सकते हैं। + +वैकल्पिक रूप से, `pnpm dev` चलाने से पहले `my-app/` directory में (उसी directory में जिसमें `package.json` है) एक `.env` file बनाएं: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` -## 2. action जोड़ें {#add-an-action} +Dev server restart करें। एक बार AI engine कनेक्ट होने पर, Setup panel खुद छुप जाता है और agent chat करने के लिए तैयार है। -Action वह typed operation है जिसे agent और UI दोनों call करते हैं. इस flow के लिए `actions/analyze-responses.ts` बनाएं: यह form responses analyze करता है और custom chat chart के लिए validated shape लौटाता है. +> **Blank screen?** अपनी `my-app/` directory में `ANTHROPIC_API_KEY=sk-ant-...` के साथ एक `.env` file बनाएं, फिर `pnpm dev` restart करें। In-app Setup panel केवल तभी दिखता है जब ऐप load हो जाए, इसलिए एक missing key जो ऐप को render होने से रोकती है, उसे UI के बजाय environment variable के माध्यम से ठीक करना होगा। - -```ts -// actions/analyze-responses.ts +## 3. एक action जोड़ें {#add-an-action} + +Action एक typed operation है जिसे आपका agent और आपका UI दोनों call कर सकते हैं। यही वह तरीका है जिससे agent आपके ऐप में काम करता है। Actions `actions/` directory में रहते हैं और chat से, React components से, CLI से, या schedule पर trigger किए जा सकते हैं। आप उन्हें एक बार define करते हैं और कहीं से भी call करते हैं। + +### Starter action आज़माएं + +Chat template में `actions/hello.ts` पर एक `hello` action शामिल है: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +इसे terminal से चलाएं (अपनी `my-app/` directory के अंदर): + +```bash +pnpm action hello --name Alice +``` + +या अपने ऐप को `http://localhost:8080` पर खोलें और वहाँ chat में agent से पूछें: + +> Use the hello action with the name Alice. + +### अपना खुद का action जोड़ें + +Starter action को अपने domain के पहले real operation से बदलें। यह example किसी भी text में आप pass करते हैं उसमें words, sentences, और paragraphs गिनता है। यह सब कुछ locally compute करता है, इसलिए configure करने के लिए कुछ नहीं है और कोई external service कनेक्ट नहीं करनी है: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "प्रतिक्रिया चार्ट" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -इसे सीधे चलाएं: +Terminal से आज़माएं: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -या browser में agent से पूछें: +या अपने ऐप को `http://localhost:8080` पर खोलें और वहाँ chat में agent से पूछें: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### एक बार define करें, कहीं से भी call करें + +यह action अब chat, React hooks, CLI, HTTP, MCP, A2A, scheduled jobs, और webhooks से reachable है। + +TIP: जब भी आप चाहते हैं कि agent बिना किसी ambiguity के कोई specific action call करे, तो इसे "Run the `` action" के रूप में phrase करना सबसे reliable है। Natural-language prompts तब अच्छा काम करते हैं जब agent के पास आपके ऐप के domain के बारे में पर्याप्त context हो। बिना data या context वाले नए ऐप के लिए, explicit होना ज़्यादा सुरक्षित है। + +## 4. Result को inline render करें {#render-inline} + +जब agent `analyze-text` चलाता है, तो यह structured data return करता है: एक title और counts की एक array। By default agent उस data को prose में describe करेगा: "The text has 9 words, 2 sentences..." और इसी तरह। यह काम करता है, लेकिन आप result को एक real UI component (एक bar chart, एक table, एक card) के रूप में भी render कर सकते हैं सीधे chat transcript के अंदर, वहीं जहाँ agent ने जवाब दिया। -> डेमो फ़ॉर्म की हाल की प्रतिक्रियाओं का विश्लेषण करें. +यही `chatUI.renderer` action में करता है। यह एक label है जो कहता है "जब यह action का result chat में दिखे, तो इसे text में summarize करने के बजाय इस React component को दें।" Component validated action output को props के रूप में receive करता है और जो चाहे render करता है। -## 3. result को chat में render करें {#render-inline} +अगले step में, आप `app/chat-renderers.tsx` बनाएंगे, लेकिन पहले, `app/root.tsx` में एक import line जोड़ें ताकि यह startup पर चले: -क्योंकि action `outputSchema` और product-specific `chatUI.renderer` declare करता है, transcript structured data को plain text में नहीं बदलता. फिर app startup से उसी exact renderer id के लिए React component register करें, जैसे `app/root.tsx` से `app/chat-renderers.tsx` को एक बार import करके: +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +इसे file के top पर अपने अन्य imports के साथ जोड़ें। `root.tsx` में बस यही बदलाव है। Import केवल यह सुनिश्चित करता है कि file चले और renderer register हो। अब renderer file बनाएं: - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; -type ResponseChartResult = { +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +217,333 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Reusable generic outputs के लिए framework built-in `data-chart` और `data-table` renderers भी देता है, साथ ही combined summary/chart/table cards के लिए `data-insights`. देखें [Native Chat UI](/docs/native-chat-ui). +एक बार renderer register होने के बाद, agent का response इस तरह दिखता है। Text के एक paragraph के बजाय, आपका React component सीधे chat transcript के अंदर render होता है। + +इस step का उपयोग तब करें जब result वहीं belong करता हो जहाँ agent बोल रहा है: + +- setup summaries +- छोटी reports +- approvals +- tables या charts जो inline inspect करने के लिए काफी छोटे हों +- durable app views में links + +Reusable generic outputs के लिए, framework built-in `data-chart` और `data-table` renderers भी ship करता है, साथ ही combined summary/chart/table cards के लिए `data-insights`। देखें [Native Chat UI](/docs/native-chat-ui)। Agent द्वारा runtime पर बनाए गए temporary controls के लिए, देखें [Generative UI](/docs/generative-ui)। + +## 5. SQL में data persist करें {#persist-data} + +अभी, हर बार जब agent `analyze-text` चलाता है तो result chat में दिखता है और फिर गायब हो जाता है। वापस देखने के लिए कुछ नहीं है, agent बाद में reference नहीं कर सकता, और data के आसपास कोई page नहीं बना सकते। SQL में persist करना इसे ठीक करता है: agent एक table में results लिखता है, और agent और आपका UI दोनों उन्हें कभी भी वापस read कर सकते हैं। + +Agent-Native ऐप में by default एक SQL database उपलब्ध है: locally SQLite, और production में आपका configured provider (Postgres, Turso/libSQL, Cloudflare D1)। - -
User

डेमो प्रतिक्रियाओं का विश्लेषण करें.

Agent

responses.response-chart से रेंडर किया गया.

प्रतिक्रिया चार्ट

सोम
मंगल
बुध
गुरु
" - } - /> - +### Database plugin wire करें -## 4. SQL में data persist करें {#persist-data} +Chat template में by default database plugin शामिल नहीं है। इसे initialize करने के लिए `server/plugins/db.ts` बनाएं। यही वह है जो migrations चलाता है और database को आपके actions के लिए available बनाता है: -जब result को दोबारा देखना, compare करना या update करना हो, उसे SQL में रखें और reads/writes को actions के पीछे रखें: +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; -Table को framework के portable helpers से define करें, `drizzle-orm/sqlite-core` या `drizzle-orm/pg-core` imports से नहीं: +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` + +Array में प्रत्येक entry एक additive migration है। जब आप बाद में नए columns या tables जोड़ते हैं, तो एक नया version object append करें। मौजूदा ones को कभी edit न करें। + +### Schema define करें -```ts -// server/db/schema.ts +`server/db/schema.ts` बनाएं। `server/db/` directory अभी exist नहीं कर सकती, इसलिए अगर ज़रूरत हो तो इसे बनाएं। यह file typed helpers का उपयोग करके आपके tables describe करती है ताकि आपके actions को full TypeScript autocomplete मिले: + +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -ये helpers configured SQL backend चुनते हैं, इसलिए वही schema local SQLite और production में Postgres, Turso/libSQL, D1 या किसी supported SQL provider पर चल सकता है. +Dialect-specific imports के बजाय framework schema helpers (`table`, `text`, `integer`, `now`) का उपयोग करें जैसे `sqliteTable`, `pgTable`। ये configured SQL backend automatically pick करते हैं, इसलिए वही schema locally SQLite पर और production में किसी भी supported provider पर चलता है। + +दोनों files जोड़ने के बाद, dev server restart करें ताकि migration चले: + +```bash +pnpm dev +``` + +Terminal output में इन दो lines को देखें। ये confirm करती हैं कि table बनाई गई: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` + +`NitroViteError` lines, `BETTER_AUTH_SECRET` warning, और `SECRETS_ENCRYPTION_KEY` warning जो भी दिखती हैं, local dev के लिए सामान्य हैं और ignore की जा सकती हैं। -- `create-response-insight` नया insight लिखता है. -- `list-response-insights` page के लिए data पढ़ता है. -- `update-response-insight` record update करता है. -- `analyze-responses` chat का native widget लौटाता रहता है. +### Table के लिए actions जोड़ें -Browser के लिए अलग duplicate REST route न बनाएं; UI और agent को actions साझा करने चाहिए. +अब वे action files बनाएं जो table को read और write करें। ये आपकी `actions/` directory में जाती हैं, वहीं जहाँ `hello.ts` और `analyze-text.ts` हैं। आप उन्हें खुद बनाते हैं, प्रति operation एक file। Agent और आपका UI उन्हें उसी तरह call करेंगे जैसे वे किसी अन्य action को call करते हैं। + +**`actions/save-text-analysis.ts`** database में एक result row लिखता है। Result को durable बनाने के लिए `analyze-text` चलाने के बाद इसे call करें: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -## 5. agent के खोलने योग्य page जोड़ें {#add-a-page} +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`** सभी saved results read करता है। Agent इसे past analyses summarize करने के लिए call कर सकता है, और आपका UI इसे एक page populate करने के लिए उपयोग कर सकता है: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** id द्वारा एक row हटाता है: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +एक बार ये files save होने पर dev server उन्हें automatically pick कर लेता है। कोई restart नहीं चाहिए। Terminal से analyses list करके आज़माएं: + +```bash +pnpm action list-text-analyses +``` + +आपको एक empty array दिखनी चाहिए। Table exist करती है और action काम करता है; बस अभी तक कुछ save नहीं हुआ: + +``` +[] +``` + +Data `data/app.db` में save होता है, आपके project directory में एक SQLite file जो पहली बार run पर automatically बनती है। Production में आप `DATABASE_URL` को एक hosted database पर point करेंगे, लेकिन locally यह file ही काफी है। + +कुछ save करने के लिए, पहले counts पाने के लिए `analyze-text` चलाएं: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +आपको इस तरह का output दिखेगा: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +फिर उन values को `save-text-analysis` में pass करें: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +अब `list-text-analyses` फिर से चलाएं और आपको saved row दिखेगी: + +```bash +pnpm action list-text-analyses +``` + +या `http://localhost:8080` पर chat में agent से दोनों steps एक साथ करने के लिए कहें: + +> Run analyze-text on "Hello world", then save the result. + +## 6. एक ऐसा page जोड़ें जिसे agent खोल सके {#add-a-page} + +Conversational interaction के लिए Chat बहुत अच्छा है, लेकिन कुछ data को dedicated UI में बेहतर inspect किया जाता है: एक table जिसे आप scan, sort, या rows delete कर सकते हैं। यह step एक React route जोड़ता है जो `text_analyses` में save किए गए सब कुछ display करता है, उन्हीं `list-text-analyses` और `delete-text-analysis` actions का उपयोग करके जो आपने पहले ही लिखी हैं। कोई दूसरी data layer नहीं है। Page उसी SQL state का एक view है जिसे agent read और write करता है। + +`app/routes/text-analyses.tsx` पर route file बनाएं। `app/routes/` में Route files framework द्वारा automatically pick की जाती हैं। Filename URL path बन जाता है, इसलिए यह page `http://localhost:8080/text-analyses` पर उपलब्ध होगा। + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery` `list-text-analyses` call करता है और result को live रखता है। अगर page खुला होने पर agent कोई नई row save करता है, तो वह automatically दिखती है। `useActionMutation` तब `delete-text-analysis` call करता है जब user Delete पर click करता है, फिर query invalidate करता है ताकि list refresh हो। + +अपने browser में `http://localhost:8080/text-analyses` खोलें। अगर आपने पिछले step में कोई analysis save की थी तो आपको वह listed दिखेगी। फिर chat में agent से पूछें: + +> Open the text analyses page. + +अगर आपको 404 मिले, तो अपना dev server restart करने की कोशिश करें। + +## 7. Navigation extend करें {#extend-navigation} + +Sidebar `app/config/nav.ts` में configure होता है। इसे खोलें और Text analyses page के लिए एक entry जोड़ें: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` -`/response-insights` route जोड़ें और `useActionQuery("list-response-insights")`, `useActionMutation("create-response-insight")` इस्तेमाल करें. यह page SQL state का projection है; data वही actions लिखते हैं. +File save करें। Dev server automatically change pick up करता है और sidebar बिना restart के update हो जाता है। - -

प्रतिक्रिया insights

फ़ॉर्म प्रतिक्रियाओं से agent ने बनाए insights.

मूल्य निर्धारण थीम

यूज़र टीम प्लान मांगते हैं.

ऑनबोर्डिंग ड्रॉप-ऑफ़

तीन चरणों को ज्यादा स्पष्ट copy चाहिए.

डेमो फ़ॉर्म42 प्रतिक्रियाओं का विश्लेषण हुआ
अगला चरणदो draft insights देखें
" - } - /> -
+### Agent navigation -## 6. agent को navigate करने दें {#agent-navigation} +Sidebar link users को manually navigate करने देता है। Agent भी Chat template के साथ आने वाले दो built-in actions का उपयोग करके अपने आप pages खोल सकता है: -Chat template में `view-screen` और `navigate` actions शामिल हैं. आपकी app बढ़ने के साथ इन्हें extend करें: +- **`view-screen`** current route read करता है और एक compact summary return करता है कि user क्या देख रहा है। +- **`navigate`** browser के history में एक same-origin path लिखता है। -- `view-screen` को application state पढ़कर current route, selected insight, active filters, और agent को जरूरी किसी भी compact page context को return करना चाहिए. -- `navigate` को `/response-insights` जैसा same-origin path तब लिखना चाहिए जब agent तय करे कि user को result visually देखना चाहिए. -- action results में `href: "/response-insights"` जैसे links हो सकते हैं ताकि chat एक स्पष्ट "प्रतिक्रिया insights खोलें" button दिखा सके. +जैसे-जैसे आप अधिक pages जोड़ते हैं, `navigate` को updated रखें ताकि agent को पता रहे कि कौन से destinations exist करते हैं। Model को उनके बारे में reason करने के लिए `AGENTS.md` में available paths document करें। -जब full-page chat app page खोलता है, तो [Agent Surfaces](/docs/agent-surfaces#rich-chat) से `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks` और `chatViewTransition` इस्तेमाल करें ताकि वही thread रखते हुए chat side panel में slide हो जाए. +जब ऐप में full-page chat route और एक app page दोनों हों, तो [Agent Surfaces](/docs/agent-surfaces#rich-chat) में described shared chat handoff helpers का उपयोग करें: `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks`, और `chatViewTransition`। यह full chat को side panel में slide करने देता है जैसे page खुलती है, user के durable data inspect करते समय वही thread बनाए रखता है। ## Project structure {#project-structure} ```text my-app/ - actions/ # Agent और UI से कॉल किए जा सकने वाले operations - app/ # React routes, pages, और chat surfaces - server/ # Nitro server और SQL schema - AGENTS.md # App agent के लिए हमेशा सक्रिय instructions - .agents/ # ज़रूरत पड़ने पर agent जिन skills को लोड करता है - data/app.db # DATABASE_URL सेट न होने पर local SQLite state + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset ``` -## Analytics के लिए मजबूत starting point चाहिए? {#analytics-starting-point} +## क्या आप एक full analytics starting point चाहते हैं? {#analytics-starting-point} -ऊपर वाला प्रतिक्रिया insights example जानबूझकर छोटा है ताकि framework की pieces साफ दिखें. अगर आप वास्तविक analytics product बना रहे हैं, तो [Analytics](/docs/template-analytics) से शुरू करें. यह robust starting point है: अपने providers connect करें, existing dashboards और agent actions इस्तेमाल करें, फिर वहीं से customize करें. +ऊपर का text-analyses example जानबूझकर छोटा है ताकि आप framework के pieces देख सकें। अगर आप एक real analytics product बना रहे हैं, तो इसके बजाय [Analytics](/docs/template-analytics) से शुरू करें। यह robust starting point है: अपने providers कनेक्ट करें, existing dashboards और agent actions का उपयोग करें, फिर वहाँ से ऐप customize करें। -## आगे क्या {#next} +## आगे क्या है {#next} -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks और transport. -- **[Native Chat UI](/docs/native-chat-ui)** — action results को tables, charts और typed cards के रूप में render करें. -- **[Chat Template](/docs/template-chat)** — न्यूनतम chat-first app. -- **[Analytics Template](/docs/template-analytics)** — robust analytics app starting point; providers connect करें और customize करें. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state और selected objects. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation और external agents. +- **[Actions](/docs/actions)**: schemas, auth, approvals, hooks, और transport। +- **[Native Chat UI](/docs/native-chat-ui)**: action results को tables, charts, और typed cards के रूप में render करें। +- **[Chat Template](/docs/template-chat)**: वह minimal chat-first ऐप जो आपने अभी बनाया। +- **[Analytics Template](/docs/template-analytics)**: एक robust analytics ऐप starting point; providers कनेक्ट करें और वहाँ से customize करें। +- **[Context Awareness](/docs/context-awareness)**: `view-screen`, `navigate`, route state, और selected objects। +- **[Agent Surfaces](/docs/agent-surfaces)**: chat, inline UI, app pages, embedded sidecars, automation, और external agents। +- **[Deployment](/docs/deployment)**: अपने ऐप को अपने domain पर डालें। diff --git a/packages/core/docs/content/locales/ja-JP/getting-started.mdx b/packages/core/docs/content/locales/ja-JP/getting-started.mdx index 9e6866f91f..26a72dc16b 100644 --- a/packages/core/docs/content/locales/ja-JP/getting-started.mdx +++ b/packages/core/docs/content/locales/ja-JP/getting-started.mdx @@ -1,23 +1,30 @@ --- title: "はじめに" -description: "チャットから始まる agentic app を作成し、action を追加し、構造化結果をインライン表示して、agent が開ける永続ページへ広げます。" +description: "チャット優先のエージェントアプリを作成し、アクションを追加して、構造化された結果をインラインでレンダリングし、エージェントが開ける永続的なページへと発展させます。" --- # はじめに -Agent-Native は agentic applications を作るためのフレームワークです。AI agent と UI は同じ [actions](/docs/actions)、SQL データ、application state を共有します。最初に headless agent を作るのではなく、ユーザーがすぐ会話できる Chat app から始め、必要なアプリ画面を足していきます。 +Agent-Nativeはエージェントアプリケーション向けです。AIエージェントとUIが同じ[アクション](/docs/actions)、SQLデータ、アプリケーション状態を共有するアプリです。チャットから始めることで、ユーザーはすぐにエージェントと会話でき、ワークフローで必要になったアプリのサーフェスを追加していくことができます。 -基本の流れ: +最も手軽な方法は**Chatテンプレート**を使うことです。動作するAIチャットインターフェース、永続的なスレッド、認証、そして拡張可能な`actions/`ディレクトリを提供する最小限のアプリです。ほとんどのAgent-Nativeアプリはここから成長していきます。 -1. Chat app を作成する。 -2. action を追加する。 -3. action の結果をチャット内のネイティブ UI として表示する。 -4. データを SQL に永続化する。 -5. 視覚的に確認したいときに agent が開けるページを追加する。 +最初に辿る道筋は次のとおりです: -## 1. Chat app を作成する {#create-your-app} +1. チャットアプリを作成する。 +2. AIエンジンを接続してエージェントが応答できるようにする。 +3. アクションを1つ追加する。 +4. アクションの結果をチャット内にインラインでレンダリングする。 +5. SQLにデータを永続化する。 +6. 文字情報よりも視覚的な確認が適切な場合にエージェントが開けるページを追加する。 -[Node.js 22+](https://nodejs.org) と [pnpm](https://pnpm.io) が必要です。 +完成度の高いドメインアプリが欲しい場合は、[Mail](/docs/template-mail)、[Calendar](/docs/template-calendar)、[Forms](/docs/template-forms)、[Analytics](/docs/template-analytics)、[Plan](/docs/template-plan)などのリッチなテンプレートをクローンしてください。ブラウザUIをまだ使いたくない場合は、このチュートリアルの後に[Automation-First Apps](/docs/pure-agent-apps)を参照してください。 + +## 1. チャットアプリを作成する {#create-your-app} + +[Node.js 22+](https://nodejs.org)と[pnpm](https://pnpm.io)が必要です。 + +ターミナルを開き、Agent Nativeアプリを作成したいディレクトリに移動して、次のコマンドを実行します: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +33,182 @@ pnpm install pnpm dev ``` -このテンプレートには durable chat threads、auth、live sync、`actions/` ディレクトリ、標準の `view-screen` / `navigate` actions、拡張できる React app が含まれます。 +これにより、永続的なチャットスレッド、認証、ライブ同期、`actions/`ディレクトリ、標準の`view-screen`および`navigate`アクション、そして拡張可能な小さなReactアプリが得られます。 + +開発サーバーが起動し、ブラウザで`http://localhost:8080`が開きます。起動時のログに`NitroViteError: Vite environment "nitro" is unavailable`のような行がいくつか表示されることがありますが、これは初回起動時の通常の競合状態であり、自動的に解消されます。ターミナルの出力に**VITE ready**が表示されたらアプリの準備完了です。 + +アプリが起動したとき、アプリ内ではなくログイン画面が表示される場合があります。その場合は、ログイン画面を満たすために適当なログイン情報でサインアップしてください。ローカル開発環境では、メール認証は実際には不要です。 + +### 別の種類のアプリが必要な場合 + +このガイドではChatテンプレートを使用しています。これはエージェントアプリケーションへの最短ルートだからです。エージェントは初日から動作でき、成長の基盤となる実際のアプリサーフェスが得られます。ただし、別の種類のアプリを作成したい場合は、フラグなしで`create`を実行すると、ドメインテンプレートや高度なアプリ形状のCLIピッカーが表示されます: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. AIエンジンを接続する {#connect-ai} + +AIエンジンを接続するまで、エージェントチャットは応答できません。ログイン後、**Connect AI**ボタンをクリックしてください。 + +2つのオプションがあります: + +**オプションA:Builderを接続する。** **Connect Builder.io**をクリックし、Builder Spaceを選択して、**Authorize**ボタンをクリックします。手動でキーを入力する必要はありません。 + +**オプションB:独自のキーを追加する。** AnthropicまたはOpenAIのAPIキーを入力します。Anthropicのキーは[console.anthropic.com](https://console.anthropic.com)で取得できます。 + +または、`pnpm dev`を実行する前に`my-app/`ディレクトリ(`package.json`が含まれるディレクトリ)に`.env`ファイルを作成します: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` -## 2. action を追加する {#add-an-action} +開発サーバーを再起動してください。AIエンジンが接続されると、Setupパネルが非表示になり、エージェントはチャットの準備ができます。 -action は agent と UI が共有する typed operation です。この流れでは `actions/analyze-responses.ts` を作成し、フォーム回答を分析してカスタムチャットグラフ用の検証済み形状を返します。 +> **画面が真っ白?** `my-app/`ディレクトリに`ANTHROPIC_API_KEY=sk-ant-...`を含む`.env`ファイルを作成し、`pnpm dev`を再起動してください。アプリ内のSetupパネルはアプリがロードされた後にのみ表示されます。アプリのレンダリングを妨げるキーの欠如は、UIではなく環境変数で修正する必要があります。 - -```ts -// actions/analyze-responses.ts +## 3. アクションを追加する {#add-an-action} + +アクションは、エージェントとUIの両方から呼び出せる型付きの操作です。エージェントがアプリ内で何かを行う仕組みです。アクションは`actions/`ディレクトリに配置され、チャット、Reactコンポーネント、CLI、またはスケジュールからトリガーできます。一度定義すれば、どこからでも呼び出せます。 + +### スターターアクションを試す + +Chatテンプレートには`actions/hello.ts`に`hello`アクションが含まれています: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +ターミナルから実行します(`my-app/`ディレクトリ内で): + +```bash +pnpm action hello --name Alice +``` + +または`http://localhost:8080`でアプリを開き、チャットでエージェントに尋ねます: + +> Use the hello action with the name Alice. + +### 独自のアクションを追加する + +スターターアクションを自分のドメインの最初の実際の操作に置き換えましょう。この例では、渡したテキストの単語数、文数、段落数を数えます。すべてローカルで計算されるため、設定や外部サービスへの接続は不要です: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "回答チャート" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -直接実行できます: +ターミナルから試します: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -またはブラウザーで agent に依頼します: +または`http://localhost:8080`でアプリを開き、チャットでエージェントに尋ねます: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### 一度定義すれば、どこからでも呼び出せる + +このアクションは、チャット、Reactフック、CLI、HTTP、MCP、A2A、スケジュールジョブ、Webhookから呼び出せるようになりました。 + +TIP: エージェントに曖昧さなく特定のアクションを呼び出させたい場合は、「``アクションを実行して」という言い方が最も確実です。自然言語のプロンプトは、エージェントがアプリのドメインについて十分なコンテキストを持っている場合に有効です。データやコンテキストがまだない新しいアプリでは、明示的な指示の方が安全です。 + +## 4. 結果をインラインでレンダリングする {#render-inline} + +エージェントが`analyze-text`を実行すると、構造化データ(タイトルとカウントの配列)が返されます。デフォルトでは、エージェントはそのデータを散文で説明します:「テキストには9単語、2文...」などです。それでも機能しますが、エージェントが応答した場所のチャットトランスクリプト内に、実際のUIコンポーネント(棒グラフ、テーブル、カード)として結果をレンダリングすることもできます。 -> デモフォームの最近の回答を分析して。 +これがアクション内の`chatUI.renderer`の役割です。「このアクションの結果がチャットに表示されたとき、テキストで要約する代わりにこのReactコンポーネントに渡す」というラベルです。コンポーネントは検証済みのアクション出力をpropsとして受け取り、任意のものをレンダリングします。 -## 3. チャット内に結果を表示する {#render-inline} +次のステップで`app/chat-renderers.tsx`を作成しますが、まず`app/root.tsx`に1行のインポートを追加して、起動時に実行されるようにします: -action が `outputSchema` とプロダクト固有の `chatUI.renderer` を宣言しているため、チャットは構造化データを長い文章に潰しません。次に、app の起動時にその正確な renderer id に対応する React コンポーネントを登録します。たとえば `app/root.tsx` から `app/chat-renderers.tsx` を一度 import します。 +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +ファイルの先頭にある他のインポートの隣に追加してください。`root.tsx`への変更はこれだけです。このインポートは、ファイルが実行されてレンダラーが登録されることを保証するだけです。次にレンダラーファイルを作成します: - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; -type ResponseChartResult = { +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +217,333 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -再利用できる汎用出力には、framework 組み込みの `data-chart` と `data-table` renderers も使えます。`data-insights` は summary/chart/table を組み合わせたカード向けです。詳しくは [Native Chat UI](/docs/native-chat-ui) を参照してください。 +レンダラーが登録されると、エージェントの応答は次のようになります。テキストの段落の代わりに、ReactコンポーネントがチャットトランスクリプトD内に直接レンダリングされます。 + +このステップは、エージェントが発言している場所に結果が属する場合に使用します: + +- セットアップのサマリー +- 短いレポート +- 承認 +- インラインで確認できるほど小さなテーブルやグラフ +- 永続的なアプリビューへのリンク + +再利用可能な汎用出力については、フレームワークには組み込みの`data-chart`と`data-table`レンダラー、さらに組み合わせたサマリー/チャート/テーブルカード用の`data-insights`も含まれています。[Native Chat UI](/docs/native-chat-ui)を参照してください。エージェントが実行時に作成する一時的なコントロールについては、[Generative UI](/docs/generative-ui)を参照してください。 + +## 5. SQLにデータを永続化する {#persist-data} + +現時点では、エージェントが`analyze-text`を実行するたびに結果がチャットに表示されますが、すぐに消えてしまいます。後で振り返るものも、エージェントが後で参照できるものも、データを中心にページを構築する方法もありません。SQLに永続化することでこれが解決されます。エージェントが結果をテーブルに書き込み、エージェントとUIの両方がいつでも読み戻せます。 + +Agent-Nativeアプリはデフォルトでデータベースが利用可能です:ローカルではSQLite、本番環境では設定したプロバイダー(Postgres、Turso/libSQL、Cloudflare D1)が使用されます。 - -
User

デモの回答を分析して。

Agent

responses.response-chart で描画。

回答チャート

" - } - /> - +### データベースプラグインを接続する -## 4. SQL に永続化する {#persist-data} +Chatテンプレートはデフォルトではデータベースプラグインを含みません。`server/plugins/db.ts`を作成して初期化します。これがマイグレーションを実行し、アクションでデータベースを使用可能にします: -結果を後で見返す、比較する、更新する必要があるなら SQL に保存し、読み書きは actions の後ろに置きます。 +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; -テーブルは `drizzle-orm/sqlite-core` や `drizzle-orm/pg-core` から import するのではなく、framework のポータブルな helper で定義します。 +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` + +配列の各エントリは追加型のマイグレーションです。後で新しいカラムやテーブルを追加する場合は、新しいバージョンオブジェクトを追記してください。既存のものは絶対に編集しないでください。 + +### スキーマを定義する -```ts -// server/db/schema.ts +`server/db/schema.ts`を作成します。`server/db/`ディレクトリがまだ存在しない場合は作成してください。このファイルは型付きヘルパーを使ってテーブルを記述することで、アクションで完全なTypeScriptオートコンプリートが得られます: + +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -これらの helper は設定された SQL backend を選ぶため、同じ schema がローカルの SQLite と、production の Postgres、Turso/libSQL、D1、その他の対応 SQL provider で動きます。 +`sqliteTable`、`pgTable`、またはダイアレクト固有のインポートの代わりに、フレームワークのスキーマヘルパー(`table`、`text`、`integer`、`now`)を使用してください。これらは設定されたSQLバックエンドを自動的に選択するため、同じスキーマがローカルのSQLiteと本番環境の任意のサポートされたプロバイダーで動作します。 + +両方のファイルを追加したら、マイグレーションが実行されるように開発サーバーを再起動します: + +```bash +pnpm dev +``` + +ターミナルの出力でこの2行を探してください。テーブルが作成されたことを確認します: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` + +`NitroViteError`行、`BETTER_AUTH_SECRET`警告、`SECRETS_ENCRYPTION_KEY`警告も表示されますが、これらはローカル開発では正常であり無視できます。 -- `create-response-insight` は新しい insight を書き込みます。 -- `list-response-insights` はページ用のデータを読みます。 -- `update-response-insight` はレコードを更新します。 -- `analyze-responses` はチャット用のネイティブ widget を返し続けます。 +### テーブル用のアクションを追加する -ブラウザー専用の重複 REST route は作らず、UI と agent が actions を共有します。 +次に、テーブルの読み書きを行うアクションファイルを作成します。これらは`hello.ts`や`analyze-text.ts`と同じ`actions/`ディレクトリに配置します。操作ごとに1ファイルを自分で作成します。エージェントとUIは他のアクションと同じ方法でこれらを呼び出します。 + +**`actions/save-text-analysis.ts`**は結果行をデータベースに書き込みます。`analyze-text`の実行後にこれを呼び出して結果を永続化します: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; -## 5. agent が開けるページを追加する {#add-a-page} +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`**は保存済みの全結果を読み取ります。エージェントはこれを呼び出して過去の分析を要約でき、UIはページを表示するために使用できます: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`**はIDで行を削除します: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +これらのファイルが保存されると、開発サーバーは自動的に検出します。再起動は不要です。ターミナルから分析一覧を試してみましょう: + +```bash +pnpm action list-text-analyses +``` + +空の配列が表示されるはずです。テーブルは存在し、アクションは機能しています。まだ何も保存されていないだけです: + +``` +[] +``` + +データは`data/app.db`(プロジェクトディレクトリのSQLiteファイル)に保存され、初回実行時に自動的に作成されます。本番環境では`DATABASE_URL`をホストされたデータベースに向けますが、ローカルではこのファイルだけで十分です。 + +何かを保存するには、まず`analyze-text`を実行してカウントを取得します: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +次のような出力が表示されます: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +次にこれらの値を`save-text-analysis`に渡します: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +再度`list-text-analyses`を実行すると、保存された行が表示されます: + +```bash +pnpm action list-text-analyses +``` + +または`http://localhost:8080`のチャットでエージェントに両方のステップを一度に行うよう依頼します: + +> Run analyze-text on "Hello world", then save the result. + +## 6. エージェントが開けるページを追加する {#add-a-page} + +チャットは会話的なやり取りに最適ですが、データによっては専用UIで確認した方が良い場合があります(スキャン、ソート、行の削除ができるテーブルなど)。このステップでは、`text_analyses`に保存されたすべてのデータを表示するReactルートを追加します。すでに書いた`list-text-analyses`と`delete-text-analysis`アクションを同じように使用します。第二のデータレイヤーはありません。このページは、エージェントが読み書きする同じSQLステートのビューにすぎません。 + +`app/routes/text-analyses.tsx`にルートファイルを作成します。`app/routes/`内のルートファイルはフレームワークによって自動的に検出されます。ファイル名がURLパスになるため、このページは`http://localhost:8080/text-analyses`で利用できます。 + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery`は`list-text-analyses`を呼び出し、結果をライブに保ちます。ページが開いている間にエージェントが新しい行を保存すると、自動的に表示されます。`useActionMutation`はユーザーがDeleteをクリックしたときに`delete-text-analysis`を呼び出し、クエリを無効化してリストを更新します。 + +ブラウザで`http://localhost:8080/text-analyses`を開きます。前のステップで分析を保存していれば、それが一覧表示されます。次にチャットでエージェントに依頼します: + +> Open the text analyses page. + +404が表示された場合は、開発サーバーを再起動してください。 + +## 7. ナビゲーションを拡張する {#extend-navigation} + +サイドバーは`app/config/nav.ts`で設定されています。開いて、テキスト分析ページのエントリを追加してください: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` -`/response-insights` route を追加し、`useActionQuery("list-response-insights")` と `useActionMutation("create-response-insight")` を使います。このページは SQL state の表示であり、データは同じ actions で書き込まれます。 +ファイルを保存してください。開発サーバーは自動的に変更を検知し、再起動なしでサイドバーが更新されます。 - -

回答インサイト

エージェントがフォーム回答から作成したインサイト。

料金テーマ

ユーザーはチームプランを求めています。

オンボーディング離脱

3つのステップにより明確な文言が必要です。

デモフォーム42件の回答を分析済み
次のステップ2件のインサイト下書きを確認
" - } - /> -
+### エージェントナビゲーション -## 6. agent にナビゲートさせる {#agent-navigation} +サイドバーリンクにより、ユーザーは手動でナビゲートできます。エージェントはChatテンプレートに付属する2つの組み込みアクションを使用して、自分でページを開くこともできます: -Chat テンプレートには `view-screen` と `navigate` action が含まれます。アプリの成長に合わせて拡張してください。 +- **`view-screen`**は現在のルートを読み取り、ユーザーが見ているもののコンパクトなサマリーを返します。 +- **`navigate`**はブラウザの履歴に同一オリジンのパスを書き込みます。 -- `view-screen` は application state を読み取り、現在の route、選択中の insight、有効なフィルター、agent が必要とするコンパクトなページコンテキストを返す必要があります。 -- `navigate` は、agent がユーザーに結果を視覚的に確認させるべきと判断したときに、`/response-insights` のような same-origin path を書き込む必要があります。 -- action の結果には `href: "/response-insights"` のようなリンクを含められるため、チャットは明示的な「回答インサイトを開く」ボタンを提示できます。 +ページを追加していくにつれて、エージェントが利用可能な目的地を把握できるよう、`navigate`を更新し続けてください。モデルがそれらについて推論できるよう、`AGENTS.md`に利用可能なパスを文書化してください。 -フルページ chat から app ページを開くときは、[Agent Surfaces](/docs/agent-surfaces#rich-chat) の `AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、`useAgentChatHomeHandoffLinks`、`chatViewTransition` を使い、同じ thread のままチャットをサイドバーへ移動します。 +アプリにフルページのチャットルートとアプリページの両方がある場合は、[Agent Surfaces](/docs/agent-surfaces#rich-chat)で説明されている共有チャットハンドオフヘルパー(`AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、`useAgentChatHomeHandoffLinks`、`chatViewTransition`)を使用してください。これにより、ユーザーが永続的なデータを確認する際に、同じスレッドを維持したままフルチャットがサイドパネルにスライドインします。 -## プロジェクト構成 {#project-structure} +## プロジェクト構造 {#project-structure} ```text my-app/ - actions/ # エージェントとUIから呼び出せる操作 - app/ # Reactのルート、ページ、チャット画面 - server/ # NitroサーバーとSQLスキーマ - AGENTS.md # アプリアージェントの常時有効な指示 - .agents/ # 必要に応じてエージェントが読み込むスキル - data/app.db # DATABASE_URL未設定時のローカルSQLite状態 + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset ``` -## 本格的な Analytics の出発点が必要な場合 {#analytics-starting-point} +## 完全な分析の出発点が欲しい場合 {#analytics-starting-point} -上の 回答インサイト 例は、framework の部品を見やすくするために小さくしています。実際の analytics product を作るなら、[Analytics](/docs/template-analytics) から始めてください。providers を接続し、既存の dashboards と agent actions を使い、そこからカスタマイズできる堅牢な出発点です。 +上記のtext-analysesの例は、フレームワークの各部分を理解できるように意図的に小さくしています。実際の分析製品を構築する場合は、代わりに[Analytics](/docs/template-analytics)から始めてください。それが堅牢な出発点です:プロバイダーを接続し、既存のダッシュボードとエージェントアクションを使用し、そこからアプリをカスタマイズしてください。 ## 次のステップ {#next} -- **[Actions](/docs/actions)** — schemas、auth、approvals、hooks、transport。 -- **[Native Chat UI](/docs/native-chat-ui)** — action 結果をテーブル、グラフ、typed cards として表示。 -- **[Chat Template](/docs/template-chat)** — 最小の chat-first app。 -- **[Analytics Template](/docs/template-analytics)** — 堅牢な analytics app の出発点。providers を接続してカスタマイズできます。 -- **[Context Awareness](/docs/context-awareness)** — `view-screen`、`navigate`、route state、選択中のオブジェクト。 -- **[Agent Surfaces](/docs/agent-surfaces)** — chat、inline UI、app pages、embedded sidecars、automation、外部 agent。 +- **[Actions](/docs/actions)**:スキーマ、認証、承認、フック、トランスポート。 +- **[Native Chat UI](/docs/native-chat-ui)**:アクションの結果をテーブル、グラフ、型付きカードとしてレンダリング。 +- **[Chat Template](/docs/template-chat)**:作成したばかりの最小限のチャット優先アプリ。 +- **[Analytics Template](/docs/template-analytics)**:堅牢な分析アプリの出発点。プロバイダーを接続してそこからカスタマイズ。 +- **[Context Awareness](/docs/context-awareness)**:`view-screen`、`navigate`、ルートステート、選択オブジェクト。 +- **[Agent Surfaces](/docs/agent-surfaces)**:チャット、インラインUI、アプリページ、埋め込みサイドカー、自動化、外部エージェント。 +- **[Deployment](/docs/deployment)**:アプリを独自ドメインに公開する。 diff --git a/packages/core/docs/content/locales/ko-KR/getting-started.mdx b/packages/core/docs/content/locales/ko-KR/getting-started.mdx index 46f21af9a6..4d862c743d 100644 --- a/packages/core/docs/content/locales/ko-KR/getting-started.mdx +++ b/packages/core/docs/content/locales/ko-KR/getting-started.mdx @@ -1,24 +1,39 @@ --- title: "시작하기" -description: "채팅 우선 agentic app을 만들고, action을 추가하고, 구조화된 결과를 인라인으로 렌더링한 뒤 agent가 열 수 있는 영구 페이지로 확장합니다." +description: "채팅 우선 에이전트 앱을 만들고, 액션을 추가하고, 구조화된 결과를 인라인으로 렌더링한 다음, 에이전트가 열 수 있는 영구 페이지로 확장하세요." --- # 시작하기 -Agent-Native는 agentic applications를 만들기 위한 프레임워크입니다. AI agent와 UI는 같은 [actions](/docs/actions), SQL 데이터, application state를 공유합니다. 기본 경로는 headless agent가 아니라 사용자가 즉시 대화할 수 있는 Chat app에서 시작한 뒤 필요한 앱 화면을 추가하는 것입니다. +Agent-Native는 에이전트 애플리케이션을 위한 프레임워크입니다. AI 에이전트와 UI가 +동일한 [액션](/docs/actions), SQL 데이터, 애플리케이션 상태를 공유하는 앱입니다. 사용자가 +에이전트와 즉시 대화할 수 있도록 채팅으로 시작한 다음, 워크플로우에 맞는 앱 화면을 추가하세요. -권장 흐름: +가장 빠른 시작 방법은 **Chat 템플릿**입니다. 작동하는 AI 채팅 인터페이스, 지속적인 스레드, +인증, 그리고 확장 가능한 `actions/` 디렉터리를 제공하는 최소한의 앱입니다. 대부분의 +Agent-Native 앱이 여기서 성장합니다. -1. Chat app을 만듭니다. -2. action을 하나 추가합니다. -3. action 결과를 채팅 안의 native inline UI로 렌더링합니다. -4. 데이터를 SQL에 저장합니다. -5. 시각적으로 확인해야 할 때 agent가 열 수 있는 페이지를 추가합니다. +첫 번째 유용한 경로는 다음과 같습니다: -## 1. Chat app 만들기 {#create-your-app} +1. 채팅 앱을 만듭니다. +2. 에이전트가 응답할 수 있도록 AI 엔진을 연결합니다. +3. 액션 하나를 추가합니다. +4. 채팅에서 액션 결과를 인라인으로 렌더링합니다. +5. SQL에 데이터를 저장합니다. +6. 트랜스크립트에 또 다른 단락을 추가하는 것보다 시각적 검사가 더 나을 때 에이전트가 + 열 수 있는 페이지를 추가합니다. + +완전한 도메인 앱을 원하시나요? [Mail](/docs/template-mail), [Calendar](/docs/template-calendar), +[Forms](/docs/template-forms), [Analytics](/docs/template-analytics), 또는 +[Plan](/docs/template-plan)과 같은 풍부한 템플릿을 복제하세요. 아직 브라우저 UI가 필요 +없으신가요? 이 튜토리얼 이후에 [자동화 우선 앱](/docs/pure-agent-apps)을 참조하세요. + +## 1. 채팅 앱 만들기 {#create-your-app} [Node.js 22+](https://nodejs.org)와 [pnpm](https://pnpm.io)이 필요합니다. +터미널을 열고 Agent Native 앱을 만들 디렉터리로 이동한 후 다음을 실행하세요: + ```bash npx @agent-native/core@latest create my-app --template chat cd my-app @@ -26,75 +41,209 @@ pnpm install pnpm dev ``` -이 템플릿에는 durable chat threads, auth, live sync, `actions/` 디렉터리, 표준 `view-screen` 및 `navigate` actions, 확장 가능한 React app이 포함됩니다. +이렇게 하면 지속적인 채팅 스레드, 인증, 라이브 동기화, `actions/` 디렉터리, +표준 `view-screen` 및 `navigate` 액션, 그리고 확장 가능한 소규모 React 앱이 제공됩니다. + +개발 서버가 시작되고 브라우저에서 `http://localhost:8080`이 열립니다. 초기 부팅 중에 +`NitroViteError: Vite environment "nitro" is unavailable`과 같은 시작 로그 메시지가 +몇 개 보일 수 있습니다. 이는 초기 부팅 중 정상적인 경쟁 조건으로 자동으로 해결됩니다. +터미널 출력에 **VITE ready**가 표시되면 앱이 준비된 것입니다. + +앱이 시작되면 앱 대신 로그인 화면이 나타날 수 있습니다. 이 경우 로그인 화면을 통과하기 위해 +임의의 로그인 정보로 가입하면 됩니다. 로컬 개발에서는 실제로 이메일 인증이 필요하지 않습니다. + +### 다른 유형의 앱을 원하는 경우 + +이 가이드는 Chat 템플릿을 사용합니다. 에이전트 애플리케이션으로의 가장 짧은 경로이기 때문입니다. +에이전트가 첫날부터 작동할 수 있고, 성장할 수 있는 실제 앱 화면이 있습니다. 그러나 다른 종류의 +앱을 만들고 싶다면 도메인 템플릿과 고급 앱 형태를 위한 CLI 선택기를 사용하여 플래그 없이 +`create`를 실행하세요: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. AI 엔진 연결하기 {#connect-ai} + +AI 엔진을 연결하기 전까지는 에이전트 채팅이 응답할 수 없습니다. 로그인 후 **Connect AI** 버튼을 클릭하세요. + +두 가지 옵션이 있습니다: + +**옵션 A: Builder 연결.** **Connect Builder.io**를 클릭하고 Builder Space를 선택한 후 **Authorize** 버튼을 클릭하세요. 키를 수동으로 제공할 필요가 없습니다. -## 2. action 추가하기 {#add-an-action} +**옵션 B: 직접 키 추가.** Anthropic 또는 OpenAI API 키를 입력하세요. Anthropic 키는 +[console.anthropic.com](https://console.anthropic.com)에서 받을 수 있습니다. + +또는 `pnpm dev`를 실행하기 전에 `my-app/` 디렉터리(`package.json`이 있는 동일한 디렉터리)에 +`.env` 파일을 만드세요: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` -action은 agent와 UI가 함께 호출하는 typed operation입니다. 이 흐름에서는 `actions/analyze-responses.ts`를 만들고, 폼 응답을 분석해 custom chat chart를 위한 검증된 형태를 반환합니다. +개발 서버를 재시작하세요. AI 엔진이 연결되면 설정 패널이 숨겨지고 에이전트가 채팅할 준비가 됩니다. - -```ts -// actions/analyze-responses.ts +> **빈 화면?** `my-app/` 디렉터리에 `ANTHROPIC_API_KEY=sk-ant-...`가 포함된 `.env` 파일을 +> 만든 후 `pnpm dev`를 재시작하세요. 앱 내 설정 패널은 앱이 로드된 후에만 나타나므로, 앱 +> 렌더링을 방해하는 누락된 키는 UI가 아닌 환경 변수를 통해 수정해야 합니다. + +## 3. 액션 추가하기 {#add-an-action} + +액션은 에이전트와 UI 모두 호출할 수 있는 타입이 지정된 작업입니다. 에이전트가 앱에서 +작업을 수행하는 방식입니다. 액션은 `actions/` 디렉터리에 있으며 채팅에서, React 컴포넌트에서, +CLI에서, 또는 스케줄에 따라 트리거될 수 있습니다. 한 번 정의하면 어디서나 호출할 수 있습니다. + +### 스타터 액션 사용해보기 + +Chat 템플릿에는 `actions/hello.ts`에 `hello` 액션이 포함되어 있습니다: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +터미널에서 실행하세요(`my-app/` 디렉터리 내): + +```bash +pnpm action hello --name Alice +``` + +또는 `http://localhost:8080`에서 앱을 열고 채팅에서 에이전트에게 요청하세요: + +> Use the hello action with the name Alice. + +### 나만의 액션 추가하기 + +스타터 액션을 도메인의 첫 번째 실제 작업으로 교체하세요. 이 예시는 전달하는 텍스트의 +단어, 문장, 단락을 계산합니다. 모든 것을 로컬에서 계산하므로 구성할 것도, 연결할 외부 +서비스도 없습니다: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "응답 차트" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -직접 실행할 수 있습니다. +터미널에서 실행해보세요: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -또는 브라우저에서 agent에게 요청합니다. +또는 `http://localhost:8080`에서 앱을 열고 채팅에서 에이전트에게 요청하세요: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### 한 번 정의하면 어디서나 호출 가능 + +이 액션은 이제 채팅, React 훅, CLI, HTTP, MCP, A2A, 스케줄된 작업, 웹훅에서 모두 +접근할 수 있습니다. + +팁: 에이전트가 모호함 없이 특정 액션을 호출하도록 하려면 "Run the `` action" +형식으로 지정하는 것이 가장 안정적입니다. 에이전트가 앱의 도메인에 대한 충분한 컨텍스트를 +가지면 자연어 프롬프트도 잘 작동합니다. 데이터나 컨텍스트가 없는 완전히 새로운 앱에서는 +명시적인 것이 더 안전합니다. -> 데모 양식의 최근 응답을 분석해 주세요. +## 4. 결과를 인라인으로 렌더링하기 {#render-inline} -## 3. 채팅 안에 결과 렌더링하기 {#render-inline} +에이전트가 `analyze-text`를 실행하면 구조화된 데이터인 제목과 개수 배열이 반환됩니다. +기본적으로 에이전트는 해당 데이터를 산문으로 설명합니다: "텍스트에 9개의 단어, 2개의 +문장이 있습니다..." 등. 이것도 작동하지만, 에이전트가 응답한 바로 그 위치의 채팅 +트랜스크립트 안에 실제 UI 컴포넌트(막대 차트, 테이블, 카드)로 결과를 렌더링할 수도 있습니다. -action이 `outputSchema`와 제품별 `chatUI.renderer`를 선언하므로 transcript가 구조화된 데이터를 긴 텍스트로 바꾸지 않습니다. 그런 다음 app startup에서 정확히 같은 renderer id에 대한 React component를 등록합니다. 예를 들어 `app/root.tsx`에서 `app/chat-renderers.tsx`를 한 번 import합니다. +이것이 액션의 `chatUI.renderer`가 하는 일입니다. "이 액션의 결과가 채팅에 나타날 때, +텍스트로 요약하는 대신 이 React 컴포넌트에 전달하세요"라고 말하는 레이블입니다. +컴포넌트는 검증된 액션 출력을 props로 받아 원하는 것을 렌더링합니다. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +다음 단계에서 `app/chat-renderers.tsx`를 만들겠지만, 먼저 시작 시 실행되도록 +`app/root.tsx`에 임포트 한 줄을 추가하세요: -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +파일 상단의 다른 임포트와 함께 추가하세요. 이것이 `root.tsx`의 유일한 변경사항입니다. +임포트는 파일이 실행되고 렌더러가 등록되도록 보장할 뿐입니다. 이제 렌더러 파일을 만드세요: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,97 +252,383 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -재사용 가능한 generic output에는 framework 내장 `data-chart`와 `data-table` renderer도 사용할 수 있고, `data-insights`는 summary/chart/table이 결합된 카드에 사용합니다. 자세한 내용은 [Native Chat UI](/docs/native-chat-ui)를 참조하세요. - - -
User

데모 응답을 분석해 주세요.

Agent

responses.response-chart로 렌더링됨.

응답 차트

" - } - /> - +렌더러가 등록되면 에이전트의 응답은 다음과 같이 보입니다. 텍스트 단락 대신 React +컴포넌트가 채팅 트랜스크립트 안에 직접 렌더링됩니다. + +결과가 에이전트가 말하는 위치에 속할 때 이 단계를 사용하세요: + +- 설정 요약 +- 짧은 보고서 +- 승인 +- 인라인으로 검사하기에 충분히 작은 테이블이나 차트 +- 지속적인 앱 뷰로의 링크 + +재사용 가능한 일반 출력을 위해 프레임워크에는 내장 `data-chart` 및 `data-table` +렌더러와 결합된 요약/차트/테이블 카드를 위한 `data-insights`도 제공됩니다. +[Native Chat UI](/docs/native-chat-ui)를 참조하세요. 에이전트가 런타임에 만드는 +임시 컨트롤은 [Generative UI](/docs/generative-ui)를 참조하세요. + +## 5. SQL에 데이터 저장하기 {#persist-data} + +현재 에이전트가 `analyze-text`를 실행할 때마다 결과가 채팅에 나타났다가 사라집니다. +나중에 돌아볼 것도 없고, 에이전트가 나중에 참조할 수도 없으며, 데이터를 기반으로 +페이지를 구성할 방법도 없습니다. SQL에 저장하면 이 문제가 해결됩니다. 에이전트가 +결과를 테이블에 쓰면, 에이전트와 UI 모두 언제든지 읽어올 수 있습니다. + +Agent-Native 앱에는 기본적으로 SQL 데이터베이스가 있습니다. 로컬에서는 SQLite, +프로덕션에서는 구성된 프로바이더(Postgres, Turso/libSQL, Cloudflare D1)를 사용합니다. + +### 데이터베이스 플러그인 연결하기 + +Chat 템플릿은 기본적으로 데이터베이스 플러그인을 포함하지 않습니다. 초기화하려면 +`server/plugins/db.ts`를 만드세요. 이것이 마이그레이션을 실행하고 액션에서 +데이터베이스를 사용할 수 있게 해줍니다: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. SQL에 데이터 저장하기 {#persist-data} +배열의 각 항목은 추가적인 마이그레이션입니다. 나중에 새 열이나 테이블을 추가할 때는 +새 버전 객체를 추가하세요. 기존 것은 절대 편집하지 마세요. -결과를 다시 보거나 비교하거나 업데이트해야 한다면 SQL에 저장하고 읽기/쓰기를 actions 뒤에 둡니다. +### 스키마 정의하기 -테이블은 `drizzle-orm/sqlite-core` 또는 `drizzle-orm/pg-core`에서 가져오지 말고 framework의 portable helper로 정의합니다. +`server/db/schema.ts`를 만드세요. `server/db/` 디렉터리가 아직 없을 수 있으므로 +필요하다면 만드세요. 이 파일은 타입이 지정된 헬퍼를 사용하여 테이블을 설명하므로 +액션에서 TypeScript 자동 완성을 사용할 수 있습니다: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -이 helper들은 설정된 SQL backend를 선택하므로 같은 schema가 로컬 SQLite와 프로덕션의 Postgres, Turso/libSQL, D1 또는 다른 지원 SQL provider에서 실행될 수 있습니다. +`sqliteTable`, `pgTable`, 또는 방언별 임포트 대신 프레임워크 스키마 헬퍼(`table`, +`text`, `integer`, `now`)를 사용하세요. 구성된 SQL 백엔드를 자동으로 선택하므로 +동일한 스키마가 로컬 SQLite와 지원되는 모든 프로덕션 프로바이더에서 실행됩니다. + +두 파일을 추가한 후, 마이그레이션이 실행되도록 개발 서버를 재시작하세요: + +```bash +pnpm dev +``` + +터미널 출력에서 다음 두 줄을 확인하세요. 테이블이 생성되었음을 확인합니다: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` + +`NitroViteError` 줄, `BETTER_AUTH_SECRET` 경고, 그리고 `SECRETS_ENCRYPTION_KEY` +경고도 나타나지만 로컬 개발에서는 정상이므로 무시할 수 있습니다. -- `create-response-insight`는 새 insight를 씁니다. -- `list-response-insights`는 페이지 데이터를 읽습니다. -- `update-response-insight`는 레코드를 업데이트합니다. -- `analyze-responses`는 계속 채팅용 native widget을 반환합니다. +### 테이블용 액션 추가하기 -브라우저 전용 중복 REST route를 만들지 말고 UI와 agent가 actions를 공유하게 하세요. +이제 테이블을 읽고 쓰는 액션 파일을 만드세요. `hello.ts` 및 `analyze-text.ts`와 +같은 위치인 `actions/` 디렉터리에 넣습니다. 작업별로 파일 하나씩 직접 만드세요. +에이전트와 UI는 다른 액션을 호출하는 것과 같은 방식으로 이를 호출합니다. -## 5. agent가 열 수 있는 페이지 추가하기 {#add-a-page} +**`actions/save-text-analysis.ts`**는 결과 행을 데이터베이스에 씁니다. +`analyze-text`를 실행한 후 결과를 지속시키기 위해 이것을 호출하세요: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`**는 저장된 모든 결과를 읽습니다. 에이전트는 +이것을 호출하여 과거 분석을 요약할 수 있고, UI는 페이지를 채우는 데 사용할 수 있습니다: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`**는 id로 행을 삭제합니다: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +이 파일들이 저장되면 개발 서버가 자동으로 감지합니다. 재시작이 필요 없습니다. +터미널에서 분석 목록을 확인해보세요: + +```bash +pnpm action list-text-analyses +``` + +빈 배열이 보여야 합니다. 테이블이 존재하고 액션이 작동하지만 아직 저장된 것이 없습니다: + +``` +[] +``` + +데이터는 `data/app.db`에 저장됩니다. 처음 실행할 때 프로젝트 디렉터리에 자동으로 +생성되는 SQLite 파일입니다. 프로덕션에서는 `DATABASE_URL`을 호스팅된 데이터베이스로 +지정하겠지만, 로컬에서는 이 파일만 있으면 됩니다. + +무언가를 저장하려면 먼저 `analyze-text`를 실행하여 개수를 얻으세요: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +다음과 같은 출력이 표시됩니다: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +그런 다음 해당 값을 `save-text-analysis`에 전달하세요: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +이제 `list-text-analyses`를 다시 실행하면 저장된 행이 보입니다: + +```bash +pnpm action list-text-analyses +``` + +또는 `http://localhost:8080`의 채팅에서 에이전트에게 두 단계를 한 번에 수행하도록 요청하세요: + +> Run analyze-text on "Hello world", then save the result. + +## 6. 에이전트가 열 수 있는 페이지 추가하기 {#add-a-page} + +채팅은 대화형 상호작용에 훌륭하지만, 일부 데이터는 전용 UI에서 검사하는 것이 더 좋습니다. +스캔하거나 정렬하거나 행을 삭제할 수 있는 테이블처럼요. 이 단계에서는 이미 작성한 +`list-text-analyses` 및 `delete-text-analysis` 액션을 사용하여 `text_analyses`에 +저장된 모든 것을 표시하는 React 라우트를 추가합니다. 두 번째 데이터 레이어는 없습니다. +페이지는 에이전트가 읽고 쓰는 동일한 SQL 상태 위의 뷰일 뿐입니다. + +`app/routes/text-analyses.tsx`에 라우트 파일을 만드세요. `app/routes/`의 라우트 +파일은 프레임워크에 의해 자동으로 감지됩니다. 파일 이름이 URL 경로가 되므로 이 +페이지는 `http://localhost:8080/text-analyses`에서 사용할 수 있습니다. + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery`는 `list-text-analyses`를 호출하고 결과를 라이브로 유지합니다. +페이지가 열려 있는 동안 에이전트가 새 행을 저장하면 자동으로 나타납니다. +`useActionMutation`은 사용자가 Delete를 클릭할 때 `delete-text-analysis`를 호출한 +다음 쿼리를 무효화하여 목록이 새로고침됩니다. + +브라우저에서 `http://localhost:8080/text-analyses`를 여세요. 이전 단계에서 분석을 +저장했다면 목록에서 볼 수 있습니다. 그런 다음 채팅에서 에이전트에게 요청하세요: + +> Open the text analyses page. + +404가 발생하면 개발 서버를 재시작해보세요. + +## 7. 내비게이션 확장하기 {#extend-navigation} + +사이드바는 `app/config/nav.ts`에서 구성됩니다. 파일을 열고 Text analyses +페이지의 항목을 추가하세요: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` -`/response-insights` route를 추가하고 `useActionQuery("list-response-insights")`, `useActionMutation("create-response-insight")`를 사용합니다. 이 페이지는 SQL state의 투영이며 데이터는 같은 actions로 계속 쓰입니다. +파일을 저장하세요. 개발 서버가 변경 사항을 자동으로 감지하고 재시작 없이 +사이드바가 업데이트됩니다. - -

응답 인사이트

에이전트가 양식 응답에서 만든 인사이트입니다.

가격 책정 주제

사용자가 팀 요금제를 요청합니다.

온보딩 이탈

세 단계에 더 명확한 문구가 필요합니다.

데모 양식42개 응답 분석됨
다음 단계인사이트 초안 두 개 검토
" - } - /> -
+### 에이전트 내비게이션 -## 6. agent가 탐색하게 하기 {#agent-navigation} +사이드바 링크를 통해 사용자가 수동으로 탐색할 수 있습니다. 에이전트는 Chat +템플릿에 포함된 두 가지 내장 액션을 사용하여 스스로 페이지를 열 수도 있습니다: -Chat 템플릿에는 `view-screen`과 `navigate` action이 포함되어 있습니다. 앱이 성장함에 따라 이를 확장하세요: +- **`view-screen`**은 현재 라우트를 읽고 사용자가 보고 있는 것의 간결한 요약을 반환합니다. +- **`navigate`**는 브라우저 히스토리에 동일 출처 경로를 씁니다. -- `view-screen`은 application state를 읽어 현재 route, 선택된 insight, 활성 필터, agent에게 필요한 간결한 페이지 컨텍스트를 반환해야 합니다. -- `navigate`는 agent가 사용자에게 결과를 시각적으로 확인시켜야 한다고 판단할 때 `/response-insights`와 같은 same-origin 경로를 기록해야 합니다. -- action 결과에는 `href: "/response-insights"` 같은 링크를 포함할 수 있어, chat이 명시적인 "응답 인사이트 열기" 버튼을 제공할 수 있습니다. +더 많은 페이지를 추가할수록 에이전트가 어떤 목적지가 있는지 알 수 있도록 `navigate`를 +업데이트하세요. `AGENTS.md`에 사용 가능한 경로를 문서화하여 모델이 이에 대해 +추론할 수 있도록 하세요. -전체 페이지 chat에서 app 페이지를 열 때는 [Agent Surfaces](/docs/agent-surfaces#rich-chat)의 `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks`, `chatViewTransition`을 사용해 같은 thread를 유지한 채 chat을 사이드바로 이동시킵니다. +앱에 전체 페이지 채팅 라우트와 앱 페이지가 모두 있을 때, [Agent Surfaces](/docs/agent-surfaces#rich-chat)에 +설명된 공유 채팅 핸드오프 헬퍼를 사용하세요: +`AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, +`useAgentChatHomeHandoffLinks`, `chatViewTransition`. 이를 통해 페이지가 열릴 때 +전체 채팅이 사이드 패널로 슬라이드되어 사용자가 지속적인 데이터를 검사하는 동안 동일한 +스레드를 유지할 수 있습니다. ## 프로젝트 구조 {#project-structure} ```text my-app/ - actions/ # 에이전트와 UI에서 호출할 수 있는 작업 - app/ # React 라우트, 페이지, 채팅 화면 - server/ # Nitro 서버와 SQL 스키마 - AGENTS.md # 앱 에이전트를 위한 상시 지침 - .agents/ # 필요할 때 에이전트가 불러오는 스킬 - data/app.db # DATABASE_URL이 설정되지 않았을 때의 로컬 SQLite 상태 + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset ``` -## 완성도 높은 Analytics 시작점이 필요하다면 {#analytics-starting-point} +## 전체 분석 시작점을 원하시나요? {#analytics-starting-point} -위 응답 인사이트 예시는 framework 조각을 보여주기 위해 일부러 작게 만들었습니다. 실제 analytics product를 만들고 있다면 [Analytics](/docs/template-analytics)에서 시작하세요. providers를 연결하고, 기존 dashboards와 agent actions를 사용한 뒤, 거기서부터 커스터마이즈할 수 있는 강력한 시작점입니다. +위의 text-analyses 예시는 프레임워크 구성 요소를 볼 수 있도록 의도적으로 작게 만들었습니다. +실제 분석 제품을 구축하는 경우 대신 [Analytics](/docs/template-analytics)에서 시작하세요. +강력한 시작점입니다. 프로바이더를 연결하고, 기존 대시보드와 에이전트 액션을 사용한 다음, +거기서 앱을 커스터마이즈하세요. ## 다음 단계 {#next} -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks, transport. -- **[Native Chat UI](/docs/native-chat-ui)** — action 결과를 테이블, 차트, typed cards로 렌더링. -- **[Chat Template](/docs/template-chat)** — 최소 chat-first app. -- **[Analytics Template](/docs/template-analytics)** — 강력한 analytics app 시작점. providers를 연결하고 커스터마이즈하세요. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state, 선택된 객체. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation, 외부 agent. +- **[Actions](/docs/actions)**: 스키마, 인증, 승인, 훅, 트랜스포트. +- **[Native Chat UI](/docs/native-chat-ui)**: 액션 결과를 테이블, 차트, 타입 카드로 + 렌더링하기. +- **[Chat Template](/docs/template-chat)**: 방금 만든 최소한의 채팅 우선 앱. +- **[Analytics Template](/docs/template-analytics)**: 강력한 분석 앱 시작점; 프로바이더를 + 연결하고 거기서 커스터마이즈하세요. +- **[Context Awareness](/docs/context-awareness)**: `view-screen`, `navigate`, + 라우트 상태, 선택된 객체. +- **[Agent Surfaces](/docs/agent-surfaces)**: 채팅, 인라인 UI, 앱 페이지, 임베디드 + 사이드카, 자동화, 외부 에이전트. +- **[Deployment](/docs/deployment)**: 자신의 도메인에 앱 배포하기. diff --git a/packages/core/docs/content/locales/pt-BR/getting-started.mdx b/packages/core/docs/content/locales/pt-BR/getting-started.mdx index 09dc95562a..1f5ca65fe6 100644 --- a/packages/core/docs/content/locales/pt-BR/getting-started.mdx +++ b/packages/core/docs/content/locales/pt-BR/getting-started.mdx @@ -1,23 +1,39 @@ --- -title: "Primeiros passos" -description: "Crie uma app agentic começando pelo chat, adicione uma action, renderize resultados estruturados inline e evolua para uma página persistente que o agent pode abrir." +title: "Primeiros Passos" +description: "Crie um aplicativo agêntico com foco em chat, adicione uma ação, renderize resultados estruturados inline e evolua para uma página persistente que o agente pode abrir." --- -# Primeiros passos +# Primeiros Passos -Agent-Native é para criar agentic applications: o AI agent e a UI compartilham as mesmas [actions](/docs/actions), dados SQL e application state. O caminho principal começa pelo Chat para que usuários possam falar com o agent imediatamente, e depois adiciona as superfícies de app que o fluxo merece. +O Agent-Native é para aplicações agênticas: apps onde o agente de IA e a UI +compartilham as mesmas [ações](/docs/actions), dados SQL e estado da aplicação. Comece +com o chat para que os usuários possam conversar com o agente imediatamente, e então adicione as superfícies do app que seu fluxo de trabalho exige. -O fluxo útil: +O caminho mais rápido é o **template Chat**: um app mínimo que oferece uma +interface de chat com IA funcional, threads duráveis, autenticação e um diretório `actions/` +pronto para ser estendido. É a base da qual a maioria dos apps Agent-Native cresce. -1. Criar uma Chat app. -2. Adicionar uma action. -3. Renderizar o resultado da action inline no chat. -4. Persistir dados em SQL. -5. Adicionar uma página que o agent pode abrir quando a inspeção visual fizer mais sentido. +O primeiro caminho útil é: -## 1. Crie uma Chat app {#create-your-app} +1. Criar um app de chat. +2. Conectar um motor de IA para que o agente possa responder. +3. Adicionar uma ação. +4. Renderizar o resultado da ação inline no chat. +5. Persistir dados em SQL. +6. Adicionar uma página que o agente pode abrir quando a inspeção visual é melhor do que mais um + parágrafo na transcrição. -Você precisa de [Node.js 22+](https://nodejs.org) e [pnpm](https://pnpm.io). +Quer um app de domínio completo? Clone um template rico como +[Mail](/docs/template-mail), [Calendar](/docs/template-calendar), +[Forms](/docs/template-forms), [Analytics](/docs/template-analytics) ou +[Plan](/docs/template-plan). Quer ainda sem UI no navegador? Veja +[Apps Orientados a Automação](/docs/pure-agent-apps) após este tutorial. + +## 1. Criar um app de chat {#create-your-app} + +Você precisará de [Node.js 22+](https://nodejs.org) e [pnpm](https://pnpm.io). + +Abra um terminal, vá para o diretório onde deseja seu app Agent Native e execute: ```bash npx @agent-native/core@latest create my-app --template chat @@ -26,75 +42,214 @@ pnpm install pnpm dev ``` -O template inclui durable chat threads, auth, live sync, diretório `actions/`, actions padrão `view-screen` e `navigate`, e uma app React pronta para estender. +Isso fornece threads de chat duráveis, autenticação, sincronização em tempo real, um diretório `actions/`, +ações padrão `view-screen` e `navigate`, e um pequeno app React que você pode +estender. + +O servidor de desenvolvimento inicia e abre `http://localhost:8080` no seu navegador. Você pode +notar algumas linhas de log de inicialização como `NitroViteError: Vite environment "nitro" is +unavailable`. Essas são uma condição de corrida normal durante a inicialização inicial e +se resolvem sozinhas. O app está pronto quando **VITE ready** aparecer na +saída do terminal. + +Quando o app iniciar, você pode se encontrar na tela de login em vez de dentro do app. Nesse caso, basta se cadastrar com um login fictício para satisfazer a tela de login. +Para desenvolvimento local, nenhuma verificação de e-mail é realmente necessária. + +### Se você quiser um tipo diferente de app + +Este guia usa o template Chat porque é o caminho mais curto +para uma aplicação agêntica: o agente pode agir desde o primeiro dia, e você tem uma superfície real do app +para crescer. Porém, se você quiser criar um tipo diferente de app, execute `create` sem flags para o seletor de CLI de templates de domínio e formatos avançados de app: + +```bash +npx @agent-native/core@latest create my-app +``` + +## 2. Conectar um motor de IA {#connect-ai} + +O chat do agente não pode responder até que você conecte um motor de IA. Após fazer login, clique no botão **Connect AI**. + +Você tem duas opções: + +**Opção A: Conectar o Builder.** Clique em **Connect Builder.io**, escolha o Builder Space e clique no botão **Authorize**. Você não precisa fornecer nenhuma chave manualmente. + +**Opção B: Adicionar suas próprias chaves.** Insira sua chave de API da Anthropic ou OpenAI. Você pode obter uma +chave da Anthropic em [console.anthropic.com](https://console.anthropic.com). + +Como alternativa, crie um arquivo `.env` no diretório `my-app/` (o mesmo +diretório que contém `package.json`) antes de executar `pnpm dev`: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +Reinicie o servidor de desenvolvimento. Assim que um motor de IA estiver conectado, o painel de Configuração +se oculta e o agente está pronto para conversar. + +> **Tela em branco?** Crie um arquivo `.env` no diretório `my-app/` com +> `ANTHROPIC_API_KEY=sk-ant-...`, e então reinicie `pnpm dev`. O painel de Configuração +> no app só aparece após o app ter carregado, então uma chave ausente que impede +> o app de renderizar precisa ser corrigida via variável de ambiente em vez de +> pela UI. + +## 3. Adicionar uma ação {#add-an-action} + +Uma ação é uma operação tipada que tanto seu agente quanto sua UI podem chamar. É +como o agente faz coisas no seu app. As ações ficam no diretório `actions/` +e podem ser acionadas pelo chat, por componentes React, pela CLI ou em um +agendamento. Você as define uma vez e as chama de qualquer lugar. + +### Experimente a ação inicial + +O template Chat inclui uma ação `hello` em `actions/hello.ts`: + +```ts filename="actions/hello.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +Execute a partir do terminal (dentro do seu diretório `my-app/`): -## 2. Adicione uma action {#add-an-action} +```bash +pnpm action hello --name Alice +``` + +Ou abra seu app em `http://localhost:8080` e peça ao agente no chat: + +> Use the hello action with the name Alice. + +### Adicione sua própria ação -Uma action é uma operação tipada que o agent e a UI chamam juntos. Para este fluxo, crie `actions/analyze-responses.ts`: ela analisa respostas de formulários e retorna um formato validado para um gráfico de chat customizado. +Substitua a ação inicial pela primeira operação real do seu domínio. Este exemplo +conta palavras, frases e parágrafos em qualquer texto que você passar. Ele computa +tudo localmente, então não há nada para configurar e nenhum serviço externo para conectar: - -```ts -// actions/analyze-responses.ts +```ts filename="actions/analyze-text.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "Gráfico de respostas" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -Execute diretamente: +Experimente pelo terminal: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -Ou peça ao agent no navegador: +Ou abra seu app em `http://localhost:8080` e peça ao agente no chat: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### Defina uma vez, chame de qualquer lugar + +Esta ação agora está acessível pelo chat, hooks React, CLI, HTTP, MCP, A2A, +jobs agendados e webhooks. + +DICA: Sempre que quiser que o agente chame uma ação específica sem ambiguidade, formulá-la como "Run the `` action" é o mais confiável. Prompts em linguagem natural funcionam bem assim que o agente tem contexto suficiente sobre o domínio do seu app. Para um app totalmente novo sem dados ou contexto ainda, ser explícito é mais seguro. -> Analise as respostas recentes do formulário de demonstração. +## 4. Renderizar o resultado inline {#render-inline} -## 3. Renderize o resultado no chat {#render-inline} +Quando o agente executa `analyze-text`, ele retorna dados estruturados: um título e um +array de contagens. Por padrão, o agente descreverá esses dados em prosa: "The +text has 9 words, 2 sentences..." e assim por diante. Isso funciona, mas você também pode +renderizar o resultado como um componente de UI real (um gráfico de barras, uma tabela, um cartão) +diretamente dentro da transcrição do chat, exatamente onde o agente respondeu. -Como a action declara `outputSchema` e uma `chatUI.renderer` específica do produto, o transcript não transforma dados estruturados em texto corrido. Registre um componente React para esse id exato no startup da app, por exemplo importando `app/chat-renderers.tsx` uma vez a partir de `app/root.tsx`: +É isso que `chatUI.renderer` na ação faz. É um rótulo que diz "quando +o resultado desta ação aparecer no chat, entregue-o a este componente React em vez de +resumir em texto." O componente recebe a saída validada da ação como +props e renderiza o que você quiser. - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +No próximo passo, você criará `app/chat-renderers.tsx`, mas primeiro, adicione uma linha de import +em `app/root.tsx` para que ele seja executado na inicialização: -type ResponseChartResult = { +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +Adicione-a junto com seus outros imports no topo do arquivo. Essa é a única +alteração em `root.tsx`. O import apenas garante que o arquivo seja executado e registre o +renderizador. Agora crie o arquivo do renderizador: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -103,83 +258,373 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -Para saídas genéricas reutilizáveis, o framework também oferece renderers integrados `data-chart` e `data-table`, além de `data-insights` para cards combinados de resumo/gráfico/tabela. Veja [Native Chat UI](/docs/native-chat-ui). - - -
User

Analise respostas de demo.

Agent

Renderizado com responses.response-chart.

Gráfico de respostas

Seg
Ter
Qua
Qui
" - } - /> - +Assim que o renderizador estiver registrado, a resposta do agente ficará assim. Em vez de +um parágrafo de texto, seu componente React renderiza diretamente dentro da transcrição do chat. + +Use este passo quando o resultado pertence ao lugar onde o agente está falando: + +- resumos de configuração +- relatórios curtos +- aprovações +- tabelas ou gráficos pequenos o suficiente para inspecionar inline +- links para visualizações duráveis do app + +Para saídas genéricas reutilizáveis, o framework também inclui renderizadores embutidos +`data-chart` e `data-table`, além de `data-insights` para cartões combinados de +resumo/gráfico/tabela. Veja [Native Chat UI](/docs/native-chat-ui). Para +controles temporários que o agente cria em tempo de execução, veja +[Generative UI](/docs/generative-ui). + +## 5. Persistir dados em SQL {#persist-data} + +Agora, toda vez que o agente executa `analyze-text` o resultado aparece no chat +e depois desaparece. Não há nada para consultar depois, nada que o agente possa +referenciar mais tarde, e nenhuma forma de construir uma página em torno dos dados. Persistir em SQL +resolve isso: o agente grava resultados em uma tabela, e tanto o agente quanto sua UI +podem lê-los de volta a qualquer momento. + +Apps Agent-Native têm um banco de dados SQL disponível por padrão: SQLite localmente, +e seu provedor configurado (Postgres, Turso/libSQL, Cloudflare D1) em +produção. + +### Conectar o plugin de banco de dados + +O template Chat não inclui um plugin de banco de dados por padrão. Crie +`server/plugins/db.ts` para inicializá-lo. É isso que executa as migrações e +torna o banco de dados disponível para suas ações: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. Persista dados em SQL {#persist-data} +Cada entrada no array é uma migração aditiva. Quando você adicionar novas colunas ou +tabelas depois, acrescente um novo objeto de versão. Nunca edite os existentes. -Quando o resultado precisa ser revisitado, comparado ou atualizado, salve em SQL e mantenha leitura/escrita atrás de actions: +### Definir o schema -Defina a tabela com os helpers portáveis do framework, não com imports de `drizzle-orm/sqlite-core` ou `drizzle-orm/pg-core`: +Crie `server/db/schema.ts`. O diretório `server/db/` pode não existir ainda, +então crie-o se necessário. Este arquivo descreve suas tabelas usando helpers tipados para que +suas ações tenham autocomplete completo do TypeScript: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -Esses helpers escolhem o backend SQL configurado, então o mesmo schema pode rodar com SQLite local e em produção com Postgres, Turso/libSQL, D1 ou outro provider SQL suportado. +Use os helpers de schema do framework (`table`, `text`, `integer`, `now`) em vez de +`sqliteTable`, `pgTable` ou imports específicos de dialeto. Eles selecionam o backend SQL configurado +automaticamente, para que o mesmo schema rode localmente no SQLite e em +produção em qualquer provedor suportado. -- `create-response-insight` escreve um novo insight. -- `list-response-insights` lê dados para a página. -- `update-response-insight` atualiza um registro. -- `analyze-responses` continua retornando o widget nativo do chat. +Após adicionar ambos os arquivos, reinicie o servidor de desenvolvimento para que a migração seja executada: -Não crie uma REST route duplicada só para o navegador; UI e agent devem compartilhar actions. +```bash +pnpm dev +``` -## 5. Adicione uma página que o agent pode abrir {#add-a-page} +Procure essas duas linhas na saída do terminal. Elas confirmam que a tabela foi criada: -Adicione a route `/response-insights` com `useActionQuery("list-response-insights")` e `useActionMutation("create-response-insight")`. A página é uma projeção do SQL state; os dados continuam sendo escritos pelas mesmas actions. +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` - -

Insights de respostas

Insights que o agente criou a partir das respostas do formulário.

Tema de preços

Usuários pedem um plano de equipe.

Queda no onboarding

Três etapas precisam de texto mais claro.

Formulário de demonstração42 respostas analisadas
Próximo passoRevisar dois rascunhos de insights
" - } - /> -
+As linhas `NitroViteError`, o aviso `BETTER_AUTH_SECRET` e o +aviso `SECRETS_ENCRYPTION_KEY` que também aparecem são normais para desenvolvimento local e +podem ser ignorados. -## 6. Deixe o agent navegar {#agent-navigation} +### Adicionar ações para a tabela -O template de Chat inclui as actions `view-screen` e `navigate`. Estenda-as conforme sua app cresce: +Agora crie os arquivos de ação que leem e escrevem na tabela. Eles ficam no seu +diretório `actions/`, no mesmo lugar que `hello.ts` e `analyze-text.ts`. Você +os cria manualmente, um arquivo por operação. O agente e sua UI os chamarão +da mesma forma que chamam qualquer outra ação. -- `view-screen` deve ler o application state e retornar a route atual, o insight selecionado, os filtros ativos e qualquer contexto compacto de página que o agent precise. -- `navigate` deve escrever um caminho same-origin como `/response-insights` quando o agent decidir que o usuário deve inspecionar o resultado visualmente. -- Resultados de action podem incluir links como `href: "/response-insights"` para que o chat ofereça um botão explícito "Abrir insights de respostas". +**`actions/save-text-analysis.ts`** grava uma linha de resultado no banco de dados. +Chame isso após executar `analyze-text` para tornar o resultado durável: -Quando o chat em página inteira abrir uma página da app, use `AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, `useAgentChatHomeHandoffLinks` e `chatViewTransition` de [Agent Surfaces](/docs/agent-surfaces#rich-chat) para mover o chat para a lateral sem perder o thread. +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`** lê todos os resultados salvos. O agente pode +chamar isso para resumir análises passadas, e sua UI pode usá-lo para popular uma página: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** remove uma linha pelo id: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +Assim que esses arquivos forem salvos, o servidor de desenvolvimento os detecta automaticamente. Não é +necessário reiniciar. Tente listar as análises pelo terminal: + +```bash +pnpm action list-text-analyses +``` + +Você deverá ver um array vazio. A tabela existe e a ação funciona; ainda não há +nada salvo: + +``` +[] +``` + +Os dados são salvos em `data/app.db`, um arquivo SQLite no diretório do seu projeto que +é criado automaticamente na primeira execução. Em produção você apontaria +`DATABASE_URL` para um banco de dados hospedado, mas localmente este arquivo é tudo que você +precisa. + +Para salvar algo, primeiro execute `analyze-text` para obter as contagens: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +Você verá uma saída como: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +Então passe esses valores para `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +Agora execute `list-text-analyses` novamente e você verá a linha salva: + +```bash +pnpm action list-text-analyses +``` + +Ou peça ao agente no chat em `http://localhost:8080` para fazer ambos os passos de uma vez: + +> Run analyze-text on "Hello world", then save the result. + +## 6. Adicionar uma página que o agente pode abrir {#add-a-page} + +O chat é ótimo para interação conversacional, mas alguns dados são melhor inspecionados +em uma UI dedicada: uma tabela que você pode varrer, ordenar ou excluir linhas. Este passo +adiciona uma rota React que exibe tudo salvo em `text_analyses`, usando as +mesmas ações `list-text-analyses` e `delete-text-analysis` que você já escreveu. +Não há segunda camada de dados. A página é apenas uma visualização sobre o mesmo estado SQL +que o agente lê e escreve. + +Crie o arquivo de rota em `app/routes/text-analyses.tsx`. Arquivos de rota em +`app/routes/` são automaticamente detectados pelo framework. O nome do arquivo +se torna o caminho da URL, então esta página estará disponível em +`http://localhost:8080/text-analyses`. + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery` chama `list-text-analyses` e mantém o resultado atualizado em tempo real. Se o +agente salvar uma nova linha enquanto a página estiver aberta, ela aparece automaticamente. +`useActionMutation` chama `delete-text-analysis` quando o usuário clica em Delete, +e então invalida a query para que a lista seja atualizada. + +Abra `http://localhost:8080/text-analyses` no seu navegador. Se você salvou uma +análise no passo anterior, ela estará listada. Então peça ao agente no chat: + +> Open the text analyses page. + +Se você receber um 404, tente reiniciar seu servidor de desenvolvimento. + +## 7. Estender a navegação {#extend-navigation} + +A barra lateral é configurada em `app/config/nav.ts`. Abra o arquivo e adicione +uma entrada para a página de análise de texto: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` + +Salve o arquivo. O servidor de desenvolvimento detecta a alteração automaticamente e +a barra lateral é atualizada sem precisar reiniciá-lo. + +### Navegação do agente + +O link da barra lateral permite que os usuários naveguem manualmente. O agente também +pode abrir páginas por conta própria usando duas ações integradas que vêm com o +template Chat: + +- **`view-screen`** lê a rota atual e retorna um resumo compacto do + que o usuário está vendo. +- **`navigate`** grava um caminho de mesma origem no histórico do navegador. + +Conforme você adicionar mais páginas, mantenha `navigate` atualizado para que o agente +saiba quais destinos existem. Documente os caminhos disponíveis em `AGENTS.md` para +que o modelo possa raciocinar sobre eles. + +Quando o app tiver tanto uma rota de chat completo quanto uma página do app, use os helpers de +handoff de chat compartilhado descritos em [Agent Surfaces](/docs/agent-surfaces#rich-chat): +`AgentChatSurface`, `AgentSidebar`, `useAgentChatHomeHandoff`, +`useAgentChatHomeHandoffLinks` e `chatViewTransition`. Isso permite que o chat completo +deslize para o painel lateral conforme a página abre, mantendo a mesma thread enquanto +o usuário inspeciona dados duráveis. ## Estrutura do projeto {#project-structure} ```text my-app/ - actions/ # Operações acionáveis pelo agente e pela interface - app/ # Rotas, páginas e superfícies de chat em React - server/ # Servidor Nitro e esquema SQL + actions/ # Operações chamáveis pelo agente e pela UI + app/ # Rotas React, páginas e superfícies de chat + server/ # Servidor Nitro e schema SQL AGENTS.md # Instruções sempre ativas para o agente do app .agents/ # Skills que o agente carrega quando relevante data/app.db # Estado SQLite local quando DATABASE_URL não está definido @@ -187,13 +632,22 @@ my-app/ ## Quer um ponto de partida completo para analytics? {#analytics-starting-point} -O exemplo de Insights de respostas acima é pequeno de propósito para mostrar as peças do framework. Se você estiver criando um produto real de analytics, comece com [Analytics](/docs/template-analytics). É o ponto de partida robusto: conecte seus providers, use os dashboards e agent actions existentes e personalize a partir daí. - -## Próximos passos {#next} - -- **[Actions](/docs/actions)** — schemas, auth, approvals, hooks e transport. -- **[Native Chat UI](/docs/native-chat-ui)** — tabelas, gráficos e typed cards para resultados de actions. -- **[Chat Template](/docs/template-chat)** — a app mínima chat-first. -- **[Analytics Template](/docs/template-analytics)** — uma base robusta para analytics; conecte providers e personalize a partir daí. -- **[Context Awareness](/docs/context-awareness)** — `view-screen`, `navigate`, route state e objetos selecionados. -- **[Agent Surfaces](/docs/agent-surfaces)** — chat, inline UI, app pages, embedded sidecars, automation e agents externos. +O exemplo de análise de texto acima é intencionalmente pequeno para que você possa ver as +peças do framework. Se você estiver construindo um produto de analytics real, comece pelo +[Analytics](/docs/template-analytics). Ele é o ponto de partida robusto: +conecte seus provedores, use os dashboards e ações do agente existentes, e então +personalize o app a partir daí. + +## O que vem a seguir {#next} + +- **[Actions](/docs/actions)**: schemas, autenticação, aprovações, hooks e transporte. +- **[Native Chat UI](/docs/native-chat-ui)**: renderize resultados de ações como tabelas, + gráficos e cartões tipados. +- **[Chat Template](/docs/template-chat)**: o app mínimo com foco em chat que você acabou + de criar. +- **[Analytics Template](/docs/template-analytics)**: um ponto de partida robusto para apps de analytics; conecte provedores e personalize a partir daí. +- **[Context Awareness](/docs/context-awareness)**: `view-screen`, `navigate`, + estado de rota e objetos selecionados. +- **[Agent Surfaces](/docs/agent-surfaces)**: chat, UI inline, páginas do app, + sidecars embutidos, automação e agentes externos. +- **[Deployment](/docs/deployment)**: coloque seu app no seu próprio domínio. diff --git a/packages/core/docs/content/locales/zh-CN/getting-started.mdx b/packages/core/docs/content/locales/zh-CN/getting-started.mdx index 582e224628..a80204372d 100644 --- a/packages/core/docs/content/locales/zh-CN/getting-started.mdx +++ b/packages/core/docs/content/locales/zh-CN/getting-started.mdx @@ -1,25 +1,38 @@ --- title: "开始使用" -description: "创建一个聊天优先的 agentic app,添加 action,内联渲染结构化结果,然后扩展为 agent 可以打开的持久页面。" +description: "创建一个以聊天为核心的智能体应用,添加一个动作,在聊天中内联渲染结构化结果,然后扩展为智能体可以打开的持久化页面。" --- -# 开始使用 +# 入门指南 -Agent-Native 用来构建 agentic applications:AI agent 和 UI 共享同一组 -[actions](/docs/actions)、SQL 数据和 application state。默认路径不是先做 -headless agent,而是先有一个用户可以马上对话的 Chat app,然后逐步增加应用界面。 +Agent-Native 专为智能体应用而设计:在这类应用中,AI 智能体与 UI 共享相同的 +[动作](/docs/actions)、SQL 数据和应用状态。从聊天开始,让用户能够立即与智能体交互, +然后随着工作流的发展添加应用界面。 -推荐流程: +最快的入门方式是使用 **Chat 模板**:这是一个最小化的应用,提供可用的 AI 聊天界面、 +持久化线程、身份验证以及一个随时可扩展的 `actions/` 目录。它是大多数 Agent-Native +应用的基础。 -1. 创建 Chat app。 -2. 添加一个 action。 -3. 在聊天中以内联原生 UI 渲染 action 结果。 -4. 把数据持久化到 SQL。 -5. 添加一个页面,让 agent 在需要可视化检查时打开它。 +第一条有效路径是: -## 1. 创建 Chat app {#create-your-app} +1. 创建一个聊天应用。 +2. 连接 AI 引擎,让智能体能够响应。 +3. 添加一个动作。 +4. 在聊天中内联渲染动作结果。 +5. 将数据持久化到 SQL。 +6. 添加一个智能体可以打开的页面——当视觉检查比在对话记录中再写一段文字更有效时。 -需要 [Node.js 22+](https://nodejs.org) 和 [pnpm](https://pnpm.io)。 +想直接使用完整的领域应用?克隆一个功能丰富的模板,例如 +[Mail](/docs/template-mail)、[Calendar](/docs/template-calendar)、 +[Forms](/docs/template-forms)、[Analytics](/docs/template-analytics) 或 +[Plan](/docs/template-plan)。还不需要浏览器 UI?请在本教程之后查看 +[自动化优先应用](/docs/pure-agent-apps)。 + +## 1. 创建聊天应用 {#create-your-app} + +你需要 [Node.js 22+](https://nodejs.org) 和 [pnpm](https://pnpm.io)。 + +打开终端,进入你想要创建 Agent Native 应用的目录,然后运行: ```bash npx @agent-native/core@latest create my-app --template chat @@ -28,83 +41,201 @@ pnpm install pnpm dev ``` -这个模板已经包含 durable chat threads、auth、live sync、`actions/` 目录、 -标准的 `view-screen` 和 `navigate` actions,以及可以继续扩展的 React app。 +这将为你提供持久化聊天线程、身份验证、实时同步、一个 `actions/` 目录、 +标准的 `view-screen` 和 `navigate` 动作,以及一个可扩展的小型 React 应用。 + +开发服务器启动后会在浏览器中打开 `http://localhost:8080`。你可能会看到一些启动日志, +例如 `NitroViteError: Vite environment "nitro" is unavailable`。这是初始启动时的正常竞态条件, +会自动解决。当终端输出中显示 **VITE ready** 时,应用已准备就绪。 + +应用启动时,你可能会看到登录界面而不是应用本身。此时,只需使用一个虚拟账号注册以通过登录界面。 +在本地开发环境中,实际上不需要邮箱验证。 -如果想使用完整模板选择器,也可以运行: +### 如果你想要不同类型的应用 + +本指南使用 Chat 模板,因为它是创建智能体应用的最短路径:智能体从第一天起就可以执行操作, +你也拥有一个可以持续扩展的真实应用界面。但是,如果你想创建其他类型的应用, +可以不带参数运行 `create` 来使用 CLI 选择器,浏览领域模板和高级应用形态: ```bash npx @agent-native/core@latest create my-app ``` -## 2. 添加 action {#add-an-action} +## 2. 连接 AI 引擎 {#connect-ai} + +在连接 AI 引擎之前,智能体聊天无法响应。登录后,点击 **Connect AI** 按钮。 + +你有两个选项: + +**选项 A:连接 Builder。** 点击 **Connect Builder.io**,选择 Builder Space,然后点击 **Authorize** 按钮。无需手动提供任何密钥。 + +**选项 B:添加自己的密钥。** 输入你的 Anthropic 或 OpenAI API 密钥。你可以在 +[console.anthropic.com](https://console.anthropic.com) 获取 Anthropic 密钥。 + +或者,在运行 `pnpm dev` 之前,在 `my-app/` 目录(即包含 `package.json` 的目录)中创建一个 `.env` 文件: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +重启开发服务器。连接 AI 引擎后,设置面板会自动隐藏,智能体即可开始聊天。 + +> **空白屏幕?** 在你的 `my-app/` 目录中创建一个包含 +> `ANTHROPIC_API_KEY=sk-ant-...` 的 `.env` 文件,然后重启 `pnpm dev`。应用内的设置 +> 面板只有在应用加载完成后才会显示,因此如果缺少密钥导致应用无法渲染,需要通过环境变量而不是 +> UI 来修复。 + +## 3. 添加动作 {#add-an-action} -Action 是 agent 和 UI 共同调用的一次 typed operation。可以把入门示例换成 -`actions/analyze-responses.ts`:它分析表单反馈,并返回自定义聊天图表的已验证数据形状。 +动作是一个有类型的操作,智能体和 UI 都可以调用。它是智能体在应用中执行操作的方式。 +动作存放在 `actions/` 目录中,可以从聊天、React 组件、CLI 或按计划触发。 +你只需定义一次,就可以从任何地方调用。 - -```ts -// actions/analyze-responses.ts +### 试用入门动作 + +Chat 模板在 `actions/hello.ts` 中包含了一个 `hello` 动作: + +```ts filename="actions/hello.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +从终端运行(在你的 `my-app/` 目录内): + +```bash +pnpm action hello --name Alice +``` + +或者在浏览器中打开 `http://localhost:8080`,在聊天中向智能体提问: + +> Use the hello action with the name Alice. + +### 添加自己的动作 + +用你的领域中第一个真实操作替换入门动作。此示例统计传入文本的单词数、句子数和段落数。 +它在本地进行所有计算,无需任何配置,也不需要连接外部服务: + +```ts filename="actions/analyze-text.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "回复图表" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -直接运行: +从终端试运行: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -也可以在浏览器里问 agent: +或者在浏览器中打开 `http://localhost:8080`,在聊天中向智能体提问: + +> Run the analyze-text action on "Hello world. How are you today?" + +#### 定义一次,随处调用 + +此动作现在可以从聊天、React hooks、CLI、HTTP、MCP、A2A、定时任务和 webhook 中调用。 -> 分析演示表单的最新回复。 +提示:如果你希望智能体无歧义地调用某个特定动作,将其表述为"Run the `` action"是最可靠的方式。一旦智能体对应用的领域有足够的了解,自然语言提示也会很好用。对于没有任何数据或上下文的全新应用,明确指令更为安全。 -## 3. 内联渲染结果 {#render-inline} +## 4. 内联渲染结果 {#render-inline} -因为 action 声明了 `outputSchema` 和产品专属的 `chatUI.renderer`,聊天记录不需要把结构化数据压成一段文字。然后在 app 启动时为这个完全相同的 renderer id 注册 React 组件,例如从 `app/root.tsx` 导入一次 `app/chat-renderers.tsx`: +当智能体运行 `analyze-text` 时,它返回结构化数据:一个标题和一组计数。默认情况下, +智能体会用文字描述这些数据:"The text has 9 words, 2 sentences..."等等。这没问题, +但你也可以将结果渲染为真正的 UI 组件(柱状图、表格、卡片),直接嵌入到聊天记录中, +就在智能体响应的位置。 + +这就是动作中 `chatUI.renderer` 的作用。它是一个标签,意思是"当此动作的结果出现在聊天中时, +将其交给这个 React 组件渲染,而不是用文字总结。"组件接收已验证的动作输出作为 props, +渲染你想要的任何内容。 + +在下一步中,你将创建 `app/chat-renderers.tsx`,但首先,在 `app/root.tsx` 中添加一行导入, +以便它在启动时运行: + +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +将其添加到文件顶部的其他导入旁边。这是对 `root.tsx` 的唯一更改。该导入只是确保文件运行并注册渲染器。现在创建渲染器文件: -type ResponseChartResult = { +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -113,98 +244,364 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -对于可复用的通用输出,框架也提供内置 `data-chart` 和 `data-table` renderers,以及用于组合摘要/图表/表格卡片的 `data-insights`。更多模式见 [Native Chat UI](/docs/native-chat-ui)。 - - -
User

分析演示回复。

Agent

由 responses.response-chart 渲染。

回复图表

周一
周二
周三
周四
" - } - /> - +渲染器注册后,智能体的响应如下所示。你的 React 组件会直接在聊天记录中渲染, +而不是一段文字。 + +在以下情况下使用此步骤,当结果应显示在智能体说话的位置时: + +- 设置摘要 +- 简短报告 +- 审批 +- 小到可以内联检查的表格或图表 +- 指向持久化应用视图的链接 + +对于可复用的通用输出,框架还内置了 `data-chart` 和 `data-table` 渲染器, +以及用于组合摘要/图表/表格卡片的 `data-insights`。请参阅 [Native Chat UI](/docs/native-chat-ui)。 +对于智能体在运行时动态创建的临时控件,请参阅 [Generative UI](/docs/generative-ui)。 + +## 5. 将数据持久化到 SQL {#persist-data} + +目前,每次智能体运行 `analyze-text` 时,结果会出现在聊天中然后消失。没有可以回顾的内容, +智能体之后也无法引用,也无法围绕数据构建页面。持久化到 SQL 解决了这个问题: +智能体将结果写入表,智能体和 UI 随时都可以读取。 + +Agent-Native 应用默认提供 SQL 数据库:本地使用 SQLite,生产环境使用你配置的提供商 +(Postgres、Turso/libSQL、Cloudflare D1)。 + +### 连接数据库插件 + +Chat 模板默认不包含数据库插件。创建 `server/plugins/db.ts` 来初始化它。 +这是运行迁移并使数据库可供动作使用的配置: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -## 4. 持久化数据 {#persist-data} +数组中的每个条目都是一个累加迁移。当你之后添加新列或新表时,追加一个新的版本对象。永远不要编辑现有的条目。 -当结果需要回看、比较或更新时,把数据写入 SQL。为 回复洞察 添加 schema,并把读写都放在 actions 后面: +### 定义 schema -用 framework 的可移植 helpers 定义表,不要从 `drizzle-orm/sqlite-core` 或 `drizzle-orm/pg-core` 直接导入: +创建 `server/db/schema.ts`。`server/db/` 目录可能还不存在,如有需要请先创建。 +此文件使用有类型的辅助函数描述你的表,以便动作获得完整的 TypeScript 自动补全: -```ts -// server/db/schema.ts +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -这些 helpers 会选择当前配置的 SQL backend,所以同一份 schema 可以在本地 SQLite 上运行,也可以在生产环境的 Postgres、Turso/libSQL、D1 或其他受支持 SQL provider 上运行。 +使用框架的 schema 辅助函数(`table`、`text`、`integer`、`now`),而不是 +`sqliteTable`、`pgTable` 或特定方言的导入。它们会自动选择已配置的 SQL 后端, +因此相同的 schema 在本地 SQLite 和生产环境中任何受支持的提供商上都能运行。 -- `create-response-insight` 写入新的 insight。 -- `list-response-insights` 为页面读取数据。 -- `update-response-insight` 在 agent 学到更多后更新记录。 -- `analyze-responses` 继续返回聊天里的原生 widget。 +添加这两个文件后,重启开发服务器以运行迁移: + +```bash +pnpm dev +``` -不要为浏览器单独写一条重复的 REST route;UI 和 agent 应该共享 actions。 +在终端输出中查找以下两行。它们确认表已创建: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` + +同时出现的 `NitroViteError` 行、`BETTER_AUTH_SECRET` 警告和 +`SECRETS_ENCRYPTION_KEY` 警告在本地开发中是正常的,可以忽略。 + +### 为表添加动作 + +现在创建读写该表的动作文件。这些文件放在你的 `actions/` 目录中,与 `hello.ts` 和 +`analyze-text.ts` 在同一位置。你自己创建它们,每个操作一个文件。智能体和 UI +调用它们的方式与调用其他任何动作相同。 + +**`actions/save-text-analysis.ts`** 向数据库写入一行结果。 +运行 `analyze-text` 后调用此动作,使结果持久化: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` -## 5. 添加 agent 可以打开的页面 {#add-a-page} +**`actions/list-text-analyses.ts`** 读取所有已保存的结果。智能体可以调用此动作来 +汇总过往分析,你的 UI 也可以用它来填充页面: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** 通过 id 删除一行: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +保存这些文件后,开发服务器会自动检测到它们。无需重启。从终端试运行列出分析: + +```bash +pnpm action list-text-analyses +``` + +你应该看到一个空数组。表已存在,动作也正常工作;只是还没有保存任何内容: + +``` +[] +``` + +数据保存在 `data/app.db`,这是项目目录中的一个 SQLite 文件,首次运行时会自动创建。 +在生产环境中,你会将 `DATABASE_URL` 指向托管数据库,但在本地这个文件就够了。 + +要保存内容,首先运行 `analyze-text` 获取统计数据: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +你会看到如下输出: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +然后将这些值传给 `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +再次运行 `list-text-analyses`,你会看到已保存的行: + +```bash +pnpm action list-text-analyses +``` + +或者在 `http://localhost:8080` 的聊天中让智能体一次完成两个步骤: + +> Run analyze-text on "Hello world", then save the result. + +## 6. 添加智能体可以打开的页面 {#add-a-page} + +聊天非常适合对话式交互,但有些数据在专用 UI 中检查更为方便: +一个可以扫描、排序或删除行的表格。此步骤添加一个 React 路由, +使用你已经编写的 `list-text-analyses` 和 `delete-text-analysis` 动作, +显示 `text_analyses` 中保存的所有内容。没有第二个数据层。该页面只是对智能体读写的 +同一 SQL 状态的一个视图。 + +在 `app/routes/text-analyses.tsx` 创建路由文件。`app/routes/` 中的路由文件 +会被框架自动识别。文件名会成为 URL 路径,因此该页面将在 +`http://localhost:8080/text-analyses` 可访问。 + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery` 调用 `list-text-analyses` 并保持结果实时更新。如果智能体在页面打开时 +保存了新行,它会自动显示。`useActionMutation` 在用户点击 Delete 时调用 +`delete-text-analysis`,然后使查询失效以刷新列表。 + +在浏览器中打开 `http://localhost:8080/text-analyses`。如果你在上一步保存了分析, +你会看到它列出来。然后在聊天中向智能体提问: + +> Open the text analyses page. + +如果出现 404,请尝试重启开发服务器。 + +## 7. 扩展导航 {#extend-navigation} + +侧边栏在 `app/config/nav.ts` 中配置。打开该文件并为文本分析页面添加一个条目: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` -添加 `/response-insights` route,用 `useActionQuery("list-response-insights")` -读取数据,用 `useActionMutation("create-response-insight")` 触发分析。这个页面只是 SQL state 的投影;数据仍由同一组 actions 写入。 +保存文件。开发服务器会自动检测到更改,侧边栏无需重启即可更新。 - -

回复洞察

Agent 从表单回复创建的洞察。

定价主题

用户想要团队计划。

入门流程流失

三个步骤需要更清晰的文案。

演示表单已分析 42 条回复
下一步查看两个洞察草稿
" - } - /> -
+### 智能体导航 -## 6. 让 agent 导航 {#agent-navigation} +侧边栏链接允许用户手动导航。智能体也可以使用 Chat 模板内置的两个动作自行打开页面: -Chat 模板包含 `view-screen` 和 `navigate` action。随着应用的成长扩展它们: +- **`view-screen`** 读取当前路由,返回用户正在查看内容的简洁摘要。 +- **`navigate`** 将同源路径写入浏览器历史记录。 -- `view-screen` 应读取 application state,返回当前 route、选中的 insight、生效的筛选条件,以及 agent 需要的任何精简页面上下文。 -- 当 agent 判断用户需要直观查看结果时,`navigate` 应写入 `/response-insights` 这样的同源 path。 -- action 结果可以包含 `href: “/response-insights”` 这样的链接,让聊天提供明确的”打开回复洞察”按钮。 +随着你添加更多页面,请保持 `navigate` 的更新,让智能体了解有哪些可用目标。在 `AGENTS.md` 中记录可用路径,以便模型对其进行推理。 -当完整聊天页打开 app 页面时,使用 [Agent Surfaces](/docs/agent-surfaces#rich-chat) 里的 `AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、`useAgentChatHomeHandoffLinks` 和 `chatViewTransition`,让聊天滑到侧边栏并保留同一个 thread。 +当应用同时拥有完整页面聊天路由和应用页面时,请使用 +[Agent Surfaces](/docs/agent-surfaces#rich-chat) 中描述的共享聊天切换辅助函数: +`AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、 +`useAgentChatHomeHandoffLinks` 和 `chatViewTransition`。这样可以在页面打开时 +将完整聊天滑入侧边栏,在用户检查持久化数据时保持同一线程。 ## 项目结构 {#project-structure} ```text my-app/ - actions/ # Agent 和界面都可调用的操作 - app/ # React 路由、页面和聊天界面 - server/ # Nitro 服务器和 SQL 架构 - AGENTS.md # 应用 Agent 的常驻指令 - .agents/ # Agent 在相关场景加载的技能 - data/app.db # 未设置 DATABASE_URL 时的本地 SQLite 状态 + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset ``` -## 想要完整的 Analytics 起点? {#analytics-starting-point} +## 想要一个完整的分析起点? {#analytics-starting-point} -上面的 回复洞察 示例故意保持很小,方便你看清框架部件。如果你要构建真正的 analytics 产品,请从 [Analytics](/docs/template-analytics) 开始。它是更强健的起点:连接 providers,使用现有 dashboards 和 agent actions,然后继续自定义。 +上面的 text-analyses 示例有意保持简单,让你能够看清框架的各个部分。如果你正在构建 +真正的分析产品,请从 [Analytics](/docs/template-analytics) 开始。它是更强大的起点: +连接你的数据提供商,使用现有的仪表盘和智能体动作,然后在此基础上自定义应用。 ## 下一步 {#next} -- **[Actions](/docs/actions)** — schemas、auth、approvals、hooks 和 transport。 -- **[Native Chat UI](/docs/native-chat-ui)** — 把 action 结果渲染为表格、图表和 typed cards。 -- **[Chat Template](/docs/template-chat)** — 最小聊天优先 app。 -- **[Analytics Template](/docs/template-analytics)** — 强健的 analytics app 起点;连接 providers 后继续自定义。 -- **[Context Awareness](/docs/context-awareness)** — `view-screen`、`navigate`、route state 和选中对象。 -- **[Agent Surfaces](/docs/agent-surfaces)** — chat、inline UI、app pages、embedded sidecars、automation 和外部 agent。 +- **[动作](/docs/actions)**:schema、身份验证、审批、hooks 和传输方式。 +- **[Native Chat UI](/docs/native-chat-ui)**:将动作结果渲染为表格、图表和类型化卡片。 +- **[Chat 模板](/docs/template-chat)**:你刚刚创建的最小化聊天优先应用。 +- **[Analytics 模板](/docs/template-analytics)**:强大的分析应用起点;连接提供商后自定义。 +- **[上下文感知](/docs/context-awareness)**:`view-screen`、`navigate`、路由状态和选中对象。 +- **[Agent Surfaces](/docs/agent-surfaces)**:聊天、内联 UI、应用页面、嵌入式侧边栏、自动化和外部智能体。 +- **[部署](/docs/deployment)**:将应用发布到你自己的域名。 diff --git a/packages/core/docs/content/locales/zh-TW/getting-started.mdx b/packages/core/docs/content/locales/zh-TW/getting-started.mdx index 5a705e100a..1584921bec 100644 --- a/packages/core/docs/content/locales/zh-TW/getting-started.mdx +++ b/packages/core/docs/content/locales/zh-TW/getting-started.mdx @@ -1,25 +1,35 @@ --- -title: "入門" -description: "建立聊天優先的 agentic app,新增 action,內嵌渲染結構化結果,然後擴展成 agent 可以開啟的持久頁面。" +title: "快速入門" +description: "建立一個以對話為核心的智能代理應用程式,新增動作、在內文中渲染結構化結果,然後擴展為代理可以開啟的持久頁面。" --- -# 入門 +# 快速入門 -Agent-Native 用來建置 agentic applications:AI agent 和 UI 共享同一組 -[actions](/docs/actions)、SQL 資料與 application state。預設路徑不是先做 -headless agent,而是先有一個使用者能立即對話的 Chat app,然後逐步增加應用介面。 +Agent-Native 專為智能代理應用程式設計:這類應用程式中,AI 代理與 UI +共享相同的 [actions](/docs/actions)、SQL 資料和應用程式狀態。從對話介面開始,讓使用者能立即與代理互動,然後隨著工作流程的發展,逐步新增應用程式介面。 -建議流程: +最快的入門方式是使用 **Chat 模板**:這是一個最精簡的應用程式,提供可運作的 AI 對話介面、持久對話串、驗證機制,以及一個可供擴展的 `actions/` 目錄。它是大多數 Agent-Native 應用程式的基礎。 -1. 建立 Chat app。 -2. 新增一個 action。 -3. 在聊天中用原生 inline UI 渲染 action 結果。 -4. 將資料持久化到 SQL。 -5. 新增一個頁面,讓 agent 在需要視覺化檢查時開啟。 +第一條實用的路徑是: -## 1. 建立 Chat app {#create-your-app} +1. 建立一個對話應用程式。 +2. 連接 AI 引擎,讓代理可以回應。 +3. 新增一個動作。 +4. 在對話中以內嵌方式渲染動作結果。 +5. 將資料持久化儲存到 SQL。 +6. 新增一個代理可以開啟的頁面,當視覺化檢視比在對話記錄中再多一段文字更合適時使用。 -需要 [Node.js 22+](https://nodejs.org) 和 [pnpm](https://pnpm.io)。 +想要一個完整的領域應用程式?請複製豐富的模板,例如 +[Mail](/docs/template-mail)、[Calendar](/docs/template-calendar)、 +[Forms](/docs/template-forms)、[Analytics](/docs/template-analytics) 或 +[Plan](/docs/template-plan)。還不需要瀏覽器 UI?完成本教學後,請參閱 +[Automation-First Apps](/docs/pure-agent-apps)。 + +## 1. 建立對話應用程式 {#create-your-app} + +您需要 [Node.js 22+](https://nodejs.org) 和 [pnpm](https://pnpm.io)。 + +開啟終端機,切換到您想要建立 Agent Native 應用程式的目錄,並執行: ```bash npx @agent-native/core@latest create my-app --template chat @@ -28,81 +38,185 @@ pnpm install pnpm dev ``` -這個模板已包含 durable chat threads、auth、live sync、`actions/` 目錄、標準的 `view-screen` 與 `navigate` actions,以及可繼續擴充的 React app。 +這將提供您持久對話串、驗證機制、即時同步、一個 `actions/` 目錄、 +標準的 `view-screen` 和 `navigate` 動作,以及一個可供擴展的小型 React 應用程式。 + +開發伺服器啟動後,會在您的瀏覽器中開啟 `http://localhost:8080`。您可能會看到幾行啟動日誌,例如 `NitroViteError: Vite environment "nitro" is unavailable`。這是初始啟動時的正常競態條件,會自動解決。當終端機輸出中顯示 **VITE ready** 時,應用程式即已就緒。 + +應用程式啟動後,您可能會看到登入畫面而非應用程式主頁。此時,只需使用任意帳號資料完成登入即可。 +在本機開發環境中,實際上不需要電子郵件驗證。 -若想使用完整模板選擇器,也可以執行: +### 如果您想要不同類型的應用程式 + +本指南使用 Chat 模板,因為它是建立智能代理應用程式的最短路徑:代理從第一天起就能採取行動,您也擁有一個真實的應用程式介面可以擴展。但是,如果您想建立不同類型的應用程式,請執行不帶旗標的 `create` 指令,以使用 CLI 選擇器來選取領域模板和進階應用程式形態: ```bash npx @agent-native/core@latest create my-app ``` -## 2. 新增 action {#add-an-action} +## 2. 連接 AI 引擎 {#connect-ai} + +在連接 AI 引擎之前,代理對話無法回應。登入後,點擊 **Connect AI** 按鈕。 + +您有兩個選項: + +**選項 A:連接 Builder。** 點擊 **Connect Builder.io**,選擇 Builder Space,然後點擊 **Authorize** 按鈕。您不需要手動提供任何金鑰。 + +**選項 B:新增您自己的金鑰。** 輸入您的 Anthropic 或 OpenAI API 金鑰。您可以在 [console.anthropic.com](https://console.anthropic.com) 取得 Anthropic 金鑰。 + +或者,在執行 `pnpm dev` 之前,在 `my-app/` 目錄(即包含 `package.json` 的目錄)中建立一個 `.env` 檔案: + +```bash +echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env +``` + +重新啟動開發伺服器。一旦連接了 AI 引擎,設定面板會自動隱藏,代理即可開始對話。 + +> **畫面空白?** 在您的 `my-app/` 目錄中建立一個包含 +> `ANTHROPIC_API_KEY=sk-ant-...` 的 `.env` 檔案,然後重新啟動 `pnpm dev`。應用程式內的設定面板只有在應用程式載入後才會顯示,因此若缺少金鑰導致應用程式無法渲染,需要透過環境變數而非 UI 來修復。 + +## 3. 新增動作 {#add-an-action} + +動作是一種具有型別的操作,您的代理和 UI 都可以呼叫。這是代理在應用程式中執行操作的方式。動作存放在 `actions/` 目錄中,可以從對話、React 元件、CLI 或排程中觸發。您只需定義一次,即可從任何地方呼叫。 + +### 試用入門動作 + +Chat 模板在 `actions/hello.ts` 中包含一個 `hello` 動作: + +```ts filename="actions/hello.ts" +import { defineAction } from "@agent-native/core/action"; +import { z } from "zod"; + +export default defineAction({ + description: "Return a friendly greeting.", + schema: z.object({ + name: z.string().default("world").describe("Name to greet"), + }), + http: { method: "GET" }, + run: async ({ name }) => { + return { message: `Hello, ${name}!` }; + }, +}); +``` + +從終端機執行(在您的 `my-app/` 目錄中): + +```bash +pnpm action hello --name Alice +``` + +或在 `http://localhost:8080` 開啟您的應用程式,並在對話中詢問代理: + +> Use the hello action with the name Alice. + +### 新增您自己的動作 -Action 是 agent 與 UI 共同呼叫的一個 typed operation。可將入門範例換成 `actions/analyze-responses.ts`:它分析表單回覆並返回自訂聊天圖表的已驗證資料形狀。 +將入門動作替換為您領域中的第一個真實操作。以下範例會計算您傳入的任何文字的字數、句數和段落數。它在本地端完成所有計算,因此無需進行任何設定,也不需要連接外部服務: - -```ts -// actions/analyze-responses.ts +```ts filename="actions/analyze-text.ts" import { defineAction } from "@agent-native/core/action"; import { z } from "zod"; -const responseChartResultSchema = z.object({ +const textStatsSchema = z.object({ title: z.string(), - points: z.array(z.object({ day: z.string(), responses: z.number() })), + points: z.array(z.object({ label: z.string(), value: z.number() })), }); export default defineAction({ - description: "Analyze recent form responses and render a custom chart.", - schema: z.object({ - formId: z.string().default("demo") + description: "Count words, sentences, and paragraphs in a block of text.", + schema: z.object({ + text: z + .string() + .default( + "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs.", + ), }), - outputSchema: responseChartResultSchema, - chatUI: { - renderer: "responses.response-chart", - title: "回覆圖表" + outputSchema: textStatsSchema, + chatUI: { + renderer: "text.stats-chart", + title: "Text stats", }, readOnly: true, - run: async ({ formId }) => ({ - title: `Responses for ${formId}`, - points: [{ day: "Mon", responses: 12 }, { day: "Tue", responses: 18 }, { day: "Wed", responses: 24 }, { day: "Thu", responses: 21 }], + run: async ({ text }) => ({ + title: "Text statistics", + points: [ + { label: "Characters", value: text.length }, + { label: "Words", value: text.split(/\s+/).filter(Boolean).length }, + { + label: "Sentences", + value: text.split(/[.!?]+/).filter(Boolean).length, + }, + { + label: "Paragraphs", + value: text.split(/\n\n+/).filter(Boolean).length, + }, + ], }), }); ``` -直接執行: +從終端機試用: ```bash -pnpm action analyze-responses --formId demo +pnpm action analyze-text --text "Hello world. How are you today?" ``` -或在瀏覽器中詢問 agent: +或在 `http://localhost:8080` 開啟您的應用程式,並在對話中詢問代理: -> 分析示範表單的最新回覆。 +> Run the analyze-text action on "Hello world. How are you today?" -## 3. 內嵌渲染結果 {#render-inline} +#### 定義一次,隨處呼叫 -因為 action 宣告了 `outputSchema` 與產品專屬的 `chatUI.renderer`,聊天紀錄不必把結構化資料壓成文字。然後在 app 啟動時為這個完全相同的 renderer id 註冊 React 元件,例如從 `app/root.tsx` 匯入一次 `app/chat-renderers.tsx`: +此動作現在可從對話、React hooks、CLI、HTTP、MCP、A2A、排程任務和 webhook 中存取。 - -```tsx -import { registerActionChatRenderer, type ToolRendererProps,} from "@agent-native/core/client/chat"; +提示:每當您希望代理毫無歧義地呼叫特定動作時,將其表達為「Run the `` action」是最可靠的方式。一旦代理對您應用程式的領域有足夠的上下文,自然語言提示也能很好地運作。對於一個全新的、尚無資料或上下文的應用程式,明確指定更為安全。 -type ResponseChartResult = { +## 4. 以內嵌方式渲染結果 {#render-inline} + +當代理執行 `analyze-text` 時,它會返回結構化資料:一個標題和一個計數陣列。預設情況下,代理會以文字描述該資料:「文字有 9 個單字、2 個句子……」等等。這雖然可行,但您也可以將結果作為真實的 UI 元件(長條圖、表格、卡片)直接渲染在對話記錄中,就在代理回應的位置。 + +這就是動作中 `chatUI.renderer` 的作用。它是一個標籤,告知系統「當此動作的結果出現在對話中時,將其交給這個 React 元件,而不是以文字摘要呈現。」該元件接收經過驗證的動作輸出作為 props,並渲染您想要的任何內容。 + +在下一步中,您將建立 `app/chat-renderers.tsx`,但首先,在 `app/root.tsx` 中新增一行 import,使其在啟動時執行: + +```ts filename="app/root.tsx" +import "./chat-renderers"; +``` + +將其與檔案頂部的其他 import 一起新增。這是對 `root.tsx` 的唯一修改。這個 import 只是確保檔案執行並註冊渲染器。現在建立渲染器檔案: + +```tsx filename="app/chat-renderers.tsx" +import { + registerActionChatRenderer, + type ToolRendererProps, +} from "@agent-native/core/client/chat"; + +type TextStatsResult = { title: string; - points: Array<{ day: string; responses: number }>; + points: Array<{ label: string; value: number }>; }; -function ResponseChart({ context }: ToolRendererProps) { - const result = context.resultJson as ResponseChartResult; - const max = Math.max(...result.points.map((point) => point.responses), 1); +const MAX_BAR_PX = 80; + +function TextStatsChart({ context }: ToolRendererProps) { + const result = context.resultJson as TextStatsResult; + const max = Math.max(...result.points.map((point) => point.value), 1); return (

{result.title}

-
+
{result.points.map((point) => ( -
-
- {point.day} +
+
+ {point.label}
))}
@@ -111,97 +225,344 @@ function ResponseChart({ context }: ToolRendererProps) { } registerActionChatRenderer({ - id: "responses.response-chart", - renderer: "responses.response-chart", - Component: ResponseChart, + id: "text.stats-chart", + renderer: "text.stats-chart", + Component: TextStatsChart, }); ``` -對於可重用的通用輸出,framework 也提供內建 `data-chart` 與 `data-table` renderers,以及用於組合摘要/圖表/表格卡片的 `data-insights`。更多模式請見 [Native Chat UI](/docs/native-chat-ui)。 +渲染器註冊後,代理的回應看起來像這樣。您的 React 元件不再以文字段落呈現,而是直接渲染在對話記錄中。 - -
User

分析示範回覆。

Agent

由 responses.response-chart 渲染。

回覆圖表

週一
週二
週三
週四
" - } - /> - +當結果應該顯示在代理說話的位置時,請使用此步驟: -## 4. 持久化資料 {#persist-data} +- 設定摘要 +- 簡短報告 +- 審核確認 +- 足夠小可以內嵌檢視的表格或圖表 +- 指向持久應用程式視圖的連結 + +對於可重複使用的通用輸出,框架也提供內建的 `data-chart` 和 `data-table` 渲染器,以及用於組合摘要/圖表/表格卡片的 `data-insights`。請參閱 [Native Chat UI](/docs/native-chat-ui)。對於代理在執行時動態建立的臨時控制項,請參閱 [Generative UI](/docs/generative-ui)。 + +## 5. 將資料持久化儲存到 SQL {#persist-data} + +目前,每次代理執行 `analyze-text`,結果都會出現在對話中然後消失。沒有任何可供回顧的內容,代理也無法稍後參考,更無法圍繞資料建立頁面。持久化儲存到 SQL 可以解決這個問題:代理將結果寫入資料表,代理和您的 UI 都可以隨時讀取。 + +Agent-Native 應用程式預設提供 SQL 資料庫:本機使用 SQLite, +生產環境使用您配置的提供者(Postgres、Turso/libSQL、Cloudflare D1)。 + +### 接入資料庫外掛程式 + +Chat 模板預設不包含資料庫外掛程式。建立 +`server/plugins/db.ts` 來初始化它。這是執行遷移並使資料庫可供動作使用的設定: + +```ts filename="server/plugins/db.ts" +import { runMigrations } from "@agent-native/core/db"; + +export default runMigrations( + [ + { + version: 1, + sql: `CREATE TABLE IF NOT EXISTS text_analyses ( + id TEXT PRIMARY KEY, + input TEXT NOT NULL, + char_count INTEGER NOT NULL, + word_count INTEGER NOT NULL, + sentence_count INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP + )`, + }, + ], + { table: "text_analyses_migrations" }, +); +``` -當結果需要回看、比較或更新時,將資料寫入 SQL。為 回覆洞察 新增 schema,並把讀寫都放在 actions 後面: +陣列中的每個條目都是一個累加式遷移。當您稍後新增欄位或資料表時,請附加一個新的版本物件。切勿編輯現有的條目。 -用 framework 的可移植 helpers 定義資料表,不要直接從 `drizzle-orm/sqlite-core` 或 `drizzle-orm/pg-core` 匯入: +### 定義結構描述 -```ts -// server/db/schema.ts +建立 `server/db/schema.ts`。`server/db/` 目錄可能尚不存在, +如有需要請先建立。此檔案使用具有型別的輔助函式描述您的資料表,讓您的動作獲得完整的 TypeScript 自動完成: + +```ts filename="server/db/schema.ts" import { integer, now, table, text } from "@agent-native/core/db/schema"; -export const responseInsights = table("response_insights", { +export const textAnalyses = table("text_analyses", { id: text("id").primaryKey(), - formId: text("form_id").notNull(), - title: text("title").notNull(), - summary: text("summary").notNull(), - responseCount: integer("response_count").notNull(), + input: text("input").notNull(), + charCount: integer("char_count").notNull(), + wordCount: integer("word_count").notNull(), + sentenceCount: integer("sentence_count").notNull(), createdAt: text("created_at").notNull().default(now()), - updatedAt: text("updated_at").notNull().default(now()), }); ``` -這些 helpers 會選擇目前設定的 SQL backend,因此同一份 schema 可以在本機 SQLite 上執行,也可以在 production 的 Postgres、Turso/libSQL、D1 或其他支援的 SQL provider 上執行。 +請使用框架的結構描述輔助函式(`table`、`text`、`integer`、`now`),而非 +`sqliteTable`、`pgTable` 或特定方言的 import。它們會自動選取已配置的 SQL 後端,因此相同的結構描述可以在本機的 SQLite 和生產環境的任何支援提供者上運行。 + +新增兩個檔案後,重新啟動開發伺服器以執行遷移: -- `create-response-insight` 寫入新的 insight。 -- `list-response-insights` 為頁面讀取資料。 -- `update-response-insight` 在 agent 學到更多後更新記錄。 -- `analyze-responses` 繼續返回聊天中的原生 widget。 +```bash +pnpm dev +``` + +在終端機輸出中尋找這兩行。它們確認資料表已建立: + +``` +[db] Applying 1 migration(s) on SQLite/libsql… +[db] Applied migration v1 (1 statement) +``` -不要為瀏覽器另外寫一條重複的 REST route;UI 和 agent 應共享 actions。 +同時出現的 `NitroViteError` 行、`BETTER_AUTH_SECRET` 警告和 +`SECRETS_ENCRYPTION_KEY` 警告在本機開發環境中是正常的,可以忽略。 -## 5. 新增 agent 可以開啟的頁面 {#add-a-page} +### 為資料表新增動作 + +現在建立讀寫資料表的動作檔案。這些檔案放在您的 +`actions/` 目錄中,與 `hello.ts` 和 `analyze-text.ts` 相同的位置。您自行建立它們,每個操作一個檔案。代理和您的 UI 將以呼叫任何其他動作的相同方式呼叫它們。 + +**`actions/save-text-analysis.ts`** 將結果行寫入資料庫。 +在執行 `analyze-text` 後呼叫此動作,使結果持久化: + +```ts filename="actions/save-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Save a text analysis result to the database.", + schema: z.object({ + input: z.string(), + charCount: z.number(), + wordCount: z.number(), + sentenceCount: z.number(), + }), + run: async ({ input, charCount, wordCount, sentenceCount }) => { + const id = crypto.randomUUID(); + await getDbExec().execute({ + sql: `INSERT INTO text_analyses (id, input, char_count, word_count, sentence_count) + VALUES (?, ?, ?, ?, ?)`, + args: [id, input, charCount, wordCount, sentenceCount], + }); + return { id }; + }, +}); +``` + +**`actions/list-text-analyses.ts`** 讀取所有已儲存的結果。代理可以呼叫此動作來摘要過去的分析,您的 UI 也可以使用它來填充頁面: + +```ts filename="actions/list-text-analyses.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "List all saved text analyses, newest first.", + schema: z.object({}), + run: async () => { + const result = await getDbExec().execute( + `SELECT id, input, char_count, word_count, sentence_count, created_at + FROM text_analyses + ORDER BY created_at DESC`, + ); + return result.rows; + }, +}); +``` + +**`actions/delete-text-analysis.ts`** 依 id 刪除一行: + +```ts filename="actions/delete-text-analysis.ts" +import { defineAction } from "@agent-native/core/action"; +import { getDbExec } from "@agent-native/core/db"; +import { z } from "zod"; + +export default defineAction({ + description: "Delete a saved text analysis by id.", + schema: z.object({ id: z.string() }), + run: async ({ id }) => { + await getDbExec().execute({ + sql: `DELETE FROM text_analyses WHERE id = ?`, + args: [id], + }); + return { deleted: id }; + }, +}); +``` + +這些檔案儲存後,開發伺服器會自動偵測到它們。無需重新啟動。從終端機試著列出分析結果: + +```bash +pnpm action list-text-analyses +``` + +您應該會看到一個空陣列。資料表已存在,動作也可正常運作;只是尚未儲存任何內容: + +``` +[] +``` + +資料儲存在 `data/app.db`,這是您專案目錄中的一個 SQLite 檔案,在首次執行時自動建立。在生產環境中,您會將 `DATABASE_URL` 指向託管的資料庫,但在本機,這個檔案就是您所需的一切。 + +若要儲存資料,首先執行 `analyze-text` 取得計數: + +```bash +pnpm action analyze-text --text "Hello world" +``` + +您會看到類似以下的輸出: + +``` +{ + title: 'Text statistics', + points: [ + { label: 'Characters', value: 11 }, + { label: 'Words', value: 2 }, + { label: 'Sentences', value: 1 }, + { label: 'Paragraphs', value: 1 } + ] +} +``` + +然後將這些值傳遞給 `save-text-analysis`: + +```bash +pnpm action save-text-analysis \ + --input "Hello world" \ + --charCount 11 \ + --wordCount 2 \ + --sentenceCount 1 +``` + +現在再次執行 `list-text-analyses`,您將看到已儲存的行: + +```bash +pnpm action list-text-analyses +``` + +或在 `http://localhost:8080` 的對話中詢問代理一次完成兩個步驟: + +> Run analyze-text on "Hello world", then save the result. + +## 6. 新增代理可以開啟的頁面 {#add-a-page} + +對話非常適合互動式交流,但有些資料在專用 UI 中更便於檢視:一個可以掃描、排序或刪除行的資料表。此步驟新增一個 React 路由,顯示 `text_analyses` 中儲存的所有內容,使用您已經撰寫的相同 `list-text-analyses` 和 `delete-text-analysis` 動作。沒有第二個資料層。該頁面只是代理讀寫的相同 SQL 狀態上的一個視圖。 + +在 `app/routes/text-analyses.tsx` 建立路由檔案。`app/routes/` 中的路由檔案會被框架自動偵測。檔案名稱成為 URL 路徑,因此此頁面將在 +`http://localhost:8080/text-analyses` 上可用。 + +```tsx filename="app/routes/text-analyses.tsx" +import { + useActionMutation, + useActionQuery, +} from "@agent-native/core/client/hooks"; + +export default function TextAnalysesRoute() { + const analyses = useActionQuery("list-text-analyses", {}); + const deleteAnalysis = useActionMutation("delete-text-analysis"); + + return ( +
+
+

Text analyses

+

+ Results saved by the agent or triggered manually. +

+
+
+ {analyses.data?.length === 0 && ( +

No analyses saved yet.

+ )} + {analyses.data?.map((row: any) => ( +
+
+

{row.input}

+

+ {row.word_count} words · {row.char_count} characters ·{" "} + {row.sentence_count} sentences +

+
+ +
+ ))} +
+
+ ); +} +``` + +`useActionQuery` 呼叫 `list-text-analyses` 並保持結果即時更新。如果代理在頁面開啟時儲存了新的行,它會自動出現。 +`useActionMutation` 在使用者點擊刪除時呼叫 `delete-text-analysis`,然後使查詢失效以刷新列表。 + +在瀏覽器中開啟 `http://localhost:8080/text-analyses`。如果您在前一步驟中儲存了分析結果,您會看到它列在其中。然後在對話中詢問代理: + +> Open the text analyses page. + +如果出現 404,請嘗試重新啟動您的開發伺服器。 + +## 7. 擴展導覽 {#extend-navigation} + +側邊欄在 `app/config/nav.ts` 中設定。開啟該檔案並為文字分析頁面新增一個項目: + +```ts filename="app/config/nav.ts" +export const navConfig = [ + { + label: "Chat", + href: "/", + icon: "chat", + }, + { + label: "Text analyses", + href: "/text-analyses", + icon: "list", + }, +]; +``` -新增 `/response-insights` route,用 `useActionQuery("list-response-insights")` 讀取資料,用 `useActionMutation("create-response-insight")` 觸發分析。這個頁面只是 SQL state 的投影;資料仍由同一組 actions 寫入。 +儲存檔案。開發伺服器會自動偵測到變更,側邊欄無需重新啟動即可更新。 - -

回覆洞察

Agent 從表單回覆建立的洞察。

定價主題

使用者想要團隊方案。

導入流程流失

三個步驟需要更清楚的文案。

示範表單已分析 42 則回覆
下一步檢視兩個洞察草稿
" - } - /> -
+### 代理導覽 -## 6. 讓 agent 導覽 {#agent-navigation} +側邊欄連結讓使用者可以手動導覽。代理也可以使用 Chat 模板內建的兩個動作自行開啟頁面: -Chat 範本包含 `view-screen` 和 `navigate` action。隨著應用的成長擴充它們: +- **`view-screen`** 讀取當前路由並返回使用者正在查看內容的簡潔摘要。 +- **`navigate`** 將同源路徑寫入瀏覽器的歷史記錄。 -- `view-screen` 應讀取 application state,回傳目前 route、選中的 insight、生效的篩選條件,以及 agent 需要的任何精簡頁面上下文。 -- 當 agent 判斷使用者需要直觀檢視結果時,`navigate` 應寫入 `/response-insights` 這類同源 path。 -- action 結果可以包含 `href: "/response-insights"` 這類連結,讓聊天提供明確的「開啟回覆洞察」按鈕。 +隨著您新增更多頁面,請持續更新 `navigate`,讓代理了解有哪些可用目的地。在 `AGENTS.md` 中記錄可用路徑,以便模型對其進行推理。 -當完整聊天頁開啟 app 頁面時,使用 [Agent Surfaces](/docs/agent-surfaces#rich-chat) 中的 `AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、`useAgentChatHomeHandoffLinks` 和 `chatViewTransition`,讓聊天滑到側邊欄並保留同一個 thread。 +當應用程式同時擁有全頁對話路由和應用程式頁面時,請使用 [Agent Surfaces](/docs/agent-surfaces#rich-chat) 中描述的共享對話交接輔助函式: +`AgentChatSurface`、`AgentSidebar`、`useAgentChatHomeHandoff`、 +`useAgentChatHomeHandoffLinks` 和 `chatViewTransition`。這樣可以讓完整對話在頁面開啟時滑入側邊面板,在使用者檢視持久資料的同時保持相同的對話串。 ## 專案結構 {#project-structure} ```text my-app/ - actions/ # Agent 和介面都可呼叫的操作 - app/ # React 路由、頁面與聊天介面 - server/ # Nitro 伺服器與 SQL 架構 - AGENTS.md # 應用 Agent 的常駐指令 - .agents/ # Agent 在相關情境載入的技能 - data/app.db # 未設定 DATABASE_URL 時的本機 SQLite 狀態 + actions/ # Agent-callable and UI-callable operations + app/ # React routes, pages, and chat surfaces + server/ # Nitro server and SQL schema + AGENTS.md # Always-on instructions for the app agent + .agents/ # Skills the agent loads when relevant + data/app.db # Local SQLite state when DATABASE_URL is unset ``` -## 想要完整的 Analytics 起點? {#analytics-starting-point} +## 想要一個完整的分析起始點? {#analytics-starting-point} -上面的 回覆洞察 範例刻意保持很小,方便你看清框架部件。如果你要建置真正的 analytics 產品,請從 [Analytics](/docs/template-analytics) 開始。它是更強健的起點:連接 providers,使用現有 dashboards 與 agent actions,然後繼續自訂。 +上面的 text-analyses 範例刻意設計得較小,讓您能看清楚框架的各個組成部分。如果您正在建立真正的分析產品,請從 [Analytics](/docs/template-analytics) 開始。它是穩健的起始點:連接您的提供者,使用現有的儀表板和代理動作,然後從那裡自訂應用程式。 ## 下一步 {#next} -- **[Actions](/docs/actions)** — schemas、auth、approvals、hooks 與 transport。 -- **[Native Chat UI](/docs/native-chat-ui)** — 將 action 結果渲染成表格、圖表與 typed cards。 -- **[Chat Template](/docs/template-chat)** — 最小聊天優先 app。 -- **[Analytics Template](/docs/template-analytics)** — 強健的 analytics app 起點;連接 providers 後繼續自訂。 -- **[Context Awareness](/docs/context-awareness)** — `view-screen`、`navigate`、route state 與選中物件。 -- **[Agent Surfaces](/docs/agent-surfaces)** — chat、inline UI、app pages、embedded sidecars、automation 與外部 agent。 +- **[Actions](/docs/actions)**:結構描述、驗證、審核、hooks 和傳輸方式。 +- **[Native Chat UI](/docs/native-chat-ui)**:將動作結果渲染為表格、圖表和具有型別的卡片。 +- **[Chat Template](/docs/template-chat)**:您剛建立的最精簡對話優先應用程式。 +- **[Analytics Template](/docs/template-analytics)**:穩健的分析應用程式起始點;連接提供者並從那裡自訂。 +- **[Context Awareness](/docs/context-awareness)**:`view-screen`、`navigate`、路由狀態和已選取物件。 +- **[Agent Surfaces](/docs/agent-surfaces)**:對話、內嵌 UI、應用程式頁面、嵌入式側邊欄、自動化和外部代理。 +- **[Deployment](/docs/deployment)**:將您的應用程式部署到自己的網域。 diff --git a/scripts/i18n-localized-docs-baseline.txt b/scripts/i18n-localized-docs-baseline.txt index d86bf545d5..43035da2ca 100644 --- a/scripts/i18n-localized-docs-baseline.txt +++ b/scripts/i18n-localized-docs-baseline.txt @@ -3,6 +3,19 @@ # Format: relative/localized/path.md|English source string packages/core/docs/content/locales/ar-SA/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/ar-SA/frames.mdx|type: \"code\" +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Hello world +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/ar-SA/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Text analyses +packages/core/docs/content/locales/ar-SA/getting-started.mdx|Text statistics +packages/core/docs/content/locales/ar-SA/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/ar-SA/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/ar-SA/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/ar-SA/recurring-jobs.mdx|A2A, email @@ -12,6 +25,19 @@ packages/core/docs/content/locales/ar-SA/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/ar-SA/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/de-DE/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/de-DE/frames.mdx|type: \"code\" +packages/core/docs/content/locales/de-DE/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/de-DE/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/de-DE/getting-started.mdx|Hello world +packages/core/docs/content/locales/de-DE/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/de-DE/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/de-DE/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/de-DE/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/de-DE/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/de-DE/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/de-DE/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/de-DE/getting-started.mdx|Text analyses +packages/core/docs/content/locales/de-DE/getting-started.mdx|Text statistics +packages/core/docs/content/locales/de-DE/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/de-DE/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/de-DE/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/de-DE/recurring-jobs.mdx|A2A, email @@ -21,6 +47,13 @@ packages/core/docs/content/locales/de-DE/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/de-DE/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/es-ES/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/es-ES/frames.mdx|type: \"code\" +packages/core/docs/content/locales/es-ES/getting-started.mdx|Hello world +packages/core/docs/content/locales/es-ES/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/es-ES/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/es-ES/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/es-ES/getting-started.mdx|Text analyses +packages/core/docs/content/locales/es-ES/getting-started.mdx|Text statistics +packages/core/docs/content/locales/es-ES/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/es-ES/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/es-ES/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/es-ES/recurring-jobs.mdx|A2A, email @@ -30,6 +63,19 @@ packages/core/docs/content/locales/es-ES/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/es-ES/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/fr-FR/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/fr-FR/frames.mdx|type: \"code\" +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Hello world +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/fr-FR/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Text analyses +packages/core/docs/content/locales/fr-FR/getting-started.mdx|Text statistics +packages/core/docs/content/locales/fr-FR/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/fr-FR/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/fr-FR/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/fr-FR/recurring-jobs.mdx|A2A, email @@ -39,6 +85,19 @@ packages/core/docs/content/locales/fr-FR/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/fr-FR/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/hi-IN/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/hi-IN/frames.mdx|type: \"code\" +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Hello world +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/hi-IN/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Text analyses +packages/core/docs/content/locales/hi-IN/getting-started.mdx|Text statistics +packages/core/docs/content/locales/hi-IN/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/hi-IN/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/hi-IN/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/hi-IN/recurring-jobs.mdx|A2A, email @@ -48,6 +107,19 @@ packages/core/docs/content/locales/hi-IN/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/hi-IN/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/ja-JP/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/ja-JP/frames.mdx|type: \"code\" +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Hello world +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/ja-JP/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Text analyses +packages/core/docs/content/locales/ja-JP/getting-started.mdx|Text statistics +packages/core/docs/content/locales/ja-JP/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/ja-JP/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/ja-JP/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/ja-JP/recurring-jobs.mdx|A2A, email @@ -57,6 +129,19 @@ packages/core/docs/content/locales/ja-JP/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/ja-JP/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/ko-KR/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/ko-KR/frames.mdx|type: \"code\" +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Hello world +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/ko-KR/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Text analyses +packages/core/docs/content/locales/ko-KR/getting-started.mdx|Text statistics +packages/core/docs/content/locales/ko-KR/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/ko-KR/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/ko-KR/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/ko-KR/recurring-jobs.mdx|A2A, email @@ -66,6 +151,13 @@ packages/core/docs/content/locales/ko-KR/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/ko-KR/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/pt-BR/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/pt-BR/frames.mdx|type: \"code\" +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Hello world +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Text analyses +packages/core/docs/content/locales/pt-BR/getting-started.mdx|Text statistics +packages/core/docs/content/locales/pt-BR/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/pt-BR/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/pt-BR/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/pt-BR/recurring-jobs.mdx|A2A, email @@ -75,6 +167,19 @@ packages/core/docs/content/locales/pt-BR/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/pt-BR/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/zh-CN/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/zh-CN/frames.mdx|type: \"code\" +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Hello world +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/zh-CN/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Text analyses +packages/core/docs/content/locales/zh-CN/getting-started.mdx|Text statistics +packages/core/docs/content/locales/zh-CN/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/zh-CN/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/zh-CN/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/zh-CN/recurring-jobs.mdx|A2A, email @@ -84,6 +189,19 @@ packages/core/docs/content/locales/zh-CN/template-slides.mdx|"10-slide pitch dec packages/core/docs/content/locales/zh-CN/using-your-agent.mdx|**Shared awareness is two-way.** You and the agent both read and write `application_state`, so "reply to this" or "summarize the selection" just works — and when the agent navigates, the real UI moves with it. packages/core/docs/content/locales/zh-TW/dispatch.mdx|"summarize last week's signups" packages/core/docs/content/locales/zh-TW/frames.mdx|type: \"code\" +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Agent-callable and UI-callable operations +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Always-on instructions for the app agent +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Hello world +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Local SQLite state when DATABASE_URL is unset +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Nitro server and SQL schema +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Pack my box with five dozen liquor jugs. +packages/core/docs/content/locales/zh-TW/getting-started.mdx|React routes, pages, and chat surfaces +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Results saved by the agent or triggered manually. +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Run the analyze-text action on "Hello world. How are you today?" +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Skills the agent loads when relevant +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Text analyses +packages/core/docs/content/locales/zh-TW/getting-started.mdx|Text statistics +packages/core/docs/content/locales/zh-TW/getting-started.mdx|The quick brown fox jumps over the lazy dog. packages/core/docs/content/locales/zh-TW/messaging.mdx|Microsoft Teams packages/core/docs/content/locales/zh-TW/recurring-jobs.mdx|, recompute packages/core/docs/content/locales/zh-TW/recurring-jobs.mdx|A2A, email