1. Conduid
  2. Developer Tools
  3. Unified AI
MCP server · Developer Tools

Unified AI

一个统一的AI接口库,用于简化与多种AI模型的交互,支持 mcp、agent。

Unclaimed last commit a year ago devtools
41Fair

Scored 5 months ago · breakdown

About Unified AI

Unified AI is an MCP server published by oukek in the Developer Tools category: 一个统一的AI接口库,用于简化与多种AI模型的交互,支持 mcp、agent。.

The repository has 3 stars and 0 forks, with the last commit a year ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues before depending on it in production.

Install

From source
git clone https://github.com/oukek/unified-ai

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

✦

Ask AI about Unified AI

Powered by Claude · Grounded in docs

I know everything about Unified AI. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

Static checks derived from the repository 7 days ago. Heuristics on the source, not an audit.

  • ✓README presentThe repository ships documentation.
  • !License declaredNo license file. Legally you have no right to use it in a product.
  • ✓Tests presentA test suite exists in the repository.
  • ✓Dependencies pinnedA lockfile pins dependency versions.
  • ✓No dynamic code executionSource scan found no eval, dynamic Function, or shell execution.
  • !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.

README

UnifiedAI npm version

一个统一的AI接口库,用于简化与多种AI模型的交互。

简介

@oukek/unified-ai 是一个SDK,它提供了统一的接口来访问和使用各种AI模型,简化了开发过程,让开发者可以更加专注于应用功能的开发,而不必担心底层AI模型API的差异。通过统一的接口设计,您可以轻松切换不同的AI模型,而无需修改业务代码。

特性

  • 统一的API接口设计,一套代码适配多种模型
  • 支持多种主流AI模型(如Gemini等)
  • 支持标准响应和流式响应两种交互模式
  • 强大的Agent功能(函数调用),支持链式调用执行
  • 支持Model Context Protocol (MCP),可与文件系统等外部工具交互
  • 内置JSON修复功能,自动处理模型返回的非标准JSON
  • 丰富的事件回调系统,可监控整个交互过程
  • 完全TypeScript支持,提供完善的类型定义
  • 可自定义参数,适应不同模型的特殊需求

支持的模型

  • Gemini: 支持Google的Gemini系列模型,包括gemini-pro和gemini-1.5-pro等
  • 自定义模型: 支持扩展实现自定义模型,只需实现BaseModel接口

安装

使用npm:

npm install @oukek/unified-ai

使用yarn:

yarn add @oukek/unified-ai

使用pnpm:

pnpm add @oukek/unified-ai

基本用法

初始化

首先需要初始化一个基础模型,然后将其传递给UnifiedAI:

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'

// 初始化基础模型
const geminiModel = new GeminiModel({
  apiKey: 'your_gemini_api_key_here', // 或从环境变量读取
  model: 'gemini-1.5-pro' // 指定模型名称
})

// 初始化UnifiedAI
const ai = new UnifiedAI(geminiModel, {
  // 配置选项
  maxRecursionDepth: 25, // 最大函数调用递归次数
})

基本聊天

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'

// 初始化基础模型和UnifiedAI
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 发送聊天请求
async function chat() {
  try {
    const response = await ai.unifiedChat('你好,请介绍一下自己')
    console.log('AI回复:', response.content)
    console.log('模型:', response.model)
    
    // 输出token使用情况(如果可用)
    if (response.usage) {
      console.log('Token使用:', response.usage)
    }
  }
  catch (error) {
    console.error('聊天请求失败:', error)
  }
}

chat()

带历史记录的聊天

import { GeminiModel, UnifiedAI, ChatRole } from '@oukek/unified-ai'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 带历史记录的聊天
async function chatWithHistory() {
  const history = [
    { role: ChatRole.USER, content: '你好,我叫张三' },
    { role: ChatRole.ASSISTANT, content: '你好张三,很高兴认识你!有什么我可以帮助你的吗?' }
  ]
  
  const response = await ai.unifiedChat('你猜我今年多大了?', { history })
  console.log('AI回复:', response.content) 
  // AI会回复不知道年龄,因为用户没有提供这个信息
}

chatWithHistory()

流式响应

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 流式聊天
async function streamChat() {
  try {
    const generator = ai.unifiedChatStream('请讲一个故事')
    
    for await (const chunk of generator) {
      // 每个chunk.content是一个文本片段
      process.stdout.write(chunk.content as string) // 实时显示
      
      if (chunk.isLast) {
        console.log('\n故事完成!使用的模型:', chunk.model)
      }
    }
  }
  catch (error) {
    console.error('流式聊天请求失败:', error)
  }
}

