从0到1开发一个AI助手

为什么会有这篇文章

在小红书上经常会刷到"有没有这样的工具"的帖子,其实大部分都是相对比较简单的,于是我在想与其实现这些小工具,不如打造一个小工具的工厂,用户可以根据自己的想法生成各种小工具,岂不美哉。于是就有了百变AI助手 ,之所以叫百变AI助手是因为它可以变成任何你想要的样子,为了推广自己的APP也为了分享我在做这款APP的时候学到的一点点东西,所以有了这篇文章。

你能从这篇文章中得到什么

通过这篇文章,你将学会:

  • AI助手核心功能:聊天对话、联网搜索、工具调用
  • 大模型基础使用:API调用、提示词工程、流式输出
  • Agent开发技能:工具调用机制、多轮对话管理
  • MCP协议集成:标准化工具连接,快速扩展AI能力

最终目标:构建一个能够实时联网搜索、调用各种工具的智能AI助手。

前置知识

大语言模型(LLM)

大语言模型(Large Language Model,简称LLM)是参数量巨大的深度学习模型,特别是在自然语言处理领域表现突出的神经网络模型。这些模型通过在海量文本数据上进行训练,学会了理解和生成人类语言。

简单来说,它是一个听得懂人话,可以按照你的要求输出内容的服务。

提示词(Prompt)

是你和大模型对话时的"指令语言",简单来说就是你告诉AI要做什么以及怎么做的话

"今天天气怎么样?" ← 这是提示词

"帮我写个邮件" ← 这也是提示词

"你好" ← 这同样是提示词

提示词分为系统提示词和用户的提示词。

系统提示词

作为我们的助手我们可以内置一部分提示词,在每一次请求大模型API接口的时候传递,这样它在回答的时候就会按照我们的要求实现。

假设你在打造一个翻译软件,我们明确AI负责翻译即可,而不是随意回答用户的问题,那么我们可以在请求时候要求AI只输出结果:

arduino 复制代码
// ✅ 有系统提示词
system: "你是专业翻译助手,只输出翻译结果..."
messages: [
    { role: "user", content: "翻译成中文:Hello World" }
]
// 结果:你好世界

我们不仅可以在系统提示词中要求AI按照我们的需求输出,同时也可以告诉AI一些额外的信息,这样AI在回复信息的时候可以有更多额外的信息:

swift 复制代码
// ✅ 有系统提示词
system: "你是百变AI助手 (Morphix AI),一款智能AI助手应用\n当前日期:2025-08-12"

用户提问:你好,今天的日期

// 结果:
2025-08-12 

用户提示词

用户提问的问题。

实战:构建基础AI助手

到这里我们已经可以开始一个简单的案例了。

我们使用 openrouter 为案例,openrouter 提供了免费的模型供我们直接使用,openrouter 提供各种收费模型、免费模型可以直接使用,支持的大模型非常全,国内的国外的应有尽有。

为了我们能够快速开始,我这里提供了api key,可以直接使用无需注册:

复制代码
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

大家别心疼,放心使用因为我已经限制了用量,只能使用免费的模型。

如果是线上的项目的apiKey,建议放采用密钥管理工具,避免密钥的泄露,在开发的过程中发现了一个非常好用的密钥管理工具,可以统一管理密钥,不需要在各种项目中复制来复制去,Infisical

话不多说我们直接开始。

掌握基础的调用

php 复制代码
import OpenAI from 'openai';
// 初始化openai 客户端
const openai = new OpenAI({
  baseURL: 'https://openrouter.ai/api/v1',
  apiKey: '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',
});

async function main() {
  const completion = await openai.chat.completions.create({
    model: 'https://openrouter.ai/api/v1',
    // 用户发送的信息,是一个数组
    messages: [
      {
        role: 'system',
        // 系统提示词
        content: '你是一个AI助手',
      },
      {
        role: 'user',
        content: 'who are you',
      },
    ],
  });

  console.log(completion.choices[0].message);
}

main();

多轮对话

php 复制代码
import OpenAI from 'openai';
// 初始化openai 客户端
const openai = new OpenAI({
  baseURL: 'https://openrouter.ai/api/v1',
  apiKey: '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',
});

async function main() {
  const completion = await openai.chat.completions.create({
    model: 'https://openrouter.ai/api/v1',
    // 用户发送的信息,是一个数组
    messages: [
      {
        role: 'system',
        // 系统提示词
        content: '你是一个AI助手',
      },
      {
        role: 'user',
        content: 'who are you',
      },
      {
        role: 'assistant',
        content: 'AI 的回答',
      },
      {
        role: 'user',
        content: '用户再一次的提问',
      },
    ],
  });

  console.log(completion.choices[0].message);
}

