OA0 = Omni AI 0
OA0 是一个探索 AI 的论坛
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  ydc-ai-sdk-integration:集成 Vercel AI SDK 应用程序的开发技能

ydc-ai-sdk-integration:集成 Vercel AI SDK 应用程序的开发技能

 
  debug ·  2026-02-16 00:56:57 · 3 次点击  · 0 条评论  

名称: 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


将 AI SDK 与 You.com 工具集成

本交互式工作流指导您使用 @youdotcom-oss/ai-sdk-plugin 将 You.com 工具添加到 Vercel AI SDK 应用中。

工作流程

  1. 询问:包管理器

    • 使用哪个包管理器?(npm、bun、yarn、pnpm)
    • 根据其选择安装包:
      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
  2. 询问:环境变量名称

    • 使用标准的 YDC_API_KEY 吗?
    • 还是自定义名称?(如果是自定义,获取名称)
    • 是否已在环境中设置该变量?
    • 如果没有:引导他们从 https://you.com/platform/api-keys 获取密钥。
  3. 询问:使用哪些 AI SDK 函数?

    • 是否使用 generateText()
    • 是否使用 streamText()
    • 两者都用?
  4. 询问:修改现有文件还是创建新文件?

    • 现有文件:询问要编辑哪个(些)文件。
    • 新文件:询问在何处创建文件以及如何命名。
  5. 对于每个文件,询问:

    • 要添加哪些工具?
      • youSearch(网络搜索)
      • youExpress(AI 代理)
      • youContents(内容提取)
      • 多个工具?(哪种组合?)
    • 在此文件中使用 generateText() 还是 streamText()
    • 使用哪个 AI 提供商模型?(以确定是否需要 stopWhen 参数)
  6. 参考集成示例

    请参阅下方的“集成示例”部分,了解完整的代码模式:
    * generateText() - 使用工具的基本文本生成
    * streamText() - 与 Web 框架(Next.js、Express、React)集成的流式响应

  7. 更新/创建文件

    对于每个文件:
    * 参考集成示例(根据其回答选择 generateTextstreamText
    * 添加所选工具的导入语句
    * 如果是现有文件:找到其 generateText/streamText 调用并添加 tools 对象
    * 如果是新文件:使用示例结构创建文件
    * 根据环境变量名称确定工具调用模式:
    - 标准 YDC_API_KEYyouSearch()
    - 自定义名称:youSearch({ apiKey: process.env.CUSTOM_NAME })
    * 将所选工具添加到 tools 对象
    * 如果使用 streamText + Anthropic:添加 stopWhen 参数

集成示例

generateText() - 基本文本生成

环境变量设置:

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();

streamText() - 流式响应

使用 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 }),
}

可用工具

youSearch

网络和新闻搜索 - 模型决定参数(查询、数量、国家/地区等)

youExpress

具有网络上下文的 AI 代理 - 模型决定参数(输入、工具)

youContents

网页内容提取 - 模型决定参数(URL、格式)

关键集成模式

以上示例展示了:
* 导入语句(AI SDK + 提供商 + You.com 工具)
* 环境变量验证(对新文件可选)
* 基于环境变量的工具配置
* 使用工具的 generateText/streamText 用法
* 结果处理(特别是 streamTexttextStream 解构)
* Anthropic 流式处理模式(stopWhen: stepCountIs(3)
* Web 框架集成(Next.js、Express、React)

实施检查清单

对于每个要更新/创建的文件:

  • [ ] 已添加所选工具的导入语句
  • [ ] 如果是自定义环境变量:已使用正确名称声明变量
  • [ ] 已将 tools 对象添加到 generateText/streamText
  • [ ] 每个选定的工具调用正确:
    • 标准环境变量:toolName()
    • 自定义环境变量:toolName({ apiKey })
  • [ ] 如果使用 streamText:已解构 const { textStream } = ...
  • [ ] 如果使用 Anthropic + 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 密钥处理

始终提供环境变量回退,并在 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 搜索网络以获取最新信息、新闻、文章和内容。返回带有摘要和新闻文章的网络结果。当您需要来自互联网的最新信息或事实时使用此工具。'

// ❌ 过于简短
**描述:** '搜索网络'

其他资源

  • 包 README:https://github.com/youdotcom-oss/dx-toolkit/tree/main/packages/ai-sdk-plugin
  • Vercel AI SDK 文档:https://ai-sdk.dev/docs
  • You.com API:https://you.com/platform/api-keys
3 次点击  ∙  0 人收藏  
登录后收藏  
目前尚无回复
0 条回复
About   ·   Help   ·    
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
Developed with Cursor