streamChat()

添加和使用Agent功能(函数调用)

UnifiedAI支持函数调用,可以让AI助手执行特定操作:

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'
import { z } from 'zod'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 添加函数(使用Zod验证参数)
ai.addFunction({
  name: 'getCurrentWeather',
  description: '获取指定城市的当前天气',
  parameters: z.object({
    city: z.string({
      description: '城市名称'
    })
  }),
  executor: async (params) => {
    // 实际实现会调用天气API
    return {
      temperature: 25,
      unit: 'celsius',
      condition: '晴天',
      city: params.city
    }
  }
})

// 使用带函数调用的聊天
async function chatWithFunctions() {
  const response = await ai.unifiedChat('北京今天的天气怎么样?')
  console.log('AI回复:', response.content)
  
  // 检查是否有函数调用
  if (response.functionCalls && response.functionCalls.length > 0) {
    console.log('函数调用:', response.functionCalls)
  }
}

chatWithFunctions()

多个函数和链式调用

可以添加多个函数并支持链式调用:

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'
import { z } from 'zod'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel, {
  maxRecursionDepth: 3, // 设置最大递归深度
})

// 添加天气查询函数
ai.addFunction({
  name: 'getWeather',
  description: '获取指定城市的天气信息',
  parameters: z.object({
    city: z.string({
      description: '城市名称',
    }),
  }),
  executor: async (params) => {
    const { city } = params
    // 模拟API调用
    return {
      city,
      temperature: Math.floor(Math.random() * 30) + 5,
      condition: ['晴朗', '多云', '小雨', '大雨'][Math.floor(Math.random() * 4)],
      humidity: Math.floor(Math.random() * 60) + 40,
    }
  },
})

// 添加温度转换函数
ai.addFunction({
  name: 'convertTemperature',
  description: '将温度从摄氏度转换为华氏度,或从华氏度转换为摄氏度',
  parameters: z.object({
    temperature: z.number({
      description: '要转换的温度值',
    }),
    fromUnit: z.enum(['C', 'F'], {
      description: '原始温度单位 (C 或 F)',
    }),
  }),
  executor: async (params) => {
    const { temperature, fromUnit } = params
    if (fromUnit === 'C') {
      const fahrenheit = (temperature * 9 / 5) + 32
      return { result: fahrenheit, unit: 'F' }
    }
    else {
      const celsius = (temperature - 32) * 5 / 9
      return { result: celsius, unit: 'C' }
    }
  },
})

// 链式调用示例
async function chainedFunctionCalls() {
  // AI将自动调用getWeather获取温度,然后调用convertTemperature进行转换
  const response = await ai.unifiedChat(
    '北京今天的温度是多少摄氏度?请同时告诉我对应的华氏度。'
  )
  console.log('AI回复:', response.content)
}

chainedFunctionCalls()

JSON响应格式

可以指定AI返回JSON格式的数据:

import { GeminiModel, UnifiedAI, ResponseFormat } from '@oukek/unified-ai'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 请求JSON格式响应
async function getJsonResponse() {
  const jsonPrompt = `请提供北京和上海的基本信息,包括人口、面积、所属国家和建城时间`

  const response = await ai.unifiedChat(
    jsonPrompt,
    { responseFormat: ResponseFormat.JSON }
  )

  // 响应已自动解析为JavaScript对象
  console.log('城市信息:', response.content)
  
  // 可以直接访问对象属性
  if (typeof response.content === 'object' && response.content.cities) {
    console.log('北京人口:', response.content.cities['北京'].population)
  }
}

getJsonResponse()

使用回调函数监控过程

import { GeminiModel, UnifiedAI, AgentEventType } from '@oukek/unified-ai'

// 初始化
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY
})
const ai = new UnifiedAI(geminiModel)

// 定义回调函数
const callback = (state: string, data: any) => {
  const timestamp = new Date().toISOString()
  
  switch (state) {
    case AgentEventType.RESPONSE_START:
      console.log(`[${timestamp}] 🟢 开始回答: "${data.prompt}"`)
      break

    case AgentEventType.FUNCTION_CALL_START:
      console.log(`[${timestamp}] 🔄 调用函数: ${data.functionCalls.map((f: any) => f.name).join(', ')}`)
      break

    case AgentEventType.FUNCTION_CALL_END:
      console.log(`[${timestamp}] ✅ 函数执行完成: ${data.functionCalls.map((f: any) => f.name).join(', ')}`)
      break

    case AgentEventType.RESPONSE_CHUNK:
      // 流式响应的每个块,这里不打印避免干扰输出
      break

    case AgentEventType.RESPONSE_END:
      const content = typeof data.response.content === 'string' 
        ? data.response.content 
        : JSON.stringify(data.response.content)
      console.log(`[${timestamp}] 🏁 回答完成,长度: ${content.length}字符`)
      break

    case AgentEventType.ERROR:
      console.error(`[${timestamp}] ❌ 错误:`, data.error)
      break
      
    case AgentEventType.RECURSION_START:
      console.log(`[${timestamp}] 🔁 开始递归调用`)
      break
      
    case AgentEventType.RECURSION_END:
      console.log(`[${timestamp}] 🔁 递归调用结束`)
      break
  }
}

