从零到一:开发AI应用程序的需求分析和可行性验证

释放双眼,带上耳机,听听看~!
了解如何从零到一开发AI应用程序,包括需求分析和可行性验证,介绍了Dify和Coze平台,支持快速构建生成式AI应用和聊天机器人,帮助开发者验证效果和可行性。
👉🏻 拥抱变革,紧跟时代技术的变革会让围绕着这些技术发展而来的工作模式产生巨大的冲击,每一次传统工作模式的颠覆,都会在淘汰掉原有落后工作岗位的同时,创造大量新的工作机会。而这一切,都将在潜移默化中进行,无法控制。

引言

过去的一年算是AI元年,随着ChatGPT横空出世,如何开发AI应用程序成为许多开发者的关注点。接下来我们会从需求分析、可行性验证、模型与方向选择、开发细节等多方面来介绍如何从零到一开发AI应用程序。

LLM(“大语言模型”(large language model,LLM))

从根本上始终要做的是,针对它得到的任何文本产生“合理的延续”。这里所说的“合理”是指,“人们在看到诸如数十亿个网页上的内容后,可能期待别人会这样写”。

从零到一:开发AI应用程序的需求分析和可行性验证

需求分析

在开发 AI 应用程序之前,需要进行以下几方面的需求分析:

1. 目标用户分析:

    ● 确定目标用户群体的具体特征,包括年龄、行业、使用场景等。

    ● 分析用户当前的痛点和需求,了解他们希望 AI 系统能解决的问题。

2. 应用场景分析:

    ● 细化 AI 系统可能应用的具体场景,如辅助决策、自动化任务、个性化推荐等。

    ● 分析各场景下的数据来源、输入输出要求、性能指标等。

3. 技术可行性分析:

    ● 评估当前AI技术在所需功能、数据处理、性能方面的成熟度。

    ● 确定系统架构和关键技术要点,如机器学习模型、自然语言处理等。

4. 商业可行性分析:

    ● 分析市场规模、竞争格局、商业模式等,评估产品的市场前景。

    ● 测算系统开发和运营成本,并预测潜在收益,确定项目的投资回报。

5. 法律合规性分析:

    ● 了解行业监管政策和隐私保护法规,确保系统设计符合相关要求。

    ● 分析系统可能涉及的知识产权、伦理道德等问题。

可行性验证

Dify和Coze等平台提供了强大的调试功能,可帮助您快速进行POC验证,可以在编写代码之前就可以对效果做出预判。

Dify

Dify是一款开源的大语言模型(LLM)应用开发平台。它融合了后端即服务(Backend as Service)和LLMOps的理念,使开发者可以快速搭建生产级的生成式AI应用。Dify提供了健全的应用模板和编排框架,用户可以基于这些模板快速构建大型语言模型驱动的生成式AI应用,并且可以随时按需无缝扩展,驱动业务增长。此外,Dify还支持通过可声明式的YAML文件定义AI应用的各个方面,包括Prompt、上下文等。Dify的界面直观,结合了AI工作流、RAG管道、代理能力、模型管理和可观测性功能,让用户从原型到生产的过程更加高效。

Coze

Coze是字节跳动推出的一个新一代AI应用和聊天机器人开发平台。它专为开发下一代AI chatbot而设计,无论用户是否有编程经验,都可以在Coze平台上快速创建各种类型的聊天机器人并将其部署到不同的社交平台和消息应用中。Coze支持30秒无代码生成AI Bot,集成了超过60种不同的插件,覆盖新闻阅读、旅行规划、生产力工具等多个领域。此外,Coze还提供了可视化的工作流支持,可以对插件、大语言模型、代码块等功能进行组合,从而实现复杂、稳定的业务流程编排。

总结来说,Dify更侧重于提供一个开源的LLMOps平台,帮助开发者快速构建和部署生成式AI应用,而Coze则专注于提供一个易用的、无需编程经验的平台,用于快速创建和部署各种聊天机器人和AI应用,非常合适前期的可行性和效果验证。

Dify和Coze也支持开放接口,POC期间也可以考虑直接使用平台提供的接口能力

模型与方向选择

