探索AI工具幕后,打造你自己的Agent应用

0. 前言

文本并不纯粹是一篇教学文章,我们会以制作一个智能的命令行工具为目标,在过程中穿插一些涉及到的行业中术语或扩展知识,以及列举相似功能AI软件的幕后实现,所以你也可以把本文当作一篇AI技术科普。
看完全文后,你将收获:

  • 市面上所有AI应用的幕后实现原理
  • 自制Agent框架/程序的能力
让我们一起准备迎接AGI时代的到来😆
🧠

前置知识: 什么是Agent?

👤 Agent = 自主性 + 环境交互 + 目标驱动

你可以把它想象成一个打游戏时的NPC:它能感知周围环境(比如看到玩家位置),自己做决策(决定攻击还是逃跑),然后执行动作(移动或开火)。整个过程不需要玩家操控,完全自主。

💡 核心特点:
  1. 感知 (Input):像传感器一样获取信息(比如摄像头数据、用户输入、数据库内容)。
  2. 决策 (Process):根据输入的内容,决定下一步要做什么事。
  3. 行动 (Output):对环境产生影响(比如发消息、控制机器人、执行命令)。
  4. 目标导向:所有行为都是为了完成某个任务(比如赢游戏、优化物流路线)。
🚀 和普通程序的区别

普通程序: 输入 → 处理 → 输出 (一次性)

Agent: 持续感知 → 动态决策 → 循环行动 (有AI动态做反馈循环)

就像你写了一个会自己不断做判断、调整策略的智能小助手!

1. 最终效果

我们最终的目标是实现一个简易的命令行助手,它会根据你下达的指令,尝试性的帮你实现你的目的 :
我还给它起了个名字: Commander🥰

出于教学目的,我已经尽量将这个程序做的简单,因此它的智能程度也比较有限,但相信我,看完全文,你一定会有所收获😉

配套代码已上传至Github:
https://github.com/bootun/commander

读完全篇内容之后,你所具备的知识量已经能够做基本的AI Agent应用了,你可以把下面这些问题当作课后作业,让你的Agent进行支持:

1. 帮我看下k8s中测2环境的xxx服务的最近100条日志,有没有报错?
2.帮我创建个Go HTTP服务,监听8080端口,访问返回字符串hello world, 并在本地启动这个程序
3.去看下DeepSeek最新的论文,总结下涉及到的技术点,分析下哪些行业可能因此受益,并尝试推荐给我3支股票

2.环境准备与要求

技术栈

关于技术能力,你需要具备的知识只要满足下面几点即可:

  • 在任意地方用过AI
  • 能够使用你擅长的技术栈读取用户的输入,无论是从网页、命令行,甚至是消息队列,我们需要给AI下达一个指令!
  • 了解JSON等任意一种数据格式

无论你是专注于前端还是后端,无论你更喜欢 Go 、Java还是 Python,你都能跟随本文实现自己的 AI Agent 命令行工具。


可供调用的模型

考虑到不是所有同学的电脑都有足够的配置来运行本地大模型,因此本文主要使用API的形式来调用模型,因此,你需要:

  • 准备任意厂商的API key,无论是国内还是国外,无论模型能力强弱
  • 良好通畅的网络环境,确保你能够调用你的模型提供商的接口

3. 与AI的第一次交互

做技术的同学都知道,不同公司的API风格迥异,千奇百怪,我们想和模型提供商的API做交互,是不是要适配每个不同的模型提供商的API?

我在2023年做AI应用时,确实是这样的,但万幸,我们有 Open AI !

作为这颗星球上影响力最大的AI服务提供商,再加上出现的时间早,OpenAI的话语权自ChatGPT诞生起,就是绝对的第一,包括API的定义。

在OpenAI的API服务推出一段时间后,其他厂商也陆陆续续推出了自己的API服务,为了尽量减少用户迁移的成本,各大厂商纷纷主动兼容OpenAI的接口标准,你只需修改一下接口的地址,修改下模型的名称和API key,即可无缝在各家AI厂商的模型之间流畅切换。

🌟 扩展内容 - API标准及常见API提供商