// 使用回调函数
async function chatWithCallback() {
  const response = await ai.unifiedChat('帮我查询深圳的天气', undefined, callback)
  console.log('AI回复:', response.content)
}

chatWithCallback()

Model Context Protocol (MCP) 支持

UnifiedAI支持通过MCP协议与外部工具(如文件系统)进行交互:

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
import { z } from 'zod'

async function setupAIWithMCP() {
  // 创建基础模型实例
  const baseModel = new GeminiModel({
    apiKey: process.env.GEMINI_API_KEY
  })
  
  // 创建UnifiedAI实例
  const ai = new UnifiedAI(baseModel, {
    maxRecursionDepth: 5,
  })
  
  // 创建MCP客户端
  const mcpClient = new Client({ name: 'mcp-client', version: '1.0.0' })
  const transport = new StdioClientTransport({
    command: 'npx',
    args: [
      "-y",
      "@modelcontextprotocol/server-filesystem",
      "/path/to/workspace",
    ],
  })
  
  // 连接到传输层
  await mcpClient.connect(transport)
  
  // 将MCP客户端添加到UnifiedAI
  ai.useMcp(mcpClient)
  
  // 添加自定义函数
  ai.addFunction({
    name: 'randomNumber',
    description: '获取指定范围内的随机数',
    parameters: z.object({
      min: z.number().optional(),
      max: z.number().optional(),
    }),
    executor: async ({ min = 1, max = 100 }) => {
      return Math.floor(Math.random() * (max - min + 1)) + min
    },
  })
  
  // 使用MCP功能与文件系统交互
  const response = await ai.unifiedChat(
    '创建一个test.txt文件,写入一个10-100之间的随机数,然后读取并告诉我内容'
  )
  
  console.log('AI回复:', response.content)
  
  // 关闭MCP连接
  await mcpClient.close()
}

setupAIWithMCP()

自定义模型参数

可以为模型设置自定义参数:

import { GeminiModel, UnifiedAI } from '@oukek/unified-ai'

// 初始化Gemini模型
const geminiModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY,
  model: 'gemini-1.5-pro',
  // 设置生成配置
  generationConfig: {
    temperature: 0.2,
    topP: 0.8,
    topK: 40,
    maxOutputTokens: 2048,
  },
  // 设置安全配置
  safetySettings: [
    {
      category: 'HARM_CATEGORY_HARASSMENT',
      threshold: 'BLOCK_MEDIUM_AND_ABOVE',
    },
    {
      category: 'HARM_CATEGORY_HATE_SPEECH',
      threshold: 'BLOCK_MEDIUM_AND_ABOVE',
    },
  ],
})

const ai = new UnifiedAI(geminiModel)

// 使用聊天时也可以覆盖某些参数
async function chatWithCustomParams() {
  const response = await ai.unifiedChat(
    '写一个短篇科幻故事',
    {
      temperature: 0.8, // 使用较高的温度以增加创意性
      maxTokens: 1000, // 限制输出长度
    }
  )
  console.log(response.content)
}

chatWithCustomParams()

API文档

BaseModel

所有AI模型的基类,定义了统一的接口。

abstract class BaseModel {
  getModel(model?: string): string
  
  abstract unifiedChat<T extends ChatOptions | undefined>(
    prompt: string,
    options?: T
  ): Promise<ResponseTypeForOptions<T>>
  
  abstract unifiedChatStream<T extends ChatOptions | undefined>(
    prompt: string,
    options?: T
  ): AsyncGenerator<StreamChunkTypeForOptions<T>, void, unknown>
  
  abstract getDefaultModel(): string
  
  supportsTools(_model?: string): boolean
  
  supportsSystemMessages(_model?: string): boolean
  
  convertToolsFormat(tools: AgentFunctionSchema[]): any
}