main();

看到这里你会发现 messages 随着用户的对话会越来越长,大模型按照输入和输出计费的,越到后面成本越高,但是好在大模型支持输入缓存,claude 输入缓存的价格是正常输入的 1/10,大部分模型会默认开启缓存,无需额外控制,但是 claude 的模型需要注意,需要主动声明开启缓存。

流式输出

AI的回答如果等到全部结束后再显示结果通常情况下会非常慢,如果说是深度思考的模型就更慢了。

所以大部分都是采用的流式输出,开启流式输出也非常简单,只需要增加参数stream: true即可,不过我们需要做一些额外的处理。

javascript 复制代码
import OpenAI from 'openai';
// 初始化openai 客户端
const openai = new OpenAI({
  baseURL: 'https://openrouter.ai/api/v1',
  apiKey: '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',
});

const stream = await openai.chat.completions.create({
  model: request.model || this.config.model,
  messages: [
    // 消息数组
  ],
  // 开启流式输出
  stream: true,
});

let fullContent = '';
for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content || '';
  if (content) {
    fullContent += content;
    console.log(content);
  }
}
console.log(fullContent);

利用上述知识我们已经能够构建以常规的AI助手应用了。

GitHub - paul-leo/ai-assistant-demo

这是我使用上述几个简单的api开发的一个AI助手demo,非常适合新手学习。

直接访问地址:ai-assistant-demo-theta.vercel.app/


恭喜你!看到这里你已经掌握了如何开发一个简单的AI助手,但是我知道你的求知欲不容许你只停留在这里。

我们已经可以创建一个简单的AI助手了,但是到目前为止也只能问答,但是距离Agent还差一个工具调用,有了工具调用AI就可以自动搜索,或者在需要时自动操作一些内容,例如 vibeCode 中的读写文件。

工具调用

为了让AI更加强大,我们可以告诉AI我们有哪些工具,这些工具是干什么的,工具需要哪些参数。

AI在返回结果的时候会自动识别可用的工具,然后告诉我们调用哪个工具,调用的参数是什么。

大部分的AI都提供了专门声明工具调用的字段。

工具调用原理

  1. 定义工具:告诉AI有哪些工具可用
  2. AI识别:AI判断是否需要使用工具
  3. 执行工具:我们代码执行具体工具功能
  4. 结果注入:将执行结果告诉AI
  5. 继续对话:AI基于工具结果回答用户

实战:添加搜索功能

基于上面的案例,我们拿 manus 中的一个工具声明为例: system-prompts-and-models-of-ai-tools/Manus Agent Tools & Prompt/tools.json

定义工具

json 复制代码
{
  "type": "function",
  "function": {
    "name": "info_search_web",
    "description": "使用搜索引擎搜索网页内容,用于获取最新信息或查找参考资料。",
    "parameters": {
      "type": "object",
      "properties": {
        "query": {
          "type": "string",
          "description": "搜索关键词,使用Google搜索风格,建议3-5个关键词。"
        },
        "date_range": {
          "type": "string",
          "enum": ["all", "past_hour", "past_day", "past_week", "past_month", "past_year"],
          "description": "(可选)搜索结果的时间范围筛选。可选值:all-全部,past_hour-过去1小时,past_day-过去1天,past_week-过去1周,past_month-过去1月,past_year-过去1年。"
        }
      },
      "required": ["query"]
    }
  }
}

用户提问

最近有什么重要的AI技术突破吗?特别是大模型方面的。

工具识别

AI在收到用户的请求和工具的定义后,会自动识别需要调用哪些工具,AI是否能够准确的识别到应该调用的工具,对于AI的能力是一种考验,有时候AI也不知道什么时候应该调用工具,什么时候不应该,我们可以在系统提示词中告诉AI应该如何处理,工具之间的关联性,同时工具的说明也能起到告知AI如何使用工具的作用。

json 复制代码
{
  "type": "function",
  "function": {
    "name": "info_search_web",
    "parameters": {
      "query": "AI技术突破 大模型 2024 最新发展",
      "date_range": "past_month"
    }
  }
}

工具执行

