快速回答
如何从 Node.js 为 AI agent 调用网页搜索 API?
使用 Node 18+ 内置的 fetch,用 Bearer 密钥把你的查询 POST 到 https://llmwebsearch.com/api/v1/search。LLMWebSearch 返回排序后的结果,以及前 3 个页面的 Markdown,可直接编号并传给模型生成带引用的回答。一次搜索花费 1 积分,低至 US$1.20/千次搜索(截至 2026 年 10 月)。无需 npm 包。无需 KYC;加密货币支付。
你需要什么
| Node.js | 18 或更新版本,以使用内置的 fetch;把脚本保存为 .mjs 以使用顶层 await |
|---|---|
| 软件包 | 无 |
| 密钥 | 在你的环境中的 LLMWEBSEARCH_API_KEY |
| 用于回答循环 | 一个 OpenAI 兼容的聊天 endpoint:LLM_BASE_URL、LLM_API_KEY、LLM_MODEL |
| 计费 | 1 积分支付一次查询:最多 20 条排名结果,其中前 3 条的页面会被读取并以 Markdown 返回。读取 4 到 10 个页面为 2 积分。以错误结束的调用不收费 |
如何编写可复用的搜索函数?
把调用封装一次,到处复用。下面的函数发送 body,在任何 HTTP 失败时抛出可读的错误,并返回解析后的 JSON。错误会带上 API 自己的 code,例如 rate_limited 或 insufficient_credits,让调用方决定如何处理。
const API = 'https://llmwebsearch.com/api/v1/search';
export async function search(body) {
const res = await fetch(API, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await res.json();
if (!res.ok) {
const err = new Error(`${res.status} ${data.error?.code}: ${data.error?.message}`);
err.status = res.status;
throw err;
}
return data;
}
const data = await search({ q: 'how to size a postgres connection pool', read: 3 });
for (const r of data.results) {
console.log(r.position, r.content_status, r.url, r.word_count ?? '');
}
如何在不触发速率限制的情况下运行多次搜索?
通过一个小型池来运行:固定数量的 worker,每个 worker 从共享列表中取出下一个查询。四个 worker 是一个合理的起点。每个账户都有每分钟调用上限,显示在控制面板中;联系支持即可提高。
下面的池不需要任何库。每个 worker 保留自己的结果,失败的搜索会连同错误一起记录,而不会中断整个运行。这对研究型 agent 很重要,因为一个有问题的子问题不应让整个任务失败。
const API = 'https://llmwebsearch.com/api/v1/search';
const questions = [
'what is connection pooling in postgres',
'recommended pool size per cpu core',
'pgbouncer transaction mode limitations',
'connection pool exhaustion symptoms',
'pool size for serverless functions',
];
async function search(q) {
const res = await fetch(API, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ q, read: 2, max_chars: 4000 }),
});
const data = await res.json();
if (!res.ok) return { q, error: data.error?.code ?? String(res.status) };
return { q, urls: data.results.filter((r) => r.content_status === 'ok').map((r) => r.url) };
}
async function runPool(items, size, task) {
const out = [];
let next = 0;
const worker = async () => {
while (next < items.length) {
const i = next++;
out[i] = await task(items[i]);
}
};
await Promise.all(Array.from({ length: size }, worker));
return out;
}
for (const row of await runPool(questions, 4, search)) console.log(row);
如何把结果变成带引用的答案?
给来源编号,在每个来源旁附上 URL,按预算裁剪内容,然后让模型按编号引用。API 返回的是来源,而不是写好的答案:由你自己的模型读取页面、撰写回复并引用 URL。下面的脚本可以与任何 OpenAI 兼容的 chat/completions endpoint 通信,通过三个环境变量选择,因此托管模型和本地模型都能用。
约 24,000 个字符(大致 6,000 个 token)的预算在各来源之间平均分配。页面无法读取的结果会以摘要形式传入,并加以标注。
const SEARCH = 'https://llmwebsearch.com/api/v1/search';
const BUDGET = 24000; // characters, about 6,000 tokens
async function sources(question) {
const res = await fetch(SEARCH, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ q: question, max_results: 5, read: 3 }),
});
const { results } = await res.json();
const share = Math.floor(BUDGET / Math.max(results.length, 1));
return results
.map((r, i) => {
const body = r.content_status === 'ok' ? r.content.slice(0, share) : `(snippet only) ${r.snippet}`;
return `[${i + 1}] ${r.title} - ${r.url}\n${body}`;
})
.join('\n\n');
}
async function answer(question) {
const prompt = 'Answer using only the sources below. Cite them as [1], [2] and so on. '
+ 'Say so if they do not answer the question.\n\n' + (await sources(question)) + '\n\nQuestion: ' + question;
const res = await fetch(`${process.env.LLM_BASE_URL.replace(/\/$/, '')}/chat/completions`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLM_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model: process.env.LLM_MODEL, messages: [{ role: 'user', content: prompt }] }),
});
const data = await res.json();
return data.choices[0].message.content;
}
console.log(await answer('How should I size a Postgres connection pool?'));
从 Node 调用时该用 GET 还是 POST?
在应用代码中用 POST,带 JSON body。像 include_domains 这样的数组和较长的查询放在 body 里比放在 query string 里更干净。GET 用于快速测试和无法发送 body 的工具;用 URLSearchParams 构建它的 query string,并用逗号连接列表。
两种方式的费用相同,返回的 JSON 也相同。搜索参考 列出了两种方式的全部参数。
密钥应该放在哪里?
放在运行时读取的环境变量里,绝不要放在源文件中。在服务器上,使用你平台的 secret store。在浏览器中,根本不要调用 API:密钥会暴露给每位访客。改为调用你自己的后端,再由后端调用 LLMWebSearch。
为每个 agent、应用或团队成员各建一个 API 密钥,并可在控制面板中吊销其中任意一个,而不影响其他密钥。每个服务用一个密钥,仪表盘历史更易读,泄露的密钥也容易吊销。
Node.js:常见问题
有 npm 包吗?
没有。fetch 已内置于 Node 18 及更新版本,API 就是通过 HTTPS 传输的纯 JSON。本页这些函数就是全部集成代码。
它能在 Deno、Bun 或 edge 运行时上使用吗?
代码只用到 fetch、JSON 和 process.env。具有 fetch 的运行时,把 process.env 换成其读取 secret 的方式后即可使用。
如何重试失败的调用?
只重试 429、502 和 504,每次暂停时间翻倍。绝不重试 400、401 或 402。错误页面 解释了原因,并给出了一个辅助函数。