OpenAI的接口定义我放在了这里,如果英语不是那么好,可以去看阿里百炼火山方舟(字节)、DeepSeek的API文档,他们都尽量的对OpenAI的接口做了兼容。
除了以上的几家模型提供商外,这里再介绍一些模型提供商作为扩展:

- 硅基流动(一家提供开源模型API的服务商,现在注册送14元, 可用DeepSeek满血版)
- OpenRouter(一站式API服务,他家支持调用国内外绝大多数模型API服务,相当于一个API聚合平台)
- xAI(马斯克的AI平台)
- Claude(一度是写代码最强的模型,也是率先推出Artifacts的平台,但封号比较严重)

那么现在,我们要做的事就简单多了——直接按OpenAI的接口标准来接入模型即可。这里我们使用langchiango这个库来简化这个过程。

🌟 扩展内容 - 什么是LangChain?

LangChain是个LLM Agent框架,掌握了LangChain,也就掌握了Agent的核心思想了。LangChain的原始版本是python版本,后续官方也提供了JS版本的实现。而langchaingo则是非官方的,LangChain的Go版本实现。

你并不一定要使用LangChain,如果你只是为了接入API,市面上有很多OpenAI风格的封装库,你甚至可以自己封装一个,这里我们只是为了顺便介绍LangChain。

你可以在LangChain Python和JS的官网上找到类似下面Go语言版本的代码,写法大体上都是相同的。
package main

import (
    "context"
    "fmt"
    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/llms/openai"
)

func main() {
    llm, _ := openai.New(
        openai.WithBaseURL("https://api.siliconflow.cn/v1"),
        openai.WithToken("sk-xxxxxxxxxxxxxxxxxxxxxxxx"),
        openai.WithModel("Qwen/Qwen2.5-Coder-32B-Instruct"),
    )
    prompt := "七猫小说和番茄小说哪个好看?"
    completion, _ := llms.GenerateFromSinglePrompt(context.TODO(), llm, prompt)
    fmt.Println(completion)
}

在这个例子中

  • 我使用openai.New来调用OpenAI风格的接口
  • 使用WithBaseURL修改了API的接口地址,指向硅基流动
  • 使用WithModel修改了要使用的模型为阿里开源的千问2.5,硅基流动上提供了这个模型
  • 忽略了错误,你自己在实现和调试时,请记得处理错误 ; )

执行这段代码后,你将得到LLM给的回复:

七猫小说和番茄小说都是提供网络小说阅读的平台,它们各自拥有大量的小说资源,涵盖多种类型和题材。选择哪个平台更好看,主要取决于您的个人阅读偏好和兴趣。

1. **七猫小说**:这个平台上的小说种类繁多,包括玄幻、都市、言情、历史等多种类型。七猫小说还经常举办各种活动,如签到送VIP、阅读挑战赛等,吸引了不少读者。

2. **番茄小说**:同样提供丰富的网络小说资源,番茄小说以其简洁的界面和流畅的阅读体验受到很多用户的喜爱。此外,番茄小说也经常推出各种福利活动,鼓励用户阅读。

如果您对某个特定类型的小说特别感兴趣,建议您可以在两个平台上都搜索一下,看看哪个平台有更多您喜欢的作品。同时,也可以根据平台的用户评价和推荐来选择更适合自己的平台。

现在,将第16行的字符串改为从用户输入读取(键盘,消息队列,whatever),我们就算完成了Agent中"输入"的这部分。

你可能会有些嗤之以鼻,这就是一次毫无技术含量,最基础的对话嘛。别急,我们这个最原始的输入,将作为AI要遵循的指令或要解决的问题,贯穿整个Agent的生命周期。

现在我们再进一步,来让AI帮我们做一些事情。

4. 决策

我在演示视频中,让AI帮我在本地创建了一个文件夹,AI是如何做到的呢?如果我们直接告诉AI,要让他帮我们创建一个文件夹,那么AI大概率会回复你"对不起,我做不到,但是我可以告诉你如何创建一个文件夹"这样的话。

坏消息是: 它只是个API,没办法在我们本地做任何事情。
好消息是: AI是能理解我们的意思的,并且知道如何达成目标。

想象一下《钢铁侠》中托尼和贾维斯的互动过程,托尼只需要说出想做的事,贾维斯就能理解并执行。