我们通过AI返回的信息,我们知道应该我们的工具调用的时候参数是什么,工具的执行过程AI不会帮我完成,还是需要通过我们在代码中预定义,在识别到AI返回应该调用的结果调用工具并返回结果,执行的结果我们后续会用到。

工具执行结果注入上下文

在工具执行完成后我们还是需要告诉AI工具的结果是什么,所以我们还是需要注入上下文然后再次向AI发起请求,AI就会知道更多额外的信息,同时知道下一步应该做什么。

结果注入上下文后的消息如下:

python 复制代码
{
  "model": "qwen-coder",
  "messages": [
    // 用户发起提问
    {
      "role": "user",
      "content": "2024年AI发展趋势如何?"
    },
    {
      "role": "assistant",
      // content
      "content": null,
      // AI 识别的调用
      "tool_calls": [
        {
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "info_search_web",
            "arguments": "{"query": "2024 AI发展趋势 人工智能", "date_range": "past_year"}"
          }
        }
      ]
    },
    // 工具执行的结果,注入上下文
    {
      "role": "tool",
      "tool_call_id": "call_abc123",
      "content": "{"success": true, "results": ["2024年生成式AI技术迎来重大突破,多模态大语言模型成为主流...", "AI在医疗、教育、金融等领域的应用加速普及..."], "sources": ["techcrunch.com", "arxiv.org", "openai.com"]}"
    }
  ],
  // 工具的定义,在每一次请求都应该传入
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "info_search_web",
        "description": "使用搜索引擎搜索网页内容,用于获取最新信息或查找参考资料。",
        "parameters": {
          "type": "object",
          "properties": {
            "query": {"type": "string", "description": "搜索关键词"},
            "date_range": {"type": "string", "enum": ["all", "past_hour", "past_day", "past_week", "past_month", "past_year"]}
          },
          "required": ["query"]
        }
      }
    }
  ]
}

我们回顾一下整个过程:

css 复制代码
graph TD
    A[用户提问] --> B[AI分析是否需要工具]
    B -->|需要| C[AI返回工具调用指令]
    B -->|不需要| F[直接回答]
    C --> D[代码执行具体工具]
    D --> E[将结果注入上下文]
    E --> G[AI基于工具结果回答]
    G --> H[返回最终答案]

我们利用上述能力给我们的AI增加一个搜索工具。

具体实现可查看:github.com/paul-leo/ai...

MCP (Model Context Protocol)

我们现在只有一个工具,但是随着系统的复杂性,工具越来越多,假设我们今天要让AI具备搜索的功能,明天我们又想给AI增加一个可以查询地图的功能,这些功能以前可能已经有了,但是它们是以API的形式提供服务,如果这样的话只能由开发者将这些功能添加到AI助手中,这个世界上有那么多的api,怎么可能集成得完呢。

于是聪明的工程师想到了一个办法,提供的服务的人将服务封装成一个工具包,给这些工具包增加一些描述,AI助手能够很方便的拿到一份工具包的说明书,里面有什么工具,如何使用等信息。

当用户在发起请求的时,AI助手就可以通过工具调用完成各种各样的任务。

MCP是什么

MCP 是由 Anthropic 于 2024年11月25日开源发布的一个开放标准协议,全称为"模型上下文协议"(Model Context Protocol)。它是一个用于连接AI助手与数据存储系统的统一标准,包括内容存储库、业务工具和开发环境等。 Introducing the Model Context Protocol

简单来说,MCP 就像是AI应用的"USB-C端口"。就像USB-C为设备连接各种外设提供了标准化方式一样,MCP为AI模型连接不同数据源和工具提供了标准化方式。

MCP 是一个协议,一头连接的是MCP Client(AI助手,Agent),一头连接的是服务(例如:AI Search MCP、Figma MCP)。

有这样一个标准协议,提供服务的人知道如何将其封装,MCP Client 知道能够去使用,对于用户来说大大的简化了扩展AI助手的方法,简直是一举三得。

对于我们开发AI助手来说我们可以内置一部分MCP工具,例如上面的搜索功能已经有了现成的mcp server。

为了演示MCP和工具调用的区别,我们增加一个AliMap的MCP,这样当我们询问地理位置或者导航时能够给我们实时的信息。

MCP如何使用

1. 连接MCP服务

这里需要用到 @modelcontextprotocol/sdk,是mcp的官方sdk,用于客户端连接和编写mcp服务端。

javascript 复制代码
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';

