名称: ydc-ai-sdk-integration
描述: 将 Vercel AI SDK 应用与 You.com 工具(网络搜索、AI 代理、内容提取)集成。当开发者提及 AI SDK、Vercel AI SDK、generateText、streamText 或 You.com 与 AI SDK 集成时使用。
许可证: MIT
compatibility: 需要 Node.js 18+ 和 npm/bun/yarn/pnpm
元数据:
author: youdotcom-oss
category: sdk-integration
version: "1.0.0"
keywords: vercel,vercel-ai-sdk,ai-sdk,you.com,integration,anthropic,openai,web-search,content-extraction,livecrawl,citations
本交互式工作流指导您使用 @youdotcom-oss/ai-sdk-plugin 将 You.com 工具添加到 Vercel AI SDK 应用中。
询问:包管理器
bash
npm install @youdotcom-oss/ai-sdk-plugin
# 或 bun add @youdotcom-oss/ai-sdk-plugin
# 或 yarn add @youdotcom-oss/ai-sdk-plugin
# 或 pnpm add @youdotcom-oss/ai-sdk-plugin询问:环境变量名称
YDC_API_KEY 吗?询问:使用哪些 AI SDK 函数?
generateText()?streamText()?询问:修改现有文件还是创建新文件?
对于每个文件,询问:
youSearch(网络搜索)youExpress(AI 代理)youContents(内容提取)generateText() 还是 streamText()?stopWhen 参数)参考集成示例
请参阅下方的“集成示例”部分,了解完整的代码模式:
* generateText() - 使用工具的基本文本生成
* streamText() - 与 Web 框架(Next.js、Express、React)集成的流式响应
更新/创建文件
对于每个文件:
* 参考集成示例(根据其回答选择 generateText 或 streamText)
* 添加所选工具的导入语句
* 如果是现有文件:找到其 generateText/streamText 调用并添加 tools 对象
* 如果是新文件:使用示例结构创建文件
* 根据环境变量名称确定工具调用模式:
- 标准 YDC_API_KEY:youSearch()
- 自定义名称:youSearch({ apiKey: process.env.CUSTOM_NAME })
* 将所选工具添加到 tools 对象
* 如果使用 streamText + Anthropic:添加 stopWhen 参数
环境变量设置:
import { anthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';
import { youContents, youExpress, youSearch } from '@youdotcom-oss/ai-sdk-plugin';
// 自动从环境变量读取 YDC_API_KEY
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: {
search: youSearch(),
},
prompt: '量子计算的最新进展是什么?',
});
console.log(result.text);
使用多个工具:
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: {
search: youSearch(), // 带引用的网络搜索
agent: youExpress(), // 基于网络上下文的 AI 回答
extract: youContents(), // 从 URL 提取内容
},
prompt: '研究量子计算并总结关键论文',
});
自定义 API 密钥:
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: {
search: youSearch({ apiKey: 'your-custom-key' }),
},
prompt: '在此输入您的提示',
});
完整示例:
import { anthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
const main = async () => {
try {
const result = await generateText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: {
search: youSearch(),
},
maxSteps: 5,
prompt: '量子计算的最新进展是什么?',
});
console.log('生成的文本:', result.text);
console.log('\n工具调用:', result.steps.flatMap(s => s.toolCalls));
} catch (error) {
console.error('错误:', error);
process.exit(1);
}
};
main();
使用 stopWhen 模式的基本流式处理:
import { anthropic } from '@ai-sdk/anthropic';
import { streamText, type StepResult } from 'ai';
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
// 关键:Anthropic 流式处理必须使用 stopWhen
// Anthropic 的 SDK 需要明确的停止条件
const stepCountIs = (n: number) => (stepResult: StepResult<any>) =>
stepResult.stepNumber >= n;
const result = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: { search: youSearch() },
stopWhen: stepCountIs(3), // Anthropic 必需
prompt: 'AI 的最新发展是什么?',
});
// 消费流
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
Next.js 集成(App Router):
// app/api/chat/route.ts
import { anthropic } from '@ai-sdk/anthropic';
import { streamText, type StepResult } from 'ai';
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
const stepCountIs = (n: number) => (stepResult: StepResult<any>) =>
stepResult.stepNumber >= n;
export async function POST(req: Request) {
const { prompt } = await req.json();
const result = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: { search: youSearch() },
stopWhen: stepCountIs(5),
prompt,
});
return result.toDataStreamResponse();
}
Express.js 集成:
// server.ts
import express from 'express';
import { anthropic } from '@ai-sdk/anthropic';
import { streamText, type StepResult } from 'ai';
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
const app = express();
app.use(express.json());
const stepCountIs = (n: number) => (stepResult: StepResult<any>) =>
stepResult.stepNumber >= n;
app.post('/api/chat', async (req, res) => {
const { prompt } = req.body;
const result = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: { search: youSearch() },
stopWhen: stepCountIs(5),
prompt,
});
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.setHeader('Transfer-Encoding', 'chunked');
for await (const chunk of result.textStream) {
res.write(chunk);
}
res.end();
});
app.listen(3000);
React 客户端(与 Next.js 配合):
// components/Chat.tsx
'use client';
import { useChat } from 'ai/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat',
});
return (
<div>
{messages.map(m => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">发送</button>
</form>
</div>
);
}
完整的流式处理示例:
import { anthropic } from '@ai-sdk/anthropic';
import { streamText, type StepResult } from 'ai';
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
const stepCountIs = (n: number) => (stepResult: StepResult<any>) =>
stepResult.stepNumber >= n;
const main = async () => {
try {
const result = streamText({
model: anthropic('claude-sonnet-4-5-20250929'),
tools: {
search: youSearch(),
},
stopWhen: stepCountIs(3),
prompt: 'AI 的最新发展是什么?',
});
// 流式输出到标准输出
console.log('流式响应:\n');
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
console.log('\n\n完成!');
} catch (error) {
console.error('错误:', error);
process.exit(1);
}
};
main();
根据步骤 2 中的环境变量名称:
标准 YDC_API_KEY:
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
tools: {
search: youSearch(),
}
自定义环境变量:
import { youSearch } from '@youdotcom-oss/ai-sdk-plugin';
const apiKey = process.env.THEIR_CUSTOM_NAME;
tools: {
search: youSearch({ apiKey }),
}
使用标准环境变量的多个工具:
import { youSearch, youExpress, youContents } from '@youdotcom-oss/ai-sdk-plugin';
tools: {
search: youSearch(),
agent: youExpress(),
extract: youContents(),
}
使用自定义环境变量的多个工具:
import { youSearch, youExpress, youContents } from '@youdotcom-oss/ai-sdk-plugin';
const apiKey = process.env.THEIR_CUSTOM_NAME;
tools: {
search: youSearch({ apiKey }),
agent: youExpress({ apiKey }),
extract: youContents({ apiKey }),
}
网络和新闻搜索 - 模型决定参数(查询、数量、国家/地区等)
具有网络上下文的 AI 代理 - 模型决定参数(输入、工具)
网页内容提取 - 模型决定参数(URL、格式)
以上示例展示了:
* 导入语句(AI SDK + 提供商 + You.com 工具)
* 环境变量验证(对新文件可选)
* 基于环境变量的工具配置
* 使用工具的 generateText/streamText 用法
* 结果处理(特别是 streamText 的 textStream 解构)
* Anthropic 流式处理模式(stopWhen: stepCountIs(3))
* Web 框架集成(Next.js、Express、React)
对于每个要更新/创建的文件:
tools 对象添加到 generateText/streamTexttoolName()toolName({ apiKey })streamText:已解构 const { textStream } = ...streamText:已添加 stopWhen: stepCountIs(3)全局检查清单:
问题:“找不到模块 @youdotcom-oss/ai-sdk-plugin”
解决:使用其包管理器安装
问题:“需要 YDC_API_KEY(或自定义名称)环境变量”
解决:在环境中设置(获取密钥:https://you.com/platform/api-keys)
问题:“工具执行失败,状态码 401”
解决:验证 API 密钥是否有效
问题:“响应不完整或缺失”
解决:如果使用 streamText,请增加步数。从 3 开始,根据需要递增(参见 README 故障排除部分)
问题:“textStream 不可迭代”
解决:解构:const { textStream } = streamText(...)
问题:“自定义环境变量不起作用”
解决:传递给每个工具:youSearch({ apiKey })
适用于创建自定义 AI SDK 工具或为 @youdotcom-oss/ai-sdk-plugin 做贡献的开发者:
每个工具函数遵循以下模式:
export const youToolName = (config: YouToolsConfig = {}) => {
const apiKey = config.apiKey ?? process.env.YDC_API_KEY;
return tool({
description: '供 AI 模型使用的工具描述',
inputSchema: ZodSchema,
execute: async (params) => {
if (!apiKey) {
throw new Error('需要 YDC_API_KEY');
}
const response = await callApiUtility({
params,
YDC_API_KEY: apiKey,
getUserAgent,
});
// 返回原始 API 响应以获得最大灵活性
return response;
},
});
};
始终使用来自 @youdotcom-oss/mcp 的模式:
// ✅ 从 @youdotcom-oss/mcp 导入
import { SearchQuerySchema } from '@youdotcom-oss/mcp';
export const youSearch = (config: YouToolsConfig = {}) => {
return tool({
description: '...',
inputSchema: SearchQuerySchema, // 使 AI 能够使用所有搜索参数
execute: async (params) => { ... },
});
};
// ❌ 不要重复或简化模式
const MySearchSchema = z.object({ query: z.string() }); // 缺少过滤器!
为什么这很重要:
- 丰富的模式使 AI 能够使用高级查询参数(过滤器、新鲜度、国家/地区等)
- AI 可以根据用户意图构建更智能的查询
- 防止跨包重复模式定义
- 确保与 MCP 服务器模式的一致性
始终提供环境变量回退,并在 API 调用前进行验证:
// ✅ 自动环境变量回退
const apiKey = config.apiKey ?? process.env.YDC_API_KEY;
// ✅ 在 execute 函数中检查 API 密钥
execute: async (params) => {
if (!apiKey) {
throw new Error('需要 YDC_API_KEY');
}
const response = await callApi(...);
}
始终返回原始 API 响应以获得最大灵活性:
// ✅ 返回原始 API 响应
execute: async (params) => {
const response = await fetchSearchResults({
searchQuery: params,
YDC_API_KEY: apiKey,
getUserAgent,
});
return response; // 原始响应以获得最大灵活性
}
// ❌ 不要格式化或转换响应
return {
text: formatResponse(response),
data: response,
};
为什么使用原始响应?
- AI SDK 处理结果的最大灵活性
- 不会因格式化而丢失信息
- AI SDK 处理表示层
- 更易于调试(查看实际的 API 响应)
编写能指导 AI 行为的描述:
// ✅ 为 AI 模型提供清晰指导
**描述:** '使用 You.com 搜索网络以获取最新信息、新闻、文章和内容。返回带有摘要和新闻文章的网络结果。当您需要来自互联网的最新信息或事实时使用此工具。'
// ❌ 过于简短
**描述:** '搜索网络'