我们现在要做的事就是: 让AI代入进助手这个角色设定,让它自己推理,下一步做什么,才能有助于解决问题。
我们回到让AI创建文件夹这个场景,我们要让AI做出这样的决策:

“为了实现主人的目标,我接下来需要执行mkdir 文件名

决策这一步你可以使用推理能力比较强的模型(如DeepSeek R1, OpenAI o1等),这有助于提高AI决策结果的正确率。我们来给AI加上人设:

你是一个善于执行任务的AI助手, 你需要根据用户的需求,以“我”为主语,从用户的视角来判断接下来在命令行内执行什么命令有助于解决该问题。
如果有多个选择,你只能选择你认为当前对你最有帮助的一条命令。
当前的操作系统是{{.os_name}}
⚠️上面这个Prompt并不是一个很好的Prompt,仅仅是是“够用”,Prompt作为与AI沟通的格式,也是有调优的技巧和格式的。关于Prompt的教程网络上已经非常多了,我们在这里就不深入展开了,这对我们的教程不会产生影响。

这里解释下,Windows/MacOS以及Linux平台等各个操作的命令都或多或少有些区别,告诉AI当前的操作系统,能够让AI尽可能的做出准确的决策,因此我们要尽可能的提供更多的信息给他。

🌟 扩展内容 - 如何让AI对外部进行感知  

除了操作系统外,你还可以提供当前时间、地理位置给AI,AI本身是无法获取这些信息的,但你作为应用的开发者,获取这些内容还是比较轻松的,将这些信息作为Prompt的一部分传递给AI,就能让AI“感知”到这些外部条件。

那么这段内容加在哪里呢?直接加到用户输入的前面或者后面吗? 如果你已经阅读过上面OpenAI API的接口参数,那么你就知道,我们输入给大模型的请求其实是分角色的。

OpenAI的请求的message参数允许对话中有多个不同的角色:

不同模型服务提供商,这里支持的字段也不一样,详细的文档可以去你使用的模型提供商提供的文档里查看。我们这里把上面这段话放进system角色里,把用户输入的内容放进user里, 并发送给AI:

import (
    "github.com/tmc/langchaingo/llms"
    "github.com/tmc/langchaingo/prompts"
)

func main() {
    ...
    
    // 构造推理决策使用的prompt
    reasonPrompt, _ := prompts.NewChatPromptTemplate([]prompts.MessageFormatter{
        prompts.NewSystemMessagePromptTemplate(
           `你是一个善于执行任务的AI助手, 你需要根据用户的需求,以“我”为主语,从用户的视角来判断接下来在命令行内执行什么命令有助于解决该问题。如果有多个选择,你只能选择你认为当前对你最有帮助的一条命令。当前的操作系统是{{.os_name}}`,
           []string{"os_name"},
        ),
        prompts.NewHumanMessagePromptTemplate(
           //`在当前目录下创建一个helloworld的目录`,
           //`请求www.baidu.com的首页`,
           //`列出当前目录下的文件`,
           "帮我看下当前目录下有没有main.go这个文件?`",
           nil,
        ),
    }).Format(map[string]any{
        "os_name": runtime.GOOS,
    })
    
    ctx := context.Background()
    // 将prompt发送给指定的模型让它回答
    completion, _ := llms.GenerateFromSinglePrompt(ctx, reasonModel, reasonPrompt)
    fmt.Println(completion)
}

上面这段代码会构造出这样的JSON:

{
  "messages": [
    {
      "role": "system", // system里放我们给AI的设定和要求
      "content": "你是一个善于执行任务的AI助手, 你需要根据用户的需求,以“我”为主语,从用户的视角来判断接下来在命令行内执行什么命令有助于解决该问题。如果有多个选择,你只能选择你认为当前对你最有帮助的一条命令。当前的操作系统是linux"
    },
    {
      "role": "user",  // user里放用户的问题
      "content": "帮我看一下当前目录下有没有main.go这个文件?" 
    }
  ]
}

我们将这段带有人设的Prompt发送给AI,AI的回答结果如下:

我可以在命令行内执行以下命令来查看当前目录下是否有 `main.go` 文件:

```bash
ls main.go
```