在选择AI大模型供应商时,需要考虑多个因素,以确保选型的合理性和长期合作的可行性。以下是详细的考虑因素:

  1. 模型性能和能力:

    • 通用能力和泛化能力:评估模型在不同任务和领域上的表现,包括语言理解、逻辑推理、数值计算等。
    • 鲁棒性和跨域性能:检查模型在各种数据集和场景中的稳定性和适应性。
    • 多语言能力:如果项目需要处理多种语言,供应商提供的模型是否具备这一能力。
  2. 技术和研发能力:

    • 全栈大模型训练与研发能力:供应商是否具备从数据构建到模型训练、微调和优化的全方位技术能力。
    • 业务场景落地经验:供应商在实际业务场景中应用模型的经验和案例。
  3. 安全性和可解释性:

    • AI安全治理举措:供应商是否有完善的安全管理体系和措施,确保模型的安全使用。
    • 可解释性:模型的决策过程是否透明,能够为用户提供清晰的解释。
  4. 生态开放性:

    • 生态系统和合作伙伴:供应商是否拥有强大的生态系统,能够与其他工具和平台无缝集成。
    • 开放性和兼容性:供应商是否支持开放标准和协议,便于客户自定义和扩展。
  5. 服务和支持:

    • 试错成本低:供应商是否提供低成本的POC测试服务,降低企业选型风险。
    • 服务态度和响应速度:供应商在服务过程中的态度和响应速度,是否能满足企业的需求。
  6. 价格和成本效益:

    • 费用情况:供应商的价格是否合理,是否提供性价比高的解决方案。
    • 长期成本:除了初始投资,还需考虑后续的维护、升级和运营成本。
  7. 市场条件和竞争力:

    • 市场份额和品牌影响力:供应商在市场中的地位和品牌影响力,是否有足够的客户基础和行业认可。
    • 竞争对手和替代方案:评估供应商的竞争对手及其产品性能,确保所选模型具有明显优势。
从零到一:开发AI应用程序的需求分析和可行性验证 ## The “it” in AI models is the dataset.Trained on the same dataset for long enough, pretty much every model with enough weights and training time converges to the same point.—— James Betker

OpenAI 的工程师 James Betker 通过实验发现:模型的差异其实不是关键,决定性的是你的训练材料。只要有更多更好的语料,不管用什么模型,都会得到差不多的结果。

通过综合考虑以上因素,可以更全面地评估AI大模型供应商的综合实力和适用性,从而做出明智的选择。

选择合适的协议进行前后端开发

在开发AI应用程序时,前后端之间的通信是至关重要的。选择合适的协议可以影响应用程序的性能和扩展性。以下是一些常见的协议:

  1. HTTP协议:HTTP协议是最常用的协议之一,它基于客户端-服务器模型。它具有广泛的支持和成熟的生态系统,容易实现和调试。然而,由于HTTP是基于请求-响应模式,AI非流式返回速度缓慢,它可能不适用于实时性要求较高的AI应用程序例如聊天程序。
  2. WebSocket协议:WebSocket协议提供了双向通信的能力,适用于实时应用程序。它允许服务器主动向客户端推送数据,减少了不必要的请求。然而,相对于HTTP,WebSocket的实现和调试可能会更加复杂。
  3. SSE协议:SSE基于HTTP协议,实现简单,开发者可以轻松实现服务器向客户端的单向推送。然而,无法进行双向交互。

开发细节

Hello World

从零到一:开发AI应用程序的需求分析和可行性验证

流(Stream)

为什么需要流?

LLM是逐字输出的,模型越大响应越缓慢,聊天场景下非流式返回经常需要等待一分钟!相同情况下流式返回2秒就开始有chunk数据返回,在聊天等对实时性要求较高的场景下对用户更友好。

从零到一:开发AI应用程序的需求分析和可行性验证

需要自行处理chunk,示例代码,需要实时拼接