async function getAMapMcpClient() {
    const client = new Client({
        name: 'name',
        version: '1.0.0',
    });
    const sseTransport = new SSEClientTransport(
        new URL('https://mcp.amap.com/sse?key=YOUR_AMAP_KEY_HERE')
    );
    await client.connect(sseTransport);
    return client;
}

注意:由于高德地图域名跨域的限制,在浏览器端使用MCP会失败,我们可以将MCP的调用放在我们自己的服务器,或者使用Next.js这样的全栈开发框架来解决跨域问题。

复制代码

通过获取工具声明我们就能知道这个工具包中有哪些工具,这些工具如何使用,随后我们要将这个信息告诉LLM。

2. 获取并转换工具格式

OpenAI 和 Claude 支持的格式有所区别,mcp 默认的工具声明的格式是 Claude 的,我们用的是 OpenAI 的sdk,所以我们需要先将工具的格式转换成 OpenAI sdk 工具的格式。

javascript 复制代码
export async function getAMapTools() {
    const client = await getAMapMcpClient();
    const tools = await client.listTools();
    if (!tools || !Array.isArray(tools)) {
        return [];
    }
    const openaiTools = tools.map((tool: IMcpTool) =>
        convertToolFormat(tool, 'amap')
    );
    console.log(openaiTools);
    return openaiTools;
}

// 格式转换工具
function convertToolFormat(
    tool: IMcpTool,
    mcpName: string
): OpenAI.Chat.Completions.ChatCompletionTool {
    return {
        type: 'function',
        function: {
            // 这里我们在工具前面增加了mcp的名字,这样可以避免名字冲突,也可以用于执行阶段确认哪个mcp
            name: `${mcpName}__${tool.name}`,
            description: tool.description,
            parameters: {
                type: 'object',
                properties: tool.inputSchema.properties,
                required: tool.inputSchema.required,
            },
        },
    };
}

3. 请求LLM

MCP通常会自己处理具体的调用,我们只需要知道要执行这个工具,执行完成后告诉我们结果即可。

4. 执行工具

当我们识别到LLM返回的结果中包含对应的mcp工具时我们需要处理工具调用。

typescript 复制代码
export async function execMcpTool(toolName: string, arguments: any) {
    const client = await getAMapMcpClient();
    const result = await client.callTool({
        name: toolName,
        arguments: arguments,
    });
    return result;
}

5. 再次发起请求

将执行结果添加到上下文中然后自动发起下一次请求。

到这里我们已经可以学会了如何给助手添加 MCP Server,如果我们想扩展我们AI助手也可以自己实现一些mcp,不仅能够很方便将AI助手的功能拆分成不同的模块,在百变AI助手中产品经理和程序员角色就是通过不同的提示词和不同的mcp工具集来实现的。

最后看看我们的成果: 现在AI助手还不能获取地理位置,后续我们可以编写一个获取用户位置的mcp让AI自动返回和用户相关的信息。


如果你也对AI感兴趣可以关注我,我会后续继续更新,我们一起将上述的AI助手打造成一个更强大的工具,在后续打算集成自动化能够帮助我们自动完成一些繁杂的任务。

如果你对百变AI助手好奇不妨下载一个试试:

安卓Morphix AI - Shape the Infinite, Define Your Reality

iOS百变AI助手-创建你的专属app

如果你对这个项目感兴趣,希望能够一起开发,随时欢迎!

相关推荐
gAlAxy...16 分钟前
深入理解 Cookie 与 Session —— Web 状态保持详解与实战
前端
初学小刘20 分钟前
电商双11美妆数据分析
人工智能
专注VB编程开发20年23 分钟前
c#,vb.net全局多线程锁,可以在任意模块或类中使用,但尽量用多个锁提高效率
java·前端·数据库·c#·.net
JarvanMo27 分钟前
Google Connect 8月14日纪实
前端
熊猫钓鱼>_>34 分钟前
数据挖掘常用公开数据集
人工智能·数据挖掘
新智元43 分钟前
GPT-4o替代爹味GPT-5!奥特曼光速滑跪,OpenAI连夜回滚「赛博舔狗」
人工智能·openai
新智元43 分钟前
马斯克痛失xAI大将!Grok 4缔造者突然离职,长文曝最燃创业内幕
人工智能·openai
Dm_dotnet1 小时前
如何更好地使用AI编程?
人工智能
猩猩程序员1 小时前
Go 1.24 全面拥抱 Swiss Table:让内置 map 提速 60% 的秘密
前端
1024小神1 小时前
vue3 + vite项目,如果在build的时候对代码加密混淆
前端·javascript