如果文件存在,这个命令会列出 `main.go`;如果文件不存在,则不会有任何输出。

可以看到,AI已经成功代入进去我们为他设定的人设里,决定接下来要执行ls main.go命令来查看当前文件夹下是否有main.go这个文件了。

到这里,AI已经做出了正确的决策😆,接下来我们来看看如何让AI帮我们“执行”对应的操作。

5. 执行

我们前面提到,AI只是个远程的API,无法在本地帮你执行任何操作,所以执行这个事儿,还得我们另想办法。既然AI已经给出了要执行的命令,那我们是不是只要把上面决策中的命令提取出来,帮AI执行就好啦?没错,AI执行命令的原理就是这样!现在摆在我们面前的是个新的难题:“如何解析AI的回答,从里面提取出要执行的命令呢?”。

  • 正则? 不太行,每次AI回答的格式都不固定,这个方案不太靠谱。那我们是否可以让另一个AI来帮我们提取,并以JSON等便于程序解析的格式进行回答呢?
AI说 : NLP啊,这事儿我在行啊,找我准没错!

我们来给AI构造一段新的提示词:

你是一个JSON构造器,能够从用户的输入里解析要执行的命令,并以JSON格式输出,格式如下:
{"command": 要执行的命令, "args": [参数1,参数2,..., 参数n]}
无论用户说了什么,你始终要以这种格式进行返回,没有任何例外

我们在Prompt里指定AI要以JSON的格式将要执行的命令返回出来,这样AI在回答时,就只会输出JSON格式的内容。

🌟 扩展内容 - 结构化输出与函数调用  

这里我们演示的是最笨且易于理解的方法,在真实的企业级应用中,我们通常会使用“结构化输出”这个功能。OpenAI的接口里已经提供了这个选项,我们只需要将response_format字段设置为{ "type": "json_object" }即可。  

另外还有一个生产级别的解决方案:函数调用(Function calling), 我们在和AI对话时,可以在tools参数里传递给AI,有哪些工具可以利用,以及工具接受的接口参数,AI在回答时如果觉得需要调用工具来解决,就会以JSON格式返回工具名称以及参数,我们只需要按AI给的参数调用响应的工具/接口即可。  

但要注意的是,以上两种方案并不是所有的模型或供应商都支持,详情还需要你去接入的模型文档里查看。有时候在面对一些不那么稳定或不支持这些特性的模型时,我们的“笨”方法反而出奇的有用😆

这里我没有复用刚刚的推理模型,而是选择了另一个模型来帮我们解析并输出JSON,因为这个工作并不需要很强大的推理能力,反而对稳定结构化输出的能力有比较强的要求,以防哪次返回的格式变成我们预期之外的。

我选择了Qwen2.5-32B-Instruct来作为JSON的提取和输出,该模型对结构化输出有较好的支持。

让我们把刚刚模型的决策回答作为用户输入,放进新的Prompt里,并在调用模型时加上llms.WithJSONMode()让模型尽量以JSON格式进行回答:

// 5.构造JSON模式的prompt
jsonMode, _ := prompts.NewChatPromptTemplate([]prompts.MessageFormatter{
   prompts.NewSystemMessagePromptTemplate(
      `你是一个JSON构造器,能够从用户的输入里解析命令,并以JSON格式输出,格式如下:
{"command": 要执行的命令, "args": [参数1,参数2,..., 参数n]}
无论用户说了什么,你始终要以这种格式进行返回,没有任何例外
`, nil),
   prompts.NewHumanMessagePromptTemplate(
      completion, // 将模型的决策作为用户输入放进prompt里
      nil,
   ),
}).Format(nil)

cmdJson, _ := llms.GenerateFromSinglePrompt(ctx, commandModel, jsonMode, llms.WithJSONMode())

再次运行程序并观察AI的回答, 发现AI的回答已经变成了一串JSON:

有了固定格式的JSON后,剩下要做的事就简单了,我们把这个JSON字符串解析为对应的结构体,然后“帮”AI执行这个命令就可以啦:

import "os/exec"

type Command struct {
    Command string   `json:"command"`
    Args    []string `json:"args"`
}