Disconnected from ws://127.0.0.1:3000
11:15:13
{"choices":[{"content_filter_results":{},"delta":{},"finish_reason":"stop","index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"?"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" today"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" you"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" assist"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" I"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" can"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":" How"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"!"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"Hello"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[{"content_filter_results":{},"delta":{"role":"assistant"},"finish_reason":null,"index":0}],"created":1716342249,"id":"chatcmpl-9RVJRQBCAMz74I5kAxpjDaonWwhY5","model":"gpt-4","object":"chat.completion.chunk","system_fingerprint":null}
9:44:10
{"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}]}
9:44:9
{"message": "Hello GPT"}
connected to ws://127.0.0.1:3000

如上面日志所示,chunk数据中的delta是我们主要关心的数据,需要在程序中动态拼接以达到动态聊天的目的。

Tools

你能想象到如果大模型可以接入互联网,且具备通过API的方式实现类似于ChatGPT的插件的数据能力后,它将会变的多强吗?

我们都知道大模型的知识库是截至到2021年9月份,这也就意味着在这个日期之后的知识它是一无所知的,尽管可以通过激发它的涌现能力让它具备一定的推理能力,但固有的知识是没办法涌现出来的,例如今天的天气,实时的股票价格等等。通过tools可以让LLM接入互联网甚至实时传感。

从零到一:开发AI应用程序的需求分析和可行性验证

我们可以看到tools数据格式遵循JSON Schema格式,可以同时声明多个具体。

JSON Schema是一个用于定义和验证JSON数据结构的标准化语言。它允许开发者描述JSON对象的结构、数据类型以及具体限制,从而实现对JSON数据的校验和验证。

从零到一:开发AI应用程序的需求分析和可行性验证

从零到一:开发AI应用程序的需求分析和可行性验证
本质上LLM只是声明要调用哪个工具并提供了调用的参数而已,工具的具体逻辑需要研发自行实现!

import OpenAI from "openai";
const openai = new OpenAI();


// Example dummy function hard coded to return the same weather
// In production, this could be your backend API or an external API
function getCurrentWeather(location, unit = "fahrenheit") {
  if (location.toLowerCase().includes("tokyo")) {
    return JSON.stringify({ location: "Tokyo", temperature: "10", unit: "celsius" });
  } else if (location.toLowerCase().includes("san francisco")) {
    return JSON.stringify({ location: "San Francisco", temperature: "72", unit: "fahrenheit" });
  } else if (location.toLowerCase().includes("paris")) {
    return JSON.stringify({ location: "Paris", temperature: "22", unit: "fahrenheit" });
  } else {
    return JSON.stringify({ location, temperature: "unknown" });
  }
}


async function runConversation() {
  // Step 1: send the conversation and available functions to the model
  const messages = [
    { role: "user", content: "What's the weather like in San Francisco, Tokyo, and Paris?" },
  ];
  const tools = [
    {
      type: "function",
      function: {
        name: "get_current_weather",
        description: "Get the current weather in a given location",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "The city and state, e.g. San Francisco, CA",
            },
            unit: { type: "string", enum: ["celsius", "fahrenheit"] },
          },
          required: ["location"],
        },
      },
    },
  ];


  const response = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: messages,
    tools: tools,
    tool_choice: "auto", // auto is default, but we'll be explicit
  });
  const responseMessage = response.choices[0].message;

  // Step 2: check if the model wanted to call a function
  const toolCalls = responseMessage.tool_calls;
  if (responseMessage.tool_calls) {
    // Step 3: call the function
    // Note: the JSON response may not always be valid; be sure to handle errors
    const availableFunctions = {
      get_current_weather: getCurrentWeather,
    }; // only one function in this example, but you can have multiple
    messages.push(responseMessage); // extend conversation with assistant's reply
    for (const toolCall of toolCalls) {
      const functionName = toolCall.function.name;
      const functionToCall = availableFunctions[functionName];
      const functionArgs = JSON.parse(toolCall.function.arguments);
      const functionResponse = functionToCall(
        functionArgs.location,
        functionArgs.unit
      );
      messages.push({
        tool_call_id: toolCall.id,
        role: "tool",
        name: functionName,
        content: functionResponse,
      }); // extend conversation with function response
    }
    const secondResponse = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: messages,
    }); // get a new response from the model where it can see the function response
    return secondResponse.choices;
  }
}


runConversation().then(console.log).catch(console.error);

使用SDK可以让我们更自动一点

通过工具方法openai.beta.chat.completions.runTools({…})可以自动调用JS方法并将返回结果发送回/chat/completions

import OpenAI from 'openai';

const client = new OpenAI();

async function main() {
  const runner = client.beta.chat.completions
    .runTools({
      model: 'gpt-3.5-turbo',
      messages: [{ role: 'user', content: 'How is the weather this week?' }],
      tools: [
        {
          type: 'function',
          function: {
            function: getCurrentLocation,
            parameters: { type: 'object', properties: {} },
          },
        },
        {
          type: 'function',
          function: {
            function: getWeather,
            parse: JSON.parse, // or use a validation library like zod for typesafe parsing.
            parameters: {
              type: 'object',
              properties: {
                location: { type: 'string' },
              },
            },
          },
        },
      ],
    })
    .on('message', (message) => console.log(message));

  const finalContent = await runner.finalContent();
  console.log();
  console.log('Final content:', finalContent);
}

async function getCurrentLocation() {
  return 'Boston'; // Simulate lookup
}

async function getWeather(args: { location: string }) {
  const { location } = args;
  // … do lookup …
  return { temperature, precipitation };
}

main();