方法

  • getModel(model?: string): string
    获取模型名称。

  • unifiedChat(prompt: string, options?: ChatOptions): Promise<ChatResponse>
    发送聊天请求并获取响应。

  • unifiedChatStream(prompt: string, options?: ChatOptions): AsyncGenerator<ChatStreamChunk>
    以流的形式获取聊天响应。

  • getDefaultModel(): string
    获取默认模型名称。

  • supportsTools(_model?: string): boolean
    检查模型是否原生支持工具/函数调用。

  • supportsSystemMessages(_model?: string): boolean
    检查模型是否原生支持系统消息。

  • convertToolsFormat(tools: AgentFunctionSchema[]): any
    将统一格式的工具转换为模型特定的格式。

UnifiedAI

UnifiedAI类扩展了BaseModel,并添加了Agent功能支持。

构造函数

constructor(
  baseModel: BaseModel, 
  options?: {
    functions?: AgentFunction[];
    maxRecursionDepth?: number;
    [key: string]: any;
  }
)

方法

  • unifiedChat(prompt: string, options?: ChatOptions, callback?: AgentCallback): Promise<EnhancedChatResponse>
    发送聊天请求并获取响应,支持函数调用。

  • unifiedChatStream(prompt: string, options?: ChatOptions, callback?: AgentCallback): AsyncGenerator<ChatStreamChunk>
    以流的形式获取聊天响应,支持函数调用。

  • getModel(): string
    获取底层模型信息。

  • addFunction(func: AgentFunction): void
    添加一个Agent功能(函数)。

  • addFunctions(functions: AgentFunction[]): void
    添加多个Agent功能(函数)。

  • useMcp(client: Client): this
    设置MCP客户端,支持与外部工具交互。

GeminiModel

Gemini模型的实现类。

构造函数

constructor(options: {
  apiKey: string;
  model?: string;
  generationConfig?: GenerationConfig;
  safetySettings?: SafetySetting[];
})

类型定义

ChatOptions

interface ChatOptions {
  /** 聊天历史记录 */
  history?: ChatMessage[];
  /** 模型名称 */
  model?: string;
  /** 温度参数 (0-1),控制输出的随机性 */
  temperature?: number;
  /** 最大输出长度 */
  maxTokens?: number;
  /** 响应格式 */
  responseFormat?: ResponseFormat;
  /** 系统消息 */
  systemMessage?: string;
  /** 自定义模型参数 */
  [key: string]: any;
}

ChatMessage

interface ChatMessage {
  /** 消息角色 */
  role: ChatRole;
  /** 消息内容 */
  content: string;
}

ChatRole

enum ChatRole {
  USER = 'user',
  ASSISTANT = 'assistant',
  SYSTEM = 'system',
}

ResponseFormat

enum ResponseFormat {
  /** 文本格式 */
  TEXT = 'text',
  /** JSON格式 */
  JSON = 'json',
}

AgentFunction

interface AgentFunction {
  /** 函数名称 */
  name: string;
  /** 函数描述 */
  description?: string;
  /** 函数参数模式(可使用zod进行定义) */
  parameters: z.ZodObject<any> | Record<string, any>;
  /** 函数执行器 */
  executor?: (params: Record<string, any>) => Promise<any>;
}

FunctionCall

interface FunctionCall {
  /** 函数名称 */
  name: string;
  /** 函数参数 */
  arguments: Record<string, any>;
  /** 函数执行结果 */
  result?: any;
}

ChatResponse

interface ChatResponse<T extends ResponseFormat | undefined = undefined> {
  /** 
   * 响应内容
   * 当 responseFormat 为 JSON 时,这是一个解析后的 JSON 对象
   * 当 responseFormat 为 TEXT 或未指定时,这是一个字符串
   */
  content: ContentType<T>;
  /** 标识响应内容是否为JSON对象 */
  isJsonResponse: T extends ResponseFormat.JSON ? true : boolean;
  /** 使用的模型 */
  model: string;
  /** 消耗的token数量 */
  usage?: {
    promptTokens?: number;
    completionTokens?: number;
    totalTokens?: number;
  };
  /** 额外信息,可以存储原始用户提问等元数据 */
  additionalInfo?: {
    userPrompt?: string;
    [key: string]: any;
  };
}

EnhancedChatResponse

interface EnhancedChatResponse<T extends ResponseFormat | undefined = undefined> extends ChatResponse<T> {
  /** 函数调用详情 */
  functionCalls?: FunctionCall[];
}

AgentEventType