func main() {
    ...
    // 解析出JSON命令
    var cmdMsg Command
    if err := json.Unmarshal([]byte(cmdJson), &cmdMsg); err != nil {
        log.Fatalf("解析JSON命令时出现了错误: %v", err)
    }
    // 帮执行AI想要执行的命令
    command := exec.Command(cmdMsg.Command, cmdMsg.Args...)
    output, _ := command.Output()
    log.Println(string(output))
}
🌟 注意安全

直接让AI执行未经审查的命令可能会损害你的计算机!生产环境中,在执行AI想要执行的命令之前,可以再引入一个审查模型,来对即将要执行的命令进行审查。
🌟 思维发散  

你可以对上面的Prompt稍作修改,或者试试函数调用(Function Calling)功能,让AI支持更多的操作。比如你现在有下面这几个工具:

- 打开播放器,听某首歌:func PlayMusic(歌名)
- 从数据库中搜索某个小说: func SearchRecord(小说名)
- 联网搜索一些内容: func SearchWeb(关键字)
- 往某个消息队列里发送一条消息: func SendMessage(Topc, Message)
- 打开家里的电灯func TurnOnTheLight(房间,灯ID)
- ....  

把这些工具以及它们的用途、参数列表描述给AI,让AI决定要不要使用工具,AI在回答时,就会告诉你要调用哪个工具,并帮你构造好工具的参数。所以,尽情发挥你的想象力吧,抛弃所有的if else,把一切都交给AI吧😉

6. 决策

是的,你没看错,我们这个章节的标题又是“决策”。我们在前面的小节里,让AI进行决策要执行的命令,并帮助AI执行了命令,但我们的任务还没结束。想要实现Agent,需要让AI根据命令的执行结果,来决定是否要进行下一步操作。

为了控制篇幅,在我们这个简单的场景里,我不会带你搭建一个完备的Agent系统,我们的Agent就只到这里,但我会告诉你如何继续后面的内容😉

我们把上面命令的执行结果整理后,再次询问AI,让AI结合命令的执行结果,来进行下一次回答:

你是一个善于执行IT任务的AI助手, 你需要根据用户的需求来一步一步的操作计算机
用户的问题是:{{.user_question}}
下面是你已经执行过的命令和输出:
{{.command_logs}}
请根据这些内容,对用户进行回复,告诉用户你做了什么,结果是什么

把上面prompt中的占位符补充完整,分别放入用户的问题,以及刚刚执行的命令和命令返回的结果,发给AI完整的prompt如下:

你是一个善于执行IT任务的AI助手, 你需要根据用户的需求来一步一步的操作计算机
用户的问题是: 帮我看下当前目录下有没有main.go文件?
下面是你已经执行过的命令和输出:

1.你执行了命令:
ls main.go
返回结果: 
main.go

请根据这些内容,对用户进行回复,告诉用户你做了什么,结果是什么

这样AI就能根据刚刚命令的操作结果进行回答啦, 完整的过程如下:

# 用户输入问题
> 帮我看下当前文件夹下有没有main.go这个文件

# 推理模型推理要执行的命令
2025/02/23 11:26:39 推理模型返回的决策内容: 我可以在命令行内执行以下命令来查看当前文件夹下是否有 `main.go` 这个文件:

```bash
ls main.go
```

如果文件存在,这个命令会列出 `main.go`;如果文件不存在,则不会有任何输出。
# 模型的JSON格式化回答
2025/02/23 11:26:39 模型提取出的JSON输出: {"command": "ls", "args": ["main.go"]}
# 上面命令的执行结果
2025/02/23 11:26:39 main.go
# 将执行结果反馈给AI,让AI结合这些内容进行回答
2025/02/23 11:26:39 system: 你是一个善于执行IT任务的AI助手, 你需要根据用户的需求来一步一步的操作计算机,观察结果并判断是否要继续执行命令。
用户的问题是:帮我看下当前文件夹下有没有main.go这个文件
下面是你已经执行过的命令和输出:
你执行了命令: ls main.go
执行结果: main.go


请根据这些内容,对用户进行回复,告诉用户你做了什么,结果是什么

# AI最终的回答
我已经执行了命令 `ls main.go` 来检查当前文件夹下是否存在 `main.go` 文件。执行结果是 `main.go`,这表明当前文件夹下确实存在 `main.go` 文件。