// {role: "user",      content: "How's the weather this week?"}
// {role: "assistant", tool_calls: [{type: "function", function: {name: "getCurrentLocation", arguments: "{}"}, id: "123"}
// {role: "tool",      name: "getCurrentLocation", content: "Boston", tool_call_id: "123"}
// {role: "assistant", tool_calls: [{type: "function", function: {name: "getWeather", arguments: '{"location": "Boston"}'}, id: "1234"}]}
// {role: "tool",      name: "getWeather", content: '{"temperature": "50degF", "preciptation": "high"}', tool_call_id: "1234"}
// {role: "assistant", content: "It's looking cold and rainy - you might want to wear a jacket!"}
//
// Final content: "It's looking cold and rainy - you might want to wear a jacket!"

toolCall后面需要跟上role为tool且相同tool_call_id的返回

开发一个聊天程序

从零到一:开发AI应用程序的需求分析和可行性验证

使用的库

  • Fastify
  • ChatUI
  • OpenAI SDK

服务端代码示例

// Import the framework and instantiate it
import Fastify from 'fastify'

import OpenAI from "openai/index.mjs";

const openai = new OpenAI({
    apiKey: process.env.OPEN_AI_KEY,
    baseURL: 'https://ops-ai.ekuaibao.net/v1'
});


const fastify = Fastify({
  logger: true
})

// Declare a route
fastify.post('/', async function handler (request, reply) {
  console.log('request.body', request.body)
  const chatCompletion = await openai.chat.completions.create({
    messages: request.body.messages,
    model: 'gpt-3.5-turbo',
  });
  console.log('chatCompletion', chatCompletion)
  return {
    content: chatCompletion.choices[0].message.content
  }
})

// Run the server!
try {
  await fastify.listen({ port: 3000 })
} catch (err) {
  fastify.log.error(err)
  process.exit(1)
}

前端代码示例

import React, { useState } from "react";
import Chat, { Bubble, useMessages } from "@chatui/core";
import "@chatui/core/dist/index.css";
import axios from 'axios'

export default function App() {
  const { messages, appendMsg, setTyping } = useMessages([]);
  const [conversions, setConversions] = useState([]);

  async function handleSend(type, val) {
    if (type === "text" && val.trim()) {
      appendMsg({
        type: "text",
        content: { text: val },
        position: "right"
      });

      setTyping(true);

      const newConversions = [...conversions, { role: 'user', content: val }]
      console.log('newConversions', newConversions)
      setConversions(newConversions)

      const response = await axios.post('/', { messages: newConversions })

      setConversions([...conversions, { role: 'system', content: response.data.content }])

      console.log('conversions', conversions)

      appendMsg({
        type: "text",
        content: { text: response.data.content }
      });
    }
  }

  function renderMessageContent(msg) {
    const { content } = msg;
    return <Bubble content={content.text} />;
  }

  return (
    <Chat
      navbar={{ title: "智能助理" }}
      messages={messages}
      renderMessageContent={renderMessageContent}
      onSend={handleSend}
    />
  );
}

代码地址:github.com/ZhangBohan/…

最后

当LLM准确性不是问题,价格不是问题之后还有什么是问题?

从零到一:开发AI应用程序的需求分析和可行性验证

LLM的token计算是通过将输入文本拆分成更小的单元来实现的,这个过程称为Token化。Token可以是单词、字符、子词或符号等。在实际应用中,通常假设每4个字符对应一个token。

具体来说,计算token数量的方法如下:

  1. 输入文本:将需要处理的文本输入到计算器中。
  2. Token化:将输入文本拆分成token。这一步骤可以通过不同的算法完成,如BPE(Byte Pair Encoding)、WordPiece或Unigram Language Model等。
  3. 计算token数量:根据预设的规则(如每4个字符对应一个token),计算出总的token数量。

例如,如果输入文本为“Hello World”,则其token数量为:

  • “Hell” = 4个字符,约等于1个token
  • “o ” = 2个字符,约等于0.5个token
  • “Wor” = 3个字符,约等于0.75个token
  • “ld” = 3个字符,约等于0.75个token

总计:1 + 0.5 + 0.65 + 0.75 = 3个token。

综上所述,LLM的token计算主要依赖于输入文本的Token化过程,并假设每4个字符对应一个token。

参考:
johnhax.net/2023/lets-g…

www.sohu.com/a/383003045…

nonint.com/2023/06/10/…

github.com/openai/open…

book.douban.com/subject/364…

www.ruanyifeng.com/blog/2024/0…

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

5款AI写真工具推荐|程序员X小鹿分享

2024-8-15 13:21:00

AI教程

免费AI工具海报生成工具PosterGenius - 魔搭社区推荐

2024-8-16 7:40:00

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索