enum AgentEventType {
  /** AI响应开始 */
  RESPONSE_START = 'response_start',
  /** AI响应结束 */
  RESPONSE_END = 'response_end',
  /** AI响应片段 */
  RESPONSE_CHUNK = 'response_chunk',
  /** 函数调用开始 */
  FUNCTION_CALL_START = 'function_call_start',
  /** 函数调用结束 */
  FUNCTION_CALL_END = 'function_call_end',
  /** 递归调用开始 */
  RECURSION_START = 'recursion_start',
  /** 递归调用结束 */
  RECURSION_END = 'recursion_end',
  /** 发生错误 */
  ERROR = 'error',
}

回调函数数据类型

回调函数接收不同事件的对应数据类型如下:

/**
 * 响应开始事件数据
 */
interface ResponseStartEventData {
  /** 提示内容 */
  prompt: string
  /** 请求选项 */
  options?: ChatOptions
}

/**
 * 响应结束事件数据
 */
interface ResponseEndEventData {
  /** 响应结果 */
  response: ChatResponse<any>
}

/**
 * 响应块事件数据
 */
interface ResponseChunkEventData {
  /** 响应块 */
  chunk: {
    content: string | any
    isJsonResponse: boolean
    isLast: boolean
    model?: string
  }
}

/**
 * 函数调用开始事件数据
 */
interface FunctionCallStartEventData {
  /** 函数调用列表 */
  functionCalls: FunctionCall[]
}

/**
 * 函数调用结束事件数据
 */
interface FunctionCallEndEventData {
  /** 函数调用列表(包含结果) */
  functionCalls: FunctionCall[]
}

/**
 * 递归开始事件数据
 */
interface RecursionStartEventData {
  /** 初始内容 */
  initialContent: string | Record<string, any>
  /** 函数调用列表 */
  functionCalls: FunctionCall[]
  /** 初始响应 */
  initialResponse?: ChatResponse<any>
  /** 深度 */
  depth: number
}

/**
 * 递归结束事件数据
 */
interface RecursionEndEventData {
  /** 最终内容 */
  finalContent: any
  /** 函数调用列表 */
  functionCalls: FunctionCall[]
  /** 响应 */
  response: ChatResponse<any>
  /** 深度 */
  depth: number
  /** 完成执行的函数调用 */
  completedFunctionCalls: FunctionCall[]
}

/**
 * 错误事件数据
 */
interface ErrorEventData {
  /** 提示内容 */
  prompt?: string
  /** 请求选项 */
  options?: ChatOptions
  /** 错误信息 */
  error: string
  /** 出错的函数调用 */
  functionCall?: FunctionCall
}

/**
 * 回调函数类型
 */
type AgentCallback = <T extends keyof AgentEventDataMap>(
  state: T,
  data: AgentEventDataMap[T]
) => void | Promise<void>

最佳实践

错误处理

始终使用try/catch捕获可能的错误:

async function safeChat() {
  try {
    const response = await ai.unifiedChat('...')
    // 处理响应
  } catch (error) {
    // 处理错误
    console.error('聊天出错:', error.message)
    // 可能的重试逻辑
  }
}

函数调用参数验证

使用zod进行参数验证可以增加代码健壮性:

import { z } from 'zod'

ai.addFunction({
  name: 'sendEmail',
  description: '发送电子邮件',
  parameters: z.object({
    to: z.string().email({ message: '请输入有效的邮箱地址' }),
    subject: z.string().min(1, { message: '主题不能为空' }),
    body: z.string().optional(),
  }),
  executor: async (params) => {
    // 由于有zod验证,这里可以安全地使用参数
    return { success: true, messageId: '...' }
  }
})

模型选择

根据任务复杂性选择适当的模型:

// 简单任务使用更快的模型
const fastModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY,
  model: 'gemini-1.5-flash'
})

// 复杂任务使用更强大的模型
const powerfulModel = new GeminiModel({
  apiKey: process.env.GEMINI_API_KEY,
  model: 'gemini-1.5-pro'
})

// 根据任务切换模型
const fastAI = new UnifiedAI(fastModel)
const powerfulAI = new UnifiedAI(powerfulModel)

开发

# 安装依赖
pnpm install

# 构建
pnpm build

# 运行测试
pnpm test

许可证

MIT

README fetched from the repository 7 days ago. The original is authoritative.

Questions

About Unified AI

How do I install Unified AI?

No package has been published for this server, so install it from source: clone the repository and follow its README.

Is Unified AI safe to use with an AI agent?

Its trust score is 41 out of 100 (fair). It passes 4 of the 6 static security checks run so far; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Unified AI still maintained?

The last commit was a year ago. That's long enough that you should check whether the maintainer is responding to issues before depending on it.