我们的Commander到这里就算完成啦~ 当然,它并不是个完备的Agent的程序,里面的问题也还有很多,Commander的目的是教学,希望能用这么个简单的例子告诉你Agent应用的幕后实现。

文中的Commander是线性的,并没有根据命令的反馈,再次进行下一波决策->执行->观察->决策->执行这一过程。你可以把它当做一道课后作业来实现。

就像为你的程序加上for循环那样😉

for {
    决策
    调用工具执行
    观察是否达成结果
    if (不需要继续): break
}

7. 完整代码

完整代码实现我已经放在了Github上,你可以结合着代码一起来看,会更加容易理解:
https://github.com/bootun/commander

附录: 文中未提到的内容

拥有记忆的AI

在Commander的制作过程中,所有的上下文都是我们构造好传递给AI的,AI本身并没有记忆,如果我们要实现一个智能客服/AI问答这类系统,需要把之前每一次的问答记录作为记忆传递给AI:

{
    "messages": [
        {
            "role": "system",
            "content": "你是个高冷的七猫小说客服,你回答问题从不解释理由,并且会在回答的最后加上😒表情"
        },
        {
            "role": "user",
            "content": "七猫小说好看还是番茄小说好看?"
        },
        {
            "role": "assistant",
            "content": "七猫小说😒"
        },
        {
            "role": "user",
            "content": "为什么呀?能给些理由吗?"
        },
        {
            "role": "assistant",
            "content": "别管😒"
        }
    ]
}

如果你在调用接口时,不把历史聊天记录告诉AI的话,用户在问“为什么呀”这句话时,AI就会感到茫然,然后不按照我们给他的人设回答。关于记忆,现在市面上主流的做法有下面几种:

  1. 全量记忆: 把所有的聊天记录都发给AI,这种做法比较耗费token,但通常来说效果会比较好,不太会出现AI忘记你们聊过内容的情况
  2. 最近N条记忆: 把最近的N条聊天记录发给AI,这样AI可以有效的捕捉到最近对话中的细节,但会遗忘较早的聊天内容,业界通常的做法是同时限制聊天条数,比如一个对话只能进行15、30次聊天,到达限制后就强制开启新的聊天。
  3. 总结记忆: 在AI回答后,再调用AI对历史会话做成总结,下次对话只将总结发送过去,这样可以有效减少token的使用数量,但代价就是AI会不记得那么细节的内容
  4. 总结+最近N条记忆: 最近N条的记忆全量发送给AI,而过于久远的内容则让AI进行总结,因为通常用户也不太关心非常久远之前的对话。这些方案没有绝对的好坏,根据不同的场景灵活调整即可。

DeepSeek/o1之前,普通模型如何做深度思考的

在DeepSeek和OpenAI o1这种思考模型出现之前,我们也有方法人工引导AI进行思考,一般是在Prompt中提前对AI进行引导,例如著名的思维链(Chain of Thought, CoT):

又或是AI中的ReAct框架,都是通过prompt来引导AI思考,从而释放AI的潜力

如何实现基于知识库的问答

在实际的应用场景中,通用的AI并不能完全胜任所有工作,在智能客服等需要领域垂直知识的场景,需要为AI增加对应领域的专属知识,通常我们会为AI建立一个知识库,并放入我们希望AI回答时能够结合参考的资料。目前业界主流的知识库问答有以下几种方案:

  1. 外挂向量知识库  将文件资料使用向量模型进行向量化后,存入向量数据库中,在AI进行回答时,从向量数据库中进行检索,这样能够检索出语义上与用户问题相近的资料。但向量数据库也存在许多问题,在生产级的系统中,开源的向量数据库性能往往不能满足需求,并且向量检索也不总是返回可靠的内容。
  2. 外挂数据库(接口),用AI构造参数  即使是传统数据库甚至是API接口,也能使用AI进行搜索。让AI在用户的问答中总结用户想要搜索的关键词,甚至是告诉AI数据库的表结构,让AI直接帮忙写SQL进行执行。但该方法也存在问题,AI很多时候并不能准确的识别某些“黑话”,因此可能会给出错误的参数。比如你有这样一个数据库搜索接口:
