不写后端,也能做 AI 应用
写一个网页应用,通过 SDK 调用任意模型,打包成 zip 上传。用户用 RunApps 账号登录并为自己的 AI 用量付费——你不用管服务器,也不用管 API Key。
发布免费 · 用户为自己的用量付费
上传之前
应用一创建就有 slug,不需要先传 zip。在自己电脑上对着这个 slug 开发,发布后的应用用的是同一个身份。
- 1
先把应用建成草稿
控制台 → 创作者 → 发布。填名称和描述,包留空,保存。草稿只有你自己可见,不会进入审核。
- 2
把开发服务器指向它
把 slug 作为 SDK 的 project 选项传入。从 http://localhost 登录可用,调用走真实网关、从你自己的余额扣费,文件落在应用的真实命名空间。
- 3
做好了再上传
构建、打 zip、上传到同一个应用。slug、文件、提示词都已就位,本地跑通的代码就是上线的代码。
// The slug from your draft app — the same one the published
// app will have, so nothing changes at upload time.
const client = new RunApps.Client({
authProvider: "runapps",
project: "my-tool",
});
const resp = await client.chat.create({
model: "Claude Sonnet 4.6",
messages: [{ role: "user", content: "Hello!" }],
// While developing, send a prompt inline; ship it as
// .runapps/prompts/outline.md and use { id: "outline" } later.
prompt: import.meta.env.DEV
? { template: "You write outlines in five acts." }
: { id: "outline" },
});三步发布
- 1
用 SDK 调用 AI
加一行 script 标签,或 npm i @runjobsai/sdk。用户用 RunApps 账号登录,代码里不需要 API Key。
- 2
上传 zip
上传构建好的静态文件,应用自动上线到专属的 runapps.dev 链接。
- 3
上架
直接分享链接,或提交审核后上架到应用库。
<script src="https://cdn.jsdelivr.net/npm/@runjobsai/sdk/dist/sdk.umd.js"></script>
<script>
const client = new RunApps.Client({ authProvider: "runapps" });
const resp = await client.chat.create({
model: "Claude Sonnet 4.6",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(resp.choices[0].message.content);
</script>把提示词留在服务端
前端代码里的任何东西都能在浏览器里看到。把提示词作为文件放进 zip,在 bundle.json 里登记:RunApps 从不对外提供这个文件和它登记的任何文件,应用只按 id 调用。
- 1
写提示词文件
每个提示词一个 Markdown 文件,放在项目任意位置,比如 prompts/outline.md,再在 zip 根目录的 bundle.json 里把 id 指向它。调用时才填的内容写成 {{genre}} 这样的占位符。
- 2
按名字调用
调用 chat.create 时,用 prompt 参数传入提示词的名字和变量值(见右侧代码)。服务端把提示词作为系统消息加进去,网络上只传名字。
- 3
和构建产物一起打包
把 bundle.json 和提示词文件复制进构建产物一起打 zip。发布页会列出识别到的提示词。(放在 .runapps/prompts/<id>.md 的文件不登记也能用。)
本地开发时
在自己的开发服务器上还没有上传的包,所以把提示词原文作为 prompt.template 一起发。服务端只接受应用创作者这样做;import.meta.env.DEV 保证正式构建里不会带上原文。
平台会要求模型对提示词保密,回复里一旦原样复述提示词就会被拦截。但有心人仍可能让模型换个说法透露出来,所以不要把密钥、密码写进提示词。
{
"prompts": {
"outline": "prompts/outline.md",
"rewrite": "prompts/rewrite.md"
}
}You are the outline writer for this studio.
Every story has five acts, with a twist in act four.
Genre: {{genre}}async function privatePrompt(id, vars) {
const prompt = { id, vars };
// Your dev server only: send the text along. Vite drops
// this branch (and the file) from production builds.
if (import.meta.env.DEV) {
prompt.template = (await import(
`../prompts/${id}.md?raw`)).default;
}
return prompt;
}
const resp = await client.chat.create({
model: "Claude Sonnet 4.6",
prompt: await privatePrompt("outline", { genre: "mystery" }),
messages: [{ role: "user", content: idea }],
});npm run build
cp -r bundle.json prompts dist/
cd dist && zip -r ../app.zip .