开篇
小7是一名从Java转向Golang的开发者,也是AI重度依赖者,但经常听到身边的朋友都在说一些“MCP”“Skills”之类的让人听不懂的话
他决定从最底层去了解身边的开发小助手——AI
AI体系的核心--LLM
LLM(Large Language Model),翻译为中文叫做“大语言模型”。
所有的一切要从2017年由谷歌的7位年轻人发表的一篇论文 《Attention Is All You Need》 说起。
proceedings.neurips.cc/paper_files…
感兴趣的同学可以自行阅读
前世
在这篇论文出来以前,几乎所有的语言模型都是基于RNN/LSTM/GRU等架构,存在着以下弊端:
- 串行计算:每个词语输出都需要等待上一词语完成计算,GPU利用率不高
- 依据距离计算词语关联度:一旦文字过长,某些词语的语义会被稀释。e.g. 上一章节中的“我”,在本章中模型无法很好理解
- 结构复杂:模型十分臃肿,调优难度大。
今生
这篇论文出来以后,颠覆了往前的模型训练方式,如今主流的大模型都是根据该文章的Transformer架构进行设计的。
有趣的是,论文刚发布时Google内部并没有过多关注,直到Open AI将其理论落地,ChatGPT横空出世,Google这才意识到这篇文章对于现代AI体系的重要性
Token
要了解LLM,首先要知道它是如何处理文字的
小7在输入框输入:"Hey, handsome boys and beautiful girls"
对于大模型来讲,它并不知道这句话的意思,只能逐词的分析,计算。
它会将这句话拆分成一个个的单词,每个单词如"Hey"就是一个token。
中文的分词稍微不同,但原理是一样的
Context Window
分词完成后,模型会将所有的token加载到Context Window中,也就是上下文窗口。
模型会根据其中的token进行一系列的计算,但其计算能力是有限度的,每次最多只能处理N个token
因此,依据模型能力的不同,一次所能处理的最大token数又被称为上下文窗口
预测
模型的本质是一个python程序,它会根据输入的token和预训练的数据计算出下一个token的概率
它并不能真正理解人类自然语言的语义和情感,只是一个无情的Next Token 预测器
Prompt
既然AI是基于概率工作的,那么不同的输入得到的结果可能会天差地别。
也许在人类看来,某两个词语的含义是差不多的,但在AI的计算中,两个词语的下一个Token却是完全不一样的
同样是用AI,别人能够很容易拿到想要的结果,小7的AI却总是答非所问。
这就是Prompt--提示词工程的力量。
经过不断的摸索,小7终于发现了Prompt的诀窍:
- 完整的背景
- 清晰的要求
- 明确的输出格式
e.g. 以前的小7对LLM说“我要学英语”,得到一堆无用的学习英语的理论
现在的小7会说:“我是一名大学生, 正在准备CET-6, 我希望你使用考纲内的高频词写一篇600词左右的文章,并给出准确的中文翻译”
对比两段Prompt,“我要学英语”过于广泛,LLM的Next Token预测准确性自然很低。
Memory
问题背景
在使用LLM的过程中,小7还发现:LLM是无状态的。
用大白话解释就是,LLM并不记得小7上一次跟它说了什么。
它只能处理当前Context window中的token
解决办法
小7灵机一动,将历史对话在电脑上存储起来,并给每个会话一个唯一的id。
然后写一段程序,在每次与LLM交互前,将他的输入和历史对话打包,一并发送给LLM
这样LLM有了完整的对话背景,就能够更准确的预测小7想要的Next Token
在小7的视角里,LLM就有了记忆
Tool 与 Agent
有了LLM和Prompt后,小7已经可以利用AI节省查资料的时间。
但是问题来了,LLM只能对话,不能做事
这让小7很苦恼,他有一个只思考而不干实事的员工
怎么样让AI动起来呢?
小7想出了一个办法,他写了一个工具函数Tool用于操作本地电脑上的文件,一个正则解析程序用于解析LLM生成的回答,和一个调用Tool的调度器
小7告诉LLM有哪些它可以调用的工具,一旦需要调用,告诉小7
现在,小7让LLM查询本地某个目录下有哪些文件
LLM经过思考后,认识到需要调用Tool,并输出结果
脚本程序对LLM输出的自然语言进行解析,得到Tool函数需要的参数,并交给调度器
调度器依照给定的参数(目录路径)调用Tool,并将结果和小7的要求一并交给LLM
LLM把结果整理成自然语言告诉小7。
至此,一个简单的,能够操作文件的Agent就诞生了。
Function Calling
问题背景
问题接踵而至,小7又写了一个函数工具,他希望AI也能调用这个工具🔧
他根据新工具的参数修改了解析器,让解析器能够解析新工具的参数
难道说,每新增一个工具,解析器就得重新编写?
另外,LLM生成的回复格式并不固定,它太有礼貌了,哪怕小7不需要它的礼貌用语。一旦出现正则表达式之外的表达,解析器就会失效
还有,不同厂家的模型调用API也不一样。一旦换一家模型,就得修改程序以适配其API的调用方式
解决办法
小7苦思冥想,终于找到了解决办法
1. 构建工具清单
他将所有的工具通过json文件进行定义和描述,构建出一个工具清单, 如下示例:
{
"name": "query_file",
"description": "查询指定目录下的所有文件名称",
"params": {
"path": {
"type": "string",
"description": "指定查询的路径"
},
"required": ["path"]
}
}
name:工具名称description: 工具描述params: 许多参数(本例中只有一个)封装成的对象path: 参数的名称type: 参数类型required:必填的参数
2. 规定回复格式
小7又将工具的调用请求写成了json,如下:
{
"function_call": {
"name": "query_file",
"params": {
"path": "xxx"
}
}
}
3. 约束LLM
接下来,小7写了一段程序,每次与LLM交互前,将工具清单,调用格式与小7的输入打包,一并交给LLM
4. 最后
当LLM判断出基于它已有的知识无法解决问题而需要调用工具时,按照指定的json格式输出调用指令
这样一来,新增工具无需修改解析器代码;没有无效的输出,解析更加准确;所有大模型通用
后来,小7了解到,很多行业大佬与他的想法不谋而合,并将其塑造成了LLM通过Agent调用Tool的规范,称作 Fanction Calling
MCP
问题背景
小7在Github上找到了一个特别好使的Tool,这个工具🔧是某位大佬用Rust语言写的,而小7的Agent程序却是用Go写的,因以下原因没法集成:
- 运行环境不一致
- 参数和返回值不一致
- 错误处理,数据格式转换不一致
- 模型支持不一致
无奈之下,小7学习了大佬的实现思路,然后用Go语言重写了这个Tool,将其集成到自己的Agent
小7不禁思考,全球的开发者那么多,每个人都在开发自己的Tool,但是这些Tool在功能上并没有太大差异,这样一种重复造轮子的行为让开发的效率极其低下
那么有没有办法解决呢?
解决办法
小7在网上找了大量资料,终于找到了一种叫做MCP的解决办法
MCP,Model Context Protocol,翻译为中文叫做模型上下文协议
这是Google在2024年11月25日推出的一项协议,旨在给Tool工具的使用划定一个统一标准
原文链接🔗:www.anthropic.com/news/model-…
在一个完整的MCP体系中,有以下三种角色:
Host:可以理解为Agent Application,负责协调和管理一个或多个ClientServer:为某项服务提供一系列的ToolClient:寻找Server并管理与Server的连接
官方定义如下:
- MCP Host: The AI application that coordinates and manages one or multiple MCP clients
- MCP Client: A component that maintains a connection to an MCP server and obtains context from an MCP server for the MCP host to use
- MCP Server: A program that provides context to MCP clients
小7在学习时看到满篇英文也是头大,索性直接以图说话:
在上图中,LLM发出工具调用指令,Agent通过MCP Client调用Github MCP Server所提供的action工具集中的issue_write函数,然后逐层返回执行结果,最终由LLM输出自然语言描述,经由Agent中转,呈现在用户眼前。
issue_write函数并不由小7所写,却通过MCP协议无缝集成到了小7的Agent中,让小7的Agent有了操作Github的能力
如果小7想,他还可以很方便的为Agent点技能,比如操作腾讯云CLS(Cloud Log Service 云日志服务),只需略微修改Client
整个过程中,Agent扮演了Host的角色,协调并管理着Client。
Client则像组件一样被嵌入到Agent中,负责通过MCP协议与Server建立连接并转达Tool工具的调用
Server则真正地负责Tool的调用,与Github,腾讯云CLS打交道
作为开发者,小7无需关心Tool是怎么实现和运作的,只需要通过Client调用即可
回顾
本篇文章以小7的视角,通过一步步解决问题的方式,从只能对话的LLM,讲到能够执行任务的Agent,揭露了AI的发展过程。
一种技术是怎么来的,为什么需要它,它解决了什么问题?
小7正是基于对以上问题的思考,以独特的视角向读者展开文章
最后
在下篇中,小7还会通过RAG,Skill,ReAct, Plan-Excute-RePlan构建现如今的,强大的开发助手,敬请期待~
最后的最后,如果有帮助的话,动动小手点个赞哦👍,每个赞都会鞭策笔者继续码字的🥺
如果有错误,也欢迎各位大佬指正