func SeachDB(小说名, 年份)

用户问AI,帮我搜索庆余年小说这个这个问题,如果AI并不知道庆余年是一部小说名的话,他可能会给出这样的参数:

小说名: ""
年份: "庆余年"

这个问题我们会在后面的fine-tuning部分进行解决。

如何对模型进行微调(Fine-tuning)

我们使用的AI大模型接口都是预训练好的模型,训练使用的参数我们是无法控制的。所以在遇到领域专有知识时,如果AI不知道,就可能会总结出错误的内容。

就像上面庆余年的例子一样,如果AI不知道庆余年是一部小说名,那在总结参数时,就可能错误的把庆余年当作年份参数。而自己训练模型的成本又比较高,通常效果还比较差,这个时候我们就可以使用微调来解决这个问题。

模型提供商基本都提供了微调的API接口,微调相当于对模型打了个patch,让模型能够通过后天学习来变得更强。我把阿里云的微调文档放在了这里,感兴趣的同学可以试试看。

但要注意,不是所有的模型都提供微调能力喔,细节请看模型提供商给出的文档。

ChatGPT中,跨聊天的记忆是如何实现的?

如果你在OpenAI的ChatGPT中告诉它,“我是七猫小说的员工”,那GPT会记住这一设定:

这时候你如果新开启一个话题,问他:我是哪个公司的员工,那ChatGPT会告诉你,他知道:

记忆功能的核心是ChatGPT会从你的对话中判断有没有需要记忆的内容,如果它认为有必要记住这个内容,他就会将其提取出来,在别的回答中作为上下文结合进行回答。相信你看完了本文之后,这个“记忆”功能的实现对你来说也已经不在话下啦。

如何让AI能够联网搜索

即使没有工具,到这里相信你也已经知道如何让AI进行联网了,调用网络搜索API,将返回的内容传给AI作为参数。

这样你就手搓了一个联网框架🤣

目前已经有很多已经封装好的的API服务和工具,我们可以通过查看LangChain支持的Web搜索工具列表,来发现新的工具和组件。

LangChain作为实验性质的学术研究产物,虽然直接用在生产环境不一定合适,但其中很多的理念是比较有参考意义的,对AI感兴趣的同学建议可以看看LangChain的源码,会有非常多的收获😉。

另外,你也可以试试下面这些目前AI应用中常见的搜索服务:

支持拖拉拽的可视化Agent工具

手搓还是太麻烦, 有没有拖拖拽拽的方式?如果你想试试可视化的拖拉拽方式实现自动化的Agent,或想快速利用AI做一些自动化的工作流,你可以试试下面这些工具:
  1. Dify
  2. Flowise
  3. Langflow
  4. n8n

其他注意事项

在实现Agent的过程中,有些内容需要额外注意,比如安全和任务的终止。

  • 安全: AI给出的内容并不总是安全合规的,我们在输出或执行指令前,都需要再加入对应的审查机制,来评估是否能够输出或执行AI给出的内容。
比如我们的Commander应用程序里,如果AI回答了rm -rf /这个指令,那就丸辣😢。 再或者让AI帮忙写SQL查询数据,也可能会被用户利用,查询出你限定范围之外的数据。
  • 任务的终止: 试想一下,某个任务AI觉得能够完成,但经过了许多轮的反复推理,依然还没解决。此时AI可能依旧觉得问题是可被解决的,如此一来就陷入了无限的循环中,Token燃烧量爆炸,资源消耗量爆炸,因此在设计Agent时需要做好任务的终止机制,避免陷入无尽的循环。

课后作业

还记得我在文章开始时给你提到的一些场景吗? 我们拿这个场景来举例:

帮我看下k8s中测2环境的xxx服务的最近100条日志,有没有报错?

我们来看看这个任务里的核心难点:

  • 测2环境
  • xxx服务

这些内容是我们内部专有名词,AI会使用k8s,但它不知道测2的namespace,不知道xxx服务的名字和意义,我们可以通过附录中提到的记忆、知识库、Fine-tuning等方式来让AI知道这些内容具体的含义。

我把它留做一个课后作业,感兴趣的同